Sebagian besar library dari Chapter-Chapter sebelumnya menerima DataFrame langsung, tetapi tidak semuanya. Fungsi Matplotlib murni seperti ax.bar() senang diberi ndarray, banyak kode aljabar linear menuntut array dua dimensi, dan antarmuka lama scikit-learn menerima array saja. Pada Chapter ini kita membahas tuntas pertukaran DataFrame ↔ array: method to_numpy(), atribut .values, aturan copy baru di pandas 3.0, serta arah sebaliknya dengan pd.DataFrame() dan from_records().
to_numpy() dan Aturan Copy di pandas 3.0 #
Sejak pandas 3.0, Copy-on-Write berlaku selalu: data pandas tidak boleh berubah lewat referensi luar tanpa sepengetahuan pandas. Konsekuensinya terasa di to_numpy(). Untuk DataFrame yang seluruh kolomnya bertipe sama, method ini mengembalikan view — array yang menunjuk memori yang sama dengan tabel — tetapi dalam keadaan read-only:
import pandas as pd
skor = pd.DataFrame({"quiz": [88, 79, 91], "ujian": [84, 90, 87]})
# 1. to_numpy() tanpa argumen: view baca-saja di bawah Copy-on-Write
arr = skor.to_numpy()
print("dtype:", arr.dtype, "| writeable:", arr.flags.writeable)
try:
arr[0, 0] = 0
except ValueError as e:
print("mutasi view ->", e)
print()
# 2. to_numpy(copy=True): array salinan yang terpisah
arr_copy = skor.to_numpy(copy=True)
arr_copy[0, 0] = 0
print("setelah mutasi arr_copy, DataFrame tetap utuh:")
print(skor)
print()
# 3. .values berperilaku sama dengan to_numpy()
print("values writeable:", skor.values.flags.writeable)
print()
# 4. Kolom bercampur dtype menghasilkan array object
campur = pd.DataFrame({"nama": ["Budi", "Siti"], "nilai": [88, 91]})
arr2 = campur.to_numpy()
print("dtype campuran:", arr2.dtype, "| writeable:", arr2.flags.writeable)
print("rec dtypes:", campur.to_records(index=False).dtype.names)
print(campur.to_records(index=False))
print()
# 5. Konversi dtype lewat parameter dtype (kolom numerik saja)
arr3 = skor.to_numpy(dtype="float64")
print("dtype paksa:", arr3.dtype)
print(arr3)
print()
# 6. Arah sebaliknya: ndarray menjadi DataFrame
import numpy as np
data = np.array([[40, 68000], [25, 35000]])
dari_arr = pd.DataFrame(data, columns=["jumlah", "harga"])
print(dari_arr)
dari_records = pd.DataFrame.from_records(
[{"nama": "Budi", "nilai": 88}, {"nama": "Siti", "nilai": 91}]
)
print(dari_records)Output yang Dihasilkan:
dtype: int64 | writeable: False
mutasi view -> assignment destination is read-only
setelah mutasi arr_copy, DataFrame tetap utuh:
quiz ujian
0 88 84
1 79 90
2 91 87
values writeable: False
dtype campuran: object | writeable: True
rec dtypes: ('nama', 'nilai')
[('Budi', 88) ('Siti', 91)]
dtype paksa: float64
[[88. 84.]
[79. 90.]
[91. 87.]]
jumlah harga
0 40 68000
1 25 35000
nama nilai
0 Budi 88
1 Siti 91Mari kita telaah baris-baris pentingnya:
- Baris 6-7 (
arr = skor.to_numpy()): DataFrame dua kolomint64menghasilkan arrayint64denganwriteable: False— ini view baca-saja, bukan salinan, sehingga konversinya murah untuk tabel besar. - Baris 8-11 (
try: arr[0, 0] = 0): percobaan mutasi memunculkanValueError: assignment destination is read-only. Pesan ini muncul saat kita benar-benar menulis ke array, bukan saat konversi. - Baris 15-16 (
arr_copy = skor.to_numpy(copy=True)): argumencopy=Truemenghasilkan array salinan yang bebas diubah; mutasiarr_copytidak menyentuh DataFrame — tabelnya tetap utuh pada output berikutnya. - Baris 22 (
.values): atribut lama.valuesberperilaku sama persis denganto_numpy()tanpa argumen, termasuk aturan read-only; untuk kode baru,to_numpy()disarankan karena eksplisit. - Baris 26-28 (DataFrame bercampur dtype): kolom
nama(teks) dannilai(angka) tidak bisa berbagi satu dtype angka, sehingga pandas menyusun arrayobject; array ini adalah salinan yangwriteable: True. - Baris 34 (
skor.to_numpy(dtype="float64")): parameterdtypememaksa konversi nilai menjadifloat64; parameter ini hanya berlaku pada kolom numerik — memaksakandtype="float64"pada kolom teks akan memunculkanValueError. - Baris 42-43 (
pd.DataFrame(data, columns=...)): arah sebaliknya; array dua dimensi menjadi DataFrame, dan nama kolom diberikan lewat argumencolumnskarena array tidak membawa label. - Baris 45-47 (
from_records([...])): list of dictionary menjadi DataFrame; nama kolom diambil dari key dictionary, sehingga bentuk data ini juga sering menjadi titik temu antara hasil API web dan pandas.
Aturan praktisnya sederhana: baca saja — pakai to_numpy() langsung; akan dimodifikasi — pakai to_numpy(copy=True).
to_records() dan Bentuk Antara #
Selain to_numpy(), pandas menyediakan to_records() yang menghasilkan structured array NumPy — array yang tiap elemennya berupa record dengan nama kolom sebagai field:
campur.to_records(index=False)
# rec.array([('Budi', 88), ('Siti', 91)],
# dtype=[('nama', 'O'), ('nilai', '<i8')])Bentuk ini dipakai antarmuka NumPy yang menuntut record bernama; pada contoh utama, baris 29-30 menampilkan nama-field-nya (nama, nilai) dan isinya. Karena index ikut disertakan secara bawaan, to_records(index=False) lazim dipakai agar index tidak menjadi field tambahan.
Peta Method Konversi #
| Method | Hasil | Catatan |
|---|---|---|
df.to_numpy() | ndarray | read-only bila satu dtype; salinan object bila campuran |
df.to_numpy(copy=True) | ndarray salinan | bebas dimutasi, tak mempengaruhi DataFrame |
df.values | ndarray | alias lama, perilaku sama dengan to_numpy() |
df.to_records(index=False) | structured array | membawa nama kolom sebagai field |
pd.DataFrame(array, columns=...) | DataFrame | label harus diberikan manual |
pd.DataFrame.from_records(list_of_dict) | DataFrame | kolom diambil dari key dictionary |
Dengan peta ini, kebutuhan “beri saya array-nya saja” selalu punya jawaban yang benar. Chapter berikutnya menutup Section ini dengan pemandangan yang lebih luas: format-format data lain — pyarrow.Table, Parquet, Feather, JSON, pickle — yang bisa ditinggali dan dihuni pandas.
Referensi resmi untuk pendalaman: pandas.DataFrame.to_numpy , pandas.DataFrame.from_records , dan panduan Copy-on-Write .