python - Pandas:滚动窗口来计算频率 - 最快的方法

标签 python pandas dataframe count rolling-computation

我想计算过去 x 天某个值的出现频率。在下面的示例中,我想计算过去 28 天 Name 列中值的频率。数据已按日期

排序
import pandas as pd
import time

d = {'Name': ['Jack', 'Jim', 'Jack', 'Jim', 'Jack', 'Jack', 'Jim', 'Jack', 'Jane', 'Jane'],
     'Date': ['08/01/2021',
              '27/01/2021',
              '05/02/2021',
              '10/02/2021',
              '17/02/2021',
              '18/02/2021',
              '20/02/2021',
              '21/02/2021',
              '22/02/2021',
              '29/03/2021']}
df = pd.DataFrame(data=d)
df['Date'] = pd.to_datetime(df.Date, format='%d/%m/%Y')
# Make sure pandas is sorted by Date
df = df.sort_values('Date')

我在 StackOverFlow 上找到了一些解决方案,但所有这些解决方案在数据集上都不正确,速度也不快。

方法 1 - 不太正确

df['count1'] = df.set_index('Date').groupby('Name', sort=False)['Name'].rolling('28d', closed='both').count().tolist()

方法 2 - 正确的方法,但速度很慢 <~ 来自 link

df['count2'] = df.assign(count=1).groupby(['Name']).apply(lambda x: x.rolling('28d', on='Date').sum())['count']

方法 3 - 使用 sum - 不正确

df['count3'] = df.assign(count=1).groupby('Name').rolling('28d', on='Date').sum().reset_index().sort_values('Date')['count']

方法4 - 也使用sum - 不正确,因为索引不正确 <~ this link

df['count4'] = df.set_index('Date').assign(count_last=1).groupby('Name').rolling('28d').sum().reset_index()["count_last"]

输出

   Name       Date  count1  count2  count3  count4
0  Jack 2021-01-08     1.0     1.0     1.0     1.0
1   Jim 2021-01-27     2.0     1.0     1.0     1.0
2  Jack 2021-02-05     2.0     1.0     2.0     2.0
3   Jim 2021-02-10     3.0     2.0     3.0     3.0
4  Jack 2021-02-17     4.0     2.0     4.0     4.0 #<~ all are wrong here except approach 2
5  Jack 2021-02-18     1.0     3.0     1.0     1.0
6   Jim 2021-02-20     2.0     3.0     1.0     1.0
7  Jack 2021-02-21     3.0     4.0     1.0     1.0
8  Jane 2021-02-22     1.0     1.0     2.0     2.0
9  Jane 2021-03-29     1.0     1.0     3.0     3.0

表演

Method 1: 0.0014538764953613281 ms
Method 2: 0.0034720897674560547 ms
Method 3: 0.002077817916870117 ms
Method 4: 0.0035729408264160156 ms

已更新 <~解决方案

根据标记的答案,我编写了这个解决方案,我认为当数据具有 NULL 值和重复项时它会起作用。此外,它不会改变原始数据集的大小。

def frequency_of_last_n_days(df: pd.DataFrame, identifier: str, timestamp: str, delta: int) -> pd.DataFrame:
   
    col_name = "count_%s" % identifier
    temp_df = df.set_index(timestamp) \
        .groupby(identifier, sort=False)[identifier] \
        .rolling('%sd' % delta, closed='both') \
        .count() \
        .rename(col_name)
    temp_df = temp_df[~temp_df.index.duplicated(keep="first")]
    return df.merge(temp_df, how="left", left_on=[identifier, timestamp], right_index=True)

frequency_of_last_n_days(df, "Name", "Date", 30)

最佳答案

IIUC,问题出在您的 tolist() 上,它扰乱了索引对齐并打乱了输出。

改用合并:

df2 = (df
 .merge(df.set_index('Date')
          .groupby('Name', sort=False)['Name']
          .rolling('28d', closed='both') # do you really want closed="both"?
          .count().rename('count'),
        left_on=['Name', 'Date'], right_index=True
        )
)

输出:

   Name       Date  count
0  Jack 2021-01-08    1.0
1   Jim 2021-01-27    1.0
2  Jack 2021-02-05    2.0 <- if you want 1 here, remove closed='both'
3   Jim 2021-02-10    2.0
4  Jack 2021-02-17    2.0
5  Jack 2021-02-18    3.0
6   Jim 2021-02-20    3.0
7  Jack 2021-02-21    4.0
8  Jane 2021-02-22    1.0
9  Jane 2021-03-29    1.0

关于python - Pandas:滚动窗口来计算频率 - 最快的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71952088/

相关文章:

python - 如何在 Windows Server 2012 上使用 "OverflowError: Python int too large to convert to C long"错误进行转换?

python - 从 Pandas 数据框中删除不需要的值

python - 漂亮的打印 Pandas : how to set the columns as displayed rows (and rows as displayed columns)?

python - 如何向现有 DataFrame 添加新列?

python - 在导出到 Excel 之前如何命名 DataFrame 中的行和列?

python - 如何在 Python 中找到与某个项目最频繁的配对

python - 如何获取行的 nr 直到下一个值

Python - 我可以在不编写 getter/setter 的情况下包装属性赋值吗

python - matplotlib 子图 : how to freeze x and y axis?

python-3.x - 将函数应用于 Pandas 数据框并添加新列?