绘制风玫瑰图
# 导入包
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段,每列数据表示各方向风速的频数统计值
N | NNE | NE | ENE | E | ESE | SE | SSE | S | SSW | SW | WSW | W | WNW | NW | NNW | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0~0.2 | 192 | 67 | 137 | 212 | 271 | 215 | 29 | 231 | 297 | 262 | 107 | 90 | 108 | 213 | 59 | 107 |
0.3~1.5 | 194 | 108 | 185 | 45 | 92 | 285 | 135 | 263 | 217 | 119 | 197 | 263 | 167 | 167 | 285 | 205 |
1.6~3.3 | 147 | 52 | 51 | 222 | 264 | 171 | 183 | 203 | 48 | 148 | 148 | 73 | 58 | 264 | 293 | 125 |
3.4~5.4 | 62 | 51 | 277 | 77 | 139 | 287 | 102 | 111 | 119 | 73 | 141 | 104 | 223 | 282 | 67 | 147 |
绘制频数风速玫瑰图:
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()
已经可以初步满足需求了,不过我们发现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()
这样基本满足我们的需求了,但是有的时候我们需要绘制百分比玫瑰图,下面我们讨论如何制作百分比风玫瑰图:
数据转化为百分比
_sum = data.apply(np.sum)
data = data / _sum
data
N | NNE | NE | ENE | E | ESE | SE | SSE | S | SSW | SW | WSW | W | WNW | NW | NNW | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0~0.2 | 0.322689 | 0.241007 | 0.210769 | 0.381295 | 0.353786 | 0.224426 | 0.064588 | 0.285891 | 0.436123 | 0.435216 | 0.180438 | 0.169811 | 0.194245 | 0.230022 | 0.083807 | 0.183219 |
0.3~1.5 | 0.326050 | 0.388489 | 0.284615 | 0.080935 | 0.120104 | 0.297495 | 0.300668 | 0.325495 | 0.318649 | 0.197674 | 0.332209 | 0.496226 | 0.300360 | 0.180346 | 0.404830 | 0.351027 |
1.6~3.3 | 0.247059 | 0.187050 | 0.078462 | 0.399281 | 0.344648 | 0.178497 | 0.407572 | 0.251238 | 0.070485 | 0.245847 | 0.249578 | 0.137736 | 0.104317 | 0.285097 | 0.416193 | 0.214041 |
3.4~5.4 | 0.104202 | 0.183453 | 0.426154 | 0.138489 | 0.181462 | 0.299582 | 0.227171 | 0.137376 | 0.174743 | 0.121262 | 0.237774 | 0.196226 | 0.401079 | 0.304536 | 0.095170 | 0.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()
数据遮挡处理
细心的同学可能已经发现,由于数据绘制是按行来绘制的,那么如果先绘制的数据值比较小的话,会被后面的数据遮挡。以风频数据为例,处理思路如下:
- 按列绘制数据(先处理同一风向的数据)
- 每列中先将数值较大的绘制出来-解决遮挡的问题
- 最后一列处理下图例
此外加入了自定义颜色(根据风向分类条数自定义)
- 完整代码如下
# 导入包
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!