python详解绘制风玫瑰图

绘制风玫瑰图

# 导入包
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
#-------设置支持中文----------------------#
import matplotlib as mpl
mpl.rcParams['font.sans-serif'] = ['SimHei']   #设置简黑字体
mpl.rcParams['axes.unicode_minus'] = False
#-------自定义坐标轴刻度格式----------------#
from matplotlib.ticker import FuncFormatter

创建数据集:

np.random.seed(0)
data = pd.DataFrame(np.random.randint(20, 300, (4, 16)), 
                    index=['0~0.2', '0.3~1.5', '1.6~3.3', '3.4~5.4'],
                    columns='N NNE NE ENE E ESE SE SSE S SSW SW WSW W WNW NW NNW'.split())
data   #风速选择了4段,每列数据表示各方向风速的频数统计值
NNNENEENEEESESESSESSSWSWWSWWWNWNWNNW
0~0.219267137212271215292312972621079010821359107
0.3~1.51941081854592285135263217119197263167167285205
1.6~3.31475251222264171183203481481487358264293125
3.4~5.46251277771392871021111197314110422328267147

绘制频数风速玫瑰图:

N = 16 # 风速分布为16个方向
theta = np.linspace(0, 2*np.pi, N, endpoint=False) # 获取16个方向的角度值
width = np.pi / N  # 绘制扇型的宽度,可以自行调整
labels = list(data.columns) # 自定义坐标标签为 N , NSN, ……
# 开始绘图
plt.figure(figsize=(8,8))
ax = plt.subplot(111, projection='polar')
for idx in data.index:
    # 每一行绘制一个扇形
    radii = data.loc[idx] # 每一行数据
    ax.bar(theta, radii, width=width, bottom=0.0, label=idx, tick_label=labels)
plt.title('风玫瑰图示意图')
plt.legend(loc=4, bbox_to_anchor=(1.15, -0.07)) # 将label显示出来, 并调整位置
plt.show()

image
已经可以初步满足需求了,不过我们发现N位置差了90°,而且为了满足‘上北下南左西右东’的习惯,需要逆时针方向绘制。所以做两个修改:

N = 16 # 风速分布为16个方向
theta = np.linspace(0, 2*np.pi, N, endpoint=False) # 获取16个方向的角度值
width = np.pi / N  # 绘制扇型的宽度,可以自行调整
labels = list(data.columns) # 自定义坐标标签为 N , NSN, ……
# 开始绘图
plt.figure(figsize=(8,8))
ax = plt.subplot(111, projection='polar')
for idx in data.index:
    # 每一行绘制一个扇形
    radii = data.loc[idx] # 每一行数据
    ax.bar(theta, radii, width=width, bottom=0.0, label=idx, tick_label=labels)
#------------------------------------#
ax.set_theta_zero_location('N') #设置零度方向北
ax.set_theta_direction(-1)    # 逆时针方向绘图
plt.title('风玫瑰图示意图')
plt.legend(loc=4, bbox_to_anchor=(1.15, -0.07)) # 将label显示出来, 并调整位置
plt.show()

image
这样基本满足我们的需求了,但是有的时候我们需要绘制百分比玫瑰图,下面我们讨论如何制作百分比风玫瑰图:

数据转化为百分比

_sum = data.apply(np.sum)
data = data / _sum
data
NNNENEENEEESESESSESSSWSWWSWWWNWNWNNW
0~0.20.3226890.2410070.2107690.3812950.3537860.2244260.0645880.2858910.4361230.4352160.1804380.1698110.1942450.2300220.0838070.183219
0.3~1.50.3260500.3884890.2846150.0809350.1201040.2974950.3006680.3254950.3186490.1976740.3322090.4962260.3003600.1803460.4048300.351027
1.6~3.30.2470590.1870500.0784620.3992810.3446480.1784970.4075720.2512380.0704850.2458470.2495780.1377360.1043170.2850970.4161930.214041
3.4~5.40.1042020.1834530.4261540.1384890.1814620.2995820.2271710.1373760.1747430.1212620.2377740.1962260.4010790.3045360.0951700.251712

数据转化成功,我们绘制图片唯一要修改的就是yaxis(本质是bar图,我们仔细观察上面的图不难发现原因)刻度的表达,将其转化为百分比:

N = 16 # 风速分布为16个方向
theta = np.linspace(0, 2*np.pi, N, endpoint=False) # 获取16个方向的角度值
width = np.pi / N  # 绘制扇型的宽度,可以自行调整
labels = list(data.columns) # 自定义坐标标签为 N , NSN, ……
# 开始绘图
plt.figure(figsize=(8,8))
ax = plt.subplot(111, projection='polar')
for idx in data.index:
    # 每一行绘制一个扇形
    radii = data.loc[idx] # 每一行数据
    ax.bar(theta, radii, width=width, bottom=0.0, label=idx, tick_label=labels)
#------------------------------------#
ax.set_theta_zero_location('N') #设置零度方向北
ax.set_theta_direction(-1)    # 逆时针方向绘图
#--------自定义yaxis的刻度格式-----------#
plt.gca().yaxis.set_major_formatter(FuncFormatter(lambda s, position: '{:.0f}%'.format(100*s)))
plt.title('风玫瑰图示意图')
plt.legend(loc=4, bbox_to_anchor=(1.15, -0.07)) # 将label显示出来, 并调整位置
plt.show()

数据遮挡处理

细心的同学可能已经发现,由于数据绘制是按行来绘制的,那么如果先绘制的数据值比较小的话,会被后面的数据遮挡。以风频数据为例,处理思路如下:

  1. 按列绘制数据(先处理同一风向的数据)
  2. 每列中先将数值较大的绘制出来-解决遮挡的问题
  3. 最后一列处理下图例

此外加入了自定义颜色(根据风向分类条数自定义

  • 完整代码如下
# 导入包
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
#-------设置支持中文----------------------#
import matplotlib as mpl

mpl.rcParams['font.sans-serif'] = ['PingFang HK'] #设置字体
mpl.rcParams['axes.unicode_minus'] = False


def plot_bar(ax, theta, values, width, colors):
    plt_data = [(i, values[i]) for i in range(len(values))]
    for idx, data in sorted(plt_data, key=lambda s: s[-1], reverse=True):
        ax.bar(theta, data, width, color=colors[idx])


def lengend_plot(ax, theta, values, width, colors):
    plt_data = [(i, values[i]) for i in range(len(values))]
    for idx, data in sorted(plt_data, key=lambda s: s[-1], reverse=True):
        p = ax.bar(theta, data, width, color=colors[idx])
        yield (idx, p)


if __name__ == "__main__":
    # 生成数据
    np.random.seed(0)
    data = pd.DataFrame(np.random.randint(20, 300, (4, 8)),
                        index=['0~0.2', '0.3~1.5', '1.6~3.3', '3.4~5.4'],
                        columns='N NE E SE S SW W NW'.split())

    N = data.shape[-1] # 由数据模板获取风向类别
    theta = np.linspace(0, 2 * np.pi, N, endpoint=False) # 获取16个方向的角度值
    width = np.pi / N # 绘制扇型的宽度,可以自行调整
    labels = data.columns.to_list() # 获取标签
    colors = ['#060080', '#00D5FF', '#FFE600',
              '#800000'] # 自定义颜色,颜色的尺寸必须与数据的分类条数一致

    # 开始绘图
    plt.figure(figsize=(10, 8))
    ax = plt.subplot(111, projection='polar')

    for i in range(N - 1):
        values = data.iloc[:, i].to_list()
        plot_bar(ax, theta[i], values, width, colors)

    handles = lengend_plot(ax, theta[N - 1], data.iloc[:, N - 1], width,
                           colors)
    handles = [p for idx, p in sorted(handles, key=lambda s: s[0])]

    ax.set_xticks(theta) # 自定义坐标轴标签
    ax.set_xticklabels(labels)
    ax.set_theta_zero_location('N') #设置零度方向北
    ax.set_theta_direction(-1) # 逆时针方向绘图
    plt.title('风玫瑰图示意图')
    plt.legend(handles, data.index, loc=4,
               bbox_to_anchor=(1.15, -0.07)) # 将label显示出来, 并调整位置
    plt.show()
  • 绘制结果
    在这里插入图片描述
    如果喜欢的话欢迎👍点赞评论,⭐️github地址欢迎star、flow!
  • 31
    点赞
  • 199
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值