【问题标题】:How to merge a rows of a dataframe with the first available match in R?如何将数据框的行与 R 中的第一个可用匹配合并?
【发布时间】:2022-01-07 12:14:58
【问题描述】:

我有两个数据框:一个(称为df_persons)的记录具有唯一的person_id,但具有不唯一的stratum_id,另一个(称为df_population)具有相同的记录stratum_id's,以及它们的多个重复行。在下面重新创建它们的代码:

df_persons    = data.frame(person_id=c(101, 102, 103), stratum_id=c(1,2,1))
df_population = data.frame(stratum_id=c(1,1,1,1,2,2,2,2,3,3))

现在我想要一种将 df_persons 中的数据与 df_population 合并的方法,以便 df_persons 中的每一行都与 df_population 的第一个匹配 (key = stratum_id) 行合并,该行之前没有匹配过。在下面找到所需的解决方案:

# manual way to merge first available match
df_population$person = c(101, 103, NA, NA, 102, NA, NA, NA, NA, NA)

我为此编写了一个有效的循环(见下文)。问题是df_persons 有 83.000 条记录,df_population 有 1300 万条记录。因此循环需要太长时间 + 我的电脑无法处理它。

# create empty person column in df_population
df_population$person = NA

# order both df's to speed up
df_population = df_population[order(df_population$stratum_id),]
df_persons    = df_persons[order(df_persons$stratum_id),]

# loop through all persons in df_person, and for each find the first available match
for(i_person in 1:nrow(df_persons))
{
  match = F
  i_pop = 0

  while(!match)
  {
    i_pop = i_pop+1
    if(df_population$stratum_id[i_pop] == df_persons$stratum_id[i_person] & is.na(df_population$person[i_pop]))
    {
      match = T
      df_population$person[i_pop] = df_persons$person[i_person]
    }
  }
} 

任何有助于加快此过程的帮助将不胜感激。我已经查看了 data.frame 包,但到目前为止无济于事,但我确实认为我需要摆脱循环才能执行代码。

【问题讨论】:

    标签: r merge


    【解决方案1】:

    这是data.table 方法。代码的 cmets 中有更多解释。

    library(data.table)
    # make them data.table
    setDT(df_persons)
    setDT(df_population)
    # create dummy values to join on
    df_persons[, id := rowid(stratum_id)]
    df_population[, id := rowid(stratum_id)]
    # join by refence
    df_population[df_persons, person_id := i.person_id, on = .(stratum_id, id)][]
    # drop the dummy id column
    df_population[, id := NULL][]
    #    stratum_id person_id
    # 1:          1       101
    # 2:          1       103
    # 3:          1        NA
    # 4:          1        NA
    # 5:          2       102
    # 6:          2        NA
    # 7:          2        NA
    # 8:          2        NA
    # 9:          3        NA
    #10:          3        NA
    

    【讨论】:

      【解决方案2】:

      1) dplyr 使用 dplyr 为每个数据帧添加一个序列号,然后将它们合并:

      library(dplyr)
      
      df_population %>%
        group_by(stratum_id) %>%
        mutate(seq = 1:n()) %>%
        ungroup %>%
        left_join(df_persons %>% group_by(stratum_id) %>% mutate(seq = 1:n()))
      

      给予:

      Joining, by = c("stratum_id", "seq")
      # A tibble: 10 x 3
         stratum_id   seq person_id
              <dbl> <int>     <dbl>
       1          1     1       101
       2          1     2       103
       3          1     3        NA
       4          1     4        NA
       5          2     1       102
       6          2     2        NA
       7          2     3        NA
       8          2     4        NA
       9          3     1        NA
      10          3     2        NA
      

      2) 基础 R 或基础 R:

      p1 <- transform(df_population, seq = ave(stratum_id, stratum_id, FUN = seq_along))
      p2 <- transform(df_persons, seq = ave(stratum_id, stratum_id, FUN = seq_along))
      merge(p1, p2, all.x = TRUE, all.y = FALSE)
      

      3) sqldf 在 SQL 中,我们有以下内容。 dbname= 参数使其在 R 之外执行处理,但如果您有足够的内存,则可以省略它,它将使用 R 内的内存。

      library(sqldf)
      
      seqno <- "sum(1) over (partition by stratum_id rows unbounded preceding)"
      
      fn$sqldf("
        with 
          p1 as (select *, $seqno seq from df_population),
          p2 as (select *, $seqno seq from df_persons)
        select * from p1 left join p2 using (stratum_id, seq)
      ", dbname = tempfile())
      

      【讨论】:

      • 这第一个选项就像一个魅力 - 我的电脑花了 2 分钟,而我的原始代码大约需要一个小时。第二个选项给我带来了一些麻烦。第三个也有效,但明显比选项 1 慢。
      【解决方案3】:

      如下图直接使用pmatch

      df_population$person_id <- df_persons$person_id[pmatch(df_population$stratum_id, df_persons$stratum_id)]
      
      df_population
         stratum_id person_id
      1           1       101
      2           1       103
      3           1        NA
      4           1        NA
      5           2       102
      6           2        NA
      7           2        NA
      8           2        NA
      9           3        NA
      10          3        NA
      

      【讨论】:

      • 为什么使用允许部分匹配的pmatch,而不仅仅是match
      • 不知道为什么,但是使用 match 而不是 pmatch 并没有得到想要的结果。
      • @dash2 这是因为pmatch 匹配partialmatch 不会进行部分匹配,因此一旦匹配了一个数字,就可以重复使用它。但根据问题,您无需重新匹配匹配的数字
      • Onyambu 是对的。不幸的是,在像我这样大的数据集上,它使 R 崩溃了两次。
      猜你喜欢
      • 2018-03-25
      • 1970-01-01
      • 2012-05-23
      • 1970-01-01
      • 1970-01-01
      • 2020-04-19
      • 1970-01-01
      • 1970-01-01
      • 2019-01-09
      相关资源
      最近更新 更多