Gruplama işlevleri (tapply, by, aggregate) ve * uygula ailesi


1040

R'de bir "harita" py yapmak istediğimde, genellikle applyailede bir işlev kullanmaya çalışırım .

Ancak, ben oldukça aralarındaki farklılıkları hiç anlamadım - {nasıl sapply, lapplyçoğu kez bu kadar - vs.} çıkış gibi görünecektir, hatta girdi ne olabilir neyi giriş / gruplandırılmış girişi, fonksiyonu uygulamak sadece istediğimi elde edene kadar hepsini gözden geçir.

Birisi hangisinin ne zaman kullanılacağını açıklayabilir mi?

Mevcut (muhtemelen yanlış / eksik) anlayışım ...

  1. sapply(vec, f): girdi bir vektördür. çıkış elemanı bir vektör / matris olup iolup f(vec[i])olmadığını, size bir matris veren fbir çok eleman çıkışı

  2. lapply(vec, f): ile aynı sapply, ancak çıktı bir liste?

  3. apply(matrix, 1/2, f): input bir matristir. çıktı bir vektördür, burada eleman if'dir (matrisin satır / sütun i)
  4. tapply(vector, grouping, f): output, bir matris / dizidir; burada matris / dizideki bir öğe , vektörün fbir gruplamasındaki değerdir ve satır / sütun adlarına iletilirgg
  5. by(dataframe, grouping, f): ggruplandırma olsun. fgrubun / veri çerçevesinin her sütununa uygulanır . fher sütunda gruplama ve değerini güzel yazdırın .
  6. aggregate(matrix, grouping, f): benzer by, ancak çıktıyı güzel yazdırmak yerine, her şeyi bir veri çerçevesine yapıştırır.

Yan soru: Hala öğrenmemiş plyr veya yeniden biçimlendirme var - olurdu plyrya reshapetamamen bunların tümünü değiştir?


33
yan sorunuza: birçok şey için plyr *apply()ve yerine doğrudan bir alternatiftir by. plyr (en azından benim için) her zaman tam olarak hangi veri formatını beklediğini ve tam olarak ne tüküreceğini bildiğimden çok daha tutarlı görünüyor. Bu bana çok fazla güçlük kazandırıyor.
JD Long

12
Ayrıca, eklemenizi tavsiye ederim: doByve seçim ve uygulama yetenekleri data.table.
Iterator

7
sapplysadece çıktı lapplyeklenmesi ile simplify2array. applyatom vektörüne zorlar, fakat çıktı vektör veya liste olabilir. byveri çerçevelerini alt veri çerçevelerine böler, ancak fsütunlarda ayrı olarak kullanılmaz. Yalnızca 'data.frame' sınıfı için bir yöntem varsa, fsütun olarak uygulanır by. aggregategenel olduğundan, ilk argümanın farklı sınıfları için farklı yöntemler mevcuttur.
IRTFM

8
Anımsatıcı: l 'liste' içindir, s 'basitleştirme' içindir, t 'tip başına' içindir (gruplamanın her seviyesi bir türdür)
Lutz Prechelt

Ayrıca Rfast paketinde, R'nin eşdeğerlerinden daha hızlı olan hercol.apply, Apply.condition ve daha fazlası gibi bazı işlevler de vardır
Stefanos

Yanıtlar:


1330

R, yardım dosyalarında (örn. ?apply) Açıkça açıklanan birçok * uygulama işlevine sahiptir . Bununla birlikte, bunlardan yeterli olanı var, başlangıç ​​kullanımıR'ler hangisinin durumları için uygun olduğuna karar vermekte zorluk çekebilir veya hatta hepsini hatırlayabilir. "Burada * uygulama işlevi kullanmalıyım" diye genel bir fikirleri olabilir, ancak ilk başta hepsini düz tutmak zor olabilir.

* Uygulanan ailenin işlevselliğinin çoğunun son derece popüler plyrpaket tarafından kapsanmasına rağmen (diğer cevaplarda belirtilmiştir) , temel işlevler yararlı ve bilmeye değer kalır.

Bu cevabın, yeni kullanımları için bir tür işaret görevi görmesi amaçlanmıştır, bu onların özel sorunları için doğru * uygulama işlevine yönlendirilmelerine yardımcı olur. Not Bu edilir değil sadece kusar amaçlanan veya R belgelerine değiştirin! Umut, bu cevabın hangi * uygulama fonksiyonunun sizin durumunuza uygun olduğuna karar vermenize yardımcı olması ve daha sonra araştırmak size kalmış olmasıdır. Bir istisna dışında, performans farklılıkları ele alınmayacaktır.

  • Apply - Bir matrisin (ve daha yüksek boyutlu analogların) satırlarına veya sütunlarına bir işlev uygulamak istediğinizde; ilk önce bir matrise zorlanacağı için veri çerçeveleri için genellikle önerilmez.

    # 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
    

    Eğer bir 2D matris için satır / sütun araçları veya toplamları istiyorsanız, emin araştırmak olmak son derece optimize, yıldırım hızlı colMeans, rowMeans, colSums, rowSums.

  • lapply - Sırasıyla listenin her öğesine bir işlev uygulamak ve bir listeyi geri almak istediğinizde.

    Bu, diğer * uygulama işlevlerinin birçoğunun görevidir. Kodlarını geri soyun ve genellikle lapplyaltında bulacaksınız .

    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 - Sırasıyla listenin her öğesine bir işlev uygulamak istediğinizde, ancak liste yerine bir vektör geri istediğinizde .

    Kendinizi yazarken bulursanız unlist(lapply(...)), durun ve düşünün 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 
    

    Daha ileri düzey kullanımlarda sapply, sonucu çok boyutlu bir diziye zorlamaya çalışacaktır. Örneğin, işlevimiz aynı uzunluktaki vektörleri döndürürse, sapplybunları bir matrisin sütunları olarak kullanır:

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

    Eğer fonksiyonumuz 2 boyutlu bir matris döndürürse, sapplyesasen aynı şeyi yapar ve her döndürülen matrisi tek bir uzun vektör olarak ele alır:

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

    Belirtmedikçe simplify = "array", bu durumda çok boyutlu bir dizi oluşturmak için tek tek matrisleri kullanır:

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

    Bu davranışların her biri elbette aynı uzunlukta veya boyutta fonksiyon döndüren vektörler veya matrislerimize bağlıdır.

  • vapply - Kullanmak istediğinizde sapplybelki de kodunuzdan biraz daha fazla hız almanız gerektiğinde.

    İçin vapply, temelde R süre Coercing döndürülen değerler tek atomik vektör sığacak şekilde kaydedebilirsiniz sıralama şey sizin işlevi dönecektir dair bir örnek vermek.

    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 - Birkaç veri yapınız varsa (örn. vektörler, listeler) ve her birinin ilk öğelerine, ardından her birinin ikinci öğelerine vb. bir işlev uygulamak istediğinizde, sonucu bir vektör / diziye olduğu gibi zorlar sapply.

    Bu, işlevinizin birden çok argümanı kabul etmesi gerektiği için çok değişkenlidir.

    #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
    
  • Harita - Bir sarıcı için mapplybirlikte SIMPLIFY = FALSE, listesini döndürmek için garanti edilir böylece.

    Map(sum, 1:5, 1:5, 1:5)
    [[1]]
    [1] 3
    
    [[2]]
    [1] 6
    
    [[3]]
    [1] 9
    
    [[4]]
    [1] 12
    
    [[5]]
    [1] 15
    
  • rapply - İç içe liste yapısının her öğesine yinelemeli olarak bir işlev uygulamak istediğinizde .

    Size ne kadar nadir rapplyolduğu hakkında bir fikir vermek için, bu cevabı ilk gönderirken bunu unuttum! Açıkçası, eminim birçok insan bunu kullanıyor, ama YMMV. rapplyen iyi uygulama için kullanıcı tanımlı bir işlevle gösterilmiştir:

    # 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 - Bir vektörün alt kümelerine bir işlev uygulamak istediğinizde ve alt kümeler genellikle bir faktör olmak üzere başka bir vektör tarafından tanımlanır.

    * Kara koyunları ailenin türünü uygularlar. Yardım dosyasının "düzensiz dizi" ifadesini kullanması biraz kafa karıştırıcı olabilir , ancak aslında oldukça basittir.

    Bir vektör:

    x <- 1:20

    Grupları tanımlayan bir faktör (aynı uzunlukta!):

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

    xHer bir alt gruptaki değerleri şu şekilde tanımlayın y:

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

    Alt grupların birkaç faktörlü bir listenin benzersiz kombinasyonları ile tanımlandığı yerlerde daha karmaşık örnekler ele alınabilir. tapplybölünmüş başvuru-birleştirmek R yaygındır fonksiyonları (ruhu içinde benzer aggregate, by, ave, ddplyDolayısıyla onun yüz karası durumu, vb.)


32
Bunu bulacaksınız inanın bysaf bölünmüş lapply olup aggregateolup tapplyçekirdeklerindeki. Kara koyunların mükemmel bir kumaş yaptığını düşünüyorum.
IRTFM

21
Harika bir yanıt! Bu resmi R belgelerinin bir parçası olmalıdır :). Bir minik öneri: belki kullanmayla ilgili bazı mermi ekleyebilir aggregateve byde? (Sonunda açıklamanızdan sonra onları anlıyorum!), Ancak oldukça yaygındır, bu yüzden bu iki işlev için ayırmak ve bazı spesifik örneklere sahip olmak yararlı olabilir.)
grautur

3
@grautur (a) çok uzun ve (b) belgelerin yeniden yazılmasını önlemek için bu cevaptan bir şeyleri aktif olarak budanıyordum. , Vb. İşlevlerin * uygulama işlevlerine dayalı olmasına rağmen aggregate, bybunları kullanma yaklaşımınızın ayrı bir yanıtta özetlenmesi gereken kullanıcı perspektifinden yeterince farklı olduğuna karar verdim . Eğer zamanım varsa, belki de başka biri beni dövecek ve benim oyumu kazanacak.
joran

4
ayrıca, ?Mapbir akrabası olarakmapply
baptiste

3
@jsanders - Buna katılmam. data.frames, R'nin kesinlikle merkezi bir parçasıdır ve bir listnesne olarak lapplyözellikle kullanılarak sıklıkla manipüle edilir . Ayrıca, geleneksel dikdörtgen veri kümesinde vektörleri / birçok türde faktörü birlikte gruplamak için kaplar görevi görürler. İken data.tableve plyrbiraz daha rahat bulabileceği sözdizimi belirli bir tür ekleyebiliriz, bunlar uzanan ve hareket ettiğinizi data.framesırasıyla s.
thelatemail

191

Yan notta, çeşitli plyrişlevler temel *applyişlevlere nasıl karşılık gelir ( http://had.co.nz/plyr/ adresindeki web sitesinden giriş belgesine ve belge belgesine )

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 

Hedeflerinden biri plyr, işlev adındaki giriş ve çıkış veri türlerini kodlayan, işlevlerin her biri için tutarlı adlandırma kuralları sağlamaktır. Aynı zamanda çıktıda tutarlılık sağlar, bu dlply()sayede ldply()yararlı çıktı üretmek için çıktı kolayca geçirilebilir .

Kavramsal olarak öğrenme plyr, temel *applyfonksiyonları anlamaktan daha zor değildir .

plyrve reshapeişlevler günlük kullanımımda neredeyse tüm bu işlevlerin yerini aldı. Ancak, Intro'dan Plyr belgesine de:

İlgili işlevler tapplyve sweepkarşılık gelen işlevi yoktur plyrve yararlı kalır. mergeözetleri orijinal verilerle birleştirmek için kullanışlıdır.


13
R'yi sıfırdan öğrenmeye başladığımda plyr ÇOK *apply()fonksiyonunu öğrenmekten daha kolay buldum . Benim ddply()için SQL toplama fonksiyonlarına aşina olduğum için çok sezgiseldi. ddply()bazıları diğer komutlarla daha iyi çözülebilen birçok sorunu çözmek için çekiçim haline geldi.
JD Uzun

1
Sanırım plyrfonksiyonların arkasındaki kavramın fonksiyonlara benzer olduğunu düşündüm *apply, bu yüzden birini yapabilirseniz, diğerini yapabilirsiniz, ancak plyrfonksiyonların hatırlanması daha kolaydır. Ama ddply()çekiç üzerinde tamamen katılıyorum !
JoFrhwld

1
Plyr paketi, join()birleştirmeye benzer görevleri gerçekleştiren bir işleve sahiptir. Belki de daha çok plyr bağlamında bahsetmek gerekir.
marbel

Unutulmayalım, unutulmuş fakireapply
JDL

Genel olarak büyük cevap, ama bence yarar vapplyve dezavantajları önemsiz sapply. Bunun en büyük avantajı, vapplyçıktı türünü ve uzunluğunu zorlamasıdır, böylece beklenen çıktıyı ya da bilgilendirici bir hatayı alırsınız. Öte yandan, sapplyher zaman açık olmayan kuralları izleyerek çıktıyı basitleştirmeye çalışacak ve aksi takdirde bir listeye geri dönecektir. Örneğin, bu üretecek çıkış türünü tahmin etmeye çalışın: sapply(list(1:5, 6:10, matrix(1:4, 2)), function(x) head(x, 1)). Ne olmuş sapply(list(matrix(1:4, 2), matrix(1:4, 2)), ...)?
Alexey Shiklomanov


100

İlk önce Joran'ın mükemmel cevabı ile başlayın - şüpheli her şey daha iyi olabilir.

Daha sonra aşağıdaki anımsatıcılar, her biri arasındaki farkları hatırlamaya yardımcı olabilir. Bazıları açık olsa da, diğerleri daha az olabilir - bunlar için Joran'ın tartışmalarında bir gerekçe bulacaksınız.

hafıza geliştirme sistemi

  • lapplyBir olan liste bir liste veya vektör üzerinde hareket eder ve bir liste döndüren geçerlidir.
  • sapplya, basit lapply (bir vektör ya da bir matris mümkün döndürme için işlev varsayılan)
  • vapplyBir olduğunu uygulamak doğrulanmış (dönüş nesne türü önceden belirlenmifl- sağlar)
  • rapplyBir olan özyinelemeli iç içe listeler, listeler içinde yani listeleri için geçerlidir
  • tapplyBir olduğu etiketli etiketleri alt kümelerini belirlemek nerede uygulamak
  • apply olduğu genel : Bir matrisin satırlar veya sütunlar bir fonksiyonu uygular (veya, daha genel olarak, bir dizi boyutlarına)

Doğru Arka Planı Oluşturma

Kullanılıyorsa applyailesini yine sana biraz yabancı hisseder, o zaman bakış kilit noktasını eksik olduğunu olabilir.

Bu iki makale yardımcı olabilir. Fonksiyonapply ailesi tarafından sağlanan fonksiyonel programlama tekniklerini motive etmek için gerekli altyapıyı sağlarlar .

Lisp kullanıcıları paradigmayı hemen tanıyacaklar. Lisp'e aşina değilseniz, başınızı FP'ye götürdüğünüzde, R'de kullanmak için güçlü bir bakış açısı kazanmış olacaksınız ve applyçok daha mantıklı olacaksınız.


51

Bu yazının (çok mükemmel) cevaplarının eksikliğini byve aggregateaçıklamalarını fark ettiğimden beri . İşte katkım.

TARAFINDAN

byOlarak belgelerinde belirtildiği fonksiyon, bir "sargı" gibi olsa da olabilir tapply. byİşleyemediğimiz bir görevi hesaplamak istediğimiz zaman ortaya çıkan güç ortaya çıkar tapply. Bir örnek bu kod:

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 

Bu iki nesne yazdırmak, varsa ctve cbbiz "esasen" aynı sonuçları ve sadece farklılıkları nasıl gösterilir ve farklı olan classsırasıyla nitelikler byiçin cbve arrayiçin ct.

Söylediğim gibi, bykullanamadığımız zaman ortaya çıkan güç tapply; aşağıdaki kod bir örnektir:

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

R, argümanların aynı uzunluklara sahip olması gerektiğini söylüyor: " summarytüm değişkenin irisfaktör boyunca hesaplanmasını istiyoruz Species": ancak R bunu yapamaz çünkü nasıl ele alınacağını bilmez.

İle byfonksiyonu, R için özel bir yöntem sevk data framesınıf ve daha sonra izin summaryfonksiyonu ilk bağımsız değişken uzunluk (ve çok tipi) farklı olsa bile çalışır.

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     

gerçekten işe yarıyor ve sonuç çok şaşırtıcı. Her değişkenin bybirlikte Species(örneğin her biri için) hesapladığı bir sınıf nesnesidir summary.

İlk bağımsız değişken a data frameise, gönderilen işlevin bu nesne sınıfı için bir yöntemi olması gerektiğini unutmayın. Örneğin mean, bu kodu, hiçbir anlamı olmayan bu koda sahip olacağımız işlevle kullanıyoruz :

 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

AGREGA

aggregatebu şekilde kullanırsak başka bir kullanım şekli olarak görülebilir 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

İki yakın fark ikinci argüman, olan aggregate gerekir ise bir liste tapply kutusu (zorunlu değil) bir liste ve çıkış olduğu aggregatebir süre bir veri çerçevesi olan tapplybir bir array.

Bunun gücü aggregate, verilerin alt kümelerini subsetargümanla kolayca işleyebilmesidir ve tsnesneler için de yöntemlere sahip olmasıdır formula.

Bu öğeler bazı durumlarda aggregatebununla çalışmayı kolaylaştırır tapply. İşte bazı örnekler (belgelerde mevcuttur):

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

Aynı şeyi elde edebiliriz tapplyancak sözdizimi biraz daha zor ve çıktı (bazı durumlarda) daha az okunabilir:

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

Orada kullanamayan diğer zamanlar byya tapplyve biz kullanmak zorunda 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

tapplyBir aramada önceki sonucu elde edemiyoruz, ancak Monthher eleman için ortalamayı hesaplamamız ve sonra birleştirmemiz gerekiyor (ayrıca , işlevin yöntemleri varsayılan olarak the'e sahip na.rm = TRUEolduğu için, şunu çağırmamız gerektiğini unutmayın ):formulaaggregatena.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

Bununla birlikte by, aslında aşağıdaki işlev çağrısının bir hata döndürdüğünü başaramayız (ancak büyük olasılıkla sağlanan işlevle ilgilidir mean):

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

Diğer zamanlarda sonuçlar aynıdır ve farklılıklar sadece sınıfta (ve daha sonra nasıl gösterildiğini / yazdırıldığını ve sadece - değil, nasıl alt kümeleneceğini) nesnesindedir:

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

Önceki kod aynı hedefe ve sonuçlara ulaşır, bazı noktalarda hangi aracın kullanılacağı sadece kişisel zevkler ve ihtiyaçlar meselesidir; önceki iki nesnenin alt kümeleme açısından çok farklı ihtiyaçları vardır.


Söylediğim gibi, tapply kullanamadığımızda onun gücü ortaya çıkar; Aşağıdaki kod bir örnektir: BU YUKARIDA KULLANILAN KELİMELERDİR. Ve özeti hesaplama örneği verdiniz. Peki diyelim özet istatistikleri sadece temizlenmesi gereken hesaplanabilir: örneğin data.frame(tapply(unlist(iris[,-5]),list(rep(iris[,5],ncol(iris[-5])),col(iris[-5])),summary))bu tapply . With the right splitting there is nothing you cant do with tapply . The only thing is it returns a matrix. Please be careful by saying we cant use tapply` bir kullanımdır
Onyambu

35

Her fonksiyon için kullanım durumlarındaki farklılıkları tartışan birçok harika cevap vardır. Yanıtların hiçbiri performanstaki farklılıkları tartışmıyor. Bu, çeşitli fonksiyonların çeşitli girdiler beklediği ve çeşitli çıktılar ürettiği için makul bir nedendir, ancak çoğu seri / grup tarafından değerlendirmek için genel bir ortak amaca sahiptir. Cevabım performansa odaklanacak. Yukarıdaki vektörlerden girdi oluşturma zamanlamaya dahil edildiğinden, applyfonksiyon da ölçülmez.

Ben iki farklı fonksiyon test ettik sumve lengthaynı anda. Test edilen hacim girişte 50M ve çıkışta 50K'dır. Ayrıca, soru sorulduğu sırada yaygın olarak kullanılmayan iki popüler paketi de dahil ettim data.tableve dplyr. Her ikisi de iyi bir performans hedefliyorsanız kesinlikle bakmaya değer.

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

Dplyr'ın applt işlevlerinden daha düşük olması normal midir?
Mostafa

1
@DimitriPetrenko Ben öyle düşünmüyorum, neden burada olduğundan emin değilim. Oyuna giren birçok faktör olduğu için kendi verilerinize karşı test etmek en iyisidir.
jangorecki

28

Buradaki tüm harika cevaplara rağmen, söylenmeyi hak eden 2 temel fonksiyon daha var, kullanışlı outerfonksiyon ve belirsiz eapplyfonksiyon

dış

outerdaha sıradan bir fonksiyon olarak gizlenmiş çok kullanışlı bir fonksiyondur. outerAçıklamasının yardımını okursanız diyor ki:

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], ...).

bu sadece lineer cebir tipi şeyler için yararlı gibi görünüyor. Bununla birlikte, mapplygirişlerin iki vektörüne bir fonksiyon uygulamak gibi kullanılabilir . Fark, mapplyişlevi ilk iki öğeye ve daha sonra ikinci iki vb.'ye outeruygulayacaktır , oysa işlevi ilk vektörden bir öğenin ve ikinciden bir öğenin her kombinasyonuna uygulayacaktır. Örneğin:

 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

Bir değer vektörüne ve bir koşul vektörüne sahip olduğumda ve hangi değerlerin hangi koşulları karşıladığını görmek istediğimde bunu şahsen kullandım.

eapply

eapplygibi lapplyyerine bir listedeki her elemana bir işlev uygulamadan yerine, bir ortamda her eleman için bir işlev uygular dışında. Örneğin, global ortamda kullanıcı tanımlı işlevlerin bir listesini bulmak istiyorsanız:

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 

Açıkçası bunu çok kullanmıyorum ama çok fazla paket oluşturuyorsanız veya çok fazla ortam oluşturuyorsanız kullanışlı olabilir.


25

Belki de kayda değer ave. aveolan tapplybireyin samimi kuzeni. Sonuçları doğrudan veri çerçevenize geri ekleyebileceğiniz bir formda döndürür.

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
##   ...

Gibi eserler bu baz paketinde şey yok avebütün veri çerçevelerinin (aynı bygibidir tapplyveri çerçevelerinin). Ama şekerleme yapabilirsiniz:

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
## ...

12

Son zamanlarda oldukça kullanışlı sweepişlevi keşfettim ve tamlık uğruna buraya ekledim:

süpürme

Temel fikir için süpürme bir dizi row- veya sütun-bazlı içinden ve bir tadil edilmiş dizi döner. Bir örnek bunu açıklığa kavuşturacaktır (kaynak: datacamp ):

Diyelim ki bir matrisiniz var ve bunu sütun bazında standartlaştırmak istiyorsunuz :

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

Not: Bu basit örnek için aynı sonuç elbette daha kolay bir şekilde elde edilebilir.
apply(dataPoints, 2, scale)


1
Bu gruplama ile ilgili mi?
Frank

2
@Frank: Sana dürüst olmak gerekirse, bu yazının başlığı oldukça yanıltıcı: Sorunun kendisini okuduğunuzda "başvuru ailesi" ile ilgili. sweepburada bahsedilen tüm diğerleri gibi daha üst düzey bir işlevdir, örn apply. sapply, lapplyYani aynı soru 1000'den fazla upvotes ve verilen örnekler ile kabul edilen cevap hakkında sorulabilir. applyOrada verilen örneğe bir göz atın .
vonjd

2
süpürmenin yanıltıcı bir adı, yanıltıcı varsayılanları ve yanıltıcı parametre adı vardır :). Bunu şu şekilde anlamam daha kolay: 1) STATS, ilk girişle aynı boyutta bir matris oluşturmak için tekrarlanacak vektör veya tek bir değerdir, 2) 1. girişe ve bu yeni matrise FUN uygulanacaktır. Belki daha iyi gösterildiği: sweep(matrix(1:6,nrow=2),2,7:9,list). Genellikle daha verimlidir applynerede çünkü applydöngüler, sweepvektörleştirilmiş fonksiyonlarını kullanabilir.
Moody_Mudskipper

2

Gelen çöküşü geçenlerde CRAN yayınlandı pakette, ben ortak çoğu sadece 2 fonksiyonları içine işlevselliğini uygulamak sıkıştırmak için çalıştılar:

  1. dapply(Data-Apply), matrislerin ve data.frames satırlarına veya (varsayılan) sütunlarına işlevler uygular ve (varsayılan) aynı türden ve aynı niteliklere sahip bir nesne döndürür (her hesaplamanın sonucu atomik değilse ve drop = TRUE). Performans lapplydata.frame sütunları ile karşılaştırılabilir ve applymatris satırları veya sütunlarından yaklaşık 2 kat daha hızlıdır . Paralellik mclapply(yalnızca MAC için) aracılığıyla kullanılabilir .

Sözdizimi:

dapply(X, FUN, ..., MARGIN = 2, parallel = FALSE, mc.cores = 1L, 
       return = c("same", "matrix", "data.frame"), drop = TRUE)

Örnekler:

# Apply to columns:
dapply(mtcars, log)
dapply(mtcars, sum)
dapply(mtcars, quantile)
# Apply to rows:
dapply(mtcars, sum, MARGIN = 1)
dapply(mtcars, quantile, MARGIN = 1)
# Return as matrix:
dapply(mtcars, quantile, return = "matrix")
dapply(mtcars, quantile, MARGIN = 1, return = "matrix")
# Same for matrices ...
  1. BYvektör, matris ve data.frame yöntemiyle split-Apply-combine hesaplama için kullanılan bir S3 jenerikidir. Bu önemli ölçüde daha hızlı daha tapply, byve aggregate(bir de daha hızlı plyr, büyük verilerde dplyrdaha hızlı olsa).

Sözdizimi:

BY(X, g, FUN, ..., use.g.names = TRUE, sort = TRUE,
   expand.wide = FALSE, parallel = FALSE, mc.cores = 1L,
   return = c("same", "matrix", "data.frame", "list"))

Örnekler:

# Vectors:
BY(iris$Sepal.Length, iris$Species, sum)
BY(iris$Sepal.Length, iris$Species, quantile)
BY(iris$Sepal.Length, iris$Species, quantile, expand.wide = TRUE) # This returns a matrix 
# Data.frames
BY(iris[-5], iris$Species, sum)
BY(iris[-5], iris$Species, quantile)
BY(iris[-5], iris$Species, quantile, expand.wide = TRUE) # This returns a wider data.frame
BY(iris[-5], iris$Species, quantile, return = "matrix") # This returns a matrix
# Same for matrices ...

Gruplama değişkenlerinin listeleri de sağlanabilir g.

Performans hakkında konuşmak: Çöküşün temel amacı, R'de yüksek performanslı programlamayı teşvik etmek ve split-uygula-birleştirin ötesine geçmektir. Bu amaçla paket C tam bir set ++ tabanlı hızlı jenerik işlevi vardır: fmean, fmedian, fmode, fsum, fprod, fsd, fvar, fmin, fmax, ffirst, flast, fNobs, fNdistinct, fscale, fbetween, fwithin, fHDbetween, fHDwithin, flag, fdiffve fgrowth. Gruplanmış hesaplamaları verilerden tek bir geçişte gerçekleştirirler (yani bölme ve yeniden birleştirme yok).

Sözdizimi:

fFUN(x, g = NULL, [w = NULL,] TRA = NULL, [na.rm = TRUE,] use.g.names = TRUE, drop = TRUE)

Örnekler:

v <- iris$Sepal.Length
f <- iris$Species

# Vectors
fmean(v)             # mean
fmean(v, f)          # grouped mean
fsd(v, f)            # grouped standard deviation
fsd(v, f, TRA = "/") # grouped scaling
fscale(v, f)         # grouped standardizing (scaling and centering)
fwithin(v, f)        # grouped demeaning

w <- abs(rnorm(nrow(iris)))
fmean(v, w = w)      # Weighted mean
fmean(v, f, w)       # Weighted grouped mean
fsd(v, f, w)         # Weighted grouped standard-deviation
fsd(v, f, w, "/")    # Weighted grouped scaling
fscale(v, f, w)      # Weighted grouped standardizing
fwithin(v, f, w)     # Weighted grouped demeaning

# Same using data.frames...
fmean(iris[-5], f)                # grouped mean
fscale(iris[-5], f)               # grouped standardizing
fwithin(iris[-5], f)              # grouped demeaning

# Same with matrices ...

Paket vinyetlerinde kıstaslar sunuyorum. Hızlı işlevlerle programlama , özellikle daha küçük verilerde değil, aynı zamanda büyük verilerde de dplyr veya data.table ile programlamaya göre çok daha hızlıdır .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.