d
次元ベクトルが N
個並んだ ndarray x
が与えられたとします. 例えば d=3
次元空間ならこんな感じ.
>>> x = np.random.normal(size=(8,3))
>>> x
array([[-0.35165099, 0.75272535, 0.2327542 ],
[ 0.1591356 , -0.42135054, -0.47746836],
[-0.65012207, 0.42038207, 1.46586837],
[ 1.89249086, -0.9594884 , 1.49124505],
[-2.04557264, -1.00044391, -3.70700719],
[-0.53573469, -0.91952356, 0.02255211],
[ 1.19644064, 0.13071411, -1.80561646],
[-0.68594263, -0.53059182, -0.47844146]])
>>>
>>> x.shape
(8, 3)
本記事ではこの N
個のデータに対して一斉に Euclid ノルムを計算するといった問題を扱います. 従って欲しいものは shape (N,)
の ndarray になります. なお以下では x
, y
は同じ shape (N, d)
の 2 つのベクトルを表します.
Python (numpy) 版
for i in range(N)
みたいな感じで順番に処理するのはとても遅いのでやめましょう. そのための numpy です. 要点は np.sum
または np.max
するときに axis=1
と指定することです.
Euclid 距離
$$\left| x, y \right| = \left[ \sum_{i=0}^{d-1} (x_i - y_i)^2 \right]^\frac{1}{2}$$
Euclid 距離
euclid = np.sqrt(np.sum( (x-y)**2, axis=1))
Euclid ノルム
euclid = np.sqrt(np.sum( (x**2, axis=1))
マンハッタン距離
$$\left| x, y \right| = \sum_{i=0}^{d-1} \left| x_i - y_i \right|$$
マンハッタン距離
manhattan = np.sum(np.fabs(x-y), axis=1))
マンハッタンノルム
manhattan = np.sum(np.fabs(x), axis=1))
Chebyshev 距離
$$\left| x, y \right| = \max ( | x - y | )$$
Chebyshev 距離
chebyshev = np.amax(np.fabs(x - y), axis=1)
Chebyshev ノルム
chebyshev = np.amax(np.fabs(x), axis=1)
Minkowski 距離 (Lp ノルム)
$$\left| x, y \right| = \left[ \sum_{i=0}^{d-1} \left| x_i - y_i \right|^p \right]^\frac{1}{p}$$
Minkowski 距離
minkowski = (np.sum( np.fabs(x-y)**p, axis=1))**(1./p)
$L^p$ ノルム
minkowski = (np.sum( np.fabs(x)**p, axis=1))**(1./p)
Rust 版
D: usize
をコンパイル時定数として x: Vec<[f64;D]>
に対して同じことをしてみます. 書くのが面倒になってきたのでノルムだけ.
Euclid 距離
let euclid = x.iter()
.map(|v| v.iter().map(|&u| u*u).sum::<f64>().sqrt())
.collect::<Vec<_>>();
マンハッタン距離
let manhattan = x.iter()
.map(|v| v.iter().map(|&u| u.abs()).sum::<f64>())
.collect::<Vec<_>>();
Chebyshev 距離
let chebyshev = x.iter()
.map(|v| v.iter().map(|&u| u.abs()).fold(0.0/0.0, f64::max))
.collect::<Vec<_>>();
Minkowski 距離
let minkowski = x.iter()
.map(|v| v.iter().map(|&u| u.abs().powf(p)).sum::<f64>().powf(1./p))
.collect::<Vec<_>>();
Rust - ndarray 版
let euclid = (&x*&x).sum_axis(Axis(1))
.mapv_into(|v| v.sqrt());
let manhattan = &x.mapv_into(|v| v.abs())
.sum_axis(Axis(1));
let chebyshev = &x.mapv_into(|v| v.abs())
.fold_axis(Axis(1), 0.0 / 0.0, |&p, &u| { f64::max(p,u)});
let minkowski = &x.mapv_into(|v| v.abs().powf(p))
.sum_axis(Axis(1))
.mapv_into(|v| v.powf(1./p));