Bab Normal Distribution menghasilkan satu kolom angka. Data nyata jarang sezaman itu: pengukuran tubuh memuat tinggi dan berat badan yang naik-turun bersama, sementara catatan pengeluaran rumah tangga memuat pos pangan dan transportasi yang bergerak searah ketika harga naik. Untuk membangkitkan banyak kolom sekaligus dengan hubungan antarkolom yang terkendali, Generator menyediakan multivariate_normal(mean, cov, size).
Argumen mean adalah vektor pusat untuk tiap dimensi, sedangkan cov adalah matriks kovarians yang menentukan dua hal sekaligus: varians tiap dimensi (diagonal) dan kekuatan hubungan tiap pasangan dimensi (di luar diagonal). Konsep di balik kedua angka itu sudah dibahas pada Korelasi dan Kovarians; bab ini memakainya sebagai bahan pembentuk data, lalu membongkar mekanisme yang bekerja di balik layar.
Vektor Fitur Berkorelasi dengan rng.multivariate_normal #
Kita bangun data pengukuran 100.000 orang dengan dua fitur: tinggi (cm) dan berat (kg). Target sebarannya dirumuskan sebagai matriks kovarians: varians tinggi 64 (std 8 cm), varians berat 36 (std 6 kg), dan kovarians 24. Kovarians 24 tersebut menyiratkan korelasi 24 / (8 * 6) = 0.5 — makin tinggi, kecenderungannya makin berat.
import numpy as np
mean = np.array([165.0, 60.0])
cov = np.array([[64.0, 24.0], [24.0, 36.0]])
rng = np.random.default_rng(42)
sampel = rng.multivariate_normal(mean, cov, size=100_000)
print(sampel[:5].round(2))Output yang Dihasilkan:
[[165.11 54.41]
[157.05 60.55]
[182.98 63.25]
[164.76 58.15]
[167.13 56.59]]Bedah baris demi baris:
- Baris 3-4 (
mean,cov):meanberupa vektor dua elemen sehingga tiap baris sampel juga dua elemen;covberupa matriks 2x2 yang selalu simetris. - Baris 6 (
size=100_000): hasilnya matriks 100.000 x 2 — tiap baris adalah satu pengamatan (satu “orang”) dengan dua fitur, persis format data tabular pada umumnya. - Baris 7 (
sampel[:5].round(2)): lima baris pertama saja yang dicetak, dibulatkan dua desimal; baris pertama, misalnya, berupa orang dengan tinggi 165,11 cm dan berat 54,41 kg.
Verifikasi dengan np.cov dan np.corrcoef #
Sampel berukuran besar harus mereproduksi matriks kovarians target. Fungsi np.cov menganggap tiap baris input sebagai satu variabel, sementara sampel kita berupa baris pengamatan — karena itu inputnya ditranspos menjadi np.cov(sampel.T). Pasangannya, np.corrcoef, mengubah kovarians menjadi matriks korelasi:
import numpy as np
mean = np.array([165.0, 60.0])
cov = np.array([[64.0, 24.0], [24.0, 36.0]])
rng = np.random.default_rng(42)
sampel = rng.multivariate_normal(mean, cov, size=100_000)
print("cov sampel :", np.cov(sampel.T).round(3))
print("cov target :", cov)
print("korelasi :", np.corrcoef(sampel.T).round(2))Output yang Dihasilkan:
cov sampel : [[63.856 23.827]
[23.827 36.249]]
cov target : [[64. 24.]
[24. 36.]]
korelasi : [[1. 0.5]
[0.5 1. ]]Angka sampel 63.856, 23.827, dan 36.249 mendekati target 64, 24, dan 36; korelasi sampel tepat 0,5 pada dua desimal. Pola ini berlaku sama untuk pasangan pengeluaran pangan-transportasi: isi diagonal dengan varians tiap pos dan isi diagonal luar dengan kovarians yang diinginkan, lalu satu panggilan method menghasilkan seluruh dataset.
Pola Cholesky: mean + Z @ L.T #
Bagaimana method memaksakan korelasi pada angka yang acak? Jawabannya ada di aljabar linear. Matriks kovarians difaktorkan menjadi matriks segitiga L — dekomposisi Cholesky yang dibahas pada Decompositions — lalu sampel standar tanpa korelasi Z (dari standard_normal) ditransformasi dengan pola mean + Z @ L.T. Argumen method mengatur jenis faktorisasinya:
| method | Mekanisme | Catatan |
|---|---|---|
'svd' | dekomposisi nilai singular | pilihan bawaan (default) |
'eigh' | dekomposisi eigen matriks simetris | setara 'svd' untuk matriks simetris |
'cholesky' | faktor segitiga bawah L | paling murah secara komputasi |
Kita buktikan pola itu secara manual dan bandingkan dengan method 'cholesky':
import numpy as np
mean = np.array([165.0, 60.0])
cov = np.array([[64.0, 24.0], [24.0, 36.0]])
L = np.linalg.cholesky(cov)
print(L.round(3))
rng = np.random.default_rng(42)
manual = mean + rng.standard_normal((100_000, 2)) @ L.T
rng = np.random.default_rng(42)
bawaan = rng.multivariate_normal(mean, cov, size=100_000, method="cholesky")
print(np.array_equal(manual, bawaan))
print(np.cov(manual.T).round(3))Output yang Dihasilkan:
[[8. 0. ]
[3. 5.196]]
True
[[63.839 23.857]
[23.857 36.281]]Bedah baris demi baris:
- Baris 5 (
np.linalg.cholesky(cov)): menghasilkan faktor segitiga bawah; perkalianL @ L.Tmengembalikancovsecara persis. - Baris 8 (
manual = mean + ... @ L.T):Zberupa 100.000 sampel normal standar yang kolom-kolomnya saling bebas. PerkalianZ @ L.Tmemutar dan meregangkan sebaran tersebut sehingga kovariansnya menjadicov, lalumeanmenggeser pusatnya. - Baris 9-11: dengan seed yang sama,
multivariate_normal(..., method="cholesky")menghasilkan angka yang identik bit-per-bit —np.array_equalbernilaiTrue— sehingga polamean + Z @ L.Tmemang dipakai di dalam method tersebut.
Matriks Kovarians Harus Positif Semidefinite #
Tidak semua matriks boleh menjadi cov. Syaratnya positive semidefinite (PSD): tidak ada kombinasi arah yang memperkecil varians. Korelasi 1,2 antara dua variabel, misalnya, mustahil terjadi. Matriks [[64, 80], [80, 36]] memiliki nilai eigen -31,2 dan 131,2 — salah satunya negatif — sehingga matriks tersebut bukan PSD:
import numpy as np
import warnings
mean = np.array([165.0, 60.0])
cov_rusak = np.array([[64.0, 80.0], [80.0, 36.0]])
rng = np.random.default_rng(42)
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
rng.multivariate_normal(mean, cov_rusak, size=3)
print("Warning :", caught[0].message)
try:
rng.multivariate_normal(mean, cov_rusak, size=3, check_valid="raise")
except ValueError as e:
print("ValueError:", e)Output yang Dihasilkan:
Warning : covariance is not symmetric positive-semidefinite.
ValueError: covariance is not symmetric positive-semidefinite.Dengan multivariate_normal, pola mean + Z @ L.T dari Decompositions, dan jaga-jaga PSD, kita dapat menyimulasikan dataset fitur berkorelasi tanpa menyentuh data pribadi — pemutusan yang rapi dari Normal Distribution satu dimensi menuju data tabular multikolom.