Rの関数から sparseMatrix {Matrix} を確認します。
疎行列とは
疎行列(そぎょうれつ、Sparse Matrix)とは、成分のほとんどが0である行列のことです。
対義語は、成分の多くが0以外の値で埋まっている「密行列(みつぎょうれつ、Dense Matrix)」です。
密行列(Dense Matrix)の例 \[
\begin{pmatrix}
1 & 2 & 3 \\
4 & 5 & 6 \\
7 & 8 & 9
\end{pmatrix}
\] すべての場所に0では無い数値が存在しています。
疎行列(Sparse Matrix)の例 \[
\begin{pmatrix}
0 & 0 & 0 & 5 \\
0 & 2 & 0 & 0 \\
0 & 0 & 0 & 0 \\
1 & 0 & 0 & 0
\end{pmatrix}
\] ほとんどが0で、疎らに0以外の数値が存在しています。
疎行列として扱うメリット
メモリの節約
例えば、\(10,000 \times 10,000\) の行列があるとします。要素数は1億個です。
もし要素の99%が0だった場合、その1億個のデータをすべて記憶するのはメモリの無駄です。
疎行列アルゴリズムを利用すれば、「どこに、何の値があるか(非ゼロ要素)」だけを記録すればよいため、メモリ使用量を減らすことが出来ます。
計算速度の向上
行列計算において、0を足しても値は変わらず、0を掛けると答えは必ず0になります。
疎行列アルゴリズムを利用すると、0の部分の計算をスキップすることができるため、計算処理が高速になります。
Rによるシミュレーション
関数sparseMatrix {Matrix}の疎行列アルゴリズムを使うことで、疎行列演算の「メモリ消費量が減少すること」と「計算速度が向上すること」の2点を確認します。
サンプル疎行列は 2000×2000の行列(要素数400万個)とし、全体の1%だけ0以外の数値で埋め、残りの99%は0にします。
始めに、関数sparseMatrix {Matrix}により、疎行列をmat_sparseMatrixとして作成し、続いて、関数as.matrix {base}により、作成した疎行列の0を0とし保持する通常の行列に変換して、mat_as.matrixとします。
その後、メモリ使用量および計算速度の比較のための2つの演算をシミュレーションします。
# 必要なパッケージをロード
library(Matrix)
# --- 1. テストデータの作成 ---
# 行列のサイズ (N x N)
N <- 2000
# 非ゼロ要素の割合 (1%だけ値が入っている = 99%は0)
density <- 0.01
# ランダムな疎行列データの生成
seed <- 20251127
set.seed(seed)
# 全要素数の1%分のインデックスを作成
n_nonzero <- N * N * density
i_idx <- sample(1:N, n_nonzero, replace = TRUE)
j_idx <- sample(1:N, n_nonzero, replace = TRUE)
values <- rnorm(n_nonzero)
# A: 関数sparseMatrixによる疎行列の作成
mat_sparseMatrix <- sparseMatrix(i = i_idx, j = j_idx, x = values, dims = c(N, N))
cat("--- mat_sparseMatrix の一部を確認 ---\n")
mat_sparseMatrix[1:10, 1:10]
cat("\n--- mat_sparseMatrix の非ゼロ要素数を確認 ---\n")
print(paste("非ゼロ要素数:", Matrix::nnzero(mat_sparseMatrix)))--- mat_sparseMatrix の一部を確認 ---
10 x 10 sparse Matrix of class "dgCMatrix"
[1,] . . . . . . . . . .
[2,] . . . . . . . . . .
[3,] . . . . . . . . . .
[4,] . . . . . . . . . .
[5,] . . . . . . . . . .
[6,] . . . . . . . . . .
[7,] . . . . . . . . . .
[8,] . . . . . . . . . .
[9,] . . . . . . . . . .
[10,] . . . . . . . . . .
--- mat_sparseMatrix の非ゼロ要素数を確認 ---
[1] "非ゼロ要素数: 39778"# B: 関数as.matrixによる疎行列の作成
mat_as.matrix <- as.matrix(mat_sparseMatrix)
cat("--- mat_as.matrix の一部を確認 ---\n")
mat_as.matrix[1:10, 1:10]--- mat_as.matrix の一部を確認 ---
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 0 0 0 0 0 0 0 0 0 0
[2,] 0 0 0 0 0 0 0 0 0 0
[3,] 0 0 0 0 0 0 0 0 0 0
[4,] 0 0 0 0 0 0 0 0 0 0
[5,] 0 0 0 0 0 0 0 0 0 0
[6,] 0 0 0 0 0 0 0 0 0 0
[7,] 0 0 0 0 0 0 0 0 0 0
[8,] 0 0 0 0 0 0 0 0 0 0
[9,] 0 0 0 0 0 0 0 0 0 0
[10,] 0 0 0 0 0 0 0 0 0 0# --- 2. メリット1:メモリ使用量の比較 ---
cat("--- 1. メモリ使用量の比較 ---\n")
mem_normal <- object.size(mat_as.matrix)
mem_sparse <- object.size(mat_sparseMatrix)
# 削減率の計算
reduction <- as.numeric(mem_normal) / as.numeric(mem_sparse)
cat(sprintf("-> sparseMatrix {Matrix} を利用することで、メモリ効率が 約 %.1f 倍 になりました。\n\n", reduction))
# --- 3. メリット2:計算速度の比較 ---
cat("--- 2. 計算速度の比較 (行列の掛け算) ---\n")
# 密行列の掛け算 (t(A) %*% A)
time_normal <- system.time({
res_dense <- crossprod(mat_as.matrix)
})
# 疎行列の掛け算
time_sparse <- system.time({
res_sparse <- crossprod(mat_sparseMatrix)
})
# 速度倍率の計算
speedup <- time_normal["elapsed"] / time_sparse["elapsed"]
cat(sprintf("-> sparseMatrix {Matrix} を利用することで、計算が 約 %.1f 倍 速くなりました。\n", speedup))--- 1. メモリ使用量の比較 ---
-> sparseMatrix {Matrix} を利用することで、メモリ効率が 約 65.7 倍 になりました。
--- 2. 計算速度の比較 (行列の掛け算) ---
-> sparseMatrix {Matrix} を利用することで、計算が 約 91.8 倍 速くなりました。以上です。
