NA 值的 pandas' 损坏的交叉表的干净替代品
Clean alternative to pandas' broken crosstab for NA values
我正在尝试获得一个类似于 R 的 table 函数的函数,参数 useNA
允许我在交叉 table 中包含 NA 值。
这是一个小例子:
df = pd.DataFrame({"a": [0, 1, pd.NA, pd.NA], "b":[2, pd.NA, 3, pd.NA]})
print(pd.crosstab(df["a"], df["b"], dropna=False)
我从中得到的是
b 2 3
a
0 1 0
但我希望它像
b 2 3 NA
a
0 1 0 0
1 0 0 1
NA 0 1 1
这不仅忽略了数据框中四分之三的行,结果还取决于插入两个系列的顺序,这里是 pd.crosstab(df["b"], df["a"], dropna=False)
:
a 0 1
b
2 1 0
我能想到的解决方法是查看两个系列中的唯一值并创建一个它们都不在其中的新值,然后使用它暂时用它替换 NA 值 fillna
,但这感觉很糟糕,如果没有什么东西可以完全符合我的要求,我会感到惊讶。
此外,如果两个系列之一没有缺失数据,该解决方案将无法按预期工作。
编辑:添加示例来说明最后一部分。
df = pd.DataFrame({"a": [0, 1, 2, 3], "b":[2, pd.NA, 3, pd.NA]})
print(pd.crosstab(df["a"].fillna("NA"), df["b"].fillna("NA"), dropna=False)
输出:
b 2 3 NA
a
0 1 0 0
1 0 0 1
2 0 1 0
3 0 0 1
预计:
b 2 3 NA
a
0 1 0 0
1 0 0 1
2 0 1 0
3 0 0 1
NA 0 0 0
您可以将缺失值替换为 NA
:
print(pd.crosstab(t["a"].fillna('NA'), t["b"].fillna('NA')))
b 2 3 NA
a
0 1 0 0
1 0 0 1
NA 0 1 1
编辑:添加由 NA
填充的新行,然后从交集 NA, NA
:
中减去 1
t = pd.DataFrame({"a": [0, 1, 2, 3], "b":[2, pd.NA, 3, pd.NA]})
df = t.append(pd.DataFrame('NA', index=[-1], columns=t.columns)).fillna('NA')
df = pd.crosstab(df["a"], df["b"])
df.loc['NA','NA'] -= 1
print(df)
b 2 3 NA
a
0 1 0 0
1 0 0 1
2 0 1 0
3 0 0 1
NA 0 0 0
使用 groupby.size
+ stack
可以使用:
t = pd.DataFrame({"a": [0, 1, 2, 3], "b":[2, pd.NA, 3, pd.NA]})
df = t.append(pd.DataFrame(np.nan, index=[-1], columns=t.columns))
df = df.groupby(['a', 'b'], dropna = False).size().unstack(fill_value=0)
df.loc[np.nan,np.nan] -= 1
print(df)
b 2.0 3.0 NaN
a
0.0 1 0 0
1.0 0 0 1
2.0 0 1 0
3.0 0 0 1
NaN 0 0 0
crosstab
是一个方便的选项,包裹在 pd.pivot_table 周围;您可以直接转到 groupby(pd.pivot_table 是 groupby 的包装器)并复制您的输出:
df.groupby(['a', 'b'], dropna = False).size().unstack(fill_value=0)
b 2.0 3.0 NaN
a
0.0 1 0 0
1.0 0 0 1
NaN 0 1 1
如果您可以在 pandas 上进行 PR 以改进交叉表功能
,这也会有所帮助
我正在尝试获得一个类似于 R 的 table 函数的函数,参数 useNA
允许我在交叉 table 中包含 NA 值。
这是一个小例子:
df = pd.DataFrame({"a": [0, 1, pd.NA, pd.NA], "b":[2, pd.NA, 3, pd.NA]})
print(pd.crosstab(df["a"], df["b"], dropna=False)
我从中得到的是
b 2 3
a
0 1 0
但我希望它像
b 2 3 NA
a
0 1 0 0
1 0 0 1
NA 0 1 1
这不仅忽略了数据框中四分之三的行,结果还取决于插入两个系列的顺序,这里是 pd.crosstab(df["b"], df["a"], dropna=False)
:
a 0 1
b
2 1 0
我能想到的解决方法是查看两个系列中的唯一值并创建一个它们都不在其中的新值,然后使用它暂时用它替换 NA 值 fillna
,但这感觉很糟糕,如果没有什么东西可以完全符合我的要求,我会感到惊讶。
此外,如果两个系列之一没有缺失数据,该解决方案将无法按预期工作。
编辑:添加示例来说明最后一部分。
df = pd.DataFrame({"a": [0, 1, 2, 3], "b":[2, pd.NA, 3, pd.NA]})
print(pd.crosstab(df["a"].fillna("NA"), df["b"].fillna("NA"), dropna=False)
输出:
b 2 3 NA
a
0 1 0 0
1 0 0 1
2 0 1 0
3 0 0 1
预计:
b 2 3 NA
a
0 1 0 0
1 0 0 1
2 0 1 0
3 0 0 1
NA 0 0 0
您可以将缺失值替换为 NA
:
print(pd.crosstab(t["a"].fillna('NA'), t["b"].fillna('NA')))
b 2 3 NA
a
0 1 0 0
1 0 0 1
NA 0 1 1
编辑:添加由 NA
填充的新行,然后从交集 NA, NA
:
1
t = pd.DataFrame({"a": [0, 1, 2, 3], "b":[2, pd.NA, 3, pd.NA]})
df = t.append(pd.DataFrame('NA', index=[-1], columns=t.columns)).fillna('NA')
df = pd.crosstab(df["a"], df["b"])
df.loc['NA','NA'] -= 1
print(df)
b 2 3 NA
a
0 1 0 0
1 0 0 1
2 0 1 0
3 0 0 1
NA 0 0 0
使用 groupby.size
+ stack
可以使用:
t = pd.DataFrame({"a": [0, 1, 2, 3], "b":[2, pd.NA, 3, pd.NA]})
df = t.append(pd.DataFrame(np.nan, index=[-1], columns=t.columns))
df = df.groupby(['a', 'b'], dropna = False).size().unstack(fill_value=0)
df.loc[np.nan,np.nan] -= 1
print(df)
b 2.0 3.0 NaN
a
0.0 1 0 0
1.0 0 0 1
2.0 0 1 0
3.0 0 0 1
NaN 0 0 0
crosstab
是一个方便的选项,包裹在 pd.pivot_table 周围;您可以直接转到 groupby(pd.pivot_table 是 groupby 的包装器)并复制您的输出:
df.groupby(['a', 'b'], dropna = False).size().unstack(fill_value=0)
b 2.0 3.0 NaN
a
0.0 1 0 0
1.0 0 0 1
NaN 0 1 1
如果您可以在 pandas 上进行 PR 以改进交叉表功能
,这也会有所帮助