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

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

Rで解析:多次元配列の操作を快適に「arrayhelpers」パッケージ

複数の条件を組み合わせた集計結果は、多次元配列としてまとめると都合よく扱えます。しかし、配列の一部を条件名で取り出したり、次元を畳み込んで表計算的に整えたり、作図用にロング形式のデータフレームへ変換したりする作業は手間がかかります。

このパッケージは便利だと思います。

本パッケージにより、多次元配列の操作を簡単にすることが可能です。収録されているコマンドは配列インデックスとベクトルインデックスを相互に変換する、次元数を指定して配列を畳み込み元の形状へ復元する、多次元配列をロング形式のデータフレームへ変換する、などが用意されています。また、次元名を指定した抽出や代入、形状を保ったままの行・列集計、グループ単位での合計も可能です。本パッケージの利用で、多次元配列を扱う前処理を簡潔に記述できるのではないかと考えます。

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

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

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

# パッケージのインストール
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配列、行列やベクトルも指定可能なし
olddim、dimnames、namesを要素に持つリスト、末尾からn番目の要素を使用NULL
nmakeNdによる変換を何段階さかのぼるか1L
usedim復元に使用する次元の指定TRUE
fromendTRUEでusedimに指定した数値を末尾から数えるFALSE
drop1次元配列をベクトルへ変換するか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

多次元配列をロング形式へ変換する:array2dfコマンド

ワイド形式の多次元配列を、値とラベルの列からなるロング形式のデータフレームまたは数値行列へ変換します。返り値の行数は各次元の長さの積、列数は次元数に1を加えた数です。作図や回帰分析へ値を利用するときの整形に利用できると考えます。

オプション意味初期値
x変換対象の配列なし
levels各次元の水準を指定するリスト、NULLで列を出力せず、NAで1から次元長までの数値列、TRUEでdimnamesを水準として使用なし
matrixTRUEでデータフレームではなく数値行列を返すFALSE
label.xxの値を格納する列の名前deparse(substitute(x))
na.rmxの値が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
dropFALSEで次元数を保持し、集計した次元の長さを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

配列の最初の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標本のグループを示す整数またはfactorNULL
dimグループ合計をとる次元1L
reorderグループを並べ替えるかTRUE
na.rm欠測値を除いて計算するかFALSE
無視されるなし
drop1次元配列をベクトルへ変換するか!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


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

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