Rで解析:多次元配列の操作を快適に「arrayhelpers」パッケージ
複数の条件を組み合わせた集計結果は、多次元配列としてまとめると都合よく扱えます。しかし、配列の一部を条件名で取り出したり、次元を畳み込んで表計算的に整えたり、作図用にロング形式のデータフレームへ変換したりする作業は手間がかかります。
このパッケージは便利だと思います。
本パッケージにより、多次元配列の操作を簡単にすることが可能です。収録されているコマンドは配列インデックスとベクトルインデックスを相互に変換する、次元数を指定して配列を畳み込み元の形状へ復元する、多次元配列をロング形式のデータフレームへ変換する、などが用意されています。また、次元名を指定した抽出や代入、形状を保ったままの行・列集計、グループ単位での合計も可能です。本パッケージの利用で、多次元配列を扱う前処理を簡潔に記述できるのではないかと考えます。
パッケージバージョンは1.1-2。Windows 11 x64 (build 26200)のR version 4.6.1で確認しています。
<おすすめのRに関する書籍です>
パッケージのインストール
下記コマンドを実行してください。
# パッケージのインストール
install.packages("arrayhelpers")
# パッケージの読み込み
library("arrayhelpers")コマンド例
詳細はコメント、パッケージのヘルプを確認してください。
Rの配列は、dim属性を持つベクトルとして格納されており、第1次元から順に添字が変化します。そのため、配列は「行・列・階層」の組み合わせによる配列インデックスと、格納順に沿った通し番号であるベクトルインデックスの両方で指定できます。本パッケージは、この2種類のインデックスの変換をはじめ、次元の畳み込みと復元、ロング形式への変換、次元名を用いた抽出といった操作をまとめて提供します。
以下の例では、サンプルデータとして、3か所の直売所における4品目の出荷量を2か月分まとめた3次元配列を作成します。
# 直売所×品目×月の3次元で出荷量(kg)を格納する
出荷量 <- array(
c(120, 85, 64, 143, 98, 71, 156, 110, 82, 47, 33, 25,
138, 92, 70, 165, 104, 88, 172, 121, 95, 52, 40, 29),
dim = c(3, 4, 2),
dimnames = list(
直売所 = c("江別", "千歳", "北広島"),
品目 = c("トマト", "スイートコーン", "ジャガイモ", "アスパラ"),
月 = c("7月", "8月")))配列インデックスとベクトルインデックスを相互に変換する:array2vec, vec2arrayコマンド
array2vecコマンドは配列インデックスからベクトルインデックスを求め、vec2arrayコマンドはその逆です。
| オプション | 意味 | 初期値 |
|---|---|---|
| iarr | 配列の各次元に対するインデックスを並べたベクトル | なし |
| dim | 配列の次元を表すベクトル、dimコマンドの返り値 | なし |
vec2arrayコマンドのオプションは下記です。
| オプション | 意味 | 初期値 |
|---|---|---|
| ivec | ベクトルインデックスを表すスカラー | なし |
| dim | 配列の次元を表すベクトル、dimコマンドの返り値 | なし |
# 出荷量が最大となる要素のベクトルインデックスを取得する
最大位置 <- which.max(出荷量)
最大位置
[1] 19
# ベクトルインデックスを配列インデックスへ変換する
最大index <- vec2array(最大位置, dim = dim(出荷量))
最大index
i j k
[1,] 1 3 2
# 変換した配列インデックスを次元名に置き換えて読み下す
c(dimnames(出荷量)[[1]][最大index[1]],
dimnames(出荷量)[[2]][最大index[2]],
dimnames(出荷量)[[3]][最大index[3]])
[1] "江別" "ジャガイモ" "8月"
# 「北広島・トマト・8月」の配列インデックスを行列で指定する
対象index <- matrix(c(3, 1, 2), nrow = 1)
対象index
[,1] [,2] [,3]
[1,] 3 1 2
# 配列インデックスをベクトルインデックスへ変換する
対象位置 <- array2vec(対象index, dim = dim(出荷量))
対象位置
[1] 15
# 行列指定と通し番号指定で同じ要素が取り出せることを確認する
c(出荷量[対象index], 出荷量[対象位置])
[1] 70 70配列を指定した次元数へ畳み込み、元の形状へ復元する:makeNd, restoredimコマンド
makeNdコマンドは、指定した次元数に満たない場合は長さ1の次元を追加し、超える場合は余分な次元を最後の次元にまとめます。畳み込み前の形状はold属性として保持されるため、restoredimコマンドで元に戻せます。
| オプション | 意味 | 初期値 |
|---|---|---|
| a | 配列、行列やベクトルも指定可能 | なし |
| N | 目的の次元数、0でdim属性とdimnames属性を削除しベクトル化 | なし |
restoredimコマンドのオプションは下記です。
| オプション | 意味 | 初期値 |
|---|---|---|
| a | 配列、行列やベクトルも指定可能 | なし |
| old | dim、dimnames、namesを要素に持つリスト、末尾からn番目の要素を使用 | NULL |
| n | makeNdによる変換を何段階さかのぼるか | 1L |
| usedim | 復元に使用する次元の指定 | TRUE |
| fromend | TRUEでusedimに指定した数値を末尾から数える | FALSE |
| drop | 1次元配列をベクトルへ変換するか | FALSE |
# 現在の次元数を確認する
ndim(出荷量)
[1] 3
# 3次元配列を2次元へ畳み込み, 月方向を列に展開する
平坦化 <- makeNd(出荷量, 2)
平坦化
直売所 [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
江別 120 143 156 47 138 165 172 52
千歳 85 98 110 33 92 104 121 40
北広島 64 71 82 25 70 88 95 29
attr(,"old")
attr(,"old")[[1]]
attr(,"old")[[1]]$names
NULL
attr(,"old")[[1]]$dimnames
attr(,"old")[[1]]$dimnames$直売所
[1] "江別" "千歳" "北広島"
attr(,"old")[[1]]$dimnames$品目
[1] "トマト" "スイートコーン" "ジャガイモ" "アスパラ"
attr(,"old")[[1]]$dimnames$月
[1] "7月" "8月"
attr(,"old")[[1]]$dim
[1] 3 4 2
# 畳み込み後の次元を確認する
dim(平坦化)
[1] 3 8
# 次元数を4に増やし、長さ1の次元を追加する
dim(makeNd(出荷量, 4))
[1] 3 4 2 1
# old属性を手がかりに元の3次元へ復元する
復元 <- restoredim(平坦化)
復元
, , 月 = 7月
品目
直売所 トマト スイートコーン ジャガイモ アスパラ
江別 120 143 156 47
千歳 85 98 110 33
北広島 64 71 82 25
, , 月 = 8月
品目
直売所 トマト スイートコーン ジャガイモ アスパラ
江別 138 165 172 52
千歳 92 104 121 40
北広島 70 88 95 29
# 復元結果が元の配列と一致するか確認する
identical(復元, 出荷量)
[1] TRUE
<おすすめのRに関する書籍です>
多次元配列をロング形式へ変換する:array2dfコマンド
ワイド形式の多次元配列を、値とラベルの列からなるロング形式のデータフレームまたは数値行列へ変換します。返り値の行数は各次元の長さの積、列数は次元数に1を加えた数です。作図や回帰分析へ値を利用するときの整形に利用できると考えます。
| オプション | 意味 | 初期値 |
|---|---|---|
| x | 変換対象の配列 | なし |
| levels | 各次元の水準を指定するリスト、NULLで列を出力せず、NAで1から次元長までの数値列、TRUEでdimnamesを水準として使用 | なし |
| matrix | TRUEでデータフレームではなく数値行列を返す | FALSE |
| label.x | xの値を格納する列の名前 | deparse(substitute(x)) |
| na.rm | xの値がNAとなる行を削除するか | FALSE |
# 3次元配列をロング形式のデータフレームへ変換する
長形式 <- array2df(出荷量,
levels = list(直売所 = TRUE, 品目 = TRUE, 月 = TRUE),
label.x = "出荷量")
# 先頭2行を確認する
head(長形式, 2)
出荷量 直売所 品目 月
1 120 江別 トマト 7月
2 85 千歳 トマト 7月
# 各列の型を確認する
str(長形式)
'data.frame': 24 obs. of 4 variables:
$ 出荷量: num 120 85 64 143 98 71 156 110 82 47 ...
$ 直売所: Factor w/ 3 levels "江別","千歳",..: 1 2 3 1 2 3 1 2 3 1 ...
$ 品目 : Factor w/ 4 levels "トマト","スイートコーン",..: 1 1 1 2 2 2 3 3 3 4 ...
$ 月 : Factor w/ 2 levels "7月","8月": 1 1 1 1 1 1 1 1 1 1 ...
# NULLを指定した次元は列として出力されない
head(array2df(出荷量,
levels = list(NULL, 品目 = TRUE, NULL),
label.x = "出荷量"), 6)
出荷量 品目
1 120 トマト
2 85 トマト
3 64 トマト
4 143 スイートコーン
5 98 スイートコーン
6 71 スイートコーン
# NAを指定すると次元番号がそのまま数値列となる
head(array2df(出荷量,
levels = list(直売所 = NA, 品目 = NA, 月 = NA),
label.x = "出荷量", matrix = TRUE), 5)
出荷量 直売所 品目 月
[1,] 120 1 1 1
[2,] 85 2 1 1
[3,] 64 3 1 1
[4,] 143 1 2 1
[5,] 98 2 2 1
# 欠測を含む配列を用意する
欠測入り <- 出荷量
欠測入り[2, 4, 1] <- NA
# na.rmにTRUEを指定し、欠測行を除いた行数を確認する
nrow(array2df(欠測入り,
levels = list(直売所 = TRUE, 品目 = TRUE, 月 = TRUE),
label.x = "出荷量", na.rm = TRUE))
[1] 23形状を保ったまま行・列の合計と平均を求める:colSums, rowSums, colMeans, rowMeansコマンド
base環境の同名コマンドを拡張したコマンドで、dropオプションにFALSEを指定すると次元数が保持され、集計した次元の長さが1になります。形状を保ったまま集計できるため、集計結果を元の配列と組み合わせて計算する場面で扱いやすく便利です。
| オプション | 意味 | 初期値 |
|---|---|---|
| x | 数値、複素数、整数、論理値からなる2次元以上の配列、または数値のデータフレーム | なし |
| na.rm | 欠測値を除いて計算するか | FALSE |
| dims | 行または列とみなす次元の数、row系は「dims + 1」以降、col系は「1からdims」までを集計 | 1L |
| drop | FALSEで次元数を保持し、集計した次元の長さを1にする | TRUE |
AsIsクラスに対応するcolSums.AsIs、colMeans.AsIs、rowSums.AsIs、rowMeans.AsIsも用意されています。
# 直売所ごとの総出荷量を求める
rowSums(出荷量)
江別 千歳 北広島
993 683 524
# 直売所ごとの平均出荷量を求める
rowMeans(出荷量)
江別 千歳 北広島
124.125 85.375 65.500
# 第1次元を集計し、品目×月の表を得る
colSums(出荷量)
月
品目 7月 8月
トマト 269 300
スイートコーン 312 357
ジャガイモ 348 388
アスパラ 105 121
# 形状を保ったまま集計し、集計した次元の長さを1にする
colSums(出荷量, drop = FALSE)
, , 月 = 7月
品目
直売所 トマト スイートコーン ジャガイモ アスパラ
[1,] 269 312 348 105
, , 月 = 8月
品目
直売所 トマト スイートコーン ジャガイモ アスパラ
[1,] 300 357 388 121
# dimsに2を指定し、第1・第2次元をまとめて集計する
colSums(出荷量, dims = 2)
7月 8月
1034 1166
# rowSumsでdimsに2を指定し、第3次元のみを集計する
rowSums(出荷量, dims = 2)
品目
直売所 トマト スイートコーン ジャガイモ アスパラ
江別 258 308 328 99
千歳 177 202 231 73
北広島 134 159 177 54次元名を指定して配列を抽出・代入する:slice, slice <- hogeコマンド
角括弧による抽出の代替となるコマンドで、次元をiが第1次元、jが第2次元というように引数名で指定します。抽出したい次元だけを書けばよいため、カンマの数を数える必要がなく、次元数の多い配列でも記述が明確になります。代入版のslice <- hogeも用意されています。
| オプション | 意味 | 初期値 |
|---|---|---|
| a | ベクトル、行列、配列 | なし |
| … | 次元ごとのインデックス指定、引数名iが第1次元、jが第2次元のように対応し、指定方法は角括弧による抽出と同じ | なし |
| drop | 抽出後に長さ1の次元を落とすか | TRUE |
代入版のslice <- hoge コマンドでは、上記に加えて下記のオプションを指定します。
| オプション | 意味 | 初期値 |
|---|---|---|
| value | 代入する値 | なし |
# 第2次元を次元名で指定して2品目を抽出する
slice(出荷量, j = c("トマト", "アスパラ"))
, , 月 = 7月
品目
直売所 トマト アスパラ
江別 120 47
千歳 85 33
北広島 64 25
, , 月 = 8月
品目
直売所 トマト アスパラ
江別 138 52
千歳 92 40
北広島 70 29
# 第1次元と第3次元を指定し、次元数を保ったまま抽出する
slice(出荷量, i = 1, k = 2, drop = FALSE)
, , 月 = 8月
品目
直売所 トマト スイートコーン ジャガイモ アスパラ
江別 138 165 172 52
# 元データを複製する
補正 <- 出荷量
# 千歳の8月を集計対象外として0を代入する
slice(補正, i = "千歳", k = "8月") <- 0
補正
, , 月 = 7月
品目
直売所 トマト スイートコーン ジャガイモ アスパラ
江別 120 143 156 47
千歳 85 98 110 33
北広島 64 71 82 25
, , 月 = 8月
品目
直売所 トマト スイートコーン ジャガイモ アスパラ
江別 138 165 172 52
千歳 0 0 0 0
北広島 70 88 95 29
<おすすめのRに関する書籍です>
配列の最初の2つの次元を入れ替える:taコマンド
3次元以上の配列に対して、第1次元と第2次元を入れ替えます。base環境のtコマンドは行列のみが対象ですが、taコマンドは3次元以上でも第3次元以降を保ったまま転置できます。表の縦横を入れ替えて確認したい場面で利用できます。
| オプション | 意味 | 初期値 |
|---|---|---|
| x | 配列 | なし |
# 第1次元と第2次元を入れ替えて品目を行に配置する
ta(出荷量)
, , 月 = 7月
直売所
品目 江別 千歳 北広島
トマト 120 85 64
スイートコーン 143 98 71
ジャガイモ 156 110 82
アスパラ 47 33 25
, , 月 = 8月
直売所
品目 江別 千歳 北広島
トマト 138 92 70
スイートコーン 165 104 88
ジャガイモ 172 121 95
アスパラ 52 40 29
# 入れ替え後の次元を確認する
dim(ta(出荷量))
[1] 4 3 2形状復元用の属性を取り除く:deloldコマンド
makeNdコマンドで畳み込んだ配列には、元の形状を記録したold属性が付与されます。この属性は表示すると長くなるため、復元が不要になった段階でdeloldコマンドを使い、出力を簡潔に保ちます。
| オプション | 意味 | 初期値 |
|---|---|---|
| a | 配列 | なし |
# makeNdで畳み込んだ配列を用意する
畳込 <- makeNd(出荷量, 2)
# 付与されている属性を確認する
names(attributes(畳込))
[1] "dim" "old" "dimnames"
# old属性を取り除いて表示を簡潔にする
delold(畳込)
直売所 [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
江別 120 143 156 47 138 165 172 52
千歳 85 98 110 33 92 104 121 40
北広島 64 71 82 25 70 88 95 29
# 属性が削除されたことを確認する
names(attributes(delold(畳込)))
[1] "dim" "dimnames"グループ単位で配列を合計する:groupsumコマンド
base環境のrowsumコマンドを拡張し、任意の次元に沿ってグループ別の合計を求めます。集計しない次元は保持されるため、配列の形を保ったまま処理できます。
| オプション | 意味 | 初期値 |
|---|---|---|
| x | 集計対象の配列 | なし |
| group | 標本のグループを示す整数またはfactor | NULL |
| dim | グループ合計をとる次元 | 1L |
| reorder | グループを並べ替えるか | TRUE |
| na.rm | 欠測値を除いて計算するか | FALSE |
| … | 無視される | なし |
| drop | 1次元配列をベクトルへ変換するか | !is.array(x) |
# 直売所を地域区分にまとめるグループを用意する
地域 <- c("石狩南部", "石狩南部", "石狩中部")
# 第1次元を地域単位で合計する
groupsum(出荷量, group = 地域, dim = 1)
, , 月 = 7月
品目
直売所 トマト スイートコーン ジャガイモ アスパラ
石狩中部 64 71 82 25
石狩南部 205 241 266 80
, , 月 = 8月
品目
直売所 トマト スイートコーン ジャガイモ アスパラ
石狩中部 70 88 95 29
石狩南部 230 269 293 92
# 第2次元を品目分類単位で合計する
groupsum(出荷量, group = c("果菜", "果菜", "根菜", "茎菜"), dim = 2)
, , 月 = 7月
品目
直売所 果菜 茎菜 根菜
江別 263 47 156
千歳 183 33 110
北広島 135 25 82
, , 月 = 8月
品目
直売所 果菜 茎菜 根菜
江別 303 52 172
千歳 196 40 121
北広島 158 29 95
# 地域別の集計結果へ、さらに品目分類別の集計を重ねる
groupsum(groupsum(出荷量, group = 地域, dim = 1),
group = c("果菜", "果菜", "根菜", "茎菜"), dim = 2)
, , 月 = 7月
品目
直売所 果菜 茎菜 根菜
石狩中部 135 25 82
石狩南部 446 80 266
, , 月 = 8月
品目
直売所 果菜 茎菜 根菜
石狩中部 158 29 95
石狩南部 499 92 293
<おすすめのRに関する書籍です>
この記事が誰かの役に立ちますように。