直接从R中读取.dat和.dct文件

6

我需要使用 .dct 文件来读取 .dat 文件。有没有人在 R 中做过这个操作?

文件的格式如下:

dictionary {
  # how many lines per record
  _lines(1)
  # start defining the first line
  _line(1)

  # starting column / storage type / variable name / read format / variable label
  _column(1)    str8    aid    %8s    "respondent identifier"
  ...
}

“读取格式”就像以下这些:
%2f        2 column integer variable
%12s      12 column string variable
%8.2f      8 column number with 2 implied decimal places. 

存储类型可在此处查看:http://www.stata.com/help.cgi?datatypes 其他信息来源网站: http://library.columbia.edu/indiv/dssc/technology/stata_write.html http://www.stata.com/support/faqs/data-management/reading-fixed-format-data/ .dat文件是一堆数字,对应于.dct文件中指定的变量。(这可能是固定宽度列中的数据)。
以下是一个真实的例子:
.dtc文件 http://goo.gl/qHZOk 数据 http://goo.gl/FRGRF 来自Stata网站的一个具体示例是:
.dat文件(在这个实例中为“test.raw”)
C1245A101George Costanza
B1223B011Cosmo Kramer
dictionary using test2.raw {
 _column(1)     str5     code   %5s
 _column(2)     int      call   %4f
 _column(6)     str1     city   %1s
 _column(7)     int      neigh  %3f
 _column(10)    str16    name   %16s
}

生成的数据文件:
      +-----------------------------------------------+
      |  code   call   city   neigh              name |
      |-----------------------------------------------|
   1. | C1245   1245      A     101   George Costanza |
   2. | B1223   1223      B      11      Cosmo Kramer |
      +-----------------------------------------------+

2
你能提供一些关于你所说的这些文件的文档或参考资料吗?从一些初步的搜索来看,我猜测这些是来自Stata的文件? - Dason
2
.dct 文件是什么?你提到的 .dat 文件类型具体是哪种?我们需要更详细的信息才能回答你。 - thelatemail
1
请提供给我们一些示例文件,完整的示例以及更多关于它们来自哪里的信息。否则,解决方案可能仅有一百万只猴子用一百万台打字机也能找到。 - Spacedman
我完全赞同@Spacedman的观点,如果这些文件来自于stata(这只是猜测),或许memisc包会很有用,正如read.dta帮助文档中所建议的那样。你可以在阅读了精彩的数据导入/导出手册后,前往该帮助文档。 - mnel
1
@sdaza - 我已经编辑了你的问题,提供了一些实际有用的信息。这是从谷歌上花费5分钟得到的结果 - 你能确认看起来是否可以吗? - thelatemail
谢谢@thelatemail,我的问题只是是否有办法使用R读取那些文件。我有几个大的.dct和.dat文件,我想用R读取它们。有什么想法吗? - sdaza
2个回答

14

@thelatemail说得很准确,下面是我编写的一个小函数,可以帮助你开始实现更健壮的解决方案:

read.dat.dct <- function(dat, dct) {
    temp <- readLines(dct)
    pattern <- "_column\\(([0-9]+)\\)\\s+([a-z0-9]+)\\s+([a-z0-9_]+)\\s+%([0-9]+).*"
    classes <- c("numeric", "character", "character", "numeric")
    metadata <- setNames(lapply(1:4, function(x) {
        out <- gsub(pattern, paste("\\", x, sep = ""), temp)
        out <- gsub("^\\s+|\\s+$|.*\\{|\\}", "", out)
        out <- out[out != ""]
        class(out) <- classes[x] ; out }), 
                         c("StartPos", "Str", "ColName", "ColWidth"))
    read.fwf(dat, widths = metadata[["ColWidth"]], 
             col.names = metadata[["ColName"]])
}

在错误检查、泛化函数等方面,您仍需要做很多工作。例如,该函数无法处理重叠的列,就像@thelatemail添加到您的问题中的示例一样。可以使用形式为“StartPos[n] + ColWidth[n]”应等于“StartPos[n+1]”的一些错误检查来停止读取文件,并显示错误消息,如果这不成立。此外,也可以从该函数生成的“元数据”列表中提取结果数据的类别,并在read.fwf中使用colClasses参数进行指定。

以下是一个dat文件和一个dct文件的示例:

将以下两行复制并粘贴到文本编辑器中,并将其保存到工作目录中,名称为“test.dat”。

C1245A101George Costanza
B1223B011Cosmo Kramer

复制并粘贴以下行到文本编辑器中,并将其保存在您的工作目录中,命名为“test.dct”

dictionary using test.dat {
    _column(1)     str1     code   %1s
    _column(2)     int      call   %4f
    _column(6)     str1     city   %1s
    _column(7)     int      neigh  %3f
    _column(10)    str16    name   %16s
}

现在运行该函数:

read.dat.dct(dat = "test.dat", dct = "test.dct")
#   code call city neigh            name
# 1    C 1245    A   101 George Costanza
# 2    B 1223    B    11    Cosmo Kramer

更新:一个改进的函数(仍有很大的提升空间)

read.dat.dct <- function(dat, dct, labels.included = "no") {
    temp <- readLines(dct)
    temp <- temp[grepl("_column", temp)]
    switch(labels.included,
           yes = {
               pattern <- "_column\\(([0-9]+)\\)\\s+([a-z0-9]+)\\s+(.*)\\s+%([0-9]+)[a-z]\\s+(.*)"
               classes <- c("numeric", "character", "character", "numeric", "character")
               N <- 5
               NAMES <- c("StartPos", "Str", "ColName", "ColWidth", "ColLabel")
           },
           no = {
               pattern <- "_column\\(([0-9]+)\\)\\s+([a-z0-9]+)\\s+(.*)\\s+%([0-9]+).*"
               classes <- c("numeric", "character", "character", "numeric")
               N <- 4
               NAMES <- c("StartPos", "Str", "ColName", "ColWidth")
           })
    metadata <- setNames(lapply(1:N, function(x) {
        out <- gsub(pattern, paste("\\", x, sep = ""), temp)
        out <- gsub("^\\s+|\\s+$", "", out)
        out <- gsub('\"', "", out, fixed = TRUE)
        class(out) <- classes[x] ; out }), NAMES)

    metadata[["ColName"]] <- make.names(gsub("\\s", "", metadata[["ColName"]]))

    myDF <- read.fwf(dat, widths = metadata[["ColWidth"]], 
             col.names = metadata[["ColName"]])
    if (labels.included == "yes") {
        attr(myDF, "col.label") <- metadata[["ColLabel"]]
    }
    myDF
}

它如何运作与你的数据?

temp <- read.dat.dct(dat = "http://dl.getdropbox.com/u/18116710/21600-0009-Data.txt", 
                     dct = "http://dl.getdropbox.com/u/18116710/21600-0009-Setup.dct",
                     labels.included = "yes")
dim(temp)                     # How big is the dataset?
# [1] 180  40
head(temp[, 1:6])             # What do the first few columns & rows look like?
#   CASEID      AID RRELNO RPREGNO H3PC1.H3PC1 H3PC2.H3PC2
# 1      1 57118381      5       1           1           1
# 2      2 57134970      1       2           1           1
# 3      3 57135078      1       1           1           1
# 4      4 57135078      5       1           1           1
# 5      5 57164981      1       1           7           3
# 6      6 57191909      1       3           1           1
head(attr(temp, "col.label")) # What are the variable labels?
# [1] "CASE IDENTIFICATION NUMBER"             "RESPONDENT IDENTIFIER"                 
# [3] "ROMANTIC RELATIONSHIP NUMBER"           "RELATIONSHIP PREGNANCY NUMBER"         
# [5] "S23Q1 1 TOLD PARTNER PREGNANT-W3"       "S23Q2 MONTHS PREG WHEN TOLD PARTNER-W3"

原始的例子怎么样?

read.dat.dct("test.dat", "test.dct", labels.included = "no")
#   code call city neigh            name
# 1    C 1245    A   101 George Costanza
# 2    B 1223    B    11    Cosmo Kramer

1
@sdaza - 不想太刻薄,但stackoverflow的人不是你的个人研究员。你提了一个模糊的问题,我加了很多内容使其能够回答。现在Ananda给了你一个接近一般化的答案。在某个阶段,我们期望你能在提供的信息基础上进一步发展,而不是不断改变目标。 - thelatemail
谢谢@thelatemail。我非常感激你和其他人提供的所有帮助。我并不是有意要挖苦别人。Ananda的解决方案非常棒!我会尝试去看看是否能解决我的问题。我的问题只是想知道是否有人之前做过类似的事情,但显然这个问题并不常见。直接从R中获取这些.dat和.dct文件似乎并不简单。我的最后解决方案将是先使用STATA,然后再导入到R中。再次感谢大家! - sdaza
看起来很棒,@Ananda Mahto。我仍然无法处理我的文件。我添加了一个示例。谢谢!.dtc文件goo.gl/qHZOk数据goo.gl/FRGRF - sdaza
谢谢@AnandaMahto。我不熟悉正则表达式,所以我一直无法处理我的dct文件中第四列的数据格式:%12.7f。我尝试使用 \s+% (.*),但我得到了这个:警告消息:在class(out)<- classes [x]时引入NAs,数据集仅产生NAs。你为此表达式是:\ s +%([0-9] +)[a-z]。我将尝试用这个:https://dev59.com/bm025IYBdhLWcg3wl3Em - sdaza
它运行得很好。这里有另一个例子:dct http://goo.gl/jmj9V dat http://goo.gl/Ix4yu。我使用的大部分数据都是受限制的,所以我将使用您的函数,如果我有进一步的问题,我会告诉您。我将尝试使用您的函数改进varname.varname变量名称模式,以便我们只获得“varname”。谢谢! - sdaza

10

您可以尝试使用?read.fwf来读取.dat文件,因为.dat数据本质上只是一个固定宽度的数据文件。

请参见此处- 整理混乱的记事本数据- 使用.dct字典文件中的column(X)值作为宽度。

可以使用readLines从字典文件中提取信息,然后将其传递到read.fwf调用中的参数中。

例如:'变量名称'与col.names=参数对齐,'存储类型'与colClasses=参数对齐。

这样做需要一些手动处理。


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