किसी फ़ंक्शन के लिए data.frame स्तंभ नाम पास करें


119

मैं एक data.frame ( x) और columnइसे स्वीकार करने के लिए एक फ़ंक्शन लिखने की कोशिश कर रहा हूं । फ़ंक्शन x पर कुछ गणना करता है और बाद में एक और data.frame देता है। मैं फ़ंक्शन के स्तंभ नाम को पास करने के लिए सर्वोत्तम-अभ्यास विधि पर अटका हुआ हूं।

दो न्यूनतम उदाहरण fun1और fun2नीचे वांछित परिणाम का उत्पादन करते हैं x$column, max()उदाहरण के रूप में उपयोग करके संचालन करने में सक्षम होते हैं । हालांकि, दोनों प्रतीत होता है पर निर्भर हैं (कम से कम मेरे लिए) अपात्र

  1. substitute()और संभवतः कॉल करेंeval()
  2. एक चरित्र वेक्टर के रूप में कॉलम नाम को पारित करने की आवश्यकता है।

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")

मैं fun(df, B)उदाहरण के लिए, फ़ंक्शन को कॉल करने में सक्षम होना चाहूंगा । अन्य विकल्पों पर मैंने विचार किया है लेकिन कोशिश नहीं की है:

  • columnकॉलम संख्या के पूर्णांक के रूप में पास करें। मुझे लगता है कि इससे बचना होगा substitute()। आदर्श रूप से, फ़ंक्शन या तो स्वीकार कर सकता है।
  • with(x, get(column)), लेकिन, भले ही यह काम करता है, मुझे लगता है कि यह अभी भी आवश्यकता होगी substitute
  • का उपयोग करें, formula()और match.call()न ही जिनमें से मुझे बहुत अनुभव है।

निर्विवाद : को do.call()प्राथमिकता दी जाती है eval()?

जवाबों:


108

आप सीधे कॉलम नाम का उपयोग कर सकते हैं:

df <- data.frame(A=1:10, B=2:11, C=3:12)
fun1 <- function(x, column){
  max(x[,column])
}
fun1(df, "B")
fun1(df, c("B","A"))

स्थानापन्न, eval, आदि का उपयोग करने की कोई आवश्यकता नहीं है।

तुम भी एक पैरामीटर के रूप में वांछित कार्य पारित कर सकते हैं:

fun1 <- function(x, column, fn) {
  fn(x[,column])
}
fun1(df, "B", max)

वैकल्पिक रूप से, [[एक समय में एक एकल कॉलम का चयन करने के लिए भी काम करता है:

df <- data.frame(A=1:10, B=2:11, C=3:12)
fun1 <- function(x, column){
  max(x[[column]])
}
fun1(df, "B")

14
क्या स्तंभ नाम को स्ट्रिंग के रूप में पारित करने का कोई तरीका है?
किमी

2
आपको कॉलम के लिए वर्ण या पूर्णांक अनुक्रमणिका के रूप में उद्धृत कॉलम नाम को पास करना होगा। बस पास होने से Bयह मान लिया जाएगा कि बी एक ऑब्जेक्ट ही है।
शेन

समझा। मुझे यकीन नहीं है कि मैं कैसे
घटिया

3
धन्यवाद! मैंने पाया कि [[समाधान केवल वही था जो मेरे लिए काम करता था।
इकोलॉजीटोम


78

यह उत्तर मौजूदा उत्तर के रूप में समान तत्वों में से कई को कवर करेगा, लेकिन यह मुद्दा (कार्यों के लिए स्तंभ नाम पारित करना) अक्सर पर्याप्त होता है कि मैं चाहता था कि एक ऐसा उत्तर हो जो चीजों को थोड़ा अधिक व्यापक रूप से कवर करता हो।

मान लें कि हमारे पास एक बहुत ही सरल डेटा फ़्रेम है:

dat <- data.frame(x = 1:4,
                  y = 5:8)

और हम एक फ़ंक्शन लिखना चाहते हैं जो एक नया कॉलम बनाता zहै जो कॉलम xऔर का योग है y

यहां एक बहुत ही सामान्य ठोकर है कि एक प्राकृतिक (लेकिन गलत) प्रयास अक्सर ऐसा दिखता है:

foo <- function(df,col_name,col1,col2){
      df$col_name <- df$col1 + df$col2
      df
}

#Call foo() like this:    
foo(dat,z,x,y)

यहाँ समस्या यह है कि df$col1अभिव्यक्ति का मूल्यांकन नहीं करता है col1। यह केवल dfशब्दशः कहे जाने वाले कॉलम के लिए दिखता है col1। यह व्यवहार ?Extract"पुनरावर्ती (सूची-जैसा) ऑब्जेक्ट्स" अनुभाग के तहत वर्णित है ।

सरल, और सबसे अधिक बार सुझाए गए समाधान के बस से स्विच कर रहा है $करने के लिए [[तारों के रूप में और समारोह तर्क पारित:

new_column1 <- function(df,col_name,col1,col2){
    #Create new column col_name as sum of col1 and col2
    df[[col_name]] <- df[[col1]] + df[[col2]]
    df
}

> new_column1(dat,"z","x","y")
  x y  z
1 1 5  6
2 2 6  8
3 3 7 10
4 4 8 12

इसे अक्सर "सर्वश्रेष्ठ अभ्यास" माना जाता है क्योंकि यह एक ऐसी विधि है जिसे पेंच करना सबसे कठिन है। स्तंभ नामों को तार के रूप में पारित करना लगभग उतना ही अस्पष्ट है जितना आप प्राप्त कर सकते हैं।

निम्नलिखित दो विकल्प अधिक उन्नत हैं। कई लोकप्रिय पैकेज इस प्रकार की तकनीकों का उपयोग करते हैं, लेकिन उन्हें अच्छी तरह से उपयोग करने के लिए अधिक देखभाल और कौशल की आवश्यकता होती है, क्योंकि वे सूक्ष्म जटिलताओं और असफलता के अप्रत्याशित बिंदुओं को पेश कर सकते हैं। हेडली की एडवांस्ड आर बुक का यह खंड इन मुद्दों में से कुछ के लिए एक उत्कृष्ट संदर्भ है।

यदि आप वास्तव में उपयोगकर्ता को उन सभी उद्धरणों को टाइप करने से बचाना चाहते हैं, तो एक विकल्प नंगे, बिना कॉलम वाले नामों को स्ट्रिंग में बदलना हो सकता है deparse(substitute()):

new_column2 <- function(df,col_name,col1,col2){
    col_name <- deparse(substitute(col_name))
    col1 <- deparse(substitute(col1))
    col2 <- deparse(substitute(col2))

    df[[col_name]] <- df[[col1]] + df[[col2]]
    df
}

> new_column2(dat,z,x,y)
  x y  z
1 1 5  6
2 2 6  8
3 3 7 10
4 4 8 12

यह, स्पष्ट रूप से, शायद थोड़ा मूर्खतापूर्ण है, क्योंकि हम वास्तव में एक ही काम कर रहे हैं new_column1, अतिरिक्त कार्यों के एक गुच्छा के साथ नंगे नामों को तार में बदलने के लिए।

अंत में, यदि हम वास्तव में कल्पना करना चाहते हैं, तो हम यह तय कर सकते हैं कि जोड़ने के लिए दो कॉलम के नामों को पारित करने के बजाय, हम और अधिक लचीला होना चाहते हैं और दो चर के अन्य संयोजनों के लिए अनुमति देते हैं। उस स्थिति में हम संभवतः eval()दो स्तंभों को शामिल करते हुए अभिव्यक्ति का उपयोग कर सकते हैं:

new_column3 <- function(df,col_name,expr){
    col_name <- deparse(substitute(col_name))
    df[[col_name]] <- eval(substitute(expr),df,parent.frame())
    df
}

बस मनोरंजन के लिए, मैं अभी भी deparse(substitute())नए कॉलम के नाम का उपयोग कर रहा हूं । यहाँ, निम्नलिखित सभी काम करेंगे:

> new_column3(dat,z,x+y)
  x y  z
1 1 5  6
2 2 6  8
3 3 7 10
4 4 8 12
> new_column3(dat,z,x-y)
  x y  z
1 1 5 -4
2 2 6 -4
3 3 7 -4
4 4 8 -4
> new_column3(dat,z,x*y)
  x y  z
1 1 5  5
2 2 6 12
3 3 7 21
4 4 8 32

तो संक्षिप्त उत्तर मूल रूप से है: स्ट्रिंग के रूप में data.frame स्तंभ नामों को पास [[करें और एकल कॉलम का चयन करने के लिए उपयोग करें। केवल में जाने पर शुरू eval, substituteआदि यदि आप वास्तव में पता है कि तुम क्या कर रहे हैं।


1
निश्चित नहीं है कि यह चयनित सर्वश्रेष्ठ उत्तर क्यों नहीं है।
इयान

मैं भी! महान व्याख्या!
अल्फ्रेडो जी मार्केज़

22

व्यक्तिगत रूप से मुझे लगता है कि स्तंभ को एक स्ट्रिंग के रूप में पारित करना बहुत बदसूरत है। मुझे कुछ करना पसंद है:

get.max <- function(column,data=NULL){
    column<-eval(substitute(column),data, parent.frame())
    max(column)
}

जो उपज देगा:

> get.max(mpg,mtcars)
[1] 33.9
> get.max(c(1,2,3,4,5))
[1] 5

ध्यान दें कि data.frame का विनिर्देश वैकल्पिक कैसे है। आप अपने कॉलम के कार्यों के साथ भी काम कर सकते हैं:

> get.max(1/mpg,mtcars)
[1] 0.09615385

9
आपको उद्धरण चिह्नों का उपयोग करके सोचने की आदत से बाहर निकलने की आवश्यकता है। उनका उपयोग न करना बदसूरत है! क्यों? क्योंकि आपने एक ऐसा फंक्शन बनाया है जिसे केवल अंतःक्रियात्मक रूप से उपयोग किया जा सकता है - इसके साथ प्रोग्राम करना बहुत मुश्किल है।
13

27
मुझे एक बेहतर तरीका दिखाया जा रहा है, लेकिन मैं इस और qplot (x = mpg, डेटा = mtcars) के बीच का अंतर देखने में विफल रहा। ggplot2 एक स्तंभ को एक स्ट्रिंग के रूप में कभी नहीं पारित करता है, और मुझे लगता है कि यह इसके लिए बेहतर है। आप यह क्यों कहते हैं कि यह केवल अंतःक्रियात्मक रूप से उपयोग किया जा सकता है? किस परिस्थिति में यह अवांछनीय परिणाम देगा? इसके साथ प्रोग्राम करना कितना कठिन है? पोस्ट के शरीर में मैं प्रदर्शित करता हूं कि यह अधिक लचीला कैसे है।
इयान फेलो

4
5 साल बाद -) .. हमें इसकी आवश्यकता क्यों है: parent.frame ()?
mql4beginner

15
7 साल बाद: अभी भी बदसूरत उद्धरण का उपयोग नहीं कर रहा है?
Spacedman

11

एक अन्य तरीका tidy evaluationदृष्टिकोण का उपयोग करना है। किसी डेटा फ़्रेम के स्तंभों को स्ट्रिंग्स या नंगे स्तंभ नामों के रूप में पास करना बहुत सरल है। tidyeval यहाँ और अधिक देखें ।

library(rlang)
library(tidyverse)

set.seed(123)
df <- data.frame(B = rnorm(10), D = rnorm(10))

स्तंभ नाम का उपयोग स्ट्रिंग्स के रूप में करें

fun3 <- function(x, ...) {
  # capture strings and create variables
  dots <- ensyms(...)
  # unquote to evaluate inside dplyr verbs
  summarise_at(x, vars(!!!dots), list(~ max(., na.rm = TRUE)))
}

fun3(df, "B")
#>          B
#> 1 1.715065

fun3(df, "B", "D")
#>          B        D
#> 1 1.715065 1.786913

नंगे कॉलम नामों का उपयोग करें

fun4 <- function(x, ...) {
  # capture expressions and create quosures
  dots <- enquos(...)
  # unquote to evaluate inside dplyr verbs
  summarise_at(x, vars(!!!dots), list(~ max(., na.rm = TRUE)))
}

fun4(df, B)
#>          B
#> 1 1.715065

fun4(df, B, D)
#>          B        D
#> 1 1.715065 1.786913
#>

2019-03-01 को रेप्रेक्स पैकेज (v0.2.1.9000) द्वारा बनाया गया



1

एक अतिरिक्त विचार के रूप में, यदि कस्टम फ़ंक्शन के लिए अयोग्य कॉलम नाम को पास करना आवश्यक है, तो शायद match.call()इस मामले में उपयोगी हो सकता है deparse(substitute()): विकल्प के रूप में :

df <- data.frame(A = 1:10, B = 2:11)

fun <- function(x, column){
  arg <- match.call()
  max(x[[arg$column]])
}

fun(df, A)
#> [1] 10

fun(df, B)
#> [1] 11

यदि कॉलम नाम में कोई टाइपो है, तो एक त्रुटि के साथ बंद करना सुरक्षित होगा:

fun <- function(x, column) max(x[[match.call()$column]])
fun(df, typo)
#> Warning in max(x[[match.call()$column]]): no non-missing arguments to max;
#> returning -Inf
#> [1] -Inf

# Stop with error in case of typo
fun <- function(x, column){
  arg <- match.call()
  if (is.null(x[[arg$column]])) stop("Wrong column name")
  max(x[[arg$column]])
}

fun(df, typo)
#> Error in fun(df, typo): Wrong column name
fun(df, A)
#> [1] 10

2019-01-11 को रेप्रेक्स पैकेज (v0.2.1) द्वारा बनाया गया

मुझे नहीं लगता कि मैं इस दृष्टिकोण का उपयोग करूंगा क्योंकि ऊपर दिए गए उत्तरों में बताए अनुसार उद्धृत कॉलम नाम को पारित करने की तुलना में अतिरिक्त टाइपिंग और जटिलता है, लेकिन अच्छी तरह से, एक दृष्टिकोण है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.