根据存储在另一列中的现有列的名称创建新列(使用dplyr)

9

请考虑以下数据集:

df <- tibble(v1 = 1:5, v2= 101:105, v3 = c("v1", "v2", "v1", "v2", "v1"))

# A tibble: 5 × 3
     v1    v2 v3   
  <int> <int> <chr>
1     1   101 v1   
2     2   102 v2   
3     3   103 v1   
4     4   104 v2   
5     5   105 v1  

我想生成一个新的列,该列根据 v3 中所列出的是 v1 还是 v2,从而取值。
    # A tibble: 5 × 4
     v1    v2 v3       v4
  <int> <int> <chr> <dbl>
1     1   101 v1        1
2     2   102 v2      102
3     3   103 v1        3
4     4   104 v2      104
5     5   105 v1        5

通常情况下,我会使用if_else,或者如果有更多的情况,就会用case_when。但是,我有很多列,所以我不想有一个很长的case_when语句。有没有办法让R将v3中的值解释为列名?我尝试了用{{ }}来包含表达式,并使用.data[[ ]],但我似乎不能弄清正确的语法。
5个回答

8
一个 tidyverse 的选项是使用 rowwise 并使用 cur_data() 进行提取。
library(dplyr)
df %>% 
  rowwise %>%
  mutate(v4 = cur_data()[[v3]]) %>% 
  ungroup
# A tibble: 5 × 4
     v1    v2 v3       v4
  <int> <int> <chr> <int>
1     1   101 v1        1
2     2   102 v2      102
3     3   103 v1        3
4     4   104 v2      104
5     5   105 v1        5

或者更简洁的方法是在 rowwise 之后使用 get

df %>%
  rowwise %>%
  mutate(v4 = get(v3)) %>%
  ungroup

或者在base R中,使用行/列索引以加快执行速度。

df$v4 <- as.data.frame(df[1:2])[cbind(seq_len(nrow(df)), 
      match(df$v3, names(df)))]
df$v4
[1]   1 102   3 104   5

1
第一种和第二种方法都既易读又简洁,特别是get选项。谢谢! - conflictcoder

8

这里有一种向量化的方法,无需逐行操作或逐个映射map

df %>%
  mutate(v4 = cbind(v1,v2)[ cbind(row_number(), match(v3, c("v1", "v2"))) ])
# # A tibble: 5 x 4
#      v1    v2 v3       v4
#   <int> <int> <chr> <int>
# 1     1   101 v1        1
# 2     2   102 v2      102
# 3     3   103 v1        3
# 4     4   104 v2      104
# 5     5   105 v1        5

1
很好地运用了 row_number()。我一直在寻找一种向量化这些“查找列”操作的方法,但一直依赖于 rowwisepmap 等等,这太棒了。 - GuedesBF
1
是的,我总是尽可能避免使用rowwise,除非绝对必要,而更喜欢矢量化方法。谢谢。 - r2evans

6

以下是使用 pivot_longer 的方法:

  1. 使用 pivot_longer 将其转换为长格式。
  2. 筛选(filter)
  3. bind_cols() 连接 v1v2
library(tidyr)
library(dplyr)
df %>% 
  pivot_longer(
    -v3,
    names_to = "name",
    values_to = "v4"
  ) %>% 
  filter(v3 == name) %>% 
  bind_cols(v1 = df$v1, v2=df$v2) %>% 
  select(v1, v2, v3, v4)

  v1    v2 v3       v4
  <int> <int> <chr> <int>
1     1   101 v1        1
2     2   102 v2      102
3     3   103 v1        3
4     4   104 v2      104
5     5   105 v1        5

5
你可以尝试以下基础 R 代码,使用 diag + as.matrix(或 t):
transform(
  df,
  v4 = diag(as.matrix(df[v3]))
)

或者
transform(
  df,
  v4 = diag(t(df[v3]))
)

这提供了

  v1  v2 v3  v4
1  1 101 v1   1
2  2 102 v2 102
3  3 103 v1   3
4  4 104 v2 104
5  5 105 v1   5

4

一个基于R的解决方案:

df <- tibble(v1 = 1:5, v2= 101:105, v3 = c("v1", "v2", "v1", "v2", "v1"))

df$v4 <- apply(df, 1, function(x) x[x[3]])
df         

#> # A tibble: 5 × 4
#>      v1    v2 v3    v4   
#>   <int> <int> <chr> <chr>
#> 1     1   101 v1    1    
#> 2     2   102 v2    102  
#> 3     3   103 v1    3    
#> 4     4   104 v2    104  
#> 5     5   105 v1    5

另一个可能的解决方案是使用purrr::pmap_dfr

library(tidyverse)    

df <- tibble(v1 = 1:5, v2= 101:105, v3 = c("v1", "v2", "v1", "v2", "v1"))

df %>% 
  mutate(pmap_dfr(., ~ list(v4 = if_else(..3 == "v1", ..1, ..2))))

#> # A tibble: 5 × 4
#>      v1    v2 v3       v4
#>   <int> <int> <chr> <int>
#> 1     1   101 v1        1
#> 2     2   102 v2      102
#> 3     3   103 v1        3
#> 4     4   104 v2      104
#> 5     5   105 v1        5

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接