Indexierung#
Index-Objekte#
Die Index-Objekte von pandas sind für die Achsenbeschriftungen und andere Metadaten, wie den Achsennamen, verantwortlich. Jedes Array oder jede andere Sequenz von Beschriftungen, die ihr bei der Konstruktion einer Serie oder eines DataFrame verwendet, wird intern in einen Index umgewandelt:
[1]:
import pandas as pd
obj = pd.Series(range(7), index=pd.date_range("2022-02-02", periods=7))
[2]:
obj.index
[2]:
DatetimeIndex(['2022-02-02', '2022-02-03', '2022-02-04', '2022-02-05',
'2022-02-06', '2022-02-07', '2022-02-08'],
dtype='datetime64[ns]', freq='D')
[3]:
obj.index[3:]
[3]:
DatetimeIndex(['2022-02-05', '2022-02-06', '2022-02-07', '2022-02-08'], dtype='datetime64[ns]', freq='D')
Indexobjekte sind unveränderlich (immutable) und können daher nicht geändert werden:
[4]:
obj.index[1] = '2022-02-03'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/tmp/ipykernel_29494/1385405160.py in <module>
----> 1 obj.index[1] = '2022-02-03'
~/spack/var/spack/environments/python-38/.spack-env/view/lib/python3.8/site-packages/pandas/core/indexes/base.py in __setitem__(self, key, value)
4583 @final
4584 def __setitem__(self, key, value):
-> 4585 raise TypeError("Index does not support mutable operations")
4586
4587 def __getitem__(self, key):
TypeError: Index does not support mutable operations
Die Unveränderlichkeit macht die gemeinsame Nutzung von Indexobjekten in Datenstrukturen sicherer:
[5]:
import numpy as np
labels = pd.Index(np.arange(3))
labels
[5]:
Int64Index([0, 1, 2], dtype='int64')
[6]:
obj2 = pd.Series(np.random.randn(3),index=labels)
[7]:
obj2
[7]:
0 -0.622445
1 0.199567
2 -2.865669
dtype: float64
[8]:
obj2.index is labels
[8]:
True
Um einem Array ähnlich zu sein verhält sich ein Index auch wie eine Menge mit fester Größe:
[9]:
data1 = {'Code': ['U+0000', 'U+0001', 'U+0002', 'U+0003', 'U+0004', 'U+0005'],
'Decimal': [0, 1, 2, 3, 4, 5],
'Octal': ['001', '002', '003', '004', '004', '005']}
df1 = pd.DataFrame(data1)
[10]:
df1
[10]:
Code | Decimal | Octal | |
---|---|---|---|
0 | U+0000 | 0 | 001 |
1 | U+0001 | 1 | 002 |
2 | U+0002 | 2 | 003 |
3 | U+0003 | 3 | 004 |
4 | U+0004 | 4 | 004 |
5 | U+0005 | 5 | 005 |
[11]:
df1.columns
[11]:
Index(['Code', 'Decimal', 'Octal'], dtype='object')
[12]:
'Code' in df1.columns
[12]:
True
[13]:
'Key' in df1.columns
[13]:
False
Achsenindizes mit doppelten Labels#
Anders als Python-Sets kann ein Pandas-Index doppelte Label enthalten:
[14]:
data2 = {'Code': ['U+0006', 'U+0007'],
'Decimal': [6, 7],
'Octal': ['006', '007']}
df2 = pd.DataFrame(data2)
df12 = df1.append(df2)
df12
[14]:
Code | Decimal | Octal | |
---|---|---|---|
0 | U+0000 | 0 | 001 |
1 | U+0001 | 1 | 002 |
2 | U+0002 | 2 | 003 |
3 | U+0003 | 3 | 004 |
4 | U+0004 | 4 | 004 |
5 | U+0005 | 5 | 005 |
0 | U+0006 | 6 | 006 |
1 | U+0007 | 7 | 007 |
Bei der Auswahl von doppelten Bezeichnungen werden alle Vorkommen der betreffenden Bezeichnung ausgewählt:
[15]:
df12.loc[1]
[15]:
Code | Decimal | Octal | |
---|---|---|---|
1 | U+0001 | 1 | 002 |
1 | U+0007 | 7 | 007 |
[16]:
df12.loc[2]
[16]:
Code U+0002
Decimal 2
Octal 003
Name: 2, dtype: object
Die Datenauswahl ist einer der Hauptpunkte, der sich bei Duplikaten anders verhält. Die Indizierung eines Labels mit mehreren Einträgen ergibt eine Serie, während einzelne Einträge einen Einzelwert ergeben. Dies kann euren Code komplizierter machen, da der Ausgabetyp der Indizierung je nachdem, ob ein Label wiederholt wird oder nicht, variieren kann. Zudem setzen viele Pandas-Funktionen, wie z.B. reindex
, voraus, dass Label eindeutig sind. Anhand der Eigenschaft is_unique
des Index könnt
ihr feststellen, ob seine Label eindeutig sind oder nicht:
[17]:
df12.index.is_unique
[17]:
False
Um doppelte Label zu vermeiden, könnt ihr z.B. ignore_index=True
verwenden:
[18]:
df12 = df1.append(df2, ignore_index=True)
df12
[18]:
Code | Decimal | Octal | |
---|---|---|---|
0 | U+0000 | 0 | 001 |
1 | U+0001 | 1 | 002 |
2 | U+0002 | 2 | 003 |
3 | U+0003 | 3 | 004 |
4 | U+0004 | 4 | 004 |
5 | U+0005 | 5 | 005 |
6 | U+0006 | 6 | 006 |
7 | U+0007 | 7 | 007 |
Einige Indexmethoden und -eigenschaften#
Jeder Index verfügt über eine Reihe von Methoden und Eigenschaften für die Mengenlogik, die andere allgemeine Fragen zu den darin enthaltenen Daten beantworten. Im folgenden einige nützliche Methoden und Eigenschaften:
Methode |
Beschreibung |
---|---|
|
verkettet zusätzliche Indexobjekte, wodurch ein neuer Index entsteht |
|
berechnet die Differenz zweier Mengen als Index |
|
berechnet die Schnittmenge |
|
berechnet die Vereinigungsmenge |
|
berechnet ein boolesches Array, das angibt, ob jeder Wert in der übergebenen Sammlung enthalten ist |
|
berechnet einen neuen Index, wobei das Element in Index |
|
berechnet einen neuen Index durch Löschen der übergebenen Werte |
|
berechnet neuen Index durch Einfügen des Elements in den Index |
|
gibt |
|
gibt |
|
berechnet das Array der eindeutigen Werte im Index |
Neuindizierung mit reindex
#
Eine wichtige Methode für Pandas-Objekte ist die Neuindizierung, d.h. die Erstellung eines neuen Objekts mit neu angeordneten Werten, die mit dem neuen Index übereinstimmen. Betrachtet z.B.:
[19]:
obj = pd.Series(range(7), index=pd.date_range("2022-02-02", periods=7))
[20]:
obj
[20]:
2022-02-02 0
2022-02-03 1
2022-02-04 2
2022-02-05 3
2022-02-06 4
2022-02-07 5
2022-02-08 6
Freq: D, dtype: int64
[21]:
new_index = pd.date_range("2022-02-03", periods=7)
[22]:
obj.reindex(new_index)
[22]:
2022-02-03 1.0
2022-02-04 2.0
2022-02-05 3.0
2022-02-06 4.0
2022-02-07 5.0
2022-02-08 6.0
2022-02-09 NaN
Freq: D, dtype: float64
reindex
erstellt einen neuen Index und indiziert den DataFrame neu. Standardmäßig werden Werte im neuen Index, für die es keine entsprechenden Datensätze im DataFrame gibt, zu NaN
.
Bei geordneten Daten wie Zeitreihen kann es wünschenswert sein, bei der Neuindizierung Werte zu interpolieren oder zu füllen. Die Option method
ermöglicht dies mit einer Methode wie ffill
, die die Werte vorwärts füllt:
[23]:
obj.reindex(new_index, method='ffill')
[23]:
2022-02-03 1
2022-02-04 2
2022-02-05 3
2022-02-06 4
2022-02-07 5
2022-02-08 6
2022-02-09 6
Freq: D, dtype: int64
Bei einem DataFrame kann reindex
entweder den (Zeilen-)Index, die Spalten oder beides ändern. Wenn nur eine Sequenz übergeben wird, werden die Zeilen im Ergebnis neu indiziert:
[24]:
df1.reindex(range(7))
[24]:
Code | Decimal | Octal | |
---|---|---|---|
0 | U+0000 | 0.0 | 001 |
1 | U+0001 | 1.0 | 002 |
2 | U+0002 | 2.0 | 003 |
3 | U+0003 | 3.0 | 004 |
4 | U+0004 | 4.0 | 004 |
5 | U+0005 | 5.0 | 005 |
6 | NaN | NaN | NaN |
Die Spalten können mit dem Schlüsselwort columns
neu indiziert werden:
[25]:
encoding = ['Octal', 'Code', 'Description']
df1.reindex(columns=encoding)
[25]:
Octal | Code | Description | |
---|---|---|---|
0 | 001 | U+0000 | NaN |
1 | 002 | U+0001 | NaN |
2 | 003 | U+0002 | NaN |
3 | 004 | U+0003 | NaN |
4 | 004 | U+0004 | NaN |
5 | 005 | U+0005 | NaN |
Argumente der Funktion reindex
#
Argument |
Beschreibung |
---|---|
|
Neue Sequenz, die als Index verwendet werden soll. Kann eine Index-Instanz oder eine andere sequenzähnliche Python-Datenstruktur sein. Ein Index wird genau so verwendet, wie er ist, ohne dass er kopiert wird. |
|
Die neu zu indizierende Achse, entweder |
|
Interpolationsmethode; |
|
Ersatzwert, der zu verwenden ist, wenn fehlende Daten durch Neuindizierung eingefügt werden. Verwendet |
|
Beim Vorwärts- oder Rückwärtsfüllen die maximale Anzahl der zu füllenden Elemente. |
|
Beim Vorwärts- oder Rückwärtsauffüllen die maximale Größe der Lücke, die bei ungenauen Übereinstimmungen gefüllt werden soll. |
|
Einfachen Index auf Ebene von |
|
Wenn |
Achsenindizes umbenennen#
Die Achsenbeschriftungen können durch eine Funktion oder ein Mapping umgewandelt werden, um neue, anders beschriftete Objekte zu erzeugen. Ihr könnt auch die Achsen an Ort und Stelle ändern, ohne eine neue Datenstruktur zu erstellen. Hier ist ein einfaches Beispiel:
[26]:
df3 = pd.DataFrame(np.arange(12).reshape((3, 4)),
index=['Deutsch', 'English', 'Français'],
columns=[1,2,3,4])
df3
[26]:
1 | 2 | 3 | 4 | |
---|---|---|---|---|
Deutsch | 0 | 1 | 2 | 3 |
English | 4 | 5 | 6 | 7 |
Français | 8 | 9 | 10 | 11 |
Achsenindizes umbenennen mit map
#
Wie Series
haben auch die Achsenindizes eine map
-Methode:
[27]:
transform = lambda x: x[:2].upper()
df3.index.map(transform)
[27]:
Index(['DE', 'EN', 'FR'], dtype='object')
Ihr könnt den Index zuweisen und den DataFrame an Ort und Stelle ändern:
[28]:
df3.index = df3.index.map(transform)
df3
[28]:
1 | 2 | 3 | 4 | |
---|---|---|---|---|
DE | 0 | 1 | 2 | 3 |
EN | 4 | 5 | 6 | 7 |
FR | 8 | 9 | 10 | 11 |
Achsenindizes umbenennen mit rename
#
Wenn ihr eine umgewandelte Version eures Datensatzes erstellen möchtet ohne das Original zu verändern, könnt ihr rename
verwenden:
[29]:
df3.rename(index=str.lower)
[29]:
1 | 2 | 3 | 4 | |
---|---|---|---|---|
de | 0 | 1 | 2 | 3 |
en | 4 | 5 | 6 | 7 |
fr | 8 | 9 | 10 | 11 |
Insbesondere kann rename
in Verbindung mit einem dict
-ähnlichen Objekt verwendet werden, das neue Werte für eine Teilmenge der Achsenbeschriftungen liefert:
[30]:
df3.rename(index={'DE': 'BE', 'EN': 'DE', 'FR': 'EN'},
columns={1: 0, 2: 1, 3: 2, 4: 3})
[30]:
0 | 1 | 2 | 3 | |
---|---|---|---|---|
BE | 0 | 1 | 2 | 3 |
DE | 4 | 5 | 6 | 7 |
EN | 8 | 9 | 10 | 11 |
rename
erspart euch das manuelle Kopieren des DataFrame und die Zuweisung seiner Index- und Spaltenattribute. Wenn ihr einen Datensatz an Ort und Stelle ändern möchtet, übergebt zusätzlich noch inplace=True
:
[31]:
df3.rename(index={'DE': 'BE', 'EN': 'DE', 'FR': 'EN'},
columns={1: 0, 2: 1, 3: 2, 4: 3},
inplace=True)
df3
[31]:
0 | 1 | 2 | 3 | |
---|---|---|---|---|
BE | 0 | 1 | 2 | 3 |
DE | 4 | 5 | 6 | 7 |
EN | 8 | 9 | 10 | 11 |
Hierarchische Indizierung#
Die hierarchische Indizierung ist eine wichtige Funktion von pandas, die euch ermöglicht, mehrere Indexebenen auf einer Achse zu haben. Dies bietet euch die Möglichkeit, mit höherdimensionalen Daten in einer niedrigdimensionalen Form zu arbeiten.
Beginnen wir mit einem einfachen Beispiel: Erstellen wir eine Reihe Liste von Listen als Index:
[32]:
hits = pd.Series([83080,20336,11376,1228,468],
index=[['Jupyter Tutorial',
'Jupyter Tutorial',
'PyViz Tutorial',
'Python Basics',
'Python Basics'],
['de', 'en', 'de', 'de', 'en']])
hits
[32]:
Jupyter Tutorial de 83080
en 20336
PyViz Tutorial de 11376
Python Basics de 1228
en 468
dtype: int64
Was ihr seht, ist eine graphische Ansicht einer Serie mit einem pandas.MultiIndex. Die „Lücken“ in der Indexanzeige bedeuten, dass die Beschriftung darüber verwendet werden soll.
[33]:
hits.index
[33]:
MultiIndex([('Jupyter Tutorial', 'de'),
('Jupyter Tutorial', 'en'),
( 'PyViz Tutorial', 'de'),
( 'Python Basics', 'de'),
( 'Python Basics', 'en')],
)
Bei einem hierarchisch indizierten Objekt ist eine so genannte partielle Indizierung möglich, mit der ihr Teilmengen der Daten gezielt auswählen könnt:
[34]:
hits['Jupyter Tutorial']
[34]:
de 83080
en 20336
dtype: int64
[35]:
hits['Jupyter Tutorial':'Python Basics']
[35]:
Jupyter Tutorial de 83080
en 20336
PyViz Tutorial de 11376
Python Basics de 1228
en 468
dtype: int64
[36]:
hits.loc[['Jupyter Tutorial', 'Python Basics']]
[36]:
Jupyter Tutorial de 83080
en 20336
Python Basics de 1228
en 468
dtype: int64
Die Auswahl ist sogar von einer „inneren“ Ebene aus möglich. Im folgenden wähle ich alle Werte mit dem Wert 1
aus der zweiten Indexebene aus:
[37]:
hits.loc[:, 'de']
[37]:
Jupyter Tutorial 83080
PyViz Tutorial 11376
Python Basics 1228
dtype: int64
Bei einem DataFrame kann jede Achse einen hierarchischen Index haben:
[38]:
version_hits = [[19651,0,30134,0,33295,0],
[4722,1825,3497,2576,4009,3707],
[2573,0,4873,0,3930,0],
[525,0,427,0,276,0],
[157,0,85,0,226,0]]
df = pd.DataFrame(version_hits,
index=[['Jupyter Tutorial',
'Jupyter Tutorial',
'PyViz Tutorial',
'Python Basics',
'Python Basics'],
['de', 'en', 'de', 'de', 'en']],
columns=[['12/2021', '12/2021',
'01/2022', '01/2022',
'02/2022', '02/2022'],
['latest', 'stable',
'latest', 'stable',
'latest', 'stable']])
df
[38]:
12/2021 | 01/2022 | 02/2022 | |||||
---|---|---|---|---|---|---|---|
latest | stable | latest | stable | latest | stable | ||
Jupyter Tutorial | de | 19651 | 0 | 30134 | 0 | 33295 | 0 |
en | 4722 | 1825 | 3497 | 2576 | 4009 | 3707 | |
PyViz Tutorial | de | 2573 | 0 | 4873 | 0 | 3930 | 0 |
Python Basics | de | 525 | 0 | 427 | 0 | 276 | 0 |
en | 157 | 0 | 85 | 0 | 226 | 0 |
Die Hierarchieebenen können Namen haben (als Zeichenketten oder beliebige Python-Objekte). Wenn dies der Fall ist, werden diese in der Konsolenausgabe angezeigt:
[39]:
df.index.names = ['Title', 'Language']
df.columns.names = ['Month', 'Version']
df
[39]:
Month | 12/2021 | 01/2022 | 02/2022 | ||||
---|---|---|---|---|---|---|---|
Version | latest | stable | latest | stable | latest | stable | |
Title | Language | ||||||
Jupyter Tutorial | de | 19651 | 0 | 30134 | 0 | 33295 | 0 |
en | 4722 | 1825 | 3497 | 2576 | 4009 | 3707 | |
PyViz Tutorial | de | 2573 | 0 | 4873 | 0 | 3930 | 0 |
Python Basics | de | 525 | 0 | 427 | 0 | 276 | 0 |
en | 157 | 0 | 85 | 0 | 226 | 0 |
Warnung:
Achtet darauf, dass die Indexnamen Month
und Version
nicht Teil der Zeilenbezeichnungen (der df.index
-Werte) sind.
Mit pandas.MultiIndex.from_arrays kann selbst ein MultiIndex
erstellt und dann wiederverwendet werden; die Spalten im vorangehenden DataFrame mit Ebenennamen könnten so erstellt werden:
[40]:
pd.MultiIndex.from_arrays([['Jupyter Tutorial',
'Jupyter Tutorial',
'PyViz Tutorial',
'Python Basics',
'Python Basics'],
['de', 'en', 'de', 'de', 'en']],
names=['Title', 'Language'])
[40]:
MultiIndex([('Jupyter Tutorial', 'de'),
('Jupyter Tutorial', 'en'),
( 'PyViz Tutorial', 'de'),
( 'Python Basics', 'de'),
( 'Python Basics', 'en')],
names=['Title', 'Language'])
Mit der Teilspaltenindizierung könnt ihr auf ähnliche Weise Spaltengruppen auswählen:
[41]:
df.loc['Jupyter Tutorial']
[41]:
Month | 12/2021 | 01/2022 | 02/2022 | |||
---|---|---|---|---|---|---|
Version | latest | stable | latest | stable | latest | stable |
Language | ||||||
de | 19651 | 0 | 30134 | 0 | 33295 | 0 |
en | 4722 | 1825 | 3497 | 2576 | 4009 | 3707 |
[42]:
df.loc['PyViz Tutorial':'Python Basics']
[42]:
Month | 12/2021 | 01/2022 | 02/2022 | ||||
---|---|---|---|---|---|---|---|
Version | latest | stable | latest | stable | latest | stable | |
Title | Language | ||||||
PyViz Tutorial | de | 2573 | 0 | 4873 | 0 | 3930 | 0 |
Python Basics | de | 525 | 0 | 427 | 0 | 276 | 0 |
en | 157 | 0 | 85 | 0 | 226 | 0 |
loc
mit Liste von Tupeln:
[43]:
df.loc[[('Jupyter Tutorial', 'de'), ('PyViz Tutorial', 'de')]]
[43]:
Month | 12/2021 | 01/2022 | 02/2022 | ||||
---|---|---|---|---|---|---|---|
Version | latest | stable | latest | stable | latest | stable | |
Title | Language | ||||||
Jupyter Tutorial | de | 19651 | 0 | 30134 | 0 | 33295 | 0 |
PyViz Tutorial | de | 2573 | 0 | 4873 | 0 | 3930 | 0 |
loc
mit Liste des zweiten Spaltenindex:
[44]:
df.loc[:, ['de'], :]
[44]:
Month | 12/2021 | 01/2022 | 02/2022 | ||||
---|---|---|---|---|---|---|---|
Version | latest | stable | latest | stable | latest | stable | |
Title | Language | ||||||
Jupyter Tutorial | de | 19651 | 0 | 30134 | 0 | 33295 | 0 |
PyViz Tutorial | de | 2573 | 0 | 4873 | 0 | 3930 | 0 |
Python Basics | de | 525 | 0 | 427 | 0 | 276 | 0 |
Verwenden von slice
:
[45]:
df.loc[slice('Jupyter Tutorial'), :]
[45]:
Month | 12/2021 | 01/2022 | 02/2022 | ||||
---|---|---|---|---|---|---|---|
Version | latest | stable | latest | stable | latest | stable | |
Title | Language | ||||||
Jupyter Tutorial | de | 19651 | 0 | 30134 | 0 | 33295 | 0 |
en | 4722 | 1825 | 3497 | 2576 | 4009 | 3707 |
[46]:
df.loc[slice('Jupyter Tutorial', 'PyViz Tutorial'), :]
[46]:
Month | 12/2021 | 01/2022 | 02/2022 | ||||
---|---|---|---|---|---|---|---|
Version | latest | stable | latest | stable | latest | stable | |
Title | Language | ||||||
Jupyter Tutorial | de | 19651 | 0 | 30134 | 0 | 33295 | 0 |
en | 4722 | 1825 | 3497 | 2576 | 4009 | 3707 | |
PyViz Tutorial | de | 2573 | 0 | 4873 | 0 | 3930 | 0 |
[47]:
df.loc[slice('Jupyter Tutorial', 'PyViz Tutorial'), '12/2021']
[47]:
Version | latest | stable | |
---|---|---|---|
Title | Language | ||
Jupyter Tutorial | de | 19651 | 0 |
en | 4722 | 1825 | |
PyViz Tutorial | de | 2573 | 0 |
Verwenden von slice
, und Listen:
[48]:
df.loc[slice('Jupyter Tutorial', 'PyViz Tutorial'), ['de'], :]
[48]:
Month | 12/2021 | 01/2022 | 02/2022 | ||||
---|---|---|---|---|---|---|---|
Version | latest | stable | latest | stable | latest | stable | |
Title | Language | ||||||
Jupyter Tutorial | de | 19651 | 0 | 30134 | 0 | 33295 | 0 |
PyViz Tutorial | de | 2573 | 0 | 4873 | 0 | 3930 | 0 |
Ansicht vs. Kopie#
In Pandas hängt es von der Struktur und den Datentypen des ursprünglichen DataFrame ab, ob ihr einen View erhaltet oder nicht – und ob Änderungen, die an einem View vorgenommen werden, in den ursprünglichen DataFrame zurück übertragen werden.
stack
und unstack
#
Die hierarchische Indizierung spielt eine wichtige Rolle bei der Umformung von Daten und gruppenbasierten Operationen wie der Bildung einer Pivot-Tabelle. Zum Beispiel könnt ihr diese Daten mit der pandas.Series.unstack-Methode in einen DataFrame umordnen:
[49]:
hits.unstack()
[49]:
de | en | |
---|---|---|
Jupyter Tutorial | 83080.0 | 20336.0 |
PyViz Tutorial | 11376.0 | NaN |
Python Basics | 1228.0 | 468.0 |
Die umgekehrte Operation von unstack ist stack:
[50]:
hits.unstack().stack()
[50]:
Jupyter Tutorial de 83080.0
en 20336.0
PyViz Tutorial de 11376.0
Python Basics de 1228.0
en 468.0
dtype: float64
Umordnen und Sortieren von Ebenen#
Es kann vorkommen, dass ihr die Reihenfolge der Ebenen auf einer Achse neu anordnen oder die Daten nach den Werten in einer bestimmten Ebene sortieren wollt. Die Funktion pandas.DataFrame.swaplevel nimmt zwei Ebenennummern oder -namen entgegen und gibt ein neues Objekt zurück, in dem die Ebenen vertauscht sind (die Daten bleiben jedoch unverändert):
[51]:
df.swaplevel('Language', 'Title')
[51]:
Month | 12/2021 | 01/2022 | 02/2022 | ||||
---|---|---|---|---|---|---|---|
Version | latest | stable | latest | stable | latest | stable | |
Language | Title | ||||||
de | Jupyter Tutorial | 19651 | 0 | 30134 | 0 | 33295 | 0 |
en | Jupyter Tutorial | 4722 | 1825 | 3497 | 2576 | 4009 | 3707 |
de | PyViz Tutorial | 2573 | 0 | 4873 | 0 | 3930 | 0 |
Python Basics | 525 | 0 | 427 | 0 | 276 | 0 | |
en | Python Basics | 157 | 0 | 85 | 0 | 226 | 0 |
pandas.DataFrame.sort_index hingegen sortiert die Daten nur nach den Werten in einer einzigen Ebene. Beim Vertauschen von Ebenen ist es nicht unüblich, auch sort_index
zu verwenden, damit das Ergebnis lexikografisch nach der angegebenen Ebene sortiert wird:
[52]:
df.sort_index(level=0)
[52]:
Month | 12/2021 | 01/2022 | 02/2022 | ||||
---|---|---|---|---|---|---|---|
Version | latest | stable | latest | stable | latest | stable | |
Title | Language | ||||||
Jupyter Tutorial | de | 19651 | 0 | 30134 | 0 | 33295 | 0 |
en | 4722 | 1825 | 3497 | 2576 | 4009 | 3707 | |
PyViz Tutorial | de | 2573 | 0 | 4873 | 0 | 3930 | 0 |
Python Basics | de | 525 | 0 | 427 | 0 | 276 | 0 |
en | 157 | 0 | 85 | 0 | 226 | 0 |
[53]:
df.swaplevel(0, 1).sort_index(level=0)
[53]:
Month | 12/2021 | 01/2022 | 02/2022 | ||||
---|---|---|---|---|---|---|---|
Version | latest | stable | latest | stable | latest | stable | |
Language | Title | ||||||
de | Jupyter Tutorial | 19651 | 0 | 30134 | 0 | 33295 | 0 |
PyViz Tutorial | 2573 | 0 | 4873 | 0 | 3930 | 0 | |
Python Basics | 525 | 0 | 427 | 0 | 276 | 0 | |
en | Jupyter Tutorial | 4722 | 1825 | 3497 | 2576 | 4009 | 3707 |
Python Basics | 157 | 0 | 85 | 0 | 226 | 0 |
Hinweis:
Die Leistung der Datenauswahl ist bei hierarchisch indizierten Objekten wesentlich besser, wenn der Index lexikografisch sortiert ist, beginnend mit der äußersten Ebene, d.h. dem Ergebnis des Aufrufs von sort_index(level=0)
oder sort_index()
.
Zusammenfassende Statistiken nach Ebene#
Viele deskriptive und zusammenfassende Statistiken für DataFrame
und Series
verfügen über eine Ebenenoption, mit der die Ebene angeben können, nach der ihr auf einer bestimmten Achse aggregieren könnt. Betrachtet den obigen DataFrame
; wir können entweder die Zeilen oder die Spalten nach der Ebene aggregieren wie folgt:
[54]:
df.groupby(level='Language').sum()
[54]:
Month | 12/2021 | 01/2022 | 02/2022 | |||
---|---|---|---|---|---|---|
Version | latest | stable | latest | stable | latest | stable |
Language | ||||||
de | 22749 | 0 | 35434 | 0 | 37501 | 0 |
en | 4879 | 1825 | 3582 | 2576 | 4235 | 3707 |
[55]:
df.groupby(level='Month', axis=1).sum()
[55]:
Month | 01/2022 | 02/2022 | 12/2021 | |
---|---|---|---|---|
Title | Language | |||
Jupyter Tutorial | de | 30134 | 33295 | 19651 |
en | 6073 | 7716 | 6547 | |
PyViz Tutorial | de | 4873 | 3930 | 2573 |
Python Basics | de | 427 | 276 | 525 |
en | 85 | 226 | 157 |
Intern wird dazu die pandas.DataFrame.groupby-Maschinerie von Pandas verwendet, die in Gruppenoperationen näher erläutert wird.
Indizierung mit den Spalten eines DataFrame#
Es ist nicht ungewöhnlich, eine oder mehrere Spalten eines DataFrame als Zeilenindex zu verwenden; alternativ könnt ihr den Zeilenindex auch in die Spalten des DataFrame verschieben. Hier ist ein Beispiel-DataFrame:
[56]:
data = [['Jupyter Tutorial', 'de', 19651,0,30134,0,33295,0],
['Jupyter Tutorial', 'en', 4722,1825,3497,2576,4009,3707],
['PyViz Tutorial', 'de', 2573,0,4873,0,3930,0],
['Python Basics', 'de', 525,0,427,0,276,0],
['Python Basics', 'en', 157,0,85,0,226,0]]
df = pd.DataFrame(data)
df
[56]:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
---|---|---|---|---|---|---|---|---|
0 | Jupyter Tutorial | de | 19651 | 0 | 30134 | 0 | 33295 | 0 |
1 | Jupyter Tutorial | en | 4722 | 1825 | 3497 | 2576 | 4009 | 3707 |
2 | PyViz Tutorial | de | 2573 | 0 | 4873 | 0 | 3930 | 0 |
3 | Python Basics | de | 525 | 0 | 427 | 0 | 276 | 0 |
4 | Python Basics | en | 157 | 0 | 85 | 0 | 226 | 0 |
Die Funktion pandas.DataFrame.set_index erstellt einen neuen DataFrame, der eine oder mehrere seiner Spalten als Index verwendet:
[57]:
df2 = df.set_index([0,1])
df2
[57]:
2 | 3 | 4 | 5 | 6 | 7 | ||
---|---|---|---|---|---|---|---|
0 | 1 | ||||||
Jupyter Tutorial | de | 19651 | 0 | 30134 | 0 | 33295 | 0 |
en | 4722 | 1825 | 3497 | 2576 | 4009 | 3707 | |
PyViz Tutorial | de | 2573 | 0 | 4873 | 0 | 3930 | 0 |
Python Basics | de | 525 | 0 | 427 | 0 | 276 | 0 |
en | 157 | 0 | 85 | 0 | 226 | 0 |
Standardmäßig werden die Spalten aus dem DataFrame entfernt, Ihr könnt sie aber auch drin lassen, indem ihr drop=False
an set_index
übergebt:
[58]:
df.set_index([0,1], drop=False)
[58]:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | ||
---|---|---|---|---|---|---|---|---|---|
0 | 1 | ||||||||
Jupyter Tutorial | de | Jupyter Tutorial | de | 19651 | 0 | 30134 | 0 | 33295 | 0 |
en | Jupyter Tutorial | en | 4722 | 1825 | 3497 | 2576 | 4009 | 3707 | |
PyViz Tutorial | de | PyViz Tutorial | de | 2573 | 0 | 4873 | 0 | 3930 | 0 |
Python Basics | de | Python Basics | de | 525 | 0 | 427 | 0 | 276 | 0 |
en | Python Basics | en | 157 | 0 | 85 | 0 | 226 | 0 |
pandas.DataFrame.reset_index hingegen bewirkt das Gegenteil von set_index
; die hierarchischen Indexebenen werden in die Spalten verschoben:
[59]:
df2.reset_index()
[59]:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
---|---|---|---|---|---|---|---|---|
0 | Jupyter Tutorial | de | 19651 | 0 | 30134 | 0 | 33295 | 0 |
1 | Jupyter Tutorial | en | 4722 | 1825 | 3497 | 2576 | 4009 | 3707 |
2 | PyViz Tutorial | de | 2573 | 0 | 4873 | 0 | 3930 | 0 |
3 | Python Basics | de | 525 | 0 | 427 | 0 | 276 | 0 |
4 | Python Basics | en | 157 | 0 | 85 | 0 | 226 | 0 |