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)
Cell In[4], line 1
----> 1 obj.index[1] = "2022-02-03"

File ~/.local/share/virtualenvs/python-311-6zxVKbDJ/lib/python3.11/site-packages/pandas/core/indexes/base.py:5157, in Index.__setitem__(self, key, value)
   5155 @final
   5156 def __setitem__(self, key, value):
-> 5157     raise TypeError("Index does not support mutable operations")

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]:
Index([0, 1, 2], dtype='int64')
[6]:
obj2 = pd.Series(np.random.randn(3),index=labels)
[7]:
obj2
[7]:
0   -0.426526
1    0.038709
2    0.316950
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 = pd.concat([df1, 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

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:

[16]:
df12.index.is_unique
[16]:
False

Um doppelte Label zu vermeiden, könnt ihr z.B. ignore_index=True verwenden:

[17]:
df12 = pd.concat([df1, df2], ignore_index=True)

df12
[17]:
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

append

verkettet zusätzliche Indexobjekte, wodurch ein neuer Index entsteht

difference

berechnet die Differenz zweier Mengen als Index

intersection

berechnet die Schnittmenge

union

berechnet die Vereinigungsmenge

isin

berechnet ein boolesches Array, das angibt, ob jeder Wert in der übergebenen Sammlung enthalten ist

delete

berechnet einen neuen Index, wobei das Element in Index i gelöscht wird

drop

berechnet einen neuen Index durch Löschen der übergebenen Werte

insert

berechnet neuen Index durch Einfügen des Elements in den Index i

is_monotonic

gibt True zurück, wenn jedes Element größer oder gleich dem vorherigen Element ist

is_unique

gibt True zurück, wenn der Index keine doppelten Werte enthält.

unique

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.:

[18]:
obj = pd.Series(range(7), index=pd.date_range("2022-02-02", periods=7))
[19]:
obj
[19]:
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
[20]:
new_index = pd.date_range("2022-02-03", periods=7)
[21]:
obj.reindex(new_index)
[21]:
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:

[22]:
obj.reindex(new_index, method="ffill")
[22]:
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:

[23]:
df1.reindex(range(7))
[23]:
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:

[24]:
encoding = ["Octal", "Code", "Description"]

df1.reindex(columns=encoding)
[24]:
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

labels

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.

axis

Die neu zu indizierende Achse, entweder index (Zeilen) oder columns (Spalten). Die Vorgabe ist index. Ihr könnt alternativ reindex(index=new_labels) oder reindex(columns=new_labels) verwenden.

method

Interpolationsmethode; ffill füllt vorwärts, während bfill rückwärts füllt.

fill_value

Ersatzwert, der zu verwenden ist, wenn fehlende Daten durch Neuindizierung eingefügt werden. Verwendet fill_value='missing' (das Standardverhalten), wenn die fehlenden Bezeichnungen im Ergebnis Nullwerte haben sollen.

limit

Beim Vorwärts- oder Rückwärtsfüllen die maximale Anzahl der zu füllenden Elemente.

tolerance

Beim Vorwärts- oder Rückwärtsauffüllen die maximale Größe der Lücke, die bei ungenauen Übereinstimmungen gefüllt werden soll.

level

Einfachen Index auf Ebene von MultiIndex abgleichen; andernfalls Teilmenge auswählen.

copy

Wenn True, werden die zugrunde liegenden Daten immer kopiert, auch wenn der neue Index dem alten Index entspricht; wenn False, werden die Daten nicht kopiert, wenn die Indizes gleichwertig sind.

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:

[25]:
df3 = pd.DataFrame(
    np.arange(12).reshape((3, 4)),
    index=["Deutsch", "English", "Français"],
    columns=[1, 2, 3, 4],
)

df3
[25]:
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:

[26]:
transform = lambda x: x[:2].upper()

df3.index.map(transform)
[26]:
Index(['DE', 'EN', 'FR'], dtype='object')

Ihr könnt den Index zuweisen und den DataFrame an Ort und Stelle ändern:

[27]:
df3.index = df3.index.map(transform)

df3
[27]:
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:

[28]:
df3.rename(index=str.lower)
[28]:
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:

[29]:
df3.rename(
    index={"DE": "BE", "EN": "DE", "FR": "EN"},
    columns={1: 0, 2: 1, 3: 2, 4: 3},
)
[29]:
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:

[30]:
df3.rename(
    index={"DE": "BE", "EN": "DE", "FR": "EN"},
    columns={1: 0, 2: 1, 3: 2, 4: 3},
    inplace=True,
)

df3
[30]:
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:

[31]:
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
[31]:
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.

[32]:
hits.index
[32]:
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:

[33]:
hits["Jupyter Tutorial"]
[33]:
de    83080
en    20336
dtype: int64
[34]:
hits["Jupyter Tutorial":"Python Basics"]
[34]:
Jupyter Tutorial  de    83080
                  en    20336
PyViz Tutorial    de    11376
Python Basics     de     1228
                  en      468
dtype: int64
[35]:
hits.loc[["Jupyter Tutorial", "Python Basics"]]
[35]:
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:

[36]:
hits.loc[:, "de"]
[36]:
Jupyter Tutorial    83080
PyViz Tutorial      11376
Python Basics        1228
dtype: int64

Bei einem DataFrame kann jede Achse einen hierarchischen Index haben:

[37]:
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
[37]:
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:

[38]:
df.index.names = ["Title", "Language"]
df.columns.names = ["Month", "Version"]

df
[38]:
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:

[39]:
pd.MultiIndex.from_arrays(
    [
        [
            "Jupyter Tutorial",
            "Jupyter Tutorial",
            "PyViz Tutorial",
            "Python Basics",
            "Python Basics",
        ],
        ["de", "en", "de", "de", "en"],
    ],
    names=["Title", "Language"],
)
[39]:
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:

[40]:
df.loc["Jupyter Tutorial"]
[40]:
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
[41]:
df.loc["PyViz Tutorial":"Python Basics"]
[41]:
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:

[42]:
df.loc[[("Jupyter Tutorial", "de"), ("PyViz Tutorial", "de")]]
[42]:
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:

[43]:
df.loc[:, ["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
Python Basics de 525 0 427 0 276 0

Verwenden von slice:

[44]:
df.loc[slice("Jupyter Tutorial"), :]
[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
en 4722 1825 3497 2576 4009 3707
[45]:
df.loc[slice("Jupyter Tutorial", "PyViz 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
PyViz Tutorial de 2573 0 4873 0 3930 0
[46]:
df.loc[slice("Jupyter Tutorial", "PyViz Tutorial"), "12/2021"]
[46]:
Version latest stable
Title Language
Jupyter Tutorial de 19651 0
en 4722 1825
PyViz Tutorial de 2573 0

Verwenden von slice, und Listen:

[47]:
df.loc[slice("Jupyter Tutorial", "PyViz Tutorial"), ["de"], :]
[47]:
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.

**Siehe auch:** * [Returning a view versus a copy](https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy) * [Views and Copies in pandas](https://www.practicaldatascience.org/html/views_and_copies_in_pandas.html)

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:

[48]:
hits.unstack()
[48]:
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:

[49]:
hits.unstack().stack()
[49]:
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):

[50]:
df.swaplevel("Language", "Title")
[50]:
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:

[51]:
df.sort_index(level=0)
[51]:
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
[52]:
df.swaplevel(0, 1).sort_index(level=0)
[52]:
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:

[53]:
df.groupby(level="Language").sum()
[53]:
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
[54]:
df.groupby(level="Month", axis=1).sum()
[54]:
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:

[55]:
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
[55]:
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:

[56]:
df2 = df.set_index([0, 1])

df2
[56]:
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:

[57]:
df.set_index([0, 1], drop=False)
[57]:
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:

[58]:
df2.reset_index()
[58]:
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