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

plt.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 np

plt.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文件
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 要绘制Python二手房数据的户型分布,我们可以使用Matplotlib和Seaborn这两个Python的数据可库来实现。 首先,我们需要导入需要的库,包括Pandas用于数据处理,Matplotlib和Seaborn用于数据可: ``` import pandas as pd import matplotlib.pyplot as plt import seaborn as sns ``` 接下来,我们可以加载二手房数据集,并筛选出户型的列: ``` data = pd.read_csv('二手房数据.csv') 户型 = data['户型'] ``` 为了绘制户型分布,我们可以使用Seaborn中的countplot函数: ``` sns.countplot(x=户型) plt.xlabel('户型') plt.ylabel('数量') plt.title('二手房户型分布') plt.xticks(rotation=45) plt.show() ``` 这段代码将根据户型列的取值数量绘制一个柱状,横坐标为户型的值,纵坐标为数量。我们还可以设置横坐标标签、纵坐标标签和表标题,使其更加清晰易懂。为了防止横坐标标签过于拥挤,我们可以通过`rotation`参数来旋转横坐标标签。 最后,我们通过`plt.show()`来显示表。 综上所述,以上代码可以使用Python绘制二手房数据的户型分布。 ### 回答2: Python是一种强大的编程语言,它支持各种数据处理和可操作。用Python进行二手房数据可绘制绘制户型分布非常简单。 首先,你需要获取二手房数据,并将其储存在一个适当的数据结构中,比如列表或者Pandas的DataFrame。每个数据项应该包含有关房屋的各种信息,例如户型、面积、价格等。 接下来,你可以使用Python中的各种数据可库来绘制户型分布。其中最常用的库之一是Matplotlib,它提供了各种绘功能,包括直方、柱状和饼等。 如果你想要绘制户型的频率分布,可以使用Matplotlib的柱状功能。你可以将不同户型作为X轴,每个户型在数据中的数量作为Y轴。通过将数据传递给matplotlib.pyplot的bar函数,你可以生成一个美观的柱状。你还可以添加标签和标题来增加表的可读性。 另外,如果你想要绘制户型的相对比例,可以使用Matplotlib的饼功能。你可以计算每个户型在数据中的占比,并将其作为数据传递给matplotlib.pyplot的pie函数。你还可以添加例和标签来更好地展示不同户型的比例关系。 除了Matplotlib,还有其他一些Python库也可以用于二手房数据的可,例如Seaborn和Plotly。这些库提供了更多的可选项和样式,可以让你创建出更丰富和交互式的表。 总而言之,使用Python进行二手房数据可,并绘制户型分布是相对简单的。只需几行代码,你就可以创建出漂亮和易于理解的形,从中获取对二手房市场的洞察。 ### 回答3: 要实现python二手房数据的可绘制户型分布,可以采用以下步骤: 1. 数据收集:首先,需要收集二手房的相关数据,包括房屋的户型信息。可以通过爬取房屋交易网站或者使用开放的二手房数据API来获取数据。 2. 数据处理:将获取到的数据进行处理,提取出户型的信息,并进行统计。 3. 数据可:使用Python的数据可库,例如Matplotlib或seaborn,绘制户型分布。可以选择柱状、饼等方式来展示不同户型的数量分布。 4. 表设计:设置表的标题、坐标轴标签以及例等信息,使得表更加清晰易懂。 5. 表展示:将生成的户型分布保存为片或者直接在程序中展示出来。 6. 结果分析:根据生成的户型分布,可以观察不同户型的数量占比,并分析市场上各种户型的热门程度。 通过以上步骤,可以利用Python实现二手房数据的可绘制户型分布,帮助我们更加直观地了解二手房市场的房型结构,并为房屋购买决策提供参考。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值