Pandas crosstab() 函数与包含 NaN 值的数据框的混淆行为

Posted

技术标签:

【中文标题】Pandas crosstab() 函数与包含 NaN 值的数据框的混淆行为【英文标题】:Confusing behaviour of Pandas crosstab() function with dataframe containing NaN values 【发布时间】:2016-01-23 01:08:29 【问题描述】:

我使用 Python 3.4.1 和 numpy 0.10.1 和 pandas 0.17.0。我有一个大型数据框,列出了个体动物的物种和性别。这是一个真实世界的数据集,不可避免地存在由 NaN 表示的缺失值。数据的简化版本可以生成为:

import numpy as np
import pandas as pd
tempDF = pd.DataFrame( 'id': [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],
                        'species': ["dog","dog",np.nan,"dog","dog","cat","cat","cat","dog","cat","cat","dog","dog","dog","dog",np.nan,"cat","cat","dog","dog"],
                        'gender': ["male","female","female","male","male","female","female",np.nan,"male","male","female","male","female","female","male","female","male","female",np.nan,"male"])

打印数据框给出:

    gender  id species
0     male   1     dog
1   female   2     dog
2   female   3     NaN
3     male   4     dog
4     male   5     dog
5   female   6     cat
6   female   7     cat
7      NaN   8     cat
8     male   9     dog
9     male  10     cat
10  female  11     cat
11    male  12     dog
12  female  13     dog
13  female  14     dog
14    male  15     dog
15  female  16     NaN
16    male  17     cat
17  female  18     cat
18     NaN  19     dog
19    male  20     dog

我想生成一个交叉表来显示每个物种中雄性和雌性的数量,使用以下内容:

pd.crosstab(tempDF['species'],tempDF['gender'])

这会产生下表:

gender   female  male
species              
cat           4     2
dog           3     7

这是我所期望的。但是,如果我包含 margins=True 选项,它会产生:

pd.crosstab(tempDF['species'],tempDF['gender'],margins=True)

gender   female  male  All
species                   
cat           4     2    7
dog           3     7   11
All           9     9   20

如您所见,边际总数似乎不正确,可能是由于数据框中的数据缺失造成的。这是预期的行为吗?在我看来,这似乎很混乱。当然,边际总计应该是表格中出现的行和列的总计,并且不包括表格中未表示的任何缺失数据。包括 dropna=False 不会影响结果。

我可以在创建表之前删除任何带有 NaN 的行,但这似乎需要做很多额外的工作,并且在进行分析时需要考虑很多额外的事情。我应该将此报告为错误吗?

【问题讨论】:

也许用 df.dropna() 创建第二个数据帧,然后在这个新数据帧上调用交叉表? 我同意这是一个选项,但它为应该是一个非常简单的过程增加了一层额外的复杂性。而且(如果数据框中有很多其他变量,每个变量都有 NaN),这可能意味着为您想要生成的每个交叉表生成大量新的数据框。 【参考方案1】:

我想一种解决方法是在创建表之前将 NaN 转换为“缺失”,然后交叉管将包括专门针对缺失值的列和行:

pd.crosstab(tempDF['species'].fillna('missing'),tempDF['gender'].fillna('missing'),margins=True)

gender   female  male  missing  All
species                            
cat           4     2        1    7
dog           3     7        1   11
missing       2     0        0    2
All           9     9        2   20

就个人而言,我希望看到默认行为,这样我就不必记住在每个交叉表计算中替换所有 NaN。

【讨论】:

【参考方案2】:

您不是唯一遇到这种情况的人。 它不仅发生在 pd.crosstab 上,还发生在 pd.pivot_table 和 DataFrame.groupby 上

在文档中,它说关于 groupby 不包括 Na's:

GroupBy 中的 NA 组会被自动排除。这种行为是 例如,与 R 一致。

您可以在这篇文章中找到一些好的解决方案: groupby columns with NaN (missing) values

也许有一天会有人解决这个问题:https://github.com/pandas-dev/pandas/issues/10772

【讨论】:

以上是关于Pandas crosstab() 函数与包含 NaN 值的数据框的混淆行为的主要内容,如果未能解决你的问题,请参考以下文章

Python使用pandas的crosstab函数计算混淆矩阵并使用Seaborn可视化混淆矩阵实战

pandas pivot_table透视表crosstab交叉表aggfunc函数详解及实战

Pandas:透视表(pivotTab)和交叉表(crossTab)

使用 CrossTab Pandas 获得的索引 DataFrame [重复]

2018.03.29 python-pandas 数据透视pivot table / 交叉表crosstab

NA 值的干净替代 pandas 的损坏交叉表