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")