Saya mencoba menulis fungsi untuk menerima data.frame ( x
) dan column
dari itu. Fungsi ini melakukan beberapa kalkulasi pada x dan kemudian mengembalikan data.frame lain. Saya terjebak pada metode praktik terbaik untuk meneruskan nama kolom ke fungsi.
Dua contoh minimal fun1
dan di fun2
bawah ini menghasilkan hasil yang diinginkan, dapat melakukan operasi pada x$column
, menggunakan max()
sebagai contoh. Namun, keduanya mengandalkan yang tampaknya (setidaknya bagi saya) janggal
- panggilan ke
substitute()
dan mungkineval()
- kebutuhan untuk melewatkan nama kolom sebagai vektor karakter.
fun1 <- function(x, column){
do.call("max", list(substitute(x[a], list(a = column))))
}
fun2 <- function(x, column){
max(eval((substitute(x[a], list(a = column)))))
}
df <- data.frame(B = rnorm(10))
fun1(df, "B")
fun2(df, "B")
Saya ingin dapat memanggil fungsi tersebut sebagai fun(df, B)
, misalnya. Opsi lain yang telah saya pertimbangkan tetapi belum saya coba:
- Lulus
column
sebagai bilangan bulat dari nomor kolom. Saya pikir ini akan menghindarsubstitute()
. Idealnya, fungsinya bisa menerima keduanya. with(x, get(column))
, tetapi, bahkan jika berhasil, saya pikir ini masih akan dibutuhkansubstitute
- Memanfaatkan
formula()
danmatch.call()
, tidak ada yang saya punya banyak pengalaman dengannya.
Subquestion : Apakah do.call()
lebih disukai eval()
?
B
akan menganggap bahwa B adalah objek itu sendiri.[[
solusinya adalah satu-satunya yang berhasil untuk saya.Jawaban ini akan mencakup banyak elemen yang sama dengan jawaban yang sudah ada, tetapi masalah ini (meneruskan nama kolom ke fungsi) cukup sering muncul sehingga saya ingin ada jawaban yang mencakup hal-hal sedikit lebih komprehensif.
Misalkan kita memiliki kerangka data yang sangat sederhana:
dan kami ingin menulis fungsi yang membuat kolom baru
z
yang merupakan jumlah kolomx
dany
.Batu sandungan yang sangat umum di sini adalah bahwa upaya alami (tetapi tidak benar) sering kali terlihat seperti ini:
Masalahnya di sini adalah itu
df$col1
tidak mengevaluasi ekspresicol1
. Ini hanya mencari kolom yangdf
secara harfiah disebutcol1
. Perilaku ini dijelaskan di?Extract
bawah bagian "Objek rekursif (seperti daftar)".Solusi paling sederhana, dan paling sering direkomendasikan adalah dengan beralih dari
$
ke[[
dan meneruskan argumen fungsi sebagai string:Ini sering dianggap "praktik terbaik" karena ini adalah metode yang paling sulit untuk gagal. Meneruskan nama kolom sebagai string sama jelasnya dengan yang Anda bisa.
Dua opsi berikut ini lebih maju. Banyak paket populer yang menggunakan jenis teknik ini, tetapi menggunakannya dengan baik membutuhkan lebih banyak perhatian dan keterampilan, karena paket tersebut dapat menimbulkan kerumitan halus dan titik kegagalan yang tidak terduga. Ini bagian dari buku Lanjutan R Hadley adalah referensi yang sangat baik untuk beberapa masalah ini.
Jika Anda benar - benar ingin menyelamatkan pengguna agar tidak mengetik semua tanda kutip tersebut, salah satu opsi mungkin adalah mengonversi nama kolom yang kosong dan tidak bertanda kutip menjadi string menggunakan
deparse(substitute())
:Ini, sejujurnya, mungkin agak konyol, karena kami benar-benar melakukan hal yang sama seperti di
new_column1
, hanya dengan banyak pekerjaan tambahan untuk mengubah nama kosong menjadi string.Terakhir, jika kita ingin benar - benar mewah, kita mungkin memutuskan bahwa daripada memasukkan nama dua kolom untuk ditambahkan, kita ingin lebih fleksibel dan memungkinkan kombinasi lain dari dua variabel. Dalam hal ini kami kemungkinan akan menggunakan
eval()
ekspresi yang melibatkan dua kolom:Cuma iseng, saya masih pakai
deparse(substitute())
untuk nama kolom baru. Di sini, semua hal berikut akan berfungsi:Jadi jawaban singkatnya pada dasarnya adalah: berikan nama kolom data.frame sebagai string dan gunakan
[[
untuk memilih kolom tunggal. Hanya mulai menggalieval
,substitute
, dll jika Anda benar-benar tahu apa yang Anda lakukan.sumber
Secara pribadi saya berpikir bahwa melewatkan kolom sebagai string cukup jelek. Saya suka melakukan sesuatu seperti:
yang akan menghasilkan:
Perhatikan bagaimana spesifikasi data.frame bersifat opsional. Anda bahkan dapat bekerja dengan fungsi kolom Anda:
sumber
Cara lain adalah dengan menggunakan
tidy evaluation
pendekatan. Sangat mudah untuk melewatkan kolom dari bingkai data baik sebagai string atau nama kolom kosong. Lihat lebih lanjut ditidyeval
sini .Gunakan nama kolom sebagai string
Gunakan nama kolom kosong
Dibuat pada 01-03-2019 oleh paket reprex (v0.2.1.9000)
sumber
Sebagai pemikiran tambahan, jika diperlukan untuk meneruskan nama kolom tanpa tanda kutip ke fungsi kustom, mungkin
match.call()
dapat berguna juga dalam kasus ini, sebagai alternatif untukdeparse(substitute())
:Jika ada kesalahan ketik pada nama kolom, maka akan lebih aman untuk menghentikan kesalahan:
Dibuat pada 2019-01-11 oleh paket reprex (v0.2.1)
Saya tidak berpikir saya akan menggunakan pendekatan ini karena ada pengetikan dan kerumitan ekstra daripada hanya meneruskan nama kolom yang dikutip seperti yang ditunjukkan pada jawaban di atas, tetapi yah, ini adalah pendekatan.
sumber