【问题标题】:R dplyr with multiple columns with same stem nameR dplyr 具有多个具有相同词干名称的列
【发布时间】:2018-02-14 00:22:07
【问题描述】:

只要对应的列>0,我需要将一些数据列设置为 NA。

我可以使用 mutate 和两列的名称来执行此操作,但我想要一个范围版本,在其中我从第一列的名称创建相应列的名称

(x<-data.frame(x1=(1:4),map.x1=c(0,0,7,0),x2=c(2,2,2,2),map.x2=c(0,7,0,0)))

mutate(x, x1=ifelse(map.x1>0, NA, x1))

mutate_at(x, vars(starts_with("x")), function(v) { 
  m.name <- paste0("map.", deparse(substitute(v)))
  ifelse(get(m.name)>0, NA, v)
  )
}

我可以看到 ifelse() 不高兴,因为它希望第一个参数是一个对象,并且我已经给出了一个表达式。

我找不到方法。我什至想知道是否有某种方法可以避免在 paste0() 或 get() 中使用 function(v) 和使用 (.)

我也在考虑重塑,这样我就可以进行一次变异。这里的最佳做法是什么?

【问题讨论】:

  • 函数的括号和方括号乱七八糟。

标签: r dplyr


【解决方案1】:

这是一种无需重塑数据的解决方案。

library(dplyr)
library(rlang)

custom_mutate <- function(df, v){
  v <- enquo(v)
  map.v <- paste0("map.", quo_name(v))

  df %>%
    mutate(UQE(v) := ifelse((!!sym(map.v)) > 0, NA, (!!v))) %>% 
    pull(UQE(v))
}

mutate_at(x, vars(starts_with("x")), funs(custom_mutate(df = x, v = .)))

#   x1 map.x1 x2 map.x2
# 1  1      0  2      0
# 2  2      0 NA      7
# 3 NA      7  2      0
# 4  4      0  2      0

mutate_at 调用中的函数仅适用于列,而不适用于整个数据框。因此,您必须明确告诉函数在哪里查找您的 map.x1 列。
要从您正在使用的列中获取名称,首先您需要使用enquov 转换为quosure。然后你可以使用quo_name 来构造map.-name。在下面的mutate 调用中,重要的是告诉dplyrv 是一个quosure(因此UQE 包裹在它周围,这类似于@987654335 中它前面的!! @-ifelse 语句的一部分)。
对于map.x1 列,您必须使用rlang-package 中的sym-函数来获取裸名(不带引号),然后再次使用!! 告诉dplyr 将此作为列名。

我尽量解释我的解决方案,而不是技术性的。有关如何使用 dplyr 进行编程的详细说明,请参见此处:Programming with dplyr

【讨论】:

  • 谢谢@kath,我希望能提供一个没有重塑的答案。我必须仔细阅读这篇文章,因为我之前没有使用过 quosure(刚刚在视频中看到过)。结果可能 reshape 更“可读”,但这是我想知道的。再次感谢。
【解决方案2】:

这是获得所需输出的一种方法。无需编写自定义函数。重塑文件就足够了。

library(tibble)
library(dplyr)
library(stats)

# creating dataframe with proper names
x <-
  tibble::as_data_frame(cbind(
    x_1 = c(1:4),
    map.x_1 = c(0, 0, 7, 0),
    x_2 = c(2, 2, 2, 2),
    map.x_2 = c(0, 7, 0, 0)
  )) %>%
  tibble::rownames_to_column(df = ., var = 'id')

# converting to long format
x_long <- stats::reshape(
  as.data.frame(x),
  timevar = "level",
  varying = dput(as.character(as.vector(names(
    x[, base::grep("^x|^map", names(x))]
  )))),
  direction = "long",
  idvar = c("id"),
  sep = "_"
)
#> c("x_1", "map.x_1", "x_2", "map.x_2")

# converting the dataframe based on condition
x_long %>%
  group_by(.data = ., level) %>%
  dplyr::mutate(.data = .,
                x = base::ifelse(test = map.x > 0,
                                 yes = NA,
                                 no = x))
#> # A tibble: 8 x 4
#> # Groups:   level [2]
#>   id    level     x map.x
#>   <chr> <dbl> <dbl> <dbl>
#> 1 1      1.00  1.00  0   
#> 2 2      1.00  2.00  0   
#> 3 3      1.00 NA     7.00
#> 4 4      1.00  4.00  0   
#> 5 1      2.00  2.00  0   
#> 6 2      2.00 NA     7.00
#> 7 3      2.00  2.00  0   
#> 8 4      2.00  2.00  0

reprex package (v0.1.1.9000) 于 2018 年 2 月 14 日创建。

【讨论】:

  • 感谢@Indrajeet,我想我要重塑了。这是我考虑过的一个选择。很高兴能写出一些代码。
  • @D.Bontempo 酷,在这种情况下,请接受答案 (stackoverflow.com/help/someone-answers),以便关闭此线程。
  • 再次感谢,我不擅长关闭线程。我会等一会儿,因为具有两个动态列名的 dplyr 问题仍然很有趣。如果没有人很快回答,我会关闭这个帖子,并考虑在未来更通用的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-10
  • 1970-01-01
  • 2017-02-20
  • 1970-01-01
  • 1970-01-01
  • 2013-03-31
  • 2011-09-16
相关资源
最近更新 更多