【可视化】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绘制风车玫瑰图动画的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/auzurdragon/article/details/133386667
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/278345

相关文章

Python中常用的四种取整方式分享

《Python中常用的四种取整方式分享》在数据处理和数值计算中,取整操作是非常常见的需求,Python提供了多种取整方式,本文为大家整理了四种常用的方法,希望对大家有所帮助... 目录引言向零取整(Truncate)向下取整(Floor)向上取整(Ceil)四舍五入(Round)四种取整方式的对比综合示例应

python 3.8 的anaconda下载方法

《python3.8的anaconda下载方法》本文详细介绍了如何下载和安装带有Python3.8的Anaconda发行版,包括Anaconda简介、下载步骤、安装指南以及验证安装结果,此外,还介... 目录python3.8 版本的 Anaconda 下载与安装指南一、Anaconda 简介二、下载 An

Python自动化处理手机验证码

《Python自动化处理手机验证码》手机验证码是一种常见的身份验证手段,广泛应用于用户注册、登录、交易确认等场景,下面我们来看看如何使用Python自动化处理手机验证码吧... 目录一、获取手机验证码1.1 通过短信接收验证码1.2 使用第三方短信接收服务1.3 使用ADB读取手机短信1.4 通过API获取

python安装whl包并解决依赖关系的实现

《python安装whl包并解决依赖关系的实现》本文主要介绍了python安装whl包并解决依赖关系的实现,文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录一、什么是whl文件?二、我们为什么需要使用whl文件来安装python库?三、我们应该去哪儿下

Python脚本实现图片文件批量命名

《Python脚本实现图片文件批量命名》这篇文章主要为大家详细介绍了一个用python第三方库pillow写的批量处理图片命名的脚本,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录前言源码批量处理图片尺寸脚本源码GUI界面源码打包成.exe可执行文件前言本文介绍一个用python第三方库pi

Python中多线程和多进程的基本用法详解

《Python中多线程和多进程的基本用法详解》这篇文章介绍了Python中多线程和多进程的相关知识,包括并发编程的优势,多线程和多进程的概念、适用场景、示例代码,线程池和进程池的使用,以及如何选择合适... 目录引言一、并发编程的主要优势二、python的多线程(Threading)1. 什么是多线程?2.

Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)

《Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)》本文介绍了如何使用Python和Selenium结合ddddocr库实现图片验证码的识别和点击功能,感兴趣的朋友一起看... 目录1.获取图片2.目标识别3.背景坐标识别3.1 ddddocr3.2 打码平台4.坐标点击5.图

Python自动化Office文档处理全攻略

《Python自动化Office文档处理全攻略》在日常办公中,处理Word、Excel和PDF等Office文档是再常见不过的任务,手动操作这些文档不仅耗时耗力,还容易出错,幸运的是,Python提供... 目录一、自动化处理Word文档1. 安装python-docx库2. 读取Word文档内容3. 修改

Python重命名文件并移动到对应文件夹

《Python重命名文件并移动到对应文件夹》在日常的文件管理和处理过程中,我们可能会遇到需要将文件整理到不同文件夹中的需求,下面我们就来看看如何使用Python实现重命名文件并移动到对应文件夹吧... 目录检查并删除空文件夹1. 基本需求2. 实现代码解析3. 代码解释4. 代码执行结果5. 总结方法补充在

Python自动化办公之合并多个Excel

《Python自动化办公之合并多个Excel》在日常的办公自动化工作中,尤其是处理大量数据时,合并多个Excel表格是一个常见且繁琐的任务,下面小编就来为大家介绍一下如何使用Python轻松实现合... 目录为什么选择 python 自动化目标使用 Python 合并多个 Excel 文件安装所需库示例代码