【问题标题】:Merge dataframes using an extra condition r使用额外条件 r 合并数据帧
【发布时间】:2023-03-12 22:50:02
【问题描述】:

我知道这应该是一种更简单或更智能的方式来做我需要的事情,但几天后我还没有找到它。

我有 2 个需要使用额外条件合并的数据框。例如:

df1 <- data.frame(Username = c("user1", "user2", "user3", "user4", "user5", "user6"))
df2 <- data.frame(File_Name = c(rep("StudyABC", 5), rep("AnotherStudyCDE", 4)), Username = c("user1", rep(c("user2", "user3", "user4", "user5"),2)))
print(df1)
print(df2)

我需要在 df1 中创建 2 个名为 ABC 和 CDE 的新列,其中包含它们的“File_Name”值。当然,真实数据是数百行,没有排序,所以无法按范围选择。

我发现的一个解决方案(不优雅)是:

df2_filtered <- df2 %>% filter(str_detect(File_Name, "ABC"))
df1 <- left_join(df1, df2_filtered, by = "Username")
names(df1)[2] <- "ABC"

df2_filtered <- df2 %>% filter(str_detect(File_Name, "CDE"))
df1 <- left_join(df1, df2_filtered, by = "Username")
names(df1)[3] <- "CDE"

print(df1)

有没有最短的方法?因为我必须重复同样的逻辑 160 次。

谢谢

【问题讨论】:

  • 您的原始数据中是否也只有 2 个新列(ABC 和 CDE)?或者在“学习”之后会有很多这样的列需要提取?
  • 其实我会有 160 个不同的列,并且行名也会不同(这是我没有很好解释的),所以我有 Study1ABC、Study2ABC、StudyAnyNumberABC、...),所以我需要将 ABC 或 CDE 存在的所有行分组到 ABC 列下

标签: r merge left-join


【解决方案1】:

您可以从File_Name 中提取"ABC""CDE" 并将数据转换为宽格式。我们可以将数据与df1 连接起来,得到最终数据帧中的所有Username

library(dplyr)

df2 %>%
  mutate(name = stringr::str_extract(File_Name, 'ABC|CDE')) %>%
  tidyr::pivot_wider(names_from = name, values_from = File_Name) %>%
  right_join(df1, by = 'Username')

#  Username ABC      CDE            
#  <chr>    <chr>    <chr>          
#1 user1    StudyABC NA             
#2 user2    StudyABC AnotherStudyCDE
#3 user3    StudyABC AnotherStudyCDE
#4 user4    StudyABC AnotherStudyCDE
#5 user5    StudyABC AnotherStudyCDE
#6 user6    NA       NA             

【讨论】:

    【解决方案2】:

    您正在寻找的是一种将数据从长转换为宽的方法,例如使用 data.table 包我会这样做:

    library(data.table)
    
    # converts data.frame to data.table
    dt <- as.data.table(df2)
    
    # I copy the file_name so one is used for the pivotting for long to wide and the other is used for filling in the data
    dt[, study := File_Name]
    dt_wide <- dcast(Username~File_Name, data=dt, value.var = "study")
    
    # have a look at df2 in wide format
    dt_wide[]
    
    # now its just a direct merge to pull it back in to df1 and turn 
    # back in to data.frame for you
    out <- merge(as.data.table(df1), dt_wide, by="Username", all.x=TRUE)
    setDF(out)
    out
    

    即使没有 data.table,也有大量关于熔化/铸造的教程。它只是知道要搜索什么,例如 Google 会抛出 https://ademos.people.uic.edu/Chapter8.html 作为第一个结果。

    【讨论】:

      【解决方案3】:

      如果一项研究可以有多个文件路径(我假设您之前的尝试就是这种情况),那么在加入之前将数据转换为宽格式是行不通的,因为每个文件路径只有一列,而不是每次研究。

      在这种情况下,一种方法是使用 for 循环在 df2 中使用研究名称创建一个附加列,然后使用 pivot_wider 将数据转换为宽格式。

      这不是一个非常 R 的方法,所以我欢迎建议避免创建空的 study 列和 for 循环

      studies <- c("ABC", "CDE")
      
      #create empty column named "study"
      df2 <- df2 %>% 
        mutate(study = NA_character_)
      
      for (i in studies) {
      df2 <- df2 %>% 
        mutate(study = if_else(grepl(i, File_Name), i, study))
      }
      
      df2 <- df2 %>% 
        pivot_wider(names_from = study, values_from = File_Name)
      
      > df2
      # A tibble: 5 x 3
        Username ABC      CDE            
        <chr>    <chr>    <chr>          
      1 user1    StudyABC NA             
      2 user2    StudyABC AnotherStudyCDE
      3 user3    StudyABC AnotherStudyCDE
      4 user4    StudyABC AnotherStudyCDE
      5 user5    StudyABC AnotherStudyCDE
      

      df2 现在是宽格式,您可以像以前一样将其加入df1 以获得所需的输出。

      df3 <- left_join(df1, df2)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-12-30
        • 1970-01-01
        • 2021-08-08
        • 2016-10-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-05
        相关资源
        最近更新 更多