10

我正在使用 R 将一些大型文本文件读入数据库,但它们包含数据库软件的非法字段名称。大文本文件的列名就在第一行——是否可以只编辑第一行而不循环遍历文件中的每一行(这似乎浪费资源)?

这里有两个例子,我试图用一些示例数据做些什么。第一个将所有内容读入 ram - 所以这不适用于我的大型数据表。第二个可以工作,但速度很慢,因为它处理文件中的每一行。

我认为解决方案跨平台工作并且不需要安装外部软件(除了 R 包)是很重要的,因为我将与其他人共享这个脚本,并且不想让他们执行更多不必要的步骤。我正在寻找仅在 R中执行此操作的最快方法 :)

# create two temporary files
tf <- tempfile() ; tf2 <- tempfile()

# write the mtcars data table to a file on the disk
write.csv( mtcars , tf )

# look at the first three lines
readLines( tf , n = 3 )

# read in the entire table
z <- readLines( tf )

# make the only substitution i care about
z[1] <- gsub( 'disp' , 'newvar' , z[1] )

# write the entire table back out to the table
writeLines( z , tf2 )

# confirm the replacement
readLines( tf2 , 2 )
# done!

# # # # # # # OR

# blank out the output file
file.remove( tf2 )

# create a file connection to the text file
incon <- file( tf , "r" )

# create a second file connection to the secondary temporary file
outcon <- file( tf2 , "w" )

# read in one line at a time
while( length( one.line <- readLines( incon , 1 ) ) > 0 ){

    # make the substitution on every line
    one.line <- gsub( 'disp' , 'newvar' , one.line )

    # write each line to the second temporary file
    writeLines( one.line , outcon )
}

# close the connections
close( incon ) ; close( outcon )

# confirm the replacement
readLines( tf2 , 2 )
# done!
4

3 回答 3

7

您为此使用了错误的工具。改用一些命令行工具。例如 using sed, smth likesed -i '1 s/disp/newvar/' file应该这样做。如果您必须在 R 中执行此操作,请使用

filename = 'myfile'
scan(pipe(paste("sed -i '1 s/disp/newvar/' ", filename, sep = "")))

这是一个特定于 Windows 的版本:

filename = 'myfile'
tf1 = tempfile()
tf2 = tempfile()

# read header, modify and write to file
header = readLines(filename, n = 1)
header = gsub('disp', 'newvar', header)
writeLines(header, tf1)

# cut the rest of the file to a separate file
scan(pipe(paste("more ", filename, " +1 > ", tf2)))

# append the two bits together
file.append(tf1, tf2)

# tf1 now has what you want
于 2013-04-08T18:21:45.070 回答
5

为什么不只编辑标题,然后分块阅读其余部分?我不知道这个文件有多大,但可能是几行(我猜是 10000)。根据您有多少内存,您可以将其调整为更大或更小。

##setup
tf <- tempfile(); tf2 <- tempfile()
write.csv(mtcars,tf)

fr <- file(tf, open="rt") #open file connection to read
fw <- file(tf2, open="wt") #open file connection to write 
header <- readLines(f,n=1) #read in header
header <- gsub( 'disp' , 'newvar' , header) #modify header    
writeLines(header,con=fw) #write header to file
while(length(body <- readLines(fr,n=10000)) > 0) {
  writeLines(body,fw) #pass rest of file in chunks of 10000
}
close(fr);close(fw) #close connections
#unlink(tf);unlink(tf2) #delete temporary files

它应该更快,因为 R 将while每 10000 行而不是每行运行一次循环。此外,R 将只调用gsub您想要的行,而不是每一行,从而节省您的 R 时间。R 不能“就地”编辑文件,可以这么说,因此无法读取和复制文件。如果您必须在 R 中执行此操作,则使您的块与内存允许的一样大,然后通过您的文件。

我看到这两种方式之间有 3 倍的性能差异:

#test file creation ~3M lines
tf <- tempfile(); tf2 <- tempfile()
fw <- file(tf,open="wt")
sapply(1:1e6,function(x) write.csv(mtcars,fw))
close(fw)

#my way
system.time({
fr <- file(tf, open="rt") #open file connection to read
fw <- file(tf2, open="wt") #open file connection to write 
header <- readLines(f,n=1) #read in header
header <- gsub( 'disp' , 'newvar' , header) #modify header    
writeLines(header,con=fw) #write header to file
while(length(body <- readLines(fr,n=10000)) > 0) {
  writeLines(body,fw) #pass rest of file in chunks of 10000
}
close(fr);close(fw) #close connections
})    
#   user  system elapsed 
#  32.96    1.69   34.85 

#OP's way
system.time({
incon <- file( tf , "r" )
outcon <- file( tf2 , "w" )
while( length( one.line <- readLines( incon , 1 ) ) > 0 ){
    one.line <- gsub( 'disp' , 'newvar' , one.line )
    writeLines( one.line , outcon )
}
close( incon ) ; close( outcon )
})
#   user  system elapsed 
# 104.36    1.92  107.03 
于 2013-04-08T21:35:10.443 回答
-1

你有没有尝试过:

iocon <- file("originalFile","r+")
header <- readLines(iocon,n=1)
header <- gsub('disp', 'newvar', header)
writeLines(header, con=iocon)

这只会覆盖第一行,并且取决于它管理系统资源的方式可能非常有效。一定要有备份。

于 2015-05-11T06:54:06.370 回答