Data machine learning di dunia nyata jarang berbentuk array angka yang rapi; data datang sebagai tabel CSV dengan nama kolom, baris berlabel, dan tipe campuran — teks, angka, tanggal. Pandas dirancang persis untuk tabel semacam itu, dan hampir semua proyek machine learning dimulai dari DataFrame: memuat data, memeriksanya, menyaringnya, lalu menyerahkan hasilnya ke model sebagai matriks fitur X dan vektor target y. Chapter ini melatih alur tersebut pada tabel karyawan kecil yang kita bangun sendiri — tanpa file eksternal — mulai dari seleksi kolom hingga agregasi per kelompok, penggabungan tabel, dan deteksi data hilang.

DataFrame dan Series #

DataFrame adalah tabel dua dimensi: baris memiliki indeks, kolom memiliki nama, dan setiap kolom boleh bertipe berbeda (teks, bilangan bulat, desimal) — perbedaan utamanya dari ndarray yang harus homogen. Series adalah satu kolom DataFrame beserta indeksnya; memilih satu kolom menghasilkan Series, memilih beberapa kolom menghasilkan DataFrame baru. Struktur ini membuat kode terbaca: karyawan["gaji_juta"] lebih jelas daripada X[:, 3].

Fungsi df.info() adalah pemeriksaan pertama standar — fungsi tersebut mencetak jumlah baris, tipe tiap kolom, dan jumlah nilai terisi per kolom. File contoh di bawah mencetak ringkasan serupa secara manual agar keluarannya ringkas, lalu melanjutkan ke operasi inti: head() untuk mengintip data, seleksi kolom, penyaringan baris dengan kondisi boolean, pengurutan nlargest, pembuatan kolom baru, pembacaan CSV, dan describe() untuk statistik ringkas.

python
# DataFrame dasar: membangun, memilih kolom, menyaring, mengurutkan,
# membaca CSV dari teks, dan ringkasan statistik.

import io

import pandas as pd

karyawan = pd.DataFrame({
    "nama": ["Budi", "Siti", "Dewi", "Andi", "Rina", "Fajar", "Lina", "Hendra"],
    "departemen": ["Teknis", "Teknis", "Produksi", "Produksi",
                   "Pemasaran", "Pemasaran", "Teknis", "Produksi"],
    "pengalaman_tahun": [2, 5, 3, 7, 1, 4, 6, 2],
    "gaji_juta": [8.0, 12.5, 9.0, 15.0, 6.5, 10.0, 13.5, 8.5],
})

print("Lima baris pertama:")
print(karyawan.head())

print("\nInformasi ringkas:")
print("Jumlah baris :", len(karyawan))
print("Jumlah kolom :", karyawan.shape[1])
print("Nama kolom   :", list(karyawan.columns))

# Seleksi kolom: satu kolom -> Series, banyak kolom -> DataFrame
print("\nDua kolom dipilih (DataFrame):")
print(karyawan[["nama", "gaji_juta"]].to_string(index=False))

# Penyaringan baris dengan kondisi boolean
senior_teknis = karyawan[(karyawan["departemen"] == "Teknis")
                         & (karyawan["pengalaman_tahun"] >= 5)]
print("\nKaryawan Teknis berpengalaman >= 5 tahun:")
print(senior_teknis.to_string(index=False))

# Pengurutan berdasarkan kolom
print("\nLima gaji tertinggi:")
print(karyawan.nlargest(5, "gaji_juta")[["nama", "departemen", "gaji_juta"]]
      .to_string(index=False))

# Kolom baru hasil hitung antar kolom
karyawan["gaji_per_pengalaman"] = (karyawan["gaji_juta"]
                                   / karyawan["pengalaman_tahun"]).round(2)
print("\nKolom baru gaji_per_pengalaman:")
print(karyawan[["nama", "gaji_juta", "pengalaman_tahun", "gaji_per_pengalaman"]]
      .to_string(index=False))

# Di proyek nyata data biasanya dibaca dari file CSV.
# Di sini isi CSV ditulis sebagai teks agar contoh tetap mandiri.
csv_teks = """nama,departemen,gaji_juta
Budi,Teknis,8.0
Siti,Teknis,12.5
Dewi,Produksi,9.0
Andi,Produksi,15.0
"""
df_csv = pd.read_csv(io.StringIO(csv_teks))
print("\nHasil pd.read_csv dari teks (4 baris):")
print(df_csv.to_string(index=False))

print("\nRingkasan statistik kolom numerik:")
print(karyawan[["pengalaman_tahun", "gaji_juta"]].describe().round(2))

Output yang Dihasilkan:

text
Lima baris pertama:
   nama departemen  pengalaman_tahun  gaji_juta
0  Budi     Teknis                 2        8.0
1  Siti     Teknis                 5       12.5
2  Dewi   Produksi                 3        9.0
3  Andi   Produksi                 7       15.0
4  Rina  Pemasaran                 1        6.5

Informasi ringkas:
Jumlah baris : 8
Jumlah kolom : 4
Nama kolom   : ['nama', 'departemen', 'pengalaman_tahun', 'gaji_juta']

Dua kolom dipilih (DataFrame):
  nama  gaji_juta
  Budi        8.0
  Siti       12.5
  Dewi        9.0
  Andi       15.0
  Rina        6.5
 Fajar       10.0
  Lina       13.5
Hendra        8.5

Karyawan Teknis berpengalaman >= 5 tahun:
nama departemen  pengalaman_tahun  gaji_juta
Siti     Teknis                 5       12.5
Lina     Teknis                 6       13.5

Lima gaji tertinggi:
 nama departemen  gaji_juta
 Andi   Produksi       15.0
 Lina     Teknis       13.5
 Siti     Teknis       12.5
Fajar  Pemasaran       10.0
 Dewi   Produksi        9.0

Kolom baru gaji_per_pengalaman:
  nama  gaji_juta  pengalaman_tahun  gaji_per_pengalaman
  Budi        8.0                 2                 4.00
  Siti       12.5                 5                 2.50
  Dewi        9.0                 3                 3.00
  Andi       15.0                 7                 2.14
  Rina        6.5                 1                 6.50
 Fajar       10.0                 4                 2.50
  Lina       13.5                 6                 2.25
Hendra        8.5                 2                 4.25

Hasil pd.read_csv dari teks (4 baris):
nama departemen  gaji_juta
Budi     Teknis        8.0
Siti     Teknis       12.5
Dewi   Produksi        9.0
Andi   Produksi       15.0

Ringkasan statistik kolom numerik:
       pengalaman_tahun  gaji_juta
count              8.00       8.00
mean               3.75      10.38
std                2.12       2.97
min                1.00       6.50
25%                2.00       8.38
50%                3.50       9.50
75%                5.25      12.75
max                7.00      15.00

Mari kita telaah baris-baris kode yang berperan penting:

  1. Baris 8–14 (pd.DataFrame({...})): tabel dibangun dari dictionary — kunci menjadi nama kolom, nilai list menjadi isi kolom. Delapan karyawan fiktif kini punya empat kolom bertipe campuran.
  2. Baris 16–22 (head dan ringkasan): head() menampilkan lima baris pertama; len, shape, dan columns memberi peta struktur tanpa mencetak seluruh data.
  3. Baris 25–26 (seleksi kolom): daftar nama kolom di dalam kurung siku ganda menghasilkan DataFrame baru — hanya Budi sampai Hendra beserta gajinya yang tampil.
  4. Baris 29–32 (penyaringan): kondisi boolean pada kolom digabung dengan &, persis pola mask NumPy; hasilnya hanya Siti dan Lina yang memenuhi kriteria Teknis berpengalaman lima tahun ke atas.
  5. Baris 35–37 (nlargest): pengurutan praktis lima gaji tertinggi tanpa sort_values lalu slicing manual.
  6. Baris 40–44 (kolom baru): kolom gaji_per_pengalaman dihitung dari dua kolom lama dalam satu ekspresi vektor — pola dasar feature engineering.
  7. Baris 48–56 (CSV dari teks): pd.read_csv biasanya membaca file; di sini string CSV dibungkus io.StringIO agar contoh mandiri tanpa file eksternal, dengan perilaku parsing yang identik.
  8. Baris 58–59 (describe): ringkasan count, mean, std, hingga kuartil — perpanjangan langsung dari statistika deskriptif Chapter sebelumnya, kini per kolom.

GroupBy: Ringkas per Kelompok #

Pertanyaan analitik hampir selalu berbentuk “per kelompok”: berapa rata-rata gaji per departemen, berapa jumlah transaksi per kota. GroupBy bekerja dalam tiga fase — split membagi baris menurut nilai kolom kunci, apply menjalankan agregasi (mean, count, max) pada tiap kelompok, combine menyatukan hasil menjadi tabel baru dengan kelompok sebagai indeks. File agregasi-groupby.py juga mendemonstrasikan merge untuk menggabungkan dua tabel lewat kolom kunci bersama — mekanisme yang sama dengan join pada SQL — serta deteksi data hilang dengan isna.

python
# GroupBy dan merge: ringkasan per departemen serta penggabungan tabel,
# ditutup deteksi data hilang dengan isna.

import pandas as pd

karyawan = pd.DataFrame({
    "nama": ["Budi", "Siti", "Dewi", "Andi", "Rina", "Fajar", "Lina", "Hendra"],
    "departemen": ["Teknis", "Teknis", "Produksi", "Produksi",
                   "Pemasaran", "Pemasaran", "Teknis", "Produksi"],
    "pengalaman_tahun": [2.0, 5.0, 3.0, 7.0, 1.0, 4.0, 6.0, 2.0],
    "gaji_juta": [8.0, 12.5, 9.0, 15.0, 6.5, 10.0, 13.5, 8.5],
})

# Rata-rata gaji per departemen, diurutkan dari yang tertinggi
rata_gaji = karyawan.groupby("departemen")["gaji_juta"].mean().round(2)
print("Rata-rata gaji per departemen (juta rupiah):")
print(rata_gaji.sort_values(ascending=False))

# Jumlah karyawan per departemen
print("\nJumlah karyawan per departemen:")
print(karyawan.groupby("departemen").size().sort_values(ascending=False))

# Beberapa agregasi sekaligus dengan named aggregation
ringkasan = karyawan.groupby("departemen").agg(
    jumlah_karyawan=("nama", "count"),
    gaji_rata_rata=("gaji_juta", "mean"),
    gaji_tertinggi=("gaji_juta", "max"),
    pengalaman_rata=("pengalaman_tahun", "mean"),
).round(2)
print("\nRingkasan banyak agregasi per departemen:")
print(ringkasan)

# Merge: gabungkan tabel bonus per departemen ke tabel karyawan
bonus = pd.DataFrame({
    "departemen": ["Pemasaran", "Produksi", "Teknis"],
    "bonus_juta": [0.8, 1.2, 1.0],
})
gabungan = karyawan.merge(bonus, on="departemen", how="left")
gabungan["total_juta"] = (gabungan["gaji_juta"] + gabungan["bonus_juta"]).round(2)
print("\nHasil merge dengan tabel bonus:")
print(gabungan[["nama", "departemen", "gaji_juta", "bonus_juta", "total_juta"]]
      .to_string(index=False))

# Deteksi data hilang: satu nilai pengalaman sengaja dikosongkan
karyawan.loc[3, "pengalaman_tahun"] = float("nan")
print("\nJumlah nilai hilang per kolom:")
print(karyawan.isna().sum())
print("\nBaris yang memiliki nilai hilang:")
print(karyawan[karyawan["pengalaman_tahun"].isna()].to_string(index=False))

Output yang Dihasilkan:

text
Rata-rata gaji per departemen (juta rupiah):
departemen
Teknis       11.33
Produksi     10.83
Pemasaran     8.25
Name: gaji_juta, dtype: float64

Jumlah karyawan per departemen:
departemen
Produksi     3
Teknis       3
Pemasaran    2
dtype: int64

Ringkasan banyak agregasi per departemen:
            jumlah_karyawan  gaji_rata_rata  gaji_tertinggi  pengalaman_rata
departemen
Pemasaran                 2            8.25            10.0             2.50
Produksi                  3           10.83            15.0             4.00
Teknis                    3           11.33            13.5             4.33

Hasil merge dengan tabel bonus:
  nama departemen  gaji_juta  bonus_juta  total_juta
  Budi     Teknis        8.0         1.0         9.0
  Siti     Teknis       12.5         1.0        13.5
  Dewi   Produksi        9.0         1.2        10.2
  Andi   Produksi       15.0         1.2        16.2
  Rina  Pemasaran        6.5         0.8         7.3
 Fajar  Pemasaran       10.0         0.8        10.8
  Lina     Teknis       13.5         1.0        14.5
Hendra   Produksi        8.5         1.2         9.7

Jumlah nilai hilang per kolom:
nama                0
departemen          0
pengalaman_tahun    1
gaji_juta           0
dtype: int64

Baris yang memiliki nilai hilang:
nama departemen  pengalaman_tahun  gaji_juta
Andi   Produksi               NaN       15.0

Bedah kode di atas baris demi baris:

  1. Baris 15–17 (groupby ... mean): baris dibagi tiga kelompok departemen, rata-rata gaji_juta dihitung per kelompok, lalu sort_values mengurutkan Teknis (11,33) di depan Pemasaran (8,25).
  2. Baris 20–21 (size): menghitung anggota tiap kelompok — cara tercepat memeriksa class balance, kelak penting saat menangani data tidak seimbang.
  3. Baris 24–31 (agg bernama): satu panggilan menghasilkan empat kolom sekaligus; setiap kolom baru didefinisikan pasangan (kolom sumber, fungsi), hasilnya DataFrame ringkas per departemen.
  4. Baris 34–42 (merge): tabel bonus tiga baris digabungkan pada kolom departemen; how="left" menjaga seluruh baris karyawan tetap utuh. Kolom total_juta lalu dihitung dari hasil gabungan — pola umum saat melengkapi data dengan tabel referensi.
  5. Baris 45–49 (isna): satu nilai pengalaman dikosongkan; isna().sum() menghitung satu nilai hilang di kolom itu, dan penyaringan isna() menunjukkan baris Andi sebagai pemiliknya — langkah pertama sebelum memutuskan strategi imputasi.

Dari DataFrame ke X dan y #

Ujung seluruh operasi pandas pada alur machine learning adalah penyerahan data ke model. Pola bakunya ringkas: pilih kolom fitur sebagai X, pilih kolom target sebagai y — misalnya X = karyawan[["pengalaman_tahun", "gaji_juta"]] dan y = karyawan["departemen"]. scikit-learn menerima DataFrame secara langsung dan menjaga nama kolomnya pada pesan galat serta hasil feature_importances_; jika kita butuh array murni, metode .to_numpy() mengubahnya tanpa menyalin label. Dengan ini rantai lengkapnya tertutup: tabel berlabel → matriks fitur → model.

KebutuhanPola pandasHasil
Pilih kolomdf[["a", "b"]]DataFrame baru
Saring barisdf[df["a"] > 5]Baris yang memenuhi kondisi
Kolom barudf["c"] = df["a"] / df["b"]Kolom hasil hitung vektor
Ringkas per grupdf.groupby("g")["a"].mean()Satu angka per kelompok
Gabung tabeldf1.merge(df2, on="k")Kolom dari dua tabel tergabung
Deteksi hilangdf.isna().sum()Jumlah NaN per kolom

Tabel dan angka saja belum cukup untuk mengenal data — pola, sebaran, dan hubungan antar variabel paling cepat terlihat lewat gambar. Kita akan mempelajari caranya pada Chapter Matplotlib untuk Visualisasi Data.

Referensi Resmi #