จะจัดการกับSettingWithCopyWarning
Pandas ได้อย่างไร?
โพสต์นี้มีไว้สำหรับผู้อ่านที่
- ต้องการที่จะเข้าใจความหมายของคำเตือนนี้
- ต้องการที่จะเข้าใจวิธีการต่าง ๆ ของการยับยั้งคำเตือนนี้
- ต้องการทราบวิธีปรับปรุงรหัสและปฏิบัติตามแนวทางปฏิบัติที่ดีเพื่อหลีกเลี่ยงคำเตือนนี้ในอนาคต
ติดตั้ง
np.random.seed(0)
df = pd.DataFrame(np.random.choice(10, (3, 5)), columns=list('ABCDE'))
df
A B C D E
0 5 0 3 3 7
1 9 3 5 2 4
2 7 6 8 8 1
คือSettingWithCopyWarning
อะไร
หากต้องการทราบวิธีจัดการกับคำเตือนนี้เป็นสิ่งสำคัญที่จะต้องเข้าใจว่ามันหมายถึงอะไรและทำไมมันถูกยกขึ้นมาตั้งแต่แรก
เมื่อกรอง DataFrames เป็นไปได้ที่จะแบ่งส่วน / ดัชนีเฟรมเพื่อส่งคืนมุมมองหรือสำเนาโดยขึ้นอยู่กับโครงร่างภายในและรายละเอียดการนำไปปฏิบัติต่าง ๆ "มุมมอง" คือตามคำแนะนำมุมมองลงในข้อมูลต้นฉบับดังนั้นการแก้ไขมุมมองอาจแก้ไขวัตถุต้นฉบับ ในทางตรงกันข้าม "สำเนา" คือการจำลองข้อมูลจากต้นฉบับและการแก้ไขสำเนาไม่มีผลกับต้นฉบับ
ดังกล่าวโดยคำตอบอื่น ๆ ที่SettingWithCopyWarning
ถูกสร้างขึ้นเพื่อดำเนินการ "กำหนดที่ถูกผูกมัด" พิจารณาdf
ในการตั้งค่าด้านบน สมมติว่าคุณต้องการเลือกค่าทั้งหมดในคอลัมน์ "B" โดยที่ค่าในคอลัมน์ "A" คือ> 5. Pandas ช่วยให้คุณทำสิ่งนี้ในรูปแบบที่แตกต่างกันบางอย่างถูกต้องกว่าคนอื่น ๆ ตัวอย่างเช่น,
df[df.A > 5]['B']
1 3
2 6
Name: B, dtype: int64
และ,
df.loc[df.A > 5, 'B']
1 3
2 6
Name: B, dtype: int64
สิ่งเหล่านี้ส่งคืนผลลัพธ์เดียวกันดังนั้นหากคุณอ่านค่าเหล่านี้เท่านั้น ดังนั้นปัญหาคืออะไร ปัญหาเกี่ยวกับการกำหนดที่ถูกโยงโซ่คือโดยทั่วไปมันยากที่จะคาดการณ์ว่ามุมมองหรือการคัดลอกจะถูกส่งกลับดังนั้นส่วนใหญ่จะกลายเป็นปัญหาเมื่อคุณพยายามที่จะกำหนดค่ากลับ หากต้องการสร้างตัวอย่างก่อนหน้านี้ให้พิจารณาวิธีการเรียกใช้โค้ดนี้โดยล่าม:
df.loc[df.A > 5, 'B'] = 4
# becomes
df.__setitem__((df.A > 5, 'B'), 4)
เดียวกับการเรียกร้องให้__setitem__
df
OTOH พิจารณารหัสนี้:
df[df.A > 5]['B'] = 4
# becomes
df.__getitem__(df.A > 5).__setitem__('B", 4)
ตอนนี้ขึ้นอยู่กับว่า__getitem__
กลับมีมุมมองหรือสำเนาการ__setitem__
ดำเนินงานอาจไม่ทำงาน
โดยทั่วไปคุณควรใช้loc
สำหรับการกำหนดตามป้ายกำกับและiloc
สำหรับการมอบหมายตามจำนวนเต็ม / ตำแหน่งเนื่องจากข้อมูลจำเพาะรับประกันว่าพวกเขาจะทำงานบนต้นฉบับเสมอ นอกจากนี้สำหรับการตั้งค่าเซลล์เดียวที่คุณควรใช้และat
iat
เพิ่มเติมสามารถพบได้ในเอกสาร
หมายเหตุ
ทุกการดำเนินงานการจัดทำดัชนีบูลทำด้วยนอกจากนี้ยังสามารถทำได้ด้วยloc
iloc
ความแตกต่างเพียงอย่างเดียวคือiloc
คาดว่าจะเป็นจำนวนเต็ม / ตำแหน่งสำหรับดัชนีหรืออาเรย์จำนวน numpy ของค่าบูลีนและดัชนีจำนวนเต็ม / ตำแหน่งสำหรับคอลัมน์
ตัวอย่างเช่น,
df.loc[df.A > 5, 'B'] = 4
สามารถเขียน NAS
df.iloc[(df.A > 5).values, 1] = 4
และ,
df.loc[1, 'A'] = 100
สามารถเขียนเป็น
df.iloc[1, 0] = 100
และอื่น ๆ
แค่บอกวิธีการระงับคำเตือน!
พิจารณาดำเนินการอย่างง่ายที่ "A" df
คอลัมน์ การเลือก "A" และหารด้วย 2 จะเป็นการเพิ่มการเตือน แต่การทำงานจะทำงาน
df2 = df[['A']]
df2['A'] /= 2
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/IPython/__main__.py:1: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
df2
A
0 2.5
1 4.5
2 3.5
มีวิธีการปิดเสียงเตือนนี้โดยตรงสองวิธี:
ทำ deepcopy
df2 = df[['A']].copy(deep=True)
df2['A'] /= 2
เปลี่ยนpd.options.mode.chained_assignment
สามารถตั้งค่าให้None
, หรือ"warn"
เป็นค่าเริ่มต้น จะระงับการเตือนทั้งหมดและจะโยน a ป้องกันไม่ให้การดำเนินการผ่านไป"raise"
"warn"
None
"raise"
SettingWithCopyError
pd.options.mode.chained_assignment = None
df2['A'] /= 2
@Peter Cottonในความคิดเห็นมาพร้อมกับวิธีที่ดีในการไม่เปลี่ยนโหมด (แก้ไขจากกระทู้นี้ ) โดยใช้ตัวจัดการบริบทเพื่อตั้งค่าโหมดตราบเท่าที่จำเป็นและตั้งค่ากลับเป็น สถานะเดิมเมื่อเสร็จสิ้น
class ChainedAssignent:
def __init__(self, chained=None):
acceptable = [None, 'warn', 'raise']
assert chained in acceptable, "chained must be in " + str(acceptable)
self.swcw = chained
def __enter__(self):
self.saved_swcw = pd.options.mode.chained_assignment
pd.options.mode.chained_assignment = self.swcw
return self
def __exit__(self, *args):
pd.options.mode.chained_assignment = self.saved_swcw
การใช้งานมีดังนี้:
# some code here
with ChainedAssignent():
df2['A'] /= 2
# more code follows
หรือเพื่อยกข้อยกเว้น
with ChainedAssignent(chained='raise'):
df2['A'] /= 2
SettingWithCopyError:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
"ปัญหา XY": ฉันทำอะไรผิด
บ่อยครั้งที่ผู้ใช้พยายามมองหาวิธีที่จะระงับข้อยกเว้นนี้โดยไม่เข้าใจว่าเหตุใดจึงถูกยกขึ้นมาตั้งแต่แรก นี่เป็นตัวอย่างที่ดีของปัญหา XYที่ผู้ใช้พยายามแก้ปัญหา "Y" ซึ่งเป็นอาการของปัญหารากลึก "X" คำถามจะถูกหยิบยกขึ้นอยู่กับปัญหาทั่วไปที่พบคำเตือนนี้แล้วจะนำเสนอโซลูชั่น
คำถามที่ 1
ฉันมี DataFrame
df
A B C D E
0 5 0 3 3 7
1 9 3 5 2 4
2 7 6 8 8 1
ฉันต้องการที่จะกำหนดค่าในคอลัมน์ "A"> 5 ถึง 1000 คาดว่าการส่งออกของฉันคือ
A B C D E
0 5 0 3 3 7
1 1000 3 5 2 4
2 1000 6 8 8 1
ทางที่ผิดจะทำเช่นนี้:
df.A[df.A > 5] = 1000 # works, because df.A returns a view
df[df.A > 5]['A'] = 1000 # does not work
df.loc[df.A 5]['A'] = 1000 # does not work
วิธีที่ถูกต้องโดยใช้loc
:
df.loc[df.A > 5, 'A'] = 1000
คำถาม 2 1
ฉันพยายามตั้งค่าในเซลล์ (1, 'D') เป็น 12345 ผลลัพธ์ที่ฉันคาดหวังคือ
A B C D E
0 5 0 3 3 7
1 9 3 5 12345 4
2 7 6 8 8 1
df['D'][1]
ฉันได้ลองวิธีที่แตกต่างกันในการเข้าถึงมือถือนี้เช่น
วิธีที่ดีที่สุดในการทำเช่นนี้คืออะไร?
1. คำถามนี้ไม่เกี่ยวข้องกับคำเตือนโดยเฉพาะ แต่เป็นการดีที่จะเข้าใจวิธีการดำเนินการนี้อย่างถูกต้องเพื่อหลีกเลี่ยงสถานการณ์ที่อาจมีคำเตือนเกิดขึ้นในอนาคต
คุณสามารถใช้วิธีการใด ๆ ต่อไปนี้เพื่อทำสิ่งนี้
df.loc[1, 'D'] = 12345
df.iloc[1, 3] = 12345
df.at[1, 'D'] = 12345
df.iat[1, 3] = 12345
คำถามที่ 3
ฉันพยายามเซ็ตค่าตามเงื่อนไขบางอย่าง ฉันมี DataFrame
A B C D E
1 9 3 5 2 4
2 7 6 8 8 1
ฉันต้องการกำหนดค่าใน "D" ให้กับ 123 เช่น "C" == 5 ฉันลองแล้ว
df2.loc[df2.C == 5, 'D'] = 123
ซึ่งดูเหมือนดี แต่ฉันยังคงได้รับ
SettingWithCopyWarning
! ฉันจะแก้ไขสิ่งนี้ได้อย่างไร
นี่อาจเป็นเพราะโค้ดสูงขึ้นในไพพ์ไลน์ของคุณ คุณสร้างdf2
จากสิ่งที่ใหญ่กว่าเช่น
df2 = df[df.A > 5]
? ในกรณีนี้การทำดัชนีบูลีนจะส่งคืนมุมมองดังนั้นdf2
จะอ้างอิงต้นฉบับ สิ่งที่คุณต้องทำคือกำหนดให้df2
กับสำเนา :
df2 = df[df.A > 5].copy()
# Or,
# df2 = df.loc[df.A > 5, :]
คำถามที่ 4
ฉันพยายามวางคอลัมน์ "C" แทน
A B C D E
1 9 3 5 2 4
2 7 6 8 8 1
แต่การใช้
df2.drop('C', axis=1, inplace=True)
SettingWithCopyWarning
พ่น ทำไมสิ่งนี้จึงเกิดขึ้น
นี่เป็นเพราะdf2
ต้องถูกสร้างเป็นมุมมองจากการดำเนินการแบ่งส่วนอื่นเช่น
df2 = df[df.A > 5]
วิธีการแก้ปัญหาที่นี่คือการอย่างใดอย่างหนึ่งทำcopy()
ของdf
หรือการใช้loc
เป็นมาก่อน