python 箱线图叠加折线图

image

 

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import numpy as np
from datetime import timedelta

# 设置全局英文字体 - 解决字体警告问题
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = ['DejaVu Sans', 'Arial', 'Helvetica', 'Verdana']
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

# 读取Excel文件
df = pd.read_excel('/root/suyue/boxpic/8月10日实况降水.xlsx')

# 1. 创建完整时间列 (年-月-日 时) - 无时区,但代表北京时间
df['Time'] = pd.to_datetime(df[['', '', '', '']].rename(columns={
    '': 'year',
    '': 'month',
    '': 'day',
    '': 'hour'
}))

# 2. 按时间分组绘制箱线图
plt.figure(figsize=(18, 8))

# 准备数据:每个时间点对应所有区站号的降水量列表
# 确保数据按时间排序
df = df.sort_values('Time')
boxplot_data = []
time_labels = []
mean_precip = []    # 存储平均降水量
max_precip = []     # 存储最大降水量
min_precip = []     # 存储最小降水量(可选)
median_precip = []  # 存储中位数降水量(可选)

# 按时间分组,计算统计量
for time, group in df.groupby('Time'):
    # 只包含有数据的时间点
    if len(group) > 0:
        precip_values = group['过去1小时降水量'].values
        boxplot_data.append(precip_values)
        time_labels.append(time)
        mean_precip.append(np.mean(precip_values))
        max_precip.append(np.max(precip_values))
        min_precip.append(np.min(precip_values))
        median_precip.append(np.median(precip_values))

# 创建箱线图 - 使用数值位置而不是日期
positions = range(len(time_labels))
boxes = plt.boxplot(boxplot_data, positions=positions, widths=0.6, patch_artist=True)

# 3. 添加折线图 - 平均降水量和最大降水量
# 平均降水量折线(蓝色)
plt.plot(positions, mean_precip, 'o-', color='red', linewidth=2, markersize=6, 
         label='Mean Precipitation', markerfacecolor='white', markeredgecolor='red')

# 最大降水量折线(红色)
plt.plot(positions, max_precip, 's-', color='darkred', linewidth=2, markersize=5,
         label='Max Precipitation', markerfacecolor='white', markeredgecolor='darkred')

# 中位数降水量折线(可选,绿色)
plt.plot(positions, median_precip, '^-', color='green', linewidth=1.5, markersize=4,
         label='Median Precipitation', alpha=0.7, markerfacecolor='white', markeredgecolor='green')

# 4. 设置X轴标签 - 使用格式化后的时间字符串
formatted_labels = [time.strftime('%m-%d %H') for time in time_labels]

# 根据数据量调整标签显示频率
n_labels = len(time_labels)
if n_labels > 24:  # 如果数据点太多,每隔几个小时显示一个标签
    step = max(1, n_labels // 24)
    visible_labels = [label if i % step == 0 else '' for i, label in enumerate(formatted_labels)]
else:
    visible_labels = formatted_labels

plt.xticks(positions, visible_labels, rotation=90, fontsize=18)
# 在plt.ylabel()之后添加:
plt.yticks(fontsize=18)  

# 5. 添加英文标签和标题
plt.xlabel('Time (Month-Day Hour)', fontsize=18)
plt.ylabel('1-Hour Precipitation (mm)', fontsize=18)
plt.grid(axis='y', alpha=0.4)

# 设置Y轴范围从-1开始
plt.ylim(bottom=-1)

# 6. 美化箱线图
colors = ['lightblue'] * len(boxplot_data)
for patch, color in zip(boxes['boxes'], colors):
    patch.set_facecolor(color)
    patch.set_alpha(0.6)  # 设置透明度,让折线更清晰

# 7. 添加图例
plt.legend(loc='upper right', fontsize=18)

# 8. 添加统计信息文本框
stats_text = f'Total Time Points: {len(time_labels)}\nTotal Stations: {len(df["区站号"].unique())}'
plt.annotate(stats_text, xy=(0.02, 0.98), xycoords='axes fraction', 
             fontsize=9, verticalalignment='top',
             bbox=dict(boxstyle="round,pad=0.3", facecolor="lightgray", alpha=0.7))

# 9. 优化显示
plt.tight_layout()

# 保存图像
output_path = '/root/suyue/boxpic/precipitation_boxplot_with_lines.png'
plt.savefig(output_path, dpi=300, bbox_inches='tight')
print(f"Plot saved to: {output_path}")

# 显示图像
plt.show()

# 打印一些统计信息
print(f"\n统计信息:")
print(f"时间点数量: {len(time_labels)}")
print(f"站点数量: {len(df['区站号'].unique())}")
print(f"平均降水量范围: {min(mean_precip):.2f} - {max(mean_precip):.2f} mm")
print(f"最大降水量范围: {min(max_precip):.2f} - {max(max_precip):.2f} mm")

precipitation_boxplot_with_lines

 

posted @ 2025-08-20 11:04  秋刀鱼CCC  Views(22)  Comments(0)    收藏  举报