读取多个CSV文件,这些文件在文件开头有不同数量的行需要跳过。

12

我需要阅读大约300个单独的CSV文件。我已经成功地使用循环和结构化的CSV文件名自动化了这个过程。但是,每个CSV文件开头都有14-17行无用的信息,并且随机变化,因此在read.table命令中硬编码一个“跳过”参数是行不通的。每个CSV的列名和列数都相同。

以下是我要处理的示例:

QUICK STATISTICS:

      Directory: Data,,,,
           File: Final_Comp_Zn_1
      Selection: SEL{Ox*1000+Doma=1201}
         Weight: None,,,
     ,,Variable: AG,,,

Total Number of Samples: 450212  Number of Selected Samples: 277


Statistics

VARIABLE,Min slice Y(m),Max slice Y(m),Count,Minimum,Maximum,Mean,Std.Dev.,Variance,Total Samples in Domain,Active Samples in Domain AG,  
6780.00,   6840.00,         7,    3.0000,   52.5000,   23.4143,   16.8507,  283.9469,        10,        10 AG,   
6840.00,   6900.00,         4,    4.0000,    5.5000,    4.9500,    0.5766,    0.3325,        13,        13 AG,   
6900.00,   6960.00,        16,    1.0000,   37.0000,    8.7625,    9.0047,   81.0848,        29,        29 AG,   
6960.00,   7020.00,        58,    3.0000,   73.5000,   10.6931,   11.9087,  141.8172,       132,       132 AG,   
7020.00,   7080.00,        23,    3.0000,  104.5000,   15.3435,   23.2233,  539.3207,        23,        23 AG,   
7080.00,   7140.00,        33,    1.0000,   15.4000,    3.8152,    2.8441,    8.0892,        35,        35 AG,
基本上我想从行VARIABLE,Min slice Y(m),Max slice Y(m)...开始读取。我能想到几种解决方案,但不知道如何编程实现。有没有什么方法可以:
  1. 先读取CSV,并找出有多少行垃圾数据,然后重新读取并指定正确的跳过行数?或者
  2. 告诉read.table在找到列标题时开始读取(因为每个CSV的列标题都相同),并忽略此之前的所有内容?
我认为解决方案(2)可能是最合适的,但我也接受任何建议!
2个回答

17

这里是采用一种可能的最简方法的示例。

首先,让我们制作一些类似于您描述的 csv 文件:

cat("blah\nblah\nblah\nVARIABLE,X1,X2\nA,1,2\n", file="myfile1.csv")
cat("blah\nVARIABLE,A1,A2\nA,1,2\n", file="myfile2.csv")
cat("blah\nblah\nVARIABLE,Z1,Z2\nA,1,2\n", file="myfile3.csv")

其次,确定数据开始的位置:

linesToSkip <- sapply(list.files(pattern = "myfile.*.csv"), 
                      function(x) grep("^VARIABLE", readLines(x))-1)

第三步,使用该信息将您的文件读入单个列表中。

lapply(names(linesToSkip), 
       function(x) read.csv(file=x, skip = linesToSkip[x]))
# [[1]]
#   VARIABLE X1 X2
# 1        A  1  2
# 
# [[2]]
#   VARIABLE A1 A2
# 1        A  1  2
# 
# [[3]]
#   VARIABLE Z1 Z2
# 1        A  1  2

编辑 #1

另一种避免读取两次数据的方法是将其读取到列表中,然后执行相同类型的处理:

myRawData <- lapply(list.files(pattern = "myfile.*.csv"), readLines)
lapply(myRawData, function(x) {
  linesToSkip <- grep("^VARIABLE", x)-1
  read.csv(text = x, skip = linesToSkip)
})

或者,同样重要的是:

lapply(list.files(pattern = "myfile.*.csv"), function(x) {
  temp <- readLines(x)
  linesToSkip <- grep("^VARIABLE", temp)-1
  read.csv(text = temp, skip = linesToSkip)
})

编辑 #2

正如@PaulHiemstra所指出的那样,您可以使用参数n仅将每个文件的少数行读入内存,而不是读取整个文件。因此,如果您确定每个文件中不会超过20行“垃圾”,并且使用了第一种方法,您可以使用以下代码:

linesToSkip <- sapply(list.files(pattern = "myfile.*.csv"), 
                      function(x) grep("^VARIABLE", readLines(x, n = 20))-1)

1
+1,虽然您正在两次阅读每个内容。您可以在对readlLines的调用中添加“n = 1000”以限制要读取的数据量,以确定需要跳过的标题。这样,较大的CSV文件不会被读取两次,而只会读取相关部分两次。这个1000行的边界有点武断,但似乎作为一个超过100行的标题有些奇怪。 - Paul Hiemstra
@PaulHiemstra,谢谢。您知道在R中从list读取是否比从磁盘读取更快的read.csv吗?考虑到OP问题中的细节,添加n = 20可能就足够了。 - A5C1D2H2I1M1N2O1R2T1
我不确定...但是先将数据读入内存,然后执行所有操作,而不是从磁盘中两次读取数据应该会更快。虽然只读取前20行,然后再读取所有内容也应该能够正常执行。 - Paul Hiemstra

9

来自data.table包的fread函数可以自动检测需要跳过多少行。该函数目前处于开发阶段。

以下是示例代码:

require(data.table)

cat("blah\nblah\nblah\nVARIABLE,X1,X2\nA,1,2\n", file="myfile1.csv")
cat("blah\nVARIABLE,A1,A2\nA,1,2\n", file="myfile2.csv")
cat("blah\nblah\nVARIABLE,Z1,Z2\nA,1,2\n", file="myfile3.csv")

lapply(list.files(pattern = "myfile.*.csv"), fread)

1
你能否添加一个代码示例?目前你的回答相当简短。 - Paul Hiemstra
@PaulHiemstra,已添加代码示例。感谢您的建议! - djhurio
1
@PaulHiemstra,请查看fread的文档。在详细信息中有一些描述。 - djhurio

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