Pandas在HDFStore中对大数据进行“Group By”查询?

22

我在一个HDFStore中有约700万行数据,包含60多列。这些数据太多,我无法将其全部载入内存。我想根据一列"A"的值对数据进行分组聚合。Pandas的文档(拆分/聚合/组合)假设我已经将所有数据都读入了DataFrame中,但是我无法将整个存储库读入内存中的DataFrame中。在HDFStore中分组数据的正确方法是什么?


1
你看过https://dev59.com/cmYq5IYBdhLWcg3wtCzO#14287518吗?Jeff的答案是这种工作流程的很好的入门材料。 - Chang She
1
这目前正在讨论中,作为pandas的未来增强功能。我真希望有一天它能被添加进去,这样我就可以最终放弃SAS了:https://github.com/pydata/pandas/issues/3202 - Zelazny7
据我所了解的文档,HDFStore 不支持 DataFrame 上的所有操作。您可以尝试使用此处描述的表查询功能手动一次选择一组。 - BrenBarn
你能给一个小例子说明你将使用哪些函数来处理这个组吗?同时提供一个小的示例框架会很有帮助。 - Jeff
请展示 df.get_dtype_counts() 并说明您是否使用了 data_columns。谢谢。 - Jeff
还有一个问题会很有帮助:您期望什么样的分组密度?例如100个组、10,000个或1M(或更多)?groupby是在多列上进行的吗?这些列的类型是什么? - Jeff
1个回答

21

这里是一个完整的例子。

import numpy as np
import pandas as pd
import os

fname = 'groupby.h5'

# create a frame
df = pd.DataFrame({'A': ['foo', 'foo', 'foo', 'foo',
                         'bar', 'bar', 'bar', 'bar',
                         'foo', 'foo', 'foo'],
                   'B': ['one', 'one', 'one', 'two',
                         'one', 'one', 'one', 'two',
                         'two', 'two', 'one'],
                   'C': ['dull', 'dull', 'shiny', 'dull',
                         'dull', 'shiny', 'shiny', 'dull',
                         'shiny', 'shiny', 'shiny'],
                   'D': np.random.randn(11),
                   'E': np.random.randn(11),
                   'F': np.random.randn(11)})


# create the store and append, using data_columns where I possibily
# could aggregate
with pd.get_store(fname) as store:
    store.append('df',df,data_columns=['A','B','C'])
    print "store:\n%s" % store

    print "\ndf:\n%s" % store['df']

    # get the groups
    groups = store.select_column('df','A').unique()
    print "\ngroups:%s" % groups

    # iterate over the groups and apply my operations
    l = []
    for g in groups:

        grp = store.select('df',where = [ 'A=%s' % g ])

        # this is a regular frame, aggregate however you would like
        l.append(grp[['D','E','F']].sum())


    print "\nresult:\n%s" % pd.concat(l, keys = groups)

os.remove(fname)

输出

store:
<class 'pandas.io.pytables.HDFStore'>
File path: groupby.h5
/df            frame_table  (typ->appendable,nrows->11,ncols->6,indexers->[index],dc->[A,B,C])

df:
      A    B      C         D         E         F
0   foo  one   dull -0.815212 -1.195488 -1.346980
1   foo  one   dull -1.111686 -1.814385 -0.974327
2   foo  one  shiny -1.069152 -1.926265  0.360318
3   foo  two   dull -0.472180  0.698369 -1.007010
4   bar  one   dull  1.329867  0.709621  1.877898
5   bar  one  shiny -0.962906  0.489594 -0.663068
6   bar  one  shiny -0.657922 -0.377705  0.065790
7   bar  two   dull -0.172245  1.694245  1.374189
8   foo  two  shiny -0.780877 -2.334895 -2.747404
9   foo  two  shiny -0.257413  0.577804 -0.159316
10  foo  one  shiny  0.737597  1.979373 -0.236070

groups:Index([bar, foo], dtype=object)

result:
bar  D   -0.463206
     E    2.515754
     F    2.654810
foo  D   -3.768923
     E   -4.015488
     F   -6.110789
dtype: float64

一些警告:

1)如果您的组密度相对较低,例如数百或数千个组,则此方法是有意义的。如果超过这个数量,则有更高效(但更复杂的)方法,并且应用您正在应用的函数(在本例中为 sum)会变得更加限制。

基本上,您将通过块迭代整个存储库,一边分组一边进行,但仅将组保留为半折叠状态(想象一下做平均值,因此您需要在结束时保持运行总数和运行计数,然后进行除法)。因此,某些操作可能会更加棘手,但可能处理许多组(而且速度非常快)。

2)可以通过保存坐标(例如,组位置,但这会更加复杂)来提高其效率。

3)不适用于多重分组(虽然是可能的,但需要像上面提到的2)一样的方法)

4)您想要分组的列必须是数据列!

5)您可以在选择中组合任何其他筛选器(顺便说一下,这是进行多重分组的一种聪明方式,您只需形成两个唯一的组列表并迭代它们的乘积,如果您有许多组,则不是非常高效,但可以工作)

希望对您有所帮助

如果此方法对您有用,请告诉我


谢谢Jeff。如果可以的话,我会给你额外的积分,因为你在git票中添加了这个例子(和一个指向SO的链接!)。 - technomalogical
你也制作了这本食谱(但还没有更新到这个问题),请参见:http://pandas.pydata.org/pandas-docs/dev/cookbook.html#hdfstore - Jeff
在 pandas 的后续版本中,该行代码 groups = store.unique('df','A') 应更改为 groups = store.select_column('df', 'A').unique() - IanH
但是store.select_column('df','A').unique()会触发完整读取,如果df['A']太大而无法放入内存怎么办? - agemO

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接