Pekerjaan data science hampir selalu mengikuti alur yang sama: memuat data mentah, memeriksa kualitasnya, membersihkan bagian yang bermasalah, lalu meringkas hasilnya menjadi angka yang siap dipakai untuk mengambil keputusan. NumPy berada di lapisan perhitungan dari seluruh alur tersebut — pandas untuk tabel berlabel maupun scikit-learn untuk model sama-sama menjalankan komputasinya di atas ndarray.
Agar pola itu terlihat utuh, kita akan mengolah data penjualan toko yang sengaja dibuat tidak rapi: sebagian kolom harga dibiarkan kosong. Seluruh alur kita kerjakan hanya dengan NumPy, sehingga setiap langkah terlihat jelas tanpa lapisan pustaka lain.
Alur mini yang kita bangun pada bab ini:
- Memuat file CSV dengan
np.genfromtxt. - Mendeteksi dan membuang baris yang mengandung
nan. - Menghitung pendapatan per kategori dengan
np.uniquedannp.add.at. - Meringkas hasilnya dengan statistik deskriptif.
Memuat Data Penjualan dari CSV #
Data mentah kita berupa file CSV berisi tiga kolom: kode kategori, harga (dalam ribuan rupiah), dan jumlah terjual. Dua baris menyisakan kolom harga kosong, dan np.genfromtxt mengganti sel kosong itu menjadi nan:
import numpy as np
isi = """kategori,harga,jumlah
1,15.0,3
2,,2
1,12.0,5
3,45.0,1
2,8.0,4
3,,2
1,13.5,6
2,9.5,3
3,50.0,4
1,11.0,2
"""
with open("penjualan.csv", "w") as f:
f.write(isi)
data = np.genfromtxt("penjualan.csv", delimiter=",", skip_header=1)
print(data.shape)
print(data)Output yang Dihasilkan:
(10, 3)
[[ 1. 15. 3. ]
[ 2. nan 2. ]
[ 1. 12. 5. ]
[ 3. 45. 1. ]
[ 2. 8. 4. ]
[ 3. nan 2. ]
[ 1. 13.5 6. ]
[ 2. 9.5 3. ]
[ 3. 50. 4. ]
[ 1. 11. 2. ]]Mari kita bedah:
- Baris 3-13 (
isi = """..."""): string multibaris menyusun isi CSV lengkap dengan baris judulnya; dua catatan penjualan sengaja tidak memiliki harga. - Baris 15-16 (
open(...)/f.write(isi)): script membuat filepenjualan.csvdi folder kerja lalu menuliskan seluruh isinya, meniru data yang biasanya diunduh dari sistem kasir. - Baris 18 (
np.genfromtxt(...)):delimiter=","memisahkan kolom danskip_header=1melewatkan baris judul. Karena kolom harga memuat sel kosong, seluruh array dipromosikan kefloat64. - Baris 19-20: hasilnya array berbentuk
(10, 3); sel kosong tampil sebagainan, bukan nol — penting agar transaksi tanpa harga tidak terbaca sebagai penjualan gratis.
Membersihkan Baris yang Tidak Valid #
Seperti dibahas pada Chapter NaN dan Infinity, satu nan cukup untuk membuat hasil agregasi ikut menjadi nan. Pola pembersihan yang umum: buat mask dengan np.isnan, gabungkan antar kolom dengan | atau .any(axis=1), lalu gunakan boolean indexing untuk mengambil baris yang valid:
import numpy as np
data = np.array([
[1., 15.0, 3.],
[2., np.nan, 2.],
[1., 12.0, 5.],
[3., 45.0, 1.],
[2., 8.0, 4.],
[3., np.nan, 2.],
[1., 13.5, 6.],
[2., 9.5, 3.],
[3., 50.0, 4.],
[1., 11.0, 2.],
])
baris_valid = ~np.isnan(data).any(axis=1)
bersih = data[baris_valid]
print("mask :", baris_valid.astype(int))
print("baris :", bersih.shape[0], "dari", data.shape[0])
print(bersih)Output yang Dihasilkan:
mask : [1 0 1 1 1 0 1 1 1 1]
baris : 8 dari 10
[[ 1. 15. 3. ]
[ 1. 12. 5. ]
[ 3. 45. 1. ]
[ 2. 8. 4. ]
[ 1. 13.5 6. ]
[ 2. 9.5 3. ]
[ 3. 50. 4. ]
[ 1. 11. 2. ]]Penjelasan tiap langkah:
- Baris 15 (
baris_valid = ~np.isnan(data).any(axis=1)):np.isnanmenghasilkan mask per sel;.any(axis=1)merangkumnya per baris —Trueberarti baris itu memuat setidaknya satunan; operator~membalik mask sehinggaTruekini menandai baris yang lengkap. - Baris 16 (
bersih = data[baris_valid]): boolean indexing mengambil baris bernilaiTruesaja; 10 baris menyusut menjadi 8 baris yang bersih. - Baris 17-19: mask tampil sebagai 0/1 per baris, diikuti hitungan dan isi data yang tersisa.
Pola ini identik dengan yang dipakai pandas saat memanggil dropna() — selisihnya hanya pada label kolom. Varian lain, misalnya mengisi nan dengan np.nanmedian alih-alih membuang baris, dibahas pada Chapter NaN-aware Aggregation.
Menghitung Pendapatan per Kategori #
Pertanyaan bisnis berikutnya: berapa pendapatan tiap kategori? NumPy tidak punya groupby, tetapi kombinasi np.unique dan np.add.at menjalankan logika yang sama: kategori diubah menjadi nomor grup, lalu setiap pendapatan diakumulasikan ke slot grupnya:
import numpy as np
bersih = np.array([
[1., 15.0, 3.],
[1., 12.0, 5.],
[2., 8.0, 4.],
[2., 9.5, 3.],
[3., 45.0, 1.],
[3., 50.0, 4.],
[1., 13.5, 6.],
[1., 11.0, 2.],
])
pendapatan = bersih[:, 1] * bersih[:, 2]
kategori = bersih[:, 0].astype(int)
unik, indeks, frek = np.unique(kategori, return_inverse=True, return_counts=True)
total = np.zeros(unik.size)
np.add.at(total, indeks, pendapatan)
print("kategori :", unik)
print("transaksi :", frek)
print("pendapatan :", total)Output yang Dihasilkan:
kategori : [1 2 3]
transaksi : [4 2 2]
pendapatan : [208. 60.5 245. ]Mari kita bedah:
- Baris 13 (
bersih[:, 1] * bersih[:, 2]): vectorization mengalikan kolom harga dengan kolom jumlah, menghasilkan pendapatan per transaksi (dalam ribuan rupiah). - Baris 14 (
astype(int)): kode kategori dikonversi ke integer agar bisa dipakai sebagai indeks grup. - Baris 15 (
np.unique(..., return_inverse=True, return_counts=True)):unikberisi kode kategori yang telah diurutkan,indeksmemetakan setiap baris ke nomor grupnya, danfrekmenghitung jumlah transaksi per grup. - Baris 16-17 (
np.add.at):np.add.at(total, indeks, pendapatan)menambahkan tiap pendapatan ke slottotalsesuai grupnya — padananGROUP BY kategori SUM(...).
Hasilnya: kategori 1 menyumbang 208 ribu dari 4 transaksi, kategori 2 sebanyak 60,5 ribu, dan kategori 3 menjadi penyumbang terbesar (245 ribu) meskipun hanya dari 2 transaksi.
Statistik Deskriptif Pendapatan #
Tahap terakhir alur: meringkas delapan nilai pendapatan menjadi ukuran yang mudah dibaca. Perhatikan bagaimana mean dan median memberi cerita yang berbeda:
import numpy as np
pendapatan = np.array([45., 60., 32., 28.5, 45., 200., 81., 22.])
print("n :", pendapatan.size)
print("mean :", np.mean(pendapatan))
print("median :", np.median(pendapatan))
print("std :", np.round(np.std(pendapatan), 2))
print("min-max :", np.min(pendapatan), "-", np.max(pendapatan))
print("kuartil :", np.percentile(pendapatan, [25, 75]))Output yang Dihasilkan:
n : 8
mean : 64.1875
median : 45.0
std : 54.28
min-max : 22.0 - 200.0
kuartil : [31.125 65.25 ]| Ukuran | Nilai | Interpretasi |
|---|---|---|
mean | 64.19 | rata-rata tertarik ke atas oleh satu transaksi 200 ribu |
median | 45.0 | separuh transaksi bernilai di bawah 45 ribu — gambaran yang lebih mewakili |
std | 54.28 | sebaran pendapatan lebar dibanding rata-ratanya |
kuartil 25-75 | 31.1 - 65.25 | 50% transaksi berada di rentang ini |
Posisi NumPy dalam Alur Data Science #
Setiap langkah di atas punya padanan langsung di pustaka yang lebih tinggi. Memahami versi arraynya membuat perilaku pustaka tersebut mudah diprediksi:
| Tahap | Versi NumPy | Saat pindah ke pustaka lain |
|---|---|---|
| Memuat CSV | np.genfromtxt | kolom teks + label → pandas (Membaca dan Menulis CSV) |
| Pembersihan | mask np.isnan + boolean indexing | dropna()/fillna() berlabel → pandas |
| Agregasi grup | np.unique + np.add.at | groupby() multi-kolom → pandas |
| Statistik ringkas | mean, median, percentile | uji statistik formal → SciPy |
| Pemodelan | tidak disediakan | scikit-learn, dibahas bab berikutnya |
Pola yang kita bangun di bab ini — muat, bersih, agregasi, ringkas — juga merupakan kerangka bab-bab berikutnya: bab selanjutnya menambahkan lapisan model di atas array yang sama.