2014-04-10 26 views
9

Lấy cảm hứng từ this answer và không có câu trả lời dễ dàng cho this question Tôi thấy mình viết một cú pháp nhỏ để làm cho cuộc sống dễ dàng hơn để lọc theo cấp MultiIndex.Slice pandas DataFrame theo cấp MultiIndex hoặc 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" 

Nhưng nếu tôi biết nhóm phát triển gấu trúc (và tôi bắt đầu, từ từ!) Đã có cách tốt để làm điều này và tôi vẫn chưa biết nó là gì!

Tôi có đúng không?

Trả lời

1

Bạn có phương thức filter có thể thực hiện những việc như thế này. Ví dụ như với ví dụ đã được hỏi trong câu hỏi 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 

Phương pháp lọc là refactored tại thời điểm (trong sắp tới 0,14), và một từ khóa level sẽ được thêm vào (vì bây giờ bạn có thể có một vấn đề nếu các nhãn giống nhau xuất hiện ở các cấp khác nhau của chỉ mục).

3

này là rất dễ dàng bằng cách sử dụng máy thái đa chỉ số mới trong chủ/0.14 (phát hành sớm), xem here

Có một vấn đề mở để làm điều này syntatically dễ dàng hơn (nó không khó để làm), xem here ví dụ như một cái gì đó như thế này: df.loc[{ 'third' : ['C1','C3'] }] tôi nghĩ là hợp lý

đây là cách bạn có thể làm điều đó (yêu cầu chủ/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] 

Tạo một indexer trên tất cả các le VELS, chọn tất cả các mục

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

Make mức chúng ta quan tâm chỉ có các mục chúng tôi quan tâm

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

Chọn nó (nó quan trọng rằng đây là một tuple!)

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

để rõ ràng, làm điều này bằng tay trên wou dataframe này ld trông giống như 'df.loc [pd.IndexSlice [:,:, ['C1', 'C3'],:],:]' hoặc 'df.loc (trục = 0) [:,:, ['C1 ',' C3 '],:] ' – joris

+0

Có cách nào để chọn phạm vi ngày từ một MultiIndex không? Tôi có 'df.loc [:, pd.IndexSlice [:,:,:,:, 'value']]' trong đó ':' đầu tiên sẽ là ngày và tôi muốn cắt theo phạm vi ngày và không chỉ một ngày. – toasteez

4

Tôi thực sự upvoted câu trả lời của joris ... nhưng tiếc là refactoring ông đề cập đến đã không xảy ra trong 0,14 và không xảy ra trong 0,17 không. Vì vậy, cho thời điểm này cho tôi đề nghị một giải pháp nhanh chóng và dơ bẩn (rõ ràng là có nguồn gốc từ một Jeff):

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 

... được sử dụng như

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

nơi value có thể thực sự là một giá trị duy nhất, mà còn là một danh sách, một lát ...

(chưa được kiểm tra với Panels và các yếu tố chiều cao, nhưng tôi hy vọng nó sẽ làm việc)

Các vấn đề liên quan