如何将数据框的行与 R 中的第一个可用匹配项合并?

How to merge a rows of a dataframe with the first available match in R?

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

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 中的每一行都与第一个匹配项(key = stratum_id) df_population 行之前没有被匹配。在下面找到所需的解决方案:

# 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 包,到目前为止无济于事,但我确实认为我需要摆脱循环才能执行代码。

这是一个data.table方法。更多解释在代码的注释中。

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

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())

只需使用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