2015-03-25 5 views
14

schneidet Ich habe einen Pandas Datenrahmen mit 3 Ebenen eines MultiIndex. Ich versuche, Zeilen dieses Datenrahmens gemäß einer Liste von Werten herauszuziehen, die zwei der Ebenen entsprechen.Wie man einen MultiIndex DataFrame mit dem MultiIndex eines anderen

Ich habe so etwas wie dies:

ix = pd.MultiIndex.from_product([[1, 2, 3], ['foo', 'bar'], ['baz', 'can']], names=['a', 'b', 'c']) 
data = np.arange(len(ix)) 
df = pd.DataFrame(data, index=ix, columns=['hi']) 
print(df) 

      hi 
a b c  
1 foo baz 0 
     can 1 
    bar baz 2 
     can 3 
2 foo baz 4 
     can 5 
    bar baz 6 
     can 7 
3 foo baz 8 
     can 9 
    bar baz 10 
     can 11 

Jetzt möchte ich alle Zeilen nehmen, wo Indexniveaus 'b' und 'c' in diesem Index sind:

ix_use = pd.MultiIndex.from_tuples([('foo', 'can'), ('bar', 'baz')], names=['b', 'c']) 

dh Werte von hi mit ('foo', 'can') oder ('bar', 'baz') in den Ebenen b bzw. c: (1, 2, 5, 6, 9, 10).

Also würde ich gerne eine slice(None) auf der ersten Ebene nehmen und bestimmte Tupel auf der zweiten und dritten Ebene herausziehen.

Anfangs dachte ich, dass das Übergeben eines Multi-Index-Objekts an .loc die Werte/Ebenen herausziehen würde, die ich wollte, aber das funktioniert nicht. Was ist der beste Weg, um so etwas zu tun?

+0

ich immer daran zu arbeiten, ein paar verschiedene Versuche unternommen hatte. Ich denke, ich habe einen ordentlichen Workaround für die Tatsache gefunden, dass dies momentan etwas schwierig zu sein scheint. Versuch es! – LondonRob

Antwort

19

Hier ist ein Weg, um diese Scheibe zu bekommen:

df.sort_index(inplace=True) 
idx = pd.IndexSlice 
df.loc[idx[:, ('foo','bar'), 'can'], :] 

  hi 
a b c  
1 bar can 3 
    foo can 1 
2 bar can 7 
    foo can 5 
3 bar can 11 
    foo can 9 

Hinweis nachgebend, die Sie Multiindex sortieren könnte, bevor Sie es in Scheiben schneiden kann. Nun Pandas ist nett genug, um zu warnen, wenn Sie es tun müssen:

KeyError: 'MultiIndex Slicing requires the index to be fully lexsorted tuple len (3), lexsort depth (1)' 

Sie können lesen Sie mehr auf, wie Slicer bis hin zum Einsatz in der docs

Wenn aus irgendeinem Grund Slicer verwendet, ist keine Option hier ist ein Weg, um die gleiche Scheibe mit .isin() Methode zu erhalten:

df[df.index.get_level_values('b').isin(ix_use.get_level_values(0)) & df.index.get_level_values('c').isin(ix_use.get_level_values(1))] 

, die eindeutig nicht so präzise ist.

UPDATE:

Für die Bedingungen, die Sie hier aktualisiert haben, ist ein Weg, es zu tun:

cond1 = (df.index.get_level_values('b').isin(['foo'])) & (df.index.get_level_values('c').isin(['can'])) 
cond2 = (df.index.get_level_values('b').isin(['bar'])) & (df.index.get_level_values('c').isin(['baz'])) 
df[cond1 | cond2] 

Herstellung:

  hi 
a b c  
1 foo can 1 
    bar baz 2 
2 foo can 5 
    bar baz 6 
3 foo can 9 
    bar baz 10 
+0

Das ist wirklich nah dran an dem was ich suche, aber ich hätte die Frage klarer formulieren sollen. In Wirklichkeit benötige ich einen Wert vom Level "c", der vom Wert im Level "b" abhängt. ZB jedes Mal, wenn Level "b" ist "foo", ich möchte den Wert, wo Ebene "c" ist "kann", und jederzeit die Ebene "b" ist "bar", ich möchte den Wert, wo Level " c "is 'baz' – choldgraf

+0

Aktualisierte die Antwort mit diesen zwei Bedingungen, die eine Idee geben sollten, wie man damit umgeht. – Primer

0

Ich finde es interessant, dass dieses doesn ‚t Arbeit:

In [45]: df.loc[(idx[:, 'foo', 'can'], idx[:, 'bar', 'baz']), ] 
Out[45]: 
      hi 
a b c  
1 bar baz 2 
     can 3 
    foo baz 0 
     can 1 
2 bar baz 6 
     can 7 
    foo baz 4 
     can 5 
3 bar baz 10 
     can 11 
    foo baz 8 
     can 9 

Es sieht so aus, als ob es irgendwie "sollte". In jedem Fall ist, hier eine vernünftige Lösung:

Lassen Sie uns die Tupel annehmen, dass Sie schneiden wollen, indem sie in den Index eines anderen DataFrame sind (da es klingt wie sie wahrscheinlich in Ihrem Fall sind!).

In [53]: ix_use = pd.MultiIndex.from_tuples([('foo', 'can'), ('bar', 'baz')], names=['b', 'c']) 
In [55]: other = pd.DataFrame(dict(a=1), index=ix_use) 
In [56]: other 
Out[56]: 
     a 
b c  
foo can 1 
bar baz 1 

Jetzt df durch den Index der other schneiden wir die Tatsache nutzen, dass .loc/.ix können Sie eine Liste von Tupeln geben (siehe das letzte Beispiel here).

Lassen Sie uns zunächst die Liste von Tupeln bauen wir wollen:

In [13]: idx = [(x,) + y for x in df.index.levels[0] for y in other.index.values] 
In [14]: idx 
Out[14]: 
[(1, 'foo', 'can'), 
(1, 'bar', 'baz'), 
(2, 'foo', 'can'), 
(2, 'bar', 'baz'), 
(3, 'foo', 'can'), 
(3, 'bar', 'baz')] 

Jetzt können wir diese Liste .ix oder .loc passieren:

In [17]: df.ix[idx] 
Out[17]: 
      hi 
a b c  
1 foo can 1 
    bar baz 2 
2 foo can 5 
    bar baz 6 
3 foo can 9 
    bar baz 10 
2

ich the query() method empfehlen würde wie nur in this Q&A.

einfach mit diesem, was meiner Meinung nach ein natürlicher Weg, um auszudrücken:

In [27]: df.query("(b == 'foo' and c == 'can') or (b == 'bar' and c == 'baz')") 
Out[27]: 
      hi 
a b c  
1 foo can 1 
    bar baz 2 
2 foo can 5 
    bar baz 6 
3 foo can 9 
    bar baz 10