作者:小小明
Pandas数据处理专家,帮助一万用户解决数据处理难题。
需求分析
寒潮的定义:
数据的输入和输出格式:
统计口径确认:
我一开始不理解,24小时内降温幅度大于8度如何计算,与需求方确认后,可以通过2日温度之差来计算。同样48小时内降温幅度可以用3日温度之差来代表,72小时内降温幅度可以用4日温度之差来代表,需求方的解释:
好了,理解清楚了需求,咱们就可以开始干活了:
读取数据
首先读取数据:
import pandas as pd
import numpy as np
df = pd.read_csv("data.csv")
df
结果:
date | number | temperature | |
---|---|---|---|
0 | 2004/12/20 | d289 | -30.38 |
1 | 2004/12/21 | d289 | -32.67 |
2 | 2004/12/22 | d289 | -33.12 |
3 | 2004/12/23 | d289 | -31.71 |
4 | 2004/12/24 | d289 | -32.76 |
… | … | … | … |
12256 | 2005/12/16 | e332 | -8.37 |
12257 | 2005/12/17 | e332 | -12.73 |
12258 | 2005/12/18 | e332 | -13.57 |
12259 | 2005/12/19 | e332 | -13.36 |
12260 | 2005/12/20 | e332 | -15.99 |
拿一个分组进行测试
取出某个分组,用于测试:
tmp = df.groupby('number').get_group('e332')
tmp
结果:
date | number | temperature | |
---|---|---|---|
11926 | 2005/1/20 | e332 | -18.06 |
11927 | 2005/1/21 | e332 | -8.76 |
11928 | 2005/1/22 | e332 | -4.77 |
11929 | 2005/1/23 | e332 | -10.81 |
11930 | 2005/1/24 | e332 | -19.91 |
… | … | … | … |
12256 | 2005/12/16 | e332 | -8.37 |
12257 | 2005/12/17 | e332 | -12.73 |
12258 | 2005/12/18 | e332 | -13.57 |
12259 | 2005/12/19 | e332 | -13.36 |
12260 | 2005/12/20 | e332 | -15.99 |
获取满足寒潮定义条件的对应数据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]
上述代码中cold_wave_idxs.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
结果:
站号 | 开始日期 | 结束日期 | 开始温度 | 结束温度 | 寒潮天数 | 温度差 | 寒潮类型 | |
---|---|---|---|---|---|---|---|---|
0 | d289 | 2005/1/2 | 2005/1/4 | -19.39 | -30.79 | 3 | 11.40 | 寒潮 |
1 | d289 | 2005/1/10 | 2005/1/13 | -21.66 | -36.06 | 4 | 14.40 | 寒潮 |
2 | d289 | 2005/1/21 | 2005/1/24 | -12.28 | -27.40 | 4 | 15.12 | 寒潮 |
3 | d289 | 2005/2/7 | 2005/2/8 | -15.31 | -26.47 | 2 | 11.16 | 寒潮 |
4 | d289 | 2005/2/15 | 2005/2/17 | -12.12 | -23.52 | 3 | 11.40 | 寒潮 |
… | … | … | … | … | … | … | … | … |
395 | e332 | 2005/9/5 | 2005/9/6 | 2.68 | -6.00 | 2 | 8.68 | 寒潮 |
396 | e332 | 2005/10/13 | 2005/10/14 | -2.52 | -12.76 | 2 | 10.24 | 寒潮 |
397 | e332 | 2005/10/22 | 2005/10/24 | -0.69 | -12.90 | 3 | 12.21 | 寒潮 |
398 | e332 | 2005/11/13 | 2005/11/15 | -5.96 | -16.56 | 3 | 10.60 | 寒潮 |
399 | e332 | 2005/11/18 | 2005/11/20 | -7.31 | -18.74 | 3 | 11.43 | 寒潮 |
感觉没啥问题。
所有寒潮级别都测试一下:
测试所有寒潮级别
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
结果:
站号 | 开始日期 | 结束日期 | 开始温度 | 结束温度 | 寒潮天数 | 温度差 | 寒潮类型 | |
---|---|---|---|---|---|---|---|---|
0 | d289 | 2005/1/2 | 2005/1/4 | -19.39 | -30.79 | 3 | 11.40 | 寒潮 |
1 | d289 | 2005/1/10 | 2005/1/13 | -21.66 | -36.06 | 4 | 14.40 | 寒潮 |
2 | d289 | 2005/1/21 | 2005/1/24 | -12.28 | -27.40 | 4 | 15.12 | 寒潮 |
3 | d289 | 2005/2/7 | 2005/2/8 | -15.31 | -26.47 | 2 | 11.16 | 寒潮 |
4 | d289 | 2005/2/15 | 2005/2/17 | -12.12 | -23.52 | 3 | 11.40 | 寒潮 |
… | … | … | … | … | … | … | … | … |
636 | e332 | 2005/2/2 | 2005/2/3 | -7.36 | -20.59 | 2 | 13.23 | 强寒潮 |
637 | e332 | 2005/10/13 | 2005/10/14 | -2.52 | -12.76 | 2 | 10.24 | 强寒潮 |
638 | e332 | 2005/10/22 | 2005/10/24 | -0.69 | -12.90 | 3 | 12.21 | 强寒潮 |
639 | e332 | 2005/1/22 | 2005/1/24 | -4.77 | -19.91 | 3 | 15.14 | 超强寒潮 |
640 | e332 | 2005/2/2 | 2005/2/3 | -7.36 | -20.59 | 2 | 13.23 | 超强寒潮 |
暂时也未发现错误。那么整理一下最终代码吧:
完整代码
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行数据)
本文完结!