Google
WWWを検索
サイト内を検索

lapply, sapply, vapply, apply, tapplyの順番に説明していきます。

1 データセットの作成

applyを試す前に、処理するデータをつくります。

df01 <- with(list(n=100), {
    x <- 1:n
    p <- runif(n)
    d <- ifelse(p<0.3, "A", ifelse(p<0.7, "B", "C"))
    z <- rnorm(n) + p
    y <- 1 + 2*x - z + rnorm(n)
    data.frame(y, x, z, d)
})

中はhead(df01)とでもして確認してください。

2 引数がベクターもしくはリスト

ベクターもしくはリストの引数を取れるsapply/lapplyは、名前からすると簡素版と言った感じですが、(ベクターに二次元構造を持たせた)行列や、(リストに各要素の長さが等しいという制約のついた)データフレームを要求するapplyよりも、構造的にはむしろ基本です。

2.1 lapply

lapplyがもっとも単純な関数で、戻り値としてリストを返してきます。

lapply(unique(df01$d), function(n){ sprintf("列名%s", n)})
[[1]]
[1] "列名B"

[[2]]
[1] "列名C"

[[3]]
[1] "列名A"

第1引数がベクターもしくはリストで、第2引数がベクターもしくはリストのそれぞれの要素を処理していく関数となります。 lapplyに渡す関数の引数が複数ある場合は、lapplyの第3引数以降に、例えば以下のように指定します。

lapply(unique(df01$d), function(n, m){ sprintf("列名%s%s", n, m)}, "です")
[[1]]
[1] "列名Bです"

[[2]]
[1] "列名Cです"

[[3]]
[1] "列名Aです"

2.2 sapply

戻り値がリストのままでは使いづらいことが多いので、ラッパー関数sapplyが用意されていて、ベクトルで結果を得ることができます。

sapply(unique(df01$d), function(n){ sprintf("列名%s", n)})
      B       C       A 
"列名B" "列名C" "列名A" 

以下のようにオプションをつけると、sapplylapplyと同じ動作になります。

sapply(unique(df01$d), function(n){ sprintf("列名%s", n)}, simplify = FALSE, USE.NAMES = TRUE)

2.3 vapply

vapplysapplyと同じ目的に使いますが、戻り値の型をチェックするところがsapplyと異なります。

sapply(df01, max) # エラーにならない
vapply(df01, max, numeric(1)) # エラーになる

利用頻度は低いようです。

3 引数が行列もしくはデータフレーム

引数が行列もしくはデータフレームのときはapply関数を用います。行ごと、もしくは列ごとに関数を用いることができます。 まずはmean関数と行列にする都合で、numeric型の列だけ選んでおきます。

df02 <- df01[,c("y", "x", "z")] 

3.1 引数がデータフレーム

第2引数が1だと行ごとの処理、2だと列ごとの処理になります。

apply(df02, 1, mean) # 行ごとに合計
  [1]   2.148507   2.808693   3.592545   3.408740   5.862681   6.275644
  [7]   6.859732   8.381733   9.319093  10.425042  10.628468  12.352579
 [13]  13.321620  13.771991  15.396994  16.214455  17.649765  18.841002
 [19]  19.054818  20.632367  21.280506  22.435104  23.430766  24.534064
 [25]  25.546526  26.115597  27.674055  27.837187  29.340145  30.395691
 [31]  30.888120  32.165604  32.604890  34.813334  35.257019  36.668818
 [37]  37.109323  37.843648  39.003415  40.272893  41.496002  42.490272
 [43]  43.088541  43.963004  45.321158  46.251095  47.050991  48.548980
 [49]  49.253420  50.412183  51.510090  52.232884  53.769973  53.947695
 [55]  55.288680  56.044641  57.564320  58.426295  59.562063  60.338447
 [61]  60.941623  62.750436  63.174948  64.332510  65.525537  66.639497
 [67]  67.872272  68.323446  69.373623  70.550177  71.429091  71.914146
 [73]  73.081721  74.394770  75.465444  76.183336  77.817141  78.642595
 [79]  79.481254  79.975997  80.835012  82.748137  82.642287  84.298794
 [85]  85.356045  86.139909  87.098656  88.725484  89.724155  90.189025
 [91]  91.773052  92.272288  92.792678  93.984802  95.504618  96.421991
 [97]  97.574773  98.687522  99.859107 100.283764
apply(df02, 2, mean) # 列ごとに合計
          y           x           z 
101.5788253  50.5000000   0.4263418 
apply(df02, 2, mean, simplify=FALSE) # 結果をlistにする
$y
[1] 101.5788

$x
[1] 50.5

$z
[1] 0.4263418

行列やデータフレームの行ごと、列ごとの合計と平均をとるだけであれば、colSums, rowSums, colMeans, rowMeansを使う方が高速です。

3.2 引数が行列

行列でもデータフレームと同様に処理できます。

X <- as.matrix(df02) # データフレームを行列にする
apply(X, 1, max) # 行ごとに最大値
  [1]   5.575355   4.482252   6.184366   6.936873  13.816381  12.164989
  [7]  13.736850  16.446137  19.829365  20.956880  21.317179  26.685183
 [13]  26.979306  25.897898  30.691625  32.537407  37.106218  38.578099
 [19]  35.372825  39.539666  42.129310  43.019563  46.961406  51.341433
 [25]  51.698436  51.281754  55.722249  55.012191  58.070626  58.722266
 [31]  59.277179  64.024715  65.220989  69.353020  70.366368  71.185812
 [37]  73.143104  74.723164  77.275330  81.930696  82.361660  84.882314
 [43]  86.906068  86.197302  89.603207  91.183188  94.284516  97.763282
 [49] 101.954752 101.115562 103.162408 103.865940 110.045337 108.443894
 [55] 110.873589 110.315316 117.263397 116.860230 118.132124 120.010923
 [61] 120.397517 125.840065 126.425628 129.218537 132.564670 134.331262
 [67] 135.392665 137.607316 138.151054 141.679776 143.157877 143.297121
 [73] 143.434346 149.167708 151.936342 152.437569 156.760725 158.639181
 [79] 159.063902 160.432364 161.307687 162.921173 165.104194 167.387090
 [85] 169.898819 171.886070 173.277552 177.700193 178.291831 180.296001
 [91] 182.220004 186.829930 183.994168 188.078932 191.950919 192.471836
 [97] 196.330529 196.689377 199.368327 201.422797
apply(X, 2, min) # 列ごとに最小値
        y         x         z 
 4.482252  1.000000 -3.194494 

4 カテゴリ変数ごとの関数に適応

カテゴリ変数ごとに平均値を求めるようなことはよくします。こういうときはtapplyが使えます。

with(df01, tapply(y, d, mean))
        A         B         C 
103.82354 106.06086  89.68655 

これだけならばxtabs(y ~ d, data=df01)を用いても同様ですが、2位グループのスコアを求めるような処理も同様にかけます。

with(df01, tapply(y, d, function(v){
    unique(sort(v))[2]
}))
        A         B         C 
16.446137  6.184366 13.736850 

5 引数の関数に渡す引数

apply関数群は自分が利用しない引数を、そのまま引数の関数に受け渡します。以下の例では、sapplyに渡すmean関数に第2引数na.rm = TRUEを与えています。

# NAを含むデータセットをつくる
df02 <- df01[, 1:3]; df02[1, 1] <- NA

# デフォルトではNAがあると計算できない
sapply(df02, mean)
         y          x          z 
        NA 50.5000000  0.4263418 
# NAを無視させれば計算できる
sapply(df02, mean, na.rm = TRUE)
          y           x           z 
102.5485573  50.5000000   0.4263418 

なお、sapply(df02, function(x) mean(x, na.rm = TRUE) )と書いても同じ結果になります。

6 関数の省略表記

R 4.1から、function(x){ ... }\(x){ ... }と書けるようになっていて、apply関数群での利用が多くなっていくかも知れません。

7 慣れたら便利

最近は他のプログラミング言語でもラムダ式は一般化したのでそうでもないでもですが、慣れるまで違和感があるかも知れません。しかし、覚えたら便利なので、積極的に使っていきましょう。