【问题标题】:How can I import SAS format files into R?如何将 SAS 格式文件导入 R?
【发布时间】:2015-10-29 18:22:47
【问题描述】:

我正在尝试分析来自 2012-2013 年 NATS 调查的数据,from this location。那里的 zip 文件夹中有三个文件,分别标记为 2012-2013 NATS format.sas、formats.sas7bcat 和 nats2012.sas7bdat。第三个文件包含实际数据,但第二个文件包含数据的标签;也就是说,例如,如果原始数据文件中的变量“种族”具有类别 1、2、3 和 4,则标签显示这些类别代表“高加索人”、“非洲裔美国人”、“西班牙裔”和'其他'。 我已经能够使用“sas7bdat”包将 sas7bdat 文件导入 R,但是当我尝试进行交叉制表时,我无法看到每个单元格代表的类别。例如,如果我尝试这样做:

table(SMOKSTATUS_R, RACEETHNIC)

我得到的是:

RACEETHNIC
SMOKSTATUS_R     1     2     3     4     5     6     7     8     9
           1  4045   455    55     7    63     0   675   393   373
           2  1183   222    38     2    26     0   217   255   154
           3 14480   957   238    14    95     3  1112   950   369
           4 23923  2532  1157    23   147     1  1755  3223   909
           5    81    18     4     0     1     0    11    17     9

据我所知,将标签添加到数据中的唯一方法是手动输入它们,但有 240 个变量,此外,目前还存在一些标签,格式为 format.sas7bcat 文件。有没有办法将格式文件导入R,以便标签可以附加到变量上?这就是在 SAS 中完成的方式,但我现在无法访问 oSAS。感谢大家的帮助。

【问题讨论】:

  • 搜索foreign 包——这里是reference manual
  • 只需阅读 SAS 程序,它有代码来定义格式并解析它。我对 NATS 不熟悉,但大多数发布 SAS 代码来定义格式的人都会以非常结构化的格式生成代码,这种格式很容易被解析。或者使用 SAS 将格式目录导出到可以从 R 中读取的 SAS 数据集。
  • readsas7dbathaven 是其他包;见this我的问答。
  • 我通常加载到 SAS 中,然后导出为单个 Stata 文件,然后使用 foreign 将其加载到 R 中。我发现效果最好,因为它将格式语句保留为元数据。
  • 这样是不是太迂回了?无论如何,谢谢大家的建议,但我要等到下周直接在 SAS 工作。 @MichaelChirico 我也无法从 SAS 数据集创建 Xport 文件,这排除了使用外部包以及您提到的包。

标签: r import sas


【解决方案1】:

这应该是一个单行:

library('haven')
sas <- read_sas('nats2012.sas7bdat', 'formats.sas7bcat')

with(sas, table(SMOKSTATUS_R, RACEETHNIC))
#             RACEETHNIC
# SMOKSTATUS_R     1     2     3     4     5     6     7     8     9
#            1  4045   455    55     7    63     0   675   393   373
#            2  1183   222    38     2    26     0   217   255   154
#            3 14480   957   238    14    95     3  1112   950   369
#            4 23923  2532  1157    23   147     1  1755  3223   909
#            5    81    18     4     0     1     0    11    17     9

table(names(attr(sas[, 'SMOKSTATUS_R'], 'labels')[sas[, 'SMOKSTATUS_R']]),
      names(attr(sas[, 'RACEETHNIC'], 'labels')[sas[, 'RACEETHNIC']]))

#                          Amer. Indian, AK Nat. Only, Non-Hispanic
# Current everyday smoker                                        63
# Current some days smoker                                       26
# Former smoker                                                  95
# Never smoker                                                  147
# Unknown                                                         1

使用haven 读入数据,但这也给了你一些有用的attributes,即变量标签:

attributes(sas$SMOKSTATUS_R)
# $label
# [1] "SMOKER STATUS (4-level)"
# 
# $class
# [1] "labelled"
# 
# $labels
# Current everyday smoker Current some days smoker            Former smoker 
#                       1                        2                        3 
# Never smoker                  Unknown 
#            4                        5 
# 
# $is_na
# [1] FALSE FALSE FALSE FALSE FALSE

你可以很容易地把它写成一个更普遍使用的函数:

do_fmt <- function(x, fmt) {
  lbl <- if (!missing(fmt))
    unlist(unname(fmt)) else attr(x, 'labels')

  if (!is.null(lbl))
    tryCatch(names(lbl[match(unlist(x), lbl)]),
             error = function(e) {
               message(sprintf('formatting failed for %s', attr(x, 'label')),
                       domain = NA)
               x
             }) else x
}

table(do_fmt(sas[, 'SMOKSTATUS_R']),
      do_fmt(sas[, 'RACEETHNIC']))

#                          Amer. Indian, AK Nat. Only, Non-Hispanic
# Current everyday smoker                                        63
# Current some days smoker                                       26
# Former smoker                                                  95
# Never smoker                                                  147
# Unknown                                                         1

并应用于整个数据集

sas[] <- lapply(sas, do_fmt)
sas$SMOKSTATUS_R[1:4]
# [1] "Never smoker"  "Former smoker" "Former smoker" "Never smoker" 

虽然有时这会失败,如下所示。 haven 包似乎有问题

attr(sas$SMOKTYPE, 'labels')
# INAPPLICABLE            REFUSED                 DK    NOT ASCERTAINED 
#     -4.00000           -0.62500           -0.50000           -0.46875 
# PREMADE CIGARETTES      ROLL-YOUR-OWN               BOTH 
#            1.00000            2.00000            3.00000 

因此,您可以使用一些简单的正则表达式来解析 format.sas 文件

locf <- function(x) {
  x <- data.frame(x, stringsAsFactors = FALSE)
  x[x == ''] <- NA
  indx <- !is.na(x)

  x[] <- lapply(seq_along(x), function(ii) {
    idx <- cumsum(indx[, ii])
    idx[idx == 0] <- NA
    x[, ii][indx[, ii]][idx]
  })
  x[, 1]
}

fmt <- readLines('~/desktop/2012-2013-NATS-Format/2012-2013-NATS-Format.sas')
## not sure if comments are allowed in the value definitions, but
## this will check for those in case
fmt <- gsub('\\*.*;|\\/\\*.*\\*\\/', '', fmt)

vars <- gsub('(?i)value\\W+(\\w*)|.', '\\1', fmt, perl = TRUE)
vars <- locf(vars)

regex <- '[\'\"].*[\'\"]|[\\w\\d-]+'
vals <- gsub(sprintf('(?i)\\s*(%s)\\s*(=)\\s*(%s)|.', regex, regex),
               '\\1\\2\\3', fmt, perl = TRUE)

View(dd <- na.omit(data.frame(values = vars, formats = vals,
                              stringsAsFactors = FALSE)))

sp <- split(dd$formats, dd$values)
sp <- lapply(sp, function(x) {
  x <- Filter(nzchar, x)
  x <- strsplit(x, '=')
  tw <- function(x) gsub('^\\s+|\\s+$', '', x)
  sapply(x, function(y)
    setNames(tw(y[1]), tw(y[2])))
})

因此,例如,烟雾类型格式(其中一种在上面失败)被解析如下:

sp['A5_']
# $A5_
# 'INAPPLICABLE'            'REFUSED'                 'DK' 
#           "-1"                 "-7"                 "-8" 
# 'NOT ASCERTAINED' 'PREMADE CIGARETTES'      'ROLL-YOUR-OWN'  'BOTH' 
#              "-9"                  "1"                  "2"     "3" 

然后你可以再次使用该函数来应用到数据

table(do_fmt(sas['SMOKTYPE'], sp['A5_']))

# 'BOTH'                 'DK'       'INAPPLICABLE' 
#   736                   17                51857 
# 'PREMADE CIGARETTES'            'REFUSED'      'ROLL-YOUR-OWN' 
#                 7184                    2                  396 

【讨论】:

  • 我一直在寻找该正则表达式失败的情况,所以我想这不是那么简单。
【解决方案2】:

formats.sas 文件应该可读并可解析为列标签向量,然后您可以像应用任何列标签向量一样应用它。

如果您希望标记分类变量(根据您的问题,这大概是您最关心的问题),这应该相当简单。您将看到如下所示的代码:

value RACEF
1 = 'Caucasian'
2 = 'African-American'
3 = 'Hispanic'
4 = 'Other'
;

您只需将其解析为向量。

如果幸运的话,它们的类别格式名称将与列名称相同(可能像我在该示例中那样带有 F);如果是这种情况,您可能只需弄清楚如何直接应用它们。

如果不是,您将不得不解析程序的后半部分。它将由如下几行组成:

format
  race RACEF.
  gender SEXF.
  income INCRF.
...
;

这当然显示了列名和格式名之间的关系,从而告诉您应该使用哪个列名向量来标记哪个列。

【讨论】:

  • 另一种方法是让有 SAS 的人为您导出 CSV 或 sas 数据集中的格式,这很容易做到;你甚至可以下载 SAS University Edition 并在那里自己做。如果这是一个更可行的选择,我可以添加另一个答案来说明如何做到这一点。
  • 感谢您的回答。我尝试使用包havensas7bdatSAScii 阅读formats.sas。我收到一条错误消息说Invalid file, or file has unsupported features.。而且我也没有 XPORT,为了尝试foreign。所以我被困住了,直到找到有人将 SAS 转换为 CSV 或其他格式。
  • formats.sas 应该是一个普通的旧常规文本文件。只需阅读您通常阅读普通旧常规文本文件的方式......或者甚至只是在 [您选择的文本编辑器] 中打开它并手动将其转换为 CSV - 这可能不是很难,如果是几分钟的工作格式化/缩进得体。
  • 再次感谢,我能够在 R 中打开文件,尽管它没有正确缩进,就像你提到的那样。输出看起来不像我想要的那样格式化。无论如何,我会接受你的回答,因为它有效,但我不愿意再花时间在这上面。我会等到我有SAS,也就是下周!再次感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-16
  • 2022-06-22
相关资源
最近更新 更多