Python Dataframe rolling_sum,数字不是日期

时间:2015-10-30 18:18:03

标签: python dataframe

假设我的数据框df[(df['person_num'] == 1) | (df['person_num'] == 2) ]的一部分如下所示:

person_num   Days    IS_TRUE
1            1       1
1            4       1
1            5       0
1            9       1
2            1       1
2            4       1
2            5       0
2            9       1

对于每个person_num,我想要计算“在某一天之前的七天内发生了多少IS_TRUE=1 ”。因此,对于第9天,我计算从第2天到第8天IS_TRUE=1的数量,并将计数添加到新列IS_TRUE_7day_WINDOW。结果将是:

person_num    Days    IS_TRUE    IS_TRUE_7day_WINDOW
1             1       1          0
1             4       1          1
1             5       0          2
1             9       1          1
2             1       1          0
2             4       1          1
2             5       0          2
2             9       1          1

我正在考虑使用这样的东西:

df.groupby('person_num').transform(pd.rolling_sum, window=7,min_periods=1)

但我认为rolling_sum仅适用于datetime,而且代码不适用于我的数据帧。是否有一种简单的方法可以将rolling_sum转换为整数(Days在我的情况下)?或者有其他方法可以快速计算我想要的列吗?

我使用for循环来计算IS_TRUE_7day_WINDOW,但由于我的数据框非常大,我花了一个小时才得到结果。我想像rolling_sum这样的东西会加速我的旧代码。

3 个答案:

答案 0 :(得分:1)

由于您提到数据框派生自数据库,因此请考虑使用子查询的SQL解决方案,该子查询在其引擎中运行计算,而不是直接在Python中运行。

下面假设一个MySQL数据库,但根据您的实际后端(SQLite,PostgreSQL,SQL Server等)调整库和连接字符串。下面应该是ANSI语法SQL,在大多数RDMS中都是合规的。

SQL解决方案

import pandas pd
import pymysql  

conn = pymysql.connect(host="localhost" port=3306,
                     user="username", passwd="***", db="databasename") 

sql = "SELECT t1.Days, t1.person_num, t1.IS_TRUE, \
        (SELECT IFNULL(SUM(t2.IS_TRUE),0)  \
           FROM TableName t2 \
          WHERE t2.person_num= t1.person_num \
            AND t2.Days >= t1.Days - 7  \
            AND t2.Days < t1.Days) AS IS_TRUE_7DAY_WINDOW \
       FROM TableName t1"

df = pd.read_sql(sql, conn)

<强>输出

Days    person_num  IS_TRUE IS_TRUE_7DAY_WINDOW
   1             1        1                   0
   4             1        1                   1
   5             1        0                   2
   9             1        1                   1
   1             2        1                   0
   4             2        1                   1
   5             2        0                   2
   9             2        1                   1                  

答案 1 :(得分:1)

您可以通过向量化隐式执行for循环,这通常比显式写入for循环更快。以下是您提供的数据的工作示例:

import pandas as pd
import numpy as np

df = pd.DataFrame({'Days': [1, 4, 5, 9, 1, 4, 5, 9],
                   'IS_TRUE': [1, 1, 0, 1, 1, 1, 0, 1],
                   'person_num': [1, 1, 1, 1, 2, 2, 2, 2]})

def window(group):
    diff = np.subtract.outer(group.Days, group.Days)
    group['IS_TRUE_7day_WINDOW'] = np.dot((diff > 0) & (diff <= 7),
                                          group['IS_TRUE'])
    return group

f.groupby('person_num').apply(window)

输出是这样的:

   Days  IS_TRUE  person_num  IS_TRUE_7day_WINDOW
0     1        1           1                    0
1     4        1           1                    1
2     5        0           1                    2
3     9        1           1                    1
4     1        1           2                    0
5     4        1           2                    1
6     5        0           2                    2
7     9        1           2                    1

答案 2 :(得分:1)

rolling_rolling_sum函数在查看返回的距离时会使用DataFrame或Series的索引。它不必是日期时间索引。下面是一些代码,用于查找每个用户的计算...

首先使用crosstab创建一个DataFrame,其中每列person_num都有一列,每天都有一行。

    >>> days_person = pd.crosstab(data['days'], 
                      data['person_num'], 
                      values=data['is_true'], 
                      aggfunc=pd.np.sum)
    >>> days_person
    person_num  1  2
    days
    1           1  1
    4           1  1
    5           0  0 
    9           1  1

接下来我会用0来填补缺失的日子,因为你只有几天的数据。

    >>> empty_data = {n: [0]*10 for n in days_person.columns}
    >>> days_person = (days_person + pd.DataFrame(empty_data)).fillna(0)
    >>> days_person
    person_num  1  2
    days
    1           1  1
    2           0  0
    3           0  0
    4           1  1
    5           0  0
    6           0  0
    7           0  0
    8           0  0 
    9           1  1

现在使用rolling_sum来获取您正在寻找的表格。请注意,第1-6天的值为NaN,因为前一天没有足够的时间进行计算。

    >>> pd.rolling_sum(days_person, 7)