创建滚动求和列,该列在达到阈值后重置

2024-03-29 10:02:18 发布

您现在位置:Python中文网/ 问答频道 /正文

这个问题与我能找到的其他类似问题不同,因为我试图将回望窗口和阈值组合成一个滚动总和。实际上,我不确定我要做的事情是否可以一步到位:

我有一个带有datetime列和value列的dataframe。我创建了一个列,在滚动时间窗口中对值列(V)求和。但是,我希望这个滚动总和在达到某个阈值后重置为0

我不知道是否有可能在一个列操作步骤中实现这一点,因为在求和的每个步骤中都有两个条件——回望窗口和阈值。如果有人有任何想法,如果这是可能的,我可能会如何实现它,请让我知道。我知道如何迭代执行此操作,但速度非常慢(我的数据帧有>;100万个条目)

例如:

回溯时间:3分钟

阈值:3

+---+-----------------------+-------+--------------------------+
|   |           myDate      |   V   | rolling | desired_column |
+---+-----------------------+-------+---------+----------------+
| 1 | 2020-04-01 10:00:00   | 0     |  0      |       0        |   
| 2 | 2020-04-01 10:01:00   | 1     |  1      |       1        | 
| 3 | 2020-04-01 10:02:00   | 2     |  3      |       3        | 
| 4 | 2020-04-01 10:03:00   | 1     |  4      |       1        | 
| 5 | 2020-04-01 10:04:00   | 0     |  4      |       1        | 
| 6 | 2020-04-01 10:05:00   | 4     |  7      |       5        | 
| 7 | 2020-04-01 10:06:00   | 1     |  6      |       1        | 
| 8 | 2020-04-01 10:07:00   | 1     |  6      |       2        | 
| 9 | 2020-04-01 10:08:00   | 0     |  6      |       0        |       
| 10| 2020-04-01 10:09:00   | 3     |  5      |       5        | 
+---+-----------------------+-------+---------+----------------+

在本例中,sum rulling sum将不考虑行上或之前任何超过(或等于)阈值3的值


Tags: 数据gtdataframedatetimevalue时间步骤条目
2条回答

下面的方法无论如何都不能节省内存,但它应该比循环更快。它假定时间是连续的,以便委托给numpy方法,否则可以在调用之前包含缺少的时间

def rolling_window(a, window):
    b = np.concatenate((np.zeros(window-1), a)) # only for 1d
    return np.array([b[..., i:i+window] for i in range(a.size)])


def dynamic_window(w: np.array, reset):
    regions = np.hstack([
        np.zeros((w.shape[0], 1)),
        np.cumsum(w, axis=-1)[:, :-1]
    ]) // reset
    return w * (regions == regions[:, -1][:, np.newaxis])

把它当作

# sample df
# please always provide a callable line of code
# you could get it with `df.head(10).to_dict('split')`
df = pd.DataFrame({
    'myDate': pd.date_range('2020-04-01 10:00', periods=10, freq='T'),
    'V': [0, 1, 2, 1, 0, 4, 1, 1, 0, 3]
})
# include all time increments
df = pd.concat([
    df,
    pd.DataFrame(pd.date_range(df['myDate'].min(),
        df['myDate'].max(), freq='T'), columns=['myDate'])
]).drop_duplicates(subset=['myDate']).fillna(0).sort_values('myDate')

df['4min_sum'] = df.rolling('4min', on='myDate')['V'].sum()

# use the functions
df['desired_column'] = dynamic_window(
    rolling_window(df['V'].to_numpy(), 4),
    3).sum(axis=-1)

输出

               myDate    V  4min_sum  desired_column
0 2020-04-01 10:00:00  0.0       0.0             0.0
1 2020-04-01 10:01:00  1.0       1.0             1.0
2 2020-04-01 10:02:00  2.0       3.0             3.0
3 2020-04-01 10:03:00  1.0       4.0             1.0
4 2020-04-01 10:04:00  0.0       4.0             1.0
5 2020-04-01 10:05:00  4.0       7.0             4.0
6 2020-04-01 10:06:00  1.0       6.0             1.0
7 2020-04-01 10:07:00  1.0       6.0             2.0
8 2020-04-01 10:08:00  0.0       6.0             0.0
9 2020-04-01 10:09:00  3.0       5.0             5.0

请注意,在10:05时,它如何输出4,而不是预期输出中的5。根据你的逻辑,应该是4;该窗口包含[2, 1, 0, 4],由于前两个数字和为3,因此该窗口应重置并返回0+4

我找不到一种矢量化的方法来在每次达到阈值时重置为0

但是Pandas列的底层容器是一个numpy数组,迭代一个numpy数组需要一段可接受的时间。因此,我想:

arr = np.zeros(len(df), dtype='int')
cum = 0
src = df['V'].values
dt = df['myDate'].values
start = 0
for i in range(len(df)):
    cum += src[i]
    while dt[start] < dt[i] - np.timedelta64(4, 'm'):
        cum -= src[start]
        start +=1
    arr[i] = cum
    if cum >=3:
        cum = 0
        start = i

df['desired_column'] = arr

它给出:

                myDate  V  rolling  desired_column
1  2020-04-01 10:00:00  0        0               0
2  2020-04-01 10:01:00  1        1               1
3  2020-04-01 10:02:00  2        3               3
4  2020-04-01 10:03:00  1        4               1
5  2020-04-01 10:04:00  0        4               1
6  2020-04-01 10:05:00  4        7               5
7  2020-04-01 10:06:00  1        6               1
8  2020-04-01 10:07:00  1        6               2
9  2020-04-01 10:08:00  0        6               2
10 2020-04-01 10:09:00  3        5               5

在我的i5机器上,长度为1000000的数组只需要几秒钟(10000的数组大约需要90秒)

相关问题 更多 >