Filter bingkai data berdasarkan nama kolom karakter (dalam dplyr)

Saya memiliki bingkai data dan ingin memfilternya dengan salah satu dari dua cara, berdasarkan kolom "ini" atau kolom "itu". Saya ingin dapat merujuk ke nama kolom sebagai variabel. Bagaimana (di dplyr, jika itu membuat perbedaan) cara saya merujuk ke nama kolom dengan variabel?

library(dplyr)
df <- data.frame(this = c(1, 2, 2), that = c(1, 1, 2))
df
#   this that
# 1    1    1
# 2    2    1
# 3    2    2
df %>% filter(this == 1)
#   this that
# 1    1    1

Namun katakanlah saya ingin menggunakan variabel column untuk menampung "ini" atau "itu", dan memfilter berapa pun nilai column. Baik as.symbol dan get berfungsi dalam konteks lain, tetapi tidak yang ini:

column <- "this"
df %>% filter(as.symbol(column) == 1)
# [1] this that
# <0 rows> (or 0-length row.names)
df %>% filter(get(column) == 1)
# Error in get("this") : object 'this' not found

Bagaimana cara mengubah nilai column menjadi nama kolom?


person William Denton    schedule 29.11.2014    source sumber
comment
Mengapa Anda ingin melakukan ini? Mencoba membuat parameter pilihan variabel?   -  person smci    schedule 30.11.2014
comment
Saya memiliki kumpulan data dengan dua jenis hal di dalamnya, dan kolom berbeda untuk masing-masingnya. Saya ingin melakukan pendekatan seperti ini karena saya menggunakan Shiny untuk membuat visualisasi interaktif dan saya ingin membiarkan orang memilih jenis hal yang akan dilihat, tetapi kemudian menggunakan kode yang sama untuk mengekstrak data, meneruskan kolom berdasarkan nama pada pilihan mereka.   -  person William Denton    schedule 30.11.2014
comment
Hampir memposting pertanyaan saya dan menemukan ini :)   -  person biocyberman    schedule 17.02.2016


Jawaban (7)


Dari file bantuan dplyr saat ini (penekanan oleh saya):

dplyr digunakan untuk menawarkan versi kembar dari setiap kata kerja yang diakhiri dengan garis bawah. Versi ini memiliki semantik evaluasi standar (SE): alih-alih mengambil argumen berdasarkan kode, seperti kata kerja NSE, versi ini mengambil argumen berdasarkan nilai. Tujuannya adalah untuk memungkinkan pemrograman dengan dplyr. Namun, dplyr kini menggunakan semantik evaluasi yang rapi. Kata kerja NSE masih menangkap argumennya, namun Anda kini dapat menghapus tanda kutip pada sebagian argumen tersebut. Ini menawarkan kemampuan program penuh dengan kata kerja NSE. Oleh karena itu, versi yang digarisbawahi sekarang tidak berguna.

Jadi pada dasarnya kita perlu melakukan dua hal, untuk dapat merujuk pada nilai "this" dari variabel column di dalam dplyr::filter():

  1. #P4# #P5#
    #P6#
    #P7#
  2. Kita perlu menghapus tanda kutip dari simbol 1).

    Arti sebenarnya dari unquoting dapat dipelajari dalam sketsa Pemrograman dengan dplyr. Hal ini dicapai dengan gula sintaksis !!.

    (Dalam versi dplyr sebelumnya (atau rlang yang mendasarinya) dulu ada situasi (termasuk milik Anda) di mana !! akan bertabrakan dengan ! tunggal, tetapi ini tidak menjadi masalah lagi sejak !! mendapatkan prioritas operator yang tepat.)

Diterapkan pada contoh Anda:

library(dplyr)
df <- data.frame(this = c(1, 2, 2),
                 that = c(1, 1, 2))
column <- "this"

df %>% filter(!!as.symbol(column) == 1)
#   this that
# 1    1    1
person Salim B    schedule 08.11.2017
comment
Untuk kasus terakhir Anda bisa menggunakan beberapa tanda kurung bulat tambahan df %>% filter((!!as.name(column)) == 1) - person Martijn vd Voort; 04.01.2018
comment
Bagus tapi 'UQ()' dan 'UQS()' tidak digunakan lagi di rlang 0.2.0 untuk membuat sintaksis quasiquotation lebih konsisten. - person ggll; 20.02.2020
comment
Terima kasih @ggll, saya telah memperbarui jawaban untuk perbaikan terkini di dplyr/rlang. - person Salim B; 20.02.2020

Saya akan menghindari penggunaan get() secara bersamaan. Sepertinya akan sangat berbahaya dalam situasi ini, terutama jika Anda sedang memprogram. Anda dapat menggunakan panggilan yang tidak dievaluasi atau string karakter yang ditempelkan, namun Anda harus menggunakan filter_(), bukan filter().

df <- data.frame(this = c(1, 2, 2), that = c(1, 1, 2))
column <- "this"

Opsi 1 - menggunakan panggilan yang tidak dievaluasi:

Anda dapat melakukan hard-code y sebagai 1, tetapi di sini saya menampilkannya sebagai y untuk mengilustrasikan bagaimana Anda dapat mengubah nilai ekspresi dengan mudah.

expr <- lazyeval::interp(quote(x == y), x = as.name(column), y = 1)
## or 
## expr <- substitute(x == y, list(x = as.name(column), y = 1))
df %>% filter_(expr)
#   this that
# 1    1    1

Opsi 2 - menggunakan paste() (dan jelas lebih mudah):

df %>% filter_(paste(column, "==", 1))
#   this that
# 1    1    1

Hal utama tentang kedua opsi ini adalah kita perlu menggunakan filter_() daripada filter(). Faktanya, dari apa yang saya baca, jika Anda memprogram dengan dplyr Anda harus selalu menggunakan fungsi *_().

Saya menggunakan posting ini sebagai referensi bermanfaat: string karakter sebagai argumen fungsi r, dan saya menggunakan dplyr versi 0.3.0.2.

person Rich Scriven    schedule 29.11.2014
comment
Opsi 1 adalah solusi tangguh seperti yang direkomendasikan oleh @hadley di beberapa postingan di SO dan di sketsa (nse). Namun, sepertinya itu bertentangan dengan gaya pemanggilan fungsi elegan Hadley. Tidak bisakah dikemas dan disajikan dengan cara yang lebih sederhana? - person biocyberman; 17.02.2016
comment
mengapa get() dianggap berbahaya? - person pluke; 18.05.2016
comment
Ini adalah jawaban yang bagus untuk versi dpylr yang lebih lama tetapi fungsi garis bawah sudah tidak digunakan lagi sehingga jawaban @ Salim-B di bawah ini lebih baik untuk kedepannya. - person Elin; 26.11.2017

Berikut solusi lain untuk versi dplyr terbaru:

df <- data.frame(this = c(1, 2, 2),
                 that = c(1, 1, 2))
column <- "this"

df %>% filter(.[[column]] == 1)

#  this that
#1    1    1
person paul_dg    schedule 17.05.2019

Mengenai solusi Richard, saya hanya ingin menambahkan jika kolom Anda adalah karakter. Anda dapat menambahkan shQuote untuk memfilter berdasarkan nilai karakter.

Misalnya, Anda bisa menggunakan

df %>% filter_(paste(column, "==", shQuote("a")))

Jika Anda memiliki beberapa filter, Anda dapat menentukan collapse = "&" di paste.

df %>$ filter_(paste(c("column1","column2"), "==", shQuote(c("a","b")), collapse = "&"))
person StatCC    schedule 10.08.2016

Cara terbaru untuk melakukan ini adalah dengan menggunakan my.data.frame %>% filter(.data[[myName]] == 1), dimana myName adalah variabel lingkungan yang berisi nama kolom.

person Phoenix Mu    schedule 21.09.2020

Atau menggunakan filter_at

library(dplyr)
df %>% 
   filter_at(vars(column), any_vars(. == 1))
person akrun    schedule 25.08.2019

Seperti yang dijelaskan Salim B di atas tetapi dengan sedikit perubahan:

df %>% filter(1 == !!as.name(column))

yaitu membalikkan kondisinya karena !! berperilaku seperti itu

!!(as.name(column)==1)
person carand    schedule 15.01.2018