当前位置: 首页 > news >正文

[特殊字符]️ 用 Python 绘制专业风玫瑰图:从气象数据到可视化的全流程指南

在数据可视化的大家族里,有一类图表宛如绽放的玫瑰,用独特的极坐标形态展示方向与数量的分布 —— 这就是风玫瑰图。它不仅是气象学中展示风向风速的经典工具,还广泛应用于城市规划、风能工程、航海航空等领域。今天,我们就来解锁如何用 Python 绘制精美专业的风玫瑰图,让你的数据以最优雅的方式 “旋转绽放”~ 🌹

📊 风玫瑰图:为什么它是方向数据的最佳拍档?

🌍 什么是风玫瑰图?

风玫瑰图(Wind Rose)本质是一种极坐标直方图:

  • 角度维度:表示方向(如 0° 为北,90° 为东)
  • 径向维度:表示该方向上数据的频率或强度
  • 颜色 / 分层:可额外展示第三维度(如风速分段)

🌟 核心优势:

  • 方向分布直观化:一眼看出主导风向、风向频率对称性等特征
  • 多维度信息整合:同时展示方向、强度(风速)、频率三层信息
  • 专业领域必备:气象报告、建筑设计(避开主导风向)、风能选址(寻找高风速区域)等场景的标准图表

🌰 应用场景举例:

  • 气象站年度风向统计:帮助机场规划跑道方向
  • 城市建筑群设计:确保通风流畅,避免污染堆积
  • 风力发电机选址:识别高风速且风向稳定的区域
  • 海洋学研究:分析洋流方向与强度分布

🛠️ 代码实现:从 0 到 1 构建风玫瑰图

先奉上完整代码,我们将像拆解精密仪器一样解析每个模块:

import numpy as npimport matplotlib.pyplot as plt# 设置全局字体为宋体,并正确显示负号plt.rcParams['font.sans-serif'] = ['SimSun']plt.rcParams['axes.unicode_minus'] = False# 1. 随机生成数据n = 10000wind_directions = np.random.rand(n) * 360 # 0-360°wind_speeds = np.random.rand(n) * 20 # 0-20 m/s# 2. 定义风向和风速分箱dir_bins = np.arange(0, 361, 30) # 每30°一个扇区speed_bins = [0, 3, 6, 9, 12, 15, 18, np.inf] # 风速分段# 3. 对数据进行分箱计数dir_idx = np.digitize(wind_directions, dir_bins) - 1speed_idx = np.digitize(wind_speeds, speed_bins) - 1num_dirs = len(dir_bins) - 1num_speeds = len(speed_bins)counts = np.zeros((num_speeds, num_dirs))for d, s in zip(dir_idx, speed_idx):if 0 <= d < num_dirs:counts[s, d] += 1# 4. 计算频率(百分比)frequencies = counts / n * 100# 5. 准备极坐标角度angles = np.deg2rad(dir_bins[:-1] + 15) # 扇区中心角# 6. 绘制风玫瑰图fig, ax = plt.subplots(subplot_kw={'projection': 'polar'}, figsize=(8, 6))bottom = np.zeros(num_dirs)for i in range(num_speeds):# 构造带上下标和单位的图例标签if i < num_speeds - 1:label = rf'{speed_bins[i]} ≤ $W_s$ < {speed_bins[i+1]} m·s$^{{-1}}$'else:label = rf'$W_s$ ≥ {speed_bins[-2]} m·s$^{{-1}}$'ax.bar(angles, frequencies[i], width=np.deg2rad(30), bottom=bottom, edgecolor='k', label=label)bottom += frequencies[i]# 设置方向和标题ax.set_theta_zero_location('N')ax.set_theta_direction(-1)ax.set_xticks(np.deg2rad([0, 45, 90, 135, 180, 225, 270, 315]))ax.set_xticklabels(['N (0°)', 'NE (45°)', 'SE (135°)', 'S (180°)', 'SW (225°)', 'W (270°)', 'NW (315°)'], fontsize=10)ax.set_title('风玫瑰图\n(随机数据示例)', fontsize=14, pad=20)# 设置径向网格标签格式为“频率 %”ax.yaxis.set_major_formatter(lambda x, pos: f'{x:.1f}%')# 自动寻找最佳位置显示图例,分两列legend = ax.legend(loc='best', fontsize=10, title='风速 (m·s⁻¹)', ncol=2)plt.setp(legend.get_title(), fontsize=11)plt.tight_layout()plt.show()

 

🔍 代码逐模块解析:玫瑰绽放的秘密

🔧 预处理模块:让中文和符号完美显示
plt.rcParams['font.sans-serif'] = ['SimSun']plt.rcParams['axes.unicode_minus'] = False
  • 为什么重要?

风玫瑰图常需标注中文方向(如 “北”“东”)和专业符号(如 m・s⁻¹)。SimSun(宋体)是中文环境下的标准字体,axes.unicode_minus避免负号显示为方块。

  • 可选优化:

若需更现代的字体,可改为['Microsoft YaHei'](微软雅黑),或['SimHei'](黑体)。

📊 数据模块:核心替换点与分箱逻辑
# 1. 随机生成数据(用户需替换为实际数据)n = 10000wind_directions = np.random.rand(n) * 360 # 0-360°wind_speeds = np.random.rand(n) * 20 # 0-20 m/s
  • ✅ 替换自己数据的关键位置!

这部分生成的是模拟数据,实际使用时需替换为真实风向风速数据,方法如下:

    • 从 CSV 文件读取:
data = np.loadtxt('wind_data.csv', delimiter=',') # 假设文件格式为[风向, 风速]wind_directions = data[:, 0]wind_speeds = data[:, 1]
    • 从 pandas DataFrame 获取:
import pandas as pddf = pd.read_excel('气象数据.xlsx')wind_directions = df['风向(°)'].valueswind_speeds = df['风速(m/s)'].values
    • 手动输入小规模数据:

wind_directions = np.array([225, 180, 315, 45, 90, ...]) # 风向数组wind_speeds = np.array([5.2, 3.7, 8.1, 2.3, 6.5, ...]) # 对应风速数组# 2. 定义风向和风速分箱(可根据需求调整)dir_bins = np.arange(0, 361, 30) # 每30°一个扇区speed_bins = [0, 3, 6, 9, 12, 15, 18, np.inf] # 风速分段
  • 分箱策略解析:
    • dir_bins将 360° 划分为 12 个扇区(30°/ 扇区),符合气象学标准分法;若需更精细,可设为 22.5°(16 个扇区)。
    • speed_bins按风速强度分段,单位为 m/s,分段点可根据数据特征调整:
      • 微风:0-3m/s,轻风:3-6m/s,中风:6-9m/s...
      • 若数据风速普遍较高,可改为 [0, 5, 10, 15, 20, np.inf]
🧮 统计模块:从原始数据到频率矩阵

# 3. 对数据进行分箱计数dir_idx = np.digitize(wind_directions, dir_bins) - 1speed_idx = np.digitize(wind_speeds, speed_bins) - 1num_dirs = len(dir_bins) - 1num_speeds = len(speed_bins)counts = np.zeros((num_speeds, num_dirs))for d, s in zip(dir_idx, speed_idx):if 0 <= d < num_dirs:counts[s, d] += 1# 4. 计算频率(百分比)frequencies = counts / n * 100
  • 核心统计逻辑:
    • np.digitize将每个风向 / 风速数据映射到对应分箱的索引
    • counts矩阵记录每个风速分段在各风向扇区的出现次数
    • frequencies转换为百分比频率,便于直观比较
🎨 绘制模块:极坐标下的玫瑰绽放

# 5. 准备极坐标角度angles = np.deg2rad(dir_bins[:-1] + 15) # 扇区中心角(如30°扇区的中心为15°)
  • 角度处理技巧:

dir_bins[:-1] + 15计算每个扇区的中心角度(如 0-30° 扇区的中心为 15°),np.deg2rad转换为弧度制(极坐标所需单位)。


# 6. 绘制风玫瑰图fig, ax = plt.subplots(subplot_kw={'projection': 'polar'}, figsize=(8, 6))bottom = np.zeros(num_dirs)for i in range(num_speeds):# 构造带上下标和单位的图例标签if i < num_speeds - 1:label = rf'{speed_bins[i]} ≤ $W_s$ < {speed_bins[i+1]} m·s$^{{-1}}$'else:label = rf'$W_s$ ≥ {speed_bins[-2]} m·s$^{{-1}}$'ax.bar(angles, frequencies[i], width=np.deg2rad(30), bottom=bottom, edgecolor='k', label=label)bottom += frequencies[i]
  • 3D 堆叠条形的极坐标实现:
    • projection='polar'激活极坐标模式,ax.bar在此模式下绘制扇形区域
    • width=np.deg2rad(30)设置每个扇区的角度宽度(30°)
    • bottom=bottom实现堆叠效果,每层条形基于下层顶部绘制
    • label使用 LaTeX 语法渲染专业公式(如 m・s⁻¹),rf''确保特殊字符正确显示
🌌 美化模块:专业图表的细节把控

# 设置方向和标题ax.set_theta_zero_location('N') # 0°指向北ax.set_theta_direction(-1) # 角度顺时针增加(符合气象学惯例)ax.set_xticks(np.deg2rad([0, 45, 90, 135, 180, 225, 270, 315]))ax.set_xticklabels(['N (0°)', 'NE (45°)', 'E (90°)', 'SE (135°)', 'S (180°)', 'SW (225°)', 'W (270°)', 'NW (315°)'], fontsize=10)# 设置径向网格标签格式ax.yaxis.set_major_formatter(lambda x, pos: f'{x:.1f}%')# 优化图例legend = ax.legend(loc='best', fontsize=10, title='风速 (m·s⁻¹)', ncol=2)
  • 方向设置的专业细节:
    • set_theta_zero_location('N')将 0° 设为正北方向(符合气象学标准)
    • set_theta_direction(-1)使角度顺时针增加(传统风向图方向)
  • 标签优化:

径向网格显示为百分比(x:.1f%),图例分两列(ncol=2)避免过长,标题使用专业单位(m・s⁻¹)

🎨 高级定制:让玫瑰图独一无二

🌈 颜色方案优化

默认颜色可能不够专业,可自定义为气象学常用配色:


# 定义风速分段颜色(从低到高渐变色)speed_colors = ['#a6cee3', '#1f78b4', '#b2df8a', '#33a02c', '#fb9a99', '#e31a1c', '#fdbf6f']for i in range(num_speeds):ax.bar(..., color=speed_colors[i], ...)

📍 自定义方向标签

若需更详细的方向标注:


# 16方向标注(气象学标准)directions = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE','S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW']angles_16 = np.deg2rad(np.arange(0, 360, 22.5)) # 22.5°间隔ax.set_xticks(angles_16)ax.set_xticklabels(directions, fontsize=8)

💡 透明度与阴影效果

增强多层堆叠的视觉层次感:


ax.bar(..., alpha=0.8, zorder=3) # 增加透明度,zorder控制图层顺序ax.grid(True, linestyle='--', alpha=0.5) # 添加半透明网格线

📊 添加数据标签

在每个扇形上显示频率数值:


for i in range(num_speeds):for d in range(num_dirs):if frequencies[i, d] > 0: # 只标注非零值ax.text(angles[d], bottom[d] - frequencies[i, d]/2, # 垂直居中f'{frequencies[i, d]:.1f}%',ha='center', va='center', fontsize=7)

🌍 实战案例:风玫瑰图的真实应用

🌉 案例 1:城市建筑规划

某沿海城市规划新区,需分析主导风向以优化建筑布局:


# 读取实际气象站数据(假设数据已按[风向, 风速]存储)real_data = np.loadtxt('coastal_wind_data.csv', delimiter=',')wind_directions = real_data[:, 0]wind_speeds = real_data[:, 1]# 调整分箱策略,关注海风特征dir_bins = np.arange(0, 361, 22.5) # 16方向更精细speed_bins = [0, 2, 4, 6, 8, 10, 12, np.inf] # 针对沿海微风较多的特点

成果: 风玫瑰图显示夏季主导东南风(SE),冬季主导西北风(NW),建筑布局应沿主导风向排列,确保通风走廊。

🏭 案例 2:风力发电场选址

某能源公司评估风能资源,需识别高风速且风向稳定区域:


# 计算各方向风速加权平均值(更关注风速强度)weighted_speeds = np.zeros(num_dirs)dir_counts = np.zeros(num_dirs)for d, dir in enumerate(dir_idx):if 0 <= dir < num_dirs:weighted_speeds[dir] += wind_speeds[d]dir_counts[dir] += 1avg_speeds = weighted_speeds / dir_counts # 各方向平均风速# 绘制风速玫瑰图(径向为平均风速,非频率)ax.bar(angles, avg_speeds, width=np.deg2rad(30), edgecolor='k', color='skyblue')

成果: 发现某方向平均风速达 8.5m/s 且频率占比 25%,是理想的风机安装方向。

🚢 案例 3:航海路线规划

某航运公司优化跨洋航线,需避开强风区域:


# 定义强风阈值(如≥12m/s为强风)strong_wind = wind_speeds >= 12strong_dirs = wind_directions[strong_wind]# 单独绘制强风方向玫瑰图strong_dir_bins = np.arange(0, 361, 45) # 更大扇区突出主导方向strong_dir_idx = np.digitize(strong_dirs, strong_dir_bins) - 1strong_counts = np.bincount(strong_dir_idx, minlength=len(strong_dir_bins)-1)strong_freq = strong_counts / len(strong_dirs) * 100

成果: 显示某航线 6-8 月西南方向强风频率达 35%,建议调整航线避开该方向。

⚠️ 避坑指南:风玫瑰图绘制常见问题

1. 数据量不足导致统计偏差

  • 现象: 样本数 n<100 时,玫瑰图形态波动大,缺乏代表性。
  • 解决方案:
    • 确保 n≥500,理想 n≥1000
    • 对短周期数据(如月度),可合并多年数据
    • 使用平滑处理:scipy.ndimage.gaussian_filter(frequencies, sigma=0.5)

2. 分箱策略不合理

  • 现象: 风向扇区过多导致图表杂乱,或风速分段不均掩盖数据特征。
  • 解决方案:
    • 常规场景使用 12 扇区(30°)或 16 扇区(22.5°)
    • 风速分段建议 5-8 段,每段区间等距或按风速等级(如蒲福风级)
    • 可通过plt.hist(wind_speeds, bins=10)先观察风速分布再确定分段

3. 极坐标视角误导

  • 现象: 径向刻度非线性导致视觉偏差(如外圈区域实际面积更大)。
  • 解决方案:
    • 明确标注径向刻度(如每 10% 一格)
    • 避免使用 3D 透视效果(ax.set_box_aspect([1,1,1]保持等比例)
    • 对重要数据添加数值标签(见前文代码)

4. 中文显示异常

  • 现象: 方向标签显示为方块或乱码。
  • 解决方案:
    • 确认字体文件存在:fc-list | grep SimSun(Linux/Mac)
    • Windows 系统可指定['Microsoft YaHei']
    • Jupyter Notebook 中可添加%matplotlib inline后重启内核

🌟 进阶玩法:让风玫瑰图动起来

🎥 动态时间序列风玫瑰

绘制不同季节的风玫瑰图动画:


from matplotlib.animation import FuncAnimation# 假设data按季节分组:[春季, 夏季, 秋季, 冬季]season_data = [spring_data, summer_data, autumn_data, winter_data]season_names = ['春季', '夏季', '秋季', '冬季']def update(season_idx):ax.clear()# 绘制当前季节风玫瑰图(代码略,同主流程)ax.set_title(f'风玫瑰图 - {season_names[season_idx]}', fontsize=14)return ax,ani = FuncAnimation(fig, update, frames=4, interval=1000, repeat=True)ani.save('season_wind_rose.gif', writer='pillow', dpi=100)

📊 与其他图表联动

创建风玫瑰图与风速分布图的组合仪表盘:


fig, (ax_polar, ax_hist) = plt.subplots(1, 2, figsize=(12, 6),subplot_kw={'projection': 'polar', 'aspect': 'auto'})# 左侧绘制风玫瑰图# 右侧绘制风速直方图ax_hist.hist(wind_speeds, bins=speed_bins, color='skyblue', edgecolor='k')ax_hist.set_xlabel('风速 (m/s)')ax_hist.set_ylabel('频次')

📈 风向风速相关性分析

在玫瑰图上叠加风速均值等值线:


# 计算各方向平均风速avg_speed_per_dir = np.zeros(num_dirs)dir_counts = np.zeros(num_dirs)for d, s in zip(dir_idx, speed_idx):if 0 <= d < num_dirs:dir_counts[d] += 1avg_speed_per_dir[d] += speed_bins[s] # 用分段中值代表该段风速avg_speed_per_dir /= dir_counts# 绘制风速等值线theta = np.linspace(0, 2*np.pi, 100)r = np.interp(theta, angles, avg_speed_per_dir)ax.plot(theta, r, 'r--', linewidth=2, label='平均风速')ax.legend()

🌼 结语:让数据在极坐标中绽放

风玫瑰图不仅是一种可视化工具,更是理解方向数据的思维框架 —— 它将抽象的角度与数值转化为具象的玫瑰形态,让隐藏在数据中的方向规律一目了然。通过今天的教程,你已经掌握了从数据处理到图表美化的全流程,现在只差替换上你自己的专业数据啦!

关键替换步骤回顾:

  1. 替换wind_directions和wind_speeds为实际风向风速数据
  1. 根据数据特征调整dir_bins和speed_bins分箱策略
  1. 修改坐标轴标签和标题,匹配实际应用场景

无论是气象分析、工程设计还是科研研究,风玫瑰图都能为你的数据展示增添专业色彩~🌪️🌹

如果在实践中遇到问题,或者想分享你的创意风玫瑰图案例,欢迎在评论区留言!让我们一起用代码绘制数据世界的美丽玫瑰~😊

http://www.xdnf.cn/news/1070461.html

相关文章:

  • vscode ssh远程连接到Linux并实现免密码登录
  • Apipost和Postman对比
  • 缓存与加速技术实践-MongoDB数据库应用
  • 【RESTful接口设计规范全解析】URL路径设计 + 动词名词区分 + 状态码 + 返回值结构 + 最佳实践 + 新手常见误区汇总
  • Python打卡:Day37
  • 算法打卡 day4
  • Spring Boot 项目中同时使用 Swagger 和 Javadoc 的完整指南
  • Selenium+Pytest自动化测试框架实战
  • 快速傅里叶变换(FFT)是什么?
  • uniapp微信小程序:editor组件placeholder字体样式修改
  • GC 学习笔记
  • 新手向:Neo4j的安装与使用
  • ubuntu22.04系统kubeadm部署k8s高可用集群
  • Redis核心知识详解:从全局命令到高级数据结构
  • 多相机人脸扫描设备如何助力高效打造数字教育孪生体?
  • 第一章-人工智能概述-机器学习基础与应用(1/36)
  • 地震资料处理——(七)地震偏移处理
  • spring-ai 1.0.0 (1)模型调用能力
  • Linux命令与脚本:高效系统管理的双刃剑
  • 自动化测试--app自动化测试之给手机设置锁屏图案
  • 【stm32】HAL库开发——CubeMX配置外部中断和配置PWM
  • 多租户多会话隔离存储架构的完整实现方案
  • Linux命令:内置命令与外部命令的本质区别
  • 高中成绩可视化平台开发笔记
  • 时间同步 gptp ptp
  • 推荐一个前端基于vue3.x,vite7.x,后端基于springboot3.4.x的完全开源的前后端分离的中后台管理系统基础项目(纯净版)
  • 操作系统面试知识点(1):操作系统基础
  • 解锁AI无限潜能!景联文科技数据产品矩阵再升级:多语言题库、海量语料、垂域代码库,全面赋能大模型训练
  • Pydantic 模型
  • vscode运行c++文件和插件的方法