import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.lines import Line2D
from matplotlib import font_manager
import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning)
以下数据如果有需要的同学可关注公众号HsuHeinrich,回复【数据可视化】自动获取~
# 导入数据
df = pd.read_csv('https://raw.githubusercontent.com/Lisa-Ho/small-data-projects/main/2023/2308-star-wars-scripts/episode1_each_line_of_anakin_clean.csv')
df.head()

image-20240129181218963
《星球大战》阿纳金对话数据: to:对话角色名称 number:数量
# 计算每个bar的位置(角度)
x_max = 2*np.pi
df['angular_pos'] = np.linspace(0, x_max, len(df), endpoint=False)
# 自定义颜色
chart_colors = {'bg': '#0C081F', 'QUI-GON': '#F271A7', 'PADME': '#40B8E1', 'OBI-WAN':'#75EAB6',
'R2D2': '#F4E55E', 'other': '#444A68'}
# 将颜色映射给'to'字段
df['colors'] = df['to'].map(chart_colors)
# 缺失值填充
df['colors'] = df['colors'].fillna(chart_colors['other'])
# 查看调色板
sns.palplot(list(chart_colors.values()))

output_7_0
# 初始化布局
fig, ax = plt.subplots(figsize=(10, 10), subplot_kw={'projection': 'polar'})
# 条形图
ax.bar(df['angular_pos'], df['number'], alpha=1, color=df['colors'] , linewidth=0, width=0.052, zorder=3)
# 自定义轴
max_value = 50
r_offset = -10
ax.set_rlim(0, max_value)
ax.set_rorigin(r_offset)
# 从顶部开始顺时针绘制条形图
ax.set_theta_zero_location('N')
ax.set_theta_direction(-1)
# 标题+著作信息
plt.figtext(0.5,1.03, 'Star Wars Episode I', fontfamily='Mandalore', fontsize=55, ha='center')
plt.figtext(0.5,0.98, 'Each line of Anakin', fontfamily='Ubuntu',fontsize=24, ha='center')
plt.figtext(0.5,0.08, 'Data: jcwieme/data-scripts-star-wars | Design: Lisa Hornung',
fontfamily='Mandalore',fontsize=8, ha='center', alpha=0.75)
plt.show()

output_8_0
# 自定义缩放y轴
max_value = 50
r_offset = -10
r2 = max_value - r_offset
alpha = r2 - r_offset
v_offset = r_offset**2 / alpha
forward = lambda value: ((value + v_offset) * alpha)**0.5 + r_offset
reverse = lambda radius: (radius - r_offset) ** 2 / alpha - v_offset
ax.set_rlim(0, max_value)
ax.set_rorigin(r_offset)
ax.set_yscale('function', functions=(
lambda value: np.where(value >= 0, forward(value), value),
lambda radius: np.where(radius > 0, reverse(radius), radius)))
# 自定义标签
ax.set_rlabel_position(0)
ax.set_yticks([10,20,30,40])
ax.set_yticklabels([10,20,30,40],fontsize=9)
# 自定义网格线
ax.set_thetagrids(angles=[])
ax.grid(visible=True, axis='y', zorder=2, linewidth=0.75)
# 移除边框
ax.spines[:].set_visible(False)
fig

output_10_0
# 初始化极坐标图
fig, ax = plt.subplots(figsize=(10, 10),
subplot_kw={'projection': 'polar'})
# 背景色
ax.set_facecolor(chart_colors['bg'])
fig.set_facecolor(chart_colors['bg'])
# 添加条形图
ax.bar(df['angular_pos'], df['number'], alpha=1, color=df['colors'],
linewidth=0, width=0.052, zorder=3)
# 从顶部开始顺时针绘制条形图
ax.set_theta_zero_location('N')
ax.set_theta_direction(-1)
# 自定义y轴缩放
max_value = 50
r_offset = -10
r2 = max_value - r_offset
alpha = r2 - r_offset
v_offset = r_offset**2 / alpha
forward = lambda value: ((value + v_offset) * alpha)**0.5 + r_offset
reverse = lambda radius: (radius - r_offset) ** 2 / alpha - v_offset
ax.set_rlim(0, max_value)
ax.set_rorigin(r_offset)
ax.set_yscale('function', functions=(
lambda value: np.where(value >= 0, forward(value), value),
lambda radius: np.where(radius > 0, reverse(radius), radius)))
# 自定义轴标签
ax.set_rlabel_position(0)
ax.set_yticks([10,20,30,40])
ax.set_yticklabels([10,20,30,40],fontsize=9, color='white',alpha=0.35)
# 自定义网格线
ax.set_thetagrids(angles=[])
ax.grid(visible=True, axis='y', zorder=2, color='white',
linewidth=0.75, alpha=0.2)
# 移除边框
ax.spines[:].set_visible(False)
# 自定义图例
# 添加图例轴
lgd = fig.add_axes([0.75,0.71, 0.15, 0.25])
# 图例元素
kw = dict(marker='o', color=chart_colors['bg'], markersize=8, alpha=1,
markeredgecolor='None', linewidth=0)
legend_elements =[Line2D([0],[0],
markerfacecolor=chart_colors['PADME'],
label='Padme',
**kw),
Line2D([0], [0],
markerfacecolor=chart_colors['QUI-GON'],
label='Qui-Gon',
**kw),
Line2D([0], [0],
markerfacecolor=chart_colors['R2D2'],
label='R2D2',
**kw),
Line2D([0], [0],
markerfacecolor=chart_colors['OBI-WAN'],
label='Obi-Wan',
**kw),
Line2D([0], [0],
markerfacecolor=chart_colors['other'],
label='Other',
**kw)]
# 移除图例边框
L = lgd.legend(frameon=False, handles=legend_elements, loc='center',
ncol=1, handletextpad=0.2, labelspacing=1)
plt.setp(L.texts, va='baseline', color='white', size=12,
fontfamily='Ubuntu')
lgd.axis('off')
# 注释
# 新增一个内圆:用于指示绘制方向
circ = fig.add_axes([0.453, 0.435, 0.12, 0.12],polar=True)
line_angular_pos = df['angular_pos'][1:-5]
line_r = [5] * len(line_angular_pos)
# 绘制开始和结束的标记
circ.plot(line_angular_pos, line_r, zorder=5, color='white',
linewidth=0.75, alpha=0.4)
circ.plot(line_angular_pos.to_list()[0], line_r[0], zorder=5, color='white',
linewidth=0,marker='o', markersize=3,alpha=0.4)
circ.plot(line_angular_pos.to_list()[-1], line_r[-1], zorder=5, color='white',
linewidth=0,marker='>', markersize=3,alpha=0.4)
# 自定义轴:从顶部开始顺时针绘制条形图
circ.set_theta_zero_location('N')
circ.set_theta_direction(-1)
circ.axis('off')
# 文本注释
ax.annotate('1 line', xy=(0.1, 48), xycoords='data', xytext=(40, 20),
textcoords='offset points',
fontsize=10, fontfamily='Ubuntu',
ha='left', va='baseline',
annotation_clip=False,
color='#ababab',
arrowprops=dict(arrowstyle='->',edgecolor='#ababab',
connectionstyle='arc3,rad=.5', alpha=0.75))
ax.annotate('Words\nper line', xy=(-0.05, 22), xycoords='data', xytext=(0, 0),
textcoords='offset points',
fontsize=10, fontfamily='Ubuntu',
ha='right', va='baseline',
annotation_clip=False,
color='#ababab')
ax.annotate('', xy=(-0.02, 38), xycoords='data', xytext=(0, -105),
textcoords='offset points',
fontsize=10, fontfamily='Ubuntu',
ha='right', va='baseline',
annotation_clip=False,
color='#ababab',
arrowprops=dict(arrowstyle='<->',edgecolor='#ababab', linewidth=0.75,
connectionstyle='arc3,rad=0', alpha=0.75 ))
lgd.annotate('Talking to', xy=(0.35, 0.78), xycoords='data', xytext=(-18, 14),
textcoords='offset points',
fontsize=10, fontfamily='Ubuntu',
ha='right', va='center',
annotation_clip=False,
color='#ababab',
arrowprops=dict(arrowstyle='->',edgecolor='#ababab',
connectionstyle='arc3,rad=-.5', alpha=0.75))
# 标题+制作信息
plt.figtext(0.5,1.03, 'Star Wars Episode I',
fontfamily='Mandalore',
fontsize=55, color='white', ha='center')
plt.figtext(0.5,0.98, 'Each line of Anakin',
fontfamily='Ubuntu',
fontsize=24, color='white', ha='center')
plt.figtext(0.5,0.1, 'Data: jcwieme/data-scripts-star-wars | Design: Lisa Hornung',
fontfamily='Ubuntu',
fontsize=8, color='white', ha='center', alpha=0.75)
plt.show()

output_12_0
参考:Customized circular bar plot in matplotlib[1]
共勉~
[1]
Customized circular bar plot in matplotlib: https://python-graph-gallery.com/532-customizing-circular-barplot-in-matplotlib/