我想将具有复杂格式的文件读入数据框或数据表。我简化了格式以获得最简单的例子,它仍然可以传达真实案例的所有复杂性。
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行:第一个是标题行,其中包含四个变量的值Point
,GFin
,Qin
和rpm
(因此必须被解析)。例如,对于第一个块,标题行是
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行实际数据,因为偶数行实际上是奇数行的最后一行!它们包含八个变量的值q1
,q2
,GF1
,GF2
,pe1
,pe2
,eff1
和eff2
。换句话说,第一个数据块(样本文件中的第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
循环来完成它,但是没有更快的方法吗?
答案 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
。