【问题标题】:Renaming group of related variables in R重命名R中的相关变量组
【发布时间】:2021-05-07 21:04:06
【问题描述】:

注意:我不确定这个问题的好标题是什么。我很乐意修复它。

假设我有这个测试数据框:

library(tidyverse)
test.dat <- tibble(
  "User1" = c("Aaron","Aaron","Charlie"),
  "User2" = c("Ben","Ben","Aaron"),
  "User3" = c("Charlie","Charlie","Ben"),
  "first_1" = c("A","A","C"),
  "first_2" = c("B","B","A"),
  "first_3" = c("C","C","B"),
  "second_1" = c("A","A","C"),
  "second_2" = c("B","B","A"),
  "second_3" = c("C","C","B"),
  "third_1" = c("A","A","C"),
  "third_2" = c("B","B","A"),
  "third_3" = c("C","C","B")
)

# A tibble: 3 x 12
  User1   User2 User3   first_1 first_2 first_3 second_1 second_2 second_3 third_1 third_2 third_3
  <chr>   <chr> <chr>   <chr>   <chr>   <chr>   <chr>    <chr>    <chr>    <chr>   <chr>   <chr>  
1 Aaron   Ben   Charlie A       B       C       A        B        C        A       B       C      
2 Aaron   Ben   Charlie A       B       C       A        B        C        A       B       C      
3 Charlie Aaron Ben     C       A       B       C        A        B        C       A       B

我希望 User1 和带有 _1 的变量始终指代 Aaron,User2 始终是 Ben,带有 _2 的变量等等。

我可以做的是通过创建临时变量Loc 来定位每个名称在 User1:User3 中的位置,然后使用case_when

test.try <- test.dat %>%
  rowwise() %>%
  mutate(U1Loc = case_when(User1 == "Aaron" ~ 1,
                           User2 == "Aaron" ~ 2,
                           User3 == "Aaron" ~ 3),
         U2Loc = case_when(User1 == "Ben" ~ 1,
                           User2 == "Ben" ~ 2,
                           User3 == "Ben" ~ 3),
         U3Loc = case_when(User1 == "Charlie" ~ 1,
                           User2 == "Charlie" ~ 2,
                           User3 == "Charlie" ~ 3)) %>%
  mutate(newUser_1 = case_when(U1Loc == 1 ~ User1,
                                U1Loc == 2 ~ User2,
                                U1Loc == 3 ~ User3),
         newUser_2 = case_when(U2Loc == 1 ~ User1,
                                U2Loc == 2 ~ User2,
                                U2Loc == 3 ~ User3),
         newUser_3 = case_when(U3Loc == 1 ~ User1,
                                U3Loc == 2 ~ User2,
                                U3Loc == 3 ~ User3)) %>%
  mutate(newFirst_1 = case_when(U1Loc == 1 ~ first_1,
                                U1Loc == 2 ~ first_2,
                                U1Loc == 3 ~ first_3),
         newFirst_2 = case_when(U2Loc == 1 ~ first_1,
                                U2Loc == 2 ~ first_2,
                                U2Loc == 3 ~ first_3),
         newFirst_3 = case_when(U3Loc == 1 ~ first_1,
                                U3Loc == 2 ~ first_2,
                                U3Loc == 3 ~ first_3)) %>%
  mutate(newSecond_1 = case_when(U1Loc == 1 ~ second_1,
                                 U1Loc == 2 ~ second_2,
                                 U1Loc == 3 ~ second_3),
         newSecond_2 = case_when(U2Loc == 1 ~ second_1,
                                 U2Loc == 2 ~ second_2,
                                 U2Loc == 3 ~ second_3),
         newSecond_3 = case_when(U3Loc == 1 ~ second_1,
                                 U3Loc == 2 ~ second_2,
                                 U3Loc == 3 ~ second_3)) %>%
  select(starts_with("new"))

得到

> test.try
# A tibble: 3 x 9
# Rowwise: 
  newUser_1 newUser_2 newUser_3 newFirst_1 newFirst_2 newFirst_3 newSecond_1 newSecond_2 newSecond_3
  <chr>     <chr>     <chr>     <chr>      <chr>      <chr>      <chr>       <chr>       <chr>      
1 Aaron     Ben       Charlie   A          B          C          A           B           C          
2 Aaron     Ben       Charlie   A          B          C          A           B           C          
3 Aaron     Ben       Charlie   A          B          C          A           B           C   

但是,变量越多,这个过程就越繁琐。除了 for 循环之外,有没有其他方法可以实现这一点,最好是使用 tidy 方法?我的猜测是使用across(),但我似乎无法让它像我想象的那样工作。

【问题讨论】:

    标签: r dplyr tidyr


    【解决方案1】:

    我们可以通过旋转到“长”格式轻松做到这一点,然后在替换值后将形状重新调整为宽

    library(dplyr)
    library(tidyr)
    out <- test.dat %>% 
        mutate(rn = row_number()) %>% 
        pivot_longer(cols = -rn, names_to = c('.value', 'grp'),
             names_sep = "(?<=[a-z])_?(?=[0-9])") %>% 
        group_by(grp) %>% 
        mutate(across(User:third, first)) %>%
        pivot_wider(names_from = grp, values_from = c(User, first, second, third)) %>%
        select(-rn)
    

    -输出

    out
    # A tibble: 3 x 12
    #  User_1 User_2 User_3  first_1 first_2 first_3 second_1 second_2 second_3 third_1 third_2 third_3
    #  <chr>  <chr>  <chr>   <chr>   <chr>   <chr>   <chr>    <chr>    <chr>    <chr>   <chr>   <chr>  
    #1 Aaron  Ben    Charlie A       B       C       A        B        C        A       B       C      
    #2 Aaron  Ben    Charlie A       B       C       A        B        C        A       B       C      
    #3 Aaron  Ben    Charlie A       B       C       A        B        C        A       B       C      
    

    如果我们想重命名以new为前缀的列

    library(stringr)
    out %>%
        rename_all(~ str_c('new', .))
    

    或者另一种选择是创建一个键值数据集来匹配和替换列中的值是否没有顺序

    keydat <- tibble(grp = as.character(1:3), UserKey = c("Aaron", "Ben", "Charlie"),
          abbr = substr(UserKey, 1, 1))
    
    
    test.dat %>% 
            mutate(rn = row_number()) %>% 
            pivot_longer(cols = -rn, names_to = c('.value', 'grp'),
                 names_sep = "(?<=[a-z])_?(?=[0-9])") %>%
            left_join(keydat) %>% 
            mutate(User = UserKey) %>% 
            mutate(across(first:third, ~ abbr)) %>%
            select(-UserKey, -abbr) %>% 
            pivot_wider(names_from = grp, values_from = c(User, first, second, third)) %>%
            select(-rn)
    # A tibble: 3 x 12
    #  User_1 User_2 User_3  first_1 first_2 first_3 second_1 second_2 second_3 third_1 third_2 third_3
    #  <chr>  <chr>  <chr>   <chr>   <chr>   <chr>   <chr>    <chr>    <chr>    <chr>   <chr>   <chr>  
    #1 Aaron  Ben    Charlie A       B       C       A        B        C        A       B       C      
    #2 Aaron  Ben    Charlie A       B       C       A        B        C        A       B       C      
    #3 Aaron  Ben    Charlie A       B       C       A        B        C        A       B       C      
    

    【讨论】:

    • 在第一种方法中:mutate(across(User:third, first)) 导致错误,因为 cross() 需要函数作为第二个参数。我假设您的意思是mutate(across(User:third, ~ first)),但它没有返回预期的输出。你愿意看看吗? 编辑:没关系,像 mutate(across(User:third, dplyr::first)) 这样指定 dplyr 会产生预期的结果。我不确定 R 认为是什么,因为我没有加载任何其他包。
    • @aiorr 我不确定为什么会出现问题。 first 也来自 data.table。可能你也有一个列名'first'?
    • 很可能,因为上一步中的 pivot_longer() 会将 first_1,2,3 转换为名为 first 的单个列。
    • 可能,我的 dplyr 版本没有支持
    猜你喜欢
    • 1970-01-01
    • 2018-10-07
    • 2017-10-10
    • 1970-01-01
    • 2020-06-16
    • 1970-01-01
    • 2014-06-17
    • 2016-11-13
    • 1970-01-01
    相关资源
    最近更新 更多