从DataFrame中选择多键横截面

joe*_*-ts 19 pandas

我有一个DataFrame"df"与(时间,股票代码)Multiindex和bid/ask/etc数据列:


                          tod    last     bid      ask      volume
    time        ticker                  
    2013-02-01  SPY       1600   149.70   150.14   150.17   1300
                SLV       1600   30.44    30.38    30.43    3892
                GLD       1600   161.20   161.19   161.21   3860

我想使用多个键选择二级(level = 1)横截面.现在,我可以用一把钥匙来做,即


    df.xs('SPY', level=1)

这给了我SPY的时间序列.选择多键横截面的最佳方法是什么,即SPY和GLD的组合横截面,如:


    df.xs(['SPY', 'GLD'], level=1)

Zel*_*ny7 9

除了使用以外,我找不到更直接的方法select:

>>> df

       last   tod
A SPY     1  1600
  SLV     2  1600
  GLD     3  1600

>>> df.select(lambda x: x[1] in ['SPY','GLD'])

       last   tod
A SPY     1  1600
  GLD     3  1600
Run Code Online (Sandbox Code Playgroud)

  • 不错,这可能是最简单的方法.我想知道它是否是效率最高的,因为为每行调用lambda可能会很慢,但是我再也不确定当前版本是否有更快的方法 (2认同)

小智 7

使用更新版本的Pandas有更好的方法:

regression_df.loc[(slice(None), ['SPY', 'GLD']), :]
Run Code Online (Sandbox Code Playgroud)

这种方法要求索引按字典顺序排序(使用df.sort_index()).


Gor*_*ean 5

为了它的价值,我做了以下事情:

\n\n
foo = pd.DataFrame(np.random.rand(12,3), \n                   index=pd.MultiIndex.from_product([['A','B','C','D'],['Green','Red','Blue']], \n                                                    names=['Letter','Color']),\n                   columns=['X','Y','Z']).sort_index()\n\nfoo.reset_index()\\\n   .loc[foo.reset_index().Color.isin({'Green','Red'})]\\\n   .set_index(foo.index.names)\n
Run Code Online (Sandbox Code Playgroud)\n\n

此方法类似于 select,但避免使用 lambda 迭代所有行。

\n\n

不过,我将其与 Panel 方法进行了比较,发现 Panel 解决方案速度更快(index/loc 为 2.91 毫秒,to_panel/to_frame 为 1.48 毫秒:

\n\n
foo.to_panel()[:,:,['Green','Red']].to_frame()\n
Run Code Online (Sandbox Code Playgroud)\n\n

次数:

\n\n
In [56]:\n%%timeit\nfoo.reset_index().loc[foo.reset_index().Color.isin({'Green','Red'})].set_index(foo.index.names)\n100 loops, best of 3: 2.91 ms per loop\n\nIn [57]:\n%%timeit\nfoo2 = foo.reset_index()\nfoo2.loc[foo2.Color.eq('Green') | foo2.Color.eq('Red')].set_index(foo.index.names)\n100 loops, best of 3: 2.85 ms per loop\n\nIn [58]:\n%%timeit\nfoo2 = foo.reset_index()\nfoo2.loc[foo2.Color.ne('Blue')].set_index(foo.index.names)\n100 loops, best of 3: 2.37 ms per loop\n\nIn [54]:\n%%timeit\nfoo.to_panel()[:,:,['Green','Red']].to_frame()\n1000 loops, best of 3: 1.18 ms per loop\n
Run Code Online (Sandbox Code Playgroud)\n\n

更新

\n\n

在(再次)重新审视这个主题后,我观察到以下内容:

\n\n
In [100]:\n%%timeit\nfoo2 = pd.DataFrame({k: foo.loc[k] for k in foo.index if k[1] in ['Green','Red']}).transpose()\nfoo2.index.names = foo.index.names\nfoo2.columns.names = foo2.columns.names\n100 loops, best of 3: 1.97 ms per loop\n\nIn [101]:\n%%timeit\nfoo2 = pd.DataFrame.from_dict({k: foo.loc[k] for k in foo.index if k[1] in ['Green','Red']}, orient='index')\nfoo2.index.names = foo.index.names\nfoo2.columns.names = foo2.columns.names\n100 loops, best of 3: 1.82 ms per loop\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您不关心保留级别的原始顺序和命名,您可以使用:

\n\n
%%timeit\npd.concat({key: foo.xs(key, axis=0, level=1) for key in ['Green','Red']}, axis=0)\n1000 loops, best of 3: 1.31 ms per loop\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您只是在第一级进行选择:

\n\n
%%timeit\npd.concat({key: foo.loc[key] for key in ['A','B']}, axis=0, names=foo.index.names)\n1000 loops, best of 3: 1.12 ms per loop\n
Run Code Online (Sandbox Code Playgroud)\n\n

相对:

\n\n
%%timeit\nfoo.to_panel()[:,['A','B'],:].to_frame()\n1000 loops, best of 3: 1.16 ms per loop\n
Run Code Online (Sandbox Code Playgroud)\n\n

另一个更新

\n\n

如果对示例的索引进行排序foo,上面的许多时间都会得到改善(时间已更新以反映预排序的索引)。不过,当索引排序时,可以使用user674155描述的解决方案:

\n\n
%%timeit\nfoo.loc[(slice(None), ['Blue','Red']),:]\n1000 loops, best of 3: 582 \xc2\xb5s per loop\n
Run Code Online (Sandbox Code Playgroud)\n\n

在我看来,这是最有效和直观的(用户不需要了解面板以及它们是如何从框架创建的)。

\n\n

注意:即使索引尚未排序,foo动态排序索引在性能上也与该to_panel选项相当。

\n