问题描述
所以我有一些海面温度异常数据。这些数据已被过滤掉,因此这些值低于某个阈值。但是,我正在尝试识别寒潮——也就是说,隔离持续时间超过连续 5 天的事件。我的数据示例如下(我一直在 xarray 数据集/数据数组和 Pandas 数据帧之间工作)。请注意,“天”是我正在查看的月份的天数(最终将扩展到全年)。我一直在搜索 SO/互联网,寻找基于“天”列提取这些 5 天或更长时间事件的方法,但我没有得到任何工作。我对编码还是比较陌生,所以我的第一个想法是遍历“day”列的行,但我不确定。任何见解表示赞赏。
以下是我的一些数据作为 Pandas df 的样子:
lat lon time day ssta
5940 24.125 262.375 1984-06-03 3 -1.233751
21072 24.125 262.375 1984-06-04 4 -1.394495
19752 24.125 262.375 1984-06-05 5 -1.379742
10223 24.125 262.375 1984-06-27 27 -1.276407
47355 24.125 262.375 1984-06-28 28 -1.840763
... ... ... ... ... ...
16738 30.875 278.875 2015-06-30 30 -1.345640
3739 30.875 278.875 2020-06-16 16 -1.212824
25335 30.875 278.875 2020-06-17 17 -1.446407
41891 30.875 278.875 2021-06-01 1 -1.714249
27740 30.875 278.875 2021-06-03 3 -1.477497
64228 rows × 5 columns
作为过滤后的 xarray:
xarray.Dataset
Dimensions: lat: 28,lon: 68,time: 1174
Coordinates:
time (time) datetime64[ns] 1982-06-01 ... 2021-06-04
lon (lon) float32 262.1 262.4 262.6 ... 278.6 278.9
lat (lat) float32 24.12 24.38 24.62 ... 30.62 30.88
day (time) int64 1 2 3 4 5 6 7 ... 28 29 30 1 2 3 4
Data variables:
ssta (time,lat,lon) float32 nan nan nan nan ... nan nan nan nan
Attributes: (0)
TLDR;我想识别(并保留)连续 5 天以上的事件,即是否有第 3 天到第 8 天,或第 21 天到第 30 天等。
解决方法
我认为,与其过滤原始数据,不如尝试使用 pandas 的方式,在这种情况下,这意味着根据您的条件获得具有真假值的系列。
您的数据似乎不包括温度,所以这是我的示例:
import pandas as pd
import numpy as np
df = pd.DataFrame(data={'temp':np.random.randint(10,high=40,size=64228,dtype='int64')})
将生成一个 DataFrame,其中有一列包含 10 到 40 度之间的随机温度。请注意,我只能使用自动生成的索引,但您可能需要使用 .set_index
将其切换到时间或日期等列。假设我们对连续超过 30 度的日子感兴趣。
is_over_30 = df['temp'] > 30
会给我们一个包含该信息的 True/False 数组。请注意,这种格式非常有用,因为我们可以使用它进行索引。例如。 df[is_over_30]
将为我们提供温度超过 30 度的日子的 dataframe
行。现在我们想将 is_over_30
中的 True/False 值向前移动一个位置,并生成一个新系列,如果两者都为真,则为真
is_over_30 & np.roll(is_over_30,-1)
基本上我们到这里就完成了,可以再写 3 个这样的卷。但是有一种方法可以写得更简洁。
from functools import reduce
is_consecutively_over_30 = reduce(lambda a,b: a&b,[np.roll(is_over_30,-i) for i in range(5)])
请记住,即使过去 4 天不能连续超过 30 度,这也可能在这里发生,因为滚动会将第一个值移动到与此相关的位置。但是您可以将最后 4 个值设置为 False 来解决此问题。
is_consecutively_over_30[-4:] = False
,
您可以使用这种方法提取法术的日期范围:
min_spell_days = 6
days = {'day': [1,2,5,6,7,8,9,10,17,19,21,22,23,24,25,26,27,31]}
df = pd.DataFrame(days)
查找连续条目之间的天数:
diff = df['day'].diff()
标记咒语的最后一天:
df['last'] = (diff == 1) & (diff.shift(-1) > 1)
累计每个法术的天数:
df['diff0'] = np.where(diff > 1,diff)
df['cs'] = df['diff0'].eq(0).cumsum()
df['spell_days'] = df.groupby('cs')['diff0'].transform('cumsum')
如果适用,将最后一个条目标记为法术的最后一天:
if diff.iat[-1] == 1:
df['last'].iat[-1] = True
选择所有符合条件的法术的最后一天:
df_spells = (df[df['last'] & (df['spell_days'] >= (min_spell_days-1))]).copy()
确定每个法术的开始、结束和持续时间:
df_spells['end_day'] = df_spells['day']
df_spells['start_day'] = (df_spells['day'] - df['spell_days'])
df_spells['spell_days'] = df['spell_days'] + 1
结果 df
:
df_spells[['start_day','end_day','spell_days']].astype('int')
start_day end_day spell_days
7 5 10 6
16 21 27 7
此外,使用日期算术“day”,您可以表示相对于某个基准日期的连续日数 - 例如 1/1/1900。这样就可以处理跨越月份和年份界限的法术。然后使用日期算术和该序列号转换回日期将是微不足道的。