按 2 列中的元素对 2 个数据帧进行 RBIND,避免嵌套循环

rbind 2 data frames by the elements in 2 columns, avoiding nested loops

提问人:DaniCee 提问时间:9/29/2023 更新时间:9/29/2023 访问量:36

问:

我有两个数据框,如下所示:

df1 <- data.frame(Marker1=c('+','+','+','-','-'), Marker2=c('+','+','+','+','-'), Marker3=c('+','-','+','-','+'),
                  Sample=c(1,1,2,3,3), Population_ID=c(1,2,1,5,6), Cells_in_Sample=c(443,23,567,98,3))
df2 <- data.frame(Population_ID=c(1,1,1,1,1,1,1,2,2,2,2,2,2,2,5,5,5,5,5,5,5,6,6,6,6,6,6,6),
                  Marker1=c('+','+','+','+',NA,NA,NA,'+','+','+','+',NA,NA,NA,'-','-','-','-',NA,NA,NA,'-','-','-','-',NA,NA,NA),
                  Marker2=c('+','+',NA,NA,'+','+',NA,'+','+',NA,NA,'+','+',NA,'+','+',NA,NA,'+','+',NA,'-','-',NA,NA,'-','-',NA),
                  Marker3=c('+',NA,'+',NA,'+',NA,'+','-',NA,'-',NA,'-',NA,'-','-',NA,'-',NA,'-',NA,'-','+',NA,'+',NA,'+',NA,'+'))

它们看起来像这样:

> df1
  Marker1 Marker2 Marker3 Sample Population_ID Cells_in_Sample
1       +       +       +      1             1             443
2       +       +       -      1             2              23
3       +       +       +      2             1             567
4       -       +       -      3             5              98
5       -       -       +      3             6               3
> head(df2)
  Population_ID Marker1 Marker2 Marker3
1             1       +       +       +
2             1       +       +    <NA>
3             1       +    <NA>       +
4             1       +    <NA>    <NA>
5             1    <NA>       +       +
6             1    <NA>       +    <NA>

df1包含我的“基础”种群,其中包含 3 个标记的组合(所有 3 个都存在),加上每个样本每个种群的计数 ()。Cells_in_Sample

df2采用 3 个标记的独特组合,并从中找出 1 和 2 的所有可能组合。请注意,这已经包括了 中的“基础”种群。df2df1

我想在这里做的只是以一种高效和优雅的方式将两者结合起来,尽可能避免嵌套循环。final_df

应保留 中每个“碱基”3 标记组合的样本和计数值,扩展到 中的所有“子组合”。因此,我应该通过和.final_dfdf1df2rbindSamplePopulation_ID

现在我设法使用嵌套循环来做到这一点,但我想知道是否有更好的解决方案。for

这是我所做的:

final_df <- NULL
for (s in unique(df1$Sample)){
  df1_sub <- subset(df1, Sample==s)
  for (p in df1_sub$Population_ID){
    df1_sub_sub <- subset(df1_sub, Population_ID==p)
    df2_sub <- subset(df2, Population_ID==p)
    df2_sub$Sample <- s
    df2_sub$Cells_in_Sample <- df1_sub_sub$Cells_in_Sample
    df2_sub <- df2_sub[,c(2,3,4,5,1,6)]
    #note there is no need to rbind df1_sub_sub and df2_sub
    #cause df2 already contains the populations from df1
    final_df <- rbind(final_df, df2_sub)
  }
}

final_df看起来和我想要的一模一样。我把它完整粘贴在下面以供参考:

> final_df
    Marker1 Marker2 Marker3 Sample Population_ID Cells_in_Sample
1         +       +       +      1             1             443
2         +       +    <NA>      1             1             443
3         +    <NA>       +      1             1             443
4         +    <NA>    <NA>      1             1             443
5      <NA>       +       +      1             1             443
6      <NA>       +    <NA>      1             1             443
7      <NA>    <NA>       +      1             1             443
8         +       +       -      1             2              23
9         +       +    <NA>      1             2              23
10        +    <NA>       -      1             2              23
11        +    <NA>    <NA>      1             2              23
12     <NA>       +       -      1             2              23
13     <NA>       +    <NA>      1             2              23
14     <NA>    <NA>       -      1             2              23
15        +       +       +      2             1             567
16        +       +    <NA>      2             1             567
17        +    <NA>       +      2             1             567
18        +    <NA>    <NA>      2             1             567
19     <NA>       +       +      2             1             567
20     <NA>       +    <NA>      2             1             567
21     <NA>    <NA>       +      2             1             567
151       -       +       -      3             5              98
161       -       +    <NA>      3             5              98
171       -    <NA>       -      3             5              98
181       -    <NA>    <NA>      3             5              98
191    <NA>       +       -      3             5              98
201    <NA>       +    <NA>      3             5              98
211    <NA>    <NA>       -      3             5              98
22        -       -       +      3             6               3
23        -       -    <NA>      3             6               3
24        -    <NA>       +      3             6               3
25        -    <NA>    <NA>      3             6               3
26     <NA>       -       +      3             6               3
27     <NA>       -    <NA>      3             6               3
28     <NA>    <NA>       +      3             6               3

有没有一种简单有效的方法可以在没有嵌套循环的情况下做到这一点?我的实际数据比这大很多倍。

谢谢!

R 数据帧 for 循环 应用 rbind

评论

1赞 thelatemail 9/29/2023
这只是合并/联接吗? ?merge(df2, df1[c("Population_ID","Sample","Cells_in_Sample")], by="Population_ID")
0赞 DaniCee 9/29/2023
没有。。。因为您有多个相同的条目与不同的 相关联,并且样本信息在Population_IDdf1Samplesdf2
1赞 Rui Barradas 9/29/2023
实际上,@thelatemail是对的,请看我的答案。
0赞 DaniCee 9/29/2023
好吧,我很傻,这确实很容易!谢谢!

答:

4赞 Rui Barradas 9/29/2023 #1

你根本不需要循环,这是合并的情况。
在下面的代码中,我仅出于测试目的复制了 以保持预期结果不变。
final_df

df3 <- final_df
row.names(df3) <- NULL

# join removing the marker columns from df1
mrg <- merge(df2, df1[4:6], by = "Population_ID")[c(2:5, 1, 6)]
mrg <- mrg[order(mrg$Sample), ]
row.names(mrg) <- NULL

identical(df3, mrg)
#> [1] TRUE

创建于 2023-09-29 使用 reprex v2.0.2

2赞 Till 9/29/2023 #2

您想要的结果可以通过 .left_join()

  1. 删除 Marker 列,因为这些列已包含在 中。df1df2
  2. left_join()这两个数据集。
  3. 对列重新排序并排列行以匹配问题中的结果表。
library(tidyverse)

df1a <-
  df1 |>
  select(-contains("Marker"))

left_join(df2, df1a, relationship = "many-to-many") |>
  select(contains("Marker"), Sample, everything()) |>
  arrange(Population_ID, Sample)
#> Joining with `by = join_by(Population_ID)`
#>    Marker1 Marker2 Marker3 Sample Population_ID Cells_in_Sample
#> 1        +       +       +      1             1             443
#> 2        +       +    <NA>      1             1             443
#> 3        +    <NA>       +      1             1             443
#> 4        +    <NA>    <NA>      1             1             443
#> 5     <NA>       +       +      1             1             443
#> 6     <NA>       +    <NA>      1             1             443
#> 7     <NA>    <NA>       +      1             1             443
#> 8        +       +       +      2             1             567
#> 9        +       +    <NA>      2             1             567
#> 10       +    <NA>       +      2             1             567
#> 11       +    <NA>    <NA>      2             1             567
#> 12    <NA>       +       +      2             1             567
#> 13    <NA>       +    <NA>      2             1             567
#> 14    <NA>    <NA>       +      2             1             567
#> 15       +       +       -      1             2              23
#> 16       +       +    <NA>      1             2              23
#> 17       +    <NA>       -      1             2              23
#> 18       +    <NA>    <NA>      1             2              23
#> 19    <NA>       +       -      1             2              23
#> 20    <NA>       +    <NA>      1             2              23
#> 21    <NA>    <NA>       -      1             2              23
#> 22       -       +       -      3             5              98
#> 23       -       +    <NA>      3             5              98
#> 24       -    <NA>       -      3             5              98
#> 25       -    <NA>    <NA>      3             5              98
#> 26    <NA>       +       -      3             5              98
#> 27    <NA>       +    <NA>      3             5              98
#> 28    <NA>    <NA>       -      3             5              98
#> 29       -       -       +      3             6               3
#> 30       -       -    <NA>      3             6               3
#> 31       -    <NA>       +      3             6               3
#> 32       -    <NA>    <NA>      3             6               3
#> 33    <NA>       -       +      3             6               3
#> 34    <NA>       -    <NA>      3             6               3
#> 35    <NA>    <NA>       +      3             6               3