Pertanyaan analisis sudah kita rumuskan; kini saatnya berkenalan dengan dataset yang akan menjawabnya. Kesalahan klasik pemula adalah langsung menghitung rata-rata tanpa memahami bentuk tabel terlebih dahulu — padahal salah membaca makna satu baris bisa merusak seluruh kesimpulan. Pada chapter ini kita membedah struktur data Toko Kopi Kita dengan alat inspeksi pandas: dimensi, nama kolom, tipe data, cuplikan baris, profil keunikan, hingga kunci yang menghubungkan dua tabel.

Makna Satu Baris: Observasi #

Aturan pertama memahami struktur: satu baris mewakili satu observasi — satu kejadian atau satu entitas yang dicatat. Kesadaran ini menentukan pertanyaan apa yang sah untuk diajukan. Pada tabel pelanggan, satu baris adalah satu pelanggan terdaftar, sehingga pertanyaan “siapa pelanggan dengan poin terbanyak?” sah. Sebaliknya, pada tabel transaksi, satu baris adalah satu pesanan, sehingga pertanyaan yang sama harus dijawab lewat penghitungan tambahan.

TabelSatu baris mewakiliContoh pertanyaan yang sah
pelanggansatu pelanggan terdaftarPelanggan siapa yang bergabung paling akhir?
transaksisatu pesanan pelangganMenu apa yang paling sering dipesan pekan ini?
rekap_hariansatu hari operasionalHari mana pendapatan terbesar bulan ini?

Satu dataset yang sama bisa diringkas pada beberapa tingkat — transaksi mentah, rekap harian, rekap bulanan. Semakin tinggi tingkatnya, semakin sedikit barisnya dan semakin banyak detail yang menyatu di dalam satu angka. Sebelum membandingkan dua tabel, pastikan keduanya berada pada tingkat yang setara; membandingkan tabel transaksi dengan tabel rekap harian adalah resep kesimpulan yang keliru.

Kita memakai dua berkas kecil sepanjang chapter ini — tabel induk pelanggan:

csv
id_pelanggan,nama,kota,poin
P01,Budi,Bandung,120
P02,Siti,Bandung,85
P03,Dewi,Surabaya,200
P04,Agus,Bandung,60
P05,Rani,Surabaya,95

dan tabel transaksi yang mencatat pesanan dengan merujuk pelanggan lewat id_pelanggan:

csv
tanggal,id_pelanggan,menu,total_ribu
2025-01-13,P01,kopi susu,48
2025-01-13,P02,teh tawar,20
2025-01-14,P03,matcha latte,56
2025-01-14,P01,kopi susu,24
2025-01-15,P05,teh tawar,40
2025-01-15,P04,matcha latte,28
2025-01-16,P02,kopi susu,72
2025-01-16,P03,teh tawar,40

Memeriksa Dimensi, Kolom, dan Tipe Data #

Pemeriksaan pertama menjawab tiga hal: berapa ukuran tabel, kolom apa saja yang tersedia, dan tipe data apa yang dikandung tiap kolom. Ketiganya kita dapat dari shape, columns, dan dtypes:

python
import pandas as pd

pelanggan = pd.read_csv("pelanggan.csv")

print("Dimensi (baris, kolom):", pelanggan.shape)
print("Nama kolom:", pelanggan.columns.tolist())
print(pelanggan.dtypes)

Output yang Dihasilkan:

text
Dimensi (baris, kolom): (5, 4)
Nama kolom: ['id_pelanggan', 'nama', 'kota', 'poin']
id_pelanggan    object
nama            object
kota            object
poin             int64
dtype: object

Mari kita baca keluarannya satu per satu:

  1. pelanggan.shape mengembalikan pasangan (5, 4): lima baris observasi dan empat kolom variabel. Ini ukuran tabel yang benar-benar terbaca — bukan ukuran berkas aslinya.
  2. pelanggan.columns.tolist() menampilkan nama kolom sebagai list Python. Di sinilah kita menguji pertanyaan analisis dari chapter sebelumnya: kolom yang tidak ada di list ini tidak bisa menjawab pertanyaan apa pun.
  3. pelanggan.dtypes melaporkan tipe data tiap kolom. Kolom teks terbaca sebagai object, sedangkan kolom bilangan bulat sebagai int64. Tipe menentukan operasi yang sah: poin bisa dihitung rata-ratanya, nama hanya bisa dihitung frekuensinya.

Perhatikan pula bahwa id_pelanggan berisi kode P01 sampai P05 — label, bukan angka. Kolom semacam ini sengaja disimpan sebagai teks agar tidak ikut terseret ke dalam hitungan numerik; menjadikannya angka justru menyesatkan, karena P01 + P02 tidak punya arti.

Melihat Cuplikan Data: head, tail, dan sample #

Setelah tahu kerangkanya, kita mengintip isinya. Tiga method untuk itu: head() menampilkan baris teratas, tail() menampilkan baris terbawah, dan sample() mengambil baris secara acak:

python
import pandas as pd

pelanggan = pd.read_csv("pelanggan.csv")

print(pelanggan.head(3))
print()
print(pelanggan.tail(3))
print()
print(pelanggan.sample(3, random_state=42))

Output yang Dihasilkan:

text
  id_pelanggan  nama      kota  poin
0          P01  Budi   Bandung   120
1          P02  Siti   Bandung    85
2          P03  Dewi  Surabaya   200

  id_pelanggan  nama      kota  poin
2          P03  Dewi  Surabaya   200
3          P04  Agus   Bandung    60
4          P05  Rani  Surabaya    95

  id_pelanggan  nama      kota  poin
1          P02  Siti   Bandung    85
4          P05  Rani  Surabaya    95
2          P03  Dewi  Surabaya   200

Ketiga cuplikan tersebut punya peran berbeda. head(3) memastikan berkas terbaca dengan benar — nama kolom cocok, nilai masuk akal. tail(3) mengecek ujung tabel, tempat baris rangkap atau catatan tambahan sering menempel. sample(3, random_state=42) mengambil tiga baris secara acak; argumen random_state=42 membekukan pilihan acaknya sehingga siapa pun yang menjalankan kode ini mendapat baris yang sama persis.

Profil Kolom: nunique dan info #

Pemeriksaan berikutnya memprofilkan tiap kolom: berapa banyak nilai uniknya, dan apakah ada sel yang kosong. Dua alatnya adalah nunique() dan info():

python
import pandas as pd

pelanggan = pd.read_csv("pelanggan.csv")

print("Jumlah nilai unik tiap kolom:")
print(pelanggan.nunique())
print()
pelanggan.info()

Output yang Dihasilkan:

text
Jumlah nilai unik tiap kolom:
id_pelanggan    5
nama            5
kota            2
poin            5
dtype: int64

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5 entries, 0 to 4
Data columns (total 4 columns):
 #   Column        Non-Null Count  Dtype 
---  ------        --------------  ----- 
 0   id_pelanggan  5 non-null      object
 1   nama          5 non-null      object
 2   kota          5 non-null      object
 3   poin          5 non-null      int64 
dtypes: int64(1), object(3)
memory usage: 292.0+ bytes

Cara membaca keluaran tersebut:

  1. nunique() menghitung nilai berbeda di tiap kolom. Kolom id_pelanggan memuat 5 nilai unik dari 5 baris — tanda kolom kunci. Kolom kota hanya memuat 2 nilai unik — tanda kolom kategorikal.
  2. RangeIndex: 5 entries, 0 to 4 menegaskan 5 baris terindeks 0 sampai 4; tidak ada baris yang gagal terbaca.
  3. Kolom Non-Null Count membandingkan jumlah nilai terisi dengan total baris. Seluruh kolom di sini mencatat 5 non-null; kolom yang mencatat lebih sedikit mengandung missing values yang harus ditangani lebih dulu.
  4. dtypes: int64(1), object(3) merangkum komposisi tipe data, lalu memory usage memperkirakan pemakaian memori — angka yang mulai penting saat tabel membesar menjadi jutaan baris.

Kunci Penghubung antar Tabel #

Dunia nyata jarang menyimpan semua data dalam satu tabel. Toko Kopi Kita memisahkan tabel induk pelanggan dari tabel transaksi, dan keduanya dihubungkan kolom id_pelanggan — kolom kunci yang nilainya sama di kedua tabel. Pemisahan seperti ini menghemat ruang: nama dan kota pelanggan cukup disimpan sekali, tidak diulang di setiap transaksi.

Berikut cara kedua tabel tersebut disambungkan:

python
import pandas as pd

pelanggan = pd.read_csv("pelanggan.csv")
transaksi = pd.read_csv("transaksi.csv")

print("id_pelanggan di tabel pelanggan:", sorted(pelanggan["id_pelanggan"]))
print("id_pelanggan di tabel transaksi:", sorted(transaksi["id_pelanggan"].unique()))
print()

gabung = transaksi.merge(pelanggan[["id_pelanggan", "nama"]], on="id_pelanggan")
print(gabung)

Output yang Dihasilkan:

text
id_pelanggan di tabel pelanggan: ['P01', 'P02', 'P03', 'P04', 'P05']
id_pelanggan di tabel transaksi: ['P01', 'P02', 'P03', 'P04', 'P05']

      tanggal id_pelanggan          menu  total_ribu  nama
0  2025-01-13          P01     kopi susu          48  Budi
1  2025-01-13          P02     teh tawar          20  Siti
2  2025-01-14          P03  matcha latte          56  Dewi
3  2025-01-14          P01     kopi susu          24  Budi
4  2025-01-15          P05     teh tawar          40  Rani
5  2025-01-15          P04  matcha latte          28  Agus
6  2025-01-16          P02     kopi susu          72  Siti
7  2025-01-16          P03     teh tawar          40  Dewi

Bedah kode di atas:

  1. sorted(pelanggan["id_pelanggan"]) memastikan lima pelanggan tercatat lengkap; sorted(transaksi["id_pelanggan"].unique()) memastikan setiap transaksi merujuk pelanggan yang memang ada. Kunci yang dirujuk tetapi tidak tercatat di tabel induk adalah foreign key yang menggantung — transaksi tanpa pemilik.
  2. pelanggan[["id_pelanggan", "nama"]] mengambil dua kolom saja dari tabel induk, sehingga tabel hasil penggabungan tidak membawa muatan kota dan poin yang tidak diperlukan.
  3. .merge(..., on="id_pelanggan") menyandingkan tiap baris transaksi dengan baris pelanggan yang ber-id_pelanggan sama. Hasilnya, tabel transaksi yang semula anonim kini memuat nama pelanggan — siap dipakai untuk analisis seperti “pelanggan mana yang paling sering memesan?”.

Rangkuman #

  • Satu baris DataFrame mewakili satu observasi; makna ini menentukan pertanyaan mana yang sah untuk dijawab tabel tersebut.
  • shape, columns, dan dtypes memetakan kerangka tabel: ukuran, variabel yang tersedia, dan operasi yang sah untuk tiap kolom.
  • head(), tail(), dan sample(random_state=...) saling melengkapi sebagai cuplikan isi; cuplikan acak paling mewakili tabel yang terurut.
  • nunique() dan info() memprofilkan keunikan, kelengkapan, dan tipe kolom; kolom dengan nilai unik sebanyak barisnya adalah kolom kunci.
  • merge(on=...) menyambungkan dua tabel lewat kolom kunci bersama — syaratnya pasangan kunci harus lengkap di kedua sisi.

Struktur sudah terpetakan, dan info() tadi memperlihatkan sekilas angka-angka di dalamnya. Pertanyaan berikutnya: bagaimana meringkas isi tabel itu menjadi angka-angka yang bermakna? Kita mulai dari kartu identitas dataset — describe() dan kawan-kawannya — pada chapter berikutnya, Ringkasan Dataset .

Referensi #

  1. pandas — User Guide
  2. pandas — Getting Started: Intro Tutorials
  3. pandas — API Reference: DataFrame.merge