替换大数据中多个值的最快方法。框架

时间:2019-09-12 20:20:47

标签: r performance dataframe

我有一个非常大的数据框,我需要替换数据框中的其他值。随着时间的推移,我写了几种不同的方法来替换需要更改的值。这是数据的子集,因此您可以了解我在说什么

df <- structure(list(CHROM = c("chr1", "chr1", "chr1", "chr1", "chr1", 
"chr1", "chr1", "chr1", "chr1", "chr1", "chr1", "chr1", "chr1", 
"chr1", "chr1", "chr1", "chr1", "chr1", "chr1", "chr1"), POS = c(619L, 
668L, 744L, 745L, 1064L, 1099L, 1121L, 1123L, 1126L, 1193L, 1208L, 
1214L, 1250L, 1265L, 1274L, 1277L, 1283L, 1307L, 1314L, 1325L
), `GEN[D86396].GT` = c("0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0"), `GEN[D86397].GT` = c("0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0"), `GEN[D00105].GT` = c("0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0"), `GEN[D00151].GT` = c("0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0"), `GEN[D00188].GT` = c("0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0"), `GEN[D00220].GT` = c("0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0"), `GEN[D00257].GT` = c("0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0"), `GEN[D00258].GT` = c("0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0"), `GEN[D00264].GT` = c("0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0"), `GEN[D00268].GT` = c("0/0", 
"0/0", "0/0", "0/0", "0/1", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/1", 
"0/0"), `GEN[D00269].GT` = c("0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0"), `GEN[D00270].GT` = c("0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0"), `GEN[D00271].GT` = c("0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0"), `GEN[D00276].GT` = c("0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0"), `GEN[D00280].GT` = c("0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0"), `GEN[D00282].GT` = c("0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/1", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0"), `GEN[D00285].GT` = c("0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0"), `GEN[D00315].GT` = c("0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0"), `GEN[D00316].GT` = c("0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0"), `GEN[D00319].GT` = c("0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0"), `GEN[D00339].GT` = c("0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", "0/0", 
"0/0", "0/0", "0/0", "0/0", "0/0", "0/0")), row.names = c(NA, 
20L), class = "data.frame")

所以我需要做的是将“ 0/0”转换为0,将“ 0/1”转换为1,将“ 1/1”转换为2,再将“ ./”转换为。设为0.1(在此示例中我认为没有)。

过去,我使用了以下

replacement<-function(x){
  x=replace(x,which(x=='./.'),0.01) 
  x=replace(x,which(x=='0/0'),0)
  x=replace(x,which(x=='0/1'),1)
  x=replace(x,which(x=='1/1'),2)
}
df=apply(df,2,replacement)
df <- as.data.frame(df)

这还可以,但是仍然需要几个小时才能运行。我也用过这个。

df <- df %>% mutate_at(
  vars(- CHROM, - POS),
  funs(case_when(
    . == "0/0" ~ 0,
    . == "0/1" ~ 1,
    . == "1/1" ~ 2,
    . == "./." ~ 0.01
  ))
)

这还可以。我意识到,对于大型数据集,某些东西将需要很长时间才能运行。我只是想知道替换值最快的方法是什么。我见过很多其他帖子,都在询问有关NA的类似问题,但是我找不到与我的问题有关的任何问题。我认为使用data.table可能是最快的方法?还是将数据帧转换为矩阵?我确定你的想法。

谢谢!

2 个答案:

答案 0 :(得分:1)

一种快速简便的解决方案是使用查找表:

lookup_table <- c("0/0" = 0, "0/1" = 1, "1/1" = 2, "./." = 0.1)
df[-(1:2)] <- lapply(df[-(1:2)], function(x) lookup_table[x])

等效(可能使用更少的最大内存):

for (j in 3:length(df)) df[[j]] <- lookup_table[df[[j]]]

基准

N <- 100e3
M <- 340
df <- data.frame(CHROM = 1, POS = seq_len(N))
for (j in 3:M) df[[j]] <- sample(c("0/0", "0/1", "1/1", "./."), N, TRUE)

system.time({
  lookup_table <- c("0/0" = 0, "0/1" = 1, "1/1" = 2, "./." = 0.01)
  df2 <- df
  df2[-(1:2)] <- lapply(df2[-(1:2)], function(x) lookup_table[x])
})
# 1.5 sec

system.time({
  replacement <- function(x) {
    x = replace(x, which(x == './.'), 0.01)
    x = replace(x, which(x == '0/0'), 0)
    x = replace(x, which(x == '0/1'), 1)
    x = replace(x, which(x == '1/1'), 2)
  }
  df3 <- as.data.frame(apply(df, 2, replacement), stringsAsFactors = FALSE)
})
# 4.5 sec

library(dplyr)
system.time({
  df4 <- df %>% mutate_at(
    -(1:2),
    ~ case_when(
      . == "0/0" ~ 0,
      . == "0/1" ~ 1,
      . == "1/1" ~ 2,
      . == "./." ~ 0.01
    )
  )
})
# 5.2 sec

答案 1 :(得分:1)

我查看了一些sed命令,我认为应该发布我发现的内容,以防万一有人遇到类似问题。

我发现可以在终端上使用的sed命令是(这将创建一个新文件,但是您不必创建新文件)

sed -e 's+0/0+0+g' -e 's+0/1+1+g' -e 's+1/1+2+g' -e 's+./.+0.01+g R.test.txt > R.test.edit.txt

或者这在R中也能正常工作

system(paste(sed -e 's+0/0+0+g' -e 's+0/1+1+g' -e 's+1/1+2+g' -e 's+./.+0.01+g R.test.txt > R.test.edit.txt))

您还可以使用IceCreamToucan提到的data.table::fread方法

df <- fread("sed -e 's+0/0+0+g' -e 's+0/1+1+g' -e 's+1/1+2+g' -e 's+./.+0.01+g' /R/R.test.txt")

有趣的是,您通常使用的sed命令是

sed 's/old text/new text/g' file > new.file

但是由于我需要替换的东西已经/带有正斜杠,所以我不得不使用+加号,这样sed不会引起混淆。

我将使用我的两个较旧的方法(上面已发布),新的sed方法以及他作为答案发布的F. Prive的方法进行性能测试。我将对整个数据集做一个较小的子集,因为测试这四种方法将花费很长时间。

编辑

因此,我测试了四种不同的方法,看看哪种方法最快。我创建了一个较小的文件来测试这四种方法。我创建的文件有1000000行和340列。

方法1

lookup_table <- c("0/0" = 0, "0/1" = 1, "1/1" = 2, "./." = 0.1)
df[-(1:2)] <- lapply(df[-(1:2)], function(x) lookup_table[x])

运行时-8分钟

方法2

replacement<-function(x){
  x=replace(x,which(x=='./.'),0.01) 
  x=replace(x,which(x=='0/0'),0)
  x=replace(x,which(x=='0/1'),1)
  x=replace(x,which(x=='1/1'),2)
}
df=apply(df,2,replacement)
df <- as.data.frame(df)

运行时-46秒

方法3

df <- df %>% mutate_at(
  vars(- CHROM, - POS),
  funs(case_when(
    . == "0/0" ~ 0,
    . == "0/1" ~ 1,
    . == "1/1" ~ 2,
    . == "./." ~ 0.01
  ))
)

运行时-42秒

方法4

df <- fread("sed -e 's+0/0+0+g' -e 's+0/1+1+g' -e 's+1/1+2+g' -e 's+./.+0.01+g' /R/R.test.txt")

运行时-2分34秒,这令人惊讶

结论-我浪费了时间