Pada Section Dasar Matematika kita menghitung norma, dot product, dan perkalian matriks dengan loop manual — dan hasilnya benar, tetapi kodenya panjang. NumPy adalah library yang menjawab kejenuhan itu: satu paket operasi numerik yang menyingkat loop menjadi satu baris dan berjalan jauh lebih cepat. NumPy juga fondasi bagi hampir semua library data science lainnya — array dari pandas, scikit-learn, hingga seaborn pada akhirnya adalah array NumPy. Pada chapter ini kita mulai dari pembuatan array (np.array), membaca shape dan dtype, lalu masuk ke tiga kekuatan utamanya: operasi vektor, broadcasting, dan agregasi dengan axis.

Membuat Array: np.array, shape, dan dtype #

Array NumPy (ndarray) dibuat dari list biasa dengan np.array(). Kita pakai harga menu Toko Kopi Kita yang sudah akrab dari Section Dasar Matematika — kopi susu 24, matcha latte 28, teh tawar 20, dan cokelat panas 30 (ribu rupiah):

python
import numpy as np

# Harga menu Toko Kopi Kita (ribu rupiah), urutan kolom tetap:
# [kopi susu, matcha latte, teh tawar, cokelat panas]
harga = np.array([24, 28, 20, 30])
harga_promo = np.array([19.0, 22.5, 15.0, 24.0])

print("Array harga :", harga)
print("Bentuk      :", harga.shape)
print("Tipe data   :", harga.dtype)
print("Tipe promo  :", harga_promo.dtype)

Output yang Dihasilkan:

text
Array harga : [24 28 20 30]
Bentuk      : (4,)
Tipe data   : int64
Tipe promo  : float64

Mari kita baca tiga informasi yang tercetak:

  1. .shape memuat bentuk array sebagai tuple: (4,) berarti satu dimensi dengan empat elemen. Array dua dimensi akan berbentuk (3, 4) — tiga baris, empat kolom — dan membaca shape sebelum memproses data adalah kebiasaan pertama, sama seperti pada chapter Vektor dan Matriks.
  2. .dtype memuat tipe data seluruh elemen. Berbeda dari list Python yang boleh campur aduk, array NumPy bersifat homogen: seluruh elemen bertipe sama.
  3. Homogenitas punya konsekuensi: satu saja elemen berkoma pada harga_promo membuat seluruh array bergeser ke float64. NumPy memilih tipe yang paling mewakili semua elemen, jadi [24, 28, 20.5] menjadi float64 seluruhnya.

Mengakses Elemen dan Slicing #

Akses elemen array mengikuti aturan list Python: indeks mulai dari 0, indeks negatif berhitung dari belakang, dan irisan a:mulai:selesai mengambil mulai hingga sebelum selesai. Untuk array dua dimensi, posisi baris dan kolom dipisah tanda koma di dalam satu kurung siku:

python
import numpy as np

harga = np.array([24, 28, 20, 30])

print("Elemen pertama   (harga[0]) :", harga[0])
print("Elemen terakhir  (harga[-1]):", harga[-1])
print("Irisan tengah    (harga[1:3]):", harga[1:3])
print()

# Matriks penjualan (gelas): baris = hari, kolom = minuman
penjualan = np.array([
    [10, 6, 4, 8],    # Senin
    [12, 5, 8, 7],    # Selasa
    [9, 7, 6, 10],    # Rabu
])
print("Bentuk matriks (3 hari x 4 minuman) :", penjualan.shape)
print("Baris pertama, penjualan Senin      :", penjualan[0])
print("Sel baris ke-2, kolom ke-3          :", penjualan[1, 2])
print("Kolom matcha latte, semua hari      :", penjualan[:, 1])

Output yang Dihasilkan:

text
Elemen pertama   (harga[0]) : 24
Elemen terakhir  (harga[-1]): 30
Irisan tengah    (harga[1:3]): [28 20]

Bentuk matriks (3 hari x 4 minuman) : (3, 4)
Baris pertama, penjualan Senin      : [10  6  4  8]
Sel baris ke-2, kolom ke-3          : 8
Kolom matcha latte, semua hari      : [6 5 7]

Empat bentuk akses pada matriks penjualan di atas adalah kosakata harian kita: penjualan[0] mengambil satu baris (Senin), penjualan[1, 2] menuju satu sel (baris ke-2, kolom ke-3, yaitu 8 gelas teh tawar Selasa), dan tanda titik dua : pada penjualan[:, 1] berarti “semua baris” sehingga hasilnya satu kolom penuh — penjualan matcha latte sepanjang tiga hari. Pola baris-observasi dan kolom-fitur tetap sama dengan konvensi matriks pada Section Dasar Matematika.

Operasi Vektor: Sekali Operator untuk Semua Elemen #

Inilah alasan utama NumPy ada. Menambah 2 ribu rupiah ke semua harga bisa dikerjakan dua cara — loop list biasa, atau satu operator pada array:

python
import numpy as np

harga = np.array([24, 28, 20, 30])

# Versi 1: loop pada list biasa — kita menulis langkahnya satu per satu
naik_list = []
for h in harga.tolist():
    naik_list.append(h + 2)

# Versi 2: operasi vektor NumPy — satu operator untuk semua elemen
naik_array = harga + 2

print("Hasil loop  :", naik_list)
print("Hasil NumPy :", naik_array.tolist())

# Perkalian element-wise: harga x jumlah terjual = pendapatan per minuman
terjual = np.array([10, 6, 4, 8])
print("Pendapatan  :", (harga * terjual).tolist())
print("Total       :", (harga * terjual).sum())

Output yang Dihasilkan:

text
Hasil loop  : [26, 30, 22, 32]
Hasil NumPy : [26, 30, 22, 32]
Pendapatan  : [240, 168, 80, 240]
Total       : 728

Kedua versi menghasilkan angka yang sama persis; bedanya pada bentuk dan kecepatan. Operator + pada array bekerja element-wise — menjumlahkan elemen yang seposisi tanpa loop yang kita tulis sendiri — dan operator * pun demikian: harga * terjual mengalikan pasangan seposisi (harga kopi susu dengan 10 gelas kopi susu, dan seterusnya) sehingga hasilnya langsung pendapatan per minuman. Kenapa NumPy cepat? Karena array homogen tersimpan sebagai satu blok memori rapat dan seluruh operasinya diimplementasikan dalam bahasa C, loop pengulangannya berjalan sebagai kode terkompilasi tanpa pemeriksaan tipe per elemen seperti pada list Python.

Perhatikan bahwa * adalah perkalian per elemen, bukan perkalian matriks — untuk perkalian matriks kita tetap memakai operator @ sebagaimana dibahas pada chapter Operasi Matriks , dan di sana pula kita sudah melihat versi loop manualnya.

Broadcasting: Aturan Penyebaran Bentuk #

Operasi element-wise mestinya menuntut kedua array berbentuk sama, tetapi NumPy melonggarkan syarat itu lewat broadcasting. Aturan intuisinya: dua array bisa dipasangkan bila pada setiap sumbu panjang keduanya sama, atau salah satunya panjangnya 1, atau sumbu itu tidak dimiliki salah satu pihak — pihak yang lebih kecil otomatis disalin sampai bentuknya cocok:

python
import numpy as np

# Skalar "disebar" ke seluruh elemen
harga = np.array([24, 28, 20, 30])
print("harga + 2 :", (harga + 2).tolist())
print()

# Vektor (3,) "disebar" ke setiap baris matriks (3, 3)
# baris = cabang [Bandung, Surabaya, Yogyakarta]
# kolom = minuman [kopi susu, matcha latte, teh tawar]
penjualan = np.array([
    [12, 8, 5],
    [15, 9, 6],
    [11, 7, 4],
])
bonus_promo = np.array([5, 3, 2])   # bonus gelas per minuman

print("Penjualan + bonus promo :")
print(penjualan + bonus_promo)

Output yang Dihasilkan:

text
harga + 2 : [26, 30, 22, 32]

Penjualan + bonus promo :
[[17 11  7]
 [20 12  8]
 [16 10  6]]

Dua contoh di atas memperlihatkan dua tingkat penyebaran. Skalar 2 adalah kasus paling sederhana: angka tunggal disebar ke seluruh elemen. Kasus kedua lebih berguna: vektor bonus_promo berbentuk (3,) dipasangkan dengan matriks (3, 3) — NumPy memperlakukan vektor itu seperti baris tunggal, lalu menyalinnya ke tiga baris sebelum menjumlahkan. Hasilnya setiap baris mendapat tambahan [5, 3, 2] sesuai posisi kolomnya, tanpa kita menulis loop tiga tingkat. Bila panjang vektor tidak sama dengan jumlah kolom, NumPy menolak dengan ValueError karena tidak ada cara penyebaran yang sah.

Agregasi: sum, mean, std, min, max, dan Arah axis #

Fungsi agregasi meringkas seluruh array menjadi satu angka, dan setiap fungsi menerima parameter axis untuk meringkas per baris atau per kolom. Kita gunakan matriks penjualan 3 hari x 4 minuman yang sama:

python
import numpy as np

# Penjualan 3 hari x 4 minuman (gelas) — matriks yang sama dengan contoh sebelumnya
penjualan = np.array([
    [10, 6, 4, 8],    # Senin
    [12, 5, 8, 7],    # Selasa
    [9, 7, 6, 10],    # Rabu
])

print("Total semua    :", penjualan.sum())
print("Rata-rata      :", penjualan.mean().round(2))
print("Simpangan baku :", penjualan.std().round(2))
print("Tertinggi      :", penjualan.max())
print("Terendah       :", penjualan.min())
print()
print("Jumlah per minuman, axis=0 :", penjualan.sum(axis=0))
print("Jumlah per hari, axis=1    :", penjualan.sum(axis=1))
print()
print("Rata-rata per minuman     :", penjualan.mean(axis=0).round(2).tolist())

Output yang Dihasilkan:

text
Total semua    : 92
Rata-rata      : 7.67
Simpangan baku : 2.21
Tertinggi      : 12
Terendah       : 4

Jumlah per minuman, axis=0 : [31 18 18 25]
Jumlah per hari, axis=1    : [28 32 32]

Rata-rata per minuman     : [10.33, 6.0, 6.0, 8.33]

Lima baris pertama adalah agregasi seluruh matriks: total 92 gelas sepekan, rata-rata 7,67 gelas per sel, simpangan baku 2,21, dan nilai terbesar-terkecil 12 dan 4. Bagian menariknya ada pada axis:

PanggilanArah peringkasanBentuk hasilArti pada data kita
penjualan.sum()Seluruh elemensatu angkaTotal sepekan: 92
penjualan.sum(axis=0)Menjejari baris (ke bawah)(4,)Total per minuman
penjualan.sum(axis=1)Menjejari kolom (ke samping)(3,)Total per hari

Angka axis=0 menjumlahkan sepanjang arah baris sehingga baris-baris “diringkap” dan yang tersisa kolom — hasil [31, 18, 18, 25] adalah total sepekan kopi susu, matcha latte, teh tawar, dan cokelat panas. Sebaliknya axis=1 meringkap kolom sehingga hasilnya per baris: Senin 28, Selasa 32, Rabu 32 gelas.

Rentang Praktis dan Angka Acak yang Deterministik #

Dua fungsi pembuat rentang sering kita perlukan: np.arange(mulai, selesai, langkah) menghasilkan deret dengan selisih tetap (nilai selesai tidak ikut), sedangkan np.linspace(mulai, selesai, n) menghasilkan tepat n titik yang berjarak rata dari mulai hingga selesai (kedua ujung ikut). Untuk angka acak, kita berkenalan dengan generator modern NumPy:

python
import numpy as np

print("arange(0, 10, 2) :", np.arange(0, 10, 2).tolist())
print("linspace(0, 1, 5):", np.linspace(0, 1, 5).tolist())
print()

# Angka acak yang dapat direproduksi: generator dengan seed 42
rng = np.random.default_rng(42)
simulasi = rng.integers(low=5, high=21, size=5)   # permintaan harian acak
print("Simulasi permintaan :", simulasi.tolist())

rng_baru = np.random.default_rng(42)
print("Diulang, seed sama  :", rng_baru.integers(low=5, high=21, size=5).tolist())

Output yang Dihasilkan:

text
arange(0, 10, 2) : [0, 2, 4, 6, 8]
linspace(0, 1, 5): [0.0, 0.25, 0.5, 0.75, 1.0]

Simulasi permintaan : [6, 17, 15, 12, 11]
Diulang, seed sama  : [6, 17, 15, 12, 11]

np.arange(0, 10, 2) berhenti di 8 karena 10 adalah batas terbuka, sementara linspace(0, 1, 5) memang membagi rata 0 sampai 1 menjadi lima titik dengan kedua ujung menyertai. Fungsi rng.integers(low, high, size) menarik bilangan bulat acak dari 5 sampai sebelum 21 — dan perhatikan hasil terakhirnya: dua generator berbeda yang diberi seed sama menghasilkan deret yang identik.

Rangkuman #

  • np.array(list) membuat array homogen; .shape memuat bentuk sebagai tuple dan .dtype tipe datanya — satu elemen berkoma menggeser seluruh array ke float64.
  • Akses elemen dan irisan mengikuti list Python; pada matriks, arr[baris, kolom] dan arr[:, j] mengambil satu sel atau satu kolom penuh.
  • Operator aritmatika bekerja element-wise tanpa loop; NumPy cepat karena array homogen dioperasikan oleh implementasi bahasa C.
  • Broadcasting menyebarkan skalar atau vektor ke bentuk yang lebih besar selama tiap sumbu panjangnya sama atau 1.
  • Agregasi sum, mean, std, min, max menerima axis: axis=0 meringkas ke bawah (per kolom), axis=1 ke samping (per baris).
  • np.arange dan np.linspace membangun rentang; np.random.default_rng(42) menghasilkan angka acak yang dapat direproduksi.

Array NumPy kuat untuk angka, tetapi data nyata jarang sekata itu: catatan penjualan membawa tanggal (teks), nama produk (teks), jumlah (bilangan), dan harga (bilangan) dalam satu tabel. Kita butuh struktur yang mengelola tabel campuran itu — Series dan DataFrame milik pandas. Kita bahas pada chapter berikutnya, Dasar Pandas .

Referensi #

  1. NumPy — The Absolute Basics for Beginners — pengantar resmi: membuat array, indexing, broadcasting, dan agregasi.
  2. NumPy — numpy.mean — rujukan resmi fungsi rata-rata beserta parameter axis.