大文件的逐行操作

Posted

技术标签:

【中文标题】大文件的逐行操作【英文标题】:Row-wise Manipulation of Large Files 【发布时间】:2017-07-01 05:55:18 【问题描述】:

所以我有一个包含大约 280 列和 10 亿数据的大型 CSV 文件,文件大小约为 20GB。下面提供了此文件的示例(大约 7 列和 4 行):

SL No.,Roll No.,J_Date,F_Date,S1,S2,S3
1,00123456789,2004/09/11,2009/08/20,43,67,56
2,987654321,2010/04/01,2015/02/20,82,98,76
3,0123459876,2000/06/25,2005/10/02,72,84,02
4,000543216789,1990/08/29,1998/05/31,15,64,82

现在鉴于文件如此之大,我将不得不一次以较小的块读取此文件,并且我能够指定块大小。但正如您可能从样本中看到的那样,“卷号”。必须读作“字符”而不是“数字”。我还需要添加列“S1”、“S2”、“S3”并将总和写入新列“MM”

上述示例的输出必须是这样的:

SL No.,Roll No.,J_Date,F_Date,S1,S2,S3,MM
1,00123456789,2004/09/11,2009/08/20,43,67,56,166
2,987654321,2010/04/01,2015/02/20,82,98,76,256
3,0123459876,2000/06/25,2005/10/02,72,84,02,158
4,000543216789,1990/08,29,1998/05/31,15,64,82,161

我知道以前有人问过类似的问题,但我发誓我找不到 1 个对我有用的答案。我提到了以下问题:

R:Loops to process large dataset(GBs) in chunks?

Trimming a huge (3.5 GB) csv file to read into R

How do i read only lines that fulfil a condition from a csv into R?

Reading numbers as strings

Read numeric input as string R 还有很多。

这可能是说我在 R 方面完全是初学者的好时机,因此非常感谢各种帮助。我已经坐了很长时间了。

【问题讨论】:

您只有求和而不是相乘的预期输出。你可以做Reduce('+', df1[5:7])rowSums(df1[5:7]) 如果您使用包 data.table 中的fread,您的第二个问题(第 2 列的类)不是问题。使用freadskipnrows 参数编写一个以块为单位处理文件的循环相当容易。由于fwrite 可以append 输出到文件也应该不是问题。 @akrun : 你能详细说明一下吗,我对 R 很陌生。 @Roland:请详细说明。我对 R 还很陌生,需要更多帮助。 如果你想得到5:7列的总和,那么df1$MM <- rowSums(df1[5:7])请在你的数据集上试试 【参考方案1】:

我不能说我自己以前做过,但我认为这应该可行。

library( data.table )

# set the input and output files
input.file <- "foo.csv"
output.file <- sub( "\\.csv$", "_output\\.csv", input.file )

# get column names by importing the first few lines
column.names <- names( fread( input.file, header = TRUE, nrows = 3L ) )

# write those column names as a line of text (header)
cat( paste( c( column.names, "MM" ), collapse = "," ),
     file = output.file, append = FALSE )
cat( "\n", file = output.file, append = TRUE )

# decide how many rows to read at a time
rows.at.a.time <- 1E4L

# begin looping
start.row <- 1L
while( TRUE ) 

    # read in only the specified lines
    input <- fread( input.file,
                    header = FALSE,
                    skip = start.row,
                    nrows = rows.at.a.time
    )

    # stop looping if no data was read
    if( nrow( input ) == 0L ) break

    # create the "MM" column
    input[ , MM := rowSums( .SD[ , 5:7 ] ) ]

    # append the data to the output file
    fwrite( input,
            file = output.file,
            append = TRUE, col.names = FALSE )

    # bump the `start.row` parameter
    start.row <- start.row + rows.at.a.time

    # stop reading if the end of the file was reached
    if( nrow( input ) < rows.at.a.time ) break


更新:为了保留字符串,您可以通过在循环内的 fread 调用中指定将所有列作为字符导入:

colClasses = rep( "character", 280 )

然后,要执行行求和(因为您现在拥有所有字符列),您需要在其中包含一个转换。以下将替换代码中的单行(上面有相同注释的那一行):

# create the "MM" column
input[ , MM := .SD[ , 5:7 ] %>%
           lapply( as.numeric ) %>%
           do.call( what = cbind ) %>%
           rowSums()
       ]

这里指定了5:7,您可以替换为要传递给rowSums()的任何列引用向量

请注意,如果将上述内容与%&gt;% 管道一起使用,则需要在代码顶部使用library(magrittr) 来加载函数。

【讨论】:

这条线output.file &lt;- sub( "\\$.csv", "_output\\.csv", file ) 给了我错误Error in as.character(x) : cannot coerce type 'closure' to vector of type 'character'。请帮忙 此解决方案是否也将第二列作为字符读取? 您需要将其添加到循环内的fread 调用中。见我刚刚添加的colClasses参数。 正如我在问题中提到的,输入文件有大约 280 列,因此手动为所有 280 列提供类将非常非常困难。您认为您可以建议另一种方法吗? @Zaire 没有理由让它成为角色。 fread 如果不能表示为整数,则将其读取为 integer64。

以上是关于大文件的逐行操作的主要内容,如果未能解决你的问题,请参考以下文章

C 语言文件操作 ( 配置文件读写 | 读取配置文件 | 函数接口形参 | 读取配置文件的逐行遍历操作 | 读取一行文本 | 查找字符 | 删除字符串前后空格 )

关于C语言中文本文件的逐行读取的实现

Python中的逐行远程数据传输

在简单的逐行计算任务中,为啥犰狳与 C 风格的数组相比如此缓慢

使用 SparkR 的逐行计算

Numpy 中的逐行索引