Pandas 多索引切片下级范围的最佳方式
Pandas multi-index best way to slice for ranges of subordinate levels
我有两个关于 pandas 数据帧多索引的问题。
假设我有一个数据帧 df 如下:
data
port bm pf
sector instrument date
1 A 2013-01-14 0 0
2013-01-15 5 5
2013-01-16 10 10
2013-01-17 15 15
2013-01-18 20 20
可以用下面的代码生成:
import pandas as pd
date = pd.bdate_range('2013-01-14','2013-01-20').repeat(5)
sector = [1,1,1,2,2] * 5
df = pd.DataFrame(dict(port=['pf']*25,sector=sector,instrument=list('ABCDE')*5,date=date,data=xrange(25)))
df = pd.concat([df,pd.DataFrame(dict(port=['bm']*25,sector=sector,instrument=list('ABCDE')*5,date=date,data=xrange(25)))],axis=0)
df = df.set_index(['port','sector','instrument','date'])
df = df.unstack('port')
我想得到两组结果:2013-01-17 的所有值和 2013-01-17 到系列结束的所有值。
首先,我知道我可以使用以下方法之一:
idx = pd.IndexSlice
targetdate = pd.Timestamp('2013-01-17')
slicer = (slice(None),slice(None),targetdate)
1) df.loc[slicer,:]
2) df.xs(pd.Timestamp('2013-01-17'),level=2)
3) df.xs(slicer,)
4) df[idx[:,:,targetdate],:]
所有这些看起来都很笨重。有没有更明显的方式我错过了?还有什么其他方法可以实现这一目标。我想我希望有类似 df.loc(level=2)[targetdate]
的东西(这当然行不通)。
第二次我只想出了一个解决方案
query = df.index.get_level_values(2) >= pd.Timestamp('2013-01-17')
df[query]
还有更有效的方法吗?
最终奖金问题:df.index.get_loc_level()
做什么?我觉得它应该对此有所帮助,但我不知道如何使用它。
谢谢
我认为这个掩码,就像你正在做的那样,在这里会非常好:
query = df.index.get_level_values(2) >= pd.Timestamp('2013-01-17')
df[query]
如果您在日期中有很多重复,您可以使用较低级别的内容来提高性能:
query = (df.index.levels[2] >= pd.Timestamp("2013-01-17"))[df.index.labels[2]]
df[query]
我可能会因此而大喊大叫...!但在某些情况下会明显更快。
get_loc_level
类似于 loc,即基于标签而不是位置:
Get integer location slice for requested label or tuple
In [21]: df.index.get_loc_level(2)
Out[21]:
(slice(15, 25, None),
MultiIndex(levels=[[u'A', u'B', u'C', u'D', u'E'], [2013-01-14 00:00:00, 2013-01-15 00:00:00, 2013-01-16 00:00:00, 2013-01-17 00:00:00, 2013-01-18 00:00:00]],
labels=[[3, 3, 3, 3, 3, 4, 4, 4, 4, 4], [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]],
names=[u'instrument', u'date']))
默认取第一个索引,但你可以传入更多...
In [21]: df.index.get_loc_level((1, "A"))
Out[21]:
(slice(0, 5, None), <class 'pandas.tseries.index.DatetimeIndex'>
[2013-01-14, ..., 2013-01-18]
Length: 5, Freq: None, Timezone: None)
我有两个关于 pandas 数据帧多索引的问题。
假设我有一个数据帧 df 如下:
data
port bm pf
sector instrument date
1 A 2013-01-14 0 0
2013-01-15 5 5
2013-01-16 10 10
2013-01-17 15 15
2013-01-18 20 20
可以用下面的代码生成:
import pandas as pd
date = pd.bdate_range('2013-01-14','2013-01-20').repeat(5)
sector = [1,1,1,2,2] * 5
df = pd.DataFrame(dict(port=['pf']*25,sector=sector,instrument=list('ABCDE')*5,date=date,data=xrange(25)))
df = pd.concat([df,pd.DataFrame(dict(port=['bm']*25,sector=sector,instrument=list('ABCDE')*5,date=date,data=xrange(25)))],axis=0)
df = df.set_index(['port','sector','instrument','date'])
df = df.unstack('port')
我想得到两组结果:2013-01-17 的所有值和 2013-01-17 到系列结束的所有值。
首先,我知道我可以使用以下方法之一:
idx = pd.IndexSlice
targetdate = pd.Timestamp('2013-01-17')
slicer = (slice(None),slice(None),targetdate)
1) df.loc[slicer,:]
2) df.xs(pd.Timestamp('2013-01-17'),level=2)
3) df.xs(slicer,)
4) df[idx[:,:,targetdate],:]
所有这些看起来都很笨重。有没有更明显的方式我错过了?还有什么其他方法可以实现这一目标。我想我希望有类似 df.loc(level=2)[targetdate]
的东西(这当然行不通)。
第二次我只想出了一个解决方案
query = df.index.get_level_values(2) >= pd.Timestamp('2013-01-17')
df[query]
还有更有效的方法吗?
最终奖金问题:df.index.get_loc_level()
做什么?我觉得它应该对此有所帮助,但我不知道如何使用它。
谢谢
我认为这个掩码,就像你正在做的那样,在这里会非常好:
query = df.index.get_level_values(2) >= pd.Timestamp('2013-01-17')
df[query]
如果您在日期中有很多重复,您可以使用较低级别的内容来提高性能:
query = (df.index.levels[2] >= pd.Timestamp("2013-01-17"))[df.index.labels[2]]
df[query]
我可能会因此而大喊大叫...!但在某些情况下会明显更快。
get_loc_level
类似于 loc,即基于标签而不是位置:
Get integer location slice for requested label or tuple
In [21]: df.index.get_loc_level(2)
Out[21]:
(slice(15, 25, None),
MultiIndex(levels=[[u'A', u'B', u'C', u'D', u'E'], [2013-01-14 00:00:00, 2013-01-15 00:00:00, 2013-01-16 00:00:00, 2013-01-17 00:00:00, 2013-01-18 00:00:00]],
labels=[[3, 3, 3, 3, 3, 4, 4, 4, 4, 4], [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]],
names=[u'instrument', u'date']))
默认取第一个索引,但你可以传入更多...
In [21]: df.index.get_loc_level((1, "A"))
Out[21]:
(slice(0, 5, None), <class 'pandas.tseries.index.DatetimeIndex'>
[2013-01-14, ..., 2013-01-18]
Length: 5, Freq: None, Timezone: None)