读入格式复杂的文件并整理

时间:2016-02-08 19:04:35

标签: r file-io dataframe

我想将具有复杂格式的文件读入数据框或数据表。我简化了格式以获得最简单的例子,它仍然可以传达真实案例的所有复杂性。

TITLE = "SomeTitleHere"
VARIABLES = "n","q[m3/hr]","gf[-]","pe[bar]","eff[%]",
ZONE DATAPACKING=POINT T="Design GF= 0.000 Q= 818.96 rpm=4800.",I=  4
  0    818.96002      0.00000      13.00000    
     61.92762
  1    818.96002      0.00000      29.86776    
     61.92762
 ZONE DATAPACKING=POINT T="Offdesign GF= 0.000 Q= 200.00 rpm=4800.",I=  4
  0    200.00000      0.00000      13.00000    
      0.00000
  1    200.00000      0.00000      37.79360    
     27.12768
 ZONE DATAPACKING=POINT T="Offdesign GF=  0.000 Q=1200.00 rpm=4800.",I=  4
  0   1200.00000      0.00000      13.00000
      0.00000
  1   1200.00000      0.00000      17.17662
     28.08889
 ZONE DATAPACKING=POINT T="Offdesign GF=  0.100 Q= 200.00 rpm=4800.",I=  4
  0    200.00000      0.10000     13.00000
      0.00000
  1    188.40880      0.04463      30.91997
     22.54672
 ZONE DATAPACKING=POINT T="Offdesign GF= 0.100 Q=1200.00 rpm=4800.",I=  4
  0   1200.00000      0.10000    13.00000    
      0.00000
  1   1177.85608      0.08308     15.94177
     13.05620

格式说明:第一行(TITLE = "SomeTitleHere")是某种评论,可以跳过。第二行包含一些变量名称及其度量单位的前缀。由于我知道哪些是变量的名称,因此也可以跳过此行。 然后,有2 * n + 1“个数据块”。每个数据块长5行:第一个是标题行,其中包含四个变量的值PointGFinQinrpm(因此必须被解析)。例如,对于第一个块,标题行是

ZONE DATAPACKING=POINT T="Design GF= 0.000 Q= 818.96 rpm=4800.",I=  4

对应

      Point GFin     Qin  rpm
     Design  0.0  818.96 4800

然后,我有4行没有字符串的数字/整数数据。 4行实际上对应于2行实际数据,因为偶数行实际上是奇数行的最后一行!它们包含八个变量的值q1q2GF1GF2pe1pe2eff1eff2。换句话说,第一个数据块(样本文件中的第5-7行)

ZONE DATAPACKING=POINT T="Design GF= 0.000 Q= 818.96 rpm=4800.",I=  4
  0    818.96002      0.00000      13.00000    
     61.92762
  1    818.96002      0.00000      29.86776    
     61.92762

对应于数据框中的以下条目

  Point GFin     Qin  rpm     q1     q2 GF1 GF2 pe1      pe2     eff1     eff2
 Design  0.0  818.96 4800 818.96 818.96   0   0  13 29.86776 61.92762 61.92762

应用相同的逻辑,对应于上述输入文件的最终数据帧应为

> df
      Point GFin     Qin  rpm      q1        q2 GF1     GF2 pe1      pe2     eff1     eff2
1    Design  0.0  818.96 4800  818.96  818.9600 0.0 0.00000  13 29.86776 61.92762 61.92762
2 OffDesign  0.0  200.00 4800  200.00  200.0000 0.0 0.00000  13 37.79360  0.00000 27.12768
3 OffDesign  0.0 1200.00 4800 1200.00 1200.0000 0.0 0.00000  13 17.17662  0.00000 28.08889
4 OffDesign  0.1  200.00 4800  200.00  188.4088 0.1 0.04463  13 30.91997  0.00000 22.54672
5 OffDesign  0.1 1200.00 4800 1200.00 1177.8561 0.1 0.08308  13 15.94177  0.00000 13.05620

如何从输入文件转到此数据框,最大限度地降低人工干预的级别?

PS当然,真实文件有数千个数据块,每个数据块有更多变量。这只是一个简单的例子。

编辑我按用户的建议读了readLines,我到了这里(TestFile是我在问题开头提供的文件):

# read test file TestFile.dat

# clear the workspace
rm(list=ls())
gc()
graphics.off()

# read full file
directory = "../test/"
filename = "TestFile.dat"
fullpath = paste0(directory,filename)
s = readLines(fullpath) # looks like R can easily read in one sweep even my original file, which has more than 60000 lines. Great!!!

# remove TITLE line and VARIABLES line
s=s[-2:-1]

# how many data points?
nstages = 1
nlines = 2*(nstages+1)+1
npoints = length(s)/nlines

# parser function
parse_point <- function(x) {}

# lapply the parser function to s
data_list=lapply(s,parse_point)    

# merge the list of data frames data_list in a single data frame
data=do.call("rbind",data_list)

我认为lapply + do.call技巧很简洁,并且省去了for的缓慢。问题是不知道如何编写lapply可以处理的解析器函数!基本上,lapply一次将parse_point应用于s的一个元素。这不行:我需要一次解析s的5个元素,即数据块:

ZONE DATAPACKING=POINT T="Design GF= 0.000 Q= 818.96 rpm=4800.",I=  4
  0    818.96002      0.00000      13.00000    
     61.92762
  1    818.96002      0.00000      29.86776    
     61.92762

有什么建议吗?我不需要一个完整的解决方案,只需要继续提示。然后我可以继续改进解决方案。

编辑2:将我暂时放在一边,因为我无法lapply parse_point,我试图专注于parse_point。 Andddd ...太棒了!我现在至少可以解析一个数据块:

library(stringr) 
index = 1
split_text_line = strsplit(s[index],split=" +")[[1]]
Point = str_sub(split_text_line[4],4)
GFin = as.numeric(split_text_line[6])
Qin =  as.numeric(split_text_line[8])
rpm = as.numeric(str_extract(split_text_line[9],"[:digit:]+"))
index = index + 1
split_text_line = strsplit(s[index],split=" +")[[1]]
q1 = split_text_line[3]
GF1 = split_text_line[4]
pe1 = split_text_line[5]
index = index + 1
eff1 = as.numeric(str_trim(s[index]))
index = index + 1
split_text_line = strsplit(s[index],split=" +")[[1]]
q2 = split_text_line[3]
GF2 = split_text_line[4]
pe2 = split_text_line[5]
index = index + 1
eff2 = as.numeric(str_trim(s[index]))
df = data.frame(Point=Point, GFin=GFin, Qin=Qin, rpm=rpm, q1=q1, q2=q2,
                GF1=GF1, GF2=GF2, pe1=pe1, pe2=pe2, eff1=eff1, eff2=eff2)

其中s是上面脚本生成的字符向量。但是,我仍然有将此解析算法应用于所有数据块的问题。我可以使用for循环来完成它,但是没有更快的方法吗?

1 个答案:

答案 0 :(得分:1)

你需要在这里稍微思考一下。您有一个文本行列表,您希望一次处理5个文本行。因此,将lapply索引列表传递到数据列表

lapply(seq(1,length(s), 5), function (x) { parse_point(s[x:x+4]) })

这将在源文件中为每组5行调用parse_point

您还可以修改parse_point以获取数组索引x而不是行列表。那就是

lapply(seq(1,length(s), 5), parse_point)

您可能需要unlist lapply的结果,或者考虑使用sapply