Menu

apply, lapply, sapply di R: penjelasan keluarga apply

Keluarga apply (apply, lapply, sapply, vapply, mapply, dan tapply) menjalankan sebuah fungsi pada setiap elemen datamu. Ini fungsi masing-masing dan kapan memakainya.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Keluarga apply dalam satu kalimat

Setiap anggota keluarga apply melakukan tugas yang sama: mengambil sebuah fungsi dan menjalankannya pada setiap elemen sebuah struktur, lalu mengumpulkan hasilnya. Yang berbeda adalah bentuk input dan bentuk output. Petanya:

  • apply(m, MARGIN, FUN): baris atau kolom sebuah matrix.
  • lapply(x, FUN): setiap elemen sebuah list atau vector; selalu mengembalikan list.
  • sapply(x, FUN): sama seperti lapply, lalu disederhanakan menjadi vector atau matrix kalau bisa.
  • vapply(x, FUN, FUN.VALUE): sapply dengan tipe hasil yang dinyatakan dan dipaksakan.
  • mapply(FUN, x, y, ...): menelusuri beberapa input secara paralel, elemen demi elemen.
  • tapply(values, groups, FUN): menerapkan FUN di dalam setiap kelompok: satu hasil per kelompok.

Semuanya menerima fungsi yang kamu tulis sendiri, termasuk fungsi anonim, dan dari situlah kekuatan keluarga ini berasal.

apply(): baris dan kolom sebuah matrix

apply menerima sebuah matrix, sebuah MARGIN (1 untuk baris, 2 untuk kolom), dan sebuah fungsi:

Matrix diisi kolom demi kolom, jadi barisnya adalah 1 3 5 dan 2 4 6: jumlah per baris tercetak sebagai 9 12 dan jumlah per kolom sebagai 3 7 11. Untuk dua kasus yang paling umum, base R menyediakan helper khusus yang lebih cepat (rowSums, colSums, rowMeans, colMeans), jadi simpan apply untuk fungsi yang tidak punya helper seperti itu, misalnya apply(m, 2, max).

Satu jebakan: apply pada data frame diam-diam mengubahnya dulu menjadi matrix, sehingga setiap kolom dipaksa ke satu tipe yang sama. Untuk data frame, perlakukan kolom-kolomnya sebagai list dan pakai lapply/sapply.

lapply() selalu memberi list, sapply() menyederhanakan

lapply memetakan sebuah fungsi ke setiap elemen dan mengembalikan sebuah list dengan panjang yang sama, apa pun yang terjadi:

Perhitungannya sama, bentuknya dua: lapply mengembalikan list yang berisi 85 dan 80; sapply melihat bahwa setiap hasil panjangnya 1 lalu menyederhanakannya menjadi numeric vector bernama, yang jauh lebih enak dibaca dan dipakai untuk perhitungan berikutnya.

Tapi kemudahan ini punya sisi tajam: tipe hasil sapply bergantung pada datanya. Kalau satu elemen saja menghasilkan panjang yang berbeda, penyederhanaan gagal dan kamu diam-diam mendapat list lagi:

Di console, itu bukan masalah besar; di dalam skrip, artinya kode yang berjalan baik sepanjang tahun bisa rusak pada hari bentuk datanya berubah. Itulah satu-satunya alasan vapply ada.

vapply(): sapply yang aman tipenya

vapply menambahkan argumen ketiga, FUN.VALUE: sebuah templat yang menyatakan seperti apa satu hasil seharusnya. integer(1) berarti "setiap pemanggilan mengembalikan tepat satu integer":

Kamu mendapat integer vector bernama, 5 6 6, dan yang lebih penting, sebuah jaminan: kalau nchar suatu saat mengembalikan dua nilai atau sebuah character, vapply akan berhenti dengan error di tempat pemanggilannya alih-alih membiarkan hasil yang salah bentuk mengalir ke kode berikutnya. Deklarasinya sekaligus menjadi dokumentasi:

vapply(words, nchar, character(1))
# Error in vapply(words, nchar, character(1)) : values must be type 'character'

Aturan praktis: sapply di console, vapply di fungsi dan skrip yang harus berjalan tanpa diawasi.

mapply() dan tapply(): input paralel dan kelompok

lapply menelusuri satu struktur. Kalau setiap pemanggilan membutuhkan elemen dari beberapa struktur pada posisi yang sama, pakai mapply. Perhatikan bahwa fungsinya ditulis pertama:

Setiap elemen output memasangkan nilai pertama dari masing-masing input, lalu nilai kedua, dan seterusnya: 10 200 3000 40000.

tapply adalah anggota yang bekerja per kelompok: fungsi ini memecah values berdasarkan groups dan menerapkan fungsinya di dalam setiap kelompok, lalu mengembalikan satu hasil per kelompok:

Rata-rata kelompok a adalah 30, rata-rata kelompok b adalah 40. Inilah jawaban base R untuk "rata-rata per kategori", pertanyaan berbentuk sama yang dijawab group_by + summarize untuk data frame. Jadi kalau kamu sudah memakai dplyr, pakai itu; tapply unggul saat kamu punya dua vector biasa dan ingin satu baris saja.

Dua kemudahan berlaku untuk seluruh keluarga. Argumen tambahan setelah fungsi diteruskan ke fungsi itu di setiap pemanggilan:

Dan fungsi anonim bisa disisipkan setiap kali tidak ada fungsi siap pakai yang cocok: sapply(x, \(v) max(v) - min(v)) menghitung rentang per elemen tanpa perlu menamai helper terlebih dulu.

Keluarga apply vs for loop

Kamu akan mendengar bahwa for loop lambat di R dan keluarga apply cepat. Itu sebagian besar mitos. Yang benar-benar lambat adalah menumbuhkan hasil di dalam loop: result <- c(result, new_value) menyalin seluruh vector di setiap putaran. Loop yang mengalokasikan output-nya di awal punya performa yang baik:

Jadi pilihlah berdasarkan keterbacaan, bukan performa. Versi apply mengatakan apa yang dilakukan dalam satu baris ("kuadratkan setiap elemen") dan mengurus alokasi untukmu, itulah sebabnya cara ini menjadi idiom untuk pekerjaan per elemen yang sederhana. For loop layak dipakai saat setiap iterasi bergantung pada hasil sebelumnya, saat kamu butuh keluar lebih awal dengan break, atau saat isinya cukup panjang sehingga lambda justru lebih merepotkan. Keduanya sama-sama R yang sah.

Yang kamu bawa pulang

  • Seluruh keluarga ini satu gagasan: jalankan sebuah fungsi pada setiap elemen, kumpulkan hasilnya.
  • apply untuk baris (MARGIN = 1) dan kolom (MARGIN = 2) sebuah matrix; utamakan rowSums/colMeans kalau tersedia.
  • lapply selalu mengembalikan list; sapply menyederhanakan kalau bisa, artinya tipe hasilnya bisa berubah mengikuti data.
  • vapply mengunci tipe hasilnya; pakai di kode yang tidak boleh memberi kejutan.
  • mapply menggabungkan beberapa input sekaligus; tapply mengagregasi nilai di dalam kelompok.
  • For loop tidak lambat dengan sendirinya; yang lambat adalah menumbuhkan vector. Pilih cara penulisan yang paling enak dibaca.

Berikutnya: pipe, operator yang merangkai pemanggilan-pemanggilan ini menjadi pipeline langkah demi langkah yang mudah dibaca.

Pertanyaan yang sering diajukan

Apa perbedaan lapply dan sapply di R?

lapply selalu mengembalikan list, tanpa kecuali. sapply menjalankan perhitungan yang sama lalu mencoba menyederhanakan hasilnya: menjadi vector kalau setiap elemen panjangnya 1, menjadi matrix kalau panjangnya sama, dan kembali ke list kalau tidak bisa. sapply lebih enak dipakai secara interaktif; lapply (atau vapply) lebih aman di skrip karena tipe hasilnya tidak pernah berubah.

Apa fungsi apply() di R?

apply(m, MARGIN, FUN) menjalankan FUN pada sebuah matrix: MARGIN = 1 menerapkannya ke setiap baris, MARGIN = 2 ke setiap kolom. apply(m, 1, sum) menghasilkan jumlah per baris, apply(m, 2, mean) menghasilkan rata-rata per kolom. Fungsi ini untuk matrix dan array; untuk list dan vector, pakai lapply/sapply.

Apakah keluarga apply lebih cepat daripada for loop di R?

Biasanya tidak jauh berbeda; itu mitos. For loop yang ditulis dengan baik dan vector hasilnya dialokasikan di awal punya performa yang sebanding. Loop yang membuat for terkenal buruk adalah loop yang menumbuhkan hasilnya satu elemen demi satu elemen, sehingga menyalin semuanya di setiap putaran. Pilih fungsi keluarga apply karena ringkas dan jelas maksudnya, bukan karena kecepatan.

Untuk apa vapply di R?

vapply adalah sapply dengan kontrak: kamu menyatakan tipe dan panjang setiap hasil, misalnya vapply(x, nchar, integer(1)). Kalau fungsinya mengembalikan hal lain, R langsung memunculkan error alih-alih diam-diam memberimu struktur yang tidak terduga. Utamakan fungsi ini di kode yang harus terus berjalan tanpa diawasi.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai