Pertanyaan Pengelompokan fungsi (tapply, by, aggregate) dan * menerapkan family


Setiap kali saya ingin melakukan sesuatu "map" py di R, saya biasanya mencoba menggunakan fungsi di apply keluarga.

Namun, saya tidak pernah mengerti perbedaan di antara mereka - bagaimana caranya {sapply, lapply, dsb.} menerapkan fungsi ke input / pengelompokan input, seperti apa output akan terlihat, atau bahkan apa inputnya - jadi saya sering melewati semuanya sampai saya mendapatkan apa yang saya inginkan.

Bisakah seseorang menjelaskan cara menggunakan yang mana?

Pemahaman saya saat ini (mungkin salah / tidak lengkap) adalah ...

  1. sapply(vec, f): input adalah sebuah vektor. output adalah vektor / matriks, di mana elemen i aku s f(vec[i]), memberi Anda matriks jika f memiliki output multi-elemen

  2. lapply(vec, f): sama dengan sapply, tetapi output adalah daftar?

  3. apply(matrix, 1/2, f): input adalah matriks. output adalah vektor, di mana elemen i adalah f (baris / kolom i dari matriks)
  4. tapply(vector, grouping, f): output adalah matriks / larik, di mana elemen dalam matriks / larik adalah nilainya f di sebuah pengelompokan g dari vektor, dan g didorong ke nama baris / kolom
  5. by(dataframe, grouping, f): biarkan g menjadi pengelompokan. menerapkan f ke setiap kolom grup / dataframe. cukup cetak pengelompokan dan nilai f di setiap kolom.
  6. aggregate(matrix, grouping, f): mirip dengan by, tetapi bukannya cukup mencetak output, agregat menempel semuanya ke dalam dataframe.

Pertanyaan sampingan: Saya masih belum belajar plyr atau membentuk kembali - akan plyr atau reshape ganti semua ini sepenuhnya?


915
2017-08-17 18:31


asal


Jawaban:


R memiliki banyak * fungsi yang dapat dijelaskan dengan jelas dalam file bantuan (mis. ?apply). Namun ada cukup banyak di antara mereka, bahwa penggunaan awal Rs mungkin mengalami kesulitan memutuskan mana yang tepat untuk situasi mereka atau bahkan mengingat semuanya. Mereka mungkin memiliki pengertian umum bahwa "Saya seharusnya menggunakan * menerapkan fungsi di sini", tetapi itu bisa sulit untuk menjaga mereka semua lurus pada awalnya.

Terlepas dari fakta (dicatat dalam jawaban lain) bahwa banyak fungsi dari * berlaku keluarga ditutupi oleh yang sangat populer plyr paket, fungsi dasar tetap berguna dan layak diketahui.

Jawaban ini dimaksudkan untuk bertindak sebagai semacam plang untuk penggunaan R yang baru untuk membantu mengarahkannya ke fungsi * terapkan yang benar untuk masalah khusus mereka. Catatan, ini tidak dimaksudkan untuk hanya memuntahkan atau mengganti dokumentasi R! Harapannya adalah bahwa jawaban ini membantu Anda memutuskan yang mana * menerapkan fungsi sesuai dengan situasi Anda dan kemudian terserah Anda untuk meneliti lebih lanjut. Dengan satu pengecualian, perbedaan kinerja tidak akan diatasi.

  • menerapkan - Ketika Anda ingin menerapkan fungsi ke baris atau kolom matriks (dan analog dimensi tinggi); umumnya tidak disarankan untuk frame data karena akan memaksa ke matriks pertama.

    # 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
    

    Jika Anda ingin baris / kolom berarti atau jumlah untuk matriks 2D, pastikan untuk selidiki yang sangat dioptimalkan, secepat kilat colMeans, rowMeans, colSums, rowSums.

  • lapply - Saat Anda ingin menerapkan fungsi ke setiap elemen a daftar secara bergantian dan dapatkan daftar kembali.

    Ini adalah pekerja keras dari banyak yang lain * menerapkan fungsi. Kulit kembali kode mereka dan Anda akan sering menemukannya lapply di bawah.

    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 - Saat Anda ingin menerapkan fungsi ke setiap elemen a daftar secara bergantian, tetapi Anda menginginkan a vektor kembali, bukan daftar.

    Jika Anda menemukan diri Anda mengetik unlist(lapply(...)), berhenti dan pertimbangkan 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 
    

    Dalam penggunaan yang lebih canggih sapply itu akan mencoba untuk memaksa hasil ke array multi-dimensi, jika sesuai. Misalnya, jika fungsi kami menghasilkan vektor dengan panjang yang sama, sapply akan menggunakannya sebagai kolom matriks:

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

    Jika fungsi kami mengembalikan matriks 2 dimensi, sapply pada dasarnya akan melakukan hal yang sama, memperlakukan setiap matriks yang dikembalikan sebagai vektor tunggal tunggal:

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

    Kecuali kita tentukan simplify = "array", dalam hal ini akan menggunakan matriks individu untuk membangun array multi-dimensi:

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

    Masing-masing perilaku ini tentu saja bergantung pada vektor atau matriks fungsi kita yang memiliki panjang atau dimensi yang sama.

  • vapply - Kapan Anda ingin menggunakannya sapply tapi mungkin perlu peras lagi kecepatan dari kode Anda.

    Untuk vapply, pada dasarnya Anda memberi R contoh hal semacam itu fungsi Anda akan kembali, yang dapat menghemat waktu kembali nilai untuk masuk dalam vektor atom tunggal.

    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
    
  • secara mapply - Karena ketika Anda memiliki beberapa struktur data (mis. vektor, daftar) dan Anda ingin menerapkan fungsi ke elemen 1 masing-masing, dan kemudian elemen ke-2 dari masing-masing, dll., memaksa hasilnya ke vektor / larik seperti di sapply.

    Ini multivariasi dalam arti bahwa fungsi Anda harus menerima beberapa argumen.

    #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
    
  • Peta - Sebuah pembungkus mapply dengan SIMPLIFY = FALSE, jadi dijamin untuk mengembalikan daftar.

    Map(sum, 1:5, 1:5, 1:5)
    [[1]]
    [1] 3
    
    [[2]]
    [1] 6
    
    [[3]]
    [1] 9
    
    [[4]]
    [1] 12
    
    [[5]]
    [1] 15
    
  • rapply - Karena ketika Anda ingin menerapkan fungsi ke setiap elemen a daftar bertingkat struktur, secara rekursif.

    Untuk memberi Anda gambaran tentang betapa tidak biasa rapply adalah, saya lupa tentang hal itu ketika pertama kali memposting jawaban ini! Tentunya, saya yakin banyak orang menggunakannya, tapi YMMV. rapply diilustrasikan dengan baik dengan fungsi yang ditentukan pengguna untuk diterapkan:

    # 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 - Karena ketika Anda ingin menerapkan fungsi ke himpunan bagian dari a vektor dan himpunan bagian ditentukan oleh beberapa vektor lain, biasanya a faktor.

    Domba hitam dari keluarga yang menerapkan, sejenis. Penggunaan file bantuan frasa "ragged array" dapat sedikit membingungkan, tapi sebenarnya cukup sederhana.

    Vektor:

    x <- 1:20
    

    Faktor (dengan panjang yang sama!) Mendefinisikan grup:

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

    Tambahkan nilai dalam x dalam setiap subkelompok yang ditentukan oleh y:

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

    Contoh yang lebih kompleks dapat ditangani di mana subkelompok didefinisikan oleh kombinasi unik dari daftar beberapa faktor. tapply aku s serupa dalam semangat untuk fungsi split-apply-combine yang umum di R (aggregate, by, ave, ddply, dll.) Oleh karena itu status domba hitam.


1204
2017-08-21 22:50



Di samping catatan, di sini adalah bagaimana berbagai plyr fungsi sesuai dengan basis *apply fungsi (dari dokumen intro ke plyr dari halaman 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 

Salah satu tujuan plyr adalah untuk menyediakan konvensi penamaan yang konsisten untuk masing-masing fungsi, menyandikan tipe data input dan output dalam nama fungsi. Ini juga memberikan konsistensi dalam output, dalam output itu dari dlply() mudah dilewati ldply() untuk menghasilkan keluaran yang bermanfaat, dll.

Secara konseptual, belajar plyr tidak lebih sulit daripada memahami dasarnya *apply fungsi.

plyr dan reshape fungsi telah menggantikan hampir semua fungsi ini dalam penggunaan saya sehari-hari. Tetapi, juga dari dokumen Intro to Plyr:

Fungsi terkait tapply dan sweep tidak memiliki fungsi yang sesuai plyr, dan tetap berguna. merge berguna untuk menggabungkan ringkasan dengan data asli.


171
2017-08-17 19:20



Dari slide 21 dari http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy:

apply, sapply, lapply, by, aggregate

(Semoga itu jelas apply sesuai dengan @ Hadley's aaply dan aggregate sesuai dengan @ Hadley's ddply dll. Slide 20 dari slideshare yang sama akan memperjelas jika Anda tidak mendapatkannya dari gambar ini.)

(di sebelah kiri adalah input, di bagian atas adalah output)


118
2017-10-09 05:29



Pertama mulai dengan Jawaban Joran yang luar biasa - apa pun yang meragukan bisa lebih baik.

Kemudian mnemonik berikut dapat membantu mengingat perbedaan antara masing-masing. Sementara beberapa jelas, yang lain mungkin kurang begitu --- untuk ini Anda akan menemukan pembenaran dalam diskusi Joran.

Ilmu tentang cara menghafal

  • lapply adalah daftar berlaku yang bertindak pada daftar atau vektor dan mengembalikan daftar.
  • sapply adalah sederhana  lapply (fungsi default untuk mengembalikan vektor atau matriks bila mungkin)
  • vapply adalah terverifikasi berlaku (memungkinkan jenis objek yang dikembalikan untuk ditentukan sebelumnya)
  • rapply adalah rekursif berlaku untuk daftar bertingkat, yaitu daftar dalam daftar
  • tapply adalah diberi tag berlaku di mana tag mengidentifikasi himpunan bagian
  • apply  aku s umum: menerapkan fungsi ke baris atau kolom matriks (atau, lebih umum, ke dimensi larik)

Membangun Latar Belakang Kanan

Jika menggunakan apply keluarga masih terasa sedikit asing bagi Anda, maka mungkin Anda kehilangan sudut pandang kunci.

Kedua artikel ini dapat membantu. Mereka memberikan latar belakang yang diperlukan untuk memotivasi teknik pemrograman fungsional yang disediakan oleh apply keluarga fungsi.

Pengguna Lisp akan segera mengenali paradigma itu. Jika Anda tidak akrab dengan Lisp, setelah Anda mendapatkan kepala Anda di sekitar FP, Anda akan mendapatkan sudut pandang yang kuat untuk digunakan di R - dan apply akan lebih masuk akal.


88
2018-04-25 00:20



Sejak saya menyadari bahwa (sangat bagus) jawaban dari posting ini kurang by dan aggregate penjelasan. Inilah kontribusiku.

OLEH

Itu by fungsi, seperti yang dinyatakan dalam dokumentasi bisa menjadi, sebagai "pembungkus" untuk tapply. Kekuatan dari by muncul ketika kita ingin menghitung tugas itu tapplytidak bisa menangani. Salah satu contohnya adalah kode ini:

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 

Jika kita mencetak dua benda ini, ct dan cb, kami "pada dasarnya" memiliki hasil yang sama dan satu-satunya perbedaan adalah bagaimana mereka ditampilkan dan berbeda class atribut, masing-masing by untuk cb dan array untuk ct.

Seperti yang saya katakan, kekuatan by muncul ketika kita tidak bisa menggunakannya tapply; kode berikut adalah salah satu contoh:

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

R mengatakan bahwa argumen harus memiliki panjang yang sama, katakan "kami ingin menghitung summary dari semua variabel dalam iris bersama faktornya Species": tapi R tidak bisa melakukan itu karena tidak tahu cara menangani.

Dengan by fungsi R mengirimkan metode khusus untuk data frame kelas dan kemudian biarkan summary berfungsi bahkan jika panjang argumen pertama (dan jenisnya juga) berbeda.

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     

itu memang berhasil dan hasilnya sangat mengejutkan. Ini adalah objek kelas by itu bersama Species (katakanlah, untuk masing-masing) menghitung summary dari setiap variabel.

Perhatikan bahwa jika argumen pertama adalah a data frame, fungsi yang dikirim harus memiliki metode untuk kelas objek itu. Sebagai contoh adalah kita menggunakan kode ini dengan mean berfungsi kita akan memiliki kode ini yang tidak memiliki arti sama sekali:

 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

AGREGAT

aggregate dapat dilihat sebagai cara lain penggunaan yang berbeda tapply jika kita menggunakannya dengan cara seperti itu.

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

Dua perbedaan langsung adalah bahwa argumen kedua tentang aggregate  harus menjadi daftar sementara tapply  bisa (tidak wajib) menjadi daftar dan output dari aggregate adalah bingkai data sementara salah satunya tapply adalah array.

Kekuatan dari aggregate adalah bahwa ia dapat menangani himpunan bagian data dengan mudah subset argumen dan memiliki metode untuk ts objek dan formula demikian juga.

Unsur-unsur ini membuat aggregate lebih mudah untuk bekerja dengan itu tapply dalam beberapa situasi. Berikut beberapa contoh (tersedia dalam dokumentasi):

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

Kita bisa mencapai hal yang sama dengan tapply tetapi sintaksnya sedikit lebih keras dan hasilnya (dalam beberapa keadaan) kurang dapat dibaca:

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

Ada saat-saat lain ketika kita tidak bisa menggunakannya by atau tapply dan kami harus menggunakannya 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

Kami tidak dapat memperoleh hasil sebelumnya dengan tapply dalam satu panggilan tetapi kita harus menghitung mean bersama Month untuk setiap elemen dan kemudian gabungkan mereka (perhatikan juga bahwa kita harus memanggilnya na.rm = TRUE, karena formula metode dari aggregate fungsi memiliki secara default 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

sementara dengan by kita tidak bisa mencapai itu sebenarnya panggilan fungsi berikut mengembalikan kesalahan (tetapi kemungkinan besar itu terkait dengan fungsi yang disediakan, mean):

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

Di lain waktu hasilnya sama dan perbedaannya hanya di kelas (dan kemudian bagaimana itu ditunjukkan / dicetak dan tidak hanya - contoh, bagaimana men-subsetnya) objek:

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

Kode sebelumnya mencapai tujuan dan hasil yang sama, di beberapa titik alat apa yang digunakan hanyalah masalah selera dan kebutuhan pribadi; dua objek sebelumnya memiliki kebutuhan yang sangat berbeda dalam hal subsetting.


39
2017-08-28 02:28



Ada banyak jawaban bagus yang membahas perbedaan dalam kasus penggunaan untuk setiap fungsi. Tak satu pun dari jawaban membahas perbedaan dalam kinerja. Itu wajar karena berbagai fungsi mengharapkan berbagai masukan dan menghasilkan berbagai output, namun kebanyakan dari mereka memiliki tujuan umum umum untuk mengevaluasi dengan seri / kelompok. Jawaban saya akan fokus pada kinerja. Karena di atas penciptaan input dari vektor termasuk dalam waktu, juga applyfungsi tidak diukur.

Saya telah menguji dua fungsi yang berbeda sum dan length sekaligus. Volume yang diuji adalah 50M pada input dan 50K pada output. Saya juga menyertakan dua paket populer yang saat ini tidak banyak digunakan pada saat pertanyaan diajukan, data.table dan dplyr. Keduanya pasti layak untuk dilihat jika Anda bertujuan untuk kinerja yang baik.

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

30
2017-12-08 22:42



Ini mungkin layak disebut ave. ave aku s tapplysepupu yang ramah. Ini mengembalikan hasil dalam bentuk yang dapat Anda pasang langsung kembali ke bingkai data Anda.

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

Tidak ada dalam paket dasar yang berfungsi seperti itu ave untuk seluruh frame data (as by seperti tapply untuk bingkai data). Tetapi Anda dapat memalsukannya:

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

20
2017-11-06 00:00



Terlepas dari semua jawaban yang bagus di sini, ada 2 lagi fungsi dasar yang layak untuk disebutkan, yang bermanfaat outer fungsi dan tidak jelas eapply fungsi

luar

outer adalah fungsi yang sangat berguna yang tersembunyi sebagai yang lebih biasa. Jika Anda membaca bantuan untuk outer deskripsinya mengatakan:

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

yang membuatnya tampak seperti ini hanya berguna untuk hal-hal jenis aljabar linier. Namun, itu bisa digunakan seperti itu mapply untuk menerapkan fungsi ke dua vektor input. Perbedaannya adalah itu mapply akan menerapkan fungsi ke dua elemen pertama dan kemudian dua yang kedua dll, sedangkan outer akan menerapkan fungsi ke setiap kombinasi dari satu elemen dari vektor pertama dan satu dari yang kedua. Sebagai contoh:

 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

Saya pribadi menggunakan ini ketika saya memiliki vektor nilai dan vektor kondisi dan ingin melihat nilai mana yang memenuhi kondisi apa.

eapply

eapply seperti lapply kecuali bahwa daripada menerapkan fungsi ke setiap elemen dalam daftar, itu berlaku fungsi untuk setiap elemen dalam suatu lingkungan. Misalnya jika Anda ingin mencari daftar fungsi yang ditentukan pengguna di lingkungan global:

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 

Terus terang saya tidak menggunakan ini terlalu banyak tetapi jika Anda sedang membangun banyak paket atau membuat banyak lingkungan itu mungkin berguna.


20
2018-05-16 03:59



Saya baru-baru ini menemukan yang agak berguna sweep berfungsi dan tambahkan di sini demi kelengkapan:

menyapu

Ide dasarnya adalah untuk menyapu melalui array baris atau kolom-bijaksana dan mengembalikan array yang dimodifikasi. Contoh akan membuat ini jelas (sumber: datacamp):

Katakanlah Anda memiliki matriks dan ingin membakukan itu kolom-bijaksana:

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

NB: untuk contoh sederhana ini, hasil yang sama tentu saja dapat dicapai dengan lebih mudah oleh
  apply(dataPoints, 2, scale)


5
2018-06-16 16:03