极坐标#

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

Polar Bar Chart#

rng = np.random.default_rng(123)

data = pd.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()
name value group
0 item 1 31 A
1 item 2 77 A
2 item 3 71 A
3 item 4 33 A
4 item 5 93 A
data_sorted = (
    data.groupby(["group"])
    .apply(lambda x: x.sort_values(["value"], ascending=False), include_groups=False)
    .reset_index(drop=True)
)

data_sorted.head()
name value
0 item 5 93
1 item 2 77
2 item 3 71
3 item 9 53
4 item 7 47
from typing import Any
from typing import Literal


def get_label_rotation(angle, offset) -> tuple[Any, Literal["right", "left"]]:
    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"].array
VALUES = data["value"].array
LABELS = data["name"].array

# 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/98a23f7b966c813ad0005a71c4ac45d444d59e1f2ed562ec48a3a3d1f8cb5352.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/34e96974fe13eec124a672f8c8b694dbcecb319f362efa50c2040ac940ba39d6.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/0c23ee9afecb9abf39412daf153be5a67bb62175f58631827834a4f453e10b65.png

Radar#

data = pd.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],
    }
)

data.head()
group var1 var2 var3 var4 var5
0 A 38.0 29 8 7 28
1 B 1.5 10 39 31 15
2 C 30.0 9 23 33 32
3 D 4.0 34 24 14 14
data.loc[0].drop("group").array
<NumpyExtensionArray>
[np.float64(38.0), np.int64(29), np.int64(8), np.int64(7), np.int64(28)]
Length: 5, dtype: object
# 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.loc[ind].drop("group").array.tolist()
    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))
plt.show()
../_images/890f3d2ff8631d9170cb127a1722ab46ed5aa8ff4aebf7b13434b5f18ddf8901.png