Wählen Sie Zeilen in Pandas MultiIndex DataFrame aus

145

Was sind die häufigsten Pandas-Methoden zum Auswählen / Filtern von Zeilen eines Datenrahmens, dessen Index ein MultiIndex ist ?

  • Schneiden basierend auf einem einzelnen Wert / Etikett
  • Schneiden basierend auf mehreren Etiketten von einer oder mehreren Ebenen
  • Filtern nach booleschen Bedingungen und Ausdrücken
  • Welche Methoden sind unter welchen Umständen anwendbar?

Annahmen zur Vereinfachung:

  1. Der Eingabedatenrahmen verfügt nicht über doppelte Indexschlüssel
  2. Der Eingabedatenrahmen unten hat nur zwei Ebenen. (Die meisten hier gezeigten Lösungen verallgemeinern sich auf N Niveaus)

Beispieleingabe:

mux = pd.MultiIndex.from_arrays([
    list('aaaabbbbbccddddd'),
    list('tuvwtuvwtuvwtuvw')
], names=['one', 'two'])

df = pd.DataFrame({'col': np.arange(len(mux))}, mux)

         col
one two     
a   t      0
    u      1
    v      2
    w      3
b   t      4
    u      5
    v      6
    w      7
    t      8
c   u      9
    v     10
d   w     11
    t     12
    u     13
    v     14
    w     15

Frage 1: Auswahl eines einzelnen Elements

Wie wähle ich Zeilen mit "a" in Ebene "eins" aus?

         col
one two     
a   t      0
    u      1
    v      2
    w      3

Wie könnte ich außerdem die Stufe "Eins" in der Ausgabe löschen?

     col
two     
t      0
u      1
v      2
w      3

Frage 1b
Wie schneide ich alle Zeilen mit dem Wert "t" auf Ebene "zwei"?

         col
one two     
a   t      0
b   t      4
    t      8
d   t     12

Frage 2: Auswählen mehrerer Werte in einer Ebene

Wie kann ich Zeilen auswählen, die den Elementen "b" und "d" in Ebene "eins" entsprechen?

         col
one two     
b   t      4
    u      5
    v      6
    w      7
    t      8
d   w     11
    t     12
    u     13
    v     14
    w     15

Frage 2b
Wie würde ich alle Werte erhalten, die "t" und "w" in Stufe "zwei" entsprechen?

         col
one two     
a   t      0
    w      3
b   t      4
    w      7
    t      8
d   w     11
    t     12
    w     15

Frage 3: Schneiden eines einzelnen Querschnitts (x, y)

Wie rufe ich einen Querschnitt ab, dh eine einzelne Zeile mit bestimmten Werten für den Index aus df? Wie rufe ich den Querschnitt von ('c', 'u'), gegeben durch

         col
one two     
c   u      9

Frage 4: Schneiden mehrerer Querschnitte [(a, b), (c, d), ...]

Wie wähle ich die beiden Zeilen aus ('c', 'u'), die und entsprechen ('a', 'w')?

         col
one two     
c   u      9
a   w      3

Frage 5: Ein Gegenstand pro Stufe

Wie kann ich alle Zeilen abrufen, die "a" in Ebene "eins" oder "t" in Ebene "zwei" entsprechen?

         col
one two     
a   t      0
    u      1
    v      2
    w      3
b   t      4
    t      8
d   t     12

Frage 6: Willkürliches Schneiden

Wie kann ich bestimmte Querschnitte schneiden? Für "a" und "b" möchte ich alle Zeilen mit den Unterebenen "u" und "v" auswählen, und für "d" möchte ich Zeilen mit der Unterebene "w" auswählen.

         col
one two     
a   u      1
    v      2
b   u      5
    v      6
d   w     11
    w     15

Frage 7 verwendet ein eindeutiges Setup, das aus einer numerischen Ebene besteht:

np.random.seed(0)
mux2 = pd.MultiIndex.from_arrays([
    list('aaaabbbbbccddddd'),
    np.random.choice(10, size=16)
], names=['one', 'two'])

df2 = pd.DataFrame({'col': np.arange(len(mux2))}, mux2)

         col
one two     
a   5      0
    0      1
    3      2
    3      3
b   7      4
    9      5
    3      6
    5      7
    2      8
c   4      9
    7     10
d   6     11
    8     12
    8     13
    1     14
    6     15

Frage 7: Filtern nach numerischer Ungleichung auf einzelnen Ebenen des Multiindex

Wie erhalte ich alle Zeilen, in denen Werte in Stufe "zwei" größer als 5 sind?

         col
one two     
b   7      4
    9      5
c   7     10
d   6     11
    8     12
    8     13
    6     15

Hinweis: In diesem Beitrag wird nicht erläutert, wie Sie MultiIndexe erstellen, Zuweisungsvorgänge für sie ausführen oder leistungsbezogene Diskussionen führen (dies sind separate Themen für ein anderes Mal).

cs95
quelle

Antworten:

165

MultiIndex / Erweiterte Indizierung

Hinweis
Dieser Beitrag ist folgendermaßen aufgebaut:

  1. Die im OP gestellten Fragen werden nacheinander behandelt
  2. Für jede Frage werden eine oder mehrere Methoden demonstriert, mit denen dieses Problem gelöst und das erwartete Ergebnis erzielt werden kann.

Hinweise (ähnlich wie diese) sind für Leser enthalten, die sich über zusätzliche Funktionen, Implementierungsdetails und andere Informationen zum jeweiligen Thema informieren möchten. Diese Notizen wurden zusammengestellt, indem die Dokumente durchsucht und verschiedene dunkle Merkmale aufgedeckt wurden, und zwar aus meiner eigenen (zugegebenermaßen begrenzten) Erfahrung.

Alle Codebeispiele wurden unter pandas v0.23.4, python3.7 erstellt und getestet . Wenn etwas nicht klar oder sachlich falsch ist oder wenn Sie keine für Ihren Anwendungsfall geeignete Lösung gefunden haben, können Sie eine Änderung vorschlagen, in den Kommentaren eine Klarstellung anfordern oder eine neue Frage öffnen .

Hier ist eine Einführung in einige gebräuchliche Redewendungen (im Folgenden als die vier Redewendungen bezeichnet), die wir häufig wieder besuchen werden

  1. DataFrame.loc- Eine allgemeine Lösung für die Auswahl nach Etikett (+ pd.IndexSlicefür komplexere Anwendungen mit Scheiben)

  2. DataFrame.xs - Extrahieren Sie einen bestimmten Querschnitt aus einer Serie / einem DataFrame.

  3. DataFrame.query- Geben Sie Schnitt- und / oder Filtervorgänge dynamisch an (dh als Ausdruck, der dynamisch ausgewertet wird. Dies gilt für einige Szenarien besser als für andere. Informationen zum Abfragen von MultiIndexes finden Sie auch in diesem Abschnitt der Dokumente .

  4. Boolesche Indizierung mit einer Maske, die mit generiert wurde MultiIndex.get_level_values(häufig in Verbindung mit Index.isin, insbesondere beim Filtern mit mehreren Werten). Dies ist unter bestimmten Umständen auch sehr nützlich.

Es ist von Vorteil, die verschiedenen Probleme beim Schneiden und Filtern im Hinblick auf die vier Redewendungen zu betrachten, um ein besseres Verständnis dafür zu erhalten, was auf eine bestimmte Situation angewendet werden kann. Es ist sehr wichtig zu verstehen, dass nicht alle Redewendungen unter allen Umständen gleich gut (wenn überhaupt) funktionieren. Wenn ein Idiom nicht als mögliche Lösung für ein Problem aufgeführt ist, bedeutet dies, dass das Idiom nicht effektiv auf dieses Problem angewendet werden kann.


Frage 1

Wie wähle ich Zeilen mit "a" in Ebene "eins" aus?

         col
one two     
a   t      0
    u      1
    v      2
    w      3

Sie können locals Allzwecklösung für die meisten Situationen Folgendes verwenden:

df.loc[['a']]

An diesem Punkt, wenn Sie bekommen

TypeError: Expected tuple, got str

Das bedeutet, dass Sie eine ältere Version von Pandas verwenden. Erwägen Sie ein Upgrade! Andernfalls verwenden Sie df.loc[('a', slice(None)), :].

Alternativ können Sie xshier verwenden, da wir einen einzelnen Querschnitt extrahieren. Beachten Sie die Argumente levelsund axis(hier können vernünftige Standardeinstellungen angenommen werden).

df.xs('a', level=0, axis=0, drop_level=False)
# df.xs('a', drop_level=False)

Hier wird das drop_level=FalseArgument benötigt, um zu verhindern, xsdass Level "Eins" im Ergebnis (das Level, auf das wir geschnitten haben) abfällt.

Eine weitere Option ist die Verwendung von query:

df.query("one == 'a'")

Wenn der Index keinen Namen hätte, müssten Sie Ihre Abfragezeichenfolge ändern "ilevel_0 == 'a'".

Schließlich mit get_level_values:

df[df.index.get_level_values('one') == 'a']
# If your levels are unnamed, or if you need to select by position (not label),
# df[df.index.get_level_values(0) == 'a']

Wie könnte ich außerdem die Stufe "Eins" in der Ausgabe löschen?

     col
two     
t      0
u      1
v      2
w      3

Dies kann einfach mit beiden durchgeführt werden

df.loc['a'] # Notice the single string argument instead the list.

Oder,

df.xs('a', level=0, axis=0, drop_level=True)
# df.xs('a')

Beachten Sie, dass wir das drop_levelArgument weglassen können (es wird Truestandardmäßig angenommen).

Hinweis
Möglicherweise stellen Sie fest, dass ein gefilterter DataFrame möglicherweise noch alle Ebenen aufweist, auch wenn diese beim Ausdrucken des DataFrame nicht angezeigt werden. Beispielsweise,

v = df.loc[['a']]
print(v)
         col
one two     
a   t      0
    u      1
    v      2
    w      3

print(v.index)
MultiIndex(levels=[['a', 'b', 'c', 'd'], ['t', 'u', 'v', 'w']],
           labels=[[0, 0, 0, 0], [0, 1, 2, 3]],
           names=['one', 'two'])

Sie können diese Ebenen entfernen, indem Sie MultiIndex.remove_unused_levels:

v.index = v.index.remove_unused_levels()

print(v.index)
MultiIndex(levels=[['a'], ['t', 'u', 'v', 'w']],
           labels=[[0, 0, 0, 0], [0, 1, 2, 3]],
           names=['one', 'two'])

Frage 1b

Wie schneide ich alle Zeilen mit dem Wert "t" auf Ebene "zwei"?

         col
one two     
a   t      0
b   t      4
    t      8
d   t     12

Intuitiv möchten Sie etwas mit slice():

df.loc[(slice(None), 't'), :]

Es funktioniert einfach! ™ Aber es ist klobig. Mit der pd.IndexSliceAPI können wir hier eine natürlichere Slicing-Syntax ermöglichen .

idx = pd.IndexSlice
df.loc[idx[:, 't'], :]

Das ist viel, viel sauberer.

Hinweis
Warum ist das nachfolgende Slice :über die Spalten erforderlich? Dies liegt daran, locdass Sie beide Achsen ( axis=0oder axis=1) auswählen und entlang schneiden können . Ohne explizit klar zu machen, auf welcher Achse das Schneiden durchgeführt werden soll, wird die Operation mehrdeutig. Siehe das große rote Kästchen in der Dokumentation zum Schneiden .

Wenn Sie Mehrdeutigkeiten entfernen möchten, locakzeptieren Sie einen axis Parameter:

df.loc(axis=0)[pd.IndexSlice[:, 't']]

Ohne den axisParameter (dh nur durch Ausführen df.loc[pd.IndexSlice[:, 't']]) wird angenommen, dass sich das Schneiden in den Spalten befindet, und unter KeyErrordiesen Umständen wird a ausgelöst.

Dies ist in Slicern dokumentiert . Für den Zweck dieses Beitrags werden jedoch alle Achsen explizit angegeben.

Mit xsist es

df.xs('t', axis=0, level=1, drop_level=False)

Mit queryist es

df.query("two == 't'")
# Or, if the first level has no name, 
# df.query("ilevel_1 == 't'") 

Und schließlich, mit get_level_values, können Sie tun

df[df.index.get_level_values('two') == 't']
# Or, to perform selection by position/integer,
# df[df.index.get_level_values(1) == 't']

Alles zum gleichen Effekt.


Frage 2

Wie kann ich Zeilen auswählen, die den Elementen "b" und "d" in Ebene "eins" entsprechen?

         col
one two     
b   t      4
    u      5
    v      6
    w      7
    t      8
d   w     11
    t     12
    u     13
    v     14
    w     15

Bei Verwendung von loc erfolgt dies auf ähnliche Weise durch Angabe einer Liste.

df.loc[['b', 'd']]

Um das obige Problem der Auswahl von "b" und "d" zu lösen, können Sie auch Folgendes verwenden query:

items = ['b', 'd']
df.query("one in @items")
# df.query("one == @items", parser='pandas')
# df.query("one in ['b', 'd']")
# df.query("one == ['b', 'd']", parser='pandas')

Hinweis
Ja, der Standardparser ist 'pandas', aber es ist wichtig hervorzuheben, dass diese Syntax nicht herkömmlich Python ist. Der Pandas-Parser generiert einen etwas anderen Analysebaum als der Ausdruck. Dies geschieht, um die Angabe einiger Vorgänge intuitiver zu gestalten. Weitere Informationen finden Sie in meinem Beitrag zur Auswertung dynamischer Ausdrücke in Pandas mit pd.eval () .

Und mit get_level_values+ Index.isin:

df[df.index.get_level_values("one").isin(['b', 'd'])]

Frage 2b

Wie würde ich alle Werte erhalten, die "t" und "w" in Stufe "zwei" entsprechen?

         col
one two     
a   t      0
    w      3
b   t      4
    w      7
    t      8
d   w     11
    t     12
    w     15

Mit locist dies nur in Verbindung mit möglich pd.IndexSlice.

df.loc[pd.IndexSlice[:, ['t', 'w']], :] 

Der erste Doppelpunkt :in pd.IndexSlice[:, ['t', 'w']]bedeutet, über die erste Ebene zu schneiden. Wenn die Tiefe des abgefragten Levels zunimmt, müssen Sie mehr Slices angeben, eines pro Level, über das geschnitten wird. Sie werden nicht mehr Ebenen angeben müssen über die von denen eine in Scheiben geschnitten, jedoch.

Mit query, das ist

items = ['t', 'w']
df.query("two in @items")
# df.query("two == @items", parser='pandas') 
# df.query("two in ['t', 'w']")
# df.query("two == ['t', 'w']", parser='pandas')

Mit get_level_valuesund Index.isin(ähnlich wie oben):

df[df.index.get_level_values('two').isin(['t', 'w'])]

Frage 3

Wie rufe ich einen Querschnitt ab, dh eine einzelne Zeile mit bestimmten Werten für den Index aus df? Wie rufe ich den Querschnitt von ('c', 'u'), gegeben durch

         col
one two     
c   u      9

Verwenden Sie diese locOption, indem Sie ein Tupel von Schlüsseln angeben:

df.loc[('c', 'u'), :]

Oder,

df.loc[pd.IndexSlice[('c', 'u')]]

Hinweis
An dieser Stelle kann es vorkommen, PerformanceWarningdass Sie auf Folgendes stoßen:

PerformanceWarning: indexing past lexsort depth may impact performance.

Dies bedeutet nur, dass Ihr Index nicht sortiert ist. Pandas hängen vom zu sortierenden Index ab (in diesem Fall lexikografisch, da es sich um Zeichenfolgenwerte handelt), um eine optimale Suche und einen optimalen Abruf zu gewährleisten. Eine schnelle Lösung wäre, Ihren DataFrame im Voraus mit zu sortieren DataFrame.sort_index. Dies ist unter Leistungsgesichtspunkten besonders wünschenswert, wenn Sie mehrere solcher Abfragen gleichzeitig ausführen möchten:

df_sort = df.sort_index()
df_sort.loc[('c', 'u')]

Sie können auch MultiIndex.is_lexsorted()überprüfen, ob der Index sortiert ist oder nicht. Diese Funktion kehrt zurück Trueoder Falseentsprechend. Sie können diese Funktion aufrufen, um festzustellen, ob ein zusätzlicher Sortierschritt erforderlich ist oder nicht.

Mit xswird hiermit einfach ein einzelnes Tupel als erstes Argument übergeben, wobei alle anderen Argumente auf die entsprechenden Standardeinstellungen gesetzt werden:

df.xs(('c', 'u'))

Mit querywerden die Dinge etwas klobig:

df.query("one == 'c' and two == 'u'")

Sie können jetzt sehen, dass dies relativ schwer zu verallgemeinern sein wird. Ist aber für dieses spezielle Problem noch in Ordnung.

Mit Zugriffen auf mehreren Ebenen get_level_valueskann weiterhin verwendet werden, wird jedoch nicht empfohlen:

m1 = (df.index.get_level_values('one') == 'c')
m2 = (df.index.get_level_values('two') == 'u')
df[m1 & m2]

Frage 4

Wie wähle ich die beiden Zeilen aus ('c', 'u'), die und entsprechen ('a', 'w')?

         col
one two     
c   u      9
a   w      3

Mit locist dies immer noch so einfach wie:

df.loc[[('c', 'u'), ('a', 'w')]]
# df.loc[pd.IndexSlice[[('c', 'u'), ('a', 'w')]]]

Mit querymüssen Sie dynamisch eine Abfragezeichenfolge generieren, indem Sie Ihre Querschnitte und Ebenen durchlaufen:

cses = [('c', 'u'), ('a', 'w')]
levels = ['one', 'two']
# This is a useful check to make in advance.
assert all(len(levels) == len(cs) for cs in cses) 

query = '(' + ') or ('.join([
    ' and '.join([f"({l} == {repr(c)})" for l, c in zip(levels, cs)]) 
    for cs in cses
]) + ')'

print(query)
# ((one == 'c') and (two == 'u')) or ((one == 'a') and (two == 'w'))

df.query(query)

100% NICHT EMPFEHLEN! Aber es ist möglich.


Frage 5

Wie kann ich alle Zeilen abrufen, die "a" in Ebene "eins" oder "t" in Ebene "zwei" entsprechen?

         col
one two     
a   t      0
    u      1
    v      2
    w      3
b   t      4
    t      8
d   t     12

Dies ist tatsächlich sehr schwierig zu tun, locwährend die Korrektheit sichergestellt wird und die Klarheit des Codes erhalten bleibt. df.loc[pd.IndexSlice['a', 't']]falsch ist, wird es interpretiert als df.loc[pd.IndexSlice[('a', 't')]](dh Auswahl eines Querschnitts). Sie können sich eine Lösung vorstellen pd.concat, mit der Sie jedes Etikett separat behandeln können:

pd.concat([
    df.loc[['a'],:], df.loc[pd.IndexSlice[:, 't'],:]
])

         col
one two     
a   t      0
    u      1
    v      2
    w      3
    t      0   # Does this look right to you? No, it isn't!
b   t      4
    t      8
d   t     12

Sie werden jedoch feststellen, dass eine der Zeilen dupliziert ist. Dies liegt daran, dass diese Reihe beide Schnittbedingungen erfüllte und daher zweimal auftrat. Sie müssen stattdessen tun

v = pd.concat([
        df.loc[['a'],:], df.loc[pd.IndexSlice[:, 't'],:]
])
v[~v.index.duplicated()]

Wenn Ihr DataFrame jedoch von Natur aus doppelte Indizes enthält (die Sie möchten), werden diese nicht beibehalten. Mit äußerster Vorsicht verwenden .

Mit queryist das blöd einfach:

df.query("one == 'a' or two == 't'")

Mit get_level_valuesist das noch einfach, aber nicht so elegant:

m1 = (df.index.get_level_values('one') == 'a')
m2 = (df.index.get_level_values('two') == 't')
df[m1 | m2] 

Frage 6

Wie kann ich bestimmte Querschnitte schneiden? Für "a" und "b" möchte ich alle Zeilen mit den Unterebenen "u" und "v" auswählen, und für "d" möchte ich Zeilen mit der Unterebene "w" auswählen.

         col
one two     
a   u      1
    v      2
b   u      5
    v      6
d   w     11
    w     15

Dies ist ein Sonderfall, den ich hinzugefügt habe, um die Anwendbarkeit der vier Redewendungen zu verstehen. Dies ist ein Fall, in dem keine von ihnen effektiv funktioniert, da das Schneiden sehr spezifisch ist und keinem wirklichen Muster folgt.

Normalerweise erfordert das Schneiden solcher Probleme das explizite Übergeben einer Liste von Schlüsseln an loc. Eine Möglichkeit, dies zu tun, ist:

keys = [('a', 'u'), ('a', 'v'), ('b', 'u'), ('b', 'v'), ('d', 'w')]
df.loc[keys, :]

Wenn Sie einige Eingaben speichern möchten, werden Sie erkennen, dass das Schneiden von "a", "b" und seinen Unterebenen ein Muster hat, sodass wir die Schneideaufgabe in zwei Teile und concatdas Ergebnis aufteilen können :

pd.concat([
     df.loc[(('a', 'b'), ('u', 'v')), :], 
     df.loc[('d', 'w'), :]
   ], axis=0)

Die Schnittspezifikation für "a" und "b" ist etwas sauberer, (('a', 'b'), ('u', 'v'))da die gleichen zu indizierenden Unterebenen für jede Ebene gleich sind.


Frage 7

Wie erhalte ich alle Zeilen, in denen Werte in Stufe "zwei" größer als 5 sind?

         col
one two     
b   7      4
    9      5
c   7     10
d   6     11
    8     12
    8     13
    6     15

Dies kann erfolgen mit query:

df2.query("two > 5")

Und get_level_values.

df2[df2.index.get_level_values('two') > 5]

Hinweis
Ähnlich wie in diesem Beispiel können wir mit diesen Konstrukten basierend auf einer beliebigen Bedingung filtern. In der Regel ist es sinnvoll , dass sich daran zu erinnern locund xssind speziell für den Etikettenbasierte Indizierung, während queryund get_level_valuesnützlich sind für den Aufbau von allgemeinen bedingten Masken zum Filtern.


Bonus-Frage

Was ist, wenn ich eine MultiIndex Spalte in Scheiben schneiden muss ?

Tatsächlich gelten die meisten Lösungen mit geringfügigen Änderungen auch für Spalten. Erwägen:

np.random.seed(0)
mux3 = pd.MultiIndex.from_product([
        list('ABCD'), list('efgh')
], names=['one','two'])

df3 = pd.DataFrame(np.random.choice(10, (3, len(mux))), columns=mux3)
print(df3)

one  A           B           C           D         
two  e  f  g  h  e  f  g  h  e  f  g  h  e  f  g  h
0    5  0  3  3  7  9  3  5  2  4  7  6  8  8  1  6
1    7  7  8  1  5  9  8  9  4  3  0  3  5  0  2  3
2    8  1  3  3  3  7  0  1  9  9  0  4  7  3  2  7

Dies sind die folgenden Änderungen, die Sie an den vier Redewendungen vornehmen müssen, damit sie mit Spalten arbeiten.

  1. Zum Schneiden locverwenden

    df3.loc[:, ....] # Notice how we slice across the index with `:`. 

    oder,

    df3.loc[:, pd.IndexSlice[...]]
  2. Übergeben Sie zur Verwendung xseinfach ein Argument axis=1.

  3. Sie können direkt mit auf die Werte auf Spaltenebene zugreifen df.columns.get_level_values. Sie müssen dann so etwas tun

    df.loc[:, {condition}] 

    Wobei {condition}eine Bedingung dargestellt wird, die mit erstellt wurde columns.get_level_values.

  4. Zur Verwendung querybesteht Ihre einzige Option darin, zu transponieren, den Index abzufragen und erneut zu transponieren:

    df3.T.query(...).T

    Nicht empfohlen, verwenden Sie eine der anderen 3 Optionen.

cs95
quelle
5

Kürzlich stieß ich auf einen Anwendungsfall, in dem ich einen 3-Level-Multi-Index-Datenrahmen hatte, in dem ich mit keiner der oben genannten Lösungen die gewünschten Ergebnisse erzielen konnte. Es ist durchaus möglich, dass die oben genannten Lösungen natürlich für meinen Anwendungsfall funktionieren, und ich habe mehrere ausprobiert, konnte sie jedoch nicht mit der mir zur Verfügung stehenden Zeit zum Laufen bringen.

Ich bin kein Experte, aber ich bin auf eine Lösung gestoßen, die in den obigen umfassenden Antworten nicht aufgeführt ist. Ich kann nicht garantieren, dass die Lösungen in irgendeiner Weise optimal sind.

Dies ist ein anderer Weg, um ein etwas anderes Ergebnis zu erzielen als in Frage 6 oben. (und wahrscheinlich auch andere Fragen)

Speziell suchte ich:

  1. Eine Möglichkeit, zwei + Werte aus einer Indexebene und einen einzelnen Wert aus einer anderen Indexebene auszuwählen, und
  2. Eine Möglichkeit, die Indexwerte aus der vorherigen Operation in der Datenrahmenausgabe zu belassen.

Als Schraubenschlüssel in den Zahnrädern (jedoch vollständig reparierbar):

  1. Die Indizes waren unbenannt.

Auf dem Spielzeugdatenrahmen unten:

    index = pd.MultiIndex.from_product([['a','b'],
                               ['stock1','stock2','stock3'],
                               ['price','volume','velocity']])

    df = pd.DataFrame([1,2,3,4,5,6,7,8,9,
                      10,11,12,13,14,15,16,17,18], 
                       index)

                        0
    a stock1 price      1
             volume     2
             velocity   3
      stock2 price      4
             volume     5
             velocity   6
      stock3 price      7
             volume     8
             velocity   9
    b stock1 price     10
             volume    11
             velocity  12
      stock2 price     13
             volume    14
             velocity  15
      stock3 price     16
             volume    17
             velocity  18

Die folgenden Funktionen funktionieren natürlich:

    df.xs(('stock1', 'velocity'), level=(1,2))

        0
    a   3
    b  12

Aber ich wollte ein anderes Ergebnis, also war meine Methode, um dieses Ergebnis zu erhalten:

   df.iloc[df.index.isin(['stock1'], level=1) & 
           df.index.isin(['velocity'], level=2)] 

                        0
    a stock1 velocity   3
    b stock1 velocity  12

Und wenn ich zwei + Werte von einer Ebene und einen einzelnen (oder 2+) Wert von einer anderen Ebene wollte:

    df.iloc[df.index.isin(['stock1','stock3'], level=1) & 
            df.index.isin(['velocity'], level=2)] 

                        0
    a stock1 velocity   3
      stock3 velocity   9
    b stock1 velocity  12
      stock3 velocity  18

Die obige Methode ist wahrscheinlich etwas umständlich, aber ich fand, dass sie meine Bedürfnisse erfüllte und als Bonus für mich leichter zu verstehen und zu lesen war.

ra
quelle
2
Schön, wusste nichts über das levelArgument zu Index.isin!
cs95