... mengenai waktu eksekusi dan / atau memori.
Jika ini tidak benar, buktikan dengan potongan kode. Perhatikan bahwa speedup oleh vektorisasi tidak masuk hitungan. Speedup harus berasal dari apply
( tapply
, sapply
, ...) itu sendiri.
The apply
fungsi dalam R tidak memberikan peningkatan kinerja lebih fungsi perulangan lainnya (misalnya for
). Satu pengecualian untuk ini adalah lapply
yang bisa sedikit lebih cepat karena ia bekerja lebih banyak dalam kode C daripada di R (lihat pertanyaan ini untuk contohnya ).
Namun secara umum, aturannya adalah Anda harus menggunakan fungsi terapkan untuk kejelasan, bukan untuk kinerja .
Saya ingin menambahkan ini yang menerapkan fungsi tidak memiliki efek samping , yang merupakan perbedaan penting ketika datang ke pemrograman fungsional dengan R. Ini dapat diganti dengan menggunakan assign
atau <<-
, tetapi itu bisa sangat berbahaya. Efek samping juga membuat program lebih sulit untuk dipahami karena keadaan variabel tergantung pada sejarah.
Edit:
Hanya untuk menekankan ini dengan contoh sepele yang secara rekursif menghitung urutan Fibonacci; ini dapat dijalankan beberapa kali untuk mendapatkan ukuran yang akurat, tetapi intinya adalah bahwa tidak ada metode yang memiliki kinerja yang sangat berbeda:
> fibo <- function(n) {
+ if ( n < 2 ) n
+ else fibo(n-1) + fibo(n-2)
+ }
> system.time(for(i in 0:26) fibo(i))
user system elapsed
7.48 0.00 7.52
> system.time(sapply(0:26, fibo))
user system elapsed
7.50 0.00 7.54
> system.time(lapply(0:26, fibo))
user system elapsed
7.48 0.04 7.54
> library(plyr)
> system.time(ldply(0:26, fibo))
user system elapsed
7.52 0.00 7.58
Edit 2:
Mengenai penggunaan paket paralel untuk R (mis. Rpvm, rmpi, snow), ini biasanya menyediakan apply
fungsi keluarga (bahkan foreach
paket itu pada dasarnya setara, terlepas dari namanya). Berikut adalah contoh sederhana dari sapply
fungsi di snow
:
library(snow)
cl <- makeSOCKcluster(c("localhost","localhost"))
parSapply(cl, 1:20, get("+"), 3)
Contoh ini menggunakan soket cluster, yang tidak perlu diinstal perangkat lunak tambahan; jika tidak, Anda akan membutuhkan sesuatu seperti PVM atau MPI (lihat halaman pengelompokan Tierney ). snow
memiliki fungsi terapkan berikut ini:
parLapply(cl, x, fun, ...)
parSapply(cl, X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)
parApply(cl, X, MARGIN, FUN, ...)
parRapply(cl, x, fun, ...)
parCapply(cl, x, fun, ...)
Masuk akal bahwa apply
fungsi harus digunakan untuk eksekusi paralel karena mereka tidak memiliki efek samping . Ketika Anda mengubah nilai variabel dalam satu for
lingkaran, itu diatur secara global. Di sisi lain, semua apply
fungsi dapat dengan aman digunakan secara paralel karena perubahan bersifat lokal pada pemanggilan fungsi (kecuali jika Anda mencoba menggunakan assign
atau <<-
, dalam hal ini Anda dapat menimbulkan efek samping). Tidak perlu dikatakan, sangat penting untuk berhati-hati tentang variabel lokal vs global, terutama ketika berhadapan dengan eksekusi paralel.
Edit:
Berikut adalah contoh sepele untuk menunjukkan perbedaan antara for
dan *apply
sejauh menyangkut efek samping:
> df <- 1:10
> # *apply example
> lapply(2:3, function(i) df <- df * i)
> df
[1] 1 2 3 4 5 6 7 8 9 10
> # for loop example
> for(i in 2:3) df <- df * i
> df
[1] 6 12 18 24 30 36 42 48 54 60
Perhatikan bagaimana df
lingkungan induk diubah oleh for
tetapi tidak *apply
.
apply
fungsi. Oleh karena itu, penataan program yang mereka gunakan berlaku memungkinkan mereka diparalelkan dengan biaya marjinal yang sangat kecil.snowfall
paket dan mencoba contoh-contoh dalam sketsa mereka.snowfall
dibangun di atassnow
paket dan abstrak detail dari paralelisasi bahkan membuatnya lebih mudah untuk mengeksekusiapply
fungsi paralel .foreach
telah tersedia dan tampaknya banyak bertanya tentang SO.lapply
"sedikit lebih cepat" daripada satufor
lingkaran. Namun, di sana, saya tidak melihat ada yang menyarankan begitu. Anda hanya menyebutkan bahwalapply
lebih cepat daripadasapply
, yang merupakan fakta terkenal karena alasan lain (sapply
mencoba menyederhanakan output dan karenanya harus melakukan banyak pengecekan ukuran data dan konversi potensial). Tidak ada yang terkait denganfor
. Apakah saya melewatkan sesuatu?Kadang-kadang speedup bisa menjadi besar, seperti ketika Anda harus bersarang untuk-loop untuk mendapatkan rata-rata berdasarkan pengelompokan lebih dari satu faktor. Di sini Anda memiliki dua pendekatan yang memberi Anda hasil yang sama persis:
Keduanya memberikan hasil yang persis sama, menjadi matriks 5 x 10 dengan rata-rata dan diberi nama baris dan kolom. Tapi:
Ini dia. Apa yang saya menangkan? ;-)
sumber
*apply
lebih cepat. Tetapi saya berpikir bahwa poin yang lebih penting adalah efek sampingnya (memperbarui jawaban saya dengan contoh).data.table
bahkan lebih cepat dan saya pikir "lebih mudah".library(data.table)
dt<-data.table(X,Y,Z,key=c("Y,Z"))
system.time(dt[,list(X_mean=mean(X)),by=c("Y,Z")])
tapply
adalah fungsi khusus untuk tugas tertentu, itu sebabnya lebih cepat daripada for loop. Itu tidak bisa melakukan apa yang bisa dilakukan untuk loop (sementara biasaapply
bisa). Anda membandingkan apel dengan jeruk.... dan seperti yang baru saja saya tulis di tempat lain, vapply adalah teman Anda! ... itu seperti sapply, tetapi Anda juga menentukan tipe nilai pengembalian yang membuatnya jauh lebih cepat.
Pembaruan 1 Januari 2020:
sumber
for
loop lebih cepat pada komputer Windows 10, 2-core saya. Saya melakukan ini dengan5e6
elemen - loop adalah 2,9 detik vs 3,1 detik untukvapply
.Saya sudah menulis di tempat lain bahwa contoh seperti Shane's tidak benar-benar menekankan perbedaan kinerja di antara berbagai jenis sintaksis perulangan karena semua waktu dihabiskan dalam fungsi daripada benar-benar menekankan loop. Selain itu, kode ini secara tidak adil membandingkan loop untuk tanpa memori dengan menerapkan fungsi keluarga yang mengembalikan nilai. Inilah contoh yang sedikit berbeda yang menekankan intinya.
Jika Anda berencana untuk menyimpan hasilnya kemudian menerapkan fungsi keluarga dapat menjadi jauh lebih dari sintaksis gula.
(unlist sederhana z hanya 0.2s sehingga hasilnya jauh lebih cepat. Menginisialisasi z dalam for loop cukup cepat karena saya memberikan rata-rata 5 dari 6 berjalan terakhir sehingga bergerak di luar system.time akan hampir tidak mempengaruhi hal-hal)
Satu hal lagi yang perlu diperhatikan adalah bahwa ada alasan lain untuk menggunakan fungsi keluarga yang berlaku terlepas dari kinerja, kejelasan, atau kurangnya efek sampingnya. SEBUAH
for
loop biasanya mempromosikan penempatan sebanyak mungkin dalam loop. Ini karena setiap loop memerlukan pengaturan variabel untuk menyimpan informasi (di antara operasi lain yang mungkin). Pernyataan yang diterapkan cenderung bias sebaliknya. Sering kali Anda ingin melakukan beberapa operasi pada data Anda, beberapa di antaranya dapat di-vektor-kan tetapi beberapa mungkin tidak bisa. Dalam R, tidak seperti bahasa lain, yang terbaik adalah memisahkan operasi-operasi tersebut dan menjalankan yang tidak di-vektor-kan dalam pernyataan yang berlaku (atau versi fungsi yang di-vektor-kan) dan yang di-vektor-kan sebagai operasi vektor yang sebenarnya. Ini sering mempercepat kinerja luar biasa.Mengambil contoh Joris Meys di mana ia menggantikan tradisional untuk loop dengan fungsi R praktis kita dapat menggunakannya untuk menunjukkan efisiensi penulisan kode dengan cara yang lebih ramah untuk speedup yang sama tanpa fungsi khusus.
Ini berakhir menjadi jauh lebih cepat daripada
for
loop dan hanya sedikit lebih lambat daripada fungsi built-in yang dioptimalkantapply
. Ini bukan karenavapply
jauh lebih cepat daripadafor
tetapi karena itu hanya melakukan satu operasi di setiap iterasi dari loop. Dalam kode ini segala sesuatu yang lain adalah vektor. Dalamfor
loop tradisional Joris Meys banyak (7?) Operasi yang terjadi di setiap iterasi dan ada cukup banyak pengaturan hanya untuk itu untuk dijalankan. Perhatikan juga seberapa kompaknyafor
versi ini.sumber
2.798 0.003 2.803; 4.908 0.020 4.934; 1.498 0.025 1.528
, dan vapply bahkan lebih baik:1.19 0.00 1.19
sapply
50% lebih lambat darifor
danlapply
dua kali lebih cepat.y
ke1:1e6
, bukannumeric(1e6)
(vektor nol). Mencoba untuk mengalokasikanfoo(0)
untukz[0]
berulang tidak menggambarkan dengan baik khasfor
penggunaan lingkaran. Pesannya dinyatakan tepat.Saat menerapkan fungsi pada subset vektor,
tapply
bisa jadi lebih cepat daripada for for. Contoh:apply
Namun, dalam kebanyakan situasi tidak memberikan peningkatan kecepatan, dan dalam beberapa kasus bahkan bisa lebih lambat:Tetapi untuk situasi ini kita punya
colSums
danrowSums
:sumber
microbenchmark
itu jauh lebih tepat daripadasystem.time
. Jika Anda mencoba membandingkansystem.time(f3(mat))
dansystem.time(f4(mat))
Anda akan mendapatkan hasil yang berbeda hampir setiap waktu. Terkadang hanya tes benchmark yang tepat yang mampu menunjukkan fungsi tercepat.