【可视化】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

相关文章

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

【WebGPU Unleashed】1.1 绘制三角形

一部2024新的WebGPU教程,作者Shi Yan。内容很好,翻译过来与大家共享,内容上会有改动,加上自己的理解。更多精彩内容尽在 dt.sim3d.cn ,关注公众号【sky的数孪技术】,技术交流、源码下载请添加微信号:digital_twin123 在 3D 渲染领域,三角形是最基本的绘制元素。在这里,我们将学习如何绘制单个三角形。接下来我们将制作一个简单的着色器来定义三角形内的像素

nudepy,一个有趣的 Python 库!

更多资料获取 📚 个人网站:ipengtao.com 大家好,今天为大家分享一个有趣的 Python 库 - nudepy。 Github地址:https://github.com/hhatto/nude.py 在图像处理和计算机视觉应用中,检测图像中的不适当内容(例如裸露图像)是一个重要的任务。nudepy 是一个基于 Python 的库,专门用于检测图像中的不适当内容。该

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip

HTML提交表单给python

python 代码 from flask import Flask, request, render_template, redirect, url_forapp = Flask(__name__)@app.route('/')def form():# 渲染表单页面return render_template('./index.html')@app.route('/submit_form',