【问题标题】:Fastest way to replace multiple values in large data.frame在大型data.frame中替换多个值的最快方法
【发布时间】:2019-09-12 20:20:47
【问题描述】:

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

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 可能是最快的方法?或者也许将数据框转换为矩阵?我相信你的想法。

提前致谢!

【问题讨论】:

  • 小时?好奇你的数据框有多大。这可能有助于确定最合适的解决方案。
  • 如果这需要很长时间,可能会在文件末尾添加字典或任何映射,并使用 notepad++、perl、awk 甚至 sed 进行正则表达式替换。然后在将文件替换为 R 后读取文件。或者如果您可以将 R 中的整个数据框折叠成一行,例如do.call(paste, c(df,collapse='\n')),然后确定大小并查看这是否是可管理的对象。使用正则表达式替换然后转换回数据框
  • 呵呵,这个文件很大。使用命令窗口。
  • 你在用windows吗?
  • 你安装了 sed 吗?只需在命令窗口中输入 sed 看看会发生什么,甚至输入 perl。您也可以查看this link,尽管不要使用-i 选项,因为这是一个就地选项。仅当您确定结果时才这样做,否则您将丢失原始文件

标签: r performance dataframe


【解决方案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 列。

方法一

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 分钟

方法二

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 秒

方法三

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 秒,令人惊讶

结论 - 我浪费了我的时间

【讨论】:

  • 仅供参考,fread 不需要末尾的“> newname”(除非您想创建一个新的 txt 文件),它将直接读取输出,而无需在操作系统级别。
  • 我实际上在使用fread 时遇到了一些奇怪的错误。我将就此发表一篇不同的文章,因为我认为它与原始问题不同。如果您有兴趣,我会为您添加链接
  • 如果您在最后删除 "> newfilename" 是否还会出现错误?
  • 当我删除 &gt; newfilename 时,输出在终端窗口中。它不会改变实际文件本身。所以在我运行 fread("sed 's+0/0+0+g' R.test.txt') 然后执行 head R.test.txt 之后,什么都没有改变。但是,由于输出在终端窗口,我可以看到它确实改变了我想要的,但没有改变文件...文件有意义吗?
  • stackoverflow.com/questions/57928576/…这是我刚刚发的帖子
【解决方案2】:

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

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

【讨论】:

  • 这也有效!我要做一个性能测试,看看哪种方法最快!感谢您发布您的答案!!感谢您花时间回答我的问题。
猜你喜欢
  • 2011-11-06
  • 1970-01-01
  • 1970-01-01
  • 2012-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多