遍历 Pandas 中的 groupby 分组

3

我有一个Pandas数据表school_df,看起来像这样:

    school_id  date_posted date_completed
0    A          2014-01-01  2014-01-01
1    A          2014-01-01  2014-01-08
2    A          2014-04-29  2014-05-01
3    B          2014-01-01  2014-01-01
4    B          2014-01-20  2014-02-23

每一行代表该学校的一个项目。我想添加两列:对于每个唯一的school_id,一个是在那个日期之前发布的项目数,另一个是在那个日期之前完成的项目数。
下面的代码可以工作,但是我有大约300,000个唯一的学校,所以运行时间很长。有没有更快的方法来获取我想要的结果?感谢您的帮助!
import pandas as pd
groups = school_df.groupby("school_id")
blank_df = pd.DataFrame()
for g, df in groups:
    df['school_previous_projects'] = df.date_posted.map(lambda x: len(df[df.date_posted < x]))
    df['school_previous_completed'] = df.date_posted.map(lambda x: len(df[df.date_completed < x]))
    blank_df = pd.concat([blank_df, df])

@BobHaffner的回答相当不错。如果你能跳出思维定势,你可以按学校分组,并逐个设置日期列的索引。然后,你就可以使用滚动计数,因为它将按日期排序。这比使用apply方法并检查每行的长度要快得多。请查看cumcount http://pandas.pydata.org/pandas-docs/stable/generated/pandas.core.groupby.GroupBy.cumcount.html - Brian Pendleton
我同意@BrianPendleton的观点。我的方法可能比你的更快,但可能有更好的方法。 - Bob Haffner
2个回答

2

试试这个。应该比您的for循环和两个map更快。从您的框架开始

    school_id  date_posted date_completed
0    A          2014-01-01  2014-01-01
1    A          2014-01-01  2014-01-08
2    A          2014-04-29  2014-05-01
3    B          2014-01-01  2014-01-01
4    B          2014-01-20  2014-02-23

接下来是一个函数。getProjectCounts()使用布尔索引和简单的count()。

def getProjectCounts(row, df):
    filter = (df["school_id"] == row["school_id"])  & (df["date_posted"] < row["date_posted"])
    dp_count = df[filter]["date_posted"].count()
    filter = (df["school_id"] == row["school_id"])  & (df["date_completed"] < row["date_completed"])
    dc_count = df[filter]["date_completed"].count()
    return pd.Series([dp_count, dc_count])

然后使用apply()函数逐行执行该函数。
school_df[["school_previous_projects","school_previous_completed"]] = school_df.apply(lambda x : getProjectCounts(x, school_df),axis=1)


  school_id date_posted date_completed  school_previous_projects  \
0         A  2014-01-01     2014-01-01                         0   
1         A  2014-01-01     2014-01-08                         0   
2         A  2014-04-29     2014-05-01                         2   
3         B  2014-01-01     2014-01-01                         0   
4         B  2014-01-20     2014-02-23                         1   

   school_previous_completed  
0                          0  
1                          1  
2                          2  
3                          0  
4                          1 

1
这是使用cumcount的版本(我简化了日期,但仍然有效):
import pandas as pd
import io


df = pd.DataFrame({'school_id': ['A', 'A', 'A', 'B', 'B'],
                   'date_posted': pd.date_range('2014-01-01', '2014-01-05'),
                   'date_completed': pd.date_range('2014-01-01', '2014-01-05')})

posted = df.set_index('date_posted').groupby('school_id').cumcount()
comp = df.set_index('date_completed').groupby('school_id').cumcount()

df['posted'] = posted.values
df['comp'] = comp.values

print df

结果为:
  date_completed date_posted school_id  posted  comp 
0     2014-01-01  2014-01-01         A       0     0 
1     2014-01-02  2014-01-02         A       1     1 
2     2014-01-03  2014-01-03         A       2     2 
3     2014-01-04  2014-01-04         B       0     0 
4     2014-01-05  2014-01-05         B       1     1 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接