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:

python
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:

text
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     91

Mari kita telaah baris-baris pentingnya:

  1. Baris 6-7 (arr = skor.to_numpy()): DataFrame dua kolom int64 menghasilkan array int64 dengan writeable: False — ini view baca-saja, bukan salinan, sehingga konversinya murah untuk tabel besar.
  2. Baris 8-11 (try: arr[0, 0] = 0): percobaan mutasi memunculkan ValueError: assignment destination is read-only. Pesan ini muncul saat kita benar-benar menulis ke array, bukan saat konversi.
  3. Baris 15-16 (arr_copy = skor.to_numpy(copy=True)): argumen copy=True menghasilkan array salinan yang bebas diubah; mutasi arr_copy tidak menyentuh DataFrame — tabelnya tetap utuh pada output berikutnya.
  4. Baris 22 (.values): atribut lama .values berperilaku sama persis dengan to_numpy() tanpa argumen, termasuk aturan read-only; untuk kode baru, to_numpy() disarankan karena eksplisit.
  5. Baris 26-28 (DataFrame bercampur dtype): kolom nama (teks) dan nilai (angka) tidak bisa berbagi satu dtype angka, sehingga pandas menyusun array object; array ini adalah salinan yang writeable: True.
  6. Baris 34 (skor.to_numpy(dtype="float64")): parameter dtype memaksa konversi nilai menjadi float64; parameter ini hanya berlaku pada kolom numerik — memaksakan dtype="float64" pada kolom teks akan memunculkan ValueError.
  7. Baris 42-43 (pd.DataFrame(data, columns=...)): arah sebaliknya; array dua dimensi menjadi DataFrame, dan nama kolom diberikan lewat argumen columns karena array tidak membawa label.
  8. 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:

python
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 #

MethodHasilCatatan
df.to_numpy()ndarrayread-only bila satu dtype; salinan object bila campuran
df.to_numpy(copy=True)ndarray salinanbebas dimutasi, tak mempengaruhi DataFrame
df.valuesndarrayalias lama, perilaku sama dengan to_numpy()
df.to_records(index=False)structured arraymembawa nama kolom sebagai field
pd.DataFrame(array, columns=...)DataFramelabel harus diberikan manual
pd.DataFrame.from_records(list_of_dict)DataFramekolom 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 .