Pandas按零值分组

46

我有一个csv文件,其中包含以下数据

Symbol  Action  Year
  AAPL     Buy  2001
  AAPL     Buy  2001
   BAC    Sell  2002
   BAC    Sell  2002
我能够像这样阅读它并按照分组进行。
df.groupby(['Symbol','Year']).count()

我明白了。

             Action
Symbol Year        
AAPL   2001       2
BAC    2002       2

我希望这个(顺序无关紧要)

             Action
Symbol Year        
AAPL   2001       2
AAPL   2002       0
BAC    2001       0
BAC    2002       2

我想知道是否可能计算0次出现

6个回答

50

你可以使用这个:

df = df.groupby(['Symbol','Year']).count().unstack(fill_value=0).stack()
print (df)

输出:

             Action
Symbol Year        
AAPL   2001       2
       2002       0
BAC    2001       0
       2002       2

1
这是一个不错的解决方案!优雅且直观,比使用 pivot_table 更好,除非后者有任何优势或特定用例。你知道有什么吗? - avg
6
这是否仅适用于一个分组对象?看起来它似乎无法正常工作,并且出现了AttributeError: 'Series'对象没有'stack'属性的错误。 - haneulkim

26

您可以使用pivot_tableunstack

print df.pivot_table(index='Symbol', 
                     columns='Year', 
                     values='Action',
                     fill_value=0, 
                     aggfunc='count').unstack()

Year  Symbol
2001  AAPL      2
      BAC       0
2002  AAPL      0
      BAC       2
dtype: int64

如果需要输出为DataFrame,请使用to_frame

print df.pivot_table(index='Symbol', 
                     columns='Year', 
                     values='Action',
                     fill_value=0, 
                     aggfunc='count').unstack()
                                     .to_frame()
                                     .rename(columns={0:'Action'})

             Action
Year Symbol        
2001 AAPL         2
     BAC          0
2002 AAPL         0
     BAC          2

这个制作了一个漂亮的数据透视表,但是使用fill_value = 0仍然不会显示出计数为0的行。我认为fill_value只用于具有缺失数据或NaN的行? - ale19
是的,参数fill_value将NaN替换为0。 - jezrael

6

数据类型类别

也许在这个主题开启时,这个功能还不存在,然而数据类型"类别"可以帮助解决问题:

# create a dataframe with one combination of a,b missing
df = pd.DataFrame({"a":[0,1,1], "b": [0,1,0]})
df = df.astype({"a":"category", "b":"category"})
print(df)

数据框长这样:

   a  b
0  0  0
1  1  1
2  1  0

现在,按a和b分组

print(df.groupby(["a","b"]).size())

产生:
a  b
0  0    1
   1    0
1  0    1
   1    1

请注意最右侧列中的0。此行为也在pandas用户指南(在页面上搜索“groupby”)中有记录。

2
我在编程中遇到了这种情况,不需要零! - Mithril
2
@Mithril 如果你的意思是你有一个分类列,而且 .groupby 给出了所有可能的组合,但你只想要观察到的组合,那么你需要使用 groupby(..., observed=True)。这在这里有文档记录:https://pandas.pydata.org/pandas-docs/stable/user_guide/groupby.html#handling-of-un-observed-categorical-values - zmbc
我希望得到所有分类列的组合,但不包括非分类列。我认为这会给出所有列的组合,只是因为其中一列是分类列。 - Denziloe

0

步骤1:创建一个数据框,存储列计数中每个非零类的计数。

count_df = df.groupby(['Symbol','Year']).size().reset_index(name='counts')

步骤2:现在使用 pivot_table 获取所需的数据框,其中包括现有和不存在的类别的计数。
df_final = pd.pivot_table(count_df,
                       index=['Symbol','Year'],
                       values='counts',                            
                       fill_value = 0,
                       dropna=False,
                       aggfunc=np.sum)

现在可以使用命令将计数的值提取为列表

list(df_final['counts'])

0

以上所有的答案都集中在groupby或者透视表上。然而,正如这篇文章这个问题中所描述的那样,这是pandas的crosstab函数的一个很好的应用场景:

import pandas as pd
df = pd.DataFrame({
    "Symbol": 2*['AAPL', 'BAC'],
    "Action": 2*['Buy', 'Sell'],
    "Year": 2*[2001,2002]
})

pd.crosstab(df["Symbol"], df["Year"]).stack()

产出:

Symbol  Year
AAPL    2001    2
        2002    0
BAC     2001    0
        2002    2

如果年数与股票代码的数量不匹配,该怎么办? - Björn Lindqvist

0
如果您不想使用 pivot_table,可以尝试以下方法:
midx = pd.MultiIndex.from_product([ df['Symbol'].unique(), df['Year'].unique()], names=['Symbol', 'Year'])
df_grouped_by = df_grouped_by.reindex(midx, fill_value=0)

我们上面实际上所做的是创建一个多索引,将两列的所有可能值相乘,然后使用该多索引将零填充到我们的分组数据框中。

这会将所有计数都设置为零,而不是那些在数据中不存在的计数。 - KLaz

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