Pandas 实战案例 - 冷空气活动寒潮级别分类
作者:小小明
需求分析
寒潮的定义:
数据的输入和输出格式:
统计口径确认:
我一开始不理解,24 小时内降温幅度大于 8 度如何计算,与需求方确认后,可以通过 2 日温度之差来计算。同样 48 小时内降温幅度可以用 3 日温度之差来代表,72 小时内降温幅度可以用 4 日温度之差来代表,需求方的解释:
好了,理解清楚了需求,咱们就可以开始干活了:
读取数据
首先读取数据:
import pandas as pd
import numpy as np
df = pd.read_csv("data.csv")
df
结果:
拿一个分组进行测试
取出某个分组,用于测试:
tmp = df.groupby('number').get_group('e332')
tmp
结果:
获取满足寒潮定义条件的对应数据 id
上图的极端情况显示,三大满足条件的 id 可能出现重复的情况,所以我使用了 set 这个无序不重复集合来保存 id:
cold_wave_idxs = set()
# 获取2天内降温幅度超过8对应的数据id
ids = tmp.index[tmp.temperature.diff(-1) >= 8].values
cold_wave_idxs.update(ids)
cold_wave_idxs.update(ids+1)
# 获取3天内降温幅度超过10对应的数据id
ids = tmp.index[tmp.temperature.diff(-2) >= 10].values
cold_wave_idxs.update(ids)
cold_wave_idxs.update(ids+1)
cold_wave_idxs.update(ids+2)
# 获取4天内降温幅度超过12对应的数据id
ids = tmp.index[tmp.temperature.diff(-3) >= 12].values
cold_wave_idxs.update(ids)
cold_wave_idxs.update(ids+1)
cold_wave_idxs.update(ids+2)
cold_wave_idxs.update(ids+3)
# 排序并转换成列表
cold_wave_idxs = sorted(cold_wave_idxs)
print(cold_wave_idxs)
结果:
[11928, 11929, 11930, 11931, 11939, 11940, 11949, 11950, 11951, 11952, 11955, 11956, 11957, 11958, 12007, 12008, 12154, 12155, 12192, 12193, 12201, 12202, 12203, 12223, 12224, 12225, 12228, 12229, 12230]
上述代码中 coldwaveidxs.update(ids+1)表示,把 ids 列表里每个 id 的后一个 id 也添加到最终列表里,利用了 numpy 数组广播变量的特性,+2 和+3 也是同理。
上述结果就是从站码为'e332'的分组中计算出满足寒潮定义的对应数据 id。
从结果可以看出,凡是连续的 id 都可以看作一个寒潮的过程,所以现在我们需要将每个寒潮过程都分为一组,为了作这样的分组,我发明了一种分组编号生成器的写法,下面已经封装成了一个方法:
分组编号生成器
def generate_group_num(values, diff=1):
group_ids = []
group_id = 0
last_v = 0
for value in values:
if value-last_v > diff:
group_id += 1
group_ids.append(group_id)
last_v = value
return group_ids
上面的方法实现了一个分组编号生成器,对于一段序列凡是连续的数字都会给一个相同的分组编号。
测试一下分组效果:
for i, cold_wave_idx_serial in pd.Series(cold_wave_idxs).groupby(generate_group_num(cold_wave_idxs)):
cold_wave_idx_serial = cold_wave_idx_serial.values
print(cold_wave_idx_serial)
结果:
[11928 11929 11930 11931]
[11939 11940]
[11949 11950 11951 11952]
[11955 11956 11957 11958]
[12007 12008]
[12154 12155]
[12192 12193]
[12201 12202 12203]
[12223 12224 12225]
[12228 12229 12230]
从结果可以看到,凡是连续的序列都分到了一组,不是连续的序列就没有分到一组。
测试对所有站计算寒潮
首先将前面的测试好的用于获取满足寒潮定义的 id 的过程封装成方法:
def get_cold_wave_idxs(df, cold_wave_level=(8, 10, 12)):
cold_wave_idxs = set()
ids = df.index[df.temperature.diff(-1) >= cold_wave_level[0]].values
cold_wave_idxs.update(ids)
cold_wave_idxs.update(ids+1)
ids = df.index[df.temperature.diff(-2) >= cold_wave_level[1]].values
cold_wave_idxs.update(ids)
cold_wave_idxs.update(ids+1)
cold_wave_idxs.update(ids+2)
ids = df.index[df.temperature.diff(-3) >= cold_wave_level[2]].values
cold_wave_idxs.update(ids)
cold_wave_idxs.update(ids+1)
cold_wave_idxs.update(ids+2)
cold_wave_idxs.update(ids+3)
return sorted(cold_wave_idxs)
然后运行:
cold_wave_result = []
for number, tmp in df.groupby('number'):
cold_wave_idxs = get_cold_wave_idxs(tmp, (8, 10, 12))
for i, cold_wave_idx_serial in pd.Series(cold_wave_idxs).groupby(generate_group_num(cold_wave_idxs)):
cold_wave_idx_serial = cold_wave_idx_serial.values
start_id, end_id = cold_wave_idx_serial[0], cold_wave_idx_serial[-1]
# 假如最低温度小于4度,则说明满足全部条件
if tmp.loc[end_id, 'temperature'] <= 4:
cold_wave_result.append(
(number, tmp.loc[start_id, 'date'], tmp.loc[end_id, 'date'],
tmp.loc[start_id, 'temperature'], tmp.loc[end_id, 'temperature'],
end_id-start_id+1,
tmp.loc[start_id, 'temperature'] -tmp.loc[end_id, 'temperature'],
'寒潮'
)
)
cold_wave_result = pd.DataFrame(cold_wave_result, columns=[
'站号', '开始日期', '结束日期', '开始温度', '结束温度', '寒潮天数', '温度差', '寒潮类型'])
cold_wave_result
结果:
没啥问题。
所有寒潮级别都测试一下:
测试所有寒潮级别
cold_wave_all = [
{
'cold_wave_temperature_diffs': (8, 10, 12),
'min_temperature_limit': 4,
'cold_wave_type': '寒潮'
},
{
'cold_wave_temperature_diffs': (10, 12, 14),
'min_temperature_limit': 2,
'cold_wave_type': '强寒潮'
},
{
'cold_wave_temperature_diffs': (12, 14, 16),
'min_temperature_limit': 0,
'cold_wave_type': '超强寒潮'
}
]
cold_wave_result = []
for number, tmp in df.groupby('number'):
for cold_wave_dict in cold_wave_all:
cold_wave_idxs = get_cold_wave_idxs(tmp, cold_wave_dict['cold_wave_temperature_diffs'])
if len(cold_wave_idxs) < 2:
continue
for i, cold_wave_idx_serial in pd.Series(cold_wave_idxs).groupby(generate_group_num(cold_wave_idxs)):
cold_wave_idx_serial = cold_wave_idx_serial.values
start_id, end_id = cold_wave_idx_serial[0], cold_wave_idx_serial[-1]
# 假如最低温度小于指定度数,则说明满足全部条件
if tmp.loc[end_id, 'temperature'] <= cold_wave_dict['min_temperature_limit']:
cold_wave_result.append(
(number, tmp.loc[start_id, 'date'], tmp.loc[end_id, 'date'],
tmp.loc[start_id, 'temperature'], tmp.loc[end_id, 'temperature'],
end_id-start_id+1,
tmp.loc[start_id, 'temperature'] - tmp.loc[end_id, 'temperature'],
cold_wave_dict['cold_wave_type']
)
)
cold_wave_result = pd.DataFrame(cold_wave_result, columns=[
'站号', '开始日期', '结束日期', '开始温度', '结束温度', '寒潮天数', '温度差', '寒潮类型'])
cold_wave_result
结果:
暂时也未发现错误。那么整理一下最终代码吧:
完整代码
import pandas as pd
import numpy as np
def generate_group_num(values, diff=1):
group_ids = []
group_id = 0
last_v = 0
for value in values:
if value-last_v > diff:
group_id += 1
group_ids.append(group_id)
last_v = value
return group_ids
def get_cold_wave_idxs(df, cold_wave_level=(8, 10, 12)):
cold_wave_idxs = set()
ids = df.index[df.temperature.diff(-1) >= cold_wave_level[0]].values
cold_wave_idxs.update(ids)
cold_wave_idxs.update(ids+1)
ids = df.index[df.temperature.diff(-2) >= cold_wave_level[1]].values
cold_wave_idxs.update(ids)
cold_wave_idxs.update(ids+1)
cold_wave_idxs.update(ids+2)
ids = df.index[df.temperature.diff(-3) >= cold_wave_level[2]].values
cold_wave_idxs.update(ids)
cold_wave_idxs.update(ids+1)
cold_wave_idxs.update(ids+2)
cold_wave_idxs.update(ids+3)
return sorted(cold_wave_idxs)
df = pd.read_csv("data.csv")
cold_wave_all = [
{
'cold_wave_temperature_diffs': (8, 10, 12),
'min_temperature_limit': 4,
'cold_wave_type': '寒潮'
},
{
'cold_wave_temperature_diffs': (10, 12, 14),
'min_temperature_limit': 2,
'cold_wave_type': '强寒潮'
},
{
'cold_wave_temperature_diffs': (12, 14, 16),
'min_temperature_limit': 0,
'cold_wave_type': '超强寒潮'
}
]
cold_wave_result = []
for number, tmp in df.groupby('number'):
for cold_wave_dict in cold_wave_all:
cold_wave_idxs = get_cold_wave_idxs(tmp, cold_wave_dict['cold_wave_temperature_diffs'])
if len(cold_wave_idxs) < 2:
continue
for i, cold_wave_idx_serial in pd.Series(cold_wave_idxs).groupby(generate_group_num(cold_wave_idxs)):
cold_wave_idx_serial = cold_wave_idx_serial.values
start_id, end_id = cold_wave_idx_serial[0], cold_wave_idx_serial[-1]
# 假如最低温度小于指定度数,则说明满足全部条件
if tmp.loc[end_id, 'temperature'] <= cold_wave_dict['min_temperature_limit']:
cold_wave_result.append(
(number, tmp.loc[start_id, 'date'], tmp.loc[end_id, 'date'],
tmp.loc[start_id, 'temperature'], tmp.loc[end_id, 'temperature'],
end_id-start_id+1,
tmp.loc[start_id, 'temperature'] - tmp.loc[end_id, 'temperature'],
cold_wave_dict['cold_wave_type']
)
)
cold_wave_result = pd.DataFrame(cold_wave_result, columns=[
'站号', '开始日期', '结束日期', '开始温度', '结束温度', '寒潮天数', '温度差', '寒潮类型'])
cold_wave_result.to_excel("cold_wave.xlsx", index=False)
最终得到的结果:
(前 60 行数据)
本文完结!
版权声明: 本文为 InfoQ 作者【小小明】的原创文章。
原文链接:【http://xie.infoq.cn/article/fb22d7f39b4944d14a4978ec2】。文章转载请联系作者。
小小明
还未添加个人签名 2019.08.26 加入
还未添加个人简介
评论