Wybierz wiersze w pandach MultiIndex DataFrame

147

Jakie są najczęstsze sposoby wybierania / filtrowania wierszy ramki danych, której indeksem jest MultiIndex, na pandach ?

  • Krojenie na podstawie jednej wartości / etykiety
  • Krojenie na podstawie wielu etykiet z jednego lub kilku poziomów
  • Filtrowanie według warunków i wyrażeń logicznych
  • Jakie metody mają zastosowanie w jakich okolicznościach

Założenia prostoty:

  1. wejściowa ramka danych nie ma zduplikowanych kluczy indeksu
  2. Ramka danych wejściowych poniżej ma tylko dwa poziomy. (Większość przedstawionych tutaj rozwiązań uogólnia się na poziomy N)

Przykładowe dane wejściowe:

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

Pytanie 1: Wybieranie pojedynczego elementu

Jak wybrać wiersze mające „a” na poziomie „jeden”?

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

Ponadto, jak mogę obniżyć poziom „jeden” na wyjściu?

     col
two     
t      0
u      1
v      2
w      3

Pytanie 1b
Jak pokroić wszystkie wiersze z wartością „t” na poziomie „dwa”?

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

Pytanie 2: Wybieranie wielu wartości na poziomie

Jak mogę wybrać wiersze odpowiadające elementom „b” i „d” na poziomie „jeden”?

         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

Pytanie 2b
Jak uzyskać wszystkie wartości odpowiadające „t” i „w” na poziomie „dwa”?

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

Pytanie 3: Cięcie pojedynczego przekroju poprzecznego (x, y)

Jak uzyskać przekrój poprzeczny, tj. Pojedynczy wiersz zawierający określone wartości indeksu z df? W szczególności, jak uzyskać przekrój poprzeczny ('c', 'u'), podany przez

         col
one two     
c   u      9

Pytanie 4: cięcie wielu przekrojów poprzecznych [(a, b), (c, d), ...]

Jak wybrać dwa wiersze odpowiadające ('c', 'u')i ('a', 'w')?

         col
one two     
c   u      9
a   w      3

Pytanie 5: Jeden przedmiot pokrojony na poziom

Jak mogę pobrać wszystkie wiersze odpowiadające „a” na poziomie „pierwszym” lub „t” na poziomie „drugim”?

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

Pytanie 6: Arbitralne cięcie

Jak mogę ciąć określone przekroje? Dla „a” i „b” chciałbym zaznaczyć wszystkie wiersze z podpoziomami „u” i „v”, a dla „d” chciałbym zaznaczyć wiersze z podpoziomem „w”.

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

Pytanie 7 wykorzysta unikalną konfigurację składającą się z poziomu numerycznego:

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

Pytanie 7: Filtrowanie według nierówności liczbowych na poszczególnych poziomach multiindeksu

Jak uzyskać wszystkie wiersze, w których wartości na poziomie „dwa” są większe niż 5?

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

Uwaga: w tym poście nie omówiono tworzenia MultiIndexów, wykonywania na nich operacji przypisania ani żadnych dyskusji związanych z wydajnością (są to osobne tematy na inny czas).

cs95
źródło

Odpowiedzi:

167

MultiIndex / Zaawansowane indeksowanie

Uwaga
Ten post będzie miał następującą strukturę:

  1. Pytania postawione w PO zostaną omówione po kolei
  2. Dla każdego pytania zostanie zademonstrowana jedna lub więcej metod mających zastosowanie do rozwiązania tego problemu i uzyskania oczekiwanego wyniku.

Uwaga (podobnie jak ta) zostaną dołączone dla czytelników zainteresowanych dodatkowymi funkcjami, szczegółami implementacji i innymi pobieżnymi informacjami na dany temat. Te notatki zostały zebrane podczas przeszukiwania dokumentów i odkrywania różnych niejasnych cech oraz na podstawie mojego własnego (wprawdzie ograniczonego) doświadczenia.

Wszystkie próbki kodu zostały utworzone i przetestowane na pandas v0.23.4, python3.7 . Jeśli coś jest niejasne lub niezgodne ze stanem faktycznym, lub jeśli nie znalazłeś rozwiązania pasującego do twojego przypadku użycia, możesz zasugerować zmianę, poprosić o wyjaśnienie w komentarzach lub otworzyć nowe pytanie ... .

Oto wprowadzenie do niektórych popularnych idiomów (odtąd nazywanych czterema idiomami), które będziemy często ponownie odwiedzać

  1. DataFrame.loc- Ogólne rozwiązanie do wyboru według etykiety (+ pd.IndexSlicedla bardziej złożonych zastosowań obejmujących plastry)

  2. DataFrame.xs - Wyodrębnij określony przekrój z Series / DataFrame.

  3. DataFrame.query- Dynamicznie określaj krojenie i / lub filtrowanie operacji (tj. Jako wyrażenie, które jest oceniane dynamicznie. Ma większe zastosowanie w niektórych scenariuszach niż w innych. Zobacz również tę sekcję dokumentacji dotyczącą zapytań dotyczących MultiIndexes.

  4. Indeksowanie logiczne z maską wygenerowaną przy użyciu MultiIndex.get_level_values(często w połączeniu z Index.isin, szczególnie podczas filtrowania z wieloma wartościami). Jest to również przydatne w niektórych okolicznościach.

Korzystne będzie przyjrzenie się różnym problemom z wycinaniem i filtrowaniem pod kątem czterech idiomów, aby lepiej zrozumieć, co można zastosować w danej sytuacji. Bardzo ważne jest, aby zrozumieć, że nie wszystkie idiomy będą działać równie dobrze (jeśli w ogóle) w każdych okolicznościach. Jeśli idiom nie został wymieniony jako potencjalne rozwiązanie problemu poniżej, oznacza to, że nie można go skutecznie zastosować do tego problemu.


Pytanie 1

Jak wybrać wiersze mające „a” na poziomie „jeden”?

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

Możesz użyć loc, jako rozwiązania ogólnego przeznaczenia, mającego zastosowanie w większości sytuacji:

df.loc[['a']]

W tym momencie, jeśli dostaniesz

TypeError: Expected tuple, got str

Oznacza to, że używasz starszej wersji pand. Rozważ aktualizację! W przeciwnym razie użyjdf.loc[('a', slice(None)), :] .

Alternatywnie możesz użyć xstutaj, ponieważ pobieramy pojedynczy przekrój. Zwróć uwagę na argumenty levelsi axis(można tutaj założyć rozsądne wartości domyślne).

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

Tutaj drop_level=Falseargument jest potrzebny, aby zapobiec spadkowi xspoziomu „jeden” w wyniku (poziom, na który się przecięliśmy).

Jeszcze inną opcją jest tutaj użycie query:

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

Jeśli indeks nie miałby nazwy, należałoby zmienić ciąg zapytania na "ilevel_0 == 'a'" .

Wreszcie, używając 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']

Ponadto, jak mogę obniżyć poziom „jeden” na wyjściu?

     col
two     
t      0
u      1
v      2
w      3

Można to łatwo zrobić za pomocą obu

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

Lub,

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

Zauważ, że możemy pominąć drop_levelargument (zakłada się, że jest to Truedomyślny).

Uwaga
Można zauważyć, że filtrowana ramka danych może nadal mieć wszystkie poziomy, nawet jeśli nie są one wyświetlane podczas drukowania ramki danych. Na przykład,

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'])

Możesz pozbyć się tych poziomów za pomocą 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'])

Pytanie 1b

Jak pokroić wszystkie wiersze z wartością „t” na poziomie „dwa”?

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

Intuicyjnie chciałbyś czegoś obejmującego slice():

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

To po prostu działa! ™ Ale jest niezgrabny. Możemy ułatwić bardziej naturalną składnię wycinania, używając pd.IndexSlicetutaj API.

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

To jest dużo, dużo czystsze.

Uwaga
Dlaczego końcowy wycinek :w kolumnach jest wymagany? Dzieje się tak, ponieważ locmożna użyć do zaznaczania i cięcia wzdłuż obu osi ( axis=0lub axis=1). Bez wyraźnego wyjaśnienia, na której osi ma zostać wykonane cięcie, operacja staje się niejednoznaczna. Zobacz duże czerwone pole w dokumentacji dotyczącej krojenia .

Jeśli chcesz usunąć jakikolwiek odcień niejasności, locakceptuje axis parametr:

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

Bez axisparametru (tj. Po prostu robiąc df.loc[pd.IndexSlice[:, 't']]) zakłada się, że cięcie odbywa się na kolumnach i KeyErrorw takiej sytuacji zostanie podniesione a.

Jest to udokumentowane w fragmentatorach . Jednak na potrzeby tego postu wyraźnie określimy wszystkie osie.

Tak xsjest

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

Tak queryjest

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

I wreszcie get_level_values, możesz to zrobić

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

Wszystko w tym samym efekcie.


pytanie 2

Jak mogę wybrać wiersze odpowiadające elementom „b” i „d” na poziomie „jeden”?

         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

Używając loc, odbywa się to w podobny sposób, określając listę.

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

Aby rozwiązać powyższy problem wyboru „b” i „d”, możesz również użyć 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')

Uwaga
Tak, domyślny parser to 'pandas', ale ważne jest, aby podkreślić, że ta składnia nie jest tradycyjnie Pythonem. Parser Pandas generuje drzewo analizy nieco inne niż wyrażenie. Ma to na celu uczynienie niektórych operacji bardziej intuicyjnymi. Aby uzyskać więcej informacji, przeczytaj mój post dotyczący oceny wyrażeń dynamicznych w pandach przy użyciu pd.eval () .

A z get_level_values+ Index.isin:

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

Pytanie 2b

Jak uzyskać wszystkie wartości odpowiadające „t” i „w” na poziomie „dwa”?

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

Dzięki loctemu jest to możliwe tylko w połączeniu z pd.IndexSlice.

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

Pierwszy okrężnicy :w pd.IndexSlice[:, ['t', 'w']]środek do krojenia w poprzek pierwszego poziomu. Wraz ze wzrostem głębokości poziomu, którego dotyczy zapytanie, należy określić więcej plasterków, po jednym na poziom. Nie będziesz jednak musiał określać więcej poziomów poza tym, który jest cięty.

Dzięki query, to jest

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')

Z get_level_valuesi Index.isin(podobnie jak powyżej):

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

pytanie 3

Jak uzyskać przekrój poprzeczny, tj. Pojedynczy wiersz zawierający określone wartości indeksu z df? W szczególności, jak uzyskać przekrój poprzeczny ('c', 'u'), podany przez

         col
one two     
c   u      9

Użyj loc, określając krotkę kluczy:

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

Lub,

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

Uwaga
W tym momencie możesz natknąć się na następujący PerformanceWarningwygląd:

PerformanceWarning: indexing past lexsort depth may impact performance.

Oznacza to po prostu, że Twój indeks nie jest posortowany. pandy zależy od sortowanego indeksu (w tym przypadku leksykograficznie, ponieważ mamy do czynienia z wartościami ciągów) dla optymalnego wyszukiwania i pobierania. Szybkim rozwiązaniem byłoby wcześniejsze posortowanie ramki DataFrame za pomocą DataFrame.sort_index. Jest to szczególnie pożądane z punktu widzenia wydajności, jeśli planujesz wykonywać wiele takich zapytań jednocześnie:

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

Możesz również użyć, MultiIndex.is_lexsorted()aby sprawdzić, czy indeks jest posortowany, czy nie. Ta funkcja zwraca Truelub Falseodpowiednio. Możesz wywołać tę funkcję, aby określić, czy dodatkowy krok sortowania jest wymagany, czy nie.

W przypadku xsjest to ponownie po prostu przekazanie pojedynczej krotki jako pierwszego argumentu, przy wszystkich innych argumentach ustawionych na odpowiednie wartości domyślne:

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

Dzięki temu querysprawy stają się nieco niezgrabne:

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

Teraz widać, że będzie to stosunkowo trudne do uogólnienia. Ale nadal jest OK dla tego konkretnego problemu.

W przypadku dostępu obejmującego wiele poziomów get_level_valuesnadal można z niego korzystać, ale nie jest to zalecane:

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

Pytanie 4

Jak wybrać dwa wiersze odpowiadające ('c', 'u')i ('a', 'w')?

         col
one two     
c   u      9
a   w      3

Dzięki loctemu jest to nadal tak proste, jak:

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

W przypadku programu querybędziesz musiał dynamicznie generować ciąg zapytania przez iterację po przekrojach i poziomach:

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% NIE POLECAM! Ale jest to możliwe.


Pytanie 5

Jak mogę pobrać wszystkie wiersze odpowiadające „a” na poziomie „pierwszym” lub „t” na poziomie „drugim”?

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

Jest to w rzeczywistości bardzo trudne loc, zapewniając jednocześnie poprawność i przejrzystość kodu. df.loc[pd.IndexSlice['a', 't']]jest niepoprawny, jest interpretowany jako df.loc[pd.IndexSlice[('a', 't')]](tj. wybór przekroju). Możesz pomyśleć o rozwiązaniu, które pd.concatumożliwiłoby obsługę każdej etykiety osobno:

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

Ale zauważysz, że jeden z wierszy jest zduplikowany. Dzieje się tak, ponieważ ten wiersz spełniał oba warunki krojenia i pojawił się dwukrotnie. Zamiast tego będziesz musiał to zrobić

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

Ale jeśli ramka DataFrame z natury zawiera zduplikowane indeksy (które chcesz), to ich nie zachowa. Używaj z najwyższą ostrożnością .

Dzięki querytemu jest to głupio proste:

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

Dzięki get_level_valuestemu jest to nadal proste, ale nie tak eleganckie:

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

Pytanie 6

Jak mogę ciąć określone przekroje? Dla „a” i „b” chciałbym zaznaczyć wszystkie wiersze z podpoziomami „u” i „v”, a dla „d” chciałbym zaznaczyć wiersze z podpoziomem „w”.

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

Jest to specjalny przypadek, który dodałem, aby pomóc zrozumieć zastosowanie czterech idiomów - jest to jeden przypadek, w którym żaden z nich nie będzie działał skutecznie, ponieważ krojenie jest bardzo specyficzne i nie przebiega według żadnego rzeczywistego wzoru.

Zwykle takie problemy z wycinaniem wymagają jawnego przekazania listy kluczy do loc. Jednym ze sposobów jest:

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

Jeśli chcesz zaoszczędzić trochę pisania, zauważysz, że istnieje wzorzec do krojenia „a”, „b” i jego podpoziomów, więc możemy podzielić zadanie krojenia na dwie części i concat wynik:

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

Specyfikacja krojenia „a” i „b” jest nieco bardziej przejrzysta, (('a', 'b'), ('u', 'v'))ponieważ indeksowane poziomy podrzędne są takie same dla każdego poziomu.


Pytanie 7

Jak uzyskać wszystkie wiersze, w których wartości na poziomie „dwa” są większe niż 5?

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

Można to zrobić za pomocą query,

df2.query("two > 5")

A get_level_values.

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

Uwaga
Podobnie jak w tym przykładzie, możemy filtrować na podstawie dowolnego warunku przy użyciu tych konstrukcji. Ogólnie rzecz biorąc, warto o tym pamiętać loci xssą one przeznaczone specjalnie do indeksowania opartego na etykietach, podczas gdy queryi get_level_valuessą pomocne przy tworzeniu ogólnych masek warunkowych do filtrowania.


Pytanie dodatkowe

A co, jeśli muszę wyciąć MultiIndex kolumnę ?

W rzeczywistości większość rozwiązań tutaj ma zastosowanie również do kolumn, z niewielkimi zmianami. Rozważać:

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

Oto następujące zmiany, które musisz wprowadzić w czterech idiomach, aby działały z kolumnami.

  1. Do krojenia z loc , użyj

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

    lub,

    df3.loc[:, pd.IndexSlice[...]]
  2. Używać xs odpowiednio, po prostu przekaż argument axis=1.

  3. Dostęp do wartości na poziomie kolumny można uzyskać bezpośrednio za pomocą df.columns.get_level_values . Będziesz wtedy musiał zrobić coś takiego

    df.loc[:, {condition}] 

    Gdzie {condition}reprezentuje warunek zbudowany przy użyciucolumns.get_level_values .

  4. Aby użyć query, jedyną opcją jest transpozycja, zapytanie o indeks i ponowna transpozycja:

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

    Niezalecane, użyj jednej z pozostałych 3 opcji.

cs95
źródło
6

Niedawno natknąłem się na przypadek użycia, w którym miałem 3+ poziomową ramkę danych z wieloma indeksami, w której nie mogłem sprawić, aby żadne z powyższych rozwiązań dało wyniki, których szukałem. Jest całkiem możliwe, że powyższe rozwiązania oczywiście działają w moim przypadku użycia, a wypróbowałem kilka, jednak nie byłem w stanie zmusić ich do pracy w dostępnym czasie.

Nie jestem ekspertem, ale natknąłem się na rozwiązanie, które nie zostało wymienione w wyczerpujących odpowiedziach powyżej. Nie daję żadnej gwarancji, że rozwiązania są w jakikolwiek sposób optymalne.

To jest inny sposób na uzyskanie nieco innego wyniku niż w pytaniu 6 powyżej. (i prawdopodobnie inne pytania)

Konkretnie szukałem:

  1. Sposób wyboru dwóch + wartości z jednego poziomu indeksu i jednej wartości z innego poziomu indeksu oraz
  2. Sposób pozostawienia wartości indeksu z poprzedniej operacji w danych wyjściowych ramki danych.

Jako klucz do małpich kół zębatych (jednak całkowicie naprawialny):

  1. Indeksy nie zostały nazwane.

W zabawkowej ramce danych poniżej:

    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

Korzystanie z poniższych działa oczywiście:

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

        0
    a   3
    b  12

Ale chciałem innego wyniku, więc moja metoda uzyskania tego wyniku była następująca:

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

                        0
    a stock1 velocity   3
    b stock1 velocity  12

A gdybym chciał mieć dwie wartości + z jednego poziomu i jedną (lub 2+) wartość z innego poziomu:

    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

Powyższa metoda jest prawdopodobnie trochę niezgrabna, jednak uznałem, że spełnia ona moje potrzeby i jako bonus była dla mnie łatwiejsza do zrozumienia i przeczytania.

ra
źródło
2
Fajnie, nie wiedziałem o levelkłótni Index.isin!
cs95