Rの関数:sparseMatrix {Matrix}

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}により、作成した疎行列の00とし保持する通常の行列に変換して、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 倍 速くなりました。

以上です。