本文へスキップ
からだにいいもの

Rのトピックスを中心に『まだ、まだ、知らない、役に立つ情報?』を発信します。

Rで解析:研究の作業手順で再利用しやすい汎用処理を集めた「thisutils」パッケージ

研究や解析のコードでは、行列から値の大きい要素を取り出す、分類結果を評価する、リスト形式の列を平坦化するといった細かな処理が繰り返し登場します。しかし、こうした処理をそのつど書き起こすと、実装のばらつきや動作確認に手間がかかります。

「thisutils」パッケージは、研究の作業手順で再利用しやすい汎用処理をまとめたパッケージです。疎行列や密行列から行・列ごとに上位k個を抽出する処理や、予測ラベルと正解ラベルから正解率・マクロF値・調整ランド指数などをまとめて算出する処理が可能です。また、リスト列を持つデータフレームの平坦化や、近傍ラベルの多様性を表すLISI指標の計算も収録されています。さらに、タイムスタンプや色を付けたメッセージ出力や、文字列に埋め込んだ式の評価も可能です。

本パッケージの利用で、解析コードの共通部分を整理し、簡単に再利用できるのではないかと考えます。

パッケージバージョンは0.5.0。Windows 11 x64 (build 26200)のR version 4.6.1で確認しています。

パッケージのインストール

下記コマンドを実行してください。

# パッケージのインストール
install.packages("thisutils")

# パッケージの読み込み
library("thisutils")
スポンサーリンク

コマンド例

詳細はコメント、パッケージのヘルプを確認してください。

このパッケージの関数は、疎行列や密行列の要素選択、分類結果の評価、リスト列の展開など、互いに独立した処理の集まりです。

文中の「疎行列」は多くの要素が0の行列、「上位k個の抽出」は各行または各列で値の大きい順にk個だけ残す操作を指します。「LISI指標」は、ある地点の近傍にどれだけ多様なラベルが混ざっているかを表す値で、1に近いほど周囲が同じラベルで占められ、対象ラベル数に近いほど均等に混ざっていることを示します。

疎な行列の上位k個の選択:run_sparse_topk_storedコマンド

疎行列の各列について、保存されている非ゼロ要素の中から値の大きい上位k個を残し、その行番号(idx)と値(value)を返します。

オプション意味初期値
xdgCMatrix型または変換可能なオブジェクトなし
k各列または行に残す上位要素の個数なし
by選択の方向。”col”は列ごと、”row”は行ごとに順位付けするc(“col”, “row”)
decreasing値の大きい順に並べるかどうかの指定TRUE
# 市区町村間の観光客の移動を疎行列で表現
# 行が出発地、列が到着地
area <- c("左京区", "右京区", "伏見区", "山科区")
flow <- Matrix::sparseMatrix(
  i = c(1, 1, 2, 3, 3, 4),
  j = c(2, 3, 1, 1, 4, 3),
  x = c(120, 80, 95, 60, 150, 45),
  dims = c(4, 4),
  dimnames = list(area, area)
)
# 到着地ごとに流入の多い上位2地区を残す
run_sparse_topk_stored(flow, k = 2, by = "col")
$idx
     [,1] [,2]
[1,]    2    3
[2,]    1   NA
[3,]    1    4
[4,]    3   NA

$value
     [,1] [,2]
[1,]   95   60
[2,]  120   NA
[3,]   80   45
[4,]  150   NA

結果の各行は到着地(入力の列)に対応します。1行目の左京区には行番号2の右京区(95)と行番号3の伏見区(60)から流入があり、値の大きい順に並んでいます。該当がない位置はNAになります。

分類指標の算出:classification_metrics_computeコマンド

予測したクラスラベルと正解のクラスラベルを比較し、正解率やマクロF値、純度、正規化相互情報量、調整ランド指数などの評価指標をまとめて返します。

オプション意味初期値
predicted予測したクラスラベルの文字ベクトルなし
truth正解のクラスラベルの文字ベクトル。predictedと同じ長さなし
rare_threshold希少クラスとみなす出現割合の閾値。この値以下のクラスがrare_recallの対象になる0.05
# 宇治茶の等級を自動判定した結果
predicted <- c("特級", "特級", "一級", "二級", "一級", "二級", "特級", "一級")
# 実際の等級
truth <- c("特級", "一級", "一級", "二級", "一級", "一級", "特級", "二級")
# 判定結果の評価指標をまとめて算出
classification_metrics_compute(predicted, truth)
$accuracy
[1] 0.625

$macro_f1
[1] 0.6238095

$purity
[1] 0.625

$nmi
[1] 0.3666953

$ari
[1] 0

$rare_recall
[1] NA

$class_table
  class precision recall        f1 support
1  一級 0.6666667    0.5 0.5714286       4
2  特級 0.6666667    1.0 0.8000000       2
3  二級 0.5000000    0.5 0.5000000       2

希少クラスが存在しないため、rare_recallはNAになります。クラスごとの適合率・再現率・F値はclass_tableにまとまります。

リスト列の展開:unnest_funコマンド

リスト形式の列を持つデータフレームを、リストの各要素が1行になるように展開します。

オプション意味初期値
dataデータフレームなし
cols展開する列なし
keep_empty長さ0の要素を欠損値1行として残すかどうかの指定FALSE
# 直売所ごとの取扱品目をリスト列に持つデータフレーム
sales <- data.frame(
  直売所 = c("大原", "宇治田原", "和束"),
  地域 = c("左京区", "綴喜郡", "相楽郡"),
  stringsAsFactors = FALSE
)
# 品目をリスト列として追加
sales$品目 <- list(
  c("九条ねぎ", "聖護院かぶ"),
  c("宇治茶"),
  c("宇治茶", "丹波栗", "山科なす")
)
# 品目を1行ずつに展開
unnest_fun(sales, cols = "品目")
    直売所   地域       品目
1     大原 左京区   九条ねぎ
2     大原 左京区 聖護院かぶ
3 宇治田原 綴喜郡     宇治茶
4     和束 相楽郡     宇治茶
5     和束 相楽郡     丹波栗
6     和束 相楽郡   山科なす

# 一部の直売所を品目未登録にしたデータ
sales2 <- sales
sales2$品目 <- list(
  c("九条ねぎ", "聖護院かぶ"),
  character(0),
  c("丹波栗")
)
# 既定では未登録の直売所は結果から外れる
unnest_fun(sales2, cols = "品目")
  直売所   地域       品目
1   大原 左京区   九条ねぎ
2   大原 左京区 聖護院かぶ
3   和束 相楽郡     丹波栗

# keep_empty = TRUE で未登録の直売所を欠損値として残す
unnest_fun(sales2, cols = "品目", keep_empty = TRUE)
    直売所   地域       品目
1     大原 左京区   九条ねぎ
2     大原 左京区 聖護院かぶ
3 宇治田原 綴喜郡       <NA>
4     和束 相楽郡     丹波栗

LISI指標の計算:compute_lisiコマンド

各観測点の近傍に含まれるラベルの多様性を、LISI指標として計算します。埋め込み座標とメタデータを渡すと、指定した列ごとに近傍の混ざり具合を評価できます。

オプション意味初期値
X行に観測点、列に埋め込み座標や特徴量を持つ行列状のオブジェクトなし
meta_data観測点1つにつき1行のデータフレームなし
label_colnamesmeta_dataのうち評価対象とする列名の文字ベクトルなし
perplexity近傍の有効なサイズ30
tol目標のperplexityを二分探索する際の許容誤差1e-05
max_iter二分探索の最大反復回数50
knn_algorithm厳密な近傍探索の方式。”auto”はデータ数に応じてclusteredかbrute_forceを選ぶc(“auto”, “brute_force”, “clustered”)
n_threadsC++ワーカーのスレッド数。NULLで自動選択NULL
max_dense_bytes密な入力とそのコピーに許容する推定バイト数の上限Inf
# 擬似データ:観測点の2次元埋め込み座標
set.seed(2024)
coords <- rbind(
  matrix(stats::rnorm(60, mean = 0), ncol = 2),
  matrix(stats::rnorm(60, mean = 3), ncol = 2)
)
# 各観測点の調査班と主な栽培品目
meta <- data.frame(
  調査班 = rep(c("北班", "南班"), each = 30),
  品目 = sample(c("宇治茶", "九条ねぎ"), 60, replace = TRUE)
)
# 近傍に含まれるラベルの多様性をLISIで評価
lisi <- compute_lisi(coords, meta, c("調査班", "品目"), perplexity = 10)
# 先頭6行を表示
head(lisi)
    調査班     品目
1 1.000489 1.466986
2 1.000088 1.414883
3 1.000422 1.442324
4 1.065254 1.660980
5 1.007146 1.917521
6 1.058346 1.513733

調査班は座標がまとまって配置されているため近傍が同じ班で占められ、値が1に近くなります。品目は座標と無関係に割り振ったため近傍で混ざり、ラベル数の2に近い値になります。

インライン式の解析:parse_inline_expressionsコマンド

文字列の中に波かっこで書いた式を評価し、その結果を埋め込んだ文字列を返します。cliパッケージの記法(.valや.pkgなど)はそのまま残ります。

オプション意味初期値
text評価する式を含む文字列なし
env式を評価する環境parent.frame()
# レポート文に作付面積の値を差し込む
menseki <- 3.2
parse_inline_expressions("作付面積は {menseki} ha")
[1] "作付面積は 3.2 ha"

# 式を書くとその計算結果が入る
shuryo <- 180
parse_inline_expressions("10aあたり {shuryo / menseki} kg")
[1] "10aあたり 56.25 kg"

# cliの記法は展開されずに残る
chiku <- "和束"
parse_inline_expressions("{.val {chiku}}")
[1] "{.val 和束}"

近傍の探索:run_biocneighbors_knnコマンド

参照行列の各行について、最近傍k個の行番号(idx)と距離(dist)を求めます。BiocNeighborsパッケージを利用するため、未導入の場合は BiocManager::install(“BiocNeighbors”) で先に導入してください。

オプション意味初期値
reference行に観測値を持つ数値の参照行列なし
query同じ列数を持つ数値のクエリ行列。省略可NULL
k求める近傍点の数なし
metric距離の種類。”euclidean”または”cosine”c(“euclidean”, “cosine”)
exclude_selfqueryがNULLのときに自分自身を近傍から除くかどうかの指定FALSE
n_threadsBiocNeighborsが使うスレッド数1L
# 圃場ごとの気温と土壌水分の測定値(1列目が気温、2列目が水分)
field <- matrix(
  c(12.1, 12.4, 18.9, 19.2, 12.6, 19.5, 25.1, 24.7, 25.4, 18.7,
    5.2, 5.0, 8.1, 7.9, 5.4, 8.3, 2.1, 2.4, 2.0, 7.7),
  ncol = 2
)
# 各圃場に測定値が近い上位2圃場を探す
run_biocneighbors_knn(field, k = 2)
$idx
      [,1] [,2]
 [1,]    2    5
 [2,]    1    5
 [3,]    4   10
 [4,]    3    6
 [5,]    2    1
 [6,]    4    3
 [7,]    9    8
 [8,]    7    9
 [9,]    7    8
[10,]    3    4

$dist
           [,1]      [,2]
 [1,] 0.3605551 0.5385165
 [2,] 0.3605551 0.4472136
 [3,] 0.3605551 0.4472136
 [4,] 0.3605551 0.5000000
 [5,] 0.4472136 0.5385165
 [6,] 0.5000000 0.6324555
 [7,] 0.3162278 0.5000000
 [8,] 0.5000000 0.8062258
 [9,] 0.3162278 0.8062258
[10,] 0.4472136 0.5385165

1行目の圃場に最も近いのは行番号2の圃場(距離0.36)、次が行番号5の圃場です。exclude_selfが既定のFALSEでも、queryを省略した場合は自分自身が近傍から除かれます。

密な行列の上位k個の選択:run_dense_topkコマンド

密な数値行列について、各列または各行の要素から上位k個を残し、その位置(行番号または列番号)と値を返します。

オプション意味初期値
x数値行列または変換可能なオブジェクトなし
k各列または行に残す要素の個数なし
by選択の方向。”col”は列ごと、”row”は行ごとに順位付けするc(“col”, “row”)
decreasing値の大きい順に並べるかどうかの指定TRUE
# 月別・観光地別の入込客数(単位:千人)
spot <- c("嵐山", "清水寺", "伏見稲荷", "天橋立")
month <- c("4月", "5月", "6月")
visitors <- matrix(
  c(320, 210, 90,
    280, 260, 110,
    350, 300, 130,
    150, 180, 60),
  nrow = 4, byrow = TRUE,
  dimnames = list(spot, month)
)
# 各月で入込客数の多い上位2地点(結果の各行が月に対応)
run_dense_topk(visitors, k = 2, by = "col")
$idx
     [,1] [,2]
[1,]    3    1
[2,]    3    2
[3,]    3    2

$value
     [,1] [,2]
[1,]  350  320
[2,]  300  260
[3,]  130  110

# 各地点で入込客数の多い上位2か月(結果の各行が地点に対応)
run_dense_topk(visitors, k = 2, by = "row")
$idx
     [,1] [,2]
[1,]    1    2
[2,]    1    2
[3,]    1    2
[4,]    2    1

$value
     [,1] [,2]
[1,]  320  210
[2,]  280  260
[3,]  350  300
[4,]  180  150

by = “col” の結果は入力の列(月)ごと、by = “row” の結果は入力の行(地点)ごとの並びです。1つ目の結果の1行目は4月で、行番号3の伏見稲荷(350)と行番号1の嵐山(320)が上位2地点です。

書式付きメッセージの出力:log_messageコマンド

進捗や結果を、種別に応じた記号やインデント、タイムスタンプを付けて表示します。verbose = FALSE やオプション設定で出力の有無をまとめて切り替えられます。

オプション意味初期値
出力するメッセージ本体なし
expr標準出力やメッセージを捕捉し、log_messageの書式で再表示する式NULL
verboseメッセージを表示するかどうかの指定NULL
message_typeメッセージの種別c(“info”, “success”, “warning”, “error”, “running”, “ask”)
cli_modelcliパッケージを使って表示するかどうかの指定TRUE
levelインデントの深さ。1は字下げなし1
symbolインデントに使う記号。指定するとlevelより優先される” “
text_color文字色。Rの色名や16進表記を指定NULL
back_color背景色。指定方法はtext_colorと同じNULL
text_style文字装飾。”bold”や”italic”などを1つ以上指定NULL
multiline_indent複数行のとき各行に同じ書式を付けるかどうかの指定FALSE
timestamp先頭に現在時刻を表示するかどうかの指定TRUE
timestamp_formatタイムスタンプの書式文字列paste0(“[“, format(Sys.time(), “%Y-%m-%d %H:%M:%S”), “] “)
timestamp_styleタイムスタンプに本文と同じ装飾を適用するかどうかの指定FALSE
plain_text本文のみを出力し、記号や時刻などを省くかどうかの指定FALSE
.envir呼び出しを評価する環境parent.frame()
.frameエラー報告に使うフレーム.envir
# 集計処理の開始を記録
log_message("宇治茶の出荷量集計を開始します", timestamp = FALSE)
ℹ 宇治茶の出荷量集計を開始します

# レベル2のインデントを付けて内訳を出力
log_message("対象は左京区・宇治市・和束町の3地区です", timestamp = FALSE, level = 2)
ℹ   対象は左京区・宇治市・和束町の3地区です

# 実行中の処理として出力
log_message("地区別に集計しています", message_type = "running", timestamp = FALSE)
◌ 地区別に集計しています

# 完了を成功メッセージとして出力
log_message("集計が完了しました", message_type = "success", timestamp = FALSE)
✔ 集計が完了しました

# verbose = FALSE を指定した行は出力されない
log_message("この行は表示されません", verbose = FALSE, timestamp = FALSE)
# 既定では先頭にタイムスタンプが付く
log_message("処理を終了します")
ℹ [2026-09-04 06:24:31] 処理を終了します

最終行のタイムスタンプ(角かっこ内の日付と時刻)は実行した時点の値になるため、環境によって表示が変わります。

# exprに渡した式の標準出力を、log_messageの書式でまとめ直す
log_message(
  expr = {
    amount <- c(120, 95, 80)
    cat("合計出荷量:", sum(amount), "kg\n")
  },
  message_type = "running",
  timestamp = FALSE
)
◌ 合計出荷量: 295 kg

この記事が誰かの役に立ちますように。

スポンサーリンク
価格および配送状況は変更される場合があります。購入時は商品ページをご確認ください。
当サイトに表示されている商品情報はAmazonから提供されたものであり、更新または削除される場合があります。
karada-goodはAmazonアソシエイトとして、適格販売により収入を得ています。