假设我的数据框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
这样的东西会加速我的旧代码。
答案 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)