通过MultiIndex级别或子级切片pandas DataFrame

Lon*_*Rob 11 python pandas

受到这个答案的启发以及对这个问题缺乏简单的回答,我发现自己写了一些语法糖,让生活更容易通过MultiIndex级别进行过滤.

def _filter_series(x, level_name, filter_by):
    """
    Filter a pd.Series or pd.DataFrame x by `filter_by` on the MultiIndex level
    `level_name`

    Uses `pd.Index.get_level_values()` in the background. `filter_by` is either
    a string or an iterable.
    """
    if isinstance(x, pd.Series) or isinstance(x, pd.DataFrame):
        if type(filter_by) is str:
            filter_by = [filter_by]

        index = x.index.get_level_values(level_name).isin(filter_by)
        return x[index]
    else:
        print "Not a pandas object"
Run Code Online (Sandbox Code Playgroud)

但是,如果我知道熊猫开发团队(我开始,慢慢地!)已经有一个很好的方法来做到这一点,我只是不知道它是什么!

我对吗?

Pie*_*ton 6

我实际上赞成 joris 的答案...但不幸的是,他提到的重构在 0.14 中没有发生,在 0.17 中也没有发生。因此,现在让我建议一个快速但肮脏的解决方案(显然源自杰夫的解决方案):

def filter_by(df, constraints):
    """Filter MultiIndex by sublevels."""
    indexer = [constraints[name] if name in constraints else slice(None)
               for name in df.index.names]
    return df.loc[tuple(indexer)] if len(df.shape) == 1 else df.loc[tuple(indexer),]

pd.Series.filter_by = filter_by
pd.DataFrame.filter_by = filter_by
Run Code Online (Sandbox Code Playgroud)

...用作

df.filter_by({'level_name' : value})
Run Code Online (Sandbox Code Playgroud)

其中value确实可以是单个值,但也可以是列表、切片......

(未经面板和更高维度元素的测试,但我确实希望它能够工作)


Jef*_*eff 5

使用 master/0.14(即将发布)中的新多索引切片器非常容易,请参见此处

有一个悬而未决的问题可以使这在语法上更容易(这并不难),请参见此处, 例如这样的内容:df.loc[{ 'third' : ['C1','C3'] }]我认为是合理的

以下是您的操作方法(需要 master/0.14):

In [2]: def mklbl(prefix,n):
   ...:     return ["%s%s" % (prefix,i)  for i in range(n)]
   ...: 


In [11]: index = MultiIndex.from_product([mklbl('A',4),
mklbl('B',2),
mklbl('C',4),
mklbl('D',2)],names=['first','second','third','fourth'])

In [12]: columns = ['value']

In [13]: df = DataFrame(np.arange(len(index)*len(columns)).reshape((len(index),len(columns))),index=index,columns=columns).sortlevel()

In [14]: df
Out[14]: 
                           value
first second third fourth       
A0    B0     C0    D0          0
                   D1          1
             C1    D0          2
                   D1          3
             C2    D0          4
                   D1          5
             C3    D0          6
                   D1          7
      B1     C0    D0          8
                   D1          9
             C1    D0         10
                   D1         11
             C2    D0         12
                   D1         13
             C3    D0         14
                   D1         15
A1    B0     C0    D0         16
                   D1         17
             C1    D0         18
                   D1         19
             C2    D0         20
                   D1         21
             C3    D0         22
                   D1         23
      B1     C0    D0         24
                   D1         25
             C1    D0         26
                   D1         27
             C2    D0         28
                   D1         29
             C3    D0         30
                   D1         31
A2    B0     C0    D0         32
                   D1         33
             C1    D0         34
                   D1         35
             C2    D0         36
                   D1         37
             C3    D0         38
                   D1         39
      B1     C0    D0         40
                   D1         41
             C1    D0         42
                   D1         43
             C2    D0         44
                   D1         45
             C3    D0         46
                   D1         47
A3    B0     C0    D0         48
                   D1         49
             C1    D0         50
                   D1         51
             C2    D0         52
                   D1         53
             C3    D0         54
                   D1         55
      B1     C0    D0         56
                   D1         57
             C1    D0         58
                   D1         59
                             ...

[64 rows x 1 columns]
Run Code Online (Sandbox Code Playgroud)

创建跨所有级别的索引器,选择所有条目

In [15]: indexer = [slice(None)]*len(df.index.names)
Run Code Online (Sandbox Code Playgroud)

让我们关心的关卡只有我们关心的条目

In [16]: indexer[df.index.names.index('third')] = ['C1','C3']
Run Code Online (Sandbox Code Playgroud)

选择它(重要的是这是一个元组!)

In [18]: df.loc[tuple(indexer),:]
Out[18]: 
                           value
first second third fourth       
A0    B0     C1    D0          2
                   D1          3
             C3    D0          6
                   D1          7
      B1     C1    D0         10
                   D1         11
             C3    D0         14
                   D1         15
A1    B0     C1    D0         18
                   D1         19
             C3    D0         22
                   D1         23
      B1     C1    D0         26
                   D1         27
             C3    D0         30
                   D1         31
A2    B0     C1    D0         34
                   D1         35
             C3    D0         38
                   D1         39
      B1     C1    D0         42
                   D1         43
             C3    D0         46
                   D1         47
A3    B0     C1    D0         50
                   D1         51
             C3    D0         54
                   D1         55
      B1     C1    D0         58
                   D1         59
             C3    D0         62
                   D1         63

[32 rows x 1 columns]
Run Code Online (Sandbox Code Playgroud)


jor*_*ris 1

你有filter可以做这样的事情的方法。例如,在链接的SO 问题中提出的示例:

In [188]: df.filter(like='0630', axis=0)
Out[188]: 
                      sales        cogs    net_pft
STK_ID RPT_Date                                   
876    20060630   857483000   729541000   67157200
       20070630  1146245000  1050808000  113468500
       20080630  1932470000  1777010000  133756300
2254   20070630   501221000   289167000  118012200
Run Code Online (Sandbox Code Playgroud)

目前(在即将发布的 0.14 中)重构了过滤器方法,并且level将添加一个关键字(因为现在如果相同的标签出现在索引的不同级别中,您可能会遇到问题)。