开始之前简单交代一下背景,我研究生导师在全国做挂面干燥项目,不同地方的气候对挂面干燥工艺有一定影响,因此经常需要我帮忙统计国内某城市一年的温湿度变化情况。以前用excle表格ctrl+C、ctrl+V方式得到一个城市的某年温湿度表,这种方式重复性操作多,而且查找数据麻烦。在学习了python后,决定做一个一劳永逸的绘制温湿度的脚本。
本文分以下几个方面介绍:
- 原始数据获取
- 应用pandas对原始数据筛选、整理
- 应用matplotlab绘制数据图像
一、原始数据获取
原始数据从中国气象数据中心地面资料下载获取,数据说明可在网站上找到相关文档。加载需自己需要的数据后,几个小时后可以从订单中下载数据。数据格式是.txt的文件,文件里面有每个月的下载地址(每个月都有单独一个下载地址)。在linux操作系统中,使用wget工具批量下载数据:
wget -i S201806271736073722600.txt -P tem
获得2019年全国主要城市的温湿度数据文件,文件名中含有RHU是相对湿度数据,含有TEM的是温度数据。每个文件名都标有年份和月份日期,如201902。
湿度数据一共有11列,其中第一列是城市编码,官网下载文件查询编码对应城市,其余每列数据代表查询如图湿度数据每列对应表。
同理,温度数据一共是13列,每列数据含义也可查询。
到此原始数据已经获取完毕。
二、应用pandas对原始数据筛选、整理
1.遍历原始数据文件
应用golb工具,遍历文件夹内所有文件名,并将文件名存入list中:
for month in range(1, 13):
if month < 10:
list_tem = glob.glob(raw_data_path + '/*TEM*20190' + str(month) + '*')
list_rhu = glob.glob(raw_data_path + '/*RHU*20190' + str(month) + '*')
else:
list_tem = glob.glob(raw_data_path + '/*TEM*2019' + str(month) + '*')
list_rhu = glob.glob(raw_data_path + '/*RHU*2019' + str(month) + '*')
用pandas按月读取数据,并将每月数据按行拼接,注意拼接时需要忽略行索引,设置ignore_index=True。此时读入的数据是一列
tem_data = pd.DataFrame()
for raw_file in list_tem:
tem_data_month = pd.read_table(raw_file, header=None, dtype=str)
tem_data = pd.concat([tem_data, tem_data_month], axis=0, ignore_index=True)
设置列索引标题,分别为城市ID、日期、最小温度、最大温度、平均温度。遍历数据框分别处理每一行数据,用split()方法按空格将数据拆分存入列表,再将每列对应的数据分别处理后填入对应列中。
tem_data.columns = ['city_id']
tem_data['date'] = None
tem_data['min'] = None
tem_data['max'] = None
tem_data['avg'] = None
list_x = []
for i in tqdm(range(len(tem_data))):
list_x = tem_data['city_id'][i].split() # 分开第i行,x列的数据。split()默认是以空格等符号来分割,返回一个列表
tem_data['city_id'][i] = list_x[0] # 分割形成的列表,并将数据填入对应列
# tem_data['data'][i] = time.strptime(' - '.join([list_x[4], list_x[5] , list_x[6]]), "%Y - %m - %d")
tem_data['data'][i] = '-'.join([list_x[4], list_x[5], list_x[6]])
tem_data['min'][i] = float(list_x[9]) / 10
tem_data['avg'][i] = float(list_x[7]) / 10
tem_data['max'][i] = float(list_x[8]) / 10
用同样方法处理湿度数据。
rhu_data = pd.DataFrame()
for raw_file in list_rhu:
rhu_data_month = pd.read_table(raw_file, header=None, dtype=str)
rhu_data = pd.concat([rhu_data, rhu_data_month], axis=0, ignore_index=True) rhu_data.columns = ['city_id']
rhu_data['data'] = None
rhu_data['rhu'] = None
list_x = []
for i in tqdm(range(len(rhu_data))):
list_x = rhu_data['city_id'][i].split()
rhu_data['city_id'][i] = list_x[0]
rhu_data['data'][i] = '-'.join([list_x[4], list_x[5], list_x[6]])
rhu_data['rhu'][i] = list_x[7]
将温度和湿度数据融合。
df = pd.merge(tem_data, rhu_data, how='left', on=['city_id', 'data'])
all_data = pd.concat([all_data, df], axis=0, ignore_index=True)
保存处理后的数据
all_data.to_csv(os.path.join(root_path, 'all_data.csv')
三、用matplotlab工具绘制温湿度图像
读取城市每天的数据,并去除数据中的未观测值,(32766为为观测值,将未观测值用前一有效数值替代)
# city_id = 54618
if city_id in df['city_id']:
day_data = df[df['city_id'] == city_id]
else:
print("没有该编号对应的城市")
# # print(df.shape)
x = df["data"]
month_min = []
month_max = []
month_avg = []
month_rhu = []
# print(day_data)
min_tem = day_data['min'].values.tolist()
max_tem = day_data['max'].values.tolist()
avg_tem = day_data['avg'].values.tolist()
rhu = day_data['rhu'].values.tolist()
modify_invalid_value(3276.6, avg_tem)
modify_invalid_value(3276.6, min_tem)
modify_invalid_value(3276.6, max_tem)
modify_invalid_value(32766, rhu)
计算月平均值,并将月平均值保存在数据框中
days = 0
for day in [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]:
month_min.append(sum(min_tem[days:days + day]) / day)
month_max.append(sum(max_tem[days:days + day]) / day)
month_avg.append(sum(avg_tem[days:days + day]) / day)
month_rhu.append(sum(rhu[days:days + day]) / day)
days = days + day
manth_data = pd.DataFrame(
{"city_id": city_id, "min_tem": month_min, "max_tem": month_max, "avg_tem": month_avg, "avg_rhu": month_rhu})
绘制数据,并保存图像和结果
months = [month for month in range(1, 13)]
fig, ax1 = plt.subplots()
ax1.plot(months, month_min, label=u"min_Tem")
ax1.plot(months, month_max, label=u"max_Tem")
ax1.plot(months, month_avg, label=u"avg_Tem")
ax2 = ax1.twinx()
ax2.plot(months, month_rhu, '--', label=u"avg_Rhu")
# _xtick_labels_ = [' '.join(time.asctime(time.strptime(i, "%Y-%m-%d")).split()[2:3])for i in df["data"]]
ax1.set_xlabel(u"Month")
ax1.set_ylabel(u"TEM(℃)")
ax2.set_ylabel("RHU(%)")
min_tem_val = min(month_min)
max_rhu_val = max(month_rhu)
ax1.set_ylim(min_tem_val - 5, 50)
ax2.set_ylim(-100, max_rhu_val + 10)
_xtick_labels = ["{}".format(i) for i in range(1, 13)]
plt.xticks(months, _xtick_labels)
# plt.title("温湿度表")
for a, b in zip(months, month_min):
ax1.text(a, b, "%.1f" % b, ha='center', va='bottom', fontsize=8)
for a, b in zip(months, month_max):
ax1.text(a, b, "%.1f" % b, ha='center', va='bottom', fontsize=8)
for a, b in zip(months, month_avg):
ax1.text(a, b, "%.1f" % b, ha='center', va='bottom', fontsize=8)
for a, b in zip(months, month_rhu):
ax2.text(a, b, "%.1f" % b, ha='center', va='bottom', fontsize=8)
handles1, labels1 = ax1.get_legend_handles_labels()
handles2, labels2 = ax2.get_legend_handles_labels()
plt.legend(handles1 + handles2, labels1 + labels2, loc='lower center')
plt.title('%d-TEM-RHU-Table' % city_id)
plt.grid()
plt.show()
fig.savefig(os.path.join(root_path, 'image', str(city_id) + '_' + city_name + '.png'), bbox_inches='tight')
# writer = pd.ExcelWriter(os.path.join(root_path, 'ShanDong.xlsx'))
day_data.to_excel(writer, str(city_id) + city_name)
manth_data.to_excel(writer, str(city_id) + city_name, startcol=9)
四、测试结果
用山东省六个城市的数据做测试。
city_id_dict = {54725: '惠民', 54776: '成山头', 54823: '济南', 54843: '潍坊', 54909: '定陶', 54916: '兖州'}
writer = pd.ExcelWriter(os.path.join(root_path, 'ShanDong.xlsx'))
for city_id, city_name in tqdm(city_id_dict.items()):
main(city_id, city_name, writer)
writer.save()
输出结果:
五、总结
终于可以不用再复制粘贴的整理全国各地的温湿度数据了,而且将自己学的东西应用到日常工作中,提高工作效率是多么么么么么爽快的一件事啊。
仍未解决的问题:
1.生成的温湿度图,不能自动插入到相应excle对应的表格里。(不能再excle中追加插入图片)
2.没有解决生成图片中文出现编码错误。
3.数据处理不够简化,应该有很大的提升空间。
在代码过程中遇到的问题(找到相应解决方案):
数据读取与处理
1.如何遍历文件夹内文件?
2.怎样读取数据
3.怎样将不同文件的数据拼接
拼接时需要忽视行索引,
4.怎样将不同文件的数据融合
5.怎样设置dataframe的列标题
6.将读入的数据分为不同的列的方法
7.如何防止第一行数据当作列标题行
8.时间戳、时间元组、时间字符串的概念及相互转换
9.显示进度条的工具使用方法
图像绘制
1.如何设置横坐标
2.如何设置两个纵坐标
3.如何显示曲线中关键点数据值