R

トップページ
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]   1.355528   2.617994   3.161656   4.073573   5.164650   6.907851
  [7]   7.469364   8.505914   9.286480  10.064940  11.478411  12.018665
 [13]  13.262795  14.582559  15.865726  16.196913  17.232445  17.328222
 [19]  19.109778  20.281572  20.934057  23.106776  23.828969  24.178164
 [25]  25.705522  26.302219  27.656999  28.611033  29.965415  30.226928
 [31]  31.691549  32.388729  33.620194  34.146871  35.435995  36.554902
 [37]  37.268276  38.976736  39.602202  40.365403  41.198490  42.825710
 [43]  43.355750  44.816395  45.524283  46.191651  47.424782  47.938282
 [49]  49.726528  49.983700  51.406084  52.193979  53.412468  54.559747
 [55]  55.534200  56.284943  57.875156  58.643521  59.287106  60.583292
 [61]  60.968064  62.640513  63.758952  64.141365  65.172695  66.455664
 [67]  66.870031  68.364434  69.054198  70.087444  71.335276  71.963958
 [73]  73.326658  74.312858  75.244567  75.580207  76.894802  78.484507
 [79]  79.395963  80.350422  81.082950  82.533563  83.495236  84.182246
 [85]  84.943941  86.410144  87.126241  88.563003  89.358945  90.778945
 [91]  90.974983  92.167414  93.521326  94.397707  95.225568  96.739741
 [97]  96.938734  98.303338  98.690728 100.501317
apply(df02, 2, mean) # 列ごとに合計
          y           x           z 
101.5933464  50.5000000   0.4757544 
apply(df02, 2, mean, simplify=FALSE) # 結果をlistにする
$y
[1] 101.5933

$x
[1] 50.5

$z
[1] 0.4757544

3.2 引数が行列

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

X <- as.matrix(df02) # データフレームを行列にする
apply(X, 1, max) # 行ごとに最大値
  [1]   1.881450   3.517223   6.185505   7.939745   9.576081  15.368247
  [7]  14.726247  17.284078  17.718266  20.630706  24.185139  23.018330
 [13]  25.680914  29.930005  32.248988  32.042508  33.090757  33.346180
 [19]  37.589240  40.221239  41.379827  47.325193  47.560091  48.390336
 [25]  51.858013  53.780560  55.311155  57.050228  59.315184  58.277747
 [31]  63.719103  65.852079  66.540909  68.654030  73.029298  73.352680
 [37]  74.493069  77.892559  80.130888  82.241111  81.346687  87.480374
 [43]  86.037225  89.667072  90.022841  91.372677  95.124914  94.892906
 [49]  97.656394 100.183337 101.226893 104.582841 108.349871 109.168084
 [55] 113.090212 113.416455 116.802127 116.962988 118.046430 121.098256
 [61] 120.312386 125.012528 126.811150 125.686391 129.500990 132.029810
 [67] 132.812198 137.086106 137.395844 139.495826 142.068496 144.426688
 [73] 148.474615 149.884316 149.770468 150.450782 154.407974 158.387042
 [79] 160.579413 160.886522 163.875901 166.308326 167.306475 166.359378
 [85] 170.390890 171.421469 174.199394 176.300963 179.043514 181.451979
 [91] 182.745988 181.426185 186.118315 188.291850 187.908695 192.812300
 [97] 194.999043 197.422805 195.826492 200.781639
apply(X, 2, min) # 列ごとに最小値
        y         x         z 
 1.881450  1.000000 -1.721315 

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

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

with(df01, tapply(y, d, mean))
        A         B         C 
 97.47676 110.25179  94.93219 

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

with(df01, tapply(y, d, function(v){
    unique(sort(v))[2]
}))
        A         B         C 
 9.576081 15.368247  6.185505 

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.4757544 
# NAを無視させれば計算できる
sapply(df02, mean, na.rm = TRUE)
          y           x           z 
102.6005373  50.5000000   0.4757544 

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

6 関数の省略表記

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

7 慣れたら便利

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