Kursus
SettingWithCopyWarning adalah peringatan yang dapat muncul di Pandas saat kita melakukan penugasan (assignment) pada sebuah DataFrame. Ini bisa terjadi ketika kita menggunakan assignment berantai (chained assignments) atau saat kita menggunakan DataFrame yang dibuat dari sebuah slice. Ini adalah sumber bug yang umum dalam kode Pandas yang mungkin pernah kita alami. Peringatan ini sulit di-debug karena bisa muncul pada kode yang tampaknya sudah benar.
Memahami SettingWithCopyWarning penting karena ini menandakan potensi masalah dalam manipulasi data. Peringatan ini menunjukkan bahwa kode Anda mungkin tidak mengubah data seperti yang dimaksudkan, yang dapat menimbulkan konsekuensi tak terduga dan bug terselubung yang sulit dilacak.
Dalam artikel ini, kita akan membahas SettingWithCopyWarning di pandas dan cara menghindarinya. Kita juga akan membahas masa depan Pandas dan bagaimana opsi copy_on_write akan mengubah cara kita bekerja dengan DataFrame.
View dan Copy pada DataFrame
Saat kita memilih sebuah slice dari DataFrame dan menugaskannya ke sebuah variabel, kita bisa mendapatkan view atau salinan DataFrame yang baru.
Pada view, memori antara kedua DataFrame dibagi bersama. Artinya, mengubah nilai dari sebuah sel yang ada di kedua DataFrame akan mengubah keduanya.

Pada copy, memori baru dialokasikan, dan dibuat DataFrame independen dengan nilai yang sama seperti yang asli. Dalam kasus ini, kedua DataFrame adalah entitas terpisah, sehingga mengubah nilai di salah satunya tidak memengaruhi yang lain.

Pandas berusaha menghindari pembuatan salinan jika memungkinkan untuk mengoptimalkan kinerja. Namun, tidak mungkin diprediksi sebelumnya apakah kita akan mendapatkan view atau copy. SettingWithCopyWarning akan muncul setiap kali kita menetapkan nilai pada sebuah DataFrame yang tidak jelas apakah itu copy atau view dari DataFrame lain.
Memahami SettingWithCopyWarning dengan Data Nyata
Kita akan menggunakan dataset Kaggle Real Estate Data London 2024 untuk mempelajari bagaimana SettingWithCopyWarning terjadi dan cara memperbaikinya.
Dataset ini berisi data properti terbaru dari London. Berikut gambaran umum kolom-kolom yang ada dalam dataset:
addedOn: Tanggal saat listing ditambahkan.title: Judul listing.descriptionHtml: Deskripsi listing dalam bentuk HTML.propertyType: Jenis properti. Nilainya akan"Not Specified"jika jenisnya tidak ditentukan.sizeSqFeetMax: Ukuran maksimum dalam satuan kaki persegi.bedrooms: Jumlah kamar tidur.listingUpdatedReason: Alasan pembaruan listing (misalnya, listing baru, penurunan harga).price: Harga listing dalam poundsterling.
Contoh dengan variabel sementara eksplisit
Misalkan kita diberi tahu bahwa properti dengan jenis properti yang tidak ditentukan adalah rumah. Karena itu, kita ingin memperbarui semua baris dengan propertyType sama dengan "Not Specified" menjadi "House". Salah satu cara untuk melakukannya adalah memfilter baris dengan jenis properti yang tidak ditentukan ke dalam variabel DataFrame sementara dan memperbarui nilai kolom propertyType seperti ini:
import pandas as pd
dataset_name = "realestate_data_london_2024_nov.csv"
df = pd.read_csv(dataset_name)
# Obtain all rows with unspecified property type
no_property_type = df[df["propertyType"] == "Not Specified"]
# Update the property type to “House” on those rows
no_property_type["propertyType"] = "House"
Menjalankan kode ini akan membuat pandas menampilkan SettingWithCopyWarning:
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
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
no_property_type["propertyType"] = "House"
Alasannya adalah karena pandas tidak dapat mengetahui apakah DataFrame no_property_type merupakan view atau copy dari df.

Ini menjadi masalah karena perilaku kode berikutnya bisa sangat berbeda tergantung apakah itu view atau copy.
Dalam contoh ini, tujuan kita adalah mengubah DataFrame asli. Ini hanya akan terjadi jika no_property_type adalah view. Jika sisa kode kita mengasumsikan bahwa df telah diubah, asumsi itu bisa salah karena tidak ada cara untuk menjamin hal tersebut. Karena perilaku yang tidak pasti ini, Pandas memunculkan peringatan untuk memberi tahu kita.
Bahkan jika kode kita berjalan dengan benar karena kita mendapatkan view, pada eksekusi berikutnya kita bisa saja mendapatkan copy, dan kode tidak akan bekerja seperti yang diharapkan. Oleh karena itu, penting untuk tidak mengabaikan peringatan ini dan memastikan kode kita selalu melakukan apa yang kita inginkan.
Contoh dengan variabel sementara tersembunyi
Pada contoh sebelumnya, jelas bahwa sebuah variabel sementara digunakan karena kita secara eksplisit menugaskan sebagian DataFrame ke variabel bernama no_property_type.
Namun, dalam beberapa kasus, hal ini tidak begitu eksplisit. Contoh paling umum di mana SettingWithCopyWarning terjadi adalah pada chained indexing. Misalkan kita mengganti dua baris terakhir dengan satu baris:
df[df["propertyType"] == "Not Specified"]["propertyType"] = "House"
Sekilas, tampaknya tidak ada variabel sementara yang dibuat. Namun, mengeksekusinya juga menghasilkan SettingWithCopyWarning.
Cara kode ini dieksekusi adalah:
df[df["propertyType"] == "Not Specified"]dievaluasi dan disimpan sementara ke memori.- Indeks
["propertyType"]pada lokasi memori sementara tersebut diakses.

Akses indeks dievaluasi satu per satu, sehingga chained indexing menghasilkan peringatan yang sama karena kita tidak tahu apakah hasil antara tersebut merupakan view atau copy. Kode di atas pada dasarnya sama dengan melakukan:
tmp = df[df["propertyType"] == "Not Specified"]
tmp["propertyType"] = "House"
Contoh ini sering disebut chained indexing karena kita merangkai akses indeks menggunakan []. Pertama, kita mengakses [df["propertyType"] == "Not Specified"] lalu ["propertyType"].

Cara Mengatasi SettingWithCopyWarning
Mari pelajari cara menulis kode agar tidak ada ambiguitas dan SettingWithCopyWarning tidak terpicu. Kita telah mempelajari bahwa peringatan ini muncul karena adanya ambiguitas apakah sebuah DataFrame adalah view atau copy dari DataFrame lain.
Cara memperbaikinya adalah memastikan setiap DataFrame yang kita buat benar-benar copy jika kita menginginkannya sebagai copy atau view jika kita menginginkannya sebagai view.
Mengubah DataFrame asli dengan aman menggunakan loc
Mari perbaiki kode dari contoh di atas saat kita ingin mengubah DataFrame asli. Untuk menghindari penggunaan variabel sementara, gunakan properti indexer loc.
df.loc[df["propertyType"] == "Not Specified", "propertyType"] = "House"
Dengan kode ini, kita bertindak langsung pada DataFrame df asli melalui properti indexer loc, sehingga tidak diperlukan variabel perantara. Inilah yang perlu kita lakukan ketika kita ingin langsung memodifikasi DataFrame asli.
Sekilas ini mungkin terlihat seperti chained indexing karena masih ada parameter, tetapi sebenarnya bukan. Yang menentukan setiap indexing adalah tanda kurung siku [].

Perlu dicatat bahwa penggunaan loc hanya aman jika kita langsung menetapkan nilai, seperti yang kita lakukan di atas. Jika kita malah menggunakan variabel sementara, kita kembali pada masalah yang sama. Berikut dua contoh kode yang tidak memperbaiki masalah:
- Menggunakan
locdengan variabel sementara:
# Menggunakan loc ditambah variabel sementara tidak memperbaiki masalah
no_property_type = df.loc[df["propertyType"] == "Not Specified"]
no_property_type["propertyType"] = "House"
- Menggunakan
locbersama dengan indexing (sama seperti chained indexing):
# Menggunakan loc lalu indexing sama saja dengan chained indexing
df.loc[df["propertyType"] == "Not Specified"]["propertyType"] = "House"
Kedua contoh ini sering membingungkan karena ada anggapan umum bahwa selama ada loc, kita sedang memodifikasi data asli. Ini tidak benar. Satu-satunya cara untuk memastikan bahwa nilai ditetapkan ke DataFrame asli adalah menetapkannya langsung menggunakan satu loc tanpa indexing terpisah.
Bekerja dengan aman pada salinan DataFrame asli menggunakan copy()
Ketika kita ingin memastikan bahwa kita beroperasi pada salinan DataFrame, kita harus menggunakan metode .copy().
Misalkan kita diminta menganalisis harga per kaki persegi dari properti. Kita tidak ingin mengubah data asli. Tujuannya adalah membuat DataFrame baru dengan hasil analisis untuk dikirim ke tim lain.
Langkah pertama adalah memfilter beberapa baris dan membersihkan data. Secara khusus, kita perlu:
- Menghapus baris di mana
sizeSqFeetMaxtidak terdefinisi. - Menghapus baris di mana
priceadalah"POA"(price on application). - Mengonversi harga menjadi nilai numerik (di dataset asli, harga berupa string dengan format:
"£25,000,000")
Kita dapat melakukan langkah-langkah di atas menggunakan kode berikut:
# 1. Filter out all properties without a size or a price
properties_with_size_and_price = df[df["sizeSqFeetMax"].notna() & (df["price"] != "POA")]
# 2. Remove the £ and , characters from the price columns
properties_with_size_and_price["price"] = properties_with_size_and_price["price"].str.replace("£", "", regex=False).str.replace(",", "", regex=False)
# 3. Convert the price column to numeric values
properties_with_size_and_price["price"] = pd.to_numeric(properties_with_size_and_price["price"])
Untuk menghitung harga per kaki persegi, kita membuat kolom baru dengan nilai hasil pembagian kolom price dengan kolom sizeSqFeetMax:
properties_with_size_and_price["pricePerSqFt"] = properties_with_size_and_price["price"] / properties_with_size_and_price["sizeSqFeetMax"]
Jika kita menjalankan kode ini, kita kembali mendapatkan SettingWithCopyWarning. Ini tidak mengejutkan karena kita secara eksplisit membuat dan memodifikasi variabel DataFrame sementara properties_with_size_and_price.
Karena kita ingin bekerja pada salinan data, bukan DataFrame asli, kita bisa memperbaiki masalah dengan memastikan bahwa properties_with_size_and_price adalah salinan DataFrame baru, bukan view, dengan menggunakan metode .copy() pada baris pertama:
properties_with_size_and_price = df[df["sizeSqFeetMax"].notna() & (df["price"] != "POA")].copy()
Menambahkan kolom baru dengan aman
Membuat kolom baru berperilaku sama seperti menetapkan nilai. Kapan pun ada ambiguitas apakah kita bekerja dengan copy atau view, pandas akan memunculkan SettingWithCopyWarning.
Jika kita ingin bekerja dengan salinan data, kita harus menyalinnya secara eksplisit menggunakan metode .copy(). Setelah itu, kita bebas menetapkan kolom baru dengan cara apa pun. Ini yang kita lakukan saat membuat kolom pricePerSqFt pada contoh sebelumnya.
Di sisi lain, jika kita ingin memodifikasi DataFrame asli ada dua kasus yang perlu dipertimbangkan.
- Jika kolom baru mencakup setiap baris, kita dapat langsung memodifikasi DataFrame asli. Ini tidak akan menyebabkan peringatan karena kita tidak akan memilih subset baris. Misalnya, kita bisa menambahkan kolom
noteuntuk setiap baris di mana jenis rumah tidak ada:
df["notes"] = df["propertyType"].apply(lambda house_type: "Missing house type" if house_type == "Not Specified" else "")
- Jika kolom baru hanya mendefinisikan nilai untuk sebagian baris, maka kita dapat menggunakan properti indexer
loc. Misalnya:
df.loc[df["propertyType"] == "Not Specified", "notes"] = "Missing house type"
Perhatikan bahwa dalam kasus ini, nilai pada kolom yang tidak dipilih akan tidak terdefinisi sehingga pendekatan pertama lebih disukai karena memungkinkan kita menentukan nilai untuk setiap baris.
Kesalahan SettingWithCopyWarning di Pandas 3.0
Saat ini, SettingWithCopyWarning hanyalah peringatan, bukan kesalahan. Kode kita tetap dieksekusi, dan Pandas hanya memberi tahu kita agar berhati-hati.
Menurut dokumentasi resmi Pandas, SettingWithCopyWarning tidak akan digunakan lagi mulai versi 3.0 dan akan digantikan oleh kesalahan sesungguhnya secara default, menerapkan standar kode yang lebih ketat.
Agar kode kita tetap kompatibel dengan versi pandas mendatang, disarankan untuk segera memperbaruinya agar memunculkan error alih-alih peringatan.
Ini dilakukan dengan mengatur opsi berikut setelah mengimpor pandas:
import pandas as pd
pd.options.mode.copy_on_write = True
Menambahkan ini pada kode yang ada akan memastikan kita menangani setiap penugasan ambigu dalam kode dan memastikan kode tetap berfungsi saat kita memperbarui ke pandas 3.0.
Kesimpulan
SettingWithCopyWarning terjadi setiap kali kode kita menimbulkan ambiguitas apakah nilai yang kita ubah berasal dari view atau copy. Kita dapat memperbaikinya dengan selalu tegas mengenai apa yang kita inginkan:
- Jika kita ingin bekerja dengan copy, kita harus menyalinnya secara eksplisit menggunakan metode
copy(). - Jika kita ingin memodifikasi DataFrame asli, kita harus menggunakan properti indexer
locdan menetapkan nilai secara langsung saat mengakses data tanpa menggunakan variabel perantara.
Meskipun bukan error, kita tidak boleh mengabaikan peringatan ini karena dapat menimbulkan hasil yang tidak terduga. Selain itu, mulai Pandas 3.0, ini akan menjadi error secara default, sehingga kita perlu menyiapkan kode untuk masa depan dengan mengaktifkan Copy-on-Write pada kode saat ini menggunakan pd.options.mode.copy_on_write = True. Ini akan memastikan kode tetap berfungsi untuk versi Pandas mendatang.
Untuk mempelajari lebih lanjut tentang Pandas, pertimbangkan sumber berikut:
FAQs
Mengapa saya mendapatkan SettingWithCopyWarning meskipun sudah menggunakan .loc?
Menggunakan loc selalu aman dan tidak akan memicu peringatan maupun menyebabkan perilaku tak terduga, dengan catatan df tidak dibuat dari DataFrame lain. Misalnya, jika df adalah DataFrame yang dibuat dari membaca dataset, maka itu aman. Namun, jika itu adalah hasil perhitungan perantara, maka hanya aman jika DataFrame tersebut disalin menggunakan metode copy().
Bagaimana cara menonaktifkan SettingWithCopyWarning?
Kita dapat menggunakan paket warnings dan menambahkan kode:
import warnings
warnings.simplefilter(action='ignore', category=pd.errors.SettingWithCopyWarning)
Namun, sangat tidak disarankan untuk menekan atau mengabaikan peringatan ini, karena ini berarti kode Anda mungkin tidak melakukan apa yang Anda kira.
Bagaimana cara mengabaikan SettingWithCopyWarning?
Kita dapat mengabaikan peringatan sementara menggunakan paket warnings dan membungkus kode kita dengan:
import warnings
with warnings.catch_warnings():
warnings.simplefilter("ignore", category=pd.errors.SettingWithCopyWarning)
# Our code goes here
Namun, sangat tidak disarankan untuk menekan atau mengabaikan peringatan ini, karena ini berarti kode Anda mungkin tidak melakukan apa yang Anda kira.
Bagaimana cara memperbaiki SettingWithCopyWarning saat menambahkan kolom baru?
Memperbaiki peringatan saat menambahkan kolom dilakukan dengan cara yang sama seperti saat menetapkan nilai. Pastikan untuk menyalin DataFrame terlebih dahulu jika Anda bermaksud bekerja pada salinan data atau gunakan loc.
Saya sudah mengikuti panduan ini, tetapi masih mendapatkan SettingWithCopyWarning. Bagaimana cara memperbaikinya?
Pada versi pandas terbaru, mengikuti praktik dari tutorial ini seharusnya tidak memicu peringatan apa pun.
Jika Anda menggunakan versi lama, kami menyarankan untuk memperbaruinya ke versi stabil terbaru. Versi lama diketahui memiliki bug terkait peringatan ini dan dapat memicunya meskipun tidak ada masalah pada kode.
Jika versi pandas sudah terbaru, kemungkinan masalah yang mendasari adalah kasus chained indexing yang tidak terlihat jelas. Tinjau dengan saksama bagaimana DataFrame dibuat.


