Daten auswählen und filtern¶
Die Indizierung von Serien (obj[...]) funktioniert analog zur Indizierung von NumPy-Arrays, außer dass ihr Indexwerte der Serie statt nur Ganzzahlen verwenden könnt. Hier sind einige Beispiele dafür:
[1]:
import numpy as np
import pandas as pd
[2]:
idx = pd.date_range("2022-02-02", periods=7)
rng = np.random.default_rng()
s = pd.Series(rng.normal(size=7), index=idx)
[3]:
s
[3]:
2022-02-02 -1.143049
2022-02-03 0.371882
2022-02-04 -0.739300
2022-02-05 0.216581
2022-02-06 -0.153057
2022-02-07 -1.024227
2022-02-08 1.677115
Freq: D, dtype: float64
[4]:
s["2022-02-03"]
[4]:
np.float64(0.3718820858366448)
[5]:
s.iloc[1]
[5]:
np.float64(0.3718820858366448)
[6]:
s[2:4]
[6]:
2022-02-04 -0.739300
2022-02-05 0.216581
Freq: D, dtype: float64
[7]:
s[["2022-02-04", "2022-02-03", "2022-02-02"]]
[7]:
2022-02-04 -0.739300
2022-02-03 0.371882
2022-02-02 -1.143049
dtype: float64
[8]:
s.iloc[[1, 3]]
[8]:
2022-02-03 0.371882
2022-02-05 0.216581
Freq: 2D, dtype: float64
[9]:
s[s > 0]
[9]:
2022-02-03 0.371882
2022-02-05 0.216581
2022-02-08 1.677115
dtype: float64
Zwar könnt ihr auf diese Weise Daten nach Label auswählen, doch die bevorzugte Methode zur Auswahl von Indexwerten ist der loc-Operator:
[10]:
s.loc[["2022-02-04", "2022-02-03", "2022-02-02"]]
[10]:
2022-02-04 -0.739300
2022-02-03 0.371882
2022-02-02 -1.143049
dtype: float64
Der Grund für die Bevorzugung von loc liegt in der unterschiedlichen Behandlung von Ganzzahlen bei der Indexierung mit []. Bei der regulären []-basierten Indizierung werden Ganzzahlen als Label behandelt, wenn der Index Ganzzahlen enthält, so dass das Verhalten je nach Datentyp des Index unterschiedlich ist. In unserem Beispiel wird der Ausdruck s.loc[[3, 2, 1]] fehlschlagen, da der Index keine ganzen Zahlen enthält:
[11]:
s.loc[[3, 2, 1]]
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
Cell In[11], line 1
----> 1 s.loc[[3, 2, 1]]
File ~/cusy/trn/jupyter-tutorial/uvenvs/py313/.venv/lib/python3.13/site-packages/pandas/core/indexing.py:1191, in _LocationIndexer.__getitem__(self, key)
1189 maybe_callable = com.apply_if_callable(key, self.obj)
1190 maybe_callable = self._check_deprecated_callable_usage(key, maybe_callable)
-> 1191 return self._getitem_axis(maybe_callable, axis=axis)
File ~/cusy/trn/jupyter-tutorial/uvenvs/py313/.venv/lib/python3.13/site-packages/pandas/core/indexing.py:1420, in _LocIndexer._getitem_axis(self, key, axis)
1417 if hasattr(key, "ndim") and key.ndim > 1:
1418 raise ValueError("Cannot index with multidimensional key")
-> 1420 return self._getitem_iterable(key, axis=axis)
1422 # nested tuple slicing
1423 if is_nested_tuple(key, labels):
File ~/cusy/trn/jupyter-tutorial/uvenvs/py313/.venv/lib/python3.13/site-packages/pandas/core/indexing.py:1360, in _LocIndexer._getitem_iterable(self, key, axis)
1357 self._validate_key(key, axis)
1359 # A collection of keys
-> 1360 keyarr, indexer = self._get_listlike_indexer(key, axis)
1361 return self.obj._reindex_with_indexers(
1362 {axis: [keyarr, indexer]}, copy=True, allow_dups=True
1363 )
File ~/cusy/trn/jupyter-tutorial/uvenvs/py313/.venv/lib/python3.13/site-packages/pandas/core/indexing.py:1558, in _LocIndexer._get_listlike_indexer(self, key, axis)
1555 ax = self.obj._get_axis(axis)
1556 axis_name = self.obj._get_axis_name(axis)
-> 1558 keyarr, indexer = ax._get_indexer_strict(key, axis_name)
1560 return keyarr, indexer
File ~/cusy/trn/jupyter-tutorial/uvenvs/py313/.venv/lib/python3.13/site-packages/pandas/core/indexes/base.py:6200, in Index._get_indexer_strict(self, key, axis_name)
6197 else:
6198 keyarr, indexer, new_indexer = self._reindex_non_unique(keyarr)
-> 6200 self._raise_if_missing(keyarr, indexer, axis_name)
6202 keyarr = self.take(indexer)
6203 if isinstance(key, Index):
6204 # GH 42790 - Preserve name from an Index
File ~/cusy/trn/jupyter-tutorial/uvenvs/py313/.venv/lib/python3.13/site-packages/pandas/core/indexes/base.py:6249, in Index._raise_if_missing(self, key, indexer, axis_name)
6247 if nmissing:
6248 if nmissing == len(indexer):
-> 6249 raise KeyError(f"None of [{key}] are in the [{axis_name}]")
6251 not_found = list(ensure_index(key)[missing_mask.nonzero()[0]].unique())
6252 raise KeyError(f"{not_found} not in index")
KeyError: "None of [Index([3, 2, 1], dtype='int64')] are in the [index]"
Während der loc-Operator ausschließlich Label indiziert, indiziert der iloc-Operator ausschließlich mit ganzen Zahlen:
[12]:
s.iloc[[3, 2, 1]]
[12]:
2022-02-05 0.216581
2022-02-04 -0.739300
2022-02-03 0.371882
Freq: -1D, dtype: float64
Ihr könnt auch mit Labels slicen, aber das funktioniert anders als das normale Python-Slicing, da der Endpunkt inklusive ist:
[13]:
s.loc["2022-02-03":"2022-02-04"]
[13]:
2022-02-03 0.371882
2022-02-04 -0.739300
Freq: D, dtype: float64
Durch die Einstellung mit diesen Methoden wird der entsprechende Abschnitt der Reihe geändert:
[14]:
s.loc["2022-02-03":"2022-02-04"] = 0
s
[14]:
2022-02-02 -1.143049
2022-02-03 0.000000
2022-02-04 0.000000
2022-02-05 0.216581
2022-02-06 -0.153057
2022-02-07 -1.024227
2022-02-08 1.677115
Freq: D, dtype: float64
Die Indizierung in einem DataFrame dient dazu, eine oder mehrere Spalten entweder mit einem einzelnen Wert oder einer Folge abzurufen:
[15]:
data = {
"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"],
"Key": ["NUL", "Ctrl-A", "Ctrl-B", "Ctrl-C", "Ctrl-D", "Ctrl-E"],
}
df = pd.DataFrame(data)
df = pd.DataFrame(data, columns=["Decimal", "Octal", "Key"], index=df["Code"])
df
[15]:
| Decimal | Octal | Key | |
|---|---|---|---|
| Code | |||
| U+0000 | 0 | 001 | NUL |
| U+0001 | 1 | 002 | Ctrl-A |
| U+0002 | 2 | 003 | Ctrl-B |
| U+0003 | 3 | 004 | Ctrl-C |
| U+0004 | 4 | 004 | Ctrl-D |
| U+0005 | 5 | 005 | Ctrl-E |
[16]:
df["Key"]
[16]:
Code
U+0000 NUL
U+0001 Ctrl-A
U+0002 Ctrl-B
U+0003 Ctrl-C
U+0004 Ctrl-D
U+0005 Ctrl-E
Name: Key, dtype: object
[17]:
df[["Decimal", "Key"]]
[17]:
| Decimal | Key | |
|---|---|---|
| Code | ||
| U+0000 | 0 | NUL |
| U+0001 | 1 | Ctrl-A |
| U+0002 | 2 | Ctrl-B |
| U+0003 | 3 | Ctrl-C |
| U+0004 | 4 | Ctrl-D |
| U+0005 | 5 | Ctrl-E |
Die Zeilenauswahlsyntax df[:2] wird aus Gründen der Bequemlichkeit bereitgestellt. Durch die Übergabe eines einzelnen Elements oder einer Liste an den []-Operator werden Spalten ausgewählt.
[18]:
df[:2]
[18]:
| Decimal | Octal | Key | |
|---|---|---|---|
| Code | |||
| U+0000 | 0 | 001 | NUL |
| U+0001 | 1 | 002 | Ctrl-A |
Ein weiterer Anwendungsfall ist die Indizierung mit einem booleschen DataFrame, der beispielsweise durch einen Skalarvergleich erzeugt wird:
[19]:
df["Decimal"] > 2
[19]:
Code
U+0000 False
U+0001 False
U+0002 False
U+0003 True
U+0004 True
U+0005 True
Name: Decimal, dtype: bool
[20]:
df[df["Decimal"] > 2]
[20]:
| Decimal | Octal | Key | |
|---|---|---|---|
| Code | |||
| U+0003 | 3 | 004 | Ctrl-C |
| U+0004 | 4 | 004 | Ctrl-D |
| U+0005 | 5 | 005 | Ctrl-E |
Ihr könnt dies boolschen DataFrames auch mit bitweisen Operatoren verknüpfen:
[21]:
df[(df["Decimal"] > 2) & (df["Decimal"] < 5)]
[21]:
| Decimal | Octal | Key | |
|---|---|---|---|
| Code | |||
| U+0003 | 3 | 004 | Ctrl-C |
| U+0004 | 4 | 004 | Ctrl-D |
[22]:
df[(df["Decimal"] < 3) | (df["Decimal"] > 4)]
[22]:
| Decimal | Octal | Key | |
|---|---|---|---|
| Code | |||
| U+0000 | 0 | 001 | NUL |
| U+0001 | 1 | 002 | Ctrl-A |
| U+0002 | 2 | 003 | Ctrl-B |
| U+0005 | 5 | 005 | Ctrl-E |
Wie Series verfügt auch DataFrame über spezielle Operatoren loc und iloc für label-basierte bzw. ganzzahlige Indizierung. Da DataFrame zweidimensional ist, könnt ihr eine Teilmenge der Zeilen und Spalten mit NumPy-ähnlicher Notation auswählen, indem ihr entweder Achsenbeschriftungen (loc) oder Ganzzahlen (iloc) verwendet.
[23]:
df.loc["U+0002", ["Decimal", "Key"]]
[23]:
Decimal 2
Key Ctrl-B
Name: U+0002, dtype: object
[24]:
df.iloc[[2], [1, 2]]
[24]:
| Octal | Key | |
|---|---|---|
| Code | ||
| U+0002 | 003 | Ctrl-B |
[25]:
df.iloc[[0, 1], [1, 2]]
[25]:
| Octal | Key | |
|---|---|---|
| Code | ||
| U+0000 | 001 | NUL |
| U+0001 | 002 | Ctrl-A |
Beide Indizierungsfunktionen arbeiten mit Slices zusätzlich zu einzelnen Label oder Listen von Label:
[26]:
df.loc[:"U+0003", "Key"]
[26]:
Code
U+0000 NUL
U+0001 Ctrl-A
U+0002 Ctrl-B
U+0003 Ctrl-C
Name: Key, dtype: object
[27]:
df.iloc[:3, :3]
[27]:
| Decimal | Octal | Key | |
|---|---|---|---|
| Code | |||
| U+0000 | 0 | 001 | NUL |
| U+0001 | 1 | 002 | Ctrl-A |
| U+0002 | 2 | 003 | Ctrl-B |
Es gibt also viele Möglichkeiten, die in einem pandas-Objekt enthaltenen Daten auszuwählen und neu anzuordnen. Im folgenden stelle ich für DataFrames eine kurze Zusammenfassung der meisten dieser Möglichkeiten zusammen:
Typ |
Hinweis |
|---|---|
|
wählt eine einzelne Spalte oder eine Folge von Spalten aus dem DataFrame aus |
|
wählt eine einzelne Zeile oder eine Teilmenge von Zeilen aus dem DataFrame nach Label aus |
|
wählt eine einzelne Spalte oder eine Teilmenge von Spalten nach dem Label aus |
|
wählt sowohl Zeilen als auch Spalten nach dem Label aus |
|
wählt eine einzelne Zeile oder eine Teilmenge von Zeilen aus dem DataFrame anhand der Ganzzahlposition aus |
|
Wählt eine einzelne Spalte oder eine Teilmenge von Spalten anhand einer ganzzahligen Position aus |
|
wählt einen Einzelwert nach Zeilen- und Spaltenbezeichnung aus |
|
wählt einen Einzelwert nach Zeilen- und Spaltenposition (Ganzzahlen) aus |
|
wählt Zeilen oder Spalten nach Labels aus |
|
veraltet seit Version 0.21.0: Verwendet stattdessen |