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:
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:
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:
- Baris 16-22 (chained assignment):
df["jumlah"][0] = 99memunculkan satu warning berkategoriChainedAssignmentErrordengan pesan “A value is being set on a copy of a DataFrame or Series through chained assignment.” — dan nilaidftetap[2, 5, 3, 8, 1]. Penulisan seperti ini tidak berlaku apa pun di pandas 3.0. - Baris 26-27 (pola yang benar):
df.loc[0, "jumlah"] = 99menulis langsung pada objek aslinya, sehingga kolom berubah menjadi[99, 5, 3, 8, 1]. Ini satu-satunya pola penulisan yang perlu dihafal. - Baris 31-34 (kolom yang disimpan):
s = df["jumlah"]lalus.iloc[1] = -5mengubahssendirian;dftetap[99, 5, 3, 8, 1]. Di pandas 3.0, kolom hasil pengambilan berperilaku seperti salinan. - Baris 38-41 (hasil seleksi):
sub = df[df["jumlah"] > 2]menghasilkan tabel baru; mutasisubtidak menyentuhdf. Tidak ada lagi ambiguitas “kadang view kadang salinan” seperti era sebelum Copy-on-Write. - Baris 45-50 (view baca-saja):
to_numpy()pada kolom satu dtype mengembalikan view denganwriteable: False; percobaan menulis memunculkanValueError: assignment destination is read-only. View tetap ada di jalur baca — dan justru itulah yang membuatnya murah. - Baris 54-57 (aliasing Python):
df2 = dftidak menyalin apa pun;df2 is dfbernilaiTrue, sehinggadf2.loc[0, "jumlah"] = 500mengubah objek yang sama dandfikut berubah menjadi[500, 5, 3, 8, 1]. Copy-on-Write mengatur data di dalam pandas, bukan aliasing antar variabel Python. - Baris 61-64 (
copy()eksplisit):df3 = df.copy()menghasilkan objek lain (isbernilaiFalse); mutasidf3tidak memengaruhidf. Inilah cara sah memisahkan dua silsilah data.
Peta Perilaku di pandas 3.0 #
| Operasi | Apa yang terjadi | Efek ke objek asal |
|---|---|---|
df.loc[0, "a"] = 99 | menulis langsung | berubah |
df["a"][0] = 99 | warning ChainedAssignmentError, tidak berlaku | tetap |
s = df["a"]; s.iloc[0] = x | s berubah sendirian | tetap |
sub = df[mask] lalu dimutasi | sub berubah sendirian | tetap |
df2 = df | alias satu objek yang sama | ikut berubah |
df3 = df.copy() | salinan berdiri sendiri | tetap |
df["a"].to_numpy() | view baca-saja | tidak 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 .