我有一个DataFrame,列中有许多缺失值,我希望将其分组:
import pandas as pd
import numpy as np
df = pd.DataFrame({'a': ['1', '2', '3'], 'b': ['4', np.NaN, '6']})
In [4]: df.groupby('b').groups
Out[4]: {'4': [0], '6': [2]}
看到Pandas已经删除了具有NaN目标值的行。 (我想要包含这些行!)
由于我需要很多这样的操作(许多cols都缺少值),并且使用比中位数(通常是随机森林)更复杂的函数,我想避免编写太复杂的代码片段。 < / p>
有什么建议吗?我应该为此编写一个函数还是有一个简单的解决方案?
答案 0 :(得分:91)
这是mentioned in the Missing Data section of the docs:
GroupBy中的NA组被自动排除。例如,此行为与R一致。
一种解决方法是在执行groupby之前使用占位符(例如-1):
In [11]: df.fillna(-1)
Out[11]:
a b
0 1 4
1 2 -1
2 3 6
In [12]: df.fillna(-1).groupby('b').sum()
Out[12]:
a
b
-1 2
4 1
6 3
那就是说,这感觉非常糟糕...也许应该有一个选项可以在groupby中包含NaN(参见this github issue - 它使用相同的占位符黑客)。
答案 1 :(得分:79)
在熊猫1.1中,您可以使用 dropna=False
来更好地控制此行为,NA values are now allowed in the grouper:
pd.__version__
# '1.1.0.dev0+2004.g8d10bfb6f'
# Example from the docs
df
a b c
0 1 2.0 3
1 1 NaN 4
2 2 1.0 3
3 1 2.0 2
# without NA (the default)
df.groupby('b').sum()
a c
b
1.0 2 3
2.0 2 5
# with NA
df.groupby('b', dropna=False).sum()
a c
b
1.0 2 3
2.0 2 5
NaN 1 4
答案 2 :(得分:23)
古老的话题,如果有人仍然绊倒了这个 - 另一个解决方法是在分组之前通过.astype(str)转换为字符串。这将保存NaN。
ajax(url)
.andThen({ successHandler code })
.onError({ errorHandler code})
in:
df = pd.DataFrame({'a': ['1', '2', '3'], 'b': ['4', np.NaN, '6']})
df['b'] = df['b'].astype(str)
df.groupby(['b']).sum()
答案 3 :(得分:7)
我无法向M. Kiewisch添加评论,因为我没有足够的声望点(只有41但需要超过50才能发表评论)。
无论如何,只是想指出M. Kiewisch解决方案不能正常工作,可能需要更多调整。考虑例如
>>> df = pd.DataFrame({'a': [1, 2, 3, 5], 'b': [4, np.NaN, 6, 4]})
>>> df
a b
0 1 4.0
1 2 NaN
2 3 6.0
3 5 4.0
>>> df.groupby(['b']).sum()
a
b
4.0 6
6.0 3
>>> df.astype(str).groupby(['b']).sum()
a
b
4.0 15
6.0 3
nan 2
表示对于组b = 4.0,相应的值是15而不是6.这里只是将1和5连接成字符串而不是将其添加为数字。
答案 4 :(得分:5)
Andy Hayden解决方案的一个小问题 - 它不再有效(因为?np.nan == np.nan
会产生False
,因此replace
函数不会实际上什么都做了。
对我有用的是:
df['b'] = df['b'].apply(lambda x: x if not np.isnan(x) else -1)
(至少那是Pandas 0.19.2的行为。很抱歉将其添加为不同的答案,我没有足够的声誉来评论。)
答案 5 :(得分:1)
我已经回答了这个问题,但有些原因将答案转换为评论。然而,这是最有效的解决方案:
无法在群组中包含(和传播)NaN是非常恶化的。引用R并不令人信服,因为这种行为与许多其他事情不一致。无论如何,假黑客也很糟糕。但是,如果有NaN,则组的大小(包括NaN)和计数(忽略NaN)会有所不同。
dfgrouped = df.groupby(['b']).a.agg(['sum','size','count'])
dfgrouped['sum'][dfgrouped['size']!=dfgrouped['count']] = None
如果这些不同,您可以将该值设置回“无”,以获取该组聚合函数的结果。
答案 6 :(得分:1)
到目前为止提供的所有答案都可能导致潜在的危险行为,因为您很可能选择了实际上是数据集一部分的虚拟值。当您创建具有许多属性的组时,这种可能性越来越大。简而言之,这种方法并不总是能很好地概括。
一个不太棘手的解决方法是使用pd.drop_duplicates()创建一个值组合的唯一索引,每个索引都有自己的ID,然后对该ID进行分组。它比较冗长,但确实可以完成工作:
def safe_groupby(df, group_cols, agg_dict):
# set name of group col to unique value
group_id = 'group_id'
while group_id in df.columns:
group_id += 'x'
# get final order of columns
agg_col_order = (group_cols + list(agg_dict.keys()))
# create unique index of grouped values
group_idx = df[group_cols].drop_duplicates()
group_idx[group_id] = np.arange(group_idx.shape[0])
# merge unique index on dataframe
df = df.merge(group_idx, on=group_cols)
# group dataframe on group id and aggregate values
df_agg = df.groupby(group_id, as_index=True)\
.agg(agg_dict)
# merge grouped value index to results of aggregation
df_agg = group_idx.set_index(group_id).join(df_agg)
# rename index
df_agg.index.name = None
# return reordered columns
return df_agg[agg_col_order]
请注意,您现在可以简单地执行以下操作:
data_block = [np.tile([None, 'A'], 3),
np.repeat(['B', 'C'], 3),
[1] * (2 * 3)]
col_names = ['col_a', 'col_b', 'value']
test_df = pd.DataFrame(data_block, index=col_names).T
grouped_df = safe_groupby(test_df, ['col_a', 'col_b'],
OrderedDict([('value', 'sum')]))
这将返回成功的结果,而不必担心会覆盖被误认为是虚拟值的真实数据。