กรองกรอบข้อมูลตามชื่อคอลัมน์อักขระ (เป็น dplyr)

ฉันมีกรอบข้อมูลและต้องการกรองด้วยวิธีใดวิธีหนึ่งจากสองวิธี โดยคอลัมน์ "นี้" หรือคอลัมน์ "นั่น" ฉันต้องการที่จะอ้างถึงชื่อคอลัมน์เป็นตัวแปร ฉันจะอ้างถึงชื่อคอลัมน์ด้วยตัวแปรได้อย่างไร (ใน dplyr หากสร้างความแตกต่าง)

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

แต่สมมติว่าฉันต้องการใช้ตัวแปร column เพื่อเก็บ "สิ่งนี้" หรือ "นั่น" และกรองค่าใดก็ตามของ column ทั้ง as.symbol และ get ทำงานในบริบทอื่น แต่ไม่ใช่สิ่งนี้:

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

ฉันจะเปลี่ยนค่า column เป็นชื่อคอลัมน์ได้อย่างไร


person William Denton    schedule 29.11.2014    source แหล่งที่มา
comment
ทำไมคุณถึงต้องการทำเช่นนี้? กำลังพยายามกำหนดพารามิเตอร์ตัวเลือกของตัวแปรอยู่ใช่ไหม   -  person smci    schedule 30.11.2014
comment
ฉันมีชุดข้อมูลที่ประกอบด้วยข้อมูล 2 ประเภท และมีคอลัมน์ที่แตกต่างกันสำหรับแต่ละรายการ ฉันต้องการเข้าถึงสิ่งต่าง ๆ ด้วยวิธีนี้เพราะฉันใช้ Shiny เพื่อสร้างการแสดงภาพเชิงโต้ตอบ และฉันต้องการให้ผู้คนเลือกประเภทของสิ่งที่จะดู แต่จากนั้นใช้โค้ดเดียวกันเพื่อแยกข้อมูล โดยส่งผ่านชื่อคอลัมน์ตาม ตามทางเลือกของพวกเขา   -  person William Denton    schedule 30.11.2014
comment
เกือบโพสต์คำถามของฉันแล้วพบสิ่งนี้ :)   -  person biocyberman    schedule 17.02.2016


คำตอบ (7)


จากไฟล์ช่วยเหลือ dplyr ปัจจุบัน (เน้นโดยฉัน):

dplyr ใช้เพื่อเสนอคำกริยาแต่ละคำต่อท้ายด้วยเครื่องหมายขีดล่างในรูปแบบคู่ เวอร์ชันเหล่านี้มีซีแมนทิกส์การประเมินมาตรฐาน (SE) แทนที่จะรับอาร์กิวเมนต์ด้วยโค้ด เช่น กริยา NSE แต่กลับรับอาร์กิวเมนต์ตามค่า จุดประสงค์ของพวกเขาคือเพื่อให้สามารถโปรแกรมด้วย dplyr ได้ อย่างไรก็ตาม dplyr ตอนนี้ใช้ซีแมนทิกส์การประเมินที่เป็นระเบียบเรียบร้อย คำกริยา NSE ยังคงจับข้อโต้แย้งได้ แต่ ขณะนี้คุณสามารถยกเลิกการใส่เครื่องหมายคำพูดบางส่วนของข้อโต้แย้งเหล่านี้ได้ ซึ่งนำเสนอความสามารถในการโปรแกรมเต็มรูปแบบด้วยคำกริยา NSE ดังนั้น เวอร์ชันที่ขีดเส้นใต้จึงไม่จำเป็นอีกต่อไป

โดยพื้นฐานแล้วเราจำเป็นต้องทำสองสิ่ง เพื่อให้สามารถอ้างอิงค่า "this" ของตัวแปร column ภายใน dplyr::filter():

  1. #P4# #P5#
    #P6#
    #P7#
  2. เราจำเป็นต้องยกเลิกเครื่องหมายคำพูดจาก 1)

    unquoting หมายถึงอะไรกันแน่สามารถเรียนรู้ได้จากบทความสั้น Programming ด้วย dplyr สามารถทำได้โดย น้ำตาลเชิงวากยสัมพันธ์ !!

    (ในเวอร์ชันก่อนหน้าของ dplyr (หรือ rlang ที่สำคัญตามลำดับ) เคยมีสถานการณ์ (รวมถึงของคุณด้วย) ที่ !! จะขัดแย้งกับ ! เดี่ยว แต่นี่ไม่ใช่ปัญหาอีกต่อไป เนื่องจาก !! ได้รับลำดับความสำคัญของตัวดำเนินการที่ถูกต้อง.)

นำไปใช้กับตัวอย่างของคุณ:

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
สำหรับกรณีหลัง คุณสามารถใช้วงเล็บเหลี่ยมเพิ่มเติมได้ df %>% filter((!!as.name(column)) == 1) - person Martijn vd Voort; 04.01.2018
comment
เยี่ยมยอด แต่ 'UQ()' และ 'UQS()' ถูกเลิกใช้งานแบบ soft-deprecated ใน rlang 0.2.0 เพื่อให้ไวยากรณ์ของ quasiquotation สอดคล้องกันมากขึ้น - person ggll; 20.02.2020
comment
ขอบคุณ @ggll ฉันได้อัปเดตคำตอบเพื่อบัญชีสำหรับการปรับปรุงล่าสุดใน dplyr/rlang - person Salim B; 20.02.2020

ฉันจะหลีกเลี่ยงการใช้ get() ทั้งหมดร่วมกัน ดูเหมือนว่าจะค่อนข้างอันตรายในสถานการณ์นี้ โดยเฉพาะอย่างยิ่งหากคุณกำลังเขียนโปรแกรม คุณสามารถใช้การเรียกที่ไม่ได้รับการประเมินหรือสตริงอักขระที่วางก็ได้ แต่คุณจะต้องใช้ filter_() แทน filter()

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

ตัวเลือกที่ 1 - การใช้การโทรที่ไม่ได้รับการประเมิน:

คุณสามารถฮาร์ดโค้ด y เป็น 1 ได้ แต่ที่นี่ฉันจะแสดงเป็น y เพื่อแสดงให้เห็นว่าคุณสามารถเปลี่ยนค่านิพจน์ได้อย่างง่ายดายได้อย่างไร

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

ตัวเลือก 2 - ใช้ paste() (และง่ายกว่าอย่างเห็นได้ชัด):

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

สิ่งสำคัญเกี่ยวกับสองตัวเลือกนี้คือเราจำเป็นต้องใช้ filter_() แทน filter() ที่จริงแล้ว จากสิ่งที่ฉันได้อ่าน หากคุณเขียนโปรแกรมด้วย dplyr คุณควรใช้ฟังก์ชัน *_() เสมอ

ฉันใช้โพสต์นี้เป็นข้อมูลอ้างอิงที่เป็นประโยชน์: สตริงอักขระเป็นอาร์กิวเมนต์ของฟังก์ชัน r, และฉันใช้ dplyr เวอร์ชัน 0.3.0.2

person Rich Scriven    schedule 29.11.2014
comment
ตัวเลือกที่ 1 เป็นโซลูชันที่มีประสิทธิภาพตามที่แนะนำโดย @hadley ในหลายโพสต์เกี่ยวกับ SO และในบทความสั้น (nse) อย่างไรก็ตาม ดูเหมือนว่าจะขัดแย้งกับสไตล์การเรียกใช้ฟังก์ชันที่หรูหราของ Hadley ไม่สามารถห่อและนำเสนอด้วยวิธีที่ง่ายกว่านี้ได้หรือ? - person biocyberman; 17.02.2016
comment
เหตุใด get() จึงถือว่าเป็นอันตราย - person pluke; 18.05.2016
comment
นี่เป็นคำตอบที่ดีสำหรับ dpylr เวอร์ชันเก่า แต่ฟังก์ชันขีดล่างเลิกใช้แล้ว ดังนั้นคำตอบของ @ Salim-B ด้านล่างจะดีกว่าในอนาคต - person Elin; 26.11.2017

นี่เป็นอีกวิธีหนึ่งสำหรับ dplyr เวอร์ชันล่าสุด:

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

เกี่ยวกับวิธีแก้ปัญหาของ Richard เพียงต้องการเพิ่มว่าหากคุณคอลัมน์นั้นเป็นตัวละคร คุณสามารถเพิ่ม shQuote เพื่อกรองตามค่าอักขระได้

ตัวอย่างเช่นคุณสามารถใช้

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

หากคุณมีตัวกรองหลายตัว คุณสามารถระบุ collapse = "&" ใน paste ได้

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

วิธีล่าสุดในการดำเนินการนี้คือการใช้ my.data.frame %>% filter(.data[[myName]] == 1) โดยที่ myName คือตัวแปรสภาพแวดล้อมที่มีชื่อคอลัมน์

person Phoenix Mu    schedule 21.09.2020

หรือใช้ filter_at

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

เช่นเดียวกับ Salim B ที่อธิบายไว้ข้างต้น แต่มีการเปลี่ยนแปลงเล็กน้อย:

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

กล่าวคือ เพียงกลับเงื่อนไขเพราะ !! มีพฤติกรรมเช่นนั้น

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