【问题标题】:groupby on sparse matrix in pandas: filling them first大熊猫稀疏矩阵上的groupby:首先填充它们
【发布时间】:2015-05-16 05:24:50
【问题描述】:

我有一个形状为 (1000000,3) 的 pandas DataFrame df,如下所示:

id      cat       team
1       'cat1'    A
1       'cat2'    A
2       'cat3'    B
3       'cat1'    A
4       'cat3'    B
4       'cat1'    B

然后,我对 cat 列进行虚拟化,以便为机器学习分类做好准备。

df2 = pandas.get_dummies(df,columns=['cat'], sparse=True)

但是当我尝试这样做时:

df2.groupby(['id','team']).sum()

它卡住了,计算永远不会结束。因此,我没有立即分组,而是尝试:

df2 = df2.fillna(0)

但它不起作用,DataFrame 仍然充满了NaN 值。为什么fillna() 函数没有按应有的方式填充我的 DataFrame? 换句话说,我从 get_dummies 得到的 pandas 稀疏矩阵如何填充为 0 而不是 NaN?

我也试过了:

df2 = pandas.get_dummies(df,columns=['cat'], sparse=True).to_sparse(fill_value=0)

这一次df2很好地填充了0,但是当我尝试时:

print df2.groupby(['id','sexe']).sum()

我明白了:

C:\Anaconda\lib\site-packages\pandas\core\groupby.pyc in loop(labels, shape)
   3545         for i in range(1, nlev):
   3546             stride //= shape[i]
-> 3547             out += labels[i] * stride
   3548 
   3549         if xnull: # exclude nulls
ValueError: operands could not be broadcast together with shapes (1205800,) (306994,) (1205800,) 

我的解决方案是:

df2 = pandas.DataFrame(np.nan_to_num(df2.as_matrix()))
df2.groupby(['id','sexe']).sum()

它可以工作,但需要大量内存。有人可以帮我找到更好的解决方案,或者至少理解为什么我不能轻松地用零填充稀疏矩阵吗?以及为什么不可能在稀疏矩阵上使用groupby() 然后sum()

【问题讨论】:

  • 对未来问题的一般建议 FWIW:如果可能,请提供一个更完整的示例,其中包含通过 randntile 和/或 repeat 等 numpy 函数提供的示例数据。每当您进入性能和效率时,准确了解数据结构就变得非常重要。例如,在您的情况下,groupbyget_dummies 的性能不仅会因数据大小而异,而且会因不同组的数量而异。人们很难在不了解更多信息的情况下回答与性能相关的问题(尤其是当您添加 sparse 时)
  • 另外,我对机器学习了解不多,但如果你能创建一个相当短(但仍然完整)的示例,从数据创建一直到机器学习代码的简化版本,您可能会得到更好的帮助。

标签: pandas fill sparse-matrix


【解决方案1】:

我认为您的问题是由于混合了 dtypes。但是你可以像这样绕过它。首先,仅向get_dummies() 提供相关列,而不是整个数据框:

df2 = pd.get_dummies(df['cat']).to_sparse(0)

之后,您可以添加其他变量,但所有变量都必须是数字。 pandas 稀疏数据框只是稀疏(和同质 dtype)numpy 数组的包装器。

df2['id'] = df['id']

   'cat1'  'cat2'  'cat3'  id
0       1       0       0   1
1       0       1       0   1
2       0       0       1   2
3       1       0       0   3
4       0       0       1   4
5       1       0       0   4

对于非数字类型,您可以执行以下操作:

df2['team'] = df['team'].astype('category').cat.codes

这个 groupby 似乎工作正常:

df2.groupby('id').sum()

    'cat1'  'cat2'  'cat3'
id                        
1        1       1       0
2        0       0       1
3        1       0       0
4        1       0       1

内存管理的另一个但可能很重要的一点是,您通常可以使用分类而不是字符串对象来节省大量内存(尽管您可能已经这样做了):

df['cat2'] = df['cat'].astype('category')

df[['cat','cat2']].memory_usage()

cat     48
cat2    30

对于小型示例数据框而言,此处节省不多,但可能与您的实际数据框有很大差异。

【讨论】:

  • 您好,谢谢您的回答。我完全按照你说的做了,但是df2.groupby('id').sum() 太长了,我认为它真的不起作用。实际上 groupby 操作不需要时间,但是不可能遍历 groupby ,或者在它之后使用任何聚合函数。
  • 问题真的是这样的:稀疏pandas DataFrame上的groupby迭代非常非常非常慢。
  • 事实上,当我不使用稀疏矩阵时,groupby.sum 非常非常快。但我需要更多内存。
  • 我不知道是否有什么可以做的(当然你可以发布一个新问题)。也许将您分组的列保留在不同的非稀疏数据框中?这可能只是稀疏结构节省内存但由于使用稀疏结构的固有开销而运行更慢的情况。不过,我有点惊讶它会产生如此大的不同。
  • 我可以用什么来做相当于“get_dummies”的事情? Redshift还有其他方法吗?
【解决方案2】:

我之前也处理过类似的问题。我所做的是,我之前应用了 groupby 操作,然后使用 get_dummies() 跟进。

这对我有用,因为 groupby 在形成数千个虚拟列(在我的情况下)之后非常慢,尤其是在稀疏数据帧上。它基本上放弃了我。首先对列进行分组,然后进行虚拟化使其工作。

df = pd.DataFrame(df.groupby(['id','team'])['cat'].unique())
df.columns = ['cat']
df.reset_index(inplace=True)
df = df[['id','team']].join(df['cat'].str.join('|').str.get_dummies().add_prefix('CAT_'))

希望这对某人有所帮助!

【讨论】:

    猜你喜欢
    • 2018-05-06
    • 1970-01-01
    • 1970-01-01
    • 2013-07-23
    • 2012-07-28
    • 2023-04-05
    • 2023-04-06
    • 1970-01-01
    • 2016-03-14
    相关资源
    最近更新 更多