2014-04-10 23 views
9

this answer'dan esinlenilen ve this question'a kolay bir cevabın bulunmadığı bir ortamda, MultiIndex düzeyine göre filtrelenmeyi kolaylaştırmak için kendimi biraz sözdizimsel şeker yazarken buldum. Ben pandalar geliştirme ekibi biliyorsanızDilim pandaları MultiIndex düzeyine veya alt seviyeye göre DataFrame

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" 

Ama (ve yavaş yavaş, başlıyorum!) Zaten bunu yapmak için iyi bir yol var ve ben sadece henüz ne olduğunu bilmiyorum!

Doğru muyum?

cevap

1

Böyle şeyler yapabilen filter yöntemine sahipsiniz. linked SO Söz konusu istendi örnekle Ör:

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 

filtre yöntemi (yaklaşan 0.14 olarak) şu anda refactored olduğunu ve şimdi bir sorun varsa olabilir çünkü bir level anahtar kelime (eklenecek aynı etiketler dizinin farklı düzeylerinde görüntülenir).

3

Bu Master'de yeni çok indeks dilimleme kullanarak çok kolay/0.14 (Yakında), bu syntatically daha kolay (onun zor değil yapmak için), here bakınız yapmak için bir açık sorunu var here

bkz böyle örn şey: df.loc[{ 'third' : ['C1','C3'] }] bence İşte

bunu yapabilirsiniz nasıl makul (gerektirir ana/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] 

le tamamında dizinleyici yaratmak VELS, seçme tüm girişler

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

biz sadece umurumda seviye umrumuzda girdilerine sahip olun yaklaşık

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

onu seçiniz (onun bu tuple olması önemlidir!)

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

Görünüşe göre df.loc [pd.IndexSlice [:,:, ['C1', 'C3'],:],:] 'veya' df.loc (eksen = 0) [:,:, ['C1 ',' C3 '],:] ' – joris

+0

Bir MultiIndex'ten bir tarih aralığı seçmek için bir yol var mı? Ben df.loc [:, pd.IndexSlice [:,:,:,:, 'değer']] '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''] bir tarih olur ve yalnızca bir tarih aralığıyla dilimlemek istiyorum. tek bir gün. – toasteez

4

Ben aslında joris'in cevabını savundum ... ama ne yazık ki o refactoring o 0.14 yılında gerçekleşmedi ve 0.17 de olmuyor. Yani şu an için beni (besbelli Jeff'in birinden türetilmiş) hızlı ve kirli çözüm önerdiğimiz:

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 
...

value gerçekten tek bir değer olabilir
df.filter_by({'level_name' : value}) 

olarak kullanılmak üzere, ama aynı zamanda bir liste, bir dilim ...

(Panelleri ve daha yüksek boyut elemanları ile denenmemiş, ama işe bekliyoruz) elle dataframe Yara sarıcı bu yaptığını, Anlaşılır olması için

İlgili konular