`Dplyr` में गतिशील चर नामों का उपयोग करें


168

मैं dplyr::mutate()एक डेटा फ्रेम में कई नए कॉलम बनाने के लिए उपयोग करना चाहता हूं । स्तंभ के नाम और उनकी सामग्री गतिशील रूप से उत्पन्न की जानी चाहिए।

आइरिस से उदाहरण डेटा:

library(dplyr)
iris <- tbl_df(iris)

मैं एक समारोह बना लिया है से अपने नए कॉलम उत्परिवर्तित करने के लिए Petal.Widthचर:

multipetal <- function(df, n) {
    varname <- paste("petal", n , sep=".")
    df <- mutate(df, varname = Petal.Width * n)  ## problem arises here
    df
}

अब मैं अपने कॉलम बनाने के लिए एक लूप बनाता हूं:

for(i in 2:5) {
    iris <- multipetal(df=iris, n=i)
}

हालांकि, चूंकि म्यूटेट सोचता है कि varname एक शाब्दिक चर नाम है, लूप केवल चार के बजाय एक नया चर (varname कहा जाता है) बनाता है (जिसे petal.2 - petal.5 कहा जाता है)।

मैं mutate()अपने गतिशील नाम को चर नाम के रूप में कैसे उपयोग कर सकता हूं ?


1
मैं उत्परिवर्तन पर जोर नहीं दे रहा हूं, मैं पूछ रहा हूं कि क्या यह संभव है। शायद यह सिर्फ एक छोटी सी चाल है जो मुझे नहीं पता है। वहाँ एक और तरीका है, के यह सुनकर करते हैं।
टिम्म एस।

मैं वहाँ का मानना है कि घड़ी को एक अंतरिक्ष में lazyeval पैकेज
बैप्टिस्ट


16
विगनेट भी उल्लेख नहीं करता है mutate_, और यह वास्तव में अन्य कार्यों से स्पष्ट नहीं है कि इसका उपयोग कैसे किया जाए।
nacnudus

जवाबों:


191

जब से तुम गतिशील रूप से एक चरित्र मूल्य के रूप में एक चर नाम का निर्माण कर रहे हैं, यह मानक data.frame अनुक्रमण उसमें कॉलम के नाम के लिए चरित्र मूल्यों के लिए अनुमति देता है का उपयोग कर काम करने के लिए और अधिक समझ में आता है। उदाहरण के लिए:

multipetal <- function(df, n) {
    varname <- paste("petal", n , sep=".")
    df[[varname]] <- with(df, Petal.Width * n)
    df
}

mutateसमारोह यह बहुत आसान नाम वाले पैरामीटर के माध्यम से नए स्तंभ नाम के लिए बनाता है। लेकिन जब आप कमांड टाइप करते हैं तो आपको नाम पता होता है। यदि आप स्तंभ नाम को गतिशील रूप से निर्दिष्ट करना चाहते हैं, तो आपको नामित तर्क बनाने की भी आवश्यकता है।


dplyr संस्करण> = 0.7

dplyr(0.7) का नवीनतम संस्करण :=डायनामिक रूप से पैरामीटर नाम असाइन करने के लिए उपयोग करके करता है । आप अपने समारोह के रूप में लिख सकते हैं:

# --- dplyr version 0.7+---
multipetal <- function(df, n) {
    varname <- paste("petal", n , sep=".")
    mutate(df, !!varname := Petal.Width * n)
}

अधिक जानकारी के लिए, उपलब्ध प्रपत्र देखें vignette("programming", "dplyr")


dplyr (> = 0.3 और <0.7)

dplyr(> = 0.3 <0.7) के थोड़ा पहले के संस्करण ने कई कार्यों के लिए "मानक मूल्यांकन" विकल्पों के उपयोग को प्रोत्साहित किया। अधिक जानकारी के लिए गैर-मानक मूल्यांकन विगनेट देखें ( vignette("nse"))।

तो यहाँ, उत्तर के mutate_()बजाय उपयोग mutate()करना है और करना है:

# --- dplyr version 0.3-0.5---
multipetal <- function(df, n) {
    varname <- paste("petal", n , sep=".")
    varval <- lazyeval::interp(~Petal.Width * n, n=n)
    mutate_(df, .dots= setNames(list(varval), varname))
}

dplyr <0.3

ध्यान दें कि यह dplyrउस समय के पुराने संस्करणों में भी संभव है जब मूल रूप से प्रश्न उत्पन्न किया गया था। यह सावधान उपयोग की आवश्यकता है quoteऔर setName:

# --- dplyr versions < 0.3 ---
multipetal <- function(df, n) {
    varname <- paste("petal", n , sep=".")
    pp <- c(quote(df), setNames(list(quote(Petal.Width * n)), varname))
    do.call("mutate", pp)
}

24
धन्यवाद, यह मददगार है। btw, मैं हमेशा नाटकीय चर बनाता हूं।
टिम्म एस।

27
हेहे। यह शायद मेरे पसंदीदा टाइपोस में से एक है जिसे मैंने थोड़ी देर में बनाया है। मुझे लगता है कि मैं इसे छोड़ दूँगा।
MrFlick

1
do.call()शायद ऐसा नहीं है जो आपको लगता है कि यह करता है: rpubs.com/hadley/do-call2 । Dplyr के देव संस्करण में nse vignette भी देखें।
हैडली

4
इसलिए अगर मुझे आपकी बात समझ में आती है तो @hadley, मैंने do.callऊपर का उपयोग करने do.call("mutate")और dfसूची में उद्धृत करने के लिए अद्यतन किया है । क्या आप जो सुझाव दे रहे थे? और जब lazyevalसंस्करण dplyrजारी किया गया संस्करण है, तो mutate_(df, .dots= setNames(list(~Petal.Width * n), varname))एक बेहतर समाधान होगा?
MrFlick

1
क्या होगा अगर मुझे केवल असाइनमेंट के बाएं हाथ की तरफ ही नहीं बल्कि दाईं ओर भी वैरिएबल कॉलम हेडर की जरूरत है? जैसे mutate(df, !!newVar := (!!var1 + !!var2) / 2)काम नहीं करता है :(
मारियो रटर जूल

55

dplyr( 0.6.0अप्रैल 2017 में प्रतीक्षित) की नई रिलीज़ में , हम इसका मूल्यांकन नहीं करने के लिए एक असाइनमेंट ( :=) और कॉलम नामों के रूप में अंडरक्वाटिंग ( !!) द्वारा पास कर सकते हैं।

 library(dplyr)
 multipetalN <- function(df, n){
      varname <- paste0("petal.", n)
      df %>%
         mutate(!!varname := Petal.Width * n)
 }

 data(iris)
 iris1 <- tbl_df(iris)
 iris2 <- tbl_df(iris)
 for(i in 2:5) {
     iris2 <- multipetalN(df=iris2, n=i)
 }   

@ MrFlick के multipetal'iris1' पर लागू आउटपुट के आधार पर जाँच करना

identical(iris1, iris2)
#[1] TRUE

26

बहुत परीक्षण और त्रुटि के बाद, मुझे UQ(rlang::sym("some string here")))स्ट्रिंग्स और डुप्लिकेट क्रियाओं के साथ काम करने के लिए पैटर्न वास्तव में उपयोगी लगा। यह बहुत आश्चर्यजनक स्थितियों में काम करने लगता है।

यहाँ एक उदाहरण के साथ है mutate। हम एक फ़ंक्शन बनाना चाहते हैं जो दो कॉलम को एक साथ जोड़ता है, जहां आप फ़ंक्शन को स्ट्रिंग के रूप में दोनों कॉलम नामों से गुजरते हैं। हम ऐसा करने के लिए असाइनमेंट ऑपरेटर के साथ मिलकर इस पैटर्न का उपयोग कर सकते हैं :=

## Take column `name1`, add it to column `name2`, and call the result `new_name`
mutate_values <- function(new_name, name1, name2){
  mtcars %>% 
    mutate(UQ(rlang::sym(new_name)) :=  UQ(rlang::sym(name1)) +  UQ(rlang::sym(name2)))
}
mutate_values('test', 'mpg', 'cyl')

पैटर्न अन्य dplyrकार्यों के साथ भी काम करता है । यहाँ है filter:

## filter a column by a value 
filter_values <- function(name, value){
  mtcars %>% 
    filter(UQ(rlang::sym(name)) != value)
}
filter_values('gear', 4)

या arrange:

## transform a variable and then sort by it 
arrange_values <- function(name, transform){
  mtcars %>% 
    arrange(UQ(rlang::sym(name)) %>%  UQ(rlang::sym(transform)))
}
arrange_values('mpg', 'sin')

इसके लिए select, आपको पैटर्न का उपयोग करने की आवश्यकता नहीं है। इसके बजाय आप उपयोग कर सकते हैं !!:

## select a column 
select_name <- function(name){
  mtcars %>% 
    select(!!name)
}
select_name('mpg')

आपके सुझाव बहुत अच्छी तरह से काम करते हैं, लेकिन मेरे पास थोड़ा मुद्दा है। मैं प्रारंभिक कॉलम myColको url में बदल देता हूं (उदाहरण के लिए), और नए नाम के साथ myColInitialValueडेटाफ़्रेम के अंत में पुराने कॉलम को कॉपी करता हूं df। लेकिन which(colnames(df)=='myCol')# के कर्नल को वापस भेजें myColInitialValue। मैंने अभी तक कोई समस्या नहीं लिखी क्योंकि मुझे कोई रेप्रेक्स नहीं मिला। मेरा लक्ष्य escapeपैरामीटर के लिए है DT::datatable()। मैं escape=FALSEप्रतीक्षा में उपयोग करता हूं । स्थिरांक के साथ यह भी काम नहीं करता है, लेकिन DT पैकेज को भी खराब # कॉलम मिलता है। :)
phili_b


ऐसा लगता है कि गतिशील चर इसका कारण नहीं हैं। (btw reprex जोड़ा)
phili_b

इस उत्तर के लिए धन्यवाद! यहाँ एक सुपर-सरल उदाहरण है कि मैंने इसका उपयोग कैसे किया:varname = sym("Petal.Width"); ggplot(iris, aes(x=!!varname)) + geom_histogram()
bdemarest

यह मेरे लिए एक सूत्र के अंदर काम करता है जहाँ !! varname काम नहीं कर रहा था।
दक्खन

12

यहाँ एक और संस्करण है, और यह यकीनन थोड़ा सरल है।

multipetal <- function(df, n) {
    varname <- paste("petal", n, sep=".")
    df<-mutate_(df, .dots=setNames(paste0("Petal.Width*",n), varname))
    df
}

for(i in 2:5) {
    iris <- multipetal(df=iris, n=i)
}

> head(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species petal.2 petal.3 petal.4 petal.5
1          5.1         3.5          1.4         0.2  setosa     0.4     0.6     0.8       1
2          4.9         3.0          1.4         0.2  setosa     0.4     0.6     0.8       1
3          4.7         3.2          1.3         0.2  setosa     0.4     0.6     0.8       1
4          4.6         3.1          1.5         0.2  setosa     0.4     0.6     0.8       1
5          5.0         3.6          1.4         0.2  setosa     0.4     0.6     0.8       1
6          5.4         3.9          1.7         0.4  setosa     0.8     1.2     1.6       2

8

हमारे साथ rlang 0.4.0घुंघराले-घुंघराले ऑपरेटर ( {{}}) हैं जो इसे बहुत आसान बनाता है।

library(dplyr)
library(rlang)

iris1 <- tbl_df(iris)

multipetal <- function(df, n) {
   varname <- paste("petal", n , sep=".")
   mutate(df, {{varname}} := Petal.Width * n)
}

multipetal(iris1, 4)

# A tibble: 150 x 6
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species petal.4
#          <dbl>       <dbl>        <dbl>       <dbl> <fct>     <dbl>
# 1          5.1         3.5          1.4         0.2 setosa      0.8
# 2          4.9         3            1.4         0.2 setosa      0.8
# 3          4.7         3.2          1.3         0.2 setosa      0.8
# 4          4.6         3.1          1.5         0.2 setosa      0.8
# 5          5           3.6          1.4         0.2 setosa      0.8
# 6          5.4         3.9          1.7         0.4 setosa      1.6
# 7          4.6         3.4          1.4         0.3 setosa      1.2
# 8          5           3.4          1.5         0.2 setosa      0.8
# 9          4.4         2.9          1.4         0.2 setosa      0.8
#10          4.9         3.1          1.5         0.1 setosa      0.4
# … with 140 more rows

हम यह भी उद्धृत पारित कर सकते हैं / गैर उद्धृत चर नाम स्तंभ नाम के रूप में नियुक्त किया जाना है।

multipetal <- function(df, name, n) {
   mutate(df, {{name}} := Petal.Width * n)
}

multipetal(iris1, temp, 3)

# A tibble: 150 x 6
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species  temp
#          <dbl>       <dbl>        <dbl>       <dbl> <fct>   <dbl>
# 1          5.1         3.5          1.4         0.2 setosa  0.6  
# 2          4.9         3            1.4         0.2 setosa  0.6  
# 3          4.7         3.2          1.3         0.2 setosa  0.6  
# 4          4.6         3.1          1.5         0.2 setosa  0.6  
# 5          5           3.6          1.4         0.2 setosa  0.6  
# 6          5.4         3.9          1.7         0.4 setosa  1.2  
# 7          4.6         3.4          1.4         0.3 setosa  0.900
# 8          5           3.4          1.5         0.2 setosa  0.6  
# 9          4.4         2.9          1.4         0.2 setosa  0.6  
#10          4.9         3.1          1.5         0.1 setosa  0.3  
# … with 140 more rows

इसके साथ ही काम करता

multipetal(iris1, "temp", 3)

4

मैं एक उत्तर भी जोड़ रहा हूं जो इसे थोड़ा बढ़ाता है क्योंकि मैं इस प्रविष्टि में आया था जब एक उत्तर खोज रहा था, और यह लगभग वही था जिसकी मुझे आवश्यकता थी, लेकिन मुझे थोड़ी और आवश्यकता थी, जो मुझे @MrFlik के उत्तर और के माध्यम से मिला आर लाजयेवल विगनेट्स।

मैं एक ऐसा फंक्शन बनाना चाहता था जो डेटाफ्रेम और कॉलम नामों के वेक्टर (जैसा कि तार) मैं स्ट्रिंग से डेट ऑब्जेक्ट में परिवर्तित करना चाहता हूं। मैं यह पता नहीं लगा सका कि कैसे as.Date()एक तर्क है कि एक स्ट्रिंग है और इसे एक कॉलम में परिवर्तित करें, इसलिए मैंने इसे नीचे दिखाया गया है।

नीचे मैंने एसई म्यूटेट ( mutate_()) और .dotsतर्क के माध्यम से ऐसा किया है। इसको बेहतर बनाने वाली आलोचनाओं का स्वागत है।

library(dplyr)

dat <- data.frame(a="leave alone",
                  dt="2015-08-03 00:00:00",
                  dt2="2015-01-20 00:00:00")

# This function takes a dataframe and list of column names
# that have strings that need to be
# converted to dates in the data frame
convertSelectDates <- function(df, dtnames=character(0)) {
    for (col in dtnames) {
        varval <- sprintf("as.Date(%s)", col)
        df <- df %>% mutate_(.dots= setNames(list(varval), col))
    }
    return(df)
}

dat <- convertSelectDates(dat, c("dt", "dt2"))
dat %>% str

3

जब मैं इंटरैक्टिव उपयोग के लिए dplyr उपयोग का आनंद ले, मैं तुम्हें lazyeval :: interp (), setNames, आदि समाधान का उपयोग करने के हुप्स के माध्यम से जाने के लिए है, क्योंकि यह असाधारण मुश्किल यह dplyr का उपयोग कर पाते हैं।

यहाँ बेस आर का उपयोग करके एक सरल संस्करण है, जिसमें यह अधिक सहज लगता है, मुझे कम से कम, फ़ंक्शन के अंदर लूप लगाने के लिए, और जो @ MrFlicks के समाधान का विस्तार करता है।

multipetal <- function(df, n) {
   for (i in 1:n){
      varname <- paste("petal", i , sep=".")
      df[[varname]] <- with(df, Petal.Width * i)
   }
   df
}
multipetal(iris, 3) 

2
+1, हालांकि मैं अभी भी dplyrगैर-संवादात्मक सेटिंग्स में बहुत अधिक उपयोग करता हूं , इसे फ़ंक्शन के अंदर वेरबेल इनपुट के साथ उपयोग करते हुए बहुत क्लूनी सिंटैक्स का उपयोग करता है।
पॉल हैमस्ट्रा

3

आप ऐसे पैकेज का आनंद ले सकते हैं, friendlyevalजो नए / आकस्मिक dplyrउपयोगकर्ताओं के लिए एक सरल साफ-साफ एपीआई और प्रलेखन प्रस्तुत करते हैं ।

आप ऐसे तार बना रहे हैं जिन्हें आप mutateस्तंभ नामों के रूप में मान सकते हैं। इसलिए friendlyevalआप लिख सकते हैं:

multipetal <- function(df, n) {
  varname <- paste("petal", n , sep=".")
  df <- mutate(df, !!treat_string_as_col(varname) := Petal.Width * n)
  df
}

for(i in 2:5) {
  iris <- multipetal(df=iris, n=i)
}

जो हुड कॉल rlangफ़ंक्शंस के तहत जाँच करता है कि varnameकॉलम नाम के रूप में कानूनी है।

friendlyeval कोड को किसी RStudio एडिन के साथ किसी भी समय समतुल्य सादे स्पष्ट कोड में बदला जा सकता है।


0

एक अन्य विकल्प: {}आसानी से गतिशील नाम बनाने के लिए उद्धरण चिह्नों के अंदर का उपयोग करें । यह अन्य समाधानों के समान है लेकिन बिल्कुल समान नहीं है, और मुझे यह आसान लगता है।

library(dplyr)
library(tibble)

iris <- as_tibble(iris)

multipetal <- function(df, n) {
  df <- mutate(df, "petal.{n}" := Petal.Width * n)  ## problem arises here
  df
}

for(i in 2:5) {
  iris <- multipetal(df=iris, n=i)
}
iris

मुझे लगता है कि यह आता है dplyr 1.0.0लेकिन निश्चित नहीं है (मेरे पास भी rlang 4.7.0अगर यह मायने रखता है)।

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