web-dev-qa-db-ja.com

グループ化機能(tapply、by、aggregate)と* applyファミリー

Rで "map" pyを実行したいときはいつでも、私はapplyファミリーの関数を使おうとします。

しかし、{sapplylapplyなど}が入力/グループ化された入力に関数をどのように適用するか、出力がどのようになるか、さらには入力がどのようになる可能性があるのか​​ - 欲しいものが手に入るまで、たいていの場合、私はそれらすべてをただ通過するだけです。

誰かがいつどれを使うかを説明できますか?

私の現在の(たぶん正しくない/不完全な)理解は….

  1. sapply(vec, f):入力はベクトルです。出力はベクトル/行列です。ここで、要素if(vec[i])で、fが複数要素の出力を持つ場合は行列を与えます。

  2. lapply(vec, f)sapplyと同じですが、出力はリストですか?

  3. apply(matrix, 1/2, f):入力は行列です。出力はベクトルです。ここで、要素iはfです(行列の行/列i)。
  4. tapply(vector, grouping, f):出力は行列/配列です。ここで、行列/配列内の要素はベクトルのグループ化fにあるgの値で、gは行/列名にプッシュされます。
  5. by(dataframe, grouping, f)gをグループにしましょう。グループ/データフレームの各列にfを適用します。各列にグループ化とfの値をきれいに表示します。
  6. aggregate(matrix, grouping, f)byに似ていますが、出力をきれいに印刷する代わりに、aggregateはすべてをデータフレームに固定します。

副次的な質問:私はまだplyrや変形を学んでいません - plyrまたはreshapeはこれらすべてを完全に置き換えるのでしょうか?

975
grautur

Rには、ヘルプファイル(例えば?apply)に正しく記述されている* apply関数がたくさんあります。しかし、それらの十分なものはありますが、useRは最初のうちどれが自分の状況に適しているのか、あるいはそれらすべてを覚えているのかを判断するのが難しいかもしれません。彼らは「ここでは* apply関数を使うべきだ」という一般的な意味を持つかもしれませんが、最初からそれらをすべてまっすぐにしておくのは難しいかもしれません。

*他の回答で述べたように、* applyファミリーの機能の大部分は非常に人気のあるplyrパッケージによってカバーされているという事実にもかかわらず、基本関数は有用であり、知る価値があります。

この答えはある種の 道標 新しいuseRが彼らの特定の問題に対して正しい* apply関数にそれらを向けるのを助けるために働くことを意図しています。注意してください、これは ではありません 単にRのドキュメンテーションを逆流させるか置き換えるためのものです!この答えがどの* apply関数があなたの状況に合っているかを決めるのを手助けすることを望んでいます、そしてそれをさらに研究するのはあなた次第です。 1つの例外を除いて、パフォーマンスの違いは解決されません。

  • apply - マトリックス(およびより高次元のアナログ)の行または列に関数を適用したい場合。最初にマトリックスに強制されるため、データフレームには通常お勧めできません。

    # Two dimensional matrix
    M <- matrix(seq(1,16), 4, 4)
    
    # apply min to rows
    apply(M, 1, min)
    [1] 1 2 3 4
    
    # apply max to columns
    apply(M, 2, max)
    [1]  4  8 12 16
    
    # 3 dimensional array
    M <- array( seq(32), dim = c(4,4,2))
    
    # Apply sum across each M[*, , ] - i.e Sum across 2nd and 3rd dimension
    apply(M, 1, sum)
    # Result is one-dimensional
    [1] 120 128 136 144
    
    # Apply sum across each M[*, *, ] - i.e Sum across 3rd dimension
    apply(M, c(1,2), sum)
    # Result is two-dimensional
         [,1] [,2] [,3] [,4]
    [1,]   18   26   34   42
    [2,]   20   28   36   44
    [3,]   22   30   38   46
    [4,]   24   32   40   48
    

    行/列の平均または2次元マトリックスの合計が必要な場合は、最適化された、非常に高速なcolMeansrowMeanscolSumsrowSumsを調べてください。

  • lapply - リストの各要素に順番に関数を適用して、リストを元に戻す場合

    これは他の* apply関数の多くの主力です。彼らのコードをはがしてみると、その下にlapplyが見つかることがよくあります。

    x <- list(a = 1, b = 1:3, c = 10:100) 
    lapply(x, FUN = length) 
    $a 
    [1] 1
    $b 
    [1] 3
    $c 
    [1] 91
    lapply(x, FUN = sum) 
    $a 
    [1] 1
    $b 
    [1] 6
    $c 
    [1] 5005
    
  • sapply - リストの各要素に順番に関数を適用したいが、リストではなく vector backが必要な場合 _

    自分でunlist(lapply(...))と入力した場合は、sapplyを中止して検討してください。

    x <- list(a = 1, b = 1:3, c = 10:100)
    # Compare with above; a named vector, not a list 
    sapply(x, FUN = length)  
    a  b  c   
    1  3 91
    
    sapply(x, FUN = sum)   
    a    b    c    
    1    6 5005 
    

    sapplyのより高度な使い方では、適切ならば、結果を多次元配列に変換しようとします。たとえば、関数が同じ長さのベクトルを返す場合、sapplyはそれらを行列の列として使用します。

    sapply(1:5,function(x) rnorm(3,x))
    

    私たちの関数が2次元行列を返す場合、sapplyは本質的に同じことをし、返された各行列を単一の長いベクトルとして扱います。

    sapply(1:5,function(x) matrix(x,2,2))
    

    simplify = "array"を指定しない限り、その場合は多次元配列を構築するために個々の行列を使用します。

    sapply(1:5,function(x) matrix(x,2,2), simplify = "array")
    

    これらの振る舞いのそれぞれは、もちろん、同じ長さまたは次元のベクトルまたは行列を返す私たちの関数に左右される。

  • vapply - あなたがsapplyを使いたいが、おそらくあなたのコードからさらにスピードを上げる必要があるとき _

    vapplyに対して、あなたは基本的にRにあなたの関数がどんな種類のものを返すかの例を与えます。

    x <- list(a = 1, b = 1:3, c = 10:100)
    #Note that since the advantage here is mainly speed, this
    # example is only for illustration. We're telling R that
    # everything returned by length() should be an integer of 
    # length 1. 
    vapply(x, FUN = length, FUN.VALUE = 0L) 
    a  b  c  
    1  3 91
    
  • mapply - いくつかのデータ構造(例えばベクトル、リスト)があり、それぞれの最初の要素に、そして次にそれぞれの2番目の要素に関数を適用したい場合など) sapplyのようなベクトル/配列へ。

    関数が複数の引数を受け取らなければならないという意味で、これは多変量です。

    #Sums the 1st elements, the 2nd elements, etc. 
    mapply(sum, 1:5, 1:5, 1:5) 
    [1]  3  6  9 12 15
    #To do rep(1,4), rep(2,3), etc.
    mapply(rep, 1:4, 4:1)   
    [[1]]
    [1] 1 1 1 1
    
    [[2]]
    [1] 2 2 2
    
    [[3]]
    [1] 3 3
    
    [[4]]
    [1] 4
    
  • Map - SIMPLIFY = FALSEを付けたmapplyへのラッパーで、リストを返すことが保証されています。

    Map(sum, 1:5, 1:5, 1:5)
    [[1]]
    [1] 3
    
    [[2]]
    [1] 6
    
    [[3]]
    [1] 9
    
    [[4]]
    [1] 12
    
    [[5]]
    [1] 15
    
  • rapply - 入れ子リスト 構造体の各要素に関数を適用したい場合は、再帰的に使用してください。

    rapplyがどれほど一般的でないかについてあなたにいくらかの考えを与えるために、この答えを最初に投稿するとき私はそれを忘れました!明らかに、私は多くの人がそれを使っていると確信しています、しかしYMMV。 rapplyは、適用するユーザー定義関数で最もよく示されています。

    # Append ! to string, otherwise increment
    myFun <- function(x){
        if(is.character(x)){
          return(paste(x,"!",sep=""))
        }
        else{
          return(x + 1)
        }
    }
    
    #A nested list structure
    l <- list(a = list(a1 = "Boo", b1 = 2, c1 = "Eeek"), 
              b = 3, c = "Yikes", 
              d = list(a2 = 1, b2 = list(a3 = "Hey", b3 = 5)))
    
    
    # Result is named vector, coerced to character          
    rapply(l, myFun)
    
    # Result is a nested list like l, with values altered
    rapply(l, myFun, how="replace")
    
  • tapply - ベクトルの サブセット に関数を適用したい場合、サブセットは他のベクトル、通常は因子によって定義されます。

    *の黒い羊は、ある種の家族に当てはまります。ヘルプファイルでの "ragged array"というフレーズの使用は少々 混乱を招く になることがありますが、実際は非常に単純です。

    ベクトル:

    x <- 1:20
    

    グループを定義する係数(同じ長さの!)

    y <- factor(rep(letters[1:5], each = 4))
    

    xで定義された各サブグループ内のyの値を合計します。

    tapply(x, y, sum)  
     a  b  c  d  e  
    10 26 42 58 74 
    

    サブグループがいくつかの要因のリストの固有の組み合わせによって定義されている場合、より複雑な例を扱うことができます。 tapplyは、精神的にはRで一般的なsplit-apply-combine関数(aggregatebyaveddplyなど)に似ています。そのため、黒い羊の地位にあります。

1253
joran

ちなみに、さまざまなplyr関数が基本の*apply関数にどのように対応しているかを説明します(plyr Webページの入門からplyr文書へ http://had.co.nz/plyr/ )。

Base function   Input   Output   plyr function 
---------------------------------------
aggregate        d       d       ddply + colwise 
apply            a       a/l     aaply / alply 
by               d       l       dlply 
lapply           l       l       llply  
mapply           a       a/l     maply / mlply 
replicate        r       a/l     raply / rlply 
sapply           l       a       laply 

plyrの目標の1つは、各関数に一貫した命名規則を提供し、入力データ型と出力データ型を関数名にエンコードすることです。また、dlply()からの出力は、有用な出力を生成するためにldply()に簡単に渡すことができるという点で、出力の一貫性を提供します。

概念的には、plyrを学ぶことは、基本の*apply関数を理解することより難しくありません。

plyrreshape関数は、私が毎日使う中でこれらの関数のほとんどすべてを置き換えました。しかし、PlyrからIntroへの文書からも:

関連する関数tapplysweepは、plyrには対応する関数がなく、便利なままです。 mergeは、要約を元のデータと組み合わせるのに便利です。

180
JoFrhwld

スライド21から http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy

apply, sapply, lapply, by, aggregate

applyが@ Hadleyのaaplyに対応し、aggregateが@ Hadleyのddplyに対応することが明らかになっています。等、同じスライドシェアのスライド20では、この画像から取得できない場合が明確になります。)

(左が入力、上が出力)

127
isomorphismes

まず始めに Joranのすばらしい答え - 疑問があればそれを改善できる。

それから、次のニーモニックはそれぞれの違いを思い出すのを助けるかもしれません。いくつかは明白ですが、他の人はあまりそうではないかもしれません---これらのためにあなたはJoranの議論で正当化を見つけるでしょう。

ニーモニック

  • lapplylist applyで、リストまたはベクトルに作用してリストを返します。
  • sapplysimplelapplyです(関数はデフォルトで可能な場合はベクトルまたは行列を返します)
  • vapply検証済みの適用(戻りオブジェクト型を事前指定可能にする)
  • rapplyは、ネストされたリスト、つまりリスト内のリストに適用されるrecursiveです。
  • tapplyは、タグがサブセットを識別する場所にtagged applyです
  • apply総称:行列の行または列(またはより一般的には配列の次元)に関数を適用します

正しい背景を構築する

applyファミリーを使うことがまだあなたにとってちょっと異質な気分になれば、それはあなたが重要な視点を欠いているということかもしれません。

この2つの記事が参考になります。それらは、関数のapplyファミリーによって提供されている 関数型プログラミング技法 を動機付けるために必要な背景を提供します。

LISPのユーザはすぐにこのパラダイムを認識するでしょう。 LISPに慣れていないのであれば、一旦FPに慣れてしまえば、Rで使用するための強力な視点が得られます - そしてapplyはもっと理にかなっています。

95
Assad Ebrahim

この投稿の(非常に優れた)回答にはbyおよびaggregateの説明がないことに気付いたので。これが私の貢献です。

によって

ドキュメントに記載されているby関数は、tapplyの「ラッパー」としても機能します。 byの力は、tapplyが処理できないタスクを計算するときに発生します。 1つの例は次のコードです。

ct <- tapply(iris$Sepal.Width , iris$Species , summary )
cb <- by(iris$Sepal.Width , iris$Species , summary )

 cb
iris$Species: setosa
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.300   3.200   3.400   3.428   3.675   4.400 
-------------------------------------------------------------- 
iris$Species: versicolor
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.000   2.525   2.800   2.770   3.000   3.400 
-------------------------------------------------------------- 
iris$Species: virginica
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.200   2.800   3.000   2.974   3.175   3.800 


ct
$setosa
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.300   3.200   3.400   3.428   3.675   4.400 

$versicolor
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.000   2.525   2.800   2.770   3.000   3.400 

$virginica
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.200   2.800   3.000   2.974   3.175   3.800 

これらの2つのオブジェクトctcbを印刷すると、「本質的に」同じ結果が得られ、違いはそれぞれの表示方法と異なるclass属性にありますby for cbおよびarray for ct

私が言ったように、byの力は、tapplyを使用できないときに生じます。次のコードは一例です。

 tapply(iris, iris$Species, summary )
Error in tapply(iris, iris$Species, summary) : 
  arguments must have same length

Rは、引数は同じ長さでなければならない、たとえば「summaryのすべての変数のirisを因子Speciesに沿って計算したい」と言いますが、Rはそれができないのです。処理方法がわかりません。

by関数を使用して、Rはdata frameクラスの特定のメソッドをディスパッチし、最初の引数の長さ(および型も)が異なる場合でもsummary関数を機能させます。

bywork <- by(iris, iris$Species, summary )

bywork
iris$Species: setosa
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.300   Min.   :2.300   Min.   :1.000   Min.   :0.100   setosa    :50  
 1st Qu.:4.800   1st Qu.:3.200   1st Qu.:1.400   1st Qu.:0.200   versicolor: 0  
 Median :5.000   Median :3.400   Median :1.500   Median :0.200   virginica : 0  
 Mean   :5.006   Mean   :3.428   Mean   :1.462   Mean   :0.246                  
 3rd Qu.:5.200   3rd Qu.:3.675   3rd Qu.:1.575   3rd Qu.:0.300                  
 Max.   :5.800   Max.   :4.400   Max.   :1.900   Max.   :0.600                  
-------------------------------------------------------------- 
iris$Species: versicolor
  Sepal.Length    Sepal.Width     Petal.Length   Petal.Width          Species  
 Min.   :4.900   Min.   :2.000   Min.   :3.00   Min.   :1.000   setosa    : 0  
 1st Qu.:5.600   1st Qu.:2.525   1st Qu.:4.00   1st Qu.:1.200   versicolor:50  
 Median :5.900   Median :2.800   Median :4.35   Median :1.300   virginica : 0  
 Mean   :5.936   Mean   :2.770   Mean   :4.26   Mean   :1.326                  
 3rd Qu.:6.300   3rd Qu.:3.000   3rd Qu.:4.60   3rd Qu.:1.500                  
 Max.   :7.000   Max.   :3.400   Max.   :5.10   Max.   :1.800                  
-------------------------------------------------------------- 
iris$Species: virginica
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.900   Min.   :2.200   Min.   :4.500   Min.   :1.400   setosa    : 0  
 1st Qu.:6.225   1st Qu.:2.800   1st Qu.:5.100   1st Qu.:1.800   versicolor: 0  
 Median :6.500   Median :3.000   Median :5.550   Median :2.000   virginica :50  
 Mean   :6.588   Mean   :2.974   Mean   :5.552   Mean   :2.026                  
 3rd Qu.:6.900   3rd Qu.:3.175   3rd Qu.:5.875   3rd Qu.:2.300                  
 Max.   :7.900   Max.   :3.800   Max.   :6.900   Max.   :2.500     

それは実際に機能し、結果は非常に驚くべきものです。クラスbyのオブジェクトであり、Speciesに沿って(たとえば、それぞれに対して)各変数のsummaryを計算します。

最初の引数がdata frameである場合、ディスパッチされる関数にはそのオブジェクトクラスのメソッドが必要であることに注意してください。たとえば、このコードをmean関数とともに使用すると、まったく意味のないこのコードが得られます。

 by(iris, iris$Species, mean)
iris$Species: setosa
[1] NA
------------------------------------------- 
iris$Species: versicolor
[1] NA
------------------------------------------- 
iris$Species: virginica
[1] NA
Warning messages:
1: In mean.default(data[x, , drop = FALSE], ...) :
  argument is not numeric or logical: returning NA
2: In mean.default(data[x, , drop = FALSE], ...) :
  argument is not numeric or logical: returning NA
3: In mean.default(data[x, , drop = FALSE], ...) :
  argument is not numeric or logical: returning NA

集計

aggregateは、別の異なる使用方法と見なすことができますtapplyこのような方法で使用する場合。

at <- tapply(iris$Sepal.Length , iris$Species , mean)
ag <- aggregate(iris$Sepal.Length , list(iris$Species), mean)

 at
    setosa versicolor  virginica 
     5.006      5.936      6.588 
 ag
     Group.1     x
1     setosa 5.006
2 versicolor 5.936
3  virginica 6.588

2つの直接的な違いは、aggregatemustの2番目の引数がリストであるのに対し、tapplycan(必須ではない)がリストであり、 aggregateの出力はデータフレームであり、tapplyの出力はarrayです。

aggregateの威力は、subset引数を使用してデータのサブセットを簡単に処理でき、tsオブジェクトとformulaのメソッドも備えていることです。

これらの要素により、状況によってはaggregateをそのtapplyで操作しやすくなります。以下に例を示します(ドキュメントで入手可能):

ag <- aggregate(len ~ ., data = ToothGrowth, mean)

 ag
  supp dose   len
1   OJ  0.5 13.23
2   VC  0.5  7.98
3   OJ  1.0 22.70
4   VC  1.0 16.77
5   OJ  2.0 26.06
6   VC  2.0 26.14

tapplyでも同じことを実現できますが、構文が少し難しくなり、出力(状況によっては)が読みにくくなります。

att <- tapply(ToothGrowth$len, list(ToothGrowth$dose, ToothGrowth$supp), mean)

 att
       OJ    VC
0.5 13.23  7.98
1   22.70 16.77
2   26.06 26.14

byまたはtapplyを使用できない場合があり、aggregateを使用する必要があります。

 ag1 <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, mean)

 ag1
  Month    Ozone     Temp
1     5 23.61538 66.73077
2     6 29.44444 78.22222
3     7 59.11538 83.88462
4     8 59.96154 83.96154
5     9 31.44828 76.89655

1回の呼び出しでtapplyを使用して以前の結果を取得することはできませんが、各要素のMonthに沿って平均を計算し、それらを結合する必要があります(na.rm = TRUEformula関数のaggregateメソッドには、デフォルトでna.action = na.omit)があるためです:

ta1 <- tapply(airquality$Ozone, airquality$Month, mean, na.rm = TRUE)
ta2 <- tapply(airquality$Temp, airquality$Month, mean, na.rm = TRUE)

 cbind(ta1, ta2)
       ta1      ta2
5 23.61538 65.54839
6 29.44444 79.10000
7 59.11538 83.90323
8 59.96154 83.96774
9 31.44828 76.90000

byでは、実際に次の関数呼び出しがエラーを返すことはありません(ただし、ほとんどの場合、提供された関数meanに関連しています)。

by(airquality[c("Ozone", "Temp")], airquality$Month, mean, na.rm = TRUE)

他の場合、結果は同じであり、違いはクラス(および、それがどのように表示/印刷されるかだけでなく、例、それをサブセット化する方法)オブジェクトのみです:

byagg <- by(airquality[c("Ozone", "Temp")], airquality$Month, summary)
aggagg <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, summary)

前のコードは同じ目標と結果を達成しますが、ある時点で使用するツールは個人の好みとニーズの問題です。前の2つのオブジェクトには、サブセット化の点で非常に異なるニーズがあります。

46
SabDeM

各機能のユースケースの違いを説明しているたくさんの素晴らしい答えがあります。答えのどれも性能の違いを論議しない。これは、さまざまな関数がさまざまな入力を期待し、さまざまな出力を生成するための合理的な原因です。私の答えはパフォーマンスに焦点を当てるつもりです。上記のため、ベクトルからの入力生成がタイミングに含まれるため、apply関数も測定されません。

私は2つの異なる関数sumlengthを同時にテストしました。テストされた容量は、入力で50M、出力で50Kです。私は質問があった時に広く使われていなかった2つの現在人気のあるパッケージ、data.tabledplyrも含めました。どちらも、優れたパフォーマンスを目指しているかどうかを確認する価値があります。

library(dplyr)
library(data.table)
set.seed(123)
n = 5e7
k = 5e5
x = runif(n)
grp = sample(k, n, TRUE)

timing = list()

# sapply
timing[["sapply"]] = system.time({
    lt = split(x, grp)
    r.sapply = sapply(lt, function(x) list(sum(x), length(x)), simplify = FALSE)
})

# lapply
timing[["lapply"]] = system.time({
    lt = split(x, grp)
    r.lapply = lapply(lt, function(x) list(sum(x), length(x)))
})

# tapply
timing[["tapply"]] = system.time(
    r.tapply <- tapply(x, list(grp), function(x) list(sum(x), length(x)))
)

# by
timing[["by"]] = system.time(
    r.by <- by(x, list(grp), function(x) list(sum(x), length(x)), simplify = FALSE)
)

# aggregate
timing[["aggregate"]] = system.time(
    r.aggregate <- aggregate(x, list(grp), function(x) list(sum(x), length(x)), simplify = FALSE)
)

# dplyr
timing[["dplyr"]] = system.time({
    df = data_frame(x, grp)
    r.dplyr = summarise(group_by(df, grp), sum(x), n())
})

# data.table
timing[["data.table"]] = system.time({
    dt = setnames(setDT(list(x, grp)), c("x","grp"))
    r.data.table = dt[, .(sum(x), .N), grp]
})

# all output size match to group count
sapply(list(sapply=r.sapply, lapply=r.lapply, tapply=r.tapply, by=r.by, aggregate=r.aggregate, dplyr=r.dplyr, data.table=r.data.table), 
       function(x) (if(is.data.frame(x)) nrow else length)(x)==k)
#    sapply     lapply     tapply         by  aggregate      dplyr data.table 
#      TRUE       TRUE       TRUE       TRUE       TRUE       TRUE       TRUE 
# print timings
as.data.table(sapply(timing, `[[`, "elapsed"), keep.rownames = TRUE
              )[,.(fun = V1, elapsed = V2)
                ][order(-elapsed)]
#          fun elapsed
#1:  aggregate 109.139
#2:         by  25.738
#3:      dplyr  18.978
#4:     tapply  17.006
#5:     lapply  11.524
#6:     sapply  11.326
#7: data.table   2.686
31
jangorecki

おそらくaveを言及する価値があります。 avetapplyの友好的ないとこです。結果は、データフレームに直接差し込むことができる形式で返されます。

dfr <- data.frame(a=1:20, f=rep(LETTERS[1:5], each=4))
means <- tapply(dfr$a, dfr$f, mean)
##  A    B    C    D    E 
## 2.5  6.5 10.5 14.5 18.5 

## great, but putting it back in the data frame is another line:

dfr$m <- means[dfr$f]

dfr$m2 <- ave(dfr$a, dfr$f, FUN=mean) # NB argument name FUN is needed!
dfr
##   a f    m   m2
##   1 A  2.5  2.5
##   2 A  2.5  2.5
##   3 A  2.5  2.5
##   4 A  2.5  2.5
##   5 B  6.5  6.5
##   6 B  6.5  6.5
##   7 B  6.5  6.5
##   ...

基本パッケージには、データフレーム全体に対してaveのように動作するものはありません(byはデータフレームに対するtapplyのように)。しかし、あなたはそれをごまかすことができます:

dfr$foo <- ave(1:nrow(dfr), dfr$f, FUN=function(x) {
    x <- dfr[x,]
    sum(x$m*x$m2)
})
dfr
##     a f    m   m2    foo
## 1   1 A  2.5  2.5    25
## 2   2 A  2.5  2.5    25
## 3   3 A  2.5  2.5    25
## ...
22
user3603486

ここにすべての素晴らしい答えがあるにもかかわらず、言及されるに値する2つ以上の基本関数があります。役に立つouter関数とあいまいなeapply関数です。

外側

outerは、もっと平凡なものとして隠されている非常に便利な関数です。 outerのヘルプを読むと、その説明は以下のようになります。

The outer product of the arrays X and Y is the array A with dimension  
c(dim(X), dim(Y)) where element A[c(arrayindex.x, arrayindex.y)] =   
FUN(X[arrayindex.x], Y[arrayindex.y], ...).

これは、これが線形代数型のものにのみ有用であるように見せます。しかし、2つの入力ベクトルに関数を適用するためにmapplyのように使うことができます。違いは、mapplyは最初の2つの要素、次に2番目の要素などに関数を適用するのに対して、outerは最初のベクトルからの1つの要素と2番目からの要素のすべての組み合わせに関数を適用するということです。例えば:

 A<-c(1,3,5,7,9)
 B<-c(0,3,6,9,12)

mapply(FUN=pmax, A, B)

> mapply(FUN=pmax, A, B)
[1]  1  3  6  9 12

outer(A,B, pmax)

 > outer(A,B, pmax)
      [,1] [,2] [,3] [,4] [,5]
 [1,]    1    3    6    9   12
 [2,]    3    3    6    9   12
 [3,]    5    5    6    9   12
 [4,]    7    7    7    9   12
 [5,]    9    9    9    9   12

私は値のベクトルと条件のベクトルがあり、どの値がどの条件を満たすかを知りたいときに個人的にこれを使いました。

eapply

eapplylapplyと似ていますが、リスト内のすべての要素に関数を適用するのではなく、環境内のすべての要素に関数を適用する点が異なります。たとえば、グローバル環境でユーザー定義関数のリストを見つけたい場合は、次のようにします。

A<-c(1,3,5,7,9)
B<-c(0,3,6,9,12)
C<-list(x=1, y=2)
D<-function(x){x+1}

> eapply(.GlobalEnv, is.function)
$A
[1] FALSE

$B
[1] FALSE

$C
[1] FALSE

$D
[1] TRUE 

率直に言って、私はこれをあまり使いませんが、あなたがたくさんのパッケージを構築しているか、あるいはたくさんの環境を作成しているなら、それは役に立つかもしれません。

21
John Paul

私は最近かなり有用なsweep関数を発見し、完全を期すためにここにそれを追加しました:

スイープ

基本的な考え方は、行方向または列方向に配列を介してsweepを実行し、変更された配列を返すことです。例を見ればわかります(source: datacamp )。

行列があり、列方向に 標準化 itしたいとしましょう。

dataPoints <- matrix(4:15, nrow = 4)

# Find means per column with `apply()`
dataPoints_means <- apply(dataPoints, 2, mean)

# Find standard deviation with `apply()`
dataPoints_sdev <- apply(dataPoints, 2, sd)

# Center the points 
dataPoints_Trans1 <- sweep(dataPoints, 2, dataPoints_means,"-")
print(dataPoints_Trans1)
##      [,1] [,2] [,3]
## [1,] -1.5 -1.5 -1.5
## [2,] -0.5 -0.5 -0.5
## [3,]  0.5  0.5  0.5
## [4,]  1.5  1.5  1.5
# Return the result
dataPoints_Trans1
##      [,1] [,2] [,3]
## [1,] -1.5 -1.5 -1.5
## [2,] -0.5 -0.5 -0.5
## [3,]  0.5  0.5  0.5
## [4,]  1.5  1.5  1.5
# Normalize
dataPoints_Trans2 <- sweep(dataPoints_Trans1, 2, dataPoints_sdev, "/")

# Return the result
dataPoints_Trans2
##            [,1]       [,2]       [,3]
## [1,] -1.1618950 -1.1618950 -1.1618950
## [2,] -0.3872983 -0.3872983 -0.3872983
## [3,]  0.3872983  0.3872983  0.3872983
## [4,]  1.1618950  1.1618950  1.1618950

注意:この簡単な例では、当然のことながら同じ結果をより簡単に達成できます。
apply(dataPoints, 2, scale)

8
vonjd