【可视化】Python绘制风车玫瑰图动画

2023-10-24 22:59

本文主要是介绍【可视化】Python绘制风车玫瑰图动画,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最终成品

在这里插入图片描述

思路

风车玫瑰图等效于极坐标系下的堆积条形图。

  1. 绘制直角坐标系下的堆积条形图。pyplot本身没有找到堆积条形图的方法,所以使用计算堆积。
  2. 因为极坐系下,x轴会卷积成一个圆,所以需要提前将x轴的刻度转成弧度。
  3. 由于x轴变换了,所以不能直接使用.bar()方法绘图,同时需要将数据构造转成 层级 * 柱数 的样式,分别绘制条形。
  4. 将子图的坐标系改成极坐标系。
  5. 使用FuncAnimation()方法绘制动画。

环境

Python 3.10

引入包

import pandas as pd
import matplotlib.pyplot as plt 
import matplotlib.animation as animation
import numpy as npplt.rcParams["font.sans-serif"] = ["SimHei"]    # 指定中文字体
plt.rcParams['axes.unicode_minus'] = False      # 指定负号等特殊符号兼容

创建测试数据

创建 (16,3) 的数集,对应16个风车柱子,每个柱子分三层。

data = pd.DataFrame(np.random.randint(70, 100, size=(16, 3)),index=[i for i in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'][:16], columns=["l1", 'l2', 'l3'])data

绘制堆积条形图

pyplot中没有找到堆积条形图的办法,只有 stackplot() 堆积面积图,所以直接用 bar()方法画。
每一层都指定前层的合计值为柱子底部 bottom,实现堆积效果。
因为数据集是随机生成的,所以每张图的柱子高度不一致,勿怪。

fig = plt.figure()
ax = fig.add_subplot()
for i in range(data.shape[1]):plt.bar(x=data.index, height=data.iloc[:, i], bottom=0 if i == 0 else data.iloc[:, 0:i].sum(axis=1))

堆积条形图

转极坐标系

在subplot子图中指定 projection=“polar” ,即可使用极坐标系。

fig = plt.figure()
ax = fig.add_subplot(projection="polar")    # 指定子图为极坐标系
for i in range(data.shape[1]):plt.bar(x=data.index, height=data.iloc[:, i], bottom=0 if i == 0 else data.iloc[:, 0:i].sum(axis=1))

极坐标下的堆积条形图由于x轴的刻度没有转换成角度,所以外圈的刻度不对,A和G靠太近了。

  1. 将x轴转换成弧度。共16个柱子,每个柱子所在弧度即是 2 π / 16 2\pi/16 2π/16
  2. 缩小桩子的宽度,避免挤到一起。
fig = plt.figure()
ax = fig.add_subplot(projection="polar")        # 指定子图为极坐标系
x = [(i * np.pi * 2)/data.shape[0] for i in range(data.shape[0])]  # 计算X轴的弧度
for i in range(data.shape[1]):plt.bar(x=x, height=data.iloc[:, i], bottom=0 if i == 0 else data.iloc[:, 0:i].sum(axis=1))

X轴刻度转成弧度比较接近了,把X轴的角度标签换成数据标签 tick_label=data.index

fig = plt.figure()
ax = fig.add_subplot(projection="polar")    # 指定子图为极坐标系
x = [(i * np.pi * 2)/data.shape[0] for i in range(data.shape[0])]  # 计算X轴
for i in range(data.shape[1]):plt.bar(x=x, height=data.iloc[:, i], bottom=0 if i == 0 else data.iloc[:, 0:i].sum(axis=1), width=np.pi/data.shape[0],   # 调整柱子宽度tick_label=data.index        # 添加标签)

修改标签

修饰文本

调整一下

  1. 把标签加到每个柱子上
  2. 隐藏坐标轴和边框

为啥plt.text()方法一次只能画一条文本?

fig = plt.figure()
ax = fig.add_subplot(projection="polar")    # 指定子图为极坐标系
ax.spines["polar"].set_visible(False)       # 隐藏边框
ax.axes.xaxis.set_visible(False)            # 隐藏X轴
ax.axes.yaxis.set_visible(False)            # 隐藏Y轴
x = [(i * np.pi * 2)/data.shape[0] for i in range(data.shape[0])]   # 计算X轴
for i in range(data.shape[1]):ax.bar(x=x, height=data.iloc[:, i], bottom=0 if i == 0 else data.iloc[:, 0:i].sum(axis=1), width=np.pi/data.shape[0],          # 调整柱子宽度tick_label=data.index               # 添加标签)# 添加文本到柱子上
for i in range(data.shape[0]):plt.text(x=x[i],                                         # 文本的X坐标y=data.iloc[i].sum(),                           # 文本的Y坐标s=f"{data.iloc[i].name}: {data.iloc[i].sum()}"  # 文本内容)

添加文本标签
修改文字:

  1. 旋转文字的角度,与柱子方向一致。
  2. 文字与柱太近了,远离一点。
  3. 同时修改对齐方式。O和P有点不齐。
fig = plt.figure()
ax = fig.add_subplot(projection="polar")    # 指定子图为极坐标系
ax.spines["polar"].set_visible(False)       # 隐藏边框
ax.axes.xaxis.set_visible(False)            # 隐藏X轴
ax.axes.yaxis.set_visible(False)            # 隐藏Y轴
x = [(i * np.pi * 2)/data.shape[0] for i in range(data.shape[0])]   # 计算X轴
for i in range(data.shape[1]):ax.bar(x=x, height=data.iloc[:, i], bottom=0 if i == 0 else data.iloc[:, 0:i].sum(axis=1), width=np.pi/data.shape[0],          # 调整柱子宽度tick_label=data.index               # 添加标签)# 添加文本到柱子上
for i in range(data.shape[0]):plt.text(x=x[i],                             # 文本的X坐标y=data.iloc[i].sum() * 1.05,        # 文本的Y坐标s=f"{data.iloc[i].name}: {data.iloc[i].sum()}",  # 文本内容rotation= (i * 360)/data.shape[0],  # 旋转角度rotation_mode="anchor",             # 指定旋转模式horizontalalignment="left",         # 水平对齐方式verticalalignment="center"          # 垂直对齐方式)

对齐文本

修饰颜色

修改柱子的颜色, 并添加柱子边框。因为配色无能,所以直接使用了plt的颜色重采样,好东西啊。
思路是先使用 resampled() 方法从调色盘中取 16 * 3 = 48 个颜色,再给每个柱子用不同的颜色。

fig = plt.figure()
ax = fig.add_subplot(projection="polar")    # 指定子图为极坐标系
ax.spines["polar"].set_visible(False)       # 隐藏边框
ax.axes.xaxis.set_visible(False)            # 隐藏X轴
ax.axes.yaxis.set_visible(False)            # 隐藏Y轴
x = [(i * np.pi * 2)/data.shape[0] for i in range(data.shape[0])]   # 计算X轴
color_list = plt.colormaps['inferno'].resampled(48).colors          # 从调色盘中重采样48个颜色,做为柱子的颜色。for i in range(data.shape[1]):ax.bar(x=x, height=data.iloc[:, i], bottom=0 if i == 0 else data.iloc[:, 0:i].sum(axis=1), width=np.pi/data.shape[0],          # 调整柱子宽度tick_label=data.index,              # 添加标签color=color_list[i*16:(i+1)*16],    # 添加颜色edgecolor="k",                      # 指定组间边框颜色linewidth=1,                        # 组间边框宽度)# 添加文本到柱子上
for i in range(data.shape[0]):plt.text(x=x[i],                             # 文本的X坐标y=data.iloc[i].sum() * 1.05,        # 文本的Y坐标s=f"{data.iloc[i].name}: {data.iloc[i].sum()}",  # 文本内容rotation= (i * 360)/data.shape[0],  # 旋转角度rotation_mode="anchor",             # 指定旋转模式horizontalalignment="left",         # 水平对齐方式verticalalignment="center",         # 垂直对齐方式)plt.show()

修饰颜色

制作动画

先作个简单的动画,即按照绘制顺序,逐个实现动画效果:

  1. 创建自定义函数 update() 保存每一帧的绘图方法。
  2. for循环的子句移入动画函数,再调用matplotlib的FuncAnimation()方法。
  3. 第0帧时,先清空画布,再绘制。
  4. 最后一帧时,把文字画上去。
fig = plt.figure()
ax = fig.add_subplot(projection="polar")    # 指定子图为极坐标系
ax.spines["polar"].set_visible(False)       # 隐藏边框
ax.axes.xaxis.set_visible(False)            # 隐藏X轴
ax.axes.yaxis.set_visible(False)            # 隐藏Y轴
ax.set_ylim([0, 300])
x = [(i * np.pi * 2)/data.shape[0] for i in range(data.shape[0])]   # 计算X轴
color_list = plt.colormaps['inferno'].resampled(48).colors# 动画函数
def update(i):if i == 0: ax.cla()ax.set_ylim([0, 300])bar = ax.bar(x=x,height=data.iloc[:, i],bottom=0 if i == 0 else data.iloc[:, 0:i].sum(axis=1), width=np.pi/data.shape[0], color=color_list[i*16:(i+1)*16],edgecolor="k",linewidth=1,)if i == 2:for i in range(data.shape[0]):plt.text(x=x[i],                             # 文本的X坐标y=data.iloc[i].sum() * 1.05,        # 文本的Y坐标s=f"{data.iloc[i].name}: {data.iloc[i].sum()}",  # 文本内容rotation= (i * 360)/data.shape[0],  # 旋转角度rotation_mode="anchor",             # 指定旋转模式horizontalalignment="left",         # 水平对齐方式verticalalignment="center",         # 垂直对齐方式)return bar anim = animation.FuncAnimation(fig=fig,        # 指定画布对象func=update,    # 指定更新方法frames=3,       # 动画帧数,即画几次的意思,和一秒多少帧无关interval=720,   # 每次绘画的间隔时间,单位是毫秒)

动画
一层一层出现的方式有点奇怪,改成分柱子出现的比较好。即每次绘制一行数据柱,绘制16次。


fig = plt.figure()
ax = fig.add_subplot(projection="polar")    # 指定子图为极坐标系
ax.spines["polar"].set_visible(False)       # 隐藏边框
ax.axes.xaxis.set_visible(False)            # 隐藏X轴
ax.axes.yaxis.set_visible(False)            # 隐藏Y轴
ax.set_ylim([0, 300])
x = [(i * np.pi * 2)/data.shape[0] for i in range(data.shape[0])]   # 计算X轴
color_list = plt.colormaps['inferno'].resampled(48).colors
bar = ax.bar(x, [0]*len(x), width=np.pi/data.shape[0])[0]# 动画函数
def update(i):if i == 0: ax.cla()                # 清除画布ax.set_ylim([0, 300])   # 重新建立Y轴范围for j in range(3):          # 分成三层画柱子bar = ax.bar(x = x[i],           # X轴坐标height=data.iloc[i, j].sum(),   # 每层柱子的高度bottom=0 if j==0 else data.iloc[i, 0:j].sum(),  # 每层柱子的底width=np.pi/data.shape[0],      # 柱子宽度color=color_list[j*16 + i],     # 柱子颜色edgecolor="k",                  # 分隔线颜色linewidth=1,                    # 分隔线宽度)plt.text(x=x[i],                             # 标签的X轴坐标,与柱子相同y=data.iloc[i].sum()*1.05,          # 标签的Y轴坐标,比柱子略高一点,避免重叠s=f"{data.iloc[i].name}: {data.iloc[i].sum()}",     # 标签的文本内容rotation= (i * 360)/data.shape[0],  # 旋转角度rotation_mode="anchor",             # 指定旋转模式horizontalalignment="left",         # 水平对齐方式verticalalignment="center",         # 垂直对齐方式)return bar anim = animation.FuncAnimation(fig=fig, func=update, frames=16, interval=720)

分行绘制

结束

附件:完整代码

import pandas as pd
import matplotlib.pyplot as plt 
import matplotlib.animation as animation
import numpy as npplt.rcParams["font.sans-serif"] = ["SimHei"]    # 指定中文字体
plt.rcParams['axes.unicode_minus'] = False      # 指定负号等特殊符号兼容data = pd.DataFrame(np.random.randint(70, 100, size=(16, 3)),index=[i for i in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'][:16], columns=["l1", 'l2', 'l3'])fig = plt.figure()
ax = fig.add_subplot(projection="polar")    # 指定子图为极坐标系
ax.spines["polar"].set_visible(False)       # 隐藏边框
ax.axes.xaxis.set_visible(False)            # 隐藏X轴
ax.axes.yaxis.set_visible(False)            # 隐藏Y轴
ax.set_ylim([0, 300])
x = [(i * np.pi * 2)/data.shape[0] for i in range(data.shape[0])]   # 计算X轴
color_list = plt.colormaps['inferno'].resampled(48).colors
bar = ax.bar(x, [0]*len(x), width=np.pi/data.shape[0])[0]# 动画函数
def update(i):if i == 0: ax.cla()                # 清除画布ax.set_ylim([0, 300])   # 重新建立Y轴范围for j in range(3):          # 分成三层画柱子bar = ax.bar(x = x[i],           # X轴坐标height=data.iloc[i, j].sum(),   # 每层柱子的高度bottom=0 if j==0 else data.iloc[i, 0:j].sum(),  # 每层柱子的底width=np.pi/data.shape[0],      # 柱子宽度color=color_list[j*16 + i],     # 柱子颜色edgecolor="k",                  # 分隔线颜色linewidth=1,                    # 分隔线宽度)plt.text(x=x[i],                             # 标签的X轴坐标,与柱子相同y=data.iloc[i].sum()*1.05,          # 标签的Y轴坐标,比柱子略高一点,避免重叠s=f"{data.iloc[i].name}: {data.iloc[i].sum()}",     # 标签的文本内容rotation= (i * 360)/data.shape[0],  # 旋转角度rotation_mode="anchor",             # 指定旋转模式horizontalalignment="left",         # 水平对齐方式verticalalignment="center",         # 垂直对齐方式)return bar anim = animation.FuncAnimation(fig=fig, func=update, frames=16, interval=720)anim.save("./sample_02.gif")	# 保存到GIF文件

这篇关于【可视化】Python绘制风车玫瑰图动画的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/278345

相关文章

Conda与Python venv虚拟环境的区别与使用方法详解

《Conda与Pythonvenv虚拟环境的区别与使用方法详解》随着Python社区的成长,虚拟环境的概念和技术也在不断发展,:本文主要介绍Conda与Pythonvenv虚拟环境的区别与使用... 目录前言一、Conda 与 python venv 的核心区别1. Conda 的特点2. Python v

Python使用python-can实现合并BLF文件

《Python使用python-can实现合并BLF文件》python-can库是Python生态中专注于CAN总线通信与数据处理的强大工具,本文将使用python-can为BLF文件合并提供高效灵活... 目录一、python-can 库:CAN 数据处理的利器二、BLF 文件合并核心代码解析1. 基础合

Python使用OpenCV实现获取视频时长的小工具

《Python使用OpenCV实现获取视频时长的小工具》在处理视频数据时,获取视频的时长是一项常见且基础的需求,本文将详细介绍如何使用Python和OpenCV获取视频时长,并对每一行代码进行深入解析... 目录一、代码实现二、代码解析1. 导入 OpenCV 库2. 定义获取视频时长的函数3. 打开视频文

Python中你不知道的gzip高级用法分享

《Python中你不知道的gzip高级用法分享》在当今大数据时代,数据存储和传输成本已成为每个开发者必须考虑的问题,Python内置的gzip模块提供了一种简单高效的解决方案,下面小编就来和大家详细讲... 目录前言:为什么数据压缩如此重要1. gzip 模块基础介绍2. 基本压缩与解压缩操作2.1 压缩文

Python设置Cookie永不超时的详细指南

《Python设置Cookie永不超时的详细指南》Cookie是一种存储在用户浏览器中的小型数据片段,用于记录用户的登录状态、偏好设置等信息,下面小编就来和大家详细讲讲Python如何设置Cookie... 目录一、Cookie的作用与重要性二、Cookie过期的原因三、实现Cookie永不超时的方法(一)

Python内置函数之classmethod函数使用详解

《Python内置函数之classmethod函数使用详解》:本文主要介绍Python内置函数之classmethod函数使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 类方法定义与基本语法2. 类方法 vs 实例方法 vs 静态方法3. 核心特性与用法(1编程客

Python函数作用域示例详解

《Python函数作用域示例详解》本文介绍了Python中的LEGB作用域规则,详细解析了变量查找的四个层级,通过具体代码示例,展示了各层级的变量访问规则和特性,对python函数作用域相关知识感兴趣... 目录一、LEGB 规则二、作用域实例2.1 局部作用域(Local)2.2 闭包作用域(Enclos

Python实现对阿里云OSS对象存储的操作详解

《Python实现对阿里云OSS对象存储的操作详解》这篇文章主要为大家详细介绍了Python实现对阿里云OSS对象存储的操作相关知识,包括连接,上传,下载,列举等功能,感兴趣的小伙伴可以了解下... 目录一、直接使用代码二、详细使用1. 环境准备2. 初始化配置3. bucket配置创建4. 文件上传到os

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四