Dieser Artikel ist eine Teilübersetzung der offiziellen Pandas-Dokumentation Benutzerhandbuch - MultiIndex / Advanced Indexing. Es ist eine Modifikation eines unnatürlichen Satzes. Die neueste Release-Version von Pandas ist zum Zeitpunkt des Schreibens dieses Artikels "0.25.3", aber der Text dieses Artikels basiert unter Berücksichtigung der Zukunft auf dem Dokument der Entwicklungsversion "1.0.0".
Wenn Sie falsche Übersetzungen, alternative Übersetzungen, Fragen usw. haben, verwenden Sie bitte den Kommentarbereich oder bearbeiten Sie die Anfrage.
In diesem Kapitel wird die Indizierung mit Multi-Index (# Hierarchischer Index (Multi-Index)) und anderen erweiterten Indizierungsfunktionen (#Andere erweiterte Indizierungsfunktionen) beschrieben.
Eine grundlegende Dokumentation zur Indizierung finden Sie unter Indizieren und Auswählen von Daten (https://qiita.com/nkay/items/d322ed9d9a14bdbf14cb).
: Warnung: ** Warnung ** Ob der Zuweisungsvorgang eine Kopie oder eine Referenz zurückgibt, hängt vom Kontext ab. Dies wird als verkettete Zuordnung bezeichnet und sollte vermieden werden. Siehe [Zurückgeben einer Ansicht oder Kopie](https://qiita.com/nkay/items/d322ed9d9a14bdbf14cb#Returning einer Ansicht oder Kopie).
Weitere Informationen finden Sie unter Kochbuch.
Hierarchische und mehrstufige Indizes sind sehr nützlich für die erweiterte Datenanalyse und -manipulation, insbesondere beim Umgang mit hochdimensionalen Daten. Im Wesentlichen können Sie eine beliebige Anzahl von Dimensionen in einer niedrigdimensionalen Datenstruktur wie "Serie" (1d) oder "Datenrahmen" (2d) speichern und bearbeiten.
Dieser Abschnitt zeigt, was ein "hierarchischer" Index bedeutet und wie er in alle oben und in den vorherigen Kapiteln beschriebenen Pandas-Indexierungsfunktionen integriert wird. Später Gruppieren (https://pandas.pydata.org/pandas-docs/stable/user_guide/groupby.html#groupby) und Schwenken und Umformen von Daten (https://pandas.pydata.org/) Wenn wir über pandas-docs / stabile / user_guide / reshaping.html # reshaping) sprechen, stellen wir Ihnen eine wichtige Anwendung vor, um zu erklären, wie Sie damit Ihre Daten für die Analyse strukturieren können.
Weitere Informationen finden Sie im Kochbuch.
_ In Version 0.24.0 geändert _: MultiIndex.labels
wurde in MultiIndex.codes
geändert und MultiIndex.set_labels
wurde in MultiIndex.set_codes
geändert.
In Pandas-Objekten wird das Objekt "Index" häufig zum Speichern von Achsenbeschriftungen verwendet, das Objekt "MultiIndex" ist jedoch eine hierarchische Version davon. Sie können sich "MultiIndex" als eine Reihe von Taples vorstellen, bei denen jeder Tapple ein Unikat ist. MultiIndex
verwendet eine Liste von Arrays (mit MultiIndex.from_arrays ()
), ein Array von Taples (mitMultiIndex.from_tuples ()
) und (MultiIndex.from_product ()
. Es kann aus einem direkten Produkt von Iterables, "DataFrame" (unter Verwendung von "MultiIndex.from_frame ()") erstellt werden. Der Konstruktor "Index" versucht, "MultiIndex" zurückzugeben, wenn eine Liste von Taples übergeben wird. Im Folgenden finden Sie verschiedene Möglichkeiten zum Initialisieren von MultiIndex.
In [1]: arrays = [['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'],
...: ['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']]
...:
In [2]: tuples = list(zip(*arrays))
In [3]: tuples
Out[3]:
[('bar', 'one'),
('bar', 'two'),
('baz', 'one'),
('baz', 'two'),
('foo', 'one'),
('foo', 'two'),
('qux', 'one'),
('qux', 'two')]
In [4]: index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])
In [5]: index
Out[5]:
MultiIndex([('bar', 'one'),
('bar', 'two'),
('baz', 'one'),
('baz', 'two'),
('foo', 'one'),
('foo', 'two'),
('qux', 'one'),
('qux', 'two')],
names=['first', 'second'])
In [6]: s = pd.Series(np.random.randn(8), index=index)
In [7]: s
Out[7]:
first second
bar one 0.469112
two -0.282863
baz one -1.509059
two -1.135632
foo one 1.212112
two -0.173215
qux one 0.119209
two -1.044236
dtype: float64
Wenn Sie alle Kombinationen (direkte Produkte) der beiden iterierbaren Elemente möchten, können Sie bequem die Methode "MultiIndex.from_product ()" verwenden.
In [8]: iterables = [['bar', 'baz', 'foo', 'qux'], ['one', 'two']]
In [9]: pd.MultiIndex.from_product(iterables, names=['first', 'second'])
Out[9]:
MultiIndex([('bar', 'one'),
('bar', 'two'),
('baz', 'one'),
('baz', 'two'),
('foo', 'one'),
('foo', 'two'),
('qux', 'one'),
('qux', 'two')],
names=['first', 'second'])
Sie können auch die Methode "MultiIndex.from_frame ()" verwenden, um einen "MultiIndex" direkt aus dem "DataFrame" zu erstellen. Dies ist eine Methode, die "MultiIndex.to_frame ()" ergänzt.
_ Ab Version 0.24.0 _
In [10]: df = pd.DataFrame([['bar', 'one'], ['bar', 'two'],
....: ['foo', 'one'], ['foo', 'two']],
....: columns=['first', 'second'])
....:
In [11]: pd.MultiIndex.from_frame(df)
Out[11]:
MultiIndex([('bar', 'one'),
('bar', 'two'),
('foo', 'one'),
('foo', 'two')],
names=['first', 'second'])
Sie können auch automatisch einen "MultiIndex" erstellen, indem Sie die Liste der Arrays direkt an die "Serie" oder den "DataFrame" übergeben, wie unten gezeigt.
In [12]: arrays = [np.array(['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux']),
....: np.array(['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two'])]
....:
In [13]: s = pd.Series(np.random.randn(8), index=arrays)
In [14]: s
Out[14]:
bar one -0.861849
two -2.104569
baz one -0.494929
two 1.071804
foo one 0.721555
two -0.706771
qux one -1.039575
two 0.271860
dtype: float64
In [15]: df = pd.DataFrame(np.random.randn(8, 4), index=arrays)
In [16]: df
Out[16]:
0 1 2 3
bar one -0.424972 0.567020 0.276232 -1.087401
two -0.673690 0.113648 -1.478427 0.524988
baz one 0.404705 0.577046 -1.715002 -1.039268
two -0.370647 -1.157892 -1.344312 0.844885
foo one 1.075770 -0.109050 1.643563 -1.469388
two 0.357021 -0.674600 -1.776904 -0.968914
qux one -1.294524 0.413738 0.276662 -0.472035
two -0.013960 -0.362543 -0.006154 -0.923061
Alle "MultiIndex" -Konstruktoren verwenden ein "names" -Argument, in dem die eigenen Zeichenfolgennamen (Labels) der Ebene gespeichert sind. Wenn kein Name angegeben ist, wird "Keine" zugewiesen.
In [17]: df.index.names
Out[17]: FrozenList([None, None])
Dieser Index kann auf der Achse in einer beliebigen Richtung des Pandas-Objekts festgelegt werden, und die Anzahl der ** Ebenen ** des Index ist beliebig.
In [18]: df = pd.DataFrame(np.random.randn(3, 8), index=['A', 'B', 'C'], columns=index)
In [19]: df
Out[19]:
first bar baz foo qux
second one two one two one two one two
A 0.895717 0.805244 -1.206412 2.565646 1.431256 1.340309 -1.170299 -0.226169
B 0.410835 0.813850 0.132003 -0.827317 -0.076467 -1.187678 1.130127 -1.436737
C -1.413681 1.607920 1.024180 0.569605 0.875906 -2.211372 0.974466 -2.006747
In [20]: pd.DataFrame(np.random.randn(6, 6), index=index[:6], columns=index[:6])
Out[20]:
first bar baz foo
second one two one two one two
first second
bar one -0.410001 -0.078638 0.545952 -1.219217 -1.226825 0.769804
two -1.281247 -0.727707 -0.121306 -0.097883 0.695775 0.341734
baz one 0.959726 -1.110336 -0.619976 0.149748 -0.732339 0.687738
two 0.176444 0.403310 -0.154951 0.301624 -2.179861 -1.369849
foo one -0.954208 1.462696 -1.743161 -0.826591 -0.345352 1.314232
two 0.690579 0.995761 2.396780 0.014871 3.357427 -0.317441
Wir haben einen übergeordneten Index "gespart", damit die Konsolenausgabe besser sichtbar ist. Sie können steuern, wie der Index angezeigt wird, indem Sie die Option multi_sparse
von pandas.set_options ()
verwenden.
In [21]: with pd.option_context('display.multi_sparse', False):
....: df
....:
Es sei daran erinnert, dass es in Ordnung ist, Taples als einzelnes, unteilbares Etikett zu verwenden.
In [22]: pd.Series(np.random.randn(8), index=tuples)
Out[22]:
(bar, one) -1.236269
(bar, two) 0.896171
(baz, one) -0.487602
(baz, two) -0.082240
(foo, one) -2.182937
(foo, two) 0.380396
(qux, one) 0.084844
(qux, two) 0.432390
dtype: float64
Der Grund, warum "MultiIndex" wichtig ist, besteht darin, dass Sie "MultiIndex" verwenden können, um Gruppierungs-, Auswahl- und Formänderungsvorgänge durchzuführen, wie im folgenden und den folgenden Kapiteln erläutert. Wie Sie in einem späteren Abschnitt sehen werden, können Sie mit hierarchisch indizierten Daten arbeiten, ohne den "MultiIndex" explizit selbst erstellen zu müssen. Wenn Sie jedoch Daten aus einer Datei laden, können Sie bei der Vorbereitung des Datasets Ihren eigenen "MultiIndex" generieren.
get_level_values ()
Die Methode gilt für jede bestimmte Ebene Gibt einen Vektor von Positionsbezeichnungen zurück.
In [23]: index.get_level_values(0)
Out[23]: Index(['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'], dtype='object', name='first')
In [24]: index.get_level_values('second')
Out[24]: Index(['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two'], dtype='object', name='second')
Eines der Hauptmerkmale eines hierarchischen Index ist die Möglichkeit, Daten anhand einer "partiellen" Bezeichnung auszuwählen, die eine Untergruppe in den Daten identifiziert. ** Teil ** Auswahl "löscht" die Ebene des resultierenden hierarchischen Index genauso wie die Auswahl einer Spalte in einem regulären DataFrame.
In [25]: df['bar']
Out[25]:
second one two
A 0.895717 0.805244
B 0.410835 0.813850
C -1.413681 1.607920
In [26]: df['bar', 'one']
Out[26]:
A 0.895717
B 0.410835
C -1.413681
Name: (bar, one), dtype: float64
In [27]: df['bar']['one']
Out[27]:
A 0.895717
B 0.410835
C -1.413681
Name: one, dtype: float64
In [28]: s['qux']
Out[28]:
one -1.039575
two 0.271860
dtype: float64
Informationen zur Auswahl auf einer tieferen Ebene finden Sie unter Abschnitte mit hierarchischen Indizes.
MultiIndex
wurde definiert, auch wenn es nicht tatsächlich verwendet wurde Enthält Indizes für alle Ebenen. Möglicherweise bemerken Sie dies, wenn Sie den Index aufteilen. Zum Beispiel
In [29]: df.columns.levels # original MultiIndex
Out[29]: FrozenList([['bar', 'baz', 'foo', 'qux'], ['one', 'two']])
In [30]: df[['foo','qux']].columns.levels # sliced
Out[30]: FrozenList([['bar', 'baz', 'foo', 'qux'], ['one', 'two']])
Dies geschieht, um Neuberechnungen des Pegels zu vermeiden und die Slice-Leistung zu verbessern. Wenn Sie nur die verwendeten Ebenen anzeigen möchten, get_level_values ()
. Sie können die Methode MultiIndex.get_level_values verwenden.
In [31]: df[['foo', 'qux']].columns.to_numpy()
Out[31]:
array([('foo', 'one'), ('foo', 'two'), ('qux', 'one'), ('qux', 'two')],
dtype=object)
# for a specific level
In [32]: df[['foo', 'qux']].columns.get_level_values(0)
Out[32]: Index(['foo', 'foo', 'qux', 'qux'], dtype='object', name='first')
Um "MultiIndex" nur mit Nutzungsstufen neu zu erstellen, [remove_unused_levels ()
](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.MultiIndex.remove_unused_levels.html# Sie können die Methode pandas.MultiIndex.remove_unused_levels verwenden.
In [33]: new_mi = df[['foo', 'qux']].columns.remove_unused_levels()
In [34]: new_mi.levels
Out[34]: FrozenList([['foo', 'qux'], ['one', 'two']])
Operationen zwischen Objekten mit unterschiedlichen Indizes, die "MultiIndex" auf der Achse haben, funktionieren wie erwartet. Die Datenausrichtung funktioniert wie ein Tapple-Index.
In [35]: s + s[:-2]
Out[35]:
bar one -1.723698
two -4.209138
baz one -0.989859
two 2.143608
foo one 1.443110
two -1.413542
qux one NaN
two NaN
dtype: float64
In [36]: s + s[::2]
Out[36]:
bar one -1.723698
two NaN
baz one -0.989859
two NaN
foo one 1.443110
two NaN
qux one -2.079150
two NaN
dtype: float64
[Reindex ()
] von Series
/ DataFrames
(https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.reindex.html#pandas.DataFrame.reindex) Die Methode kann auch einen anderen "MultiIndex" oder eine Liste oder ein Array von Tupeln empfangen.
In [37]: s.reindex(index[:3])
Out[37]:
first second
bar one -0.861849
two -2.104569
baz one -0.494929
dtype: float64
In [38]: s.reindex([('foo', 'two'), ('bar', 'one'), ('qux', 'one'), ('baz', 'one')])
Out[38]:
foo two -0.706771
bar one -0.861849
qux one -1.039575
baz one -0.494929
dtype: float64
Es ist syntaktisch etwas schwierig, MultiIndex
dazu zu bringen, eine erweiterte Indizierung mit .loc
durchzuführen, aber wir haben alle Anstrengungen unternommen, um dies zu erreichen. Im Allgemeinen haben MultiIndex-Schlüssel die Form von Taples. Das Folgende funktioniert beispielsweise wie erwartet.
In [39]: df = df.T
In [40]: df
Out[40]:
A B C
first second
bar one 0.895717 0.410835 -1.413681
two 0.805244 0.813850 1.607920
baz one -1.206412 0.132003 1.024180
two 2.565646 -0.827317 0.569605
foo one 1.431256 -0.076467 0.875906
two 1.340309 -1.187678 -2.211372
qux one -1.170299 1.130127 0.974466
two -0.226169 -1.436737 -2.006747
In [41]: df.loc[('bar', 'two')]
Out[41]:
A 0.805244
B 0.813850
C 1.607920
Name: (bar, two), dtype: float64
In diesem Beispiel funktioniert auch "df.loc [" bar "," two "]". Beachten Sie jedoch, dass diese Abkürzung im Allgemeinen irreführend sein kann.
Wenn Sie ".loc" verwenden, um aus einer bestimmten Spalte zu indizieren, müssen Sie den Taple wie folgt verwenden:
In [42]: df.loc[('bar', 'two'), 'A']
Out[42]: 0.8052440253863785
Wenn Sie nur das erste Element des Taple übergeben, müssen Sie nicht alle Ebenen von MultiIndex angeben. Beispielsweise können Sie den "Teil" -Index verwenden, um alle Elemente mit "Balken" in der ersten Ebene wie folgt abzurufen:
df.loc['bar']
Dies ist eine Abkürzung für die redundantere Notation "df.loc [('bar',),]" (in diesem Beispiel auch äquivalent zu "df.loc ['bar',]").
Das "Partial" Slice funktioniert auch sehr gut.
In [43]: df.loc['baz':'foo']
Out[43]:
A B C
first second
baz one -1.206412 0.132003 1.024180
two 2.565646 -0.827317 0.569605
foo one 1.431256 -0.076467 0.875906
two 1.340309 -1.187678 -2.211372
Sie können nach dem "Wertebereich" schneiden, indem Sie ein Stück des Taples übergeben.
In [44]: df.loc[('baz', 'two'):('qux', 'one')]
Out[44]:
A B C
first second
baz two 2.565646 -0.827317 0.569605
foo one 1.431256 -0.076467 0.875906
two 1.340309 -1.187678 -2.211372
qux one -1.170299 1.130127 0.974466
In [45]: df.loc[('baz', 'two'):'foo']
Out[45]:
A B C
first second
baz two 2.565646 -0.827317 0.569605
foo one 1.431256 -0.076467 0.875906
two 1.340309 -1.187678 -2.211372
Wie bei der Neuindizierung können Sie auch eine Liste mit Beschriftungen oder Tupeln übergeben.
In [46]: df.loc[[('bar', 'two'), ('qux', 'one')]]
Out[46]:
A B C
first second
bar two 0.805244 0.813850 1.607920
qux one -1.170299 1.130127 0.974466
: ballot_box_with_check: ** Hinweis ** Beachten Sie, dass Tapples und Listen in Pandas bei der Indizierung nicht gleich behandelt werden. Ein Taple wird als ein einzelner mehrstufiger Schlüssel interpretiert, eine Liste zeigt jedoch auf mehrere Schlüssel. Mit anderen Worten, der Taple bewegt sich horizontal (überquert die Ebene) und die Liste bewegt sich vertikal (scannt die Ebene).
Wichtig ist, dass eine Liste von Taples mehrere vollständige "MultiIndex" -Schlüssel abruft, aber ein Tapple einer Liste verweist auf einen Wert in einer Ebene.
In [47]: s = pd.Series([1, 2, 3, 4, 5, 6],
....: index=pd.MultiIndex.from_product([["A", "B"], ["c", "d", "e"]]))
....:
In [48]: s.loc[[("A", "c"), ("B", "d")]] # list of tuples
Out[48]:
A c 1
B d 5
dtype: int64
In [49]: s.loc[(["A", "B"], ["c", "d"])] # tuple of lists
Out[49]:
A c 1
d 2
B c 4
d 5
dtype: int64
Sie können "MultiIndex" in Scheiben schneiden, indem Sie mehrere Indexer bereitstellen.
Verwenden Sie auf die gleiche Weise eine der Auswahlmöglichkeiten für Slice-Label-Liste, Label und Boolesches Array, die Sie unter Select by Label (https://qiita.com/nkay/items/d322ed9d9a14bdbf14cb#Select by Label) sehen. Ich kann.
Sie können "Slice (None)" verwenden, um alle Elemente auf * dieser * Ebene auszuwählen. Es ist nicht erforderlich, alle * tieferen * Ebenen anzugeben. Sie werden durch "Slice (None)" impliziert.
Wie bei den anderen handelt es sich hierbei um eine Etikettenindizierung, die ** beide Seiten ** des Slicers umfasst.
: Warnung: ** Warnung ** Geben Sie in
.loc
alle Achsen an (** Index ** und ** Spalten **). Es gibt einige mehrdeutige Fälle, in denen der übergebene Indexer als Index auf * beiden * Achsen und nicht als "MultiIndex" einer Zeile falsch interpretiert werden kann. Schreiben Sie wie folgt.df.loc[(slice('A1', 'A3'), ...), :] # noqa: E999
Schreiben Sie nicht wie folgt.
df.loc[(slice('A1', 'A3'), ...)] # noqa: E999
In [50]: def mklbl(prefix, n):
....: return ["%s%s" % (prefix, i) for i in range(n)]
....:
In [51]: miindex = pd.MultiIndex.from_product([mklbl('A', 4),
....: mklbl('B', 2),
....: mklbl('C', 4),
....: mklbl('D', 2)])
....:
In [52]: micolumns = pd.MultiIndex.from_tuples([('a', 'foo'), ('a', 'bar'),
....: ('b', 'foo'), ('b', 'bah')],
....: names=['lvl0', 'lvl1'])
....:
In [53]: dfmi = pd.DataFrame(np.arange(len(miindex) * len(micolumns))
....: .reshape((len(miindex), len(micolumns))),
....: index=miindex,
....: columns=micolumns).sort_index().sort_index(axis=1)
....:
In [54]: dfmi
Out[54]:
lvl0 a b
lvl1 bar foo bah foo
A0 B0 C0 D0 1 0 3 2
D1 5 4 7 6
C1 D0 9 8 11 10
D1 13 12 15 14
C2 D0 17 16 19 18
... ... ... ... ...
A3 B1 C1 D1 237 236 239 238
C2 D0 241 240 243 242
D1 245 244 247 246
C3 D0 249 248 251 250
D1 253 252 255 254
[64 rows x 4 columns]
Grundlegendes Multi-Index-Slicing mit Slice-Listenbeschriftungen.
In [55]: dfmi.loc[(slice('A1', 'A3'), slice(None), ['C1', 'C3']), :]
Out[55]:
lvl0 a b
lvl1 bar foo bah foo
A1 B0 C1 D0 73 72 75 74
D1 77 76 79 78
C3 D0 89 88 91 90
D1 93 92 95 94
B1 C1 D0 105 104 107 106
... ... ... ... ...
A3 B0 C3 D1 221 220 223 222
B1 C1 D0 233 232 235 234
D1 237 236 239 238
C3 D0 249 248 251 250
D1 253 252 255 254
[24 rows x 4 columns]
In slice (None)
using Pandas.IndexSlice
Sie können eine natürlichere Syntax verwenden als :
.
In [56]: idx = pd.IndexSlice
In [57]: dfmi.loc[idx[:, :, ['C1', 'C3']], idx[:, 'foo']]
Out[57]:
lvl0 a b
lvl1 foo foo
A0 B0 C1 D0 8 10
D1 12 14
C3 D0 24 26
D1 28 30
B1 C1 D0 40 42
... ... ...
A3 B0 C3 D1 220 222
B1 C1 D0 232 234
D1 236 238
C3 D0 248 250
D1 252 254
[32 rows x 2 columns]
Mit dieser Methode können Sie sehr komplexe Auswahlen auf mehreren Achsen gleichzeitig treffen.
In [58]: dfmi.loc['A1', (slice(None), 'foo')]
Out[58]:
lvl0 a b
lvl1 foo foo
B0 C0 D0 64 66
D1 68 70
C1 D0 72 74
D1 76 78
C2 D0 80 82
... ... ...
B1 C1 D1 108 110
C2 D0 112 114
D1 116 118
C3 D0 120 122
D1 124 126
[16 rows x 2 columns]
In [59]: dfmi.loc[idx[:, :, ['C1', 'C3']], idx[:, 'foo']]
Out[59]:
lvl0 a b
lvl1 foo foo
A0 B0 C1 D0 8 10
D1 12 14
C3 D0 24 26
D1 28 30
B1 C1 D0 40 42
... ... ...
A3 B0 C3 D1 220 222
B1 C1 D0 232 234
D1 236 238
C3 D0 248 250
D1 252 254
[32 rows x 2 columns]
Sie können den Booleschen Indexer verwenden, um eine Auswahl für * Werte * bereitzustellen.
In [60]: mask = dfmi[('a', 'foo')] > 200
In [61]: dfmi.loc[idx[mask, :, ['C1', 'C3']], idx[:, 'foo']]
Out[61]:
lvl0 a b
lvl1 foo foo
A3 B0 C1 D1 204 206
C3 D0 216 218
D1 220 222
B1 C1 D0 232 234
D1 236 238
C3 D0 248 250
D1 252 254
Sie können auch das Argument "Achse" in ".loc" angeben, um einen Slicer zu interpretieren, der auf einer einzelnen Achse übergeben wird.
In [62]: dfmi.loc(axis=0)[:, :, ['C1', 'C3']]
Out[62]:
lvl0 a b
lvl1 bar foo bah foo
A0 B0 C1 D0 9 8 11 10
D1 13 12 15 14
C3 D0 25 24 27 26
D1 29 28 31 30
B1 C1 D0 41 40 43 42
... ... ... ... ...
A3 B0 C3 D1 221 220 223 222
B1 C1 D0 233 232 235 234
D1 237 236 239 238
C3 D0 249 248 251 250
D1 253 252 255 254
[32 rows x 4 columns]
Darüber hinaus können Sie Werte mit den folgenden Methoden * zuweisen *:
In [63]: df2 = dfmi.copy()
In [64]: df2.loc(axis=0)[:, :, ['C1', 'C3']] = -10
In [65]: df2
Out[65]:
lvl0 a b
lvl1 bar foo bah foo
A0 B0 C0 D0 1 0 3 2
D1 5 4 7 6
C1 D0 -10 -10 -10 -10
D1 -10 -10 -10 -10
C2 D0 17 16 19 18
... ... ... ... ...
A3 B1 C1 D1 -10 -10 -10 -10
C2 D0 241 240 243 242
D1 245 244 247 246
C3 D0 -10 -10 -10 -10
D1 -10 -10 -10 -10
[64 rows x 4 columns]
Sie können auch die rechte Seite des ausrichtbaren Objekts verwenden.
In [66]: df2 = dfmi.copy()
In [67]: df2.loc[idx[:, :, ['C1', 'C3']], :] = df2 * 1000
In [68]: df2
Out[68]:
lvl0 a b
lvl1 bar foo bah foo
A0 B0 C0 D0 1 0 3 2
D1 5 4 7 6
C1 D0 9000 8000 11000 10000
D1 13000 12000 15000 14000
C2 D0 17 16 19 18
... ... ... ... ...
A3 B1 C1 D1 237000 236000 239000 238000
C2 D0 241 240 243 242
D1 245 244 247 246
C3 D0 249000 248000 251000 250000
D1 253000 252000 255000 254000
[64 rows x 4 columns]
Cross-section
Die xs ()
-Methode von DataFrame
verwendet auch ein Ebenenargument, um die Auswahl von Daten auf einer bestimmten Ebene von MultiIndex
zu vereinfachen.
In [69]: df
Out[69]:
A B C
first second
bar one 0.895717 0.410835 -1.413681
two 0.805244 0.813850 1.607920
baz one -1.206412 0.132003 1.024180
two 2.565646 -0.827317 0.569605
foo one 1.431256 -0.076467 0.875906
two 1.340309 -1.187678 -2.211372
qux one -1.170299 1.130127 0.974466
two -0.226169 -1.436737 -2.006747
In [70]: df.xs('one', level='second')
Out[70]:
A B C
first
bar 0.895717 0.410835 -1.413681
baz -1.206412 0.132003 1.024180
foo 1.431256 -0.076467 0.875906
qux -1.170299 1.130127 0.974466
#Verwendung von Scheiben
In [71]: df.loc[(slice(None), 'one'), :]
Out[71]:
A B C
first second
bar one 0.895717 0.410835 -1.413681
baz one -1.206412 0.132003 1.024180
foo one 1.431256 -0.076467 0.875906
qux one -1.170299 1.130127 0.974466
Sie können auch Spalten mit "xs" auswählen, indem Sie das Axis-Argument angeben.
In [72]: df = df.T
In [73]: df.xs('one', level='second', axis=1)
Out[73]:
first bar baz foo qux
A 0.895717 -1.206412 1.431256 -1.170299
B 0.410835 0.132003 -0.076467 1.130127
C -1.413681 1.024180 0.875906 0.974466
#Verwendung von Scheiben
In [74]: df.loc[:, (slice(None), 'one')]
Out[74]:
first bar baz foo qux
second one one one one
A 0.895717 -1.206412 1.431256 -1.170299
B 0.410835 0.132003 -0.076467 1.130127
C -1.413681 1.024180 0.875906 0.974466
Mit xs
können Sie auch mit mehreren Tasten auswählen.
In [75]: df.xs(('one', 'bar'), level=('second', 'first'), axis=1)
Out[75]:
first bar
second one
A 0.895717
B 0.410835
C -1.413681
#Verwendung von Scheiben
In [76]: df.loc[:, ('bar', 'one')]
Out[76]:
A 0.895717
B 0.410835
C -1.413681
Name: (bar, one), dtype: float64
Sie können das ausgewählte Level beibehalten, indem Sie drop_level = False
an xs
übergeben.
In [77]: df.xs('one', level='second', axis=1, drop_level=False)
Out[77]:
first bar baz foo qux
second one one one one
A 0.895717 -1.206412 1.431256 -1.170299
B 0.410835 0.132003 -0.076467 1.130127
C -1.413681 1.024180 0.875906 0.974466
Vergleichen Sie das obige Ergebnis mit dem Fall von drop_level = True
(Standardwert).
In [78]: df.xs('one', level='second', axis=1, drop_level=True)
Out[78]:
first bar baz foo qux
A 0.895717 -1.206412 1.431256 -1.170299
B 0.410835 0.132003 -0.076467 1.130127
C -1.413681 1.024180 0.875906 0.974466
Das Pandas-Objekt reindex ()
und align (
align (align) )
Wenn Sie das Argument level
in der Methode verwenden, die gesamte Ebene Nützlich für die Übertragung von Werten an. Zum Beispiel:
In [79]: midx = pd.MultiIndex(levels=[['zero', 'one'], ['x', 'y']],
....: codes=[[1, 1, 0, 0], [1, 0, 1, 0]])
....:
In [80]: df = pd.DataFrame(np.random.randn(4, 2), index=midx)
In [81]: df
Out[81]:
0 1
one y 1.519970 -0.493662
x 0.600178 0.274230
zero y 0.132885 -0.023688
x 2.410179 1.450520
In [82]: df2 = df.mean(level=0)
In [83]: df2
Out[83]:
0 1
one 1.060074 -0.109716
zero 1.271532 0.713416
In [84]: df2.reindex(df.index, level=0)
Out[84]:
0 1
one y 1.060074 -0.109716
x 1.060074 -0.109716
zero y 1.271532 0.713416
x 1.271532 0.713416
#Ausrichtung
In [85]: df_aligned, df2_aligned = df.align(df2, level=0)
In [86]: df_aligned
Out[86]:
0 1
one y 1.519970 -0.493662
x 0.600178 0.274230
zero y 0.132885 -0.023688
x 2.410179 1.450520
In [87]: df2_aligned
Out[87]:
0 1
one y 1.060074 -0.109716
x 1.060074 -0.109716
zero y 1.271532 0.713416
x 1.271532 0.713416
Swaplevel
Die Methode swaplevel ()
besteht aus zwei Ebenen Kann ersetzt werden.
In [88]: df[:5]
Out[88]:
0 1
one y 1.519970 -0.493662
x 0.600178 0.274230
zero y 0.132885 -0.023688
x 2.410179 1.450520
In [89]: df[:5].swaplevel(0, 1, axis=0)
Out[89]:
0 1
y one 1.519970 -0.493662
x one 0.600178 0.274230
y zero 0.132885 -0.023688
x zero 2.410179 1.450520
Die Methode reorder_levels ()
ist die Methode swaplevel
. Verallgemeinern Sie, damit Sie die Ebene eines hierarchischen Index in einem Schritt ersetzen können.
In [90]: df[:5].reorder_levels([1, 0], axis=0)
Out[90]:
0 1
y one 1.519970 -0.493662
x one 0.600178 0.274230
y zero 0.132885 -0.023688
x zero 2.410179 1.450520
[Rename ()
](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rename.html] wird normalerweise zum Umbenennen von Spalten in DataFrame
verwendet Die Methode # pandas.DataFrame.rename) kann auch das Label "MultiIndex" umbenennen. Das Argument "Spalten" von "Umbenennen" kann ein Wörterbuch sein, das nur die Spalten enthält, die Sie umbenennen möchten.
In [91]: df.rename(columns={0: "col0", 1: "col1"})
Out[91]:
col0 col1
one y 1.519970 -0.493662
x 0.600178 0.274230
zero y 0.132885 -0.023688
x 2.410179 1.450520
Diese Methode kann auch verwendet werden, um eine bestimmte Bezeichnung im Hauptindex des "DataFrame" umzubenennen.
In [92]: df.rename(index={"one": "two", "y": "z"})
Out[92]:
0 1
two z 1.519970 -0.493662
x 0.600178 0.274230
zero z 0.132885 -0.023688
x 2.410179 1.450520
rename_axis ()
Die Methode lautet Index
oder MultiIndex umbenennen
. Insbesondere können Sie einen Ebenennamen für den "MultiIndex" angeben, was später nützlich ist, wenn Sie "reset_index ()" verwenden, um den Wert vom MultiIndex in eine reguläre Spalte zu verschieben.
In [93]: df.rename_axis(index=['abc', 'def'])
Out[93]:
0 1
abc def
one y 1.519970 -0.493662
x 0.600178 0.274230
zero y 0.132885 -0.023688
x 2.410179 1.450520
Beachten Sie, dass die Spalten im "DataFrame" Indizes sind. Wenn Sie also "rename_axis" mit dem Argument "column" verwenden, wird der Index umbenannt.
In [94]: df.rename_axis(columns="Cols").columns
Out[94]: RangeIndex(start=0, stop=2, step=1, name='Cols')
Rename
und rename_axis
unterstützen die Angabe eines Wörterbuchs, einer Serie
und einer Zuordnungsfunktion, um eine Bezeichnung / einen Namen einem neuen Wert zuzuordnen.
Wenn Sie direkt mit dem Index
-Objekt anstatt über den DataFrame
arbeiten möchten, dann Index.set_names ()
Sie können es mit .set_names.html # pandas.Index.set_names umbenennen.
In [95]: mi = pd.MultiIndex.from_product([[1, 2], ['a', 'b']], names=['x', 'y'])
In [96]: mi.names
Out[96]: FrozenList(['x', 'y'])
In [97]: mi2 = mi.rename("new name", level=0)
In [98]: mi2
Out[98]:
MultiIndex([(1, 'a'),
(1, 'b'),
(2, 'a'),
(2, 'b')],
names=['new name', 'y'])
: Warnung: ** Warnung ** Vor Pandas 1.0.0 war es auch möglich, den Namen des MultiIndex durch Aktualisieren des Namens der Ebene festzulegen.
>>> mi.levels[0].name = 'name via level' >>> mi.names[0] # only works for older panads 'name via level'
Ab pandas 1.0 kann der Name von MultiIndex implizit nicht aktualisiert werden. Verwenden Sie stattdessen
Index.set_names ()
Bitte gib mir.
Objekte mit "MultiIndex" müssen sortiert werden, um effektiv indiziert und in Scheiben geschnitten zu werden. Wie jeder andere Index sort_index ()
Du kannst es benutzen.
In [99]: import random
In [100]: random.shuffle(tuples)
In [101]: s = pd.Series(np.random.randn(8), index=pd.MultiIndex.from_tuples(tuples))
In [102]: s
Out[102]:
qux one 0.206053
foo two -0.251905
bar two -2.213588
one 1.063327
qux two 1.266143
baz one 0.299368
foo one -0.863838
baz two 0.408204
dtype: float64
In [103]: s.sort_index()
Out[103]:
bar one 1.063327
two -2.213588
baz one 0.299368
two 0.408204
foo one -0.863838
two -0.251905
qux one 0.206053
two 1.266143
dtype: float64
In [104]: s.sort_index(level=0)
Out[104]:
bar one 1.063327
two -2.213588
baz one 0.299368
two 0.408204
foo one -0.863838
two -0.251905
qux one 0.206053
two 1.266143
dtype: float64
In [105]: s.sort_index(level=1)
Out[105]:
bar one 1.063327
baz one 0.299368
foo one -0.863838
qux one 0.206053
bar two -2.213588
baz two 0.408204
foo two -0.251905
qux two 1.266143
dtype: float64
Wenn die Ebene von "MultiIndex" benannt ist, können Sie den Ebenennamen auch an "sort_index" übergeben.
In [106]: s.index.set_names(['L1', 'L2'], inplace=True)
In [107]: s.sort_index(level='L1')
Out[107]:
L1 L2
bar one 1.063327
two -2.213588
baz one 0.299368
two 0.408204
foo one -0.863838
two -0.251905
qux one 0.206053
two 1.266143
dtype: float64
In [108]: s.sort_index(level='L2')
Out[108]:
L1 L2
bar one 1.063327
baz one 0.299368
foo one -0.863838
qux one 0.206053
bar two -2.213588
baz two 0.408204
foo two -0.251905
qux two 1.266143
dtype: float64
Wenn Sie für höherdimensionale Objekte über "MultiIndex" verfügen, können Sie auf anderen Achsen als dem Index nach Ebenen sortieren.
In [109]: df.T.sort_index(level=1, axis=1)
Out[109]:
one zero one zero
x x y y
0 0.600178 2.410179 1.519970 0.132885
1 0.274230 1.450520 -0.493662 -0.023688
Die Indizierung funktioniert auch dann, wenn die Daten nicht sortiert sind, aber ziemlich ineffizient (und Sie sehen eine "PerformanceWarning"). Es wird auch eine Kopie der Daten anstelle der Ansicht zurückgegeben.
In [110]: dfm = pd.DataFrame({'jim': [0, 0, 1, 1],
.....: 'joe': ['x', 'x', 'z', 'y'],
.....: 'jolie': np.random.rand(4)})
.....:
In [111]: dfm = dfm.set_index(['jim', 'joe'])
In [112]: dfm
Out[112]:
jolie
jim joe
0 x 0.490671
x 0.120248
1 z 0.537020
y 0.110968
In [4]: dfm.loc[(1, 'z')]
PerformanceWarning: indexing past lexsort depth may impact performance.
Out[4]:
jolie
jim joe
1 z 0.64094
Darüber hinaus kann die Indizierung, wenn sie nicht vollständig sortiert ist, zu folgendem Fehler führen:
In [5]: dfm.loc[(0, 'y'):(1, 'z')]
UnsortedIndexError: 'Key length (2) was greater than MultiIndex lexsort depth (1)'
Die Methode is_lexsorted ()
von MultiIndex
ist ein Index. Gibt an, ob sortiert ist, und die Eigenschaft lexsort_depth
gibt die Sortiertiefe zurück.
In [113]: dfm.index.is_lexsorted()
Out[113]: False
In [114]: dfm.index.lexsort_depth
Out[114]: 1
In [115]: dfm = dfm.sort_index()
In [116]: dfm
Out[116]:
jolie
jim joe
0 x 0.490671
x 0.120248
1 y 0.110968
z 0.537020
In [117]: dfm.index.is_lexsorted()
Out[117]: True
In [118]: dfm.index.lexsort_depth
Out[118]: 2
Die Auswahl funktioniert jetzt wie erwartet.
In [119]: dfm.loc[(0, 'y'):(1, 'z')]
Out[119]:
jolie
jim joe
1 y 0.110968
z 0.537020
Wie NumPy ndarrays bietet auch pandas Index
· Series
· DataFrame
eine take ()
Methode, um Elemente entlang einer bestimmten Achse an einem bestimmten Index abzurufen. Der angegebene Index muss ein ndarray an der Position der Liste oder des ganzzahligen Index sein. take
kann auch negative ganze Zahlen relativ zum Ende des Objekts akzeptieren.
In [120]: index = pd.Index(np.random.randint(0, 1000, 10))
In [121]: index
Out[121]: Int64Index([214, 502, 712, 567, 786, 175, 993, 133, 758, 329], dtype='int64')
In [122]: positions = [0, 9, 3]
In [123]: index[positions]
Out[123]: Int64Index([214, 329, 567], dtype='int64')
In [124]: index.take(positions)
Out[124]: Int64Index([214, 329, 567], dtype='int64')
In [125]: ser = pd.Series(np.random.randn(10))
In [126]: ser.iloc[positions]
Out[126]:
0 -0.179666
9 1.824375
3 0.392149
dtype: float64
In [127]: ser.take(positions)
Out[127]:
0 -0.179666
9 1.824375
3 0.392149
dtype: float64
Für DataFrame muss der angegebene Index eine eindimensionale Liste oder ein ndarray sein, das die Zeilen- oder Spaltenposition angibt.
In [128]: frm = pd.DataFrame(np.random.randn(5, 3))
In [129]: frm.take([1, 4, 3])
Out[129]:
0 1 2
1 -1.237881 0.106854 -1.276829
4 0.629675 -1.425966 1.857704
3 0.979542 -1.633678 0.615855
In [130]: frm.take([0, 2], axis=1)
Out[130]:
0 2
0 0.595974 0.601544
1 -1.237881 -1.276829
2 -0.767101 1.499591
3 0.979542 0.615855
4 0.629675 1.857704
Beachten Sie, dass die Methode "take" des Pandas-Objekts nicht für die Verwendung mit Booleschen Indizes vorgesehen ist und unerwartete Ergebnisse zurückgeben kann.
In [131]: arr = np.random.randn(10)
In [132]: arr.take([False, False, True, True])
Out[132]: array([-1.1935, -1.1935, 0.6775, 0.6775])
In [133]: arr[[0, 1]]
Out[133]: array([-1.1935, 0.6775])
In [134]: ser = pd.Series(np.random.randn(10))
In [135]: ser.take([False, False, True, True])
Out[135]:
0 0.233141
0 0.233141
1 -0.223540
1 -0.223540
dtype: float64
In [136]: ser.iloc[[0, 1]]
Out[136]:
0 0.233141
1 -0.223540
dtype: float64
Ein kleiner Hinweis zur Leistung: Die Methode "take" behandelt einen engeren Bereich von Eingaben, wodurch eine viel schnellere Leistung erzielt werden kann als bei einem ausgefallenen Index. ]]
In [137]: arr = np.random.randn(10000, 5)
In [138]: indexer = np.arange(10000)
In [139]: random.shuffle(indexer)
In [140]: %timeit arr[indexer]
.....: %timeit arr.take(indexer, axis=0)
.....:
219 us +- 1.23 us per loop (mean +- std. dev. of 7 runs, 1000 loops each)
72.3 us +- 727 ns per loop (mean +- std. dev. of 7 runs, 10000 loops each)
In [141]: ser = pd.Series(arr[:, 0])
In [142]: %timeit ser.iloc[indexer]
.....: %timeit ser.take(indexer)
.....:
179 us +- 1.54 us per loop (mean +- std. dev. of 7 runs, 10000 loops each)
162 us +- 1.6 us per loop (mean +- std. dev. of 7 runs, 10000 loops each)
Bisher haben wir den "MultiIndex" ziemlich ausführlich behandelt. Die Dokumentation für "DatetimeIndex" und "PeriodIndex" ist hier, die Dokumentation für "TimedeltaIndex" ist [hier](https: / Siehe /dev.pandas.io/docs/user_guide/timedeltas.html#timedeltas-index).
In den folgenden Unterabschnitten werden einige andere Indextypen hervorgehoben.
CategoricalIndex
CategoricalIndex
ist ein Index, der die Unterstützung doppelter Indizes unterstützt. .. Dies ist ein Container, der Categorical
mit vielen doppelten Elementen umgibt. Ermöglicht die effiziente Indizierung und Speicherung von Indizes, die enthalten.
In [143]: from pandas.api.types import CategoricalDtype
In [144]: df = pd.DataFrame({'A': np.arange(6),
.....: 'B': list('aabbca')})
.....:
In [145]: df['B'] = df['B'].astype(CategoricalDtype(list('cab')))
In [146]: df
Out[146]:
A B
0 0 a
1 1 a
2 2 b
3 3 b
4 4 c
5 5 a
In [147]: df.dtypes
Out[147]:
A int64
B category
dtype: object
In [148]: df['B'].cat.categories
Out[148]: Index(['c', 'a', 'b'], dtype='object')
Durch das Setzen des Index wird ein CategoricalIndex
erstellt.
In [149]: df2 = df.set_index('B')
In [150]: df2.index
Out[150]: CategoricalIndex(['a', 'a', 'b', 'b', 'c', 'a'], categories=['c', 'a', 'b'], ordered=False, name='B', dtype='category')
Indizes, die __getitem__ /. Iloc / .loc
verwenden, funktionieren genauso wie Index
. Der Indexer muss ** zu einer Kategorie gehören **. Andernfalls erhalten Sie einen "KeyError".
In [151]: df2.loc['a']
Out[151]:
A
B
a 0
a 1
a 5
Der CategoricalIndex
wird nach dem Index ** beibehalten **.
In [152]: df2.loc['a'].index
Out[152]: CategoricalIndex(['a', 'a', 'a'], categories=['c', 'a', 'b'], ordered=False, name='B', dtype='category')
Wenn Sie die Indizes sortieren, werden sie nach Kategorien sortiert (da Sie den Index mit "CategoricalDtype (list (" cab "))" erstellt haben, werden sie nach "cab" sortiert).
In [153]: df2.sort_index()
Out[153]:
A
B
c 4
a 0
a 1
a 5
b 2
b 3
Groupby-Operationen für Indizes behalten auch die Eigenschaften des Index bei.
In [154]: df2.groupby(level=0).sum()
Out[154]:
A
B
c 4
a 6
b 5
In [155]: df2.groupby(level=0).sum().index
Out[155]: CategoricalIndex(['c', 'a', 'b'], categories=['c', 'a', 'b'], ordered=False, name='B', dtype='category')
Die Neuindizierungsoperation gibt einen Index des Ergebnisses zurück, der auf dem Typ des übergebenen Indexers basiert. Wenn Sie eine Liste übergeben, wird ein normaler "Index" zurückgegeben. Durch Übergeben eines Categorical
wird einCategoricalIndex
zurückgegeben, der gemäß der übergebenen Kategorie ** Categorical
dtype indiziert ist. Auf diese Weise können Sie willkürlich Werte indizieren, die ** in der Kategorie nicht vorhanden sind, ähnlich wie Sie Pandas neu indizieren.
In [156]: df3 = pd.DataFrame({'A': np.arange(3),
.....: 'B': pd.Series(list('abc')).astype('category')})
.....:
In [157]: df3 = df3.set_index('B')
In [158]: df3
Out[158]:
A
B
a 0
b 1
c 2
In [159]: df3.reindex(['a', 'e'])
Out[159]:
A
B
a 0.0
e NaN
In [160]: df3.reindex(['a', 'e']).index
Out[160]: Index(['a', 'e'], dtype='object', name='B')
In [161]: df3.reindex(pd.Categorical(['a', 'e'], categories=list('abe')))
Out[161]:
A
B
a 0.0
e NaN
In [162]: df3.reindex(pd.Categorical(['a', 'e'], categories=list('abe'))).index
Out[162]: CategoricalIndex(['a', 'e'], categories=['a', 'b', 'e'], ordered=False, name='B', dtype='category')
: Warnung: ** Warnung ** Formatierungs- und Vergleichsvorgänge in
CategoricalIndex
müssen in derselben Kategorie sein. Andernfalls erhalten Sie einen "TypeError".In [163]: df4 = pd.DataFrame({'A': np.arange(2), .....: 'B': list('ba')}) .....: In [164]: df4['B'] = df4['B'].astype(CategoricalDtype(list('ab'))) In [165]: df4 = df4.set_index('B') In [166]: df4.index Out[166]: CategoricalIndex(['b', 'a'], categories=['a', 'b'], ordered=False, name='B', dtype='category') In [167]: df5 = pd.DataFrame({'A': np.arange(2), .....: 'B': list('bc')}) .....: In [168]: df5['B'] = df5['B'].astype(CategoricalDtype(list('bc'))) In [169]: df5 = df5.set_index('B') In [170]: df5.index Out[170]: CategoricalIndex(['b', 'c'], categories=['b', 'c'], ordered=False, name='B', dtype='category')
In [1]: pd.concat([df4, df5]) TypeError: categories must match existing categories when appending
Int64Index
ist die Grundlage der Pandas-Index-Stiftung. Dies ist ein unveränderliches Array, das eine geordnete Slicable-Menge implementiert.
RangeIndex
stellt den Standardindex für alle NDFrame-Objekte bereit Eine Unterklasse von "Int64Index". RangeIndex
ist eine Version von Int64Index
, die für die Darstellung einer monotonen Reihe von Aufträgen optimiert wurde. Diese ähneln Pythons range type
.
Float64Index
Standardmäßig ist Float64Index
eine schwebende Minderheit oder Ganzzahl bei der Indizierung. Wird automatisch erstellt, wenn ein gemischter Wert aus schwebenden Brüchen übergeben wird. Dies ermöglicht ein reines Label-basiertes Slicing-Paradigma, bei dem der Skalarindex und das Slicing []
, ix
und loc
genau gleich funktionieren.
In [171]: indexf = pd.Index([1.5, 2, 3, 4.5, 5])
In [172]: indexf
Out[172]: Float64Index([1.5, 2.0, 3.0, 4.5, 5.0], dtype='float64')
In [173]: sf = pd.Series(range(5), index=indexf)
In [174]: sf
Out[174]:
1.5 0
2.0 1
3.0 2
4.5 3
5.0 4
dtype: int64
Skalare Auswahlen für "[]" und ".loc" basieren immer auf Beschriftungen. Die Angabe einer Ganzzahl entspricht einem gleichen Float-Index (z. B. entspricht "3" "3.0").
In [175]: sf[3]
Out[175]: 2
In [176]: sf[3.0]
Out[176]: 2
In [177]: sf.loc[3]
Out[177]: 2
In [178]: sf.loc[3.0]
Out[178]: 2
Der einzige Positionsindex ist über "iloc".
In [179]: sf.iloc[3]
Out[179]: 3
Der nicht gefundene Skalarindex löst einen "KeyError" aus. Slices basieren hauptsächlich auf Indexwerten, wenn "[]", "ix" und "loc" verwendet werden, und ** basieren immer auf der Position, wenn "iloc" verwendet wird. Die Ausnahme ist, wenn das Slice ein boolescher Wert ist. In diesem Fall basiert es immer auf der Position.
In [180]: sf[2:4]
Out[180]:
2.0 1
3.0 2
dtype: int64
In [181]: sf.loc[2:4]
Out[181]:
2.0 1
3.0 2
dtype: int64
In [182]: sf.iloc[2:4]
Out[182]:
3.0 2
4.5 3
dtype: int64
Mit dem Float-Index können Sie Slices mit Floating-Brüchen verwenden.
In [183]: sf[2.1:4.6]
Out[183]:
3.0 2
4.5 3
dtype: int64
In [184]: sf.loc[2.1:4.6]
Out[184]:
3.0 2
4.5 3
dtype: int64
Wenn es sich nicht um einen Float-Index handelt, lösen Slices, die Floats verwenden, einen "TypeError" aus.
In [1]: pd.Series(range(5))[3.5]
TypeError: the label [3.5] is not a proper indexer for this index type (Int64Index)
In [1]: pd.Series(range(5))[3.5:4.5]
TypeError: the slice start [3.5] is not a proper indexer for this index type (Int64Index)
Die folgenden Anwendungsfälle werden häufig für die Verwendung dieses Indextyps verwendet: Stellen Sie sich ein unregelmäßiges zeitdeltaartiges Indexierungsschema vor, bei dem die Daten als Floats aufgezeichnet werden. Dies kann beispielsweise ein Millisekundenversatz sein.
In [185]: dfir = pd.concat([pd.DataFrame(np.random.randn(5, 2),
.....: index=np.arange(5) * 250.0,
.....: columns=list('AB')),
.....: pd.DataFrame(np.random.randn(6, 2),
.....: index=np.arange(4, 10) * 250.1,
.....: columns=list('AB'))])
.....:
In [186]: dfir
Out[186]:
A B
0.0 -0.435772 -1.188928
250.0 -0.808286 -0.284634
500.0 -1.815703 1.347213
750.0 -0.243487 0.514704
1000.0 1.162969 -0.287725
1000.4 -0.179734 0.993962
1250.5 -0.212673 0.909872
1500.6 -0.733333 -0.349893
1750.7 0.456434 -0.306735
2000.8 0.553396 0.166221
2250.9 -0.101684 -0.734907
Auswahloperationen arbeiten für alle Auswahloperatoren immer auf Wertbasis.
In [187]: dfir[0:1000.4]
Out[187]:
A B
0.0 -0.435772 -1.188928
250.0 -0.808286 -0.284634
500.0 -1.815703 1.347213
750.0 -0.243487 0.514704
1000.0 1.162969 -0.287725
1000.4 -0.179734 0.993962
In [188]: dfir.loc[0:1001, 'A']
Out[188]:
0.0 -0.435772
250.0 -0.808286
500.0 -1.815703
750.0 -0.243487
1000.0 1.162969
1000.4 -0.179734
Name: A, dtype: float64
In [189]: dfir.loc[1000.4]
Out[189]:
A -0.179734
B 0.993962
Name: 1000.4, dtype: float64
Sie können die erste Sekunde (1000 Millisekunden) der Daten wie folgt abrufen:
In [190]: dfir[0:1000]
Out[190]:
A B
0.0 -0.435772 -1.188928
250.0 -0.808286 -0.284634
500.0 -1.815703 1.347213
750.0 -0.243487 0.514704
1000.0 1.162969 -0.287725
Wenn Sie eine auf einer ganzzahligen Position basierende Auswahl benötigen, verwenden Sie "iloc".
In [191]: dfir.iloc[0:5]
Out[191]:
A B
0.0 -0.435772 -1.188928
250.0 -0.808286 -0.284634
500.0 -1.815703 1.347213
750.0 -0.243487 0.514704
1000.0 1.162969 -0.287725
IntervalIndex
IntervalIndex
und seinen eigenen D-Typ IntervalDtype
und Interval
Mit dem Skalartyp können Pandas erstklassige Unterstützung für die Intervallnotation bieten. Ich werde.
IntervalIndex
ermöglicht eine eindeutige Indizierung, [cut ()
](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.cut.html#pandas Als Rückgabetyp der Kategorien .cut) und qcut ()
Wird auch verwendet.
IntervalIndex
IntervalIndex
kann als Index in Series
und DataFrame
verwendet werden.
In [192]: df = pd.DataFrame({'A': [1, 2, 3, 4]},
.....: index=pd.IntervalIndex.from_breaks([0, 1, 2, 3, 4]))
.....:
In [193]: df
Out[193]:
A
(0, 1] 1
(1, 2] 2
(2, 3] 3
(3, 4] 4
Beschriftungsbasierte Indizes über ".loc", die den Enden des Intervalls folgen, funktionieren wie erwartet und wählen dieses bestimmte Intervall aus.
In [194]: df.loc[2]
Out[194]:
A 2
Name: (1, 2], dtype: int64
In [195]: df.loc[[2, 3]]
Out[195]:
A
(1, 2] 2
(2, 3] 3
Wenn Sie die Bezeichnung * enthalten * innerhalb eines Intervalls auswählen, wird sie für jedes Intervall ausgewählt.
In [196]: df.loc[2.5]
Out[196]:
A 3
Name: (2, 3], dtype: int64
In [197]: df.loc[[2.5, 3.5]]
Out[197]:
A
(2, 3] 3
(3, 4] 4
Bei Auswahl in Intervallen werden nur genaue Übereinstimmungen zurückgegeben (Pandas 0.25.0 und höher).
In [198]: df.loc[pd.Interval(1, 2)]
Out[198]:
A 2
Name: (1, 2], dtype: int64
Wenn Sie versuchen, ein Intervall auszuwählen, das nicht genau im "IntervalIndex" enthalten ist, erhalten Sie einen "KeyError".
In [7]: df.loc[pd.Interval(0.5, 2.5)]
---------------------------------------------------------------------------
KeyError: Interval(0.5, 2.5, closed='right')
Um alle "Intervalle" auszuwählen, die ein bestimmtes "Intervall" überlappen, "überlappt ()". Erstellen Sie einen booleschen Indexer mit der Methode overraps.html # pandas.IntervalIndex.overlaps).
In [199]: idxr = df.index.overlaps(pd.Interval(0.5, 2.5))
In [200]: idxr
Out[200]: array([ True, True, True, False])
In [201]: df[idxr]
Out[201]:
A
(0, 1] 1
(1, 2] 2
(2, 3] 3
cut
und qcut
cut ()
und [qcut ()
](https: / /pandas.pydata.org/pandas-docs/stable/reference/api/pandas.qcut.html#pandas.qcut) geben beide kategoriale Objekte zurück und die von ihnen erstellten Bins befinden sich im Attribut ".categories". Es wird als IntervalIndex` gespeichert.
In [202]: c = pd.cut(range(4), bins=2)
In [203]: c
Out[203]:
[(-0.003, 1.5], (-0.003, 1.5], (1.5, 3.0], (1.5, 3.0]]
Categories (2, interval[float64]): [(-0.003, 1.5] < (1.5, 3.0]]
In [204]: c.categories
Out[204]:
IntervalIndex([(-0.003, 1.5], (1.5, 3.0]],
closed='right',
dtype='interval[float64]')
cut ()
übergibt IntervalIndex
als Argument 'bins kann auch tun. Dies ermöglicht eine bequeme Pandas-Sprache. Setzen Sie zunächst einige Daten und "Bins" auf eine feste Zahl [
cut ()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.cut). Rufen Sie html # pandas.cut) auf, um einen Bin zu erstellen. Als nächstes wurde der Wert von ".categories" anschließend ["cut ()"] genannt (https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.cut.html#) Sie können neue Daten in demselben Bin zusammenfassen, indem Sie sie an das Argument "bins" von pandas.cut übergeben.
In [205]: pd.cut([0, 3, 5, 1], bins=c.categories)
Out[205]:
[(-0.003, 1.5], (1.5, 3.0], NaN, (-0.003, 1.5]]
Categories (2, interval[float64]): [(-0.003, 1.5] < (1.5, 3.0]]
Werte außerhalb aller Bins erhalten den NaN-Wert.
Wenn Sie Intervalle mit normaler Frequenz benötigen, verwenden Sie die Funktion interval_range ()
. Sie können es verwenden, um einen "IntervalIndex" mit verschiedenen Kombinationen von "Start", "Ende" und "Perioden" zu erstellen. Der Standardzeitraum für interval_range
ist 1 für numerische Intervalle und Kalendertage für datetime-ähnliche Intervalle.
In [206]: pd.interval_range(start=0, end=5)
Out[206]:
IntervalIndex([(0, 1], (1, 2], (2, 3], (3, 4], (4, 5]],
closed='right',
dtype='interval[int64]')
In [207]: pd.interval_range(start=pd.Timestamp('2017-01-01'), periods=4)
Out[207]:
IntervalIndex([(2017-01-01, 2017-01-02], (2017-01-02, 2017-01-03], (2017-01-03, 2017-01-04], (2017-01-04, 2017-01-05]],
closed='right',
dtype='interval[datetime64[ns]]')
In [208]: pd.interval_range(end=pd.Timedelta('3 days'), periods=3)
Out[208]:
IntervalIndex([(0 days 00:00:00, 1 days 00:00:00], (1 days 00:00:00, 2 days 00:00:00], (2 days 00:00:00, 3 days 00:00:00]],
closed='right',
dtype='interval[timedelta64[ns]]')
Das Argument "freq" kann verwendet werden, um einen nicht standardmäßigen Zeitraum mit verschiedenen "Zeitraum-Aliasnamen" für datetime-ähnliche Intervalle anzugeben. # timeseries-offset-aliases) ist verfügbar.
In [209]: pd.interval_range(start=0, periods=5, freq=1.5)
Out[209]:
IntervalIndex([(0.0, 1.5], (1.5, 3.0], (3.0, 4.5], (4.5, 6.0], (6.0, 7.5]],
closed='right',
dtype='interval[float64]')
In [210]: pd.interval_range(start=pd.Timestamp('2017-01-01'), periods=4, freq='W')
Out[210]:
IntervalIndex([(2017-01-01, 2017-01-08], (2017-01-08, 2017-01-15], (2017-01-15, 2017-01-22], (2017-01-22, 2017-01-29]],
closed='right',
dtype='interval[datetime64[ns]]')
In [211]: pd.interval_range(start=pd.Timedelta('0 days'), periods=3, freq='9H')
Out[211]:
IntervalIndex([(0 days 00:00:00, 0 days 09:00:00], (0 days 09:00:00, 0 days 18:00:00], (0 days 18:00:00, 1 days 03:00:00]],
closed='right',
dtype='interval[timedelta64[ns]]')
Darüber hinaus können Sie mit dem Argument "geschlossen" angeben, wer das Intervall schließt. Standardmäßig sind die Intervalle rechts geschlossen.
In [212]: pd.interval_range(start=0, end=4, closed='both')
Out[212]:
IntervalIndex([[0, 1], [1, 2], [2, 3], [3, 4]],
closed='both',
dtype='interval[int64]')
In [213]: pd.interval_range(start=0, end=4, closed='neither')
Out[213]:
IntervalIndex([(0, 1), (1, 2), (2, 3), (3, 4)],
closed='neither',
dtype='interval[int64]')
_ Ab Version 0.23.0 _
Wenn Sie "Start", "Ende" und "Perioden" angeben, erstellt der resultierende "IntervalIndex" ein Intervall von "Start" bis "Ende" mit so vielen Elementen wie "Perioden" in gleichen Intervallen.
In [214]: pd.interval_range(start=0, end=6, periods=4)
Out[214]:
IntervalIndex([(0.0, 1.5], (1.5, 3.0], (3.0, 4.5], (4.5, 6.0]],
closed='right',
dtype='interval[float64]')
In [215]: pd.interval_range(pd.Timestamp('2018-01-01'),
.....: pd.Timestamp('2018-02-28'), periods=3)
.....:
Out[215]:
IntervalIndex([(2018-01-01, 2018-01-20 08:00:00], (2018-01-20 08:00:00, 2018-02-08 16:00:00], (2018-02-08 16:00:00, 2018-02-28]],
closed='right',
dtype='interval[datetime64[ns]]')
Die beschriftungsbasierte Indizierung mit Ganzzahlachsenbeschriftungen ist ein heikles Thema. Es wird häufig unter verschiedenen Mitgliedern der Mailingliste und der wissenschaftlichen Python-Community diskutiert. Bei Pandas ist unsere allgemeine Ansicht, dass Beschriftungen wichtiger sind als ganzzahlige Positionen. Daher ermöglichen Standardwerkzeuge wie ".loc" für ganzzahlige Achsenindizes die beschriftungsbasierte Indizierung * nur *. Der folgende Code löst eine Ausnahme aus.
In [216]: s = pd.Series(range(5))
In [217]: s[-1]
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-217-76c3dce40054> in <module>
----> 1 s[-1]
~/work/1/s/pandas/core/series.py in __getitem__(self, key)
1076 key = com.apply_if_callable(key, self)
1077 try:
-> 1078 result = self.index.get_value(self, key)
1079
1080 if not is_scalar(result):
~/work/1/s/pandas/core/indexes/base.py in get_value(self, series, key)
4623 k = self._convert_scalar_indexer(k, kind="getitem")
4624 try:
-> 4625 return self._engine.get_value(s, k, tz=getattr(series.dtype, "tz", None))
4626 except KeyError as e1:
4627 if len(self) > 0 and (self.holds_integer() or self.is_boolean()):
~/work/1/s/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_value()
~/work/1/s/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_value()
~/work/1/s/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()
~/work/1/s/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.Int64HashTable.get_item()
~/work/1/s/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.Int64HashTable.get_item()
KeyError: -1
In [218]: df = pd.DataFrame(np.random.randn(5, 4))
In [219]: df
Out[219]:
0 1 2 3
0 -0.130121 -0.476046 0.759104 0.213379
1 -0.082641 0.448008 0.656420 -1.051443
2 0.594956 -0.151360 -0.069303 1.221431
3 -0.182832 0.791235 0.042745 2.069775
4 1.446552 0.019814 -1.389212 -0.702312
In [220]: df.loc[-2:]
Out[220]:
0 1 2 3
0 -0.130121 -0.476046 0.759104 0.213379
1 -0.082641 0.448008 0.656420 -1.051443
2 0.594956 -0.151360 -0.069303 1.221431
3 -0.182832 0.791235 0.042745 2.069775
4 1.446552 0.019814 -1.389212 -0.702312
Diese bewusste Entscheidung wurde getroffen, um Mehrdeutigkeiten und subtile Fehler zu vermeiden (viele Benutzer finden einen Fehler, wenn sie die API so ändern, dass der "Fallback" bei der positionsbasierten Indizierung gestoppt wird. Ich berichtete).
Wenn der Index des "Series" - oder "DataFrame" monoton zunimmt oder abnimmt, liegt der Rand der beschriftungsbasierten Slices möglicherweise außerhalb des Bereichs des Index, wie bei der normalen Python-Listen-Slice-Indizierung. Die Monotonie des Index ist is_monotonic_increasing ()
und Sie können mit dem Attribut is_monotonic_decreasing ()
testen.
In [221]: df = pd.DataFrame(index=[2, 3, 3, 4, 5], columns=['data'], data=list(range(5)))
In [222]: df.index.is_monotonic_increasing
Out[222]: True
#Die Zeilen 0, 1 existieren nicht, geben jedoch die Zeilen 2, 3 (beide), 4 zurück
In [223]: df.loc[0:4, :]
Out[223]:
data
2 0
3 1
3 2
4 3
#Ein leerer DataFrame wird zurückgegeben, da das Slice keinen Index mehr hat
In [224]: df.loc[13:15, :]
Out[224]:
Empty DataFrame
Columns: [data]
Index: []
Wenn der Index hingegen nicht eintönig ist, müssen beide Slice-Grenzen * eindeutige * Werte des Index sein.
In [225]: df = pd.DataFrame(index=[2, 3, 1, 4, 3, 5],
.....: columns=['data'], data=list(range(6)))
.....:
In [226]: df.index.is_monotonic_increasing
Out[226]: False
#Es gibt kein Problem, da sowohl 2 als auch 4 im Index enthalten sind
In [227]: df.loc[2:4, :]
Out[227]:
data
2 0
3 1
1 2
4 3
#0 existiert nicht im Index
In [9]: df.loc[0:4, :]
KeyError: 0
#3 ist kein eindeutiges Etikett
In [11]: df.loc[2:3, :]
KeyError: 'Cannot get right slice bound for non-unique label: 3'
Index.is_monotonic_increasing
und Index.is_monotonic_decreasing
prüfen nur leicht, ob der Index eintönig ist. Um die genaue Monotonie zu sehen, wählen Sie entweder [is_unique ()
](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Index.is_unique.html# Kombinieren Sie mit dem Attribut pandas.Index.is_unique).
In [228]: weakly_monotonic = pd.Index(['a', 'b', 'c', 'c'])
In [229]: weakly_monotonic
Out[229]: Index(['a', 'b', 'c', 'c'], dtype='object')
In [230]: weakly_monotonic.is_monotonic_increasing
Out[230]: True
In [231]: weakly_monotonic.is_monotonic_increasing & weakly_monotonic.is_unique
Out[231]: False
Im Gegensatz zu Standard-Python-Sequenz-Slices, die keine Endpunkte enthalten, tun dies Pandas-Label-basierte Slices. Der Hauptgrund dafür ist, dass es oft nicht einfach ist, das "nachfolgende Label" oder das nächste Element nach einem bestimmten Label im Index zu bestimmen. Betrachten Sie zum Beispiel die folgenden "Seires".
In [232]: s = pd.Series(np.random.randn(6), index=list('abcdef'))
In [233]: s
Out[233]:
a 0.301379
b 1.240445
c -0.846068
d -0.043312
e -1.658747
f -0.819549
dtype: float64
Angenommen, Sie möchten mit einer Ganzzahl von c nach e schneiden. Dies geschieht wie folgt:
In [234]: s[2:5]
Out[234]:
c -0.846068
d -0.043312
e -1.658747
dtype: float64
Wenn Sie jedoch nur "c" und "e" angeben, kann die Bestimmung des nächsten Elements im Index etwas kompliziert sein. Zum Beispiel funktioniert Folgendes nicht:
s.loc['c':'e' + 1]
Ein sehr häufiger Anwendungsfall ist die Angabe einer bestimmten Zeitreihe, die an zwei bestimmten Daten beginnt und endet. Um dies zu ermöglichen, haben wir das Label-basierte Slice so konzipiert, dass es beide Endpunkte enthält.
In [235]: s.loc['c':'e']
Out[235]:
c -0.846068
d -0.043312
e -1.658747
dtype: float64
Dies ist wohl "praktischer als rein", aber seien Sie vorsichtig, wenn Sie erwarten, dass sich markenbasierte Slices genau wie Standard-Python-Ganzzahl-Slices verhalten.
Verschiedene Indizierungsvorgänge können den dtype von Series
ändern.
In [236]: series1 = pd.Series([1, 2, 3])
In [237]: series1.dtype
Out[237]: dtype('int64')
In [238]: res = series1.reindex([0, 4])
In [239]: res.dtype
Out[239]: dtype('float64')
In [240]: res
Out[240]:
0 1.0
4 NaN
dtype: float64
In [241]: series2 = pd.Series([True])
In [242]: series2.dtype
Out[242]: dtype('bool')
In [243]: res = series2.reindex_like(series1)
In [244]: res.dtype
Out[244]: dtype('O')
In [245]: res
Out[245]:
0 True
1 NaN
2 NaN
dtype: object
Dies liegt daran, dass die obige (Neu-) Indizierungsoperation implizit "NaNs" einfügt und den "dtype" entsprechend ändert. Dies kann Probleme bei der Verwendung von "numpy ufuncs" wie "numpy.logical_and" verursachen.
Weitere Informationen finden Sie in dieser früheren Ausgabe (https://github.com/pydata/pandas/issues/2388).