Jakie są argumenty „poziomów”, „kluczy” i nazw w funkcji concat Pandy?

96

pytania

  • Jak używać pd.concat?
  • Jaki jest levelsargument za?
  • Jaki jest keysargument za?
  • Czy jest kilka przykładów, które pomogą wyjaśnić, jak używać wszystkich argumentów?

concatFunkcją Pandy jest szwajcarski scyzoryk łączących się narzędzi. Sytuacje, w których jest to przydatne, są liczne. Istniejąca dokumentacja pomija kilka szczegółów na temat niektórych opcjonalnych argumentów. Wśród nich są argumenty levelsi keys. Postanowiłem dowiedzieć się, do czego służą te argumenty.

Zadam pytanie, które będzie bramą do wielu aspektów pd.concat.

Rozważmy ramek danych d1, d2oraz d3:

import pandas as pd

d1 = pd.DataFrame(dict(A=.1, B=.2, C=.3), [2, 3])
d2 = pd.DataFrame(dict(B=.4, C=.5, D=.6), [1, 2])
d3 = pd.DataFrame(dict(A=.7, B=.8, D=.9), [1, 3])

Gdybym miał je połączyć razem z

pd.concat([d1, d2, d3], keys=['d1', 'd2', 'd3'])

Uzyskuję oczekiwany wynik z a pandas.MultiIndexdla mojego columnsobiektu:

        A    B    C    D
d1 2  0.1  0.2  0.3  NaN
   3  0.1  0.2  0.3  NaN
d2 1  NaN  0.4  0.5  0.6
   2  NaN  0.4  0.5  0.6
d3 1  0.7  0.8  NaN  0.9
   3  0.7  0.8  NaN  0.9

Chciałem jednak skorzystać z levelsdokumentacji argumentów :

poziomy : lista sekwencji, wartość domyślna Brak. Określone poziomy (unikalne wartości) do użycia przy konstruowaniu MultiIndex. W przeciwnym razie zostaną wywnioskowane z kluczy.

Więc zdałem

pd.concat([d1, d2, d3], keys=['d1', 'd2', 'd3'], levels=[['d1', 'd2']])

I zdobądź KeyError

ValueError: Key d3 not in level Index(['d1', 'd2'], dtype='object')

To miało sens. Poziomy, które zdałem, były niewystarczające, aby opisać niezbędne poziomy wskazane przez klawisze. Gdybym niczego nie zdał, tak jak powyżej, poziomy są wywnioskowane (zgodnie z dokumentacją). Ale jak inaczej mogę wykorzystać ten argument, aby uzyskać lepszy efekt?

Gdybym zamiast tego spróbował:

pd.concat([d1, d2, d3], keys=['d1', 'd2', 'd3'], levels=[['d1', 'd2', 'd3']])

Otrzymałem takie same wyniki jak powyżej. Ale kiedy dodam jeszcze jedną wartość do poziomów,

df = pd.concat([d1, d2, d3], keys=['d1', 'd2', 'd3'], levels=[['d1', 'd2', 'd3', 'd4']])

W rezultacie otrzymuję tę samą wyglądającą ramkę danych, ale wynikowa MultiIndexma nieużywany poziom.

df.index.levels[0]

Index(['d1', 'd2', 'd3', 'd4'], dtype='object')

Jaki jest więc sens tego levelargumentu i czy powinienem używać go keysinaczej?

Używam Pythona 3.6 i Pandas 0.22.

piRSquared
źródło

Odpowiedzi:

128

Odpowiadając sobie na to pytanie, nauczyłem się wielu rzeczy i chciałem zebrać katalog przykładów i wyjaśnienia.

Konkretna odpowiedź do punktu levels sporu nadejdzie na końcu.

pandas.concat: Brakujący podręcznik

Link do aktualnej dokumentacji

Importuje i definiuje obiekty

import pandas as pd

d1 = pd.DataFrame(dict(A=.1, B=.2, C=.3), index=[2, 3])
d2 = pd.DataFrame(dict(B=.4, C=.5, D=.6), index=[1, 2])
d3 = pd.DataFrame(dict(A=.7, B=.8, D=.9), index=[1, 3])

s1 = pd.Series([1, 2], index=[2, 3])
s2 = pd.Series([3, 4], index=[1, 2])
s3 = pd.Series([5, 6], index=[1, 3])

Argumenty

objs

Pierwszy argument, z którym się spotykamy, to objs:

objs : sekwencja lub mapowanie obiektów Series, DataFrame lub Panel Jeśli dict jest przekazany, posortowane klucze będą używane jako argument keys, chyba że zostanie przekazany, w którym to przypadku wartości zostaną wybrane (patrz poniżej). Wszystkie obiekty None zostaną po cichu porzucone, chyba że wszystkie są None, w którym to przypadku zostanie zgłoszony błąd ValueError

  • Zwykle widzimy, że jest to używane z listą obiektów Serieslub DataFrame.
  • Pokażę, że to też dictmoże być bardzo przydatne.
  • Generatory mogą być również stosowane i mogą być przydatne podczas używania map, jak wmap(f, list_of_df)

Na razie będziemy trzymać się listy niektórych obiektów DataFramei Serieszdefiniowanych powyżej. Pokażę MultiIndexpóźniej, jak można wykorzystać słowniki, aby uzyskać bardzo przydatne wyniki.

pd.concat([d1, d2])

     A    B    C    D
2  0.1  0.2  0.3  NaN
3  0.1  0.2  0.3  NaN
1  NaN  0.4  0.5  0.6
2  NaN  0.4  0.5  0.6

axis

Drugi argument, który napotkamy, to axiswartość domyślna 0:

: {0 / 'indeks', 1 / 'kolumny'}, domyślnie 0 Oś do połączenia.

Dwa DataFramezaxis=0 (ułożone)

Dla wartości 0lubindex mamy na myśli: „Wyrównaj wzdłuż kolumn i dodaj do indeksu”.

Jak pokazano powyżej, gdzie użyliśmy axis=0, ponieważ 0jest to wartość domyślna, i widzimy, że indeks d2rozszerza indeks o d1pomimo nakładania się wartości 2:

pd.concat([d1, d2], axis=0)

     A    B    C    D
2  0.1  0.2  0.3  NaN
3  0.1  0.2  0.3  NaN
1  NaN  0.4  0.5  0.6
2  NaN  0.4  0.5  0.6

Dwa DataFramez axis=1(obok siebie)

W przypadku wartości 1lub columnsmamy na myśli: „Wyrównaj wzdłuż indeksu i dodaj do kolumn”,

pd.concat([d1, d2], axis=1)

     A    B    C    B    C    D
1  NaN  NaN  NaN  0.4  0.5  0.6
2  0.1  0.2  0.3  0.4  0.5  0.6
3  0.1  0.2  0.3  NaN  NaN  NaN

Widzimy, że wynikowy indeks jest sumą indeksów, a wynikowe kolumny są rozszerzeniem kolumn z d1przez kolumny d2.

Dwa (lub trzy) Seriesz axis=0(ułożone)

Przy łączeniu pandas.Seriesrazem axis=0, otrzymujemy z powrotem pandas.Series. Nazwa wynikowa Seriesbędzie, Nonechyba że wszystkie Seriespołączone mają tę samą nazwę. Zwróć uwagę na to, 'Name: A'kiedy drukujemy wynik Series. Kiedy go nie ma, możemy założyć, że Seriestak None.

               |                       |                        |  pd.concat(
               |  pd.concat(           |  pd.concat(            |      [s1.rename('A'),
 pd.concat(    |      [s1.rename('A'), |      [s1.rename('A'),  |       s2.rename('B'),
     [s1, s2]) |       s2])            |       s2.rename('A')]) |       s3.rename('A')])
-------------- | --------------------- | ---------------------- | ----------------------
2    1         | 2    1                | 2    1                 | 2    1
3    2         | 3    2                | 3    2                 | 3    2
1    3         | 1    3                | 1    3                 | 1    3
2    4         | 2    4                | 2    4                 | 2    4
dtype: int64   | dtype: int64          | Name: A, dtype: int64  | 1    5
               |                       |                        | 3    6
               |                       |                        | dtype: int64

Dwa (lub trzy) Serieszaxis=1 (obok siebie)

Podczas łączenia pandas.Serieswzdłuż axis=1jest to nameatrybut, do którego odwołujemy się, aby wywnioskować nazwę kolumny w wyniku pandas.DataFrame.

                       |                       |  pd.concat(
                       |  pd.concat(           |      [s1.rename('X'),
 pd.concat(            |      [s1.rename('X'), |       s2.rename('Y'),
     [s1, s2], axis=1) |       s2], axis=1)    |       s3.rename('Z')], axis=1)
---------------------- | --------------------- | ------------------------------
     0    1            |      X    0           |      X    Y    Z
1  NaN  3.0            | 1  NaN  3.0           | 1  NaN  3.0  5.0
2  1.0  4.0            | 2  1.0  4.0           | 2  1.0  4.0  NaN
3  2.0  NaN            | 3  2.0  NaN           | 3  2.0  NaN  6.0

Miesza się Seriesi DataFramez axis=0(w stos)

Wykonując konkatenację a Seriesi DataFramewzdłuż axis=0, konwertujemy wszystko Seriesna pojedyncze kolumny DataFrame.

Zwróć szczególną uwagę, że jest to konkatenacja axis=0; oznacza to rozszerzenie indeksu (wierszy) podczas wyrównywania kolumn. W poniższych przykładach widzimy, że indeks staje się, [2, 3, 2, 3]który jest masowym dołączaniem indeksów. Kolumny nie nakładają się, chyba że wymuszę nazewnictwo Serieskolumny z argumentem to_frame:

 pd.concat(               |
     [s1.to_frame(), d1]) |  pd.concat([s1, d1])
------------------------- | ---------------------
     0    A    B    C     |      0    A    B    C
2  1.0  NaN  NaN  NaN     | 2  1.0  NaN  NaN  NaN
3  2.0  NaN  NaN  NaN     | 3  2.0  NaN  NaN  NaN
2  NaN  0.1  0.2  0.3     | 2  NaN  0.1  0.2  0.3
3  NaN  0.1  0.2  0.3     | 3  NaN  0.1  0.2  0.3

Możesz zobaczyć, że wyniki pd.concat([s1, d1])są takie same, jak gdybym wykonałto_frame siebie.

Jednak mogę kontrolować nazwę kolumny wynikowej za pomocą parametru do to_frame. Zmiana nazwy za Seriespomocą renamemetody nie kontroluje nazwy kolumny w wynikowym DataFrame.

 # Effectively renames       |                            |
 # `s1` but does not align   |  # Does not rename.  So    |  # Renames to something
 # with columns in `d1`      |  # Pandas defaults to `0`  |  # that does align with `d1`
 pd.concat(                  |  pd.concat(                |  pd.concat(
     [s1.to_frame('X'), d1]) |      [s1.rename('X'), d1]) |      [s1.to_frame('B'), d1])
---------------------------- | -------------------------- | ----------------------------
     A    B    C    X        |      0    A    B    C      |      A    B    C
2  NaN  NaN  NaN  1.0        | 2  1.0  NaN  NaN  NaN      | 2  NaN  1.0  NaN
3  NaN  NaN  NaN  2.0        | 3  2.0  NaN  NaN  NaN      | 3  NaN  2.0  NaN
2  0.1  0.2  0.3  NaN        | 2  NaN  0.1  0.2  0.3      | 2  0.1  0.2  0.3
3  0.1  0.2  0.3  NaN        | 3  NaN  0.1  0.2  0.3      | 3  0.1  0.2  0.3

Mieszane Seriesi DataFramez axis=1(obok siebie)

Jest to dość intuicyjne. Seriesnazwa kolumny domyślnie zawiera wyliczenie takich Seriesobiektów, gdy nameatrybut nie jest dostępny.

                    |  pd.concat(
 pd.concat(         |      [s1.rename('X'),
     [s1, d1],      |       s2, s3, d1],
     axis=1)        |      axis=1)
------------------- | -------------------------------
   0    A    B    C |      X    0    1    A    B    C
2  1  0.1  0.2  0.3 | 1  NaN  3.0  5.0  NaN  NaN  NaN
3  2  0.1  0.2  0.3 | 2  1.0  4.0  NaN  0.1  0.2  0.3
                    | 3  2.0  NaN  6.0  0.1  0.2  0.3

join

Trzeci argument joinopisuje, czy wynikowe scalenie powinno być scaleniem zewnętrznym (domyślne), czy scaleniem wewnętrznym.

join : {'internal', 'external'}, default 'external'
Jak obsługiwać indeksy na innych osiach.

Okazuje się, że nie ma leftani rightopcji jakpd.concat może obsłużyć więcej niż tylko dwa obiekty do scalenia.

W przypadku d1i d2opcje wyglądają następująco:

outer

pd.concat([d1, d2], axis=1, join='outer')

     A    B    C    B    C    D
1  NaN  NaN  NaN  0.4  0.5  0.6
2  0.1  0.2  0.3  0.4  0.5  0.6
3  0.1  0.2  0.3  NaN  NaN  NaN

inner

pd.concat([d1, d2], axis=1, join='inner')

     A    B    C    B    C    D
2  0.1  0.2  0.3  0.4  0.5  0.6

join_axes

Czwarty argument to rzecz, która pozwala nam dokonać leftpołączenia i nie tylko.

join_axes : lista obiektów indeksu
Określone indeksy do użycia dla pozostałych n - 1 osi zamiast wykonywania wewnętrznej / zewnętrznej logiki zbioru.

Lewe scalanie

pd.concat([d1, d2, d3], axis=1, join_axes=[d1.index])

     A    B    C    B    C    D    A    B    D
2  0.1  0.2  0.3  0.4  0.5  0.6  NaN  NaN  NaN
3  0.1  0.2  0.3  NaN  NaN  NaN  0.7  0.8  0.9

Right Merge

pd.concat([d1, d2, d3], axis=1, join_axes=[d3.index])

     A    B    C    B    C    D    A    B    D
1  NaN  NaN  NaN  0.4  0.5  0.6  0.7  0.8  0.9
3  0.1  0.2  0.3  NaN  NaN  NaN  0.7  0.8  0.9

ignore_index

ignore_index : boolean, default False
Jeśli True, nie używaj wartości indeksu wzdłuż osi konkatenacji. Wynikowa oś będzie oznaczona jako 0, ..., n - 1. Jest to przydatne, jeśli łączysz obiekty, w których oś konkatenacji nie ma znaczących informacji o indeksowaniu. Zwróć uwagę, że wartości indeksu na innych osiach są nadal przestrzegane w połączeniu.

Na przykład, gdy układam się d1na wierzchu d2, jeśli nie dbam o wartości indeksu, mogę je zresetować lub zignorować.

                      |  pd.concat(             |  pd.concat(
                      |      [d1, d2],          |      [d1, d2]
 pd.concat([d1, d2])  |      ignore_index=True) |  ).reset_index(drop=True)
--------------------- | ----------------------- | -------------------------
     A    B    C    D |      A    B    C    D   |      A    B    C    D
2  0.1  0.2  0.3  NaN | 0  0.1  0.2  0.3  NaN   | 0  0.1  0.2  0.3  NaN
3  0.1  0.2  0.3  NaN | 1  0.1  0.2  0.3  NaN   | 1  0.1  0.2  0.3  NaN
1  NaN  0.4  0.5  0.6 | 2  NaN  0.4  0.5  0.6   | 2  NaN  0.4  0.5  0.6
2  NaN  0.4  0.5  0.6 | 3  NaN  0.4  0.5  0.6   | 3  NaN  0.4  0.5  0.6

A gdy używasz axis=1:

                                   |     pd.concat(
                                   |         [d1, d2], axis=1,
 pd.concat([d1, d2], axis=1)       |         ignore_index=True)
-------------------------------    |    -------------------------------
     A    B    C    B    C    D    |         0    1    2    3    4    5
1  NaN  NaN  NaN  0.4  0.5  0.6    |    1  NaN  NaN  NaN  0.4  0.5  0.6
2  0.1  0.2  0.3  0.4  0.5  0.6    |    2  0.1  0.2  0.3  0.4  0.5  0.6
3  0.1  0.2  0.3  NaN  NaN  NaN    |    3  0.1  0.2  0.3  NaN  NaN  NaN

keys

Możemy przekazać listę wartości skalarnych lub krotek w celu przypisania krotek lub wartości skalarnych do odpowiedniego MultiIndexu. Długość przekazanej listy musi być taka sama, jak liczba łączonych elementów.

klucze : sekwencja, wartość domyślna Brak
Jeśli minęło wiele poziomów, powinno zawierać krotki. Skonstruuj indeks hierarchiczny, używając przekazanych kluczy jako najbardziej zewnętrznego poziomu

axis=0

Podczas łączenia Seriesobiektów wzdłużaxis=0 (rozszerzania indeksu).

Te klucze stają się nowym początkowym poziomem MultiIndexobiektu w atrybucie indeksu.

 #           length 3             length 3           #         length 2        length 2
 #          /--------\         /-----------\         #          /----\         /------\
 pd.concat([s1, s2, s3], keys=['A', 'B', 'C'])       pd.concat([s1, s2], keys=['A', 'B'])
----------------------------------------------      -------------------------------------
A  2    1                                           A  2    1
   3    2                                              3    2
B  1    3                                           B  1    3
   2    4                                              2    4
C  1    5                                           dtype: int64
   3    6
dtype: int64

Możemy jednak użyć więcej niż wartości skalarnych w keysargumencie, aby stworzyć jeszcze głębszą MultiIndex. Tutaj przekazujemy tuplesdługość 2 przed dodaniem dwóch nowych poziomów a MultiIndex:

 pd.concat(
     [s1, s2, s3],
     keys=[('A', 'X'), ('A', 'Y'), ('B', 'X')])
-----------------------------------------------
A  X  2    1
      3    2
   Y  1    3
      2    4
B  X  1    5
      3    6
dtype: int64

axis=1

Trochę inaczej wygląda to w przypadku rozciągania wzdłuż kolumn. Kiedy używaliśmy axis=0(patrz powyżej), nasz keysdziałał jako MultiIndexpoziomy oprócz istniejącego indeksu. Ponieważ axis=1odnosimy się do osi, której Seriesobiekty nie mają, a mianowicie do columnsatrybutu.

Wariacje dwóch Serieswtihaxis=1

Zauważ, że nazywanie s1i ma s2znaczenie, o ile nie keyssą przekazywane, ale jest zastępowane, jeśli keyssą przekazywane.

               |                       |                        |  pd.concat(
               |  pd.concat(           |  pd.concat(            |      [s1.rename('U'),
 pd.concat(    |      [s1, s2],        |      [s1.rename('U'),  |       s2.rename('V')],
     [s1, s2], |      axis=1,          |       s2.rename('V')], |       axis=1,
     axis=1)   |      keys=['X', 'Y']) |       axis=1)          |       keys=['X', 'Y'])
-------------- | --------------------- | ---------------------- | ----------------------
     0    1    |      X    Y           |      U    V            |      X    Y
1  NaN  3.0    | 1  NaN  3.0           | 1  NaN  3.0            | 1  NaN  3.0
2  1.0  4.0    | 2  1.0  4.0           | 2  1.0  4.0            | 2  1.0  4.0
3  2.0  NaN    | 3  2.0  NaN           | 3  2.0  NaN            | 3  2.0  NaN
MultiIndexz Seriesiaxis=1
 pd.concat(
     [s1, s2],
     axis=1,
     keys=[('W', 'X'), ('W', 'Y')])
-----------------------------------
     W
     X    Y
1  NaN  3.0
2  1.0  4.0
3  2.0  NaN
Dwa DataFramezaxis=1

Podobnie jak w axis=0przykładach, keysdodaj poziomy do a MultiIndex, ale tym razem do obiektu przechowywanego w columnsatrybucie.

 pd.concat(                     |  pd.concat(
     [d1, d2],                  |      [d1, d2],
     axis=1,                    |      axis=1,
     keys=['X', 'Y'])           |      keys=[('First', 'X'), ('Second', 'X')])
------------------------------- | --------------------------------------------
     X              Y           |   First           Second
     A    B    C    B    C    D |       X                X
1  NaN  NaN  NaN  0.4  0.5  0.6 |       A    B    C      B    C    D
2  0.1  0.2  0.3  0.4  0.5  0.6 | 1   NaN  NaN  NaN    0.4  0.5  0.6
3  0.1  0.2  0.3  NaN  NaN  NaN | 2   0.1  0.2  0.3    0.4  0.5  0.6
                                | 3   0.1  0.2  0.3    NaN  NaN  NaN
Seriesi DataFramezaxis=1

To jest trudne. W takim przypadku wartość klucza skalarnego nie może działać jako jedyny poziom indeksu dla Seriesobiektu, gdy staje się on kolumną, a jednocześnie działa jako pierwszy poziom a MultiIndexdla DataFrame. Pandy ponownie użyją nameatrybutu Seriesobiektu jako źródła nazwy kolumny.

 pd.concat(           |  pd.concat(
     [s1, d1],        |      [s1.rename('Z'), d1],
     axis=1,          |      axis=1,
     keys=['X', 'Y']) |      keys=['X', 'Y'])
--------------------- | --------------------------
   X    Y             |    X    Y
   0    A    B    C   |    Z    A    B    C
2  1  0.1  0.2  0.3   | 2  1  0.1  0.2  0.3
3  2  0.1  0.2  0.3   | 3  2  0.1  0.2  0.3
Ograniczenia keysi MultiIndexwnioskowanie.

Wydaje się, że pandy tylko wydają się wywnioskować nazwy kolumn z Seriesnazwy, ale nie będą wypełniać luk podczas wykonywania analogicznej konkatenacji między ramkami danych o różnej liczbie poziomów kolumn.

d1_ = pd.concat(
    [d1], axis=1,
    keys=['One'])
d1_

   One
     A    B    C
2  0.1  0.2  0.3
3  0.1  0.2  0.3

Następnie połącz to z inną ramką danych z tylko jednym poziomem w obiekcie kolumny, a Pandy odmówią próby stworzenia krotek MultiIndexobiektu i połączenia wszystkich ramek danych, jakby to był pojedynczy poziom obiektów, skalarów i krotek.

pd.concat([d1_, d2], axis=1)

   (One, A)  (One, B)  (One, C)    B    C    D
1       NaN       NaN       NaN  0.4  0.5  0.6
2       0.1       0.2       0.3  0.4  0.5  0.6
3       0.1       0.2       0.3  NaN  NaN  NaN

Przekazywanie dictzamiast alist

Podczas przekazywania słownika pandas.concatużyje kluczy ze słownika jako keysparametru.

 # axis=0               |  # axis=1
 pd.concat(             |  pd.concat(
     {0: d1, 1: d2})    |      {0: d1, 1: d2}, axis=1)
----------------------- | -------------------------------
       A    B    C    D |      0              1
0 2  0.1  0.2  0.3  NaN |      A    B    C    B    C    D
  3  0.1  0.2  0.3  NaN | 1  NaN  NaN  NaN  0.4  0.5  0.6
1 1  NaN  0.4  0.5  0.6 | 2  0.1  0.2  0.3  0.4  0.5  0.6
  2  NaN  0.4  0.5  0.6 | 3  0.1  0.2  0.3  NaN  NaN  NaN

levels

Jest to używane w połączeniu z keysargumentem. levelsGdy pozostawi się wartość domyślną None, Pandy przyjmą unikalne wartości każdego poziomu wyniku MultiIndexi wykorzystają je jako obiekt używany w wynikowym index.levelsatrybucie.

poziomy : lista sekwencji, wartość domyślna Brak
Określone poziomy (unikalne wartości) do użycia przy konstruowaniu MultiIndex. W przeciwnym razie zostaną wywnioskowane z kluczy.

Jeśli Pandy już wywnioskują, jakie powinny być te poziomy, jaką korzyść daje samodzielne określenie tego? Pokażę jeden przykład i pozostawię ci wymyślenie innych powodów, dla których może to być przydatne.

Przykład

Zgodnie z dokumentacją levelsargumentem jest lista sekwencji. Oznacza to, że możemy użyć innej pandas.Indexjako jednej z tych sekwencji.

Rozważmy ramki danych df, który jest połączeniem d1, d2i d3:

df = pd.concat(
    [d1, d2, d3], axis=1,
    keys=['First', 'Second', 'Fourth'])

df

  First           Second           Fourth
      A    B    C      B    C    D      A    B    D
1   NaN  NaN  NaN    0.4  0.5  0.6    0.7  0.8  0.9
2   0.1  0.2  0.3    0.4  0.5  0.6    NaN  NaN  NaN
3   0.1  0.2  0.3    NaN  NaN  NaN    0.7  0.8  0.9

Poziomy obiektu kolumny to:

print(df, *df.columns.levels, sep='\n')

Index(['First', 'Second', 'Fourth'], dtype='object')
Index(['A', 'B', 'C', 'D'], dtype='object')

Jeśli użyjemy sumw a groupbyotrzymamy:

df.groupby(axis=1, level=0).sum()

   First  Fourth  Second
1    0.0     2.4     1.5
2    0.6     0.0     1.5
3    0.6     2.4     0.0

Ale co by było, gdyby zamiast ['First', 'Second', 'Fourth']innych brakujących kategorii nazwano Thirdi Fifth? I chciałem, żeby zostały uwzględnione w wynikach groupbyagregacji? Możemy to zrobić, gdybyśmy mieli plik pandas.CategoricalIndex. I możemy to określić z wyprzedzeniem za pomocą levelsargumentu.

Zamiast tego zdefiniujmy dfjako:

cats = ['First', 'Second', 'Third', 'Fourth', 'Fifth']
lvl = pd.CategoricalIndex(cats, categories=cats, ordered=True)

df = pd.concat(
    [d1, d2, d3], axis=1,
    keys=['First', 'Second', 'Fourth'],
    levels=[lvl]
)

df

   First  Fourth  Second
1    0.0     2.4     1.5
2    0.6     0.0     1.5
3    0.6     2.4     0.0

Ale pierwszy poziom obiektu kolumny to:

df.columns.levels[0]

CategoricalIndex(
    ['First', 'Second', 'Third', 'Fourth', 'Fifth'],
    categories=['First', 'Second', 'Third', 'Fourth', 'Fifth'],
    ordered=True, dtype='category')

A nasze groupbypodsumowanie wygląda następująco:

df.groupby(axis=1, level=0).sum()

   First  Second  Third  Fourth  Fifth
1    0.0     1.5    0.0     2.4    0.0
2    0.6     1.5    0.0     0.0    0.0
3    0.6     0.0    0.0     2.4    0.0

names

Służy do nazwania poziomów wyniku MultiIndex. Długość nameslisty powinna odpowiadać liczbie poziomów w wynikowym MultiIndex.

nazwy : lista, wartość domyślna Brak
Nazwy poziomów w wynikowym indeksie hierarchicznym

 # axis=0                     |  # axis=1
 pd.concat(                   |  pd.concat(
     [d1, d2],                |      [d1, d2],
     keys=[0, 1],             |      axis=1, keys=[0, 1],
     names=['lvl0', 'lvl1'])  |      names=['lvl0', 'lvl1'])
----------------------------- | ----------------------------------
             A    B    C    D | lvl0    0              1
lvl0 lvl1                     | lvl1    A    B    C    B    C    D
0    2     0.1  0.2  0.3  NaN | 1     NaN  NaN  NaN  0.4  0.5  0.6
     3     0.1  0.2  0.3  NaN | 2     0.1  0.2  0.3  0.4  0.5  0.6
1    1     NaN  0.4  0.5  0.6 | 3     0.1  0.2  0.3  NaN  NaN  NaN
     2     NaN  0.4  0.5  0.6 |

verify_integrity

Dokumentacja nie wymagająca objaśnienia

verify_integrity : boolean, default False
Sprawdź, czy nowa połączona oś zawiera duplikaty. Może to być bardzo kosztowne w porównaniu z rzeczywistą konkatenacją danych.

Ponieważ wynikowy indeks jest wynikiem konkatenacji d1i d2nie jest unikalny, nie przejdzie testu integralności.

pd.concat([d1, d2])

     A    B    C    D
2  0.1  0.2  0.3  NaN
3  0.1  0.2  0.3  NaN
1  NaN  0.4  0.5  0.6
2  NaN  0.4  0.5  0.6

I

pd.concat([d1, d2], verify_integrity=True)

> ValueError: Indeksy mają nakładające się wartości: [2]

piRSquared
źródło
23
znacznie bardziej przydatne byłoby dla społeczności wykonanie żądania ściągnięcia, aby dodać kilka brakujących przykładów (tylko kilka) do głównej dokumentacji; SO można tylko przeszukiwać i nie można przeglądać; dalsze umieszczanie linku do dokumentów byłoby przydatne tutaj - zdecydowana większość z nich jest już dobrze i całkowicie udokumentowana
Jeff
6
@Jeff, są aspekty mojego rozwoju, które były powolne. Używanie git jest jednym z nich. Obiecuję, że właśnie to chcę zacząć.
piRSquared
Użycie pd.concat(..., levels=[lvl]).groupby(axis=1, level=0).sum()daje inny wynik niż pd.concat(..., levels=[cats]).groupby(axis=1, level=0).sum(). Wiesz dlaczego? Dokumenty mówią tylko, że levelspowinna to być lista sekwencji.
unutbu
1
Świetna odpowiedź, ale myślę, że sekcja dotycząca Passing a dict instead of a listwymaga przykładu przy użyciu dyktowania, a nie listy.
unutbu
1
@unutbu Poprawiłem dictprzykład, thx. Powodem jest to, że lvljest to indeks kategoryczny i catsjest to tylko lista. Podczas grupowania według typu kategorialnego brakujące kategorie są w razie potrzeby uzupełniane zerami i zerami. Zobacz to
piRSquared