pandas groupby 中的 NaN 项目后跟包括类别列预期行为的聚合吗?
Are NaN items in pandas groupby followed by aggregation including category column expected behavior?
我聚合了一个包含类别列的 pandas DataFrame。输出包含几个我没有预料到且不理解的 NaN。示例代码:
import pandas as pd
d = pd.DataFrame({'a': [1, 1, 2, 2], 'b': [1, 1, 2, 2], 'c': [1, 2, 3, 4]})
d.astype({'b': 'category'}).groupby('a').agg({'b':'first', 'c':['mean', 'max']})
我得到以下输出:
b c
first mean max
0 1 NaN NaN
1 2 1.5 2.0
2 NaN 3.5 4.0
我本应得到以下输出:
b c
first mean max
a
1 1 1.5 2
2 2 3.5 4
有人可以解释 NaN 吗?
以下两种代码变体,一种没有类别列,一种没有 c 列的多个聚合,给出了预期的输出。
d.groupby('a').agg({'b':'first', 'c':['mean', 'max']})
d.astype({'b': 'category'}).groupby('a').agg({'b':'first', 'c':'mean'})
我正在使用 pandas 0.25.2 和 python 3.7.4。
我解释的出发点是有(和比较行为)
两个个数据帧:
d
- 原始 DataFrame,
d2 = d.astype({'b': 'category'})
- 兄弟 DataFrame,
b 列更改为 分类.
创建这两个 DataFrame 的分组:
gr = d.groupby('a')
gr2 = d2.groupby('a')
目前它们看起来完全相同,例如如果你 运行:
for key, grp in gr:
print(f'\nGroup: {key}\n{grp}')
和 gr2 的相同代码,您将得到 完全 相同的结果。
但是当你尝试
聚合 b 列。
当你 运行 gr.b.agg('first')
(在原始 DataFrame 上)时,你将得到:
a
1 1
2 2
Name: b, dtype: int64
结果是 系列 并且正如 d
内容所预期的那样:
- 对于 a == 1 的行组,b 的第一个值是 1,
- 对于 a == 2 的行组,b 的第一个值是 2.
但是如果你 运行 gr2.b.agg('first')
(在兄弟 DataFrame 上),你将得到:
[1, 2]
Categories (2, int64): [1, 2]
结果为pandas.core.arrays.categorical.Categorical类型
更重要的是,与分组键没有关系。
相反,类似的分组键是连续数字
(从 0 开始)。
你可以确认一下,运行宁gr2.agg({'b':'first'})
你会得到:
b
0 1
1 2
这就是您问题的根源。
当你 运行:
gr2.agg({'b':'first', 'c':['mean', 'max']})
合并分类和"normal"列,然后:
- 以上人工分组键产生自
b,
的聚合
- 与 actual 分组键结合 c.
对于(人工)分组键0:
- b / first 的值为 1(见上文),
- for c / mean 和 c / max 没有值,所以
结果包含 2 NaNs.
用于分组键1:
- for artificial key, b / first 的值为 2,
- 对于 true 键,c / mean 和 c / max 的值是
1.5 和 2.0,
- 但 Pandas 将它们全部放在 相同的 行中。
用于分组键2:
- 没有"artificial"具有此值的分组键,
所以在 b / first 列中有 NaN,
- 对于c / mean和c / max有是对应的值,
所以它们被打印在那里。
结论:这种情况下,转换不是一个好主意
分类类型的任何列。
仅使用 原始 DataFrame,具有 "original"(非分类)类型
你会得到你预期的结果。
从 09:14:38Z
开始编辑以下评论
你在 post 和
提到的评论。
您的 post 包含写入:
I get the following output:
b c
first mean max
0 1 NaN NaN
1 2 1.5 2.0
2 NaN 3.5 4.0
所以打印输出确实包含NaN值。
但是在你写的评论中 我没有得到任何 NaN。
为了验证这个矛盾,我又做了一次测试:
d = pd.DataFrame({'a': [1, 1, 2, 2], 'b': [1, 1, 2, 2], 'c': [1, 2, 3, 4]})
d2 = d.astype({'b': 'category'})
d2.groupby('a').agg({'b':'first', 'c':['mean', 'max']})
并得到:
b c
first mean max
0 1 NaN NaN
1 2 1.5 2.0
2 NaN 3.5 4.0
就像您的 post 和 NaN 值一样。
我认为 bug, but possible solution is use function with Series.iat
for first value of group, because Series.first
工作方式不同:
Convenience method for subsetting initial periods of time series data based on a date offset.
def first(x):
return x.iat[0]
d = pd.DataFrame({'a': [1, 1, 2, 2], 'b': [1, 1, 2, 2], 'c': [1, 2, 3, 4]})
d = d.astype({'b': 'category'}).groupby('a').agg({'b':first, 'c':['mean', 'max']})
print(d)
b c
first mean max
a
1 1 1.5 2
2 2 3.5 4
如果使用 lambda 函数,则为 MultiIndex
的第二级获取 <lambda>
字符串:
d = d.astype({'b': 'category'}).groupby('a').agg({'b':lambda x: x.iat[0], 'c':['mean', 'max']})
print(d)
b c
<lambda> mean max
a
1 1 1.5 2
2 2 3.5 4
我聚合了一个包含类别列的 pandas DataFrame。输出包含几个我没有预料到且不理解的 NaN。示例代码:
import pandas as pd
d = pd.DataFrame({'a': [1, 1, 2, 2], 'b': [1, 1, 2, 2], 'c': [1, 2, 3, 4]})
d.astype({'b': 'category'}).groupby('a').agg({'b':'first', 'c':['mean', 'max']})
我得到以下输出:
b c
first mean max
0 1 NaN NaN
1 2 1.5 2.0
2 NaN 3.5 4.0
我本应得到以下输出:
b c
first mean max
a
1 1 1.5 2
2 2 3.5 4
有人可以解释 NaN 吗?
以下两种代码变体,一种没有类别列,一种没有 c 列的多个聚合,给出了预期的输出。
d.groupby('a').agg({'b':'first', 'c':['mean', 'max']})
d.astype({'b': 'category'}).groupby('a').agg({'b':'first', 'c':'mean'})
我正在使用 pandas 0.25.2 和 python 3.7.4。
我解释的出发点是有(和比较行为) 两个个数据帧:
d
- 原始 DataFrame,d2 = d.astype({'b': 'category'})
- 兄弟 DataFrame, b 列更改为 分类.
创建这两个 DataFrame 的分组:
gr = d.groupby('a')
gr2 = d2.groupby('a')
目前它们看起来完全相同,例如如果你 运行:
for key, grp in gr:
print(f'\nGroup: {key}\n{grp}')
和 gr2 的相同代码,您将得到 完全 相同的结果。
但是当你尝试 聚合 b 列。
当你 运行 gr.b.agg('first')
(在原始 DataFrame 上)时,你将得到:
a
1 1
2 2
Name: b, dtype: int64
结果是 系列 并且正如 d
内容所预期的那样:
- 对于 a == 1 的行组,b 的第一个值是 1,
- 对于 a == 2 的行组,b 的第一个值是 2.
但是如果你 运行 gr2.b.agg('first')
(在兄弟 DataFrame 上),你将得到:
[1, 2]
Categories (2, int64): [1, 2]
结果为pandas.core.arrays.categorical.Categorical类型 更重要的是,与分组键没有关系。
相反,类似的分组键是连续数字 (从 0 开始)。
你可以确认一下,运行宁gr2.agg({'b':'first'})
你会得到:
b
0 1
1 2
这就是您问题的根源。 当你 运行:
gr2.agg({'b':'first', 'c':['mean', 'max']})
合并分类和"normal"列,然后:
- 以上人工分组键产生自 b, 的聚合
- 与 actual 分组键结合 c.
对于(人工)分组键0:
- b / first 的值为 1(见上文),
- for c / mean 和 c / max 没有值,所以 结果包含 2 NaNs.
用于分组键1:
- for artificial key, b / first 的值为 2,
- 对于 true 键,c / mean 和 c / max 的值是 1.5 和 2.0,
- 但 Pandas 将它们全部放在 相同的 行中。
用于分组键2:
- 没有"artificial"具有此值的分组键, 所以在 b / first 列中有 NaN,
- 对于c / mean和c / max有是对应的值, 所以它们被打印在那里。
结论:这种情况下,转换不是一个好主意 分类类型的任何列。
仅使用 原始 DataFrame,具有 "original"(非分类)类型 你会得到你预期的结果。
从 09:14:38Z
开始编辑以下评论你在 post 和 提到的评论。
您的 post 包含写入:
I get the following output:
b c
first mean max
0 1 NaN NaN
1 2 1.5 2.0
2 NaN 3.5 4.0
所以打印输出确实包含NaN值。
但是在你写的评论中 我没有得到任何 NaN。
为了验证这个矛盾,我又做了一次测试:
d = pd.DataFrame({'a': [1, 1, 2, 2], 'b': [1, 1, 2, 2], 'c': [1, 2, 3, 4]})
d2 = d.astype({'b': 'category'})
d2.groupby('a').agg({'b':'first', 'c':['mean', 'max']})
并得到:
b c
first mean max
0 1 NaN NaN
1 2 1.5 2.0
2 NaN 3.5 4.0
就像您的 post 和 NaN 值一样。
我认为 bug, but possible solution is use function with Series.iat
for first value of group, because Series.first
工作方式不同:
Convenience method for subsetting initial periods of time series data based on a date offset.
def first(x):
return x.iat[0]
d = pd.DataFrame({'a': [1, 1, 2, 2], 'b': [1, 1, 2, 2], 'c': [1, 2, 3, 4]})
d = d.astype({'b': 'category'}).groupby('a').agg({'b':first, 'c':['mean', 'max']})
print(d)
b c
first mean max
a
1 1 1.5 2
2 2 3.5 4
如果使用 lambda 函数,则为 MultiIndex
的第二级获取 <lambda>
字符串:
d = d.astype({'b': 'category'}).groupby('a').agg({'b':lambda x: x.iat[0], 'c':['mean', 'max']})
print(d)
b c
<lambda> mean max
a
1 1 1.5 2
2 2 3.5 4