如何在R中更高效地挑选和排序?

3

我在R中有一个包含9列和1600万行的矩阵a。第4列中的每个元素都是“Apple”或“Lion”。

我想把那些第4列中有“Apple”条目的行收集起来,并按照第一列中的整数条目排序,形成一个“新”的矩阵。

最好的方法是什么?输出将是一个.csv文件,因为在R中查看这个矩阵是不可能的(1600万行!)。

我试了两个代码:

1) 在这个代码中,首先我引入一个9维向量b,所有元素都是零。然后我运行一个for循环,检查a中的每一行是否第四个元素是“Apple”。如果是,那么这一行就被添加到b中,b也随之增长。最后,我使用order函数对b进行排序。

b=rep(0,9)
   for(i in 1:nrow(a)){
   if(a[i,4]=="Apple"){
      b=rbind(b,a[i,])
   }
}
b=order(b)
write.table(b,"Apple.csv",sep=",",append=TRUE,col.names=FALSE)

这段代码似乎无法正常工作,因为for循环运行时间过长!!

2) 我运行了一个for循环,检查 a 中每一行的第四个条目是否为 "Apple"。如果是,则将该行打印到 .csv 文件中。因此,在 R 中不会创建矩阵。在完成后,将读取该 .csv 文件,并使用 as.integer 函数将第一列转换为整数条目,再使用 order 函数对表格进行排序。

for(i in 1:nrow(a)){
   if(a[i,4]=="Apple"){
      write.table(a[i,],"Apple.csv",sep=",",append=TRUE,col.names=FALSE)
   }
}

a=read.csv("Apple.csv")
a[,1]=as.integer(a[,1])
a=order(a)

这似乎可以工作,尽管在我的笔记本电脑上速度很慢。按照目前的速度,for循环将需要5天才能完成。我不知道我的4GB内存的笔记本电脑是否本质上无法处理如此大的数据集。

有哪些方法可以优化代码?这个过程的最有效代码是什么?如果有人指点我正确的方向,我会很高兴。

2个回答

7

这里提供另一种使用流行包data.table的替代方案。DT类似于数据框,但功能更强大,设计更接近SQL。

library(data.table)

DT <- as.data.table(matrix(data = NA, nrow = 20000000, ncol = 9))
DT$V4 <- sample(c("Apple","Lion"), size = 20000000, replace = TRUE, prob = c(0.5, 0.5) )
DT$V9 <- rnorm(20000000, mean = 0, sd = 1000)

str(DT)

setkey(DT, V9)

system.time( r <- setorder( DT[V4 == "Apple"], V9 ) )
str(r)

tables()

在我的笔记本电脑上,这需要

user  system elapsed
2.35    0.30    2.64

内存使用

     NAME       NROW NCOL  MB COLS                       KEY
[1,] DT   20,000,000    9 916 V1,V2,V3,V4,V5,V6,V7,V8,V9 V9
[2,] r     9,998,016    9 420 V1,V2,V3,V4,V5,V6,V7,V8,V9 V9
Total: 1,336MB

链接: https://cran.r-project.org/web/packages/data.table/index.html 速查表: http://blog.datacamp.com/data-table-cheat-sheet/


6
你应该尝试使用 R 中专为此类工作设计的一些数据整理包。它们执行效率极高。以下我使用了 dplyr 包。
首先,我创建了一个由 2000 万行和 9 列组成的数据框。然后,我随机将第 4 列设置为 AppleLion。接着,我从以 0 为中心且具有较大标准差的正态分布中随机选择第 9 列(因此数字之间相对较远)。
我可以在不到 10 秒的时间内筛选出苹果并对其余的数据框进行排序。以下是代码。
> library(dplyr)
> 
> myDF <- as.data.frame(matrix(data = NA, nrow = 20000000, ncol = 9))
> myDF$V4 <- sample(c("Apple","Lion"), size = 20000000, replace=  TRUE, prob = c(0.5, 0.5) )
> myDF$V9 <- rnorm(20000000, mean = 0, sd = 1000)
> 
> system.time(AppleOnlySorted <- myDF %>%
+   filter(V4 == "Apple") %>%
+   arrange(V9))
   user  system elapsed 
  9.165   0.140   9.306 
> 
> head(AppleOnlySorted)
  V1 V2 V3    V4 V5 V6 V7 V8        V9
1 NA NA NA Apple NA NA NA NA -5053.535
2 NA NA NA Apple NA NA NA NA -4947.533
3 NA NA NA Apple NA NA NA NA -4853.408
4 NA NA NA Apple NA NA NA NA -4848.063
5 NA NA NA Apple NA NA NA NA -4838.298
6 NA NA NA Apple NA NA NA NA -4824.169
> 
> system.time(AppleOnlySortedDescending <- myDF %>%
+               filter(V4 == "Apple") %>%
+               arrange(desc(V9)))
   user  system elapsed 
  8.948   0.134   9.083 

我已经将命令包裹在一个 system.time 中,以展示其速度之快,但输出结果被保存在 AppleOnlySortedAppleOnlySortedDescending 中。然后,您可以将该数据框保存为 csv 或任何其他格式。
看一下 myDF 的大小只有839.2 Mb。因此,假设您的 RAM 中没有加载大量内存占用程序,那么应该没问题。
> format(object.size(myDF), units = "Mb")
[1] "839.2 Mb"

最坏的情况下,您总可以考虑使用bigmemory包将内容写入磁盘--但我怀疑您不需要这样做来完成此操作。希望这有所帮助!

我一定会使用这段代码!谢谢,我不知道这个包的存在!坦白地说,这是我第一次涉足大数据领域,之前我们所做的基本上都是R语言中的入门级别。 - Landon Carter
@LandonCarter:如果您喜欢这个答案,请通过点击旁边的复选标记将此问题标记为已回答。谢谢! - user1357015

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接