读取固定宽度文件中相同列的多个值

4
考虑以下几行来自Stata .dct文件的内容,该文件定义了Stata如何读取这个固定宽度的ASCII文件(可以在任何平台上使用任何ZIP软件进行解压缩):
start             type                            varname width  description
_column(24)       long                               rfv1   %5f  Patient's Reason for Visit #1            
_column(29)       long                               rfv2   %5f  Patient's Reason for Visit #2             
_column(34)       long                               rfv3   %5f  Patient's Reason for Visit #3             
_column(24)       long                             rfv13d   %4f  Patient's Reason for Visit #1 - broad     
_column(29)       long                             rfv23d   %4f  Patient's Reason for Visit #2 - broad     
_column(34)       long                             rfv33d   %4f  Patient's Reason for Visit #3 - broad     

基本上,此ASCII文件每行的第24到39个字符如下所示:
AAAAaBBBBbCCCCc

第一个宽泛的代码是 AAAA,同样的原因,更窄的代码是 AAAAa,以此类推。

换句话说,由于代码本身具有分层结构,在每一行中相同的字符会被读取两次以创建两个不同的变量。

相比之下,read.fwf 只需要一个 widths 参数,这种双重读取的情况就被排除了。

有没有一种标准的处理方法,可以避免通过手动扫描整个文件并逐个解析来重新创建轮子?

背景是我正在编写一个函数来解析这些 .DCT 文件,类似于 SAScii 的风格,如果我可以为每个变量指定 (start, width) 对而不仅仅是 widths,我的工作将会简单得多。


SAScii可以处理具有“重叠”列的定宽文件吗? - A5C1D2H2I1M1N2O1R2T1
@AnandaMahto 从 ?read.SAScii 中得知:"此函数无法处理重叠的列。" - Ari B. Friedman
2个回答

6
我曾开始编写一个 .DCT 文件解析器,但后来失去了动力。我的预期使用场景是简单地解析文件并创建一个 csvkit schema file,以便我可以使用 csvkit 将文件从固定宽度转换为 csv。为此,该软件包是成功的,但它非常不完善,并且只经过了极少量的测试。
需要注意的一些问题包括:(1) 并非所有 DCT 文件都具有相同的列;(2) 一些 DCT 文件具有对于隐式小数位的说明,我没有找到处理这些类型文件的方法。
您可以在 这里 找到该软件包的初始工作。
主要功能包括:
  • dct.parser -- 此函数的功能与其名称相符。它有一个 "preview" 参数,用于读取前几行以让您确定 DCT 文件是否具有您期望的所有列。
  • csvkit.schema -- 利用从 dct.parser 中提取的信息,创建了一个包含由 csvkit 的 in2csv 所需的相关列的 csv 文件。
  • csvkit.fwf2csv -- 基本上是对 csvkit 的 system 调用。也可以在 R 之外完成。

对于您的特定示例,我成功地使用以下方式进行了读取:

## The extracted data file and the DCT file are in my downloads directory
setwd("~/Downloads/") 
dct.parser("ed02.dct", preview=TRUE) ## It seems that everything is there
temp <- dct.parser("ed02.dct")       ## Can be used as a lookup table later

## The next line automatically creates a csv schema file in your 
##   working directory named, by default, "your-dct-filename.csv"
csvkit.schema(temp) 
csvkit.fwf2csv(datafile = "ED02", schema="ed02.dct.csv", output="ED02.csv")

## I haven't set up any mechanism to check on progress...
## Just check the directory and see when the file stops growing :)
ED02 <- read.csv("ED02.csv")

我原本打算尝试另一种方法(但最终没有去做),即使用paste构建substr命令,可以被sqldf用来读取包含重叠数据的列的数据。请参阅这篇博客文章以获取入门示例。


更新:一个sqldf示例

如上所述,sqldf可以很好地利用dct.parser的输出,并通过使用substr读取您的数据。以下是如何执行此操作的示例:

## The extracted data file and the DCT file are in my downloads directory
setwd("~/Downloads/") 
temp <- dct.parser("ed02.dct")       ## Can be used as a lookup table later

## Construct your "substr" command
GetMe <- paste("select", 
               paste("substr(V1, ", temp$StartPos, ", ",
                     temp$ColWidth, ") `", temp$ColName, "`", 
                     sep = "", collapse = ", "), 
               "from fixed", sep = " ")

## Load "sqldf"
library(sqldf)

fixed <- file("ED02")
ED02 <- sqldf(GetMe, file.format = list(sep = "_"))
dim(ED02)
# [1] 37337   260

可以看到,在sqldf行中需要进行一些修改。特别是,由于sqldf使用read.csv.sql,它将数据中的任何逗号字符视为分隔符。您可以将其更改为您不希望在数据中出现的内容。

顺便说一下,我选择专注于“csvkit”,因为它在执行转换时非常快速,并且最终生成的方便的标准格式csv文件可以与其他人共享(他们可能不使用R)(对他们感到惋惜!) - A5C1D2H2I1M1N2O1R2T1
这太棒了。谢谢。我用scansubstr凑合着弄出了点东西,但是这个看起来更完整、更成熟。 - Ari B. Friedman
我怀疑 sqldf 方法可以处理重叠读取。 - IRTFM
@DWin,我在原始答案中已经提到了这一点。已经进行了编辑以展示如何完成它。 - A5C1D2H2I1M1N2O1R2T1
@AriB.Friedman,substr 不是一个不合理的方法。我已经展示了如何在 sqldf 中使用它,并且速度相当快。 - A5C1D2H2I1M1N2O1R2T1

1
这篇文章只是最近被标记为Stata相关(感谢@Metrics),因此可能还没有被很多Stata爱好者注意到。
从纯粹的Stata角度来看,似乎很容易读入每个5位数的long变量,然后通过例如...提取前4位数字。
. gen rvf13d = floor(rvf13/10) 

或者将这些代码作为字符串读取,然后保留HTML标签。
. gen rvf13d = substr(rvf13, 1, 4) 

所以,您永远不需要两次读取相同的数据。
话虽如此,这似乎与问题无关,其中给出了字典文件,您不想手动编辑其中的几个。

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