1112

R で何か "map"py を実行したいときはいつでも、通常はapplyファミリ内の関数を使用しようとします。

しかし、私はそれらの違いを完全には理解していませんでしsapplylapply.私が欲しいものを手に入れるまで、それらすべてを調べてください。

誰かがいつどちらを使用するかを説明できますか?

私の現在の(おそらく間違っている/不完全な)理解は...

  1. sapply(vec, f): 入力はベクトルです。output は vector/matrix であり、 elementiはであり、複数要素の出力があるf(vec[i])場合は行列が得られますf

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

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

副次的な質問: 私はまだプライヤーやリシェイプを学んでいません。これらすべてを完全に置き換えますかplyr?reshape

4

10 に答える 10

1403

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

* applyファミリーの機能の多くが非常に人気のあるplyrパッケージでカバーされているという事実(他の回答に記載されています)にもかかわらず、基本関数は引き続き有用であり、知っておく価値があります。

この回答は、新しいuseRの一種の道標として機能し、特定の問題に対して正しい*apply関数に誘導することを目的としています。これは、単にRドキュメントを逆流または置換することを意図したものではないことに注意してください。この回答が、どの* apply関数が状況に適しているかを判断するのに役立ち、さらに調査するのはあなた次第です。1つの例外を除いて、パフォーマンスの違いは対処されません。

  • 適用-行列(および高次元の類似体)の行または列に関数を適用する場合。データフレームは最初にマトリックスに強制変換されるため、一般的にはお勧めできません。

     # 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
    

    2D行列の行/列の平均または合計が必要な場合は、高度に最適化された、電光石火の速さ、、、を必ずcolMeans調べ rowMeanscolSumsくださいrowSums

  • 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-リストの各要素に関数を順番に適用したいが、リストではなくベクトルを戻したい場合。

    入力していることに気付いた場合は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-複数のデータ構造(ベクトル、リストなど)があり、それぞれの1番目の要素、次にそれぞれの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 - withのラッパーなので、リストを返すことが保証されています。mapplySIMPLIFY = FALSE

     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-ベクトルのサブセットに関数を適用する場合、サブセットは他のベクトル(通常は係数)によって定義されます。

    ある種の*applyファミリーの黒い羊。ヘルプファイルでの「不規則な配列」というフレーズの使用は少し混乱する可能性がありますが、実際には非常に単純です。

    ベクトル:

     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関数(aggregate、、、、など)byに似ています。したがって、その黒い羊のステータスです。aveddply

于 2011-08-21T22:50:17.290 に答える
202

plyr補足として、さまざまな関数が基本関数にどのように対応するかを次に示し*applyます (イントロから plyr Web ページhttp://had.co.nz/plyr/の 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 

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

概念的には、学習は基本関数plyrを理解するのと同じくらい難しくありません。*apply

plyr機能は、reshape私の日常の使用でこれらの機能のほとんどすべてを置き換えました。しかし、Intro to Plyr ドキュメントからも:

関連する関数tapplysweepは に対応する関数がなくplyr、引き続き有用です。merge要約を元のデータと組み合わせるのに役立ちます。

于 2010-08-17T19:20:09.227 に答える
142

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

適用、sapply、lapply、by、aggregate

(うまくいけば、apply@ Hadleyaaplyaggregate対応し、@ Hadleyなどに対応するddplyことは明らかです。同じslideshareのスライド20は、この画像から取得しない場合に明確になります。)

(左側が入力、上部が出力)

于 2011-10-09T05:29:32.633 に答える
111

まずJoran のすばらしい答えから始めましょう。

次のニーモニックは、それぞれの違いを覚えるのに役立ちます。明白なものもあれば、そうではないものもあります --- これらについては、Joran の議論で正当化されます。

ニーモニック

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

適切な背景を構築する

ファミリを使用することにapplyまだ違和感を覚える場合は、重要な視点が欠けている可能性があります。

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

Lisp のユーザーは、このパラダイムをすぐに認識できます。Lisp に慣れていない場合は、FP に慣れると、R で使用するための強力な視点を得ることapplyができ、より多くの意味が理解できるようになります。

于 2014-04-25T00:20:19.703 に答える
39

各機能のユースケースの違いについて議論する素晴らしい回答がたくさんあります。パフォーマンスの違いについて議論する答えはありません。これは、さまざまな関数がさまざまな入力を期待し、さまざまな出力を生成する合理的な理由ですが、それらのほとんどは、シリーズ/グループごとに評価する一般的な共通の目的を持っています。私の答えは、パフォーマンスに焦点を当てます。上記により、ベクトルからの入力作成はタイミングに含まれ、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
于 2015-12-08T22:42:43.753 に答える
31

ここには素晴らしい答えがすべてありますが、言及する価値のある基本機能がさらに 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], ...).

これは、線形代数タイプのものにのみ役立つように思われます。mapplyただし、 2 つの入力ベクトルに関数を適用するのと同じように使用できます。違いはmapply、関数を最初の 2 つの要素に適用し、次に 2 番目の 2 つなどに適用するのに対しouter、最初のベクトルの 1 つの要素と 2 番目のベクトルの 1 つの要素のすべての組み合わせに関数を適用することです。例えば:

 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

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

適用する

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 

率直に言って、私はこれをあまり使用しませんが、多くのパッケージを構築したり、多くの環境を作成したりする場合に便利です。

于 2016-05-16T03:59:13.613 に答える