2014-04-10 65 views
9

this answer的啓發和this question缺乏簡單的回答我發現自己寫了一些小句子糖,讓生活更輕鬆地通過MultiIndex級別過濾。Slice熊貓數據框由MultiIndex level或sublevel

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" 

但是如果我知道大熊貓的開發團隊(和我開始,慢慢的!)不過已經有了很好的方式做到這一點,我只是不知道它是什麼呢!

我對不對?

回答

1

你有filter方法可以做這樣的事情。例如,與被要求在linked 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 

過濾方法是refactored的時刻(在即將到來的0.14),和一個level關鍵字將被添加(因爲現在你可以有如果出了問題相同的標籤出現在索引的不同級別)。

3

這是很容易使用掌握新的多指標切片機/ 0.14(即將發佈),請參閱here

有一個懸而未決的問題,使這個syntatically容易(它不是很難做到),見here 例如是這樣的:df.loc[{ 'third' : ['C1','C3'] }]我認爲是合理的

這裏是你如何能做到這一點(需要主/ 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] 

所有的文件創建一個索引VELS,選擇所有條目

In [15]: indexer = [slice(None)]*len(df.index.names) 

讓我們只關心程度有我們關心的條目約

In [16]: indexer[df.index.names.index('third')] = ['C1','C3'] 

選擇它(它的重要,這是一個元組!)

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] 
+0

爲清晰起見,在這個數據幀WOU手動這樣做ld看起來像'df.loc [pd.IndexSlice [:,:,['C1','C3'],:],:]'或'df.loc(axis = 0)[:,:,''C1 ','C3'],:]' – joris

+0

有沒有辦法從MultiIndex中選擇一個日期範圍?我有'df.loc [:, pd.IndexSlice [:,:,:,:,'value']]'第一個':'是一個日期,我想按日期範圍分片,而不僅僅是一天。 – toasteez

4

我確實提出了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 

...用作

df.filter_by({'level_name' : value}) 

其中value可的確是一個單值,但同樣是一個列表,切片...

(未經測試與面板和高維元素,但我不希望它的工作)