【问题标题】:Frequency tables in pandas (like plyr in R)pandas 中的频率表(如 R 中的 plyr)
【发布时间】:2013-03-13 10:21:53
【问题描述】:

我的问题是如何计算 pandas 中多个变量的频率。 我有这个数据框:

d1 = pd.DataFrame( {'StudentID': ["x1", "x10", "x2","x3", "x4", "x5", "x6",   "x7",     "x8", "x9"],
                       'StudentGender' : ['F', 'M', 'F', 'M', 'F', 'M', 'F', 'M', 'M', 'M'],
                 'ExamenYear': ['2007','2007','2007','2008','2008','2008','2008','2009','2009','2009'],
                 'Exam': ['algebra', 'stats', 'bio', 'algebra', 'algebra', 'stats', 'stats', 'algebra', 'bio', 'bio'],
                 'Participated': ['no','yes','yes','yes','no','yes','yes','yes','yes','yes'],
                  'Passed': ['no','yes','yes','yes','no','yes','yes','yes','no','yes']},
                  columns = ['StudentID', 'StudentGender', 'ExamenYear', 'Exam', 'Participated', 'Passed'])

到下面的结果

             Participated  OfWhichpassed
 ExamenYear                             
2007                   3              2
2008                   4              3
2009                   3              2

(1) 我尝试的一种可能性是计算两个数据帧并绑定它们

t1 = d1.pivot_table(values = 'StudentID', rows=['ExamenYear'], cols = ['Participated'], aggfunc = len)
t2 = d1.pivot_table(values = 'StudentID', rows=['ExamenYear'], cols = ['Passed'], aggfunc = len)
tx = pd.concat([t1, t2] , axis = 1)

Res1 = tx['yes']

(2) 第二种可能性是使用聚合函数。

import collections
dg = d1.groupby('ExamenYear')
Res2 = dg.agg({'Participated': len,'Passed': lambda x : collections.Counter(x == 'yes')[True]})

 Res2.columns = ['Participated', 'OfWhichpassed']

至少可以说,这两种方式都很尴尬。 这是如何在 pandas 中正确完成的?

P.S:我也试过 value_counts 而不是 collections.Counter 但无法让它工作

供参考:几个月前,我向 R here 提出了类似的问题,plyr 可以提供帮助

---- 更新 ------

用户 DSM 是对的。所需的表格结果有误。

(1) 选项一的代码是

 t1 = d1.pivot_table(values = 'StudentID', rows=['ExamenYear'], aggfunc = len)
 t2 = d1.pivot_table(values = 'StudentID', rows=['ExamenYear'], cols = ['Participated'], aggfunc = len)
 t3 = d1.pivot_table(values = 'StudentID', rows=['ExamenYear'], cols = ['Passed'], aggfunc = len)

 Res1 = pd.DataFrame( {'All': t1,
                       'OfWhichParticipated': t2['yes'],
                     'OfWhichPassed': t3['yes']})

它会产生结果

             All  OfWhichParticipated  OfWhichPassed
ExamenYear                                         
2007          3                    2              2
2008          4                    3              3
2009          3                    3              2

(2) 对于选项 2,感谢用户 herrfz,我弄清楚了如何使用 value_count,代码将是

Res2 = d1.groupby('ExamenYear').agg({'StudentID': len,
                                 'Participated': lambda x: x.value_counts()['yes'],
                                 'Passed': lambda x: x.value_counts()['yes']})

Res2.columns = ['All', 'OfWgichParticipated', 'OfWhichPassed']

这将产生与 Res1 相同的结果

我的问题仍然存在:

使用选项 2,是否可以两次使用相同的变量(用于另一个操作?)可以为结果变量传递自定义名称吗?

----一个新的更新----

我终于决定使用 apply,我知道它更灵活。

【问题讨论】:

  • 我不确定我是否理解您的输出。看看 2007 年,似乎有两个学生 Participated=yes,但你想要的输出是“3”——即所有 2007 年的学生。那么您是否希望新的 Participated 列的值成为计数?
  • .. 实际上,你的 Res1Res2 不同意这一点,所以我不确定你是否也决定了。
  • 你是对的:我所说的“参与”实际上是 DataFrame 的长度(而不是参与==是)。没关系,我认为第二种解决方案看起来更有希望
  • 我在Q&A 中提供了几个详细的示例和替代方法,您或其他人可能会觉得有帮助。

标签: python group-by pandas


【解决方案1】:

我终于决定使用apply

我发布了我想出的东西,希望它对其他人有用。

根据我对 Wes 的《Python for Data analysis》一书的理解

  • apply 比 agg 和 transform 更灵活,因为您可以定义自己的函数。
  • 唯一的要求是函数返回 pandas 对象标量值
  • 内部机制:在每个分组对象 abd 上调用函数,结果使用 pandas.concat 粘合在一起
  • 最后需要“硬编码”你想要的结构

这是我想出来的

def ZahlOccurence_0(x):
      return pd.Series({'All': len(x['StudentID']),
                       'Part': sum(x['Participated'] == 'yes'),
                       'Pass' :  sum(x['Passed'] == 'yes')})

当我运行它时:

 d1.groupby('ExamenYear').apply(ZahlOccurence_0)

我得到了正确的结果

            All  Part  Pass
ExamenYear                 
2007          3     2     2
2008          4     3     3
2009          3     3     2

这种方法还可以让我将频率与其他统计数据结合起来

import numpy as np
d1['testValue'] = np.random.randn(len(d1))

def ZahlOccurence_1(x):
    return pd.Series({'All': len(x['StudentID']),
        'Part': sum(x['Participated'] == 'yes'),
        'Pass' :  sum(x['Passed'] == 'yes'),
        'test' : x['testValue'].mean()})


d1.groupby('ExamenYear').apply(ZahlOccurence_1)


            All  Part  Pass      test
ExamenYear                           
2007          3     2     2  0.358702
2008          4     3     3  1.004504
2009          3     3     2  0.521511

我希望其他人会觉得这很有用

【讨论】:

    【解决方案2】:

    您可以使用 pandas crosstab 函数,该函数默认计算两个或多个变量的频率表。例如,

    > import pandas as pd
    > pd.crosstab(d1['ExamenYear'], d1['Passed'])
    Passed      no  yes
    ExamenYear         
    2007         1    2
    2008         1    3
    2009         1    2
    

    如果您还想查看每行和每列的小计,请使用margins=True 选项。

    > pd.crosstab(d1['ExamenYear'], d1['Participated'], margins=True)
    Participated  no  yes  All
    ExamenYear                
    2007           1    2    3
    2008           1    3    4
    2009           0    3    3
    All            2    8   10
    

    【讨论】:

      【解决方案3】:

      这个:

      d1.groupby('ExamenYear').agg({'Participated': len, 
                                    'Passed': lambda x: sum(x == 'yes')})
      

      看起来并不比 R 解决方案更尴尬,恕我直言。

      【讨论】:

      • 谢谢。这是一个改进。是否有可能将自定义名称传递给结果列(例如 OfWhichpassed)。显然你可以将自定义名称的元组传递给 agg ('customname', 'nameoffunction') 但它可以在这里工作吗?
      【解决方案4】:

      我喜欢用另一种方法来解决类似的问题,它使用groupbyunstack

      d1 = pd.DataFrame({'StudentID': ["x1", "x10", "x2","x3", "x4", "x5", "x6",   "x7",     "x8", "x9"],
                         'StudentGender' : ['F', 'M', 'F', 'M', 'F', 'M', 'F', 'M', 'M', 'M'],
                         'ExamenYear': ['2007','2007','2007','2008','2008','2008','2008','2009','2009','2009'],
                         'Exam': ['algebra', 'stats', 'bio', 'algebra', 'algebra', 'stats', 'stats', 'algebra', 'bio', 'bio'],
                         'Participated': ['no','yes','yes','yes','no','yes','yes','yes','yes','yes'],
                         'Passed': ['no','yes','yes','yes','no','yes','yes','yes','no','yes']},
                        columns = ['StudentID', 'StudentGender', 'ExamenYear', 'Exam', 'Participated', 'Passed'])
      

      (这只是上面的原始数据)

      d2 = d1.groupby("ExamenYear").Participated.value_counts().unstack(fill_value=0)['yes']
      d3 = d1.groupby("ExamenYear").Passed.value_counts().unstack(fill_value=0)['yes']
      d2.name = "Participated"
      d3.name = "Passed"
      
      pd.DataFrame(data=[d2,d3]).T
                  Participated  Passed
      ExamenYear                      
      2007                   2       2
      2008                   3       3
      2009                   3       2
      

      这个解决方案比上面使用apply的解决方案稍微麻烦一些,但我觉得这个更容易理解和扩展。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-12-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多