【问题标题】:How to replace values in multiple columns based on value from left-adjacent column如何根据左相邻列的值替换多列中的值
【发布时间】:2020-12-15 22:31:41
【问题描述】:

我有类似的数据(虽然数据集更大):

  correct.trial1 RT.trial1 correct.trial2 RT.trial2 correct.trial3 RT.trial3
1              1       473              0       337              1       426
2              1       496              1       407              1       421
3              1       368              0       405              1       470
4              0       333              1       475              0       473
5              0       435              0       312              1       402

我们可以用这个来制作这个样本:

set.seed(12)
df <- data.frame(correct.trial1 = sample(0:1, 5, replace=T),
                 RT.trial1 = sample(300:500, 5, replace=T),
                 correct.trial2 = sample(0:1, 5, replace=T),
                 RT.trial2 = sample(300:500, 5, replace=T),
                 correct.trial3 = sample(0:1, 5, replace=T),
                 RT.trial3 = sample(300:500, 5, replace=T))

当相邻(左)列 starts_with("correct.trial") 的值为 0 时,我想将值 starts_with("RT.trial") 替换为 NA。当然,我可以一次只做一个,例如:

library(dplyr)
df %>%
  mutate(RT.trial1 = ifelse(correct.trial1==1, RT.trial1, NA),
         RT.trial2 = ifelse(correct.trial2==1, RT.trial2, NA),
         RT.trial3 = ifelse(correct.trial3==1, RT.trial3, NA))

所以它看起来像这样:

  correct.trial1 RT.trial1 correct.trial2 RT.trial2 correct.trial3 RT.trial3
1              1       473              0        NA              1       426
2              1       496              1       407              1       421
3              1       368              0        NA              1       470
4              0        NA              1       475              0        NA
5              0        NA              0        NA              1       402

但这对于数千列是不切实际的。

问题

如何同时对所有列执行此操作? (注意:我更喜欢dplyr 解决方案,使用across 比使用mutate_at 更可取。)

尝试

不确定,但根据related post,它(可能)看起来像这样:

df %>%
  mutate_at(vars(starts_with("RT.trial")),
  ~ifelse(vars(starts_with("correct.trial"))==0, NA, .x))

【问题讨论】:

  • 谢谢@akrun,我刚刚添加了一个种子并更新了值。

标签: r dplyr across


【解决方案1】:

我们可以重塑为“长”格式,然后进行转换

library(dplyr)
library(tidyr)
df %>% 
    mutate(rn = row_number()) %>% 
    pivot_longer(cols = -rn, names_to = c(".value", "grp"), 
          names_sep="\\.") %>%
    mutate(RT = case_when(as.logical(correct) ~ RT)) %>% 
    pivot_wider(names_from = grp, values_from = c(correct, RT), 
          names_sep = ".") %>%
    select(names(df))

-输出

# A tibble: 5 x 6
#  correct.trial1 RT.trial1 correct.trial2 RT.trial2 correct.trial3 RT.trial3
#           <int>     <int>          <int>     <int>          <int>     <int>
#1              0        NA              0        NA              0        NA
#2              1       394              1       458              0        NA
#3              0        NA              1       337              0        NA
#4              1       479              0        NA              0        NA
#5              0        NA              0        NA              0        NA

base R 中,这可以通过更简单的方式完成

i1 <- grepl('correct', names(df))
df[!i1] <- (NA^!df[i1]) * df[!i1]

数据

df <- structure(list(correct.trial1 = c(0L, 1L, 0L, 1L, 0L), RT.trial1 = c(417L, 
394L, 345L, 479L, 368L), correct.trial2 = c(0L, 1L, 1L, 0L, 0L
), RT.trial2 = c(382L, 458L, 337L, 406L, 306L), correct.trial3 = c(0L, 
0L, 0L, 0L, 0L), RT.trial3 = c(469L, 364L, 361L, 359L, 309L)),
 class = "data.frame", row.names = c("1", 
"2", "3", "4", "5"))

【讨论】:

  • @RemPsyc 我添加了一个base R 方法,假设“正确”和“RT”列是对应的并且数据只有这些列,这应该更容易
  • @RemPsyc 如果是这种情况,您可以让第二个i2 &lt;- grepl("^RT\\.trial\\d+$", names(df)) 对 RT 列进行子集化
  • @RemPsyc 您可以在进入 pivot_longer 之前使用selectdf1 %&gt;% select(matches('^(RT_trial|correct\\.trial")) 或将pivot_longer 中的cols= 更改为此。我会选择列的一个子集,因为它会使事情变得更高效和更快
  • @RemPsyc 抱歉,这将是 ' 而不是 " 报价需要匹配。这是一个错字。对不起
  • 哦,好的版本是这样的:df %&gt;% select(matches('^(RT|correct\\.trial)'))(最后一个trial后面的括号,没有_trialRT)。再次感谢!
【解决方案2】:

这也有效。这是使用cross最简单的方法。

library(tidyverse)

df %>% 
  mutate(across(starts_with("RT.trial"), ~ if_else(get(str_c("correct.trial", str_sub(cur_column(), -1))) == 0, NA_integer_, .)))

这给出了:

  correct.trial1 RT.trial1 correct.trial2 RT.trial2 correct.trial3 RT.trial3
1              1       473              0        NA              1       426
2              1       496              1       407              1       421
3              1       368              0        NA              1       470
4              0        NA              1       475              0        NA
5              0        NA              0        NA              1       402

【讨论】:

  • 你应该在开始时加载你的包,否则当人们尝试测试它时它不会工作。我有一个错误:could not find function "str_c"
【解决方案3】:

如果您想坚持使用tidyverse,这里还有一个选择:

library(dplyr)

purrr::map2_dfc(df %>% select(starts_with('RT')), 
                df %>% select(starts_with('correct')),
                ~if_else(.y == 0, NA_integer_, .x)) %>%
  bind_cols(df %>% select(starts_with('correct'))) %>%
  #To get correct order of columns
  select(order(as.numeric(sub('\\D+', '', names(.)))))

#  RT.trial1 correct.trial1 RT.trial2 correct.trial2 RT.trial3 correct.trial3
#      <int>          <int>     <int>          <int>     <int>          <int>
#1       473              1        NA              0       426              1
#2       496              1       407              1       421              1
#3       368              1        NA              0       470              1
#4        NA              0       475              1        NA              0
#5        NA              0        NA              0       402              1

【讨论】:

  • 目前,此答案未提供预期的输出(更新后的 trial 列会覆盖 correct 列)。
  • 哎呀..你是对的。不得不交换列。更新的答案应该有助于@RemPsyc
  • 这样更好! :)
猜你喜欢
  • 1970-01-01
  • 2017-04-22
  • 2020-12-24
  • 2021-12-25
  • 1970-01-01
  • 2016-07-18
  • 2013-03-15
  • 2022-12-31
相关资源
最近更新 更多