读取固定宽度文件中相同列的倍数
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
参数,从而排除了这种类型的双重读取。
有没有一种标准的方法来处理这个问题,而不需要 scan
ning 在整个文件中从头开始重新创建***并手动解析它?
这里的背景是我正在编写一个函数来解析这些 .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)(对他们感到羞耻!) 这太棒了。谢谢。我曾使用scan
和 substr
将一些东西拼凑在一起,但这看起来更加成熟。
我怀疑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)
因此,您永远不需要两次读取相同的数据。
也就是说,这似乎与问题无关,其中给出了字典文件,而您不想手动编辑几个文件中的每一个。
【讨论】:
以上是关于读取固定宽度文件中相同列的倍数的主要内容,如果未能解决你的问题,请参考以下文章