极坐标#

import matplotlib.pyplot as plt
import numpy as np
import polars as pl

Polar Bar Chart#

rng = np.random.default_rng(123)

data = pl.DataFrame({
    "name": [f"item {i}" for i in range(1, 51)],
    "value": rng.integers(low=30, high=100, size=50),
    "group": ["A"] * 10 + ["B"] * 20 + ["C"] * 12 + ["D"] * 8,
})

data.head()
shape: (5, 3)
namevaluegroup
stri64str
"item 1"31"A"
"item 2"77"A"
"item 3"71"A"
"item 4"33"A"
"item 5"93"A"
data_sorted = data.group_by("group").map_groups(
    lambda x: x.sort("value", descending=True)
)
data_sorted
shape: (50, 3)
namevaluegroup
stri64str
"item 14"94"B"
"item 20"92"B"
"item 19"90"B"
"item 18"87"B"
"item 26"87"B"
"item 33"60"C"
"item 36"46"C"
"item 42"46"C"
"item 39"44"C"
"item 31"40"C"
def get_label_rotation(angle, offset):
    rotation = np.rad2deg(angle + offset)
    if angle <= np.pi:
        alignment = "right"
        rotation = rotation + 180
    else:
        alignment = "left"
    return rotation, alignment
def add_labels(ax, angles, values, labels, offset) -> None:
    # This is the space between the end of the bar and the label
    padding = 4

    # Iterate over angles, values, and labels, to add all of them.
    for angle, value, label in zip(angles, values, labels):
        # Obtain text rotation and alignment
        rotation, alignment = get_label_rotation(angle, offset)

        ax.text(
            x=angle,
            y=value + padding,
            s=label,
            ha=alignment,
            va="center",
            rotation=rotation,
            rotation_mode="anchor",
        )
OFFSET = np.pi / 2

GROUP = data["group"].to_numpy()
VALUES = data["value"].to_numpy()
LABELS = data["name"].to_numpy()

# Add three empty bars to the end of each group
PAD = 3
ANGLES_N = len(VALUES) + PAD * len(np.unique(GROUP))
ANGLES = np.linspace(0, 2 * np.pi, num=ANGLES_N, endpoint=False)
WIDTH = (2 * np.pi) / len(ANGLES)

# Obtaining the right indexes is now a little more complicated
offset = 0
IDXS = []
GROUPS_SIZE = [10, 20, 12, 8]
for size in GROUPS_SIZE:
    IDXS += list(range(offset + PAD, offset + size + PAD))
    offset += size + PAD

_, ax = plt.subplots(figsize=(10, 6), subplot_kw={"projection": "polar"})

ax.set(ylim=(-100, 100), xticks=[], yticks=[], theta_offset=OFFSET, frame_on=False)
ax.xaxis.grid(False)
ax.yaxis.grid(False)


GROUPS_SIZE = [10, 20, 12, 8]
COLORS = [f"C{i}" for i, size in enumerate(GROUPS_SIZE) for _ in range(size)]

ax.bar(ANGLES[IDXS], VALUES, width=WIDTH, color=COLORS, edgecolor="white", linewidth=2)

add_labels(ax, ANGLES[IDXS], VALUES, LABELS, OFFSET)
../_images/3574116e3113b250997972bb441af2716b2b14d7efcf1399b63a5903aebda739.png

Polar Scatter Chart#

n_point = 150

r = 2 * np.random.rand(n_point)
theta = 2 * np.pi * np.random.rand(n_point)

area = 200 * r**2  # area of each circle representing each data point
colors = theta
cmap = "hsv"
alpha = 0.75

plt.figure(figsize=(10, 8))

_, axes = plt.subplots(1, 3, figsize=(10, 6), subplot_kw={"projection": "polar"})

axes[0].scatter(theta, r, c=colors, s=area, cmap=cmap, alpha=alpha)

axes[1].scatter(theta, r, c=colors, s=area, cmap=cmap, alpha=alpha)
axes[1].set(rorigin=-2.5)
axes[1].set_theta_zero_location("W", offset=10)

axes[2].scatter(theta, r, c=colors, s=area, cmap=cmap, alpha=alpha)
axes[2].set(thetamin=45, thetamax=135)

plt.show()
<Figure size 1000x800 with 0 Axes>
../_images/5977539770a3b5c837561fe4cf754674ca39c067f0e085e3749762865ee46ead.png

Dotnut#

names = ["groupA", "groupB", "groupC", "groupD"]
size = [12, 11, 3, 30]

plt.pie(size, labels=names, wedgeprops={"linewidth": 7, "edgecolor": "white"})

my_circle = plt.Circle((0, 0), 0.7, color="white")
p = plt.gca().add_artist(my_circle)
plt.show()
../_images/7e008e27d8cdb326a359c6826b37b81d5eb08cb55e3c15cb27f2ea26a2bfbcde.png

Radar#

data = pl.DataFrame(
    {
        "group": ["A", "B", "C", "D"],
        "var1": [38, 1.5, 30, 4],
        "var2": [29, 10, 9, 34],
        "var3": [8, 39, 23, 24],
        "var4": [7, 31, 33, 14],
        "var5": [28, 15, 32, 14],
    },
    strict=False,
)

data
shape: (4, 6)
groupvar1var2var3var4var5
strf64i64i64i64i64
"A"38.0298728
"B"1.510393115
"C"30.09233332
"D"4.034241414
# number of variable
categories = data.columns[1:]
N = len(categories)

angles = [n / N * 2 * np.pi for n in range(N)]
angles += angles[:1]

_, ax = plt.subplots(figsize=(10, 6), subplot_kw={"projection": "polar"})

ax.set(
    theta_offset=np.pi / 2,
    theta_direction=-1,
    rlabel_position=0,
    ylim=(0, 40),
    xticks=angles[:-1],
    xticklabels=categories,
    yticks=[10, 20, 30],
)

plt.setp(ax.get_yticklabels(), color="grey", size=7)

for ind, cat in enumerate(data["group"]):
    values = data.drop("group").to_numpy().tolist()[ind]
    values += values[:1]
    ax.plot(angles, values, label=f"group {cat}")
    ax.fill(angles, values, alpha=0.1)

ax.legend(bbox_to_anchor=(0.1, 0.1))
<matplotlib.legend.Legend at 0x7f67b2e61d30>
../_images/873ef7b355e40a98ac2cbf3b4d517a404a15351882564dd64e4060811e9bdb49.png