将函数应用于 pandas groupby 和索引

Apply functions to pandas groupby and indexing

我试图理解 Pandas Groupby,但我目前看到一些我不理解的行为。基本上,我有一个看起来像(只显示头部)的数据集:

 userId movieId rating  timestamp   parsed_time
0   1   2       3.5     1112486027  2005-04-02 23:53:47
1   1   29      3.5     1112484676  2005-04-02 23:31:16
2   1   32      3.5     1112484819  2005-04-02 23:33:39
3   1   47      3.5     1112484727  2005-04-02 23:32:07
4   1   50      3.5     1112484580  2005-04-02 23:29:40

我检查了数据集的 NaN/null 个值,有 none 个。现在,我想计算每部电影的平均评分,以及标准偏差。

获得平均评分很简单:

ratings = pd.read_csv('ratings.csv', sep=',')

average_rating = ratings[['movieId','rating']].groupby('movieId',as_index=False).mean()
average_ratings.rename(columns={'rating':'AverageRating'}, inplace=True)

这给了我类似的东西:

 movieId    AverageRating
0   1     3.921240
1   2     3.211977
2   3     3.151040
3   4     2.861393
4   5     3.064592

所以这一切都很好,这也是我对 groupby()mean() 组合的期望。 现在,我想做同样的事情来计算电影评级的标准偏差,并将其作为新列添加到 average_rating df:

average_rating['StdDev'] = ratings[['movieId','rating']].groupby('movieId').std()

这给了我:

    movieId AverageRating   StdDev
0   1       3.921240    NaN
1   2       3.211977    0.889012
2   3       3.151040    0.951150
3   4       2.861393    1.006642
4   5       3.064592    1.095702

这里让我感到困惑的是,作为第一个条目出现在我的 StdDev 列中的 NaN。如果我手动提取行,比如 movieId [1,2] 并计算那些的均值和标准差:

print('Mean movieID 1:')
print(ratings[ratings['movieId']==1]['rating'].mean())
print('StdDev movieID 1:')
print(ratings[ratings['movieId']==1]['rating'].std())
print('Mean movieID:')
print(ratings[ratings['movieId']==2]['rating'].mean())
print('StdDev movieID 2:')
print(ratings[ratings['movieId']==2]['rating'].std())

我被退回了:

Mean movieID 1:
3.921240
StdDev movieID 1:
0.889012
Mean movieID 2:
3.211977
StdDev movieID 2:
0.951150

所以在我看来,groupby.std() 出于某种原因似乎跳过了第一个索引,将其替换为 NaN,然后​​填写正确的值,但移动了一个索引。我不理解这种行为,这不是我所期望的。谁能向我解释第二次使用 groupby 时的这种行为,以及如何避免 it/get 它做我想做的事?

问题不是在计算标准差时发生,而是在将结果分配给新列时发生StdDev。这是因为 pandas 隐含地按索引进行赋值。

下面的代码应该可以工作,因为两个 groupby 操作的结果都在 movieId 上建立了索引:

# note how I remove as_index=False
average_rating = ratings[['movieId','rating']].groupby('movieId').mean()
average_rating['StdDev'] = ratings[['movieId','rating']].groupby('movieId').std()

当然,你应该同时做这两件事:

ratings[['movieId','rating']].groupby('movieId').agg(['mean', 'std'])

更优雅(或至少更标准):

ratings.groupby('movieId')['rating'].agg(['mean', 'std'])

这里的关键是,在你的第一个groupby中,你包含了as_index=False,所以创建的df有一个新的序列索引。在您的 secong groupby 中,您不包含 as_index 参数,因此它使用 MovieID 作为索引。

当您随后将其分配为 average_ratings 中的列时,索引不是指同一事物。

在这种情况下,看起来您的索引已移动,因为您有 MovieID 1-5 和整数索引 0-4。 StdDev 列中的空值仅反映了没有 ID = 0 的电影这一事实。