【问题标题】:Pasting together all combinations of a column from one data.frame with all combinations of a column of another data.frame based on a condition根据条件将一个 data.frame 中的列的所有组合与另一个 data.frame 的列的所有组合粘贴在一起
【发布时间】:2017-09-12 16:29:01
【问题描述】:

我需要一些智慧!

我有两个数据框,比如:

test1 <- data.frame( let = replicate( 100, paste(sample(LETTERS[1:12] , 3 ) , collapse ="")  ) , num  = sample( 1:500 , 100 , replace = FALSE ))
test2 <- data.frame( let = replicate( 100, paste(sample(LETTERS[13:26] , 4 ) , collapse ="")  ) , num  = sample( 1:500 , 100 , replace = FALSE ))

head( test1 )
#   let num
# 1 KDA 430
# 2 IHB  41
# 3 GAB 473
# 4 HKJ 335
# 5 LCK 261
# 6 EIK 500

head( test2 )
#   let num
# 1 ZUYW 153
# 2 PRNW 263
# 3 OTQS 355
# 4 NYRW  87
# 5 ZYST 365
# 6 TXRN 287

现在,我想将 test1 中的所有字符串组合(即 test1$let)与 test2 中的所有字符串组合粘贴,但前提是 test1$num 和 test2$num 的差值

一种方法是:

test.merg <- NULL
i <- 1; j <- 1
for(i in 1:dim(test1)[1] ) {
  for( j in 1:dim(test2)[1]  ) { 
    if( abs( test1[i,]$num - test2[j,]$num ) <= 100  ){
      test.merg <- c(test.merg ,paste( test1[i,]$let , test2[j,]$let , sep="."   ) )
      }
    j <- j+ 1
    }
  i <- i+ 1
}
head(test.merg)
#[1] "KDA.OTQS" "KDA.ZYST" "KDA.TVRX" "KDA.VYRQ" "KDA.XRQS" "KDA.WSUR"

这很好用,但我的实际数据集当然不同且巨大,而且需要很长时间才能完成。我确信必须有一种更有效的方法来做到这一点。尝试使用 apply 系列功能,但我能想到的唯一方法是:

test1.1 <- paste( test1$let , test1$num ,sep = "_")
test2.1 <- paste( test2$let , test2$num ,sep = "_")

test.merg.1 <- unlist(lapply( test1.1 , FUN = function(x) {lapply( 
  test2.1 , FUN = function(y) {
    if( abs( as.numeric( str_split_fixed( x , "_" , 2 )[,2] )  - as.numeric( str_split_fixed( y , "_" , 2 )[,2]) ) <= 100){ 
      paste( str_split_fixed(x , "_" , 2 )[,1] , str_split_fixed(y , "_" , 2 )[,1], sep = ".")
  }
})
})
)

head(test.merg.1)
# [1] "KDA.OTQS" "KDA.ZYST" "KDA.TVRX" "KDA.VYRQ" "KDA.XRQS" "KDA.WSUR"

这已经将花费的时间减少了很多,几乎减少了 1/4,但是如果它可以变得更高效,那就太好了。更不用说,如果有一种完全不同且更好的方法来做到这一点,那就太棒了。

谢谢!

【问题讨论】:

  • 或许library(data.table); setDT(test2)[, num1 := num + 100];setDT(test1)[test2, on = .(num &lt;= num1), allow.cartesian = TRUE][, , .(let, i.let)]
  • 你的实际数据集有多大?
  • @Moody_Mudskipper:数据来自基因序列,对于一个基因,将有超过 100,000 个基因片段组合粘贴到另外 100,000 个基因片段的所有组合中。
  • 好吧,马上忘记我的解决方案:)。你在abs(test1$num-test2$num) &lt;= 100 中的比例大约是多少?如果它非常低,我们可以考虑一个算法,但你的对象的大小是10.000.000.000 * this proportion * size of an 8 character string,所以你可能需要另一种方法来解决你的一般问题......
  • 一种解决方案可能是在n 块中切割两个向量,例如10,并应用这两个解决方案中的任何一个(它们是同一件事),因此您将扩展数据集根据我的建议,这将是n^2 倍,100 倍,因此您首先创建一个包含 1 亿行的表,根据您的条件对其进行过滤,保存过滤后的对象或将其保存在某处,然后继续下一个块组合

标签: r apply nested-loops


【解决方案1】:

outer 语句的组合在这里起作用

outer(test1$let, test2$let, "paste", sep=".")[abs(outer(test1$num, test2$num, "-")) <= 100]

# [1] "DEF.VOXZ" "FHJ.VOXZ" "CHB.VOXZ" "JBH.VOXZ" etc

可重复的数据

set.seed(1)
test1 <- data.frame( let = replicate( 100, paste(sample(LETTERS[1:12] , 3 ) , collapse ="")  ) , num  = sample( 1:500 , 100 , replace = FALSE ))
test2 <- data.frame( let = replicate( 100, paste(sample(LETTERS[13:26] , 4 ) , collapse ="")  ) , num  = sample( 1:500 , 100 , replace = FALSE ))

基准测试

OP <- function() {
test.merg <- NULL
i <- 1; j <- 1
for(i in 1:dim(test1)[1] ) {
  for( j in 1:dim(test2)[1]  ) { 
    if( abs( test1[i,]$num - test2[j,]$num ) <= 100  ){
      test.merg <- c(test.merg ,paste( test1[i,]$let , test2[j,]$let , sep="."   ) )
      }
    j <- j+ 1
    }
  i <- i+ 1
}
head(test.merg)
}

myfun <- function() {
outer(test1$let, test2$let, "paste", sep=".")[abs(outer(test1$num, test2$num, "-")) <= 100]
}

library(microbenchmark)
microbenchmark(OP(), myfun(), times=10L)

Unit: milliseconds
    expr       min          lq        mean      median          uq        max neval
    OP() 4877.0017 4928.447303 5014.859718 5017.653519 5056.110679 5236.55990    10
 myfun()    5.8398    5.951762    8.501438    6.709145    7.842536   25.16273    10

几乎快 500 倍

【讨论】:

  • 谢谢!我在玩“外层”,但我没有想到要使用两个外层的组合,比如两个 lapply'es。这也是我测试的四种方式中最快的。
  • 我还在学习如何尽可能多地使用outer
  • 太棒了!那么对于outer( X , Y , FUN, ... ) FUN 可以是用户定义的函数吗?
【解决方案2】:

类似的东西?

注意:如果您的数据集真的如您所说的“巨大”,您的计算机将不会喜欢这样,但如果您想要所有可能的组合,我看不到任何其他方式。

res <- merge(test1 %>% rename_all(paste0,1),
             test2 %>% rename_all(paste0,2)) %>%
  filter(abs(num1-num2) <= 100) %>%
  mutate(str = paste(let1,let2,sep="_"))
#    let1 num1 let2 num2      str
# 1  DJE   82 VNQU  181 DJE_VNQU
# 2  JLE  238 VNQU  181 JLE_VNQU
# 3  EGI  220 VNQU  181 EGI_VNQU
# 4  KED  130 VNQU  181 KED_VNQU
# 5  CJF   81 VNQU  181 CJF_VNQU
# 6  KCH  235 VNQU  181 KCH_VNQU
# ...

head(res$str)
#[1] "DJE_VNQU" "JLE_VNQU" "EGI_VNQU" "KED_VNQU" "CJF_VNQU" "KCH_VNQU"

【讨论】:

  • 当“合并”没有“by”参数来比较和使用时,这是如何工作的?
  • 然后返回所有可能的组合,试试merge(1:3,1:2)
  • 太好了,谢谢!这很有效,并且总是喜欢使用 dplyr 的方法。但是“外部”更快,所以我接受了它作为答案。另外,不知道“合并”可以这样工作。为此+1。现在必须对我的实际问题进行升级。
  • 祝你好运:)。您能回来告诉我们您是否/如何设法利用大量数据使其软木塞?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-02
  • 2012-03-27
  • 1970-01-01
  • 2016-02-06
  • 1970-01-01
相关资源
最近更新 更多