如何在 Pandas 中使用 groupby().transform() 转换为 value_counts()?

8

我正在处理价格数据的pandas dataframe df1

  Item    Price  Minimum Most_Common_Price
0 Coffee  1      1       2
1 Coffee  2      1       2
2 Coffee  2      1       2
3 Tea     3      3       4
4 Tea     4      3       4
5 Tea     4      3       4

我使用以下技术创建了Minimum

df1["Minimum"] = df1.groupby(["Item"])['Price'].transform(min)

我该如何创建“Most_Common_Price”?
df1["Minimum"] = df1.groupby(["Item"])['Price'].transform(value_counts()) # Doesn't work

目前,我采用多步骤的方法:

for item in df1.Item.unique().tolist(): # Pseudocode
 df1 = df1[df1.Price == Item]           # Pseudocode
 df1.Price.value_counts().max()         # Pseudocode

这有点过度设计了。理想情况下,应该有一种更简单的方法,最好只用一行代码。

如何在pandas中将groupby().transform()转换为value_counts()?

3个回答

13

您可以使用groupby + transform结合value_countsidxmax方法。

df['Most_Common_Price'] = (
    df.groupby('Item')['Price'].transform(lambda x: x.value_counts().idxmax()))

df

     Item  Price  Minimum  Most_Common_Price
0  Coffee      1        1                  2
1  Coffee      2        1                  2
2  Coffee      2        1                  2
3     Tea      3        3                  4
4     Tea      4        3                  4
5     Tea      4        3                  4

一种改进方法涉及使用 pd.Series.map

# Thanks, Vaishali!
df['Item'] = (df['Item'].map(df.groupby('Item')['Price']
                        .agg(lambda x: x.value_counts().idxmax()))
df

     Item  Price  Minimum  Most_Common_Price
0  Coffee      1        1                  2
1  Coffee      2        1                  2
2  Coffee      2        1                  2
3     Tea      3        3                  4
4     Tea      4        3                  4
5     Tea      4        3                  4

1
@sudonym 注意,这种方法也适用于对象 :-) - BENY
1
@Wen 谢谢,这是我没有考虑到的重要因素! - cs95
3
例如,如果“Price”是一个字符串列,您想找到每个组中计数最高的字符串,那么这个方法仍然可以使用。而“mode”仅适用于数字。 - cs95
1
@sudonym pandas 对象的类型与 numpy 类似,它们可以包含原始类型的数组,例如 numpy.int64,或者使用 dtype=object,这种情况下,它们可以包含 任何 Python 对象。请注意,通常会以效率为代价。 - juanpa.arrivillaga
1
使用map而不是transform,性能会进一步提高。df ['Item'] .map(df.groupby('Item').Price.agg(lambda x:x.value_counts()。idxmax())) - Vaishali
显示剩余4条评论

10
一种不错的方法是使用 pd.Series.mode,如果你想要找到最常见的元素(即众数)。
In [32]: df
Out[32]:
     Item  Price  Minimum
0  Coffee      1        1
1  Coffee      2        1
2  Coffee      2        1
3     Tea      3        3
4     Tea      4        3
5     Tea      4        3

In [33]: df['Most_Common_Price'] = df.groupby(["Item"])['Price'].transform(pd.Series.mode)

In [34]: df
Out[34]:
     Item  Price  Minimum  Most_Common_Price
0  Coffee      1        1                  2
1  Coffee      2        1                  2
2  Coffee      2        1                  2
3     Tea      3        3                  4
4     Tea      4        3                  4
5     Tea      4        3                  4

正如@Wen所指出的那样,pd.Series.mode可以返回一个值的pd.Series,因此只需获取第一个即可:

Out[67]:
     Item  Price  Minimum
0  Coffee      1        1
1  Coffee      2        1
2  Coffee      2        1
3     Tea      3        3
4     Tea      4        3
5     Tea      4        3
6     Tea      3        3

In [68]: df[df.Item =='Tea'].Price.mode()
Out[68]:
0    3
1    4
dtype: int64

In [69]: df['Most_Common_Price'] = df.groupby(["Item"])['Price'].transform(lambda S: S.mode()[0])

In [70]: df
Out[70]:
     Item  Price  Minimum  Most_Common_Price
0  Coffee      1        1                  2
1  Coffee      2        1                  2
2  Coffee      2        1                  2
3     Tea      3        3                  3
4     Tea      4        3                  3
5     Tea      4        3                  3
6     Tea      3        3                  3

5
如果有两个相同的,进行轻微更改df.groupby(["Item"])['Price'].transform(lambda x : x.mode()[0]),以防止重复。 - BENY
2
在你的第一个解决方案中,pandas是否改变了评估方式?在我的机器上,df.groupby(["Item"])['Price'].transform(pd.Series.mode)返回ValueError: Length of passed values is 1, index implies 3 - 00schneider
1
@00schneider:那是因为您的情况下应用了两个或更多值的模式; 使用BENY的建议。 - MERose

0

    #Initial dataframe having Milk as Nan value to produce the scenario if we have any group nan value 
    data_stack_try = [['Coffee',1],['Coffee',2],['Coffee',2],['Tea',3],['Tea',4],['Tea',4],['Milk', np.nan]]
    df_stack_try = pd.DataFrame(data_stack_try, columns=["Item","Price"])
    print("---Before Min---")
    print(df_stack_try)
    #Created Minimum column with transform function with 'min'
    df_stack_try["Minimum"] = df_stack_try.groupby(["Item"])['Price'].transform(min)
    print("---After Min----")
    print(df_stack_try)

    #Function written to take care of null values (Milk item is np.nan)
    def mode_group(grp):
        try:
            #return mode of each group passed for each row
            return grp.mode()[0]
        except BaseException as e:
            # This exception will be raised if there is no mode value 
            # In this case it will appear for Milk value as because of nan, it can't have mode value
            print("Exception!!!")
    df_stack_try["Most_Common_Price"] = df_stack_try.groupby('Item')['Price'].transform(lambda x: mode_group(x))
    print("---After Mode----")
    print(df_stack_try)


---Before Min---
     Item  Price
0  Coffee    1.0
1  Coffee    2.0
2  Coffee    2.0
3     Tea    3.0
4     Tea    4.0
5     Tea    4.0
6    Milk    NaN
---After Min----
     Item  Price  Minimum
0  Coffee    1.0      1.0
1  Coffee    2.0      1.0
2  Coffee    2.0      1.0
3     Tea    3.0      3.0
4     Tea    4.0      3.0
5     Tea    4.0      3.0
6    Milk    NaN      NaN
Exception!!!
---After Mode----
     Item  Price  Minimum  Most_Common_Price
0  Coffee    1.0      1.0                2.0
1  Coffee    2.0      1.0                2.0
2  Coffee    2.0      1.0                2.0
3     Tea    3.0      3.0                4.0
4     Tea    4.0      3.0                4.0
5     Tea    4.0      3.0                4.0
6    Milk    NaN      NaN                NaN

请为提供的代码添加文本描述。 - Julia Meshcheryakova

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