เหตุใดการกำหนดด้วย [:] เมื่อเปรียบเทียบกับ iloc [:] ให้ผลลัพธ์ที่แตกต่างกันในรูปของแพนด้า?


13

ฉันสับสนกับวิธีการจัดทำดัชนีที่แตกต่างกันที่ใช้ilocในแพนด้า

สมมติว่าฉันกำลังพยายามแปลงดาต้าเฟรม 1 วันเป็นดาต้าเฟลมแบบ 2 มิติ ก่อนอื่นฉันมี Dataframe 1-d ต่อไปนี้

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

และฉันจะไปแปลงที่เป็น 2 มิติ Dataframe 2x4กับขนาดของ ฉันเริ่มต้นด้วยการตั้งค่า Dataframe 2 มิติดังต่อไปนี้:

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

จากนั้นฉันใช้ for-loop เพื่อช่วยฉันแปลงa_df(1-d) เป็นb_df(2-d) ด้วยรหัสต่อไปนี้

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

มันให้ผลลัพธ์ต่อไปนี้กับฉันเท่านั้น

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

แต่เมื่อฉันเปลี่ยนไปb_df.iloc[i,:] b_df.iloc[i][:]ผลลัพธ์นั้นถูกต้องเช่นนี้ซึ่งเป็นสิ่งที่ฉันต้องการ

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

ทุกคนสามารถอธิบายให้ฉันเข้าใจได้ว่าความแตกต่างระหว่าง.iloc[i,:]และ.iloc[i][:]คืออะไรและทำไมจึง.iloc[i][:]ทำงานในตัวอย่างของฉันด้านบน แต่ไม่.iloc[i,:]


นี่คืออยากรู้อยากเห็น b_df.iloc[1] = a_df.iloc[0, 4:8]กำหนดชุดที่มีค่าดัชนีชุดที่มีดัชนี[4, 5, 6, 7] [0, 1, 2, 3]ไม่มีการทับซ้อนดังนั้นจึงNaNได้รับมอบหมายให้องค์ประกอบทั้งหมด จนถึงจุดนี้มันสมเหตุสมผลสำหรับฉัน แต่เหมือนคุณฉันไม่มีความชัดเจนว่าทำไมถึงb_df.iloc[1][:] = ...มีพฤติกรรมแตกต่างกัน - ตรวจสอบวัตถุb_df.iloc[1]และb_df.iloc[1][:]ไม่เปิดเผยความแตกต่างระหว่างดัชนี การคาดเดาที่ดีที่สุดของฉันคือการกำหนดให้คัดลอกโดยตรง ( [:]) จะถือว่าเป็นกรณีพิเศษโดย Pandas ซึ่งทำให้ไม่สนใจดัชนีของผู้รับโอนและสร้างความคลาดเคลื่อนนี้
Seb

ฉันคิดว่ามันเป็นเพราะดัชนีและความสำเร็จของแถวแรกเพราะมีดัชนีเดียวกัน
Phung Duy Phong

1
สิ่งสำคัญที่ต้องจำเกี่ยวกับนุ่นคือการทำงานส่วนใหญ่ในนุ่นโดยใช้แนวคิดที่เรียกว่า 'การจัดตำแหน่งข้อมูล' หมายความว่าการดำเนินการใด ๆ ที่คุณทำกับแพนด้าจะจัดเรียงดัชนีของทั้งสองด้านของคำสั่ง ที่นี่คุณกำลังพยายามตั้งค่าดัชนี 1 โดยใช้ดัชนี 0 แพนด้าจะกำหนด nans เนื่องจากไม่มีดัชนี 0 ทางด้านขวาของการมอบหมายนั้น นอกจากนี้โปรดจำไว้ว่าส่วนหัวคอลัมน์นั้นเป็นดัชนีเช่นกัน ดังนั้นแพนด้าจะจัดเรียงส่วนหัวของคอลัมน์กับส่วนหัวของคอลัมน์
Scott Boston

3
ประการที่สองการใช้. iloc [i] [:] เรียกว่าการโยงดัชนีและโดยทั่วไปจะเป็น "no-no" ที่ค่อนข้างใหญ่ในแพนด้า มีบางอย่างที่มีแพนด้าที่สร้างมุมมองของวัตถุหรือสร้างวัตถุใหม่ในหน่วยความจำที่อาจให้ผลลัพธ์ที่ไม่คาดคิด
Scott Boston

โปรดอย่าลืมโหวตคำตอบที่ได้ผลทั้งหมดและยอมรับคำตอบที่คุณชอบมากที่สุด อาจเป็นไปได้ว่าคุณรู้สิ่งนี้ แต่นี่คือเพื่อให้ชุมชนทราบว่าคำตอบใดมีประโยชน์และให้รางวัลแก่ผู้คนสำหรับเวลาและความพยายามของพวกเขาด้วย;) ดู meta.stackexchange.com/questions/5234/ นี้และ meta.stackexchange.com/ คำถาม / 173399 /
alan.elkin

คำตอบ:


3

มีความแตกต่างกันมากและใหญ่มากระหว่างseries.iloc[:]และseries[:]เมื่อกำหนดกลับ (i)locตรวจสอบเสมอเพื่อให้แน่ใจว่าสิ่งที่คุณมอบหมายจากตรงกับดัชนีของผู้รับมอบหมาย ในขณะเดียวกัน[:]ไวยากรณ์จะกำหนดให้กับอาร์เรย์ NumPy พื้นฐานโดยไม่ผ่านการจัดตำแหน่งดัชนี

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

ตอนนี้คุณเข้าใจความแตกต่างกันแล้วลองดูสิ่งที่เกิดขึ้นในรหัสของคุณ เพียงพิมพ์ RHS ของลูปของคุณเพื่อดูว่าคุณกำลังมอบหมายอะไร:

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   

เมื่อกำหนดให้กับb_df.iloc[i, :]การทำซ้ำครั้งที่สองดัชนีจะแตกต่างกันดังนั้นจึงไม่มีการกำหนดอะไรเลยและคุณเห็นเฉพาะ NaN เท่านั้น อย่างไรก็ตามการเปลี่ยน b_df.iloc[i, :]เป็นb_df.iloc[i][:]หมายถึงคุณกำหนดให้กับอาร์เรย์ NumPy พื้นฐานดังนั้นการจัดตำแหน่งดัชนีจะถูกข้ามไป การดำเนินการนี้จะแสดงได้ดีขึ้นเป็น

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

นอกจากนี้ยังเป็นเรื่องที่ควรค่าแก่การกล่าวถึงว่านี่เป็นรูปแบบของการกำหนดสายโซ่ซึ่งไม่ใช่สิ่งที่ดีและทำให้รหัสของคุณยากต่อการอ่านและทำความเข้าใจ


1
ตอนนี้ฉันเข้าใจแล้วขอบคุณ ก่อนที่ฉันจะมอบรางวัลให้คุณคุณสามารถเพิ่มการอ้างอิงสำหรับสิ่งนี้: " [:]ไวยากรณ์กำหนดให้กับอาร์เรย์ NumPy" หรือไม่?
Seb

@Seb คุณจะไม่พบการอ้างอิงถึงสิ่งนี้ในเอกสารประกอบเพราะเป็นรายละเอียดการใช้งาน การค้นหารหัสใน GitHub นั้นจะง่ายกว่านี้ แต่ฉันคิดว่าวิธีที่ง่ายที่สุดคือการแสดงให้เห็นว่าเกิดอะไรขึ้น ฉันได้แก้ไขตัวอย่างเล็ก ๆ ที่ด้านบนสุดของคำตอบของฉันเพื่อแสดงว่าอาร์เรย์ที่อยู่ภายใต้การจัดการระหว่างการกำหนดใหม่ประเภทต่าง ๆ หวังว่าจะทำให้สิ่งต่าง ๆ ชัดเจนขึ้น!
cs95

ขอบคุณมาก! ตอนนี้มันชัดเจนกว่ามาก
Tommy Yip

0

ความแตกต่างคือในกรณีแรกล่าม Python ดำเนินการรหัสเป็น:

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

โดยที่ค่าจะเป็นด้านขวาของสมการ ในกรณีที่สองตัวแปลภาษาไพ ธ อนใช้รหัสเป็น:

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

โดยที่ค่าจะเป็นด้านขวาของสมการอีกครั้ง

ในแต่ละกรณีทั้งสองกรณีจะเรียกวิธีการที่แตกต่างกันภายในsetitemเนื่องจากความแตกต่างในคีย์ (i, slice (None)) และ slice (None) ดังนั้นเราจึงมีพฤติกรรมที่แตกต่างกัน


b_df.iloc[i]และb_df.iloc[i][:]มีดัชนีเดียวกัน เหตุใดคุณสามารถกำหนดชุดข้อมูลที่มีดัชนีที่ไม่ตรงกันให้หนึ่งรายการ แต่ไม่ใช่รายการอื่น
Seb

ในกรณีแรก _set_item จะถูกเรียกใน one_setitem_slice ที่สองจะเป็นการโทร ดังนั้นสงสัยเนื่องจากความแตกต่างของวิธีการเหล่านั้นเรามีพฤติกรรมดังกล่าวข้างต้น
MaPy

0

ใครช่วยอธิบายให้ฉันฟังได้ว่าความแตกต่างระหว่าง.iloc[i,:]และ .iloc[i][:]คืออะไร

ความแตกต่างระหว่าง.iloc[i,:]และ.iloc[i][:]

ในกรณีที่.iloc[i,:]คุณเข้าถึงโดยตรงไปยังสมบัติเฉพาะของDataFrame, โดยเลือก:คอลัมน์( ) ทั้งหมดของiแถวที่ เท่าที่ฉันรู้มันเทียบเท่ากับการปล่อยมิติที่สองที่ไม่ระบุ ( .iloc[i])

ในกรณีที่.iloc[i][:]คุณกำลังดำเนินการ 2 การดำเนินการที่ถูกผูกมัด ดังนั้นผลจากนั้นจะได้รับผลกระทบจาก.iloc[i] [:]การใช้สิ่งนี้เพื่อตั้งค่านั้นหมดกำลังใจโดย Pandas เองที่นี่พร้อมคำเตือนดังนั้นคุณไม่ควรใช้:

การคัดลอกหรือการอ้างอิงถูกส่งคืนสำหรับการดำเนินการตั้งค่าหรือไม่นั้นขึ้นอยู่กับบริบท บางครั้งสิ่งนี้เรียกว่าการกำหนดสายโซ่และควรหลีกเลี่ยง


... และทำไม.iloc[i][:]ทำงานในตัวอย่างของฉันด้านบน แต่ไม่.iloc[i,:]

ดังที่ @Scott ได้กล่าวถึงในความคิดเห็น OP การจัดตำแหน่งข้อมูลจะอยู่ภายในดังนั้นดัชนีในด้านขวาของ=จะไม่ถูกรวมหากไม่มีอยู่ทางด้านซ้าย นี่คือเหตุผลที่มีNaNค่าในแถวที่ 2

ดังนั้นเพื่อให้สิ่งต่าง ๆ ชัดเจนคุณสามารถทำดังนี้:

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

หรือคุณสามารถแปลงเป็นlistแทนที่จะใช้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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.