SettingWithCopyWarning เรื่องเล็ก ๆ ที่อาจโดนมองข้าม

Sasiwut Chaiyadecha
2 min readJun 2, 2020

--

ขอเขียน Note ไว้หน่อยเถอะ ปัญหานี้แก้นานมาก มันเป็น Warning ที่เจออยู่บ่อย ๆ แต่ก็มองข้ามมันไปตลอด เพราะที่ผ่านมามันไม่มีผลกระทบอะไรกับการทำงาน หมายถึงว่าค่าต่าง ๆ ตัวเลขต่าง ๆ หรือ Table ที่เราต้องการให้มันเป็น ยังคงถูกต้องอยู่ แต่วันนี้ค้นพบว่า เวลาที่โปรแกรมมันเตือนว่า SettingWithCopyWarning มันก็มีความหมายของมัน

ไม่รู้ว่าจะเรียกมันว่าบั๊คหรือฟีเจอร์ดี ? หรือว่าเราแหละ… ที่เป็นบั๊ค

Problem

คือเรื่องมันเป็นแบบนี้… พอดีทำ Time series model อยู่ มาถึง Step ก่อนที่จะเริ่มสร้างโมเดล เราต้องมีการแบ่ง Train, Test เป็นปกติ ซึ่งมันก็ไม่ได้ยากเย็นอะไร เลยไม่ได้อยากใช้ Library ไง มันเขียนเองก็ได้ เลยเขียนโค้ดแบ่งเป็น 80/20

train = df.iloc[0:int(df.shape[0] * 0.8)]
test = df.iloc[int(df.shape[0] * 0.8):]
หน้าตาข้อมูลก็ได้ประมาณนี้ ต่างกันที่ช่วงเวลาที่ไม่เท่ากัน

แล้วคราวนี้ก็ทำโมเดลต่อ ก็มีจังหวะที่ต้องเปลี่ยนข้อมูลในรูปแบบ Percent change ก็ไม่ได้คิดอะไรมาก ใช้ .pct_change() ปกติ

train['Close'] = train['Close'].pct_change()

มันก็มี Warning เตือนขึ้นมาว่า

__main__: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

คือเคยเห็น Warning แบบนี้มาก่อนหน้านี้แล้ว เกิดขึ้นบ่อย ๆ จังหวะที่ตัดต่อ Table ก็ไม่ได้ใส่ใจอะไร แต่ว่าอะไรไม่รู้กลับไปเปิดตัวแปร df ขึ้นมาอีกรอบ

อ่าว งง !

ทำไม 2 Dataframes มันหน้าตาเหมือนกันอ่ะ มันต้องไม่เหมือนสิ เพราะว่าเรารัน .pct_change() บนเครื่องตัวแปร train เริ่มงงละ ทำใหม่อีกกี่รอบก็ไม่หาย

Reason

จนได้มาเจอกับกระทู้นี้ใน Stackoverflow มันเป็นการทำงานรูปแบบนึงของ Pandas dataframe ที่มันจะสร้าง “โซ่” ในการ Operation บางคำสั่ง ดังนั้นถึงแม้ว่าเราสร้างตัวแปรใหม่ยังไง ตัวแปรเดิมก็ได้รับผลกระทบไปด้วย

As mentioned by other answers, the SettingWithCopyWarning was created to flag "chained assignment" operations.

Solution

คำถามคือมันจะแก้ยังไง? ได้คำตอบจากในกระทู้นั้นแหละว่า ทางแก้หลัก ๆ มี 2 ทางด้วยกันคือ

  1. ใช้ Function .copy(deep = True) ผ่านเข้าไปในตัวแปรที่เอามารับใหม่ เพื่อที่จะทำให้ Operate แค่ตัวแปรที่เราใช้งาน
  2. เปลี่ยนที่ pd.options.mode.chained_assignment ที่ค่า Default มันจะเป็น warn เราก็เปลี่ยนให้เป็น None
train = df.iloc[0:int(df.shape[0] * 0.8)].copy(deep = True)

แล้วลองมารัน .pct_change() อีกรอบนึงบน Training dataset

train['Close'] = train['Close'].pct_change()

ตัวเลขใน df เดิมไม่เปลี่ยนไปแล้ววววว

เรื่องเล็ก ๆ น้อย ๆ แบบนี้บางทีก็ไม่ควรมองข้ามนะ เนี่ยวันนี้เลยได้ความรู้ใหม่เลย ขอจดไว้ในนี้หน่อยละกัน

--

--

Sasiwut Chaiyadecha
Sasiwut Chaiyadecha

No responses yet