大文件的逐行操作
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 列的类)不是问题。使用fread
的skip
和nrows
参数编写一个以块为单位处理文件的循环相当容易。由于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()
的任何列引用向量
请注意,如果将上述内容与%>%
管道一起使用,则需要在代码顶部使用library(magrittr)
来加载函数。
【讨论】:
这条线output.file <- 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 语言文件操作 ( 配置文件读写 | 读取配置文件 | 函数接口形参 | 读取配置文件的逐行遍历操作 | 读取一行文本 | 查找字符 | 删除字符串前后空格 )