Warum führt die Zuweisung mit [:] gegenüber iloc [:] bei Pandas zu unterschiedlichen Ergebnissen?

13

Ich bin so verwirrt mit verschiedenen Indizierungsmethoden, die ilocin Pandas verwendet werden.

Angenommen, ich versuche, einen 1-D-Datenrahmen in einen 2-D-Datenrahmen zu konvertieren. Zuerst habe ich den folgenden 1-D-Datenrahmen

a_array = [1,2,3,4,5,6,7,8]
a_df = pd.DataFrame(a_array).T

Und ich werde das in einen 2D-Datenrahmen mit der Größe von konvertieren 2x4. Ich beginne mit der Voreinstellung des 2D-Datenrahmens wie folgt:

b_df = pd.DataFrame(columns=range(4),index=range(2))

Dann benutze ich die for-Schleife, um a_df(1-d) in b_df(2-d) mit dem folgenden Code zu konvertieren

for i in range(2):
    b_df.iloc[i,:] = a_df.iloc[0,i*4:(i+1)*4]

Es gibt mir nur die folgenden Ergebnisse

     0    1    2    3
0    1    2    3    4
1  NaN  NaN  NaN  NaN

Aber wenn ich geändert b_df.iloc[i,:]zu b_df.iloc[i][:]. Das Ergebnis ist wie folgt korrekt, was ich will

   0  1  2  3
0  1  2  3  4
1  5  6  7  8

Könnte mir jemand erklären, was der Unterschied zwischen .iloc[i,:]und .iloc[i][:]ist und warum .iloc[i][:]in meinem obigen Beispiel funktioniert hat, aber nicht.iloc[i,:]

Tommy Yip
quelle
Das ist merkwürdig. b_df.iloc[1] = a_df.iloc[0, 4:8]weist einer Reihe mit Index [4, 5, 6, 7]eine Reihe mit Index zu [0, 1, 2, 3]. Es gibt keine Überlappung, daher werden NaNalle Elemente zugewiesen. Bis zu diesem Punkt macht es für mich Sinn. Aber wie Sie ist mir nicht klar, warum ich mich b_df.iloc[1][:] = ...anders verhalte - ich untersuche die Objekte b_df.iloc[1]und b_df.iloc[1][:]zeige keinen Unterschied zwischen den Indizes. Meine beste Vermutung wäre, dass das direkte Zuweisen zu einer Kopie ( [:]) von Pandas als Sonderfall behandelt wird, wodurch der Index des Empfängers ignoriert wird und diese Diskrepanz entsteht.
Seb
Ich denke, es liegt am Index und dem Erfolg in der ersten Reihe, weil es den gleichen Index hat
Phung Duy Phong
1
Bei Pandas ist es wichtig, sich daran zu erinnern, dass fast alle Operationen in Pandas ein Konzept verwenden, das als "instrumentelle Datenausrichtung" bezeichnet wird. Dies bedeutet, dass fast jede Operation, die Sie mit Pandas ausführen, die Indizes beider Seiten der Anweisung ausrichtet. Hier versuchen Sie, Index 1 mit Index 0 zu setzen. Pandas weisen nans zu, da sich auf der rechten Seite dieser Zuordnung kein Index 0 befindet. Denken Sie auch daran, dass auch Spaltenüberschriften ein Index sind. Pandas richten also die Spaltenüberschrift an der Spaltenüberschrift aus.
Scott Boston
3
Zweitens wird die Verwendung von .iloc [i] [:] als Indexverkettung bezeichnet und ist bei Pandas im Allgemeinen ein ziemlich großes "Nein-Nein". Es gibt einige Probleme mit Pandas, die Ansichten eines Objekts erstellen oder ein brandneues Objekt im Speicher erstellen, das zu unerwarteten Ergebnissen führen kann.
Scott Boston
Bitte vergessen Sie nicht, alle funktionierenden Antworten zu bewerten und die zu akzeptieren, die Ihnen am besten gefällt. Wahrscheinlich wissen Sie das, aber dies soll die Community wissen lassen, welche Antworten nützlich waren, und die Leute für ihre Zeit und Mühe belohnen;) Siehe diese meta.stackexchange.com/questions/5234/ und meta.stackexchange.com/ Fragen / 173399 /
alan.elkin

Antworten:

3

Es gibt einen sehr, sehr großen Unterschied zwischen series.iloc[:]und series[:], wenn zurück zugewiesen wird. (i)locÜberprüft immer, ob das, was Sie zuweisen, mit dem Index des Empfängers übereinstimmt. In der Zwischenzeit wird die [:]Syntax dem zugrunde liegenden NumPy-Array zugewiesen, wobei die Indexausrichtung umgangen wird.

s = pd.Series(index=[0, 1, 2, 3], dtype='float')  
s                                                                          

0   NaN
1   NaN
2   NaN
3   NaN
dtype: float64

# Let's get a reference to the underlying array with `copy=False`
arr = s.to_numpy(copy=False) 
arr 
# array([nan, nan, nan, nan])

# Reassign using slicing syntax
s[:] = pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])                 
s                                                                          

0    1
1    2
2    3
3    4
dtype: int64

arr 
# array([1., 2., 3., 4.]) # underlying array has changed

# Now, reassign again with `iloc`
s.iloc[:] = pd.Series([5, 6, 7, 8], index=[3, 4, 5, 6]) 
s                                                                          

0    NaN
1    NaN
2    NaN
3    5.0
dtype: float64

arr 
# array([1., 2., 3., 4.])  # `iloc` created a new array for the series
                           # during reassignment leaving this unchanged

s.to_numpy(copy=False)     # the new underlying array, for reference                                                   
# array([nan, nan, nan,  5.]) 

Nachdem Sie den Unterschied verstanden haben, schauen wir uns an, was in Ihrem Code passiert. Drucken Sie einfach die RHS Ihrer Schleifen aus, um zu sehen, was Sie zuweisen:

for i in range(2): 
    print(a_df.iloc[0, i*4:(i+1)*4]) 

# output - first row                                                                   
0    1
1    2
2    3
3    4
Name: 0, dtype: int64
# second row. Notice the index is different
4    5
5    6
6    7
7    8
Name: 0, dtype: int64   

Bei der Zuweisung b_df.iloc[i, :]in der zweiten Iteration sind die Indizes unterschiedlich, sodass nichts zugewiesen wird und Sie nur NaNs sehen. Wenn Sie jedoch zu ändern b_df.iloc[i, :], b_df.iloc[i][:]werden Sie dem zugrunde liegenden NumPy-Array zugewiesen, sodass die Indizierungsausrichtung umgangen wird. Diese Operation wird besser ausgedrückt als

for i in range(2):
    b_df.iloc[i, :] = a_df.iloc[0, i*4:(i+1)*4].to_numpy()

b_df                                                                       

   0  1  2  3
0  1  2  3  4
1  5  6  7  8

Erwähnenswert ist auch, dass dies eine Form der verketteten Zuweisung ist, was nicht gut ist und das Lesen und Verstehen Ihres Codes erschwert.

cs95
quelle
1
Jetzt verstehe ich es, danke. Können Sie vor der Vergabe des Kopfgeldes eine Referenz hinzufügen: "Die [:]Syntax wird dem zugrunde liegenden NumPy-Array zugewiesen"?
Seb
@Seb In der Dokumentation finden Sie keine Verweise darauf, da es sich um ein Implementierungsdetail handelt. Es mag einfacher sein, den Code auf GitHub zu finden, der dafür verantwortlich ist, aber ich denke, der einfachste Weg ist, einfach zu demonstrieren, was passiert. Ich habe das kleine Beispiel oben in meiner Antwort bearbeitet, um zu zeigen, wie das zugrunde liegende Array während der verschiedenen Arten der Neuzuweisung manipuliert wird. Hoffe das macht die Dinge klarer!
cs95
Ich danke dir sehr! Es ist jetzt so viel klarer.
Tommy Yip
0

Der Unterschied besteht darin, dass der Python-Interpreter im ersten Fall den Code wie folgt ausgeführt hat:

b_df.iloc[i,:] = a_df.iloc[0,i*4:(i+1)*4]
#as
b_df.iloc.__setitem__((i, slice(None)), value)

wobei der Wert die rechte Seite der Gleichung wäre. Während im zweiten Fall der Python-Interpreter den Code wie folgt ausführte:

b_df.iloc[i][:] = a_df.iloc[0,i*4:(i+1)*4]
#as
b_df.iloc.__getitem__(i).__setitem__(slice(None), value)

wo wiederum der Wert die rechte Seite der Gleichung wäre.

In jedem dieser beiden Fälle würde aufgrund des Unterschieds zwischen den Schlüsseln (i, Slice (Keine)) und Slice (Keine) eine andere Methode innerhalb des Setitems aufgerufen. Daher haben wir ein unterschiedliches Verhalten.

MaPy
quelle
b_df.iloc[i]und b_df.iloc[i][:]haben die gleichen Indizes. Warum können Sie einer Reihe eine Reihe mit nicht übereinstimmendem Index zuweisen, der anderen jedoch nicht?
Seb
im ersten Fall würde das _set_item aufgerufen, im zweiten Fall würde one_setitem_slice aufgerufen werden.
Vermutlich
0

Könnte mir jemand erklären, was der Unterschied zwischen .iloc[i,:]und .iloc[i][:]ist

Der Unterschied zwischen .iloc[i,:]und.iloc[i][:]

Im Falle der .iloc[i,:]Sie direkt zu einem bestimmten possition des zugreifen DataFrame, indem Sie alle ( :) Spalten der iten Zeile. Soweit ich weiß, ist es gleichbedeutend, die 2. Dimension nicht spezifiziert zu lassen ( .iloc[i]).

Wenn .iloc[i][:]Sie 2 verkettete Operationen ausführen. Das Ergebnis von .iloc[i]wird dann also von beeinflusst [:]. Die Verwendung dieser Option zum Festlegen von Werten wird von Pandas selbst hier mit einer Warnung nicht empfohlen. Sie sollten sie daher nicht verwenden:

Ob eine Kopie oder eine Referenz für einen Einstellungsvorgang zurückgegeben wird, hängt möglicherweise vom Kontext ab. Dies wird manchmal als verkettete Zuordnung bezeichnet und sollte vermieden werden


... und warum .iloc[i][:]hat in meinem obigen Beispiel aber nicht funktioniert.iloc[i,:]

Wie @Scott in den OP-Kommentaren erwähnt hat, ist die Datenausrichtung intrinsisch , sodass die Indizes auf der rechten Seite von =nicht berücksichtigt werden, wenn sie nicht auf der linken Seite vorhanden sind. Aus diesem Grund befinden sich NaNin der 2. Zeile Werte.

Um die Dinge klar zu lassen, können Sie Folgendes tun:

for i in range(2):
    # Get the slice
    a_slice = a_df.iloc[0, i*4:(i+1)*4]
    # Reset the indices
    a_slice.reset_index(drop=True, inplace=True)
    # Set the slice into b_df
    b_df.iloc[i,:] = a_slice

Oder Sie können konvertieren zu listanstatt zu verwenden reset_index:

for i in range(2):
    # Get the slice
    a_slice = a_df.iloc[0, i*4:(i+1)*4]
    # Convert the slice into a list and set it into b_df
    b_df.iloc[i,:] = list(a_slice)
alan.elkin
quelle