Sebuah pertanyaan muncul hampir di setiap proyek pandas: jika kita menyimpan hasil seleksi ke variabel lain lalu mengubahnya, apakah data asli ikut berubah? Sejak pandas 3.0, jawabannya konsisten berkat perilaku Copy-on-Write yang selalu aktif. Prinsipnya sederhana: setiap hasil indexing atau method berperilaku seperti salinan, sehingga memodifikasi hasil tidak pernah menyentuh data asal. Chapter ini menguji prinsip tersebut dengan kode, membahas satu-satunya pengecualian berupa alias, dan menunjukkan kapan .copy() benar-benar dibutuhkan.

Aturan Dasar Copy-on-Write #

Copy-on-Write menunda penyalinan memori sampai ada modifikasi yang benar-benar membutuhkannya. Bagi kita sebagai pengguna, efeknya terasa sebagai jaminan: hasil df[[“kolom”]], hasil filter, hasil sort_values(), dan hasil method lain berperilaku seperti DataFrame atau Series yang berdiri sendiri. Mengubah hasil tersebut tidak pernah memengaruhi data asalnya. Pendekatan ini menghemat memori sekaligus menghilangkan perilaku tak terduga pada versi lama; rincian perubahannya tercatat di catatan rilis v3.0.0 .

Konsekuensinya, peringatan SettingWithCopyWarning yang dulu menghantui pengguna pandas tidak lagi ada di 3.0. Yang tersisa adalah aturan baru: penugasan berantai seperti df[“unit”][0] = 99 tidak mengubah df dan memunculkan warning ChainedAssignmentError. Pola yang benar selalu satu akses, yaitu df.loc[0, “unit”] = 99.

Tiga Cara Berbagi DataFrame #

Script berikut menguji tiga cara mendapatkan tabel dari penjualan: hasil seleksi kolom, alias berupa penugasan variabel biasa, dan salinan eksplisit dengan .copy(). Perhatikan nilai unit baris pertama pada setiap tahap.

python
import pandas as pd

penjualan = pd.DataFrame(
    {
        "produk": ["Kopi Gayo", "Teh Melati", "Gula Aren"],
        "unit": [12, 30, 25],
    }
)

hasil_seleksi = penjualan[["produk", "unit"]]
hasil_seleksi.loc[0, "unit"] = 999
print(penjualan)
print()
print(hasil_seleksi)
print()

alias = penjualan
alias.loc[0, "unit"] = 55
print(penjualan)
print()

stok_awal = penjualan.copy()
stok_awal.loc[0, "unit"] = 0
print(penjualan)
print()
print(stok_awal)

Output yang Dihasilkan:

text
       produk  unit
0   Kopi Gayo    12
1  Teh Melati    30
2   Gula Aren    25

       produk  unit
0   Kopi Gayo   999
1  Teh Melati    30
2   Gula Aren    25

       produk  unit
0   Kopi Gayo    55
1  Teh Melati    30
2   Gula Aren    25

       produk  unit
0   Kopi Gayo    55
1  Teh Melati    30
2   Gula Aren    25

       produk  unit
0   Kopi Gayo     0
1  Teh Melati    30
2   Gula Aren    25

Mari kita telaah kode di atas per bagian:

  1. Baris 10-15 (hasil seleksi): hasil_seleksi = penjualan[[“produk”, “unit”]] menghasilkan DataFrame baru; mengubah hasil_seleksi menjadi 999 tidak menyentuh penjualan yang tetap menampilkan 12. Hasil indexing adalah salinan.
  2. Baris 17-20 (alias): alias = penjualan tidak membuat tabel kedua; kedua nama menunjuk objek yang sama, sehingga alias.loc[0, “unit”] = 55 terlihat juga dari penjualan. Inilah pengecualian dari aturan Copy-on-Write.
  3. Baris 22-26 (salinan eksplisit): stok_awal = penjualan.copy() membuat snapshot independen; mengubah stok_awal menjadi 0 tidak memengaruhi penjualan yang tetap menampilkan 55.
  4. Kesimpulan: yang melindungi data asal adalah hasil indexing atau method serta .copy(); yang tidak melindungi adalah alias.

Kapan .copy() Dibutuhkan #

Tabel berikut merangkum kapan perubahan menyebar ke data asal:

Cara mendapat variabel baruPerubahan menyebar ke asal?
df2 = dfya, karena keduanya objek yang sama
df2 = df[[“produk”, “unit”]]tidak, hasil indexing berperilaku seperti salinan
df2 = df.sort_values(“kolom”)tidak, method mengembalikan DataFrame baru
df2 = df.copy()tidak, salinan eksplisit sejak awal

Method DataFrame.copy() menerima argumen opsional untuk menyalin index dan data secara terpisah, tetapi bentuk tanpa argumen sudah mencukupi untuk kebutuhan harian.

Pola paling umum yang membutuhkan .copy() adalah menyimpan snapshot sebelum transformasi berantai, misalnya df_bersih = df_mentah.copy() sebelum kolom-kolom mulai diubah, sehingga versi mentah tetap tersedia untuk pembanding. Sebaliknya, jangan menabur .copy() di setiap baris: Copy-on-Write sudah menangani kebutuhan salinan secara otomatis dan menundanya sampai benar-benar diperlukan, sehingga salinan manual berlebihan hanya memboroskan memori.

Memodifikasi dengan Pola Satu Akses .loc #

Untuk mengubah data langsung pada DataFrame asal, gunakan satu akses .loc: df.loc[0, “unit”] = 55 untuk satu sel, atau df.loc[mask, “unit”] = nilai_baru untuk sekelompok baris sekaligus. Pola ini jelas menunjuk baris dan kolom pada objek yang sama, sehingga tidak ada keraguan tentang data mana yang diubah.

Dengan pemahaman salinan ini, repertoar kita pada Section Manipulasi Data lengkap: menambah dan menghapus kolom, mengubah nilai, mengurutkan, menomori ulang index, mengonversi tipe, mengolah teks, serta menyalin data dengan benar. Semua keterampilan tersebut akan terpakai penuh pada Section berikutnya, Data Cleaning, yang menertibkan data tidak lengkap dan tidak konsisten.