Chapter Dasar Pandas sudah membuat kita nyaman memakai DataFrame hasil read_csv(). Akan tetapi, nyaman memakai belum berarti paham: begitu mulai menyaring baris, menambah kolom, dan membaca sel tunggal, kebingungan klasik muncul — kapan memakai [], loc, atau iloc, dan mengapa penugasan kadang diam-diam gagal. Chapter ini membedah anatomi DataFrame sampai ke tuasnya, lalu menutup satu per satu titik kebingungan tersebut.
Membangun DataFrame dari Nol #
Dua cara membangun DataFrame paling umum: dictionary of lists (kolom demi kolom) dan list of dictionaries (baris demi baris). Keduanya menghasilkan DataFrame identik:
import pandas as pd
data_kolom = {
"produk": ["Kaos Polos", "Kemeja Flanel", "Sepatu Sneakers"],
"harga": [95000, 185000, 450000],
"stok": [40, 12, 8],
}
df_kolom = pd.DataFrame(data_kolom)
print(df_kolom)
print()
data_baris = [
{"produk": "Kaos Polos", "harga": 95000, "stok": 40},
{"produk": "Kemeja Flanel", "harga": 185000, "stok": 12},
{"produk": "Sepatu Sneakers", "harga": 450000, "stok": 8},
]
df_baris = pd.DataFrame(data_baris)
print(df_baris)Output yang Dihasilkan:
produk harga stok
0 Kaos Polos 95000 40
1 Kemeja Flanel 185000 12
2 Sepatu Sneakers 450000 8
produk harga stok
0 Kaos Polos 95000 40
1 Kemeja Flanel 185000 12
2 Sepatu Sneakers 450000 8Pilih berdasarkan arah datanya: data hasil loop atau hasil API biasanya terkumpul sebagai dictionary per baris (pakai list of dicts), sedangkan data yang disusun per kolom lebih alami sebagai dictionary of lists.
Index: Alamat Setiap Baris #
Angka 0, 1, 2 di tepi kiri kedua DataFrame di atas adalah index — RangeIndex otomatis yang berperan sebagai alamat baris, terlihat juga pada info() di chapter sebelumnya. Alamat ini bisa diganti label yang lebih bermakna lewat set_index():
df_indeks = df_kolom.set_index("produk")
print(df_indeks)
print()
print(df_indeks.loc["Kemeja Flanel", "harga"])Output yang Dihasilkan:
harga stok
produk
Kaos Polos 95000 40
Kemeja Flanel 185000 12
Sepatu Sneakers 450000 8
185000Setelah set_index("produk"), nama produk menjadi alamat baris, dan df_indeks.loc["Kemeja Flanel", "harga"] membaca sel dengan dua label — persis seperti mencari nilai pada tabel dua dimensi berjudul.
loc vs iloc vs [] #
Inilah sumber kebingungan nomor satu pemula pandas. Tiga pintu akses tersebut punya peran berbeda:
| Akses | Membaca berdasarkan | Rentang slicing | Kegunaan utama |
|---|---|---|---|
df["harga"] | nama kolom | tidak berlaku | memilih kolom |
df.loc[baris, kolom] | label index dan kolom | ujung kanan ikut | membaca/menulis sel terlabel |
df.iloc[baris, kolom] | posisi integer mulai 0 | ujung kanan dipotong | membaca/menulis sel berurutan |
Ketiganya bekerja pada DataFrame yang sama:
df = pd.DataFrame({
"produk": ["Kaos Polos", "Kemeja Flanel", "Sepatu Sneakers", "Jaket Bomber"],
"kategori": ["Fashion", "Fashion", "Fashion", "Fashion"],
"harga": [95000, 185000, 450000, 520000],
"stok": [40, 12, 8, 0],
})
print(df)
print()
print("df['harga']:")
print(df["harga"])
print()
print("df.loc[1, 'harga'] :", df.loc[1, "harga"])
print("df.iloc[1, 2] :", df.iloc[1, 2])
print()
print("df.loc[:2, ['produk', 'harga']]:")
print(df.loc[:2, ["produk", "harga"]])
print()
print("df.iloc[:2, [0, 3]]:")
print(df.iloc[:2, [0, 3]])Output yang Dihasilkan:
produk kategori harga stok
0 Kaos Polos Fashion 95000 40
1 Kemeja Flanel Fashion 185000 12
2 Sepatu Sneakers Fashion 450000 8
3 Jaket Bomber Fashion 520000 0
df['harga']:
0 95000
1 185000
2 450000
3 520000
Name: harga, dtype: int64
df.loc[1, 'harga'] : 185000
df.iloc[1, 2] : 185000
df.loc[:2, ['produk', 'harga']]:
produk harga
0 Kaos Polos 95000
1 Kemeja Flanel 185000
2 Sepatu Sneakers 450000
df.iloc[:2, [0, 3]]:
produk stok
0 Kaos Polos 40
1 Kemeja Flanel 12Perhatikan hasil dua slicing terakhir: df.loc[:2, ...] mengembalikan tiga baris karena label 2 ikut, sedangkan df.iloc[:2, ...] mengembalikan dua baris karena posisi 2 dipotong — persis perilaku slicing list Python. Pada DataFrame dengan RangeIndex, label dan posisi kebetulan sama, sehingga keduanya menunjuk baris yang sama di contoh pertama; begitu index diganti label (set_index), keduanya langsung berbeda makna.
Menyaring Baris dan Mengubah Kolom #
Penyaringan baris memakai boolean expression langsung di dalam kurung siku. Setelah tersaring, kolom baru ditambahkan dengan penugasan biasa, kolom dibuang dengan drop(), dan nama dirapikan dengan rename():
df = pd.DataFrame({
"produk": ["Kaos Polos", "Kemeja Flanel", "Sepatu Sneakers", "Jaket Bomber"],
"harga": [95000, 185000, 450000, 520000],
"stok": [40, 12, 8, 0],
})
print(df[df["stok"] < 10])
print()
df["nilai_stok"] = df["harga"] * df["stok"]
print(df)Output yang Dihasilkan:
produk harga stok
2 Sepatu Sneakers 450000 8
3 Jaket Bomber 520000 0
produk harga stok nilai_stok
0 Kaos Polos 95000 40 3800000
1 Kemeja Flanel 185000 12 2220000
2 Sepatu Sneakers 450000 8 3600000
3 Jaket Bomber 520000 0 0Ekspresi df["stok"] < 10 menghasilkan Series boolean; df[...] dengan Series boolean menyaring baris yang bernilai True. Kolom nilai_stok dihitung vektorial — harga kali stok untuk seluruh baris sekaligus, persis pola array NumPy dari chapter Dasar NumPy. Lanjut ke operasi bentuk tabel:
df = df.drop(columns=["nilai_stok"])
df = df.rename(columns={"produk": "nama_produk"})
df = df.set_index("nama_produk")
print(df)Output yang Dihasilkan:
harga stok
nama_produk
Kaos Polos 95000 40
Kemeja Flanel 185000 12
Sepatu Sneakers 450000 8
Jaket Bomber 520000 0Metode drop(), rename(), dan set_index() semuanya mengembalikan DataFrame baru; penugasan df = df.... itulah yang membuat perubahan menetap.
Jebakan Chained Indexing #
Satu jebakan wajib dipecahkan sebelum chapter pembersihan. Perhatikan kode yang tampak wajar berikut, yang mencoba menyetok habis produk dengan stok di bawah enam:
import pandas as pd
df = pd.DataFrame({
"produk": ["Kaos Polos", "Kemeja Flanel", "Jaket Bomber"],
"stok": [10, 5, 8],
})
print("Stok sebelum :", df["stok"].tolist())
df[df["stok"] < 6]["stok"] = 0
print("Stok sesudah chained :", df["stok"].tolist())
df.loc[df["stok"] < 6, "stok"] = 0
print("Stok sesudah .loc :", df["stok"].tolist())Output yang Dihasilkan:
Stok sebelum : [10, 5, 8]
Stok sesudah chained : [10, 5, 8]
Stok sesudah .loc : [10, 0, 8]Baris df[df["stok"] < 6]["stok"] = 0 gagal diam-diam: stok Kemeja Flanel tetap 5. Mekanismenya: df[df["stok"] < 6] lebih dulu membuat DataFrame baru, lalu ["stok"] = 0 menulis pada salinan tersebut — DataFrame asli tidak pernah disentuh. pandas bahkan menampilkan SettingWithCopyWarning di terminal sebagai peringatan:
SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value insteadBaris df.loc[df["stok"] < 6, "stok"] = 0 melakukan penyaringan dan penugasan dalam satu langkah pada objek yang sama, sehingga perubahan menetap. Inilah mengapa konvensi resmi pandas merekomendasikan satu operasi .loc tunggal setiap kali kita membaca dan menulis sekaligus.