不使用 for 循环的交叉引用数据框
Cross-referencing data frames without using for loops
我在使用 for 循环交叉引用 2 个数据帧时遇到速度问题。总体目标是识别数据框 2 中位于数据框 1 中指定坐标之间的行(并满足其他条件)。例如df1:
chr start stop strand
1 chr1 179324331 179327814 +
2 chr21 45176033 45182188 +
3 chr5 126887642 126890780 +
4 chr5 148730689 148734146 +
df2:
chr start strand
1 chr1 179326331 +
2 chr21 45175033 +
3 chr5 126886642 +
4 chr5 148729689 +
我目前的代码是:
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)
}
我的实际数据帧是 df1 的 400 行和 df2 的 > 100 000 行,我希望进行 500 次迭代,所以,正如您可以想象的那样,这速度太慢了。我对 R 比较陌生,所以任何可能提高效率的功能提示都会很棒。
也许不够快,但可能更快并且更容易阅读:
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))
这将为您提供 df2 中每一行的 df1 中相关行的列表。我刚刚在 df1 中尝试了 500 行,在 df2 中尝试了 50000 行。它在一两秒内完成。
要添加条件,请更改 sapply
内的内部函数。如果你想将 where
放入你的第二个数据框中,你可以这样做
df2$matching_rows <- sapply(where, paste, collapse=":")
但您可能希望将其保留为列表,这是它的自然数据结构。
实际上,您甚至可以在数据框中添加一个列表列:
df2$matching_rows <- where
虽然这很不寻常。
您 运行 陷入了人们从另一种编程语言转向 R 时最常犯的两个错误。使用 for 循环而不是基于向量的操作并动态附加到数据对象。我建议当你变得更流利时,你花一些时间阅读 Patrick Burns' R Inferno,它提供了对这些问题和其他问题的一些有趣的见解。
正如@David Arenburg 和@zx8754 在上面的评论中指出的那样,有专门的包可以解决这个问题,data.table
包和@David 的方法对于较大的数据集可能非常有效。但是对于您的案例库,R 也可以非常有效地完成您需要的工作。我将在此处记录一种方法,为了清楚起见,比必要的步骤多一些,以防您感兴趣:
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, ]
我在使用 for 循环交叉引用 2 个数据帧时遇到速度问题。总体目标是识别数据框 2 中位于数据框 1 中指定坐标之间的行(并满足其他条件)。例如df1:
chr start stop strand
1 chr1 179324331 179327814 +
2 chr21 45176033 45182188 +
3 chr5 126887642 126890780 +
4 chr5 148730689 148734146 +
df2:
chr start strand
1 chr1 179326331 +
2 chr21 45175033 +
3 chr5 126886642 +
4 chr5 148729689 +
我目前的代码是:
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)
}
我的实际数据帧是 df1 的 400 行和 df2 的 > 100 000 行,我希望进行 500 次迭代,所以,正如您可以想象的那样,这速度太慢了。我对 R 比较陌生,所以任何可能提高效率的功能提示都会很棒。
也许不够快,但可能更快并且更容易阅读:
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))
这将为您提供 df2 中每一行的 df1 中相关行的列表。我刚刚在 df1 中尝试了 500 行,在 df2 中尝试了 50000 行。它在一两秒内完成。
要添加条件,请更改 sapply
内的内部函数。如果你想将 where
放入你的第二个数据框中,你可以这样做
df2$matching_rows <- sapply(where, paste, collapse=":")
但您可能希望将其保留为列表,这是它的自然数据结构。
实际上,您甚至可以在数据框中添加一个列表列:
df2$matching_rows <- where
虽然这很不寻常。
您 运行 陷入了人们从另一种编程语言转向 R 时最常犯的两个错误。使用 for 循环而不是基于向量的操作并动态附加到数据对象。我建议当你变得更流利时,你花一些时间阅读 Patrick Burns' R Inferno,它提供了对这些问题和其他问题的一些有趣的见解。
正如@David Arenburg 和@zx8754 在上面的评论中指出的那样,有专门的包可以解决这个问题,data.table
包和@David 的方法对于较大的数据集可能非常有效。但是对于您的案例库,R 也可以非常有效地完成您需要的工作。我将在此处记录一种方法,为了清楚起见,比必要的步骤多一些,以防您感兴趣:
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, ]