简体   繁体   English

交叉引用数据帧而不使用for循环

[英]Cross-referencing data frames without using for loops

Im having an issue with speed of using for loops to cross reference 2 data frames. 我在使用for循环交叉引用2个数据帧的速度方面遇到问题。 The overall aim is to identify rows in data frame 2 that lie between coordinates specified in data frame 1 (and meet other criteria). 总体目标是识别数据框2中位于数据框1中指定的坐标之间的行(并满足其他条件)。 eg df1: 例如df1:

    chr     start       stop        strand
1   chr1    179324331   179327814   +
2   chr21   45176033    45182188    +
3   chr5    126887642   126890780   +
4   chr5    148730689   148734146   +

df2: DF2:

    chr     start       strand
1   chr1    179326331   +
2   chr21   45175033    +
3   chr5    126886642   +
4   chr5    148729689   +

My current code for this is: 我当前的代码是:

for (index in 1:nrow(df1)) { 
  found_miRNAs <- ""
  curr_row = df1[index, ]; 
for (index2 in 1:nrow(df2)){
    curr_target = df2[index2, ]
    if (curr_row$chrm == curr_target$chrm & curr_row$start < curr_target$start & curr_row$stop > curr_target$start & curr_row$strand == curr_target$strand) {
      found_miRNAs <- paste(found_miRNAs, curr_target$start, sep=":")
    }
  }
  curr_row$miRNAs <- found_miRNAs
  found_log <- rbind(Mcf7_short_aUTRs2,curr_row)
}

My actual data frames are 400 lines for df1 and > 100 000 lines for df2 and I am hoping to do 500 iterations, so, as you can imagine this unworkably slow. 我的实际数据帧是df1的400行,而df2的> 100000行,我希望进行500次迭代,因此,正如您可以想象的那样,这是行不通的。 I'm relatively new to R so any hints for functions that may increase the efficiency of this would be great. 我对R还是比较陌生,因此任何可能提高此效率的函数提示都将是很棒的。

Maybe not fast enough, but probably faster and a lot easier to read: 也许速度不够快,但可能更快并且更容易阅读:

df1 <- data.frame(foo=letters[1:5], start=c(1,3,4,6,2), end=c(4,5,5,9,4))
df2 <- data.frame(foo=letters[1:5], start=c(3,2,5,4,1))
where <- sapply(df2$start, function (x) which(x >= df1$start & x <= df1$end))

This will give you a list of the relevant rows in df1 for each row in df2. 这将为df2中的每一行提供df1中相关行的列表。 I just tried it with 500 rows in df1 and 50000 in df2. 我刚刚在df1中尝试了500行,在df2中尝试了50000行。 It finished in a second or two. 一两秒就完成了。

To add criteria, change the inner function within sapply . 要添加条件,请在sapply更改内部函数。 If you then want to put where into your second data frame, you could do eg 然后,如果您想where第二个数据框中放置where ,则可以执行例如

df2$matching_rows <- sapply(where, paste, collapse=":")

But you probably want to keep it as a list, which is a natural data structure for it. 但是您可能希望将其保留为列表,这是它的自然数据结构。

Actually, you can even have a list column it in the data frame: 实际上,您甚至可以在数据框中添加一个列表列:

df2$matching_rows <- where

though this is quite unusual. 尽管这很不寻常。

You've run into two of the most common mistakes people make when coming to R from another programming language. 当您从另一种编程语言进入R时,您遇到了两个最常见的错误。 Using for loops instead of vector-based operations and dynamically appending to a data object. 使用for循环代替基于向量的操作,并动态附加到数据对象。 I'd suggest as you get more fluent you take some time to read Patrick Burns' R Inferno , it provides some interesting insight into these and other problems. 我建议随着您的流利程度的提高,您需要花一些时间阅读Patrick Burns的R Inferno ,它可以对这些问题和其他问题提供一些有趣的见解。

As @David Arenburg and @zx8754 have pointed out in the comments above there are specialized packages that can solve the problem, and the data.table package and @David's approach can be very efficient for larger datasets. 正如@David Arenburg和@ zx8754在上面的评论中指出的那样,有专门的软件包可以解决问题,而data.table软件包和data.table的方法对于较大的数据集可能非常有效。 But for your case base R can do what you need it to very efficiently as well. 但是对于您的案例,R也可以非常有效地执行您需要的操作。 I'll document one approach here, with a few more steps than necessary for clarity, just in case you're interested: 在这里,我将记录一种方法,为清晰起见,还提供了一些比必要的步骤多的步骤,以防您感兴趣:

set.seed(1001)

ranges <- data.frame(beg=rnorm(400))
ranges$end <- ranges$beg + 0.005

test <- data.frame(value=rnorm(100000))
##  Add an ID field for duplicate removal:
test$ID <- 1:nrow(test)


##  This is where you'd set your criteria.  The apply() function is just 
##      a wrapper for a for() loop over the rows in the ranges data.frame:
out <- apply(ranges, MAR=1, function(x) test[ (x[1] < test$value & x[2] > test$value), "ID"])

selected <- unlist(out)
selected <- unique( selected )

selection <- test[ selected, ]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM