Rの関数:cell2nb {spdep}

Rの関数から cell2nb {spdep} を確認します。

本ポストはこちらの続きです。

Rの関数:lagsarlm(type = "mixed") {spatialreg }
const typesetMath = (el) => { if (window.MathJax) { // MathJax Typeset window.MathJax.typeset(); } else if (window.katex...

関数 cell2nb とは

cell2nb (Cell to Neighbors) は、規則的な格子状(グリッド)の空間構造において、近傍リスト(neighbors list, nb クラスのオブジェクト)を作成するための関数です。

地理空間データ分析において、メッシュデータやラスタデータのような正則格子データを扱う際、「どのセルとどのセルが隣接しているか」を定義する必要があります。

cell2nb は、行数と列数を指定するだけで、この隣接関係を自動的に生成します。

隣接の定義には、チェスの駒の動きになぞらえた「ルーク型(Rook: 上下左右)」と「クイーン型(Queen: 全8方向)」を選択可能です。


関数 cell2nb の引数

library(spdep)
args(cell2nb)
function (nrow, ncol, type = "rook", torus = FALSE, legacy = FALSE, 
    x = NULL) 
NULL
  1. nrow

    • グリッドの行数(整数の数値)。
    • 空間格子の縦方向のサイズを決定します。
    • 第1引数に数値ではなく SpatialGridGridTopology オブジェクトが渡された場合、関数内部で自動的に行数・列数が抽出され、変数 x として処理されます。
  2. ncol

    • グリッドの列数(整数の数値)。
    • 空間格子の横方向のサイズを決定します。nrowncol の積が全セル数(\(n\))となります。
  3. type

    • 隣接関係の定義タイプを指定する文字列。デフォルトは "rook" です。
    • どのような位置関係にあるセルを「隣接」とみなすかを決定します。
      • "rook" (ルーク): 上下左右の4方向のみを隣接とみなします(共有する辺がある場合)。
      • "queen" (クイーン): 上下左右に加え、斜めの4方向も隣接とみなします(共有する点または辺がある場合)。
  4. torus

    • 空間をトーラス(ドーナツ型)構造として扱うかを指定する論理値。デフォルトは FALSE です。
    • グリッドの端の処理を決定します。
      • FALSE: 平面として扱い、端にあるセルは外側との隣接を持ちません(近傍数が少なくなります)。
      • TRUE: 右端と左端、上端と下端がつながっているとみなします。すべてのセルが等しい近傍数を持ちます。
  5. legacy

    • 古いバージョンの座標順序(IDの割り当て順)を使用するかを指定する論理値。デフォルトは FALSE です。
    • 過去の分析結果との互換性を保つために存在します。行と列のループ順序が異なります。
  6. x

    • 内部処理用の引数、またはグリッドオブジェクト自体。
    • nrow の位置に SpatialGrid オブジェクトなどが渡された場合、当該オブジェクトが x に格納され、そこから nrowncol が抽出されます。

シミュレーション用サンプルデータとコード

cell2nb の挙動、特に type="rook"type="queen" の違い、および torus の効果を視覚的に確認するため、小規模な \(5 \times 5\) のグリッドを用いたシミュレーションを行います。

サンプルデータの作成 (5x5 グリッドの座標)

rowscols を指定して、 \(5 \times 5\) の仮想的な座標点 coords を生成します。

cell2nb 関数自体は座標データを必要としませんが、結果を plot 関数で可視化する際に、各ノード(セル)をどこに配置するかを指定するために使用します。

# cell2nb自体はデータフレームを必要としませんが、
# 可視化のために各セルの中心座標を作成します。

rows <- 5
cols <- 5
n_cells <- rows * cols

# グリッドの座標を作成 (1:5 の範囲で xy座標を生成)
# spdepの並び順に合わせて座標を生成します (左上から右下へ、あるいは左下から右上へ)
# 通常、cell2nbは行(row)優先でIDを振りますが、可視化の際はexpand.gridで対応します
coords <- expand.grid(x = 1:cols, y = rows:1) # yは上から下へ描画されるよう反転

cat("--- 作成したグリッド座標の構造を確認 ---\n")
print(str(coords))
--- 作成したグリッド座標の構造を確認 ---
'data.frame':   25 obs. of  2 variables:
 $ x: int  1 2 3 4 5 1 2 3 4 5 ...
 $ y: int  5 5 5 5 5 4 4 4 4 4 ...
 - attr(*, "out.attrs")=List of 2
  ..$ dim     : Named int [1:2] 5 5
  .. ..- attr(*, "names")= chr [1:2] "x" "y"
  ..$ dimnames:List of 2
  .. ..$ x: chr [1:5] "x=1" "x=2" "x=3" "x=4" ...
  .. ..$ y: chr [1:5] "y=5" "y=4" "y=3" "y=2" ...
NULL

cell2nb による近傍リストの作成

  • Rook型: 上下左右の隣接のみをリンクとして生成します。
  • Queen型: 斜めの隣接もリンクとして生成します。
  • Torus (トーラス): type="rook" ですが torus=TRUE とすることで、グリッドの左端と右端、上端と下端がつながるように設定します。
# (A) Rook型 (上下左右)
nb_rook <- cell2nb(nrow = rows, ncol = cols, type = "rook")

# (B) Queen型 (上下左右 + 斜め)
nb_queen <- cell2nb(nrow = rows, ncol = cols, type = "queen")

# (C) Rook型 + トーラス構造 (端同士がつながる)
nb_torus <- cell2nb(nrow = rows, ncol = cols, type = "rook", torus = TRUE)

cat("--- (A) Rook型 (上下左右) ---\n")
nb_rook

cat("\n---  (B) Queen型 (上下左右 + 斜め) ---\n")
nb_queen

cat("\n---  (C) Rook型 + トーラス構造 (端同士がつながる) ---\n")
nb_torus
--- (A) Rook型 (上下左右) ---
Neighbour list object:
Number of regions: 25 
Number of nonzero links: 80 
Percentage nonzero weights: 12.8 
Average number of links: 3.2 

---  (B) Queen型 (上下左右 + 斜め) ---
Neighbour list object:
Number of regions: 25 
Number of nonzero links: 144 
Percentage nonzero weights: 23.04 
Average number of links: 5.76 

---  (C) Rook型 + トーラス構造 (端同士がつながる) ---
Neighbour list object:
Number of regions: 25 
Number of nonzero links: 100 
Percentage nonzero weights: 16 
Average number of links: 4 

3種類の近傍定義(Rook, Queen, Torus)によって、空間的なつながりの強さ(リンク数)がどのように変化するかを定量的に確認できます。

共通事項として、Number of regions: 25 は、\(5 \times 5\) のグリッド(全25セル)が正しく認識されていることを表しています。

nb_rook (通常のルーク型)
Number of nonzero links: 80 
Average number of links: 3.2 

上下左右のみを隣接とみなす定義です。

本来、グリッドの内側にあるセルは4つの近傍を持ちますが、端(エッジ)や角(コーナー)にあるセルは外側との接点を持たないため、近傍数が減少し(3つまたは2つになり)ます。

具体的には、内側9セル(\(\times 4\))、辺上の12セル(\(\times 3\))、角の4セル(\(\times 2\))の合計でリンク総数は80となります。

それゆえ、平均リンク数は4を下回る 3.2 となっています。当該数値は「境界効果(端の影響)」が強く表れていることを示唆します。

nb_queen (クイーン型)
Number of nonzero links: 144 
Average number of links: 5.76 

上下左右に加え、斜め方向も隣接とみなす定義です。

内側のセルは最大で8つの近傍を持つことになります。その結果、リンク総数は 144 へと増加しました。

Rook型と比較してネットワークの密度(Percentage nonzero weights)が 12.8% から 23.04% へと上昇しており、情報の波及経路がより密に張り巡らされていることが分かります。

nb_torus (トーラス構造)
Number of nonzero links: 100 
Average number of links: 4 

Rook型に「端同士をつなげる」処理を加えた定義です。

通常であれば近傍数が減ってしまう端や角のセルが、反対側のセルと接続されることで、全てのセルが平等に「4つ」の近傍を持つことになります。

そのため、リンク総数は \(25 \text{セル} \times 4 = 100\) となり、平均リンク数も 4 となっています。

空間的な「端」の存在がノイズとなることを防ぎたい場合に、このトーラス構造が有効であることが数値からも読み取れます。

まとめ
  • nb_rook: シンプルだが、端の影響で平均近傍数が下がる。
  • nb_queen: 斜めを含むため、つながりが多くなる。
  • nb_torus: 端の影響を排除し、全てのセルで均一なつながり(近傍数4)を保つ。

構造の違いを確認する

具体的に、2つのセル(ID=13ID=1)の近傍数を確認します。

前述のとおり、

  • Rook型: 中心のセルは4つの近傍を持ちますが、角(コーナー)のセルは2つしか近傍を持ちません。
  • Queen型: 中心のセルは8つの近傍を持ち、角のセルは3つの近傍を持ちます。
  • Torus: 全てのセルが平等に4つの近傍を持ちます(角のセルも反対側とつながるため)。
cat("----------------------------------------------------------------\n")
cat("【近傍定義によるリンク数の比較】\n")
cat(sprintf("グリッドサイズ: %d 行 x %d 列 (全 %d セル)\n", rows, cols, n_cells))
cat("----------------------------------------------------------------\n")

# 各近傍定義における総リンク数(双方向)を表示
# card() 関数は各領域の近傍数を返します。その総和がリンクの総数(片方向)です。
cat(sprintf("(A) Rook型 (上下左右) の総リンク数: %d\n", sum(card(nb_rook))))
cat(sprintf("(B) Queen型 (全8方向) の総リンク数: %d\n", sum(card(nb_queen))))
cat(sprintf("(C) Rook型 (トーラス) の総リンク数: %d\n", sum(card(nb_torus))))

cat("\n----------------------------------------------------------------\n")
cat("【中心セルと角セルの近傍数】\n")
cat("----------------------------------------------------------------\n")

# 中心セル(ID: 13, 座標(3,3)付近)と 角セル(ID: 1, 左上)の近傍数を確認
center_id <- 13
corner_id <- 1

cat(sprintf("中心セル(ID=%d) の近傍数:\n", center_id))
cat(sprintf("  Rook : %d (上下左右)\n", card(nb_rook)[center_id]))
cat(sprintf("  Queen: %d (斜め含む)\n", card(nb_queen)[center_id]))
cat(sprintf("  Torus: %d\n", card(nb_torus)[center_id]))

cat(sprintf("\n角セル(ID=%d) の近傍数:\n", corner_id))
cat(sprintf("  Rook : %d (端なので2方向のみ)\n", card(nb_rook)[corner_id]))
cat(sprintf("  Queen: %d (端なので3方向のみ)\n", card(nb_queen)[corner_id]))
cat(sprintf("  Torus: %d (端がつながるため4方向)\n", card(nb_torus)[corner_id]))
----------------------------------------------------------------
【近傍定義によるリンク数の比較】
グリッドサイズ: 5 行 x 5 列 (全 25 セル)
----------------------------------------------------------------
(A) Rook型 (上下左右) の総リンク数: 80
(B) Queen型 (全8方向) の総リンク数: 144
(C) Rook型 (トーラス) の総リンク数: 100

----------------------------------------------------------------
【中心セルと角セルの近傍数】
----------------------------------------------------------------
中心セル(ID=13) の近傍数:
  Rook : 4 (上下左右)
  Queen: 8 (斜め含む)
  Torus: 4

角セル(ID=1) の近傍数:
  Rook : 2 (端なので2方向のみ)
  Queen: 3 (端なので3方向のみ)
  Torus: 4 (端がつながるため4方向)

可視化による比較

(注意)

Figure 1 のとおり、「Rook」と「Rook + Torus」に視覚的な違いは見られません。

spdep パッケージの plot 関数は、隣接しているセルの中心同士を「直線」で結びます。

  1. 通常のRook: 隣り合うセル(例:左端と、その右隣)が結ばれます。これで格子状の線ができます。
  2. Torus(トーラス)の場合:

    • 「左端のセル」と「右端のセル」が隣接しているとみなされます。
    • この2つを結ぶ線を引こうとすると、「左端から右端へ向かう一直線」が描かれます。
    • この直線は、既存のグリッドの横線(左→中央→右)と完全に重なってしまいます
    • 縦方向(上端と下端の接続)も同様に、既存の縦線の上に長い直線が重なって描画されます。

その結果、「線は描かれているが、既存の線の下(または上)に隠れてしまって見えない」というプロットになります。

# 画面を3分割してプロット
par(mfrow = c(1, 3), mar = c(1, 1, 3, 1))

# (A) Rook Plot
plot(nb_rook, coords, col = "blue", pch = 19, cex = 1.5, lwd = 2)
title(main = "Type: Rook\n(上下左右のみ)")
box()

# (B) Queen Plot
plot(nb_queen, coords, col = "red", pch = 19, cex = 1.5, lwd = 2)
title(main = "Type: Queen\n(斜めを含む)")
box()

# (C) Torus Plot (Rook)
plot(nb_torus, coords, col = "darkgreen", pch = 19, cex = 1.5, lwd = 1)
title(main = "Type: Rook + Torus\n(端同士が接続)")
box()
Figure 1
# 左上の角にあるセル(ID: 1)の隣接状況を確認

cat("----------------------------------------------------------------\n")
cat("【左上角セル (ID=1) の隣接リスト確認】\n")
cat("----------------------------------------------------------------\n")

# (A) 通常のRook
# 端なので、右(2)と下(6)の2つだけ
cat("(A) Rook型 の隣接ID:\n")
print(nb_rook[[1]])

# (C) Rook + Torus型
# 右(2)と下(6)に加え、
# 左につながる「右端(5)」と、上につながる「下端(21)」が含まれています
cat("\n(C) Rook + Torus型 の隣接ID:\n")
print(nb_torus[[1]])

cat("----------------------------------------------------------------\n")
cat("Torus型では、ID:1 の隣接に '5' (右端) と '21' (下端) が追加されています。\n")
cat("この '1' と '5' を結ぶ線が、1-2-3-4-5 のグリッド線と重なって描画されています。\n")
cat("----------------------------------------------------------------\n")
----------------------------------------------------------------
【左上角セル (ID=1) の隣接リスト確認】
----------------------------------------------------------------
(A) Rook型 の隣接ID:
[1] 2 6

(C) Rook + Torus型 の隣接ID:
[1]  2  5  6 21
----------------------------------------------------------------
Torus型では、ID:1 の隣接に '5' (右端) と '21' (下端) が追加されています。
この '1' と '5' を結ぶ線が、1-2-3-4-5 のグリッド線と重なって描画されています。
----------------------------------------------------------------

以上です。