读取固定宽度文件中相同列的倍数

Posted

技术标签:

【中文标题】读取固定宽度文件中相同列的倍数【英文标题】:Read multiples of same columns in a fixed width file 【发布时间】:2013-04-23 15:03:51 【问题描述】:

考虑a Stata .dct file 中的以下几行,它为Stata 定义了如何读取this fixed width ASCII file(可以使用任何平台上的任何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 参数,从而排除了这种类型的双重读取。

有没有一种标准的方法来处理这个问题,而不需要 scanning 在整个文件中从头开始重新创建***并手动解析它?

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

【问题讨论】:

SAScii 是否处理具有“重叠”列的固定宽度文件? @AnandaMahto From ?read.SAScii: "这个函数不能处理重叠的列。" 【参考方案1】:

我已经开始研究 .DCT 解析器,但失去了动力。我的预期使用场景实际上是简单地解析文件并创建一个csvkitschema file,以允许我使用 csvkit 将文件从固定宽度转换为 csv。为此,该软件包是成功的,但它非常不完善,仅经过极少的测试。

需要注意的几个问题包括 (1) 并非所有 DCT 文件都有相同的列; (2) 一些 DCT 文件有 implicit 小数位的指令,而我从来没有想出处理这些类型文件的方法。

你可以在包here上找到初始工作。

主要功能有:

dct.parser -- 符合您的预期。前几行有一个“预览”参数,可让您确定 DCT 文件是否包含您期望的所有列。 csvkit.schema -- 使用从dct.parser 提取的信息,创建一个csv 文件,其中包含来自csvkit 的in2csv 所需的相关列。 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 可以使用这些命令来读取列包含重叠数据的数据。请参阅 this blog post 以获取入门示例。


更新: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)(对他们感到羞耻!) 这太棒了。谢谢。我曾使用 scansubstr 将一些东西拼凑在一起,但这看起来更加成熟。 我怀疑sqldf 方法可以处理重叠读取。 @DWin,我已经在我的原始答案中提到了这一点。已编辑以显示如何完成。 @AriB.Friedman, substr 不是不合理的做法。我已经展示了如何将它与sqldf 一起使用,而且速度相当快。【参考方案2】:

这只是刚刚用 Stata 标记(感谢@Metrics),所以可能很多 Stata 爱好者都没有注意到。

从纯 Stata 的角度来看,读取每个 5 位 long 变量似乎很简单,然后通过例如提取前 4 位数字。

. gen rvf13d = floor(rvf13/10) 

或将这些代码作为字符串读入,然后

. gen rvf13d = substr(rvf13, 1, 4) 

因此,您永远不需要两次读取相同的数据。

也就是说,这似乎与问题无关,其中给出了字典文件,而您不想手动编辑几个文件中的每一个。

【讨论】:

以上是关于读取固定宽度文件中相同列的倍数的主要内容,如果未能解决你的问题,请参考以下文章

如何从R中的大型固定宽度文件中读取特定列

使用 pyspark 中 json 文件中的模式读取固定宽度文件

读取固定宽度文件的更快方法

在 R 中跨多行的固定宽度文件中读取观察结果

读取固定宽度格式,其中宽度是从列标题中推断出来的

如何在 R 中使用 laf_open_fwf 读取固定宽度文件时管理恶意数据行