2013-01-07 9 views
12

Zusammenfassung: Das funktioniert nicht:Verständnis Pandas Datenrahmen Indizierung

df[df.key==1]['D'] = 1 

aber dies tut:

df.D[df.key==1] = 1 

Warum?

Fortpflanzung:

In [1]: import pandas as pd 

In [2]: from numpy.random import randn 

In [4]: df = pd.DataFrame(randn(6,3),columns=list('ABC')) 

In [5]: df 
Out[5]: 
      A   B   C 
0 1.438161 -0.210454 -1.983704 
1 -0.283780 -0.371773 0.017580 
2 0.552564 -0.610548 0.257276 
3 1.931332 0.649179 -1.349062 
4 1.656010 -1.373263 1.333079 
5 0.944862 -0.657849 1.526811 

In [6]: df['D']=0.0 

In [7]: df['key']=3*[1]+3*[2] 

In [8]: df 
Out[8]: 
      A   B   C D key 
0 1.438161 -0.210454 -1.983704 0 1 
1 -0.283780 -0.371773 0.017580 0 1 
2 0.552564 -0.610548 0.257276 0 1 
3 1.931332 0.649179 -1.349062 0 2 
4 1.656010 -1.373263 1.333079 0 2 
5 0.944862 -0.657849 1.526811 0 2 

Das funktioniert nicht:

In [9]: df[df.key==1]['D'] = 1 

In [10]: df 
Out[10]: 
      A   B   C D key 
0 1.438161 -0.210454 -1.983704 0 1 
1 -0.283780 -0.371773 0.017580 0 1 
2 0.552564 -0.610548 0.257276 0 1 
3 1.931332 0.649179 -1.349062 0 2 
4 1.656010 -1.373263 1.333079 0 2 
5 0.944862 -0.657849 1.526811 0 2 

aber dies tut:

In [11]: df.D[df.key==1] = 3.4 

In [12]: df 
Out[12]: 
      A   B   C D key 
0 1.438161 -0.210454 -1.983704 3.4 1 
1 -0.283780 -0.371773 0.017580 3.4 1 
2 0.552564 -0.610548 0.257276 3.4 1 
3 1.931332 0.649179 -1.349062 0.0 2 
4 1.656010 -1.373263 1.333079 0.0 2 
5 0.944862 -0.657849 1.526811 0.0 2 

Link to notebook

Meine Frage ist: Warum nur, dass die 2 Weg arbeiten? Ich kann keinen Unterschied in der Auswahl-/Indizierungslogik sehen.

Version ist 0.10.0

Edit: Dies sollte nicht mehr wie dies getan werden. Seit 0.1135 gibt es .loc, siehe hier: http://pandas.pydata.org/pandas-docs/stable/indexing.html

+0

Wie in den Antworten gesagt, scheint es ein numpiges Problem zu sein: werfen Sie einen Blick auf [diese Frage] (http://stackoverflow.com/q/9470604/1301710) für ein ähnliches Problem. Ich bin mir nicht sicher, ob es ein Problem von Ansicht gegen Kopie ist. – bmu

+0

Ich verstehe jetzt, dass es klar ist (und eigentlich einfach) den Unterschied zwischen Ansicht und Kopie. Die erste Methode stellt nur eine Kopie bereit, die als Garbage Collection erfasst wird. Das zweite Verfahren liefert eine Ansicht, daher wird die Einstellung am ursprünglichen Datenrahmen vorgenommen. (siehe Dougals Kommentare unten) –

Antwort

15

Die Pandas Dokumentation sagt:

eine Ansicht im Vergleich zu einer Kopie

Die Regeln Rückkehr über Wenn eine Sicht auf die Daten zurückgegeben wird, sind abhängig von NumPy. Immer wenn ein Array von Etiketten oder ein boolescher Vektor an der Indizierung beteiligt sind, wird das Ergebnis eine Kopie sein. Mit einzelner Label/Skalar-Indizierung und Slicing, z.B. df.ix [3: 6] oder df.ix [:, 'A'], wird eine Ansicht zurückgegeben.

In df[df.key==1]['D'] Sie zuerst tun boolean Slicing (was zu einer Kopie des Dataframe), dann wählen Sie eine Spalte [ 'D'].

In df.D[df.key==1] = 3.4, wählen Sie zuerst eine Spalte, dann boolean schneiden auf die resultierende Serie.

Dies scheint den Unterschied zu machen, obwohl ich zugeben muss, dass es ein wenig kontraintuitiv ist.

bearbeiten: Der Unterschied von Dougal identifiziert wurde, seinen Kommentar sehen: Mit der Version 1 wird die Kopie gemacht als die __getitem__ Methode für die boolean Slicing bezeichnet wird. Für Version 2 wird nur auf die __setitem__-Methode zugegriffen - also wird keine Kopie zurückgegeben, sondern nur zugewiesen.

+0

Das dachte ich auch zuerst, aber da muss noch etwas anderes los sein. 'df [df.key == 1] = 1000' weist allen Werten im Slice tatsächlich 1000 zu, also kann es keine Kopie sein. Ich vermute, dass in den Methoden __setattr__ oder __setitem__ etwas passiert. – cxrodgers

+1

aber wie ich eine boolean schneiden auf die resultierende Serie, sollte das auch eine Kopie sein, sollte es nicht? Warum funktioniert die Aufgabe so? –

+0

Sehen Sie sich Dougals Kommentar oben an. Bei Version 1 wird die Kopie erstellt, wenn die __getitem __- Methode für das boolesche Slicing aufgerufen wird. Für Version 2 wird nur auf die __setitem __- Methode zugegriffen - also wird keine Kopie zurückgegeben, sondern nur zugewiesen. –

4

Ich bin mir ziemlich sicher, dass Ihre erste Möglichkeit eine Kopie zurückgibt, statt einer Ansicht, und damit die ursprüngliche Daten nicht ändern. Ich bin mir nicht sicher, warum das so ist.

Es scheint mit der Reihenfolge in Zusammenhang stehen, in der Sie Zeilen und Spalten auswählen, nicht die Syntax zum Abrufen von Spalten. Diese beiden Arbeiten:

df.D[df.key == 1] = 1 
df['D'][df.key == 1] = 1 

Und keines dieser Werke:

df[df.key == 1]['D'] = 1 
df[df.key == 1].D = 1 

Aus diesen Beweisen, würde ich davon ausgehen, dass die Scheibe df[df.key == 1] ist eine Kopie zurück. Aber das ist nicht der Fall! df[df.key == 1] = 0 wird tatsächlich die ursprünglichen Daten ändern, als ob es eine Ansicht wäre.

Also ich bin mir nicht sicher. Mein Gefühl ist, dass sich dieses Verhalten mit der Version von Pandas geändert hat. Ich erinnere mich, dass df.D eine Kopie zurückgab und df ['D'] eine Ansicht zurückgab, aber das scheint nicht mehr wahr zu sein (Pandas 0.10.0).

Wenn Sie eine vollständigere Antwort wollen, sollten Sie in der pystatsmodels Forum schreiben: https://groups.google.com/forum/?fromgroups#!forum/pystatsmodels

+3

'df [df.key == 1]' _does_ gibt tatsächlich eine Kopie zurück (wie Thorstens Antwort darauf hinweist). Der Grund dafür, dass "df [df.key == 1] = 0" das Original verändert, ist, dass, obwohl die Syntax ein bisschen irreführend ist, das gar nicht dasselbe tut; Die Nicht-Zuweisungsversion ruft "__getitem__" und die Zuweisungsversion "__setitem__" auf. Es ist wie wenn wir 'l = [0, 1, 2]' haben, dann gibt 'l [1]' das int 1 zurück, aber 'l [1] = 5' modifiziert das Original. – Dougal