• 10.4 透视表和交叉表
    • 交叉表:crosstab

    10.4 透视表和交叉表

    透视表(pivot table)是各种电子表格程序和其他数据分析软件中一种常见的数据汇总工具。它根据一个或多个键对数据进行聚合,并根据行和列上的分组键将数据分配到各个矩形区域中。在Python和pandas中,可以通过本章所介绍的groupby功能以及(能够利用层次化索引的)重塑运算制作透视表。DataFrame有一个pivot_table方法,此外还有一个顶级的pandas.pivot_table函数。除能为groupby提供便利之外,pivot_table还可以添加分项小计,也叫做margins。

    回到小费数据集,假设我想要根据day和smoker计算分组平均数(pivot_table的默认聚合类型),并将day和smoker放到行上:

    1. In [130]: tips.pivot_table(index=['day', 'smoker'])
    2. Out[130]:
    3. size tip tip_pct total_bill
    4. day smoker
    5. Fri No 2.250000 2.812500 0.151650 18.420000
    6. Yes 2.066667 2.714000 0.174783 16.813333
    7. Sat No 2.555556 3.102889 0.158048 19.661778
    8. Yes 2.476190 2.875476 0.147906 21.276667
    9. Sun No 2.929825 3.167895 0.160113 20.506667
    10. Yes 2.578947 3.516842 0.187250 24.120000
    11. Thur No 2.488889 2.673778 0.160298 17.113111
    12. Yes 2.352941 3.030000 0.163863 19.190588

    可以用groupby直接来做。现在,假设我们只想聚合tip_pct和size,而且想根据time进行分组。我将smoker放到列上,把day放到行上:

    1. In [131]: tips.pivot_table(['tip_pct', 'size'], index=['time', 'day'],
    2. .....: columns='smoker')
    3. Out[131]:
    4. size tip_pct
    5. smoker No Yes No Yes
    6. time day
    7. Dinner Fri 2.000000 2.222222 0.139622 0.165347
    8. Sat 2.555556 2.476190 0.158048 0.147906
    9. Sun 2.929825 2.578947 0.160113 0.187250
    10. Thur 2.000000 NaN 0.159744 NaN
    11. Lunch Fri 3.000000 1.833333 0.187735 0.188937
    12. Thur 2.500000 2.352941 0.160311 0.163863

    还可以对这个表作进一步的处理,传入margins=True添加分项小计。这将会添加标签为All的行和列,其值对应于单个等级中所有数据的分组统计:

    1. In [132]: tips.pivot_table(['tip_pct', 'size'], index=['time', 'day'],
    2. .....: columns='smoker', margins=True)
    3. Out[132]:
    4. size tip_pct
    5. smoker No Yes All No Yes All
    6. time day
    7. Dinner Fri 2.000000 2.222222 2.166667 0.139622 0.165347 0.158916
    8. Sat 2.555556 2.476190 2.517241 0.158048 0.147906 0.153152
    9. Sun 2.929825 2.578947 2.842105 0.160113 0.187250 0.166897
    10. Thur 2.000000 NaN 2.000000 0.159744 NaN 0.159744
    11. Lunch Fri 3.000000 1.833333 2.000000 0.187735 0.188937 0.188765
    12. Thur 2.500000 2.352941 2.459016 0.160311 0.163863 0.161301
    13. All 2.668874 2.408602 2.569672 0.159328 0.163196 0.160803

    这里,All值为平均数:不单独考虑烟民与非烟民(All列),不单独考虑行分组两个级别中的任何单项(All行)。

    要使用其他的聚合函数,将其传给aggfunc即可。例如,使用count或len可以得到有关分组大小的交叉表(计数或频率):

    1. In [133]: tips.pivot_table('tip_pct', index=['time', 'smoker'], columns='day',
    2. .....: aggfunc=len, margins=True)
    3. Out[133]:
    4. day Fri Sat Sun Thur All
    5. time smoker
    6. Dinner No 3.0 45.0 57.0 1.0 106.0
    7. Yes 9.0 42.0 19.0 NaN 70.0
    8. Lunch No 1.0 NaN NaN 44.0 45.0
    9. Yes 6.0 NaN NaN 17.0 23.0
    10. All 19.0 87.0 76.0 62.0 244.0

    如果存在空的组合(也就是NA),你可能会希望设置一个fill_value:

    1. In [134]: tips.pivot_table('tip_pct', index=['time', 'size', 'smoker'],
    2. .....: columns='day', aggfunc='mean', fill_value=0)
    3. Out[134]:
    4. day Fri Sat Sun Thur
    5. time size smoker
    6. Dinner 1 No 0.000000 0.137931 0.000000 0.000000
    7. Yes 0.000000 0.325733 0.000000 0.000000
    8. 2 No 0.139622 0.162705 0.168859 0.159744
    9. Yes 0.171297 0.148668 0.207893 0.000000
    10. 3 No 0.000000 0.154661 0.152663 0.000000
    11. Yes 0.000000 0.144995 0.152660 0.000000
    12. 4 No 0.000000 0.150096 0.148143 0.000000
    13. Yes 0.117750 0.124515 0.193370 0.000000
    14. 5 No 0.000000 0.000000 0.206928 0.000000
    15. Yes 0.000000 0.106572 0.065660 0.000000
    16. ... ... ... ... ...
    17. Lunch 1 No 0.000000 0.000000 0.000000 0.181728
    18. Yes 0.223776 0.000000 0.000000 0.000000
    19. 2 No 0.000000 0.000000 0.000000 0.166005
    20. Yes 0.181969 0.000000 0.000000 0.158843
    21. 3 No 0.187735 0.000000 0.000000 0.084246
    22. Yes 0.000000 0.000000 0.000000 0.204952
    23. 4 No 0.000000 0.000000 0.000000 0.138919
    24. Yes 0.000000 0.000000 0.000000 0.155410
    25. 5 No 0.000000 0.000000 0.000000 0.121389
    26. 6 No 0.000000 0.000000 0.000000 0.173706
    27. [21 rows x 4 columns]

    pivot_table的参数说明请参见表10-2。

    表10-2 pivot_table的选项

    交叉表:crosstab

    交叉表(cross-tabulation,简称crosstab)是一种用于计算分组频率的特殊透视表。看下面的例子:

    1. In [138]: data
    2. Out[138]:
    3. Sample Nationality Handedness
    4. 0 1 USA Right-handed
    5. 1 2 Japan Left-handed
    6. 2 3 USA Right-handed
    7. 3 4 Japan Right-handed
    8. 4 5 Japan Left-handed
    9. 5 6 Japan Right-handed
    10. 6 7 USA Right-handed
    11. 7 8 USA Left-handed
    12. 8 9 Japan Right-handed
    13. 9 10 USA Right-handed

    作为调查分析的一部分,我们可能想要根据国籍和用手习惯对这段数据进行统计汇总。虽然可以用pivot_table实现该功能,但是pandas.crosstab函数会更方便:

    1. In [139]: pd.crosstab(data.Nationality, data.Handedness, margins=True)
    2. Out[139]:
    3. Handedness Left-handed Right-handed All
    4. Nationality
    5. Japan 2 3 5
    6. USA 1 4 5
    7. All 3 7 10

    crosstab的前两个参数可以是数组或Series,或是数组列表。就像小费数据:

    1. In [140]: pd.crosstab([tips.time, tips.day], tips.smoker, margins=True)
    2. Out[140]:
    3. smoker No Yes All
    4. time day
    5. Dinner Fri 3 9 12
    6. Sat 45 42 87
    7. Sun 57 19 76
    8. Thur 1 0 1
    9. Lunch Fri 1 6 7
    10. Thur 44 17 61
    11. All 151 93 244