Mengapa menugaskan dengan [:] versus iloc [:] menghasilkan hasil yang berbeda dalam panda?

13

Saya sangat bingung dengan metode pengindeksan yang berbeda menggunakan ilocdi panda.

Katakanlah saya sedang mencoba mengubah 1-d Dataframe menjadi 2-d Dataframe. Pertama saya memiliki Dataframe 1-d berikut

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

Dan saya akan mengubahnya menjadi 2-d Dataframe dengan ukuran 2x4. Saya mulai dengan menetapkan 2-d Dataframe sebagai berikut:

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

Kemudian saya menggunakan for-loop untuk membantu saya mengkonversi a_df(1-d) ke b_df(2-d) dengan kode berikut

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

Itu hanya memberi saya hasil berikut

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

Tetapi ketika saya berubah b_df.iloc[i,:]menjadi b_df.iloc[i][:]. Hasilnya benar seperti berikut, yang saya inginkan

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

Adakah yang bisa menjelaskan kepada saya apa perbedaan antara .iloc[i,:]dan .iloc[i][:]adalah, dan mengapa .iloc[i][:]bekerja dalam contoh saya di atas tetapi tidak.iloc[i,:]

Tommy Yip
sumber
Ini penasaran. b_df.iloc[1] = a_df.iloc[0, 4:8]menetapkan seri dengan indeks [4, 5, 6, 7]ke seri dengan indeks [0, 1, 2, 3]. Tidak ada tumpang tindih jadi NaNditugaskan untuk semua elemen. Sampai pada titik ini masuk akal bagi saya. Tetapi seperti Anda, saya tidak jelas mengapa b_df.iloc[1][:] = ...berperilaku berbeda — memeriksa objek b_df.iloc[1]dan b_df.iloc[1][:]tidak menunjukkan perbedaan di antara indeks. Tebakan terbaik saya adalah bahwa menetapkan langsung ke salinan ( [:]) diperlakukan sebagai kasus khusus oleh Pandas yang membuatnya mengabaikan indeks penerima dan membuat perbedaan ini.
Seb
Saya pikir itu karena indeks, dan sukses baris pertama karena memiliki indeks yang sama
Phung Duy Phong
1
Hal utama yang perlu saya ingat tentang panda adalah bahwa sebagian besar semua operasi di panda menggunakan konsep yang disebut 'penyelarasan data instrinic'. Artinya, hampir semua operasi yang Anda lakukan dengan panda akan menyelaraskan indeks dari kedua sisi pernyataan. Di sini Anda mencoba untuk menetapkan indeks 1 menggunakan indeks 0, panda akan menetapkan nans karena tidak ada indeks 0 di sisi kanan penugasan itu. Juga ingat bahwa tajuk kolom juga merupakan indeks. Jadi, panda akan menyelaraskan tajuk kolom ke tajuk kolom.
Scott Boston
3
Kedua, menggunakan .iloc [i] [:] disebut index chaining dan ini umumnya merupakan "tidak-tidak" yang cukup besar di panda. Ada beberapa masalah dengan panda yang menciptakan pandangan suatu objek atau membuat objek baru dalam memori yang dapat menghasilkan beberapa hasil yang tidak terduga.
Scott Boston
Tolong jangan lupa untuk membatalkan semua jawaban yang berfungsi, dan terima jawaban yang paling Anda sukai. Mungkin Anda mengetahui hal ini, tetapi ini adalah untuk memberi tahu komunitas mana jawaban yang bermanfaat dan untuk memberi penghargaan kepada orang-orang atas waktu dan usaha mereka juga;) Lihat meta.stackexchange.com/questions/5234/ ini dan meta.stackexchange.com/ pertanyaan / 173399 /
alan.elkin

Jawaban:

3

Ada perbedaan yang sangat, sangat besar antara series.iloc[:]dan series[:], ketika menetapkan kembali. (i)locselalu periksa untuk memastikan apa pun yang Anda tetapkan cocok dengan indeks penerima yang ditunjuk. Sementara itu, [:]sintaks menetapkan untuk array NumPy yang mendasarinya, melewati indeks alignment

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.]) 

Sekarang setelah Anda memahami perbedaannya, mari kita lihat apa yang terjadi dalam kode Anda. Cukup cetak RHS loop Anda untuk melihat apa yang Anda tetapkan:

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   

Saat menetapkan b_df.iloc[i, :]dalam iterasi kedua, indeks berbeda sehingga tidak ada yang ditetapkan dan Anda hanya melihat NaNs. Namun, mengubah b_df.iloc[i, :]ke b_df.iloc[i][:]berarti Anda menetapkan ke array NumPy yang mendasarinya, sehingga perataan indeks dilewati. Operasi ini lebih baik dinyatakan sebagai

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

Perlu juga disebutkan bahwa ini adalah bentuk penugasan berantai, yang bukan merupakan hal yang baik , dan juga membuat kode Anda lebih sulit dibaca dan dipahami.

cs95
sumber
1
Sekarang saya mengerti, terima kasih. Sebelum saya memberikan hadiah, dapatkah Anda menambahkan referensi untuk ini: " [:]sintaks yang diberikan ke array NumPy yang mendasarinya"?
Seb
@Seb Anda tidak akan benar-benar menemukan referensi untuk ini di dokumentasi karena ini agak detail implementasi. Mungkin lebih mudah untuk menemukan kode pada GitHub yang bertanggung jawab untuk ini, tapi saya pikir cara termudah adalah dengan menunjukkan apa yang terjadi. Saya telah mengedit contoh kecil di bagian atas jawaban saya untuk menunjukkan bagaimana array yang mendasarinya dimanipulasi selama berbagai jenis penugasan kembali. Harapan yang membuat segalanya lebih jelas!
cs95
Terima kasih banyak! Jauh lebih jelas sekarang.
Tommy Yip
0

Perbedaannya adalah bahwa dalam kasus pertama interpreter Python mengeksekusi kode sebagai:

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

di mana nilainya akan menjadi sisi kanan persamaan. Sedangkan dalam kasus kedua interpreter Python mengeksekusi kode sebagai:

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

di mana lagi nilainya akan menjadi sisi kanan persamaan.

Dalam masing-masing dua kasus metode yang berbeda akan disebut di dalam setitem karena perbedaan kunci (i, slice (None)) dan slice (None) Oleh karena itu kami memiliki perilaku yang berbeda.

MaPy
sumber
b_df.iloc[i]dan b_df.iloc[i][:]memiliki indeks yang sama. Mengapa Anda dapat menetapkan seri dengan indeks tidak cocok untuk satu tetapi tidak yang lain?
Seb
dalam kasus pertama _set_item akan dipanggil dalam one_setitem_slice yang kedua adalah panggilan. Jadi, dicurigai karena perbedaan metode tersebut kita memiliki perilaku di atas
MaPy
0

Mungkin ada orang yang menjelaskan kepada saya apa perbedaan antara .iloc[i,:]dan .iloc[i][:]adalah

Perbedaan antara .iloc[i,:]dan.iloc[i][:]

Dalam kasus .iloc[i,:]Anda mengakses langsung ke kepemilikan tertentu DataFrame, dengan memilih semua ( :) kolom ibaris ke - th. Sejauh yang saya tahu, itu setara dengan membiarkan dimensi ke-2 tidak ditentukan ( .iloc[i]).

Jika .iloc[i][:]Anda sedang melakukan 2 operasi berantai. Jadi, hasilnya .iloc[i]nanti akan terpengaruh oleh [:]. Menggunakan ini untuk menetapkan nilai tidak disarankan oleh Pandas sendiri di sini dengan peringatan, jadi Anda tidak boleh menggunakannya:

Apakah salinan atau referensi dikembalikan untuk operasi pengaturan, mungkin tergantung pada konteksnya. Ini kadang-kadang disebut tugas berantai dan harus dihindari


... dan mengapa .iloc[i][:]berhasil dalam contoh saya di atas tetapi tidak.iloc[i,:]

Seperti @Scott disebutkan pada komentar OP, penyelarasan data adalah intrinsik , sehingga indeks di sisi kanan =tidak akan dimasukkan jika mereka tidak ada di sisi kiri. Inilah sebabnya mengapa ada NaNnilai di baris ke-2.

Jadi, untuk memperjelas, Anda bisa melakukan hal berikut:

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

Atau Anda dapat mengonversi ke listalih-alih menggunakan 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
sumber