这篇博客是纪念自己第一次处理水文领域的数据,可能处理方式上有点生疏,甚至有些不当的地方,但实实在在是自己摸索出来的一种方法,后面我会把伪批量化的源码也开源出来,希望能够帮助那些跟我一样在这方面刚入门的小白。
本次实验用到的数据是来自 中国国家级地面气象站基本气象要素日值数据集(V3.0),包含了中国基本气象站、基准气候站、一般气象站在内的主要2474个站点1951年1月至最新本站气压、气温、降水量、蒸发量、相对湿度、风向风速、日照时数和0cm地温要素的日值数据。
因为我主要需要用到的是降水量,所以就选取了降水量的数据集来处理,其他的气象要素数据方法也是类似的,换汤不换药。
1级目录 | 文件名 |
---|---|
PRS | SURF_CLI_CHN_MUL_DAY-PRS-10004-YYYYMM.TXT(本站气压) |
TEM | SURF_CLI_CHN_MUL_DAY-TEM-12001-YYYYMM.TXT(气温) |
RHU | SURF_CLI_CHN_MUL_DAY-RHU-13003-YYYYMM.TXT(相对湿度) |
PRE | SURF_CLI_CHN_MUL_DAY-PRE-13011-YYYYMM.TXT(降水) |
EVP | SURF_CLI_CHN_MUL_DAY-EVP-13240-YYYYMM.TXT(蒸发) |
WIN | SURF_CLI_CHN_MUL_DAY-WIN-11002-YYYYMM.TXT(风向风速) |
SSD | SURF_CLI_CHN_MUL_DAY-SSD-14032-YYYYMM.TXT(日照) |
GST | SURF_CLI_CHN_MUL_DAY-GST-12030-0cm-YYYYMM.TXT(0cm地温) |
这里主要指处理完之后数据的存放位置和存放形式。
最后的输出结果有day,month,year三个文件夹,分别存储了处理好的日值、月值、年度累计降水数据。
#导入所需的库
import os #操作系统路径、文件等常用的库
import shutil #能够将文件移动到指定路径下的库
import pandas as pd #科学计算届的老熟人
import numpy as np # pandas的好基友
def Classify(path):
#设置数据集的路径,告诉python我们要对哪里的数据进行分类,chdir--change dir改变路径的缩写
os.chdir(path)
# getcwd -- get work dir 得到工作路径,就是查看一下当前的工作路径
os.getcwd()
# os.listdir()可以读取路径下的所有数据,并且保存成列表形式
Total_File = os.listdir(path)
# 取出命名中的年份信息 即i[-10:-6],用这个特征来进行分年份存放对应的文件
for i in Total_File:
year = i[-10:-6]
#将当前文件夹路径与对应年份相黏贴,构成每个年份文件夹的完整路径
year_Path = path+'\\'+year
#检查当前文件下有没有对应年份的文件夹,若没有的话即创建
if not os.path.exists(year_Path):
os.mkdir(year_Path)
#如果已经存在对应年份的文件夹,则根据文件的年份信息进行分类
if(year == year_Path[-4:]):
# shutil.move(源文件,指定路径):递归移动一个文件
shutil.move(i,year_Path)
os.mkdir(year_Path)
是在文件夹不存在的情况下才会创建,如果已经存在了相应的文件夹,再次运行的时候就会报错提示你已经有该文件了。def ChinaDayPRE(path,year,out):
"""
path:读取的文件夹路径
year:需要处理的年份
out:需要保存的文件路径
"""
os.chdir(out)
if not os.path.exists('day'):
os.mkdir('day')
if not os.path.exists('month'):
os.mkdir('month')
if not os.path.exists('year'):
os.mkdir('year')
# 读取某个年份的文件夹进行单年份数据处理
file_Path = path +'\\'+str(year)
l = os.listdir(file_Path)
# 设置一个列表来存放当前年份的十二个月份数据
Day_list = []
for y in range(len(l)):
file = file_Path+'\\'+l[y]
#将数据添加进列表中
col = ['站号','纬度','经度','测量海拔','年','月','日','20-8时降水量','8-20降水量',
'20-20累计降水量','20-8时降水量控制码','8-20时降水量控制码','20-20时累计降水量控制码']
Day_list.append(pd.read_csv(file,sep='\s+',names= col))
质量控制码 | 含义 |
---|---|
0 | 数据正确 |
1 | 数据可疑 |
8 | 数据缺失或无观测任务 |
9 | 未进行质量控制 |
根据描述文件的介绍,数据里的第10列为累计降水量,第13列为质量控制码,因此我们可以针对第十三列进行筛选,针对第十列来进行累积计算
# 根据第十三列的质量控制码,筛选出正确的数据,索引从0开始
for i in range(len(year_list)):
year_list[i] = year_list[i][year_list[i]['20-20时累计降水量控制码'].isin([0])]
# 检查累计降水量该列是否全为正确数据
for i in range(len(year_list)):
if year_list[i]['20-20时累计降水量控制码'].max() == 0:
flag = True
print(flag)
异常值处理
代码 | 含义 |
---|---|
32700 | 表示降水"微量" |
32XXX | XXX 为纯雾露霜 |
31XXX | XXX 为雨和雪的总量 |
30XXX | XXX 为雪量(仅包括雨夹雪,雪暴) |
经纬度处理
序号 | 中文名 | 数据类型 | 单位 |
---|---|---|---|
1 | 区站号 | Number(5) | |
2 | 纬度 | Number(5) | (度、分) |
3 | 经度 | Number(6) | (度、分) |
4 | 观测场拔海高度 | Number(7) | 0.1米 |
# 异常值和经纬度处理
# 备份一份数据,保存异常值处理之前的list
Day_process = Day_list
# 写一个度分秒转换的函数
def dfToDu(data):
D = data.astype(int)
F = (data - D)*100/60
F = round(F,2)
data = D+F
return data
for i in range(len(Day_process)):
Day_process[i].loc[Day_process[i]['20-20累计降水量'] == 32766,'20-20累计降水量'] = -999
Day_process[i].loc[Day_process[i]['20-20累计降水量'] == 32700,'20-20累计降水量'] = 0
Day_process[i].loc[(Day_process[i]['20-20累计降水量'] >= 30000) & (Day_process[i]['20-20累计降水量'] < 31000),'20-20累计降水量']= Day_process[i]['20-20累计降水量']-30000
Day_process[i].loc[(Day_process[i]['20-20累计降水量'] >= 31000) & (Day_process[i]['20-20累计降水量'] < 32000),'20-20累计降水量'] = Day_process[i]['20-20累计降水量']-31000
Day_process[i].loc[(Day_process[i]['20-20累计降水量'] >= 32000) & (Day_process[i]['20-20累计降水量'] < 33000),'20-20累计降水量'] = Day_process[i]['20-20累计降水量']-32000
Day_process[i]['20-20累计降水量'] = Day_process[i]['20-20累计降水量']*0.1
#经纬度处理
# 经纬度除以100后,小数前两位是度,可以用i取整的方法提取出来,小数后两位是分,可以乘回100得到正常的值
Day_process[i]['经度'] = Day_process[i]['经度']*0.01
Day_process[i]['纬度'] = Day_process[i]['纬度']*0.01
#对经纬度进行单位转换
Day_process[i]['经度'] = dfToDu(Day_process[i]['经度'])
Day_process[i]['纬度'] = dfToDu(Day_process[i]['纬度'])
# 去除一些冗余的列,保留站点的信息和累计降水量
daily = Day_process[0][['站号','纬度','经度','测量海拔','年','月','日','20-20累计降水量']]
for i in range(1,12):
daily = pd.concat([daily,Day_process[i][['站号','纬度','经度','测量海拔','年','月','日','20-20累计降水量']]],join='inner',axis = 0)
#导出路径设置
OutDaily = out+'/day/'+str(year)+'_day.csv'
# 日值降水量数据
daily.to_csv(OutDaily,encoding='gbk',index = False)
其实正常而言经过上述的处理已经得到一份干净的数据了,但科研人除了需要用到日值的数据,还有可能需要用到月度、年度的累计降水数据,因此我们可以稍微再做一点加工,用日值数据进行分组和聚合操作,得到对应的加工数据。
# 处理成月累计数据
month_data = daily.groupby(['站号','月']).agg({'20-20累计降水量':np.sum,'年':np.mean,'测量海拔':np.mean,'纬度':np.mean,'经度':np.mean})
#导出月度累计数据
OutMonth = out+'/month/'+str(year)+'_month.csv'
month_data.to_csv(OutMonth,encoding='gbk')
#年度数据合成
year_data=month_data.groupby(['站号']).agg({'20-20累计降水量':np.sum,'年':np.mean,'测量海拔':np.mean,'纬度':np.mean,'经度':np.mean})
#导出年度数据
OutYear = out + '/year/'+str(year)+'_year.csv'
year_data.to_csv(OutYear,encoding='gbk')
处理完之后,我们可以看到指定路径下多了三个文件夹,分别存储日值、月度和年度的数据。
# 导入所需的库
import os
import shutil
import pandas as pd
import numpy as np
def Classify(path):
#设置当前的工作路径
os.chdir(path)
os.getcwd()
Total_File = os.listdir(path)
# 取出命名中的年份信息 即i[-10:-6],用这个特征来进行分年份存放对应的文件
for i in Total_File:
year = i[-10:-6]
#将当前文件夹路径与对应年份相黏贴,构成每个年份文件夹的完整路径
year_Path = path+'\\'+year
#检查当前文件下有没有对应年份的文件夹,若没有的话即创建
if not os.path.exists(year_Path):
os.mkdir(year_Path)
#如果已经存在对应年份的文件夹,则根据文件的年份信息进行分类
if(year == year_Path[-4:]):
# shutil.move(源文件,指定路径):递归移动一个文件
shutil.move(i,year_Path)
def ChinaDayPRE(path,year,out):
"""
path:读取的文件夹路径
year:需要处理的年份
out:需要保存的文件路径
"""
os.chdir(out)
if not os.path.exists('day'):
os.mkdir('day')
if not os.path.exists('month'):
os.mkdir('month')
if not os.path.exists('year'):
os.mkdir('year')
# 读取某个年份的文件夹进行单年份数据处理
file_Path = path +'\\'+str(year)
l = os.listdir(file_Path)
# 设置一个列表来存放当前年份的十二个月份数据
Day_list = []
for y in range(len(l)):
file = file_Path+'\\'+l[y]
#将数据添加进列表中
col = ['站号','纬度','经度','测量海拔','年','月','日','20-8时降水量','8-20降水量',
'20-20累计降水量','20-8时降水量控制码','8-20时降水量控制码','20-20时累计降水量控制码']
Day_list.append(pd.read_csv(file,sep='\s+',names= col))
# 根据第十一列的质量控制码,筛选出正确的数据,索引从0开始
for i in range(len(Day_list)):
Day_list[i] = Day_list[i][Day_list[i]['20-20时累计降水量控制码'].isin([0])]
# 检查累计降水量该列是否全为正确数据
for i in range(len(Day_list)):
if Day_list[i]['20-20时累计降水量控制码'].max() == 0:
flag = True
# print(flag)
# 异常值和经纬度处理
# 备份一份数据,保存异常值处理之前的list
Day_process = Day_list
# 写一个转换函数
def dfToDu(data):
D = data.astype(int)
F = (data - D)*100/60
F = round(F,2)
data = D+F
return data
for i in range(len(Day_process)):
Day_process[i].loc[Day_process[i]['20-20累计降水量'] == 32766,'20-20累计降水量'] = -999
Day_process[i].loc[Day_process[i]['20-20累计降水量'] == 32700,'20-20累计降水量'] = 0
Day_process[i].loc[(Day_process[i]['20-20累计降水量'] >= 30000) & (Day_process[i]['20-20累计降水量'] < 31000),'20-20累计降水量']= Day_process[i]['20-20累计降水量']-30000
Day_process[i].loc[(Day_process[i]['20-20累计降水量'] >= 31000) & (Day_process[i]['20-20累计降水量'] < 32000),'20-20累计降水量'] = Day_process[i]['20-20累计降水量']-31000
Day_process[i].loc[(Day_process[i]['20-20累计降水量'] >= 32000) & (Day_process[i]['20-20累计降水量'] < 33000),'20-20累计降水量'] = Day_process[i]['20-20累计降水量']-32000
Day_process[i]['20-20累计降水量'] = Day_process[i]['20-20累计降水量']*0.1
#经纬度处理
# 经纬度除以100后,小数前两位是度,可以用i取整的方法提取出来,小数后两位是分,可以乘回100得到正常的值
Day_process[i]['经度'] = Day_process[i]['经度']*0.01
Day_process[i]['纬度'] = Day_process[i]['纬度']*0.01
Day_process[i]['经度'] = dfToDu(Day_process[i]['经度'])
Day_process[i]['纬度'] = dfToDu(Day_process[i]['纬度'])
# 去除一些冗余的列,保留站点的信息和累计降水量
daily = Day_process[0][['站号','纬度','经度','测量海拔','年','月','日','20-20累计降水量']]
for i in range(1,12):
daily = pd.concat([daily,Day_process[i][['站号','纬度','经度','测量海拔','年','月','日','20-20累计降水量']]],join='inner',axis = 0)
# 日值降水量数据
OutDaily = out+'/day/'+str(year)+'_day.csv'
daily.to_csv(OutDaily,encoding='gbk',index = False)
# 处理成月累计数据
month_data = daily.groupby(['站号','月']).agg({'20-20累计降水量':np.sum,'年':np.mean,'测量海拔':np.mean,'纬度':np.mean,'经度':np.mean})
#导出月度累计数据
OutMonth = out+'/month/'+str(year)+'_month.csv'
month_data.to_csv(OutMonth,encoding='gbk')
#年度数据合成
year_data=month_data.groupby(['站号']).agg({'20-20累计降水量':np.sum,'年':np.mean,'测量海拔':np.mean,'纬度':np.mean,'经度':np.mean})
#导出年度数据
OutYear = out + '/year/'+str(year)+'_year.csv'
year_data.to_csv(OutYear,encoding='gbk')
#修改为数据集的文件夹
path = r'D:\New Desktop\SURF_CLI_CHN_MUL_DAY_V3.0\SURF_CLI_CHN_MUL_DAY_V3.0\datasets\PRE'
#修改输出的文件路径
outpath = 'D:/New Desktop'
#先对未处理的数据进行分类
Classify(path)
#分类后对所有年份进行批量处理
for year in range(1951,2016):
ChinaDayPRE(path,year,outpath)
print('Successful!')
借鉴文章:
1. 1951-2020年中国地面气候资料日值降水数据集提取
2. python处理日值气象数据