【问题标题】:How to filter by sub-level index in Pandas如何在 Pandas 中按子级索引进行过滤
【发布时间】:2012-08-26 19:52:56
【问题描述】:

我有一个“df”,它有一个多级索引 (STK_ID,RPT_Date)

                       sales         cogs     net_pft
STK_ID RPT_Date                                      
000876 20060331          NaN          NaN         NaN
       20060630    857483000    729541000    67157200
       20060930   1063590000    925140000    50807000
       20061231    853960000    737660000    51574000
       20070331  -2695245000  -2305078000  -167642500
       20070630   1146245000   1050808000   113468500
       20070930   1327970000   1204800000    84337000
       20071231   1439140000   1331870000    53398000
       20080331  -3135240000  -2798090000  -248054300
       20080630   1932470000   1777010000   133756300
       20080930   1873240000   1733660000    92099000
002254 20061231 -16169620000 -15332705000  -508333200
       20070331   -763844000   -703460000    -1538000
       20070630    501221000    289167000   118012200
       20070930    460483000    274026000    95967000

如何编写一个命令来过滤'RPT_Date'包含'0630'的行(这是Q2报告)?结果应该是:

                       sales         cogs     net_pft
STK_ID RPT_Date                                      
000876 20060630    857483000    729541000    67157200
       20070630   1146245000   1050808000   113468500
       20080630   1932470000   1777010000   133756300
002254 20070630    501221000    289167000   118012200

我正在尝试使用 df[df['RPT_Date'].str.contains('0630')],但 Pandas 拒绝使用,因为 'RPT_Date' 不是列,而是 sub_level 索引。

感谢您的提示...

【问题讨论】:

  • 这也可以通过df.filter(like='0630', axis=0)完成

标签: python pandas


【解决方案1】:

要对列使用“str.*”方法,您可以重置索引,使用列“str.*”方法调用过滤行,然后重新创建索引。

In [72]: x = df.reset_index(); x[x.RPT_Date.str.endswith("0630")].set_index(['STK_ID', 'RPT_Date'])
Out[72]: 
                      sales        cogs    net_pft
STK_ID RPT_Date                                   
000876 20060630   857483000   729541000   67157200
       20070630  1146245000  1050808000  113468500
       20080630  1932470000  1777010000  133756300
002254 20070630   501221000   289167000  118012200

但是,这种方法并不是特别快。

In [73]: timeit x = df.reset_index(); x[x.RPT_Date.str.endswith("0630")].set_index(['STK_ID', 'RPT_Date'])
1000 loops, best of 3: 1.78 ms per loop

另一种方法建立在 MultiIndex 对象的行为与 元组列表。

In [75]: df.index
Out[75]: 
MultiIndex
[('000876', '20060331') ('000876', '20060630') ('000876', '20060930')
 ('000876', '20061231') ('000876', '20070331') ('000876', '20070630')
 ('000876', '20070930') ('000876', '20071231') ('000876', '20080331')
 ('000876', '20080630') ('000876', '20080930') ('002254', '20061231')
 ('002254', '20070331') ('002254', '20070630') ('002254', '20070930')]

在此基础上,您可以使用 df.index.map() 从 MultiIndex 创建一个布尔数组,并使用结果过滤帧。

In [76]: df[df.index.map(lambda x: x[1].endswith("0630"))]
Out[76]: 
                      sales        cogs    net_pft
STK_ID RPT_Date                                   
000876 20060630   857483000   729541000   67157200
       20070630  1146245000  1050808000  113468500
       20080630  1932470000  1777010000  133756300
002254 20070630   501221000   289167000  118012200

这也快了很多。

In [77]: timeit df[df.index.map(lambda x: x[1].endswith("0630"))]
1000 loops, best of 3: 240 us per loop

【讨论】:

  • 感谢您的提示。它简单而优雅。如果我想将 Q1、Q3、Q4 过滤在一起,即“NOT endswith('0630')”,如何将“NOT”添加到“df[df.index.map(lambda x: x [1].endswith("0630"))] " ?
  • 几个想法:1)在 lambda 中使用 not(例如,df[df.index.map(lambda x: not x[1].endswith("0630"))])。这有效,但再次迭代索引。 2) 如果将 df.index.map() 的结果保存到一个变量中,该变量将是一个 numpy 数组,可以用~ 反转(例如q2_mask = df.index.map(lambda x: x[1].endswith("0630")); df[~q2_mask])。希望对您有所帮助。
  • 酷!谢啦!找了一会儿,它允许过滤像df[df.index.map(lambda i: i[1] < another_df.xs(i[0])['DATE'])]这样的琐碎的多索引查询,这里我们从第一个索引组件中获取一个值,然后从另一个数据帧中选择所有小于对应值的行作为第二个索引。
  • 您也可以通过名称访问索引级别:df[df.index.get_level_values('RPT_Date').str.endswith('0630')]
猜你喜欢
  • 2015-11-04
  • 1970-01-01
  • 2012-08-24
  • 2013-12-12
  • 2022-06-20
  • 2016-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多