【问题标题】:Quickest way to read a subset of rows of a CSV读取 CSV 行子集的最快方法
【发布时间】:2014-10-04 07:44:51
【问题描述】:

我有一个包含 200 万行的 5GB csv。标题用逗号分隔strings,每一行用逗号分隔doubles,没有丢失或损坏的数据。它是矩形的。

我的目标是尽可能快地将随机 10% 的行(无论是否替换,都无关紧要)读入 RAM 。一个慢速解决方案(但比read.csv 快)的示例是使用fread 读取整个矩阵,然后随机保留10% 的行。

require(data.table)
X <- data.matrix(fread('/home/user/test.csv')) #reads full data.matix
X <- X[sample(1:nrow(X))[1:round(nrow(X)/10)],] #sample random 10%

但是我正在寻找最快的解决方案(这很慢,因为我需要先阅读整个内容,然后再进行修剪)。

值得奖励的解决方案将提供system.time() 对不同替代方案的估计。

其他:

  • 我使用的是 Linux
  • 我不需要正好 10% 的行。大约只有 10%。

【问题讨论】:

  • 您需要对 10% 的行进行采样多少次?
  • 您在 Linux 环境中工作吗? shuf -n N test.csv &gt; output.csv 应该适用于随机数量的观察 N 次。它似乎相当快。 stackoverflow.com/questions/9245638/…
  • 是不是 shuf 必须将整个内容读入内存(或内存映射),所以对于 5GB 文件来说很慢?值得一试...
  • 您需要恰好 10% 还是让每行有1/10 的概率是可以接受的?后者很容易使用 awk 命令行过滤器来完成,但可能不会给你正好 1/10 的行数。
  • @Spacedman 不完全是 10%。 1/10 的概率完全没问题。

标签: r performance csv io fread


【解决方案1】:

我认为这应该很快就会起作用,但请告诉我,因为我还没有尝试过使用大数据。

write.csv(iris,"iris.csv")

fread("shuf -n 5 iris.csv")

    V1  V2  V3  V4  V5         V6
1:  37 5.5 3.5 1.3 0.2     setosa
2:  88 6.3 2.3 4.4 1.3 versicolor
3:  84 6.0 2.7 5.1 1.6 versicolor
4: 125 6.7 3.3 5.7 2.1  virginica
5: 114 5.7 2.5 5.0 2.0  virginica

这会为iris 数据集抽取一个 N=5 的随机样本。

为避免再次使用标题行,这可能是一个有用的修改:

fread("tail -n+2 iris.csv | shuf -n 5", header=FALSE)

【讨论】:

  • 给我一个“找不到文件:shuf -n 5 iris.csv”。它寻找具有该名称的文件,而不是运行该命令并通过管道传输它。我有旧的 data.table 包吗?
  • @Spacedman 看着他的 MWE 的第一行,他正在保存到iris.csv 将它加载到一段他没有复制的代码中之后粘贴到示例中。
  • data.table 从 1.9.2 版开始才开始允许这样的语句(至少我相信是这种情况。我使用的是 data.table 1.9.3)
  • @user2763361 iris 不需要加载(因为它是基本 R 包的一部分)。它已经为所有人加载到环境中。
  • @Mike.Gahan 现在为我工作我已经升级了 data.table!
【解决方案2】:

这是一个包含 100000 行的文件,如下所示:

"","a","b","c"
"1",0.825049088569358,0.556148858508095,0.591679535107687
"2",0.161556158447638,0.250450366642326,0.575034103123471
"3",0.676798462402076,0.0854280597995967,0.842135070590302
"4",0.650981109589338,0.204736212035641,0.456373531138524
"5",0.51552157686092,0.420454133534804,0.12279288447462

$ wc -l d.csv 
100001 d.csv

所以这是 100000 行加上一个标题。如果从 0 到 1 的随机数大于 0.9,我们希望保留标题并采样每一行。

$ awk 'NR==1 {print} ; rand()>.9 {print}' < d.csv >sample.csv

检查:

$ head sample.csv 
"","a","b","c"
"12",0.732729186303914,0.744814146542922,0.199768838472664
"35",0.00979996216483414,0.633388962829486,0.364802648313344
"36",0.927218825090677,0.730419414117932,0.522808947600424
"42",0.383301998255774,0.349473554175347,0.311060158303007

它有 10027 行:

$ wc -l sample.csv 
10027 sample.csv

这在我的 4-yo 盒子上花费了 0.033 秒的实时时间,可能高清速度是这里的限制因素。它应该是线性缩放的,因为文件是严格逐行处理的。

然后,您可以根据需要使用read.csvfread 读入sample.csv

> s = fread("sample.csv")

【讨论】:

    【解决方案3】:

    您可以使用sqldf::read.csv.sql 和 SQL 命令来提取数据:

    library(sqldf)
    write.csv(iris, "iris.csv", quote = FALSE, row.names = FALSE) # write a csv file to test with
    read.csv.sql("iris.csv","SELECT * FROM file ORDER BY RANDOM() LIMIT 10")
       Sepal_Length Sepal_Width Petal_Length Petal_Width    Species
    1           6.3         2.8          5.1         1.5  virginica
    2           4.6         3.1          1.5         0.2     setosa
    3           5.4         3.9          1.7         0.4     setosa
    4           4.9         3.0          1.4         0.2     setosa
    5           5.9         3.0          4.2         1.5 versicolor
    6           6.6         2.9          4.6         1.3 versicolor
    7           4.3         3.0          1.1         0.1     setosa
    8           4.8         3.4          1.9         0.2     setosa
    9           6.7         3.3          5.7         2.5  virginica
    10          5.9         3.2          4.8         1.8 versicolor
    

    它不会为您计算 10%,但您可以选择返回的绝对行数限制。

    【讨论】:

      猜你喜欢
      • 2014-09-26
      • 2019-07-31
      • 2015-09-13
      • 1970-01-01
      • 2015-06-10
      • 1970-01-01
      • 2021-10-15
      • 2011-05-21
      • 2016-02-14
      相关资源
      最近更新 更多