Di balik setiap penghematan memori yang kita ukur selama ini bekerja satu aturan inti pandas 3.0: Copy-on-Write. Aturannya sederhana untuk dibaca — setiap hasil operasi pandas berperilaku seperti salinan yang berdiri sendiri, dan salinan fisik baru dikerjakan hanya ketika salah satu sisi benar-benar ditulis. Konsekuensinya luas: kode lama yang mengandalkan mutasi lewat referensi berhenti bekerja, sementara pandas berhenti membuat salinan defensif di setiap seleksi. Chapter ini menguji perilaku tersebut baris demi baris: chained assignment, kolom yang disimpan, hasil seleksi, to_numpy(), aliasing, dan .copy().

Aturan Dasar Copy-on-Write #

Copy-on-Write membagi operasi menjadi dua fase. Fase baca — mengambil kolom, memfilter baris, mengubah ke array — cukup mengamati memori yang sama, sehingga murah. Fase tulis memicu salinan hanya pada bagian yang ditulis, sehingga objek lain yang menunjuk memori yang sama tetap utuh. Karena salinan selalu “tertunda”, kita tidak perlu menebak lagi apakah sebuah seleksi mengembalikan view atau salinan: dari sisi hasil, semuanya berperilaku seperti salinan. Peringatan lama SettingWithCopyWarning sudah tidak ada; yang muncul di pandas 3.0 adalah ChainedAssignmentError berbentuk warning.

Mengujinya dengan Kode #

Skrip berikut memakai tabel lima baris agar setiap perubahan terlihat jelas; bagian chained assignment ditangkap lewat modul warnings supaya skrip tetap bersih:

python
import warnings

import pandas as pd

df = pd.DataFrame(
    {
        "kota": ["Jakarta", "Bandung", "Surabaya", "Medan", "Semarang"],
        "jumlah": [2, 5, 3, 8, 1],
    }
)
print("df awal:")
print(df)
print()

# 1. Chained assignment: tidak berlaku di pandas 3.0
with warnings.catch_warnings(record=True) as rekaman:
    warnings.simplefilter("always")
    df["jumlah"][0] = 99
print("jumlah warning:", len(rekaman))
print("kategori:", rekaman[0].category.__name__)
print("pesan:", str(rekaman[0].message).splitlines()[0])
print("df['jumlah'] tetap:", df["jumlah"].tolist())
print()

# 2. Pola yang benar: menulis lewat .loc
df.loc[0, "jumlah"] = 99
print("setelah df.loc[0, 'jumlah'] = 99:", df["jumlah"].tolist())
print()

# 3. Mengubah kolom yang disimpan tidak menyentuh df
s = df["jumlah"]
s.iloc[1] = -5
print("s berubah     :", s.tolist())
print("df tetap      :", df["jumlah"].tolist())
print()

# 4. Hasil seleksi berperilaku seperti salinan
sub = df[df["jumlah"] > 2]
sub.iloc[0, 1] = -99
print("sub berubah   :", sub["jumlah"].tolist())
print("df tetap      :", df["jumlah"].tolist())
print()

# 5. to_numpy(): view baca-saja untuk kolom satu dtype
arr = df["jumlah"].to_numpy()
print("to_numpy writeable:", arr.flags.writeable)
try:
    arr[0] = 1
except ValueError as e:
    print("mutasi view ->", e)
print()

# 6. df2 = df: masih satu objek yang sama
df2 = df
print("df2 is df:", df2 is df)
df2.loc[0, "jumlah"] = 500
print("df ikut berubah:", df["jumlah"].tolist())
print()

# 7. .copy() memisahkan dua objek
df3 = df.copy()
print("df3 is df:", df3 is df)
df3.loc[0, "jumlah"] = 7
print("df tetap:", df["jumlah"].tolist(), "| df3:", df3["jumlah"].tolist())

Output yang Dihasilkan:

text
df awal:
       kota  jumlah
0   Jakarta       2
1   Bandung       5
2  Surabaya       3
3     Medan       8
4  Semarang       1

jumlah warning: 1
kategori: ChainedAssignmentError
pesan: A value is being set on a copy of a DataFrame or Series through chained assignment.
df['jumlah'] tetap: [2, 5, 3, 8, 1]

setelah df.loc[0, 'jumlah'] = 99: [99, 5, 3, 8, 1]

s berubah     : [99, -5, 3, 8, 1]
df tetap      : [99, 5, 3, 8, 1]

sub berubah   : [-99, 5, 3, 8]
df tetap      : [99, 5, 3, 8, 1]

to_numpy writeable: False
mutasi view -> assignment destination is read-only

df2 is df: True
df ikut berubah: [500, 5, 3, 8, 1]

df3 is df: False
df tetap: [500, 5, 3, 8, 1] | df3: [7, 5, 3, 8, 1]

Mari kita telaah baris-baris pentingnya:

  1. Baris 16-22 (chained assignment): df["jumlah"][0] = 99 memunculkan satu warning berkategori ChainedAssignmentError dengan pesan “A value is being set on a copy of a DataFrame or Series through chained assignment.” — dan nilai df tetap [2, 5, 3, 8, 1]. Penulisan seperti ini tidak berlaku apa pun di pandas 3.0.
  2. Baris 26-27 (pola yang benar): df.loc[0, "jumlah"] = 99 menulis langsung pada objek aslinya, sehingga kolom berubah menjadi [99, 5, 3, 8, 1]. Ini satu-satunya pola penulisan yang perlu dihafal.
  3. Baris 31-34 (kolom yang disimpan): s = df["jumlah"] lalu s.iloc[1] = -5 mengubah s sendirian; df tetap [99, 5, 3, 8, 1]. Di pandas 3.0, kolom hasil pengambilan berperilaku seperti salinan.
  4. Baris 38-41 (hasil seleksi): sub = df[df["jumlah"] > 2] menghasilkan tabel baru; mutasi sub tidak menyentuh df. Tidak ada lagi ambiguitas “kadang view kadang salinan” seperti era sebelum Copy-on-Write.
  5. Baris 45-50 (view baca-saja): to_numpy() pada kolom satu dtype mengembalikan view dengan writeable: False; percobaan menulis memunculkan ValueError: assignment destination is read-only. View tetap ada di jalur baca — dan justru itulah yang membuatnya murah.
  6. Baris 54-57 (aliasing Python): df2 = df tidak menyalin apa pun; df2 is df bernilai True, sehingga df2.loc[0, "jumlah"] = 500 mengubah objek yang sama dan df ikut berubah menjadi [500, 5, 3, 8, 1]. Copy-on-Write mengatur data di dalam pandas, bukan aliasing antar variabel Python.
  7. Baris 61-64 (copy() eksplisit): df3 = df.copy() menghasilkan objek lain (is bernilai False); mutasi df3 tidak memengaruhi df. Inilah cara sah memisahkan dua silsilah data.

Peta Perilaku di pandas 3.0 #

OperasiApa yang terjadiEfek ke objek asal
df.loc[0, "a"] = 99menulis langsungberubah
df["a"][0] = 99warning ChainedAssignmentError, tidak berlakutetap
s = df["a"]; s.iloc[0] = xs berubah sendiriantetap
sub = df[mask] lalu dimutasisub berubah sendiriantetap
df2 = dfalias satu objek yang samaikut berubah
df3 = df.copy()salinan berdiri sendiritetap
df["a"].to_numpy()view baca-sajatidak bisa ditulis

Pemahaman copy vs view ini melengkapi alat ukur kita. Chapter berikutnya memakai semua pengetahuan itu untuk keperluan yang paling sering ditunda: mengukur waktu eksekusi operasi pandas dengan benar.

Chapter Copy-on-Write di section Advanced Pandas membahas aturan ini sebagai perilaku bawaan pandas 3.0 secara menyeluruh.

Referensi resmi untuk pendalaman: panduan Copy-on-Write , pandas.DataFrame.copy , dan catatan rilis pandas 3.0.0 .