前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Pandas实战案例 | 冷空气活动寒潮级别分类

Pandas实战案例 | 冷空气活动寒潮级别分类

作者头像
可以叫我才哥
发布2021-08-05 11:50:28
6770
发布2021-08-05 11:50:28
举报
文章被收录于专栏:可以叫我才哥

作者:小小明 Pandas数据处理专家,帮助无数用户解决过数据处理难题。

大家好,今天介绍来自盆友小明大佬的Pandas神级操作,如何把基础函数groupbydiff方法通过复杂而清晰逻辑去解决令人头大的需求,优雅~

目录:

  • 需求分析
  • 读取数据
  • 拿一个分组进行测试
  • 获取满足寒潮定义条件的对应数据id
  • 分组编号生成器
  • 测试对所有站计算寒潮
  • 测试所有寒潮级别
  • 完整代码

需求分析

寒潮的定义:

数据的输入和输出格式:

统计口径确认:

我一开始不理解,24小时内降温幅度大于8度如何计算,与需求方确认后,可以通过2日温度之差来计算。同样48小时内降温幅度可以用3日温度之差来代表,72小时内降温幅度可以用4日温度之差来代表,需求方的解释:

好了,理解清楚了需求,咱们就可以开始干活了:

读取数据

首先读取数据:

代码语言:javascript
复制
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

拿一个分组进行测试

取出某个分组,用于测试:

代码语言:javascript
复制
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:

代码语言:javascript
复制
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)

结果:

代码语言:javascript
复制
[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都可以看作一个寒潮的过程,所以现在我们需要将每个寒潮过程都分为一组,为了作这样的分组,我发明了一种分组编号生成器的写法,下面已经封装成了一个方法:

分组编号生成器

代码语言:javascript
复制
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

上面的方法实现了一个分组编号生成器,对于一段序列凡是连续的数字都会给一个相同的分组编号。

测试一下分组效果:

代码语言:javascript
复制
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)

结果:

代码语言:javascript
复制
[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的过程封装成方法:

代码语言:javascript
复制
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)

然后运行:

代码语言:javascript
复制
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

寒潮

感觉没啥问题。

所有寒潮级别都测试一下:

测试所有寒潮级别

代码语言:javascript
复制
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

超强寒潮

暂时也未发现错误。那么整理一下最终代码吧:

完整代码

代码语言:javascript
复制
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行数据)

本文完结!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 可以叫我才哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 需求分析
  • 读取数据
  • 拿一个分组进行测试
  • 获取满足寒潮定义条件的对应数据id
  • 分组编号生成器
  • 测试对所有站计算寒潮
  • 测试所有寒潮级别
  • 完整代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档