【问题标题】:Pandas HDFStore of MultiIndex DataFrames: how to efficiently get all indexesMultiIndex DataFrames的Pandas HDFStore:如何高效获取所有索引
【发布时间】:2013-07-13 04:42:16
【问题描述】:

在 Pandas 中,有没有办法以表格格式有效地提取 HDFStore 中存在的所有 MultiIndex 索引?

我可以有效地使用select() where=,但我想要所有索引,而不是任何列。我也可以 select() 使用 iterator=True 来节省 RAM,但这仍然意味着从磁盘读取几乎所有表,所以它仍然很慢。

我一直在寻找 store.root..table.* 的东西,希望我能得到一个索引值列表。我在正确的轨道上吗?

B 计划是保留一个较短的 MultiIndex 数据帧,它只包含每次我附加主要数据帧时附加的空数据帧。我可以检索它并获得比主要索引便宜得多的索引。虽然不优雅。

【问题讨论】:

    标签: python pandas hdfstore


    【解决方案1】:

    那些使用更大表的人可能会找到 Jeff 建议的解决方案,最终导致内存错误。这是一个更优雅的解决方案,但我无法在我的情况下使用(对于 2e9 行表,日期时间索引,在 16GB RAM 桌面上)。 我最终得到了以下(不幸的是不优雅)解决方案,其中h5storeHDFStore 对象,一个多索引DataFrame,保存为一个表,timestamp 索引(Float64)是一个@987654325 @一:

    %%time
    #ts = h5store.select_column(h5store.keys()[0], column='timestamp').unique()
    
    chunkshape = int(1e7) # can vary due to machine and hdf5
    
    ## get a list of chunks unique timestamps
    ts = [indx.index.get_level_values('timestamp').unique() 
              for indx in h5full.select(h5full.keys()[0], columns=['timestamp'],
                                        stop=None, # change for a smaller selection
                                        chunksize=chunkshape)
          ]
    ## drop duplicates at the the end-points
    for i in range(len(ts)-1):
        if ts[i][-1]==ts[i+1][0]:
             ts[i] = ts[i][:-1]
    ## merge to single ndarray
    ts = np.concatenate(ts)
    

    本次运行的时间(超过 2e9 行):

    CPU times: user 14min 16s, sys: 2min 34s, total: 16min 50s
    Wall time: 14min 45s
    

    【讨论】:

      【解决方案2】:

      创建多索引df

      In [35]: df = DataFrame(randn(100000,3),columns=list('ABC'))
      
      In [36]: df['one'] = 'foo'
      
      In [37]: df['two'] = 'bar'
      
      In [38]: df.ix[50000:,'two'] = 'bah'
      
      In [40]: mi = df.set_index(['one','two'])
      
      In [41]: mi
      Out[41]: 
      <class 'pandas.core.frame.DataFrame'>
      MultiIndex: 100000 entries, (foo, bar) to (foo, bah)
      Data columns (total 3 columns):
      A    100000  non-null values
      B    100000  non-null values
      C    100000  non-null values
      dtypes: float64(3)
      

      将其存储为表格

      In [42]: store = pd.HDFStore('test.h5',mode='w')
      
      In [43]: store.append('df',mi)
      

      get_storer 将返回存储的对象(但不检索数据)

      In [44]: store.get_storer('df').levels
      Out[44]: ['one', 'two']
      
      In [2]: store
      Out[2]: 
      <class 'pandas.io.pytables.HDFStore'>
      File path: test.h5
      /df            frame_table  (typ->appendable_multi,nrows->100000,ncols->5,indexers->[index],dc->[two,one])
      

      索引级别被创建为 data_columns,这意味着您可以在选择中使用它们 这是只选择索引的方法

      In [48]: store.select('df',columns=['one'])
      Out[48]: 
      <class 'pandas.core.frame.DataFrame'>
      MultiIndex: 100000 entries, (foo, bar) to (foo, bah)
      Empty DataFrame
      

      选择单个列并将其作为 mi-frame 返回

      In [49]: store.select('df',columns=['A'])
      Out[49]: 
      <class 'pandas.core.frame.DataFrame'>
      MultiIndex: 100000 entries, (foo, bar) to (foo, bah)
      Data columns (total 1 columns):
      A    100000  non-null values
      dtypes: float64(1)
      

      选择单个列作为系列(也可以是索引,因为它们存储为列)。这会很快。

      In [2]: store.select_column('df','one')
      Out[2]: 
      0     foo
      1     foo
      2     foo
      3     foo
      4     foo
      5     foo
      6     foo
      7     foo
      8     foo
      9     foo
      10    foo
      11    foo
      12    foo
      13    foo
      14    foo
      ...
      99985    foo
      99986    foo
      99987    foo
      99988    foo
      99989    foo
      99990    foo
      99991    foo
      99992    foo
      99993    foo
      99994    foo
      99995    foo
      99996    foo
      99997    foo
      99998    foo
      99999    foo
      Length: 100000, dtype: object
      

      如果你真的想要最快的只选择索引

      In [4]: %timeit store.select_column('df','one')
      100 loops, best of 3: 8.71 ms per loop
      
      In [5]: %timeit store.select('df',columns=['one'])
      10 loops, best of 3: 43 ms per loop
      

      或者得到一个完整的索引

      In [6]: def f():
         ...:     level_1 =  store.select_column('df','one')
         ...:     level_2 =  store.select_column('df','two')
         ...:     return MultiIndex.from_arrays([ level_1, level_2 ])
         ...: 
      
      In [17]: %timeit f()
      10 loops, best of 3: 28.1 ms per loop
      

      如果您想要每个级别的值,这是一种非常快速的方法

      In [2]: store.select_column('df','one').unique()
      Out[2]: array(['foo'], dtype=object)
      
      In [3]: store.select_column('df','two').unique()
      Out[3]: array(['bar', 'bah'], dtype=object)
      

      【讨论】:

      • 很棒的答案,谢谢杰夫。我想直接的答案是查看 store.root.*.table 是没有结果的。进一步考虑,受您的回答启发,没有捷径是有道理的 - 您必须让选择考虑所有数据。尝试获取一些元数据来说明哪些索引值将占用相同的空间。作为解释,并非我的多索引的所有可能组合都已填充,因此仅获取多索引的级别是不够的。谢谢。
      • 您可能会发现select_column(...).unique() 也很有用,它实际上会为您提供特定级别的值,但不确定是否对您有用
      • 我添加了我刚才建议的方法
      • @Jeff 我也喜欢select_column(...).unique() 解决方案的优雅,只是(至少在我的情况下)它对于大表失败(由于内存错误)。请在下面查看我对此案例的暂定解决方案 - 我希望看到一个更优雅的解决方案(我不喜欢我的)
      • 你可以将 start 和 stop 传递给 select_column;本质上,您可以遍历范围以找出您需要的内容,然后分割实际的行,select_as_coordinates 可能很有用,因为它可以进行完整查询并只返回您想要的行的索引。
      猜你喜欢
      • 2019-10-17
      • 2018-01-26
      • 2018-01-14
      • 1970-01-01
      • 2015-06-02
      • 2015-01-16
      • 2021-03-27
      • 2021-12-13
      • 2021-10-12
      相关资源
      最近更新 更多