"Dplyr" de dinamik değişken adları kullanın


168

dplyr::mutate()Bir veri çerçevesinde birden çok yeni sütun oluşturmak için kullanmak istiyorum . Sütun adları ve içerikleri dinamik olarak oluşturulmalıdır.

İris'den örnek veriler:

library(dplyr)
iris <- tbl_df(iris)

Petal.WidthDeğişkenden yeni sütunlarımı değiştirmek için bir işlev oluşturdum :

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

Şimdi sütunlarımı oluşturmak için bir döngü oluşturuyorum:

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

Bununla birlikte, mutate varname değişken değişmez bir isim olduğunu düşündüğü için, döngü dört yerine (petal.2 - petal.5 olarak adlandırılır) yalnızca bir yeni değişken (varname denir) oluşturur.

mutate()Dinamik adımı değişken adı olarak nasıl kullanabilirim ?


1
Mutasyon konusunda ısrar etmiyorum, bunun mümkün olup olmadığını soruyorum. Belki de bilmediğim küçük bir numara. Başka bir yol varsa, duyalım.
Timm



16
Skeçten bile bahsetmiyor mutate_ve diğer işlevlerden nasıl kullanılacağı gerçekten belli değil.
nacnudus

Yanıtlar:


191

Değişken adını karakter değeri olarak dinamik olarak oluşturduğunuzdan, sütun adları için karakter değerlerine izin veren standart data.frame dizinini kullanarak atama yapmak daha mantıklıdır. Örneğin:

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

mutateFonksiyonu çok kolay adlandırılmış parametreleri ile yeni sütunlar isim yapar. Ancak bu, komutu yazarken adı bildiğinizi varsayar. Sütun adını dinamik olarak belirtmek istiyorsanız, adlandırılmış bağımsız değişkeni de oluşturmanız gerekir.


dplyr sürümü> = 0.7

dplyr(0.7) ' nin en son sürümü, :=parametre adlarını dinamik olarak atamak için bunu kullanarak yapar . İşlevinizi şu şekilde yazabilirsiniz:

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

Daha fazla bilgi için mevcut belgelere bakın vignette("programming", "dplyr").


dplyr (> = 0,3 ve <0,7)

Biraz önceki sürümü dplyr(> = 0.3 <0.7), birçok fonksiyon için "standart değerlendirme" alternatiflerinin kullanılmasını teşvik etmiştir. Daha fazla bilgi için Standart olmayan değerlendirme skeçine bakın ( vignette("nse")).

Yani, cevap, kullanmak mutate_()yerine kullanmaktır 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

Bunun, dplyrsorunun orijinal olarak sorulduğunda var olan eski sürümlerinde de mümkün olduğunu unutmayın . Dikkatli kullanılmasını gerektirir quoteve 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
Teşekkürler, bu yardımcı oldu. btw, her zaman gerçekten dramatik değişkenler yaratırım.
Timm S.

27
Hehe. muhtemelen bir süredir yaptığım en sevdiğim yazım hatalarından biri. Sanırım bırakacağım.
MrFlick

1
do.call()muhtemelen düşündüğünü yapmaz: rpubs.com/hadley/do-call2 . Ayrıca dplyr'ın dev sürümündeki nse skeçine bakın.
hadley

4
Eğer @hadley noktanızı anlarsam, listeyi do.callkullanmak do.call("mutate")ve alıntı yapmak için yukarıdakileri güncelledim df. Önerdiğin bu mu? Ve lazyevalsürümü sürümü dplyryayınlandığında, o mutate_(df, .dots= setNames(list(~Petal.Width * n), varname))zaman daha iyi bir çözüm olur mu?
MrFlick

1
Değişken sütun başlığına yalnızca ödevin sol tarafında değil, aynı zamanda sağda da ihtiyacım olursa ne olur? örneğin mutate(df, !!newVar := (!!var1 + !!var2) / 2)çalışmıyor :(
Mario Reutter

55

Yeni sürümde dplyr( 0.6.0Nisan 2017'de bekleniyor), bir atama ( :=) da yapabilir ve değişkenleri !!değerlendirmek için unquoting ( ) yöntemini kullanarak sütun adları olarak geçirebiliriz

 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' üzerine uygulanan çıktıya göre kontrol

identical(iris1, iris2)
#[1] TRUE

26

Birçok deneme yanılma işleminden sonra UQ(rlang::sym("some string here"))), dizgiler ve dplyr fiillerle çalışmak için deseni gerçekten yararlı buldum . Birçok şaşırtıcı durumda çalışıyor gibi görünüyor.

İşte ile bir örnek mutate. Her iki sütun adını dize olarak ilettiğiniz iki sütunu bir araya getiren bir işlev oluşturmak istiyoruz. Bunu yapmak için atama operatörü ile birlikte bu kalıbı kullanabiliriz :=.

## 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')

Desen diğer dplyrişlevlerle de çalışır . İşte filter:

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

Veya 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')

Çünkü select, deseni kullanmanıza gerek yoktur. Bunun yerine şunları kullanabilirsiniz !!:

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

İpuçlarınız çok iyi çalışıyor, ancak küçük bir sorunum var. Bir başlangıç ​​sütununu myColbir url olarak değiştiririm (örneğin) ve veri myColInitialValueçerçevesinin sonundaki eski sütunu dfyeni bir adla kopyalarım . Ama bir which(colnames(df)=='myCol')# geri gönder myColInitialValue. Henüz bir sorun yazmadım çünkü bir reprex bulamadım. Amacım escapeparametresi için DT::datatable(). Bunu escape=FALSEbeklerken kullanıyorum. Sabitler ile de çalışmaz, ancak DT paketi de kötü # sütununu alıyor gibi görünüyor. :)
phili_b


Dinamik değişkenlerin nedeni olmadığı anlaşılıyor. (btw reprex eklendi)
phili_b

Bu cevap için teşekkürler! İşte nasıl kullandığımın çok basit bir örneği:varname = sym("Petal.Width"); ggplot(iris, aes(x=!!varname)) + geom_histogram()
bdemarest

Bu benim için !! varname çalışmayan bir formül içinde çalıştı.
daknowles

12

İşte başka bir sürüm ve tartışmasız biraz daha basit.

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

İle rlang 0.4.0biz kıvırcık kıvırcık operatörler (sahip {{}}) çok kolay bu hale getiren.

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

Ayrıca, sütun adları olarak atanacak alıntılanmış / sıralanmamış değişken adlarını da iletebiliriz.

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

İle aynı şekilde çalışır

multipetal(iris1, "temp", 3)

4

Ben de bu cevabı biraz artıran bir cevap ekliyorum çünkü bir cevap ararken bu girişe geldim ve bu neredeyse ihtiyacım olan şeydi, ama biraz daha ihtiyacım vardı, @MrFlik'in cevabı ve R tembel vinyetlerin.

Bir dataframe ve bir sütun adları (dizeler olarak) bir dizeden bir Date nesnesine dönüştürülmesini istediğiniz bir vektör alabilir bir işlev yapmak istedim. Ben as.Date()bir dize olan bir argüman almak ve bir sütuna dönüştürmek nasıl anlayamadık , bu yüzden aşağıda gösterildiği gibi yaptım.

Aşağıda SE mutate ( mutate_()) ve .dotsargüman ile bunu nasıl yaptım . Bunu daha iyi hale getiren eleştiriler memnuniyetle karşılanmaktadır.

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

Etkileşimli kullanım için dplyr kullanmanın tadını çıkarırken, lazyeval :: interp (), setNames vb.

Burada R üssünü kullanan, en azından bana göre daha sezgisel görünen, döngüyü işlevin içine koymanın ve @ MrFlicks'in çözümünü genişleten daha basit bir sürüm.

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, hala dplyretkileşimli olmayan ayarlarda çok şey kullanmama rağmen, bir işlev içindeki değişken girişle kullanmak çok aksak sözdizimi kullanıyor.
Paul Hiemstra

3

Daha friendlyevalyeni / sıradan dplyrkullanıcılar için basitleştirilmiş düzenli bir değerlendirme API'si ve dokümantasyonu sunan paketin tadını çıkarabilirsiniz .

mutateSütun adı olarak işlemek istediğiniz dizeler oluşturuyorsunuz . Yani kullanarak friendlyevalyazabilirsiniz:

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

Kaputun altındaki, sütun adı olarak rlangkontrol edilen işlevleri çağırır varname.

friendlyeval herhangi bir zamanda bir RStudio eklentisi ile eşdeğer düz düzenli değerlendirme koduna dönüştürülebilir.


0

Başka bir alternatif: {}Kolayca dinamik adlar oluşturmak için tırnak içinde kullanın . Bu diğer çözümlere benzer ancak tam olarak aynı değildir ve bunu daha kolay buluyorum.

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

Bence bu geliyor dplyr 1.0.0ama emin değilim ( rlang 4.7.0eğer önemliyse bende var ).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.