ทำไมฉันถึงต้องทำสำเนาของ data frame ในนุ่น


189

เมื่อเลือกดาต้าเบสย่อยจากพาเรนต์ดาต้าเฟรมฉันสังเกตว่าโปรแกรมเมอร์บางคนทำสำเนาเฟรมข้อมูลโดยใช้.copy()วิธีการ ตัวอย่างเช่น,

X = my_dataframe[features_list].copy()

... แทนที่จะเป็นเพียงแค่

X = my_dataframe[features_list]

ทำไมพวกเขาถึงทำสำเนา data frame? จะเกิดอะไรขึ้นหากฉันไม่ทำสำเนา


6
ฉันเดาว่าพวกเขาใช้ความระมัดระวังเป็นพิเศษเพื่อไม่แก้ไขเฟรมข้อมูลต้นฉบับ อาจไม่จำเป็น แต่เมื่อคุณโยนบางสิ่งเข้าด้วยกันแบบโต้ตอบปลอดภัยกว่าขออภัย
Paul H

8
ฉันคิดว่านี่ไม่ใช่คำถามโง่ ๆ ที่ให้ลบ
Elizabeth Susan Joseph

คำตอบ:


207

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

df = DataFrame({'x': [1,2]})
df_sub = df[0:1]
df_sub.x = -1
print(df)

คุณจะได้รับ:

x
0 -1
1  2

ในทางตรงกันข้ามใบต่อไปนี้ df ไม่เปลี่ยนแปลง:

df_sub_copy = df[0:1].copy()
df_sub_copy.x = -1

6
นี้เป็นสำเนาลึกหรือไม่
bikashg

6
ใช่. โหมดเริ่มต้นคือการคัดลอก "ลึก"! pandas.pydata.org/pandas-docs/stable/reference/api/…
Ambareesh

44

เพราะถ้าคุณไม่ทำสำเนาแล้วดัชนียังสามารถจัดการได้ที่อื่นแม้ว่าคุณจะกำหนด dataFrame ให้กับชื่ออื่น

ตัวอย่างเช่น:

df2 = df
func1(df2)
func2(df)

func1 สามารถแก้ไข df ได้โดยการปรับเปลี่ยน df2 ดังนั้นเพื่อหลีกเลี่ยงปัญหาดังกล่าว

df2 = df.copy()
func1(df2)
func2(df)

รอสักครู่รอคุณช่วยอธิบายได้ไหมว่าทำไมสิ่งนี้ถึงเกิดขึ้น ไม่สมเหตุสมผล
NoName

2
มันเป็นเพราะในตัวอย่างแรก `df2 = df , both variables reference the same DataFrame instance. So any changes made to df` หรือdf2จะทำกับอินสแตนซ์ของวัตถุเดียวกัน ในขณะdf2 = df.copy()ที่อินสแตนซ์วัตถุที่สองถูกสร้างขึ้นสำเนาของอันแรก แต่ตอนนี้dfและdf2อ้างอิงถึงอินสแตนซ์ของวัตถุที่แตกต่างกันและการเปลี่ยนแปลงใด ๆ จะทำกับอินสแตนซ์ DataFrame ที่เกี่ยวข้อง
เปโดร

17

จำเป็นต้องระบุว่าการคัดลอกหรือมุมมองที่ส่งคืนนั้นขึ้นอยู่กับประเภทของการจัดทำดัชนี

เอกสารของแพนด้าพูดว่า:

การคืนค่ามุมมองกับสำเนา

กฎเกี่ยวกับเมื่อการดูข้อมูลถูกส่งคืนนั้นขึ้นอยู่กับ NumPy ทั้งหมด เมื่อใดก็ตามที่อาร์เรย์ของป้ายกำกับหรือเวกเตอร์บูลีนมีส่วนร่วมในการดำเนินการจัดทำดัชนีผลลัพธ์จะเป็นสำเนา ด้วยการจัดทำดัชนีเลเบล / สเกลาร์เดี่ยวและการแบ่งส่วนเช่น df.ix [3: 6] หรือ df.ix [:, 'A'] มุมมองจะถูกส่งคืน



12

วัตถุประสงค์หลักคือเพื่อหลีกเลี่ยงการทำดัชนีที่ถูกล่ามโซ่และกำจัด SettingWithCopyWarningจุดประสงค์หลักคือการหลีกเลี่ยงการจัดทำดัชนีถูกล่ามโซ่และกำจัด

การจัดทำดัชนีที่ถูกผูกมัดที่นี่เป็นสิ่งที่ต้องการ dfc['A'][0] = 111

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

In [1]: import pandas as pd

In [2]: dfc = pd.DataFrame({'A':['aaa','bbb','ccc'],'B':[1,2,3]})

In [3]: dfc
Out[3]:
    A   B
0   aaa 1
1   bbb 2
2   ccc 3

In [4]: aColumn = dfc['A']

In [5]: aColumn[0] = 111
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

In [6]: dfc
Out[6]:
    A   B
0   111 1
1   bbb 2
2   ccc 3

นี่aColumnคือมุมมองและไม่ใช่สำเนาจาก DataFrame ดั้งเดิมดังนั้นการแก้ไขaColumnจะทำให้ต้นฉบับdfcถูกแก้ไขด้วย ถัดไปถ้าเราสร้างดัชนีแถวแรก:

In [7]: zero_row = dfc.loc[0]

In [8]: zero_row['A'] = 222
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

In [9]: dfc
Out[9]:
    A   B
0   111 1
1   bbb 2
2   ccc 3

เวลาzero_rowนี้เป็นสำเนาดังนั้นต้นฉบับdfcจะไม่ถูกแก้ไข

จากตัวอย่างสองตัวอย่างข้างต้นเราเห็นว่าไม่ชัดเจนว่าคุณต้องการเปลี่ยน DataFrame ดั้งเดิมหรือไม่ สิ่งนี้เป็นอันตรายอย่างยิ่งหากคุณเขียนสิ่งต่อไปนี้:

In [10]: dfc.loc[0]['A'] = 333
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

In [11]: dfc
Out[11]:
    A   B
0   111 1
1   bbb 2
2   ccc 3

คราวนี้มันไม่ทำงานเลย ที่นี่เราต้องการเปลี่ยนdfcแต่จริง ๆ แล้วเราปรับเปลี่ยนค่ากลางdfc.loc[0]ที่เป็นสำเนาและจะถูกยกเลิกทันที เป็นการยากมากที่จะคาดการณ์ว่าค่ากลางเช่นdfc.loc[0]หรือdfc['A']เป็นมุมมองหรือสำเนาดังนั้นจึงไม่รับประกันว่าจะมีการอัปเดต DataFrame ต้นฉบับหรือไม่ นั่นเป็นเหตุผลที่ควรหลีกเลี่ยงการทำดัชนีที่ถูกโยงโซ่และนุ่นสร้างSettingWithCopyWarningสำหรับการปรับปรุงการจัดทำดัชนีแบบนี้

.copy()ตอนนี้คือการใช้ หากต้องการกำจัดคำเตือนให้ทำสำเนาเพื่อแสดงเจตนาของคุณอย่างชัดเจน:

In [12]: zero_row_copy = dfc.loc[0].copy()

In [13]: zero_row_copy['A'] = 444 # This time no warning

เนื่องจากคุณกำลังแก้ไขสำเนาคุณจะรู้ว่าต้นฉบับdfcจะไม่เปลี่ยนแปลงและคุณไม่คาดว่าจะมีการเปลี่ยนแปลง ความคาดหวังของคุณตรงกับพฤติกรรมจากนั้นSettingWithCopyWarningจะหายไป

หมายเหตุหากคุณต้องการแก้ไข DataFrame ต้นฉบับเอกสารแนะนำให้คุณใช้loc:

In [14]: dfc.loc[0,'A'] = 555

In [15]: dfc
Out[15]:
    A   B
0   555 1
1   bbb 2
2   ccc 3

2

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


0

สมมติว่าคุณมีกรอบข้อมูลดังต่อไปนี้

df1
     A    B    C    D
4 -1.0 -1.0 -1.0 -1.0
5 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0

เมื่อคุณต้องการสร้างอีกdf2อันที่เหมือนกันdf1โดยไม่ต้องcopy

df2=df1
df2
     A    B    C    D
4 -1.0 -1.0 -1.0 -1.0
5 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0

และต้องการแก้ไขค่า df2 เท่านั้นดังต่อไปนี้

df2.iloc[0,0]='changed'

df2
         A    B    C    D
4  changed -1.0 -1.0 -1.0
5       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0

ในเวลาเดียวกัน df1 ก็ถูกเปลี่ยนเช่นกัน

df1
         A    B    C    D
4  changed -1.0 -1.0 -1.0
5       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0

ตั้งแต่สอง df เหมือนกันobjectเราสามารถตรวจสอบได้โดยใช้id

id(df1)
140367679979600
id(df2)
140367679979600

ดังนั้นพวกเขาเป็นวัตถุเดียวกันและหนึ่งการเปลี่ยนแปลงอีกคนหนึ่งจะผ่านค่าเดียวกันเช่นกัน


หากเราเพิ่มรายการcopyและตอนนี้df1และdf2ถือว่าแตกต่างกันobjectหากเราทำการเปลี่ยนแปลงแบบเดียวกันกับรายการหนึ่งรายการอื่นจะไม่เปลี่ยนแปลง

df2=df1.copy()
id(df1)
140367679979600
id(df2)
140367674641232

df1.iloc[0,0]='changedback'
df2
         A    B    C    D
4  changed -1.0 -1.0 -1.0
5       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0

ดีที่จะพูดถึงเมื่อคุณเซ็ตอัพไฟล์ย่อยดั้งเดิมมันปลอดภัยที่จะเพิ่มการคัดลอกเช่นกันเพื่อหลีกเลี่ยง SettingWithCopyWarning

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