【问题标题】:set missing values for multiple labelled variables为多个标记变量设置缺失值
【发布时间】:2017-09-17 16:54:25
【问题描述】:

如何为数据框中的多个标记向量设置缺失值。我正在使用来自 spss 的调查数据集。我正在处理大约 20 个具有相同缺失值的不同变量。所以想找到一种方法来使用 lapply() 来完成这项工作,但我做不到。

我实际上可以通过 as.numeric() 然后 recode() 使用 base R 来做到这一点,但我对 Haven 和标记类的可能性很感兴趣,所以我想找到一种方法来完成这一切哈德利的 tidyverse

感兴趣的变量大致如下所示。如果这是一个基本问题,我很抱歉,但我发现与避风港和标签包相关的帮助文档非常无用。

library(haven)
library(labelled)
v1<-labelled(c(1,2,2,2,5,6), c(agree=1, disagree=2, dk=5, refused=6))
v2<-labelled(c(1,2,2,2,5,6), c(agree=1, disagree=2, dk=5, refused=6))
v3<-data.frame(v1=v1, v2=v2)
lapply(v3, val_labels)
lapply(v3, function(x) set_na_values(x, c(5,6)))

【问题讨论】:

  • 我很难准确理解您要做什么。您的示例数据似乎没有任何缺失值或标签。我错过了什么吗?
  • 亲爱的@spindoctor,您能否获取数据集并使用dput(&lt;structure&gt;,"") 提供示例并将其添加到您的代码示例中。完整或子集都会有所帮助。注意。我更新了你发布的代码data_frame() 应该是data.frame()
  • 我想转换每个变量中的值 5 和 6,以便 R 在从标记类转换为数字类或因子类时将它们读取为缺失。
  • @spindoctor 没问题 - 你没有提到对 dplyr 的依赖。我要的是实际数据的一个子集。您可以使用 base::dput() 创建它 - 它将 R 对象的 ASCII 文本表示形式写入文件。供将来参考 - 显示数据输入、实际输出和预期输出很有用。
  • 也许你应该在读取 SPSS 数据时在 foreigh::read.spss 函数中使用 use.missings = TRUE 参数?这将根据 SPSS 数据中的缺失值定义自动为每一列执行此操作。

标签: r tidyverse r-haven


【解决方案1】:

好的,我想我现在明白你想要做什么了......

即将标签和值标记为 NA 而不删除基础导入数据...

请参阅附录以获取更详细的示例,该示例使用公共数据文件展示利用 dplyr 更新多个列、标签的示例...

建议的解决方案

df <- data_frame(s1 = c(1,2,2,2,5,6), s2 = c(1,2,2,2,5,6)) %>%
  set_value_labels(s1 = c(agree=1, disagree=2, dk=5, refused=6), 
                   s2 = c(agree=1, disagree=2, dk = tagged_na("5"), refused = tagged_na("6"))) %>%
  set_na_values(s2 = c(5,6))


val_labels(df)
is.na(df$s1)
is.na(df$s2)
df

解决结果:

> library(haven)
> library(labelled)
> library(dplyr)
> df <- data_frame(s1 = c(1,2,2,2,5,6), s2 = c(1,2,2,2,5,6)) %>%
+   set_value_labels(s1 = c(agree=1, disagree=2, dk=5, refused=6), 
+                    s2 = c(agree=1, disagree=2, dk = tagged_na("5"), refused = tagged_na("6"))) %>%
+   set_na_values(s2 = c(5,6))
> val_labels(df)
$s1
   agree disagree       dk  refused 
       1        2        5        6 

$s2
   agree disagree       dk  refused 
       1        2       NA       NA 

> is.na(df$s1)
[1] FALSE FALSE FALSE FALSE FALSE FALSE
> is.na(df$s2)
[1] FALSE FALSE FALSE FALSE  TRUE  TRUE
> df
# A tibble: 6 × 2
         s1        s2
  <dbl+lbl> <dbl+lbl>
1         1         1
2         2         2
3         2         2
4         2         2
5         5         5
6         6         6

现在我们可以操作数据了

mean(df$s1, na.rm = TRUE)
mean(df$s2, na.rm = TRUE)

> mean(df$s1, na.rm = TRUE)
[1] 3
> mean(df$s2, na.rm = TRUE)
[1] 1.75

使用 Labeled 包去除标签并替换为 R NA

如果您希望去除标签并替换为 R NA 值,您可以使用remove_labels(x, user_na_to_na = TRUE)

示例:

df <- remove_labels(df, user_na_to_na = TRUE)
df

结果:

> df <- remove_labels(df, user_na_to_na = TRUE) 
> df
# A tibble: 6 × 2
     s1    s2
  <dbl> <dbl>
1     1     1
2     2     2
3     2     2
4     2     2
5     5    NA
6     6    NA

--

SPSS 格式说明/概述:

IBM SPSS(应用程序)可以以多种格式和非矩形配置导入和导出数据;但是,数据集总是被转换为 SPSS 矩形数据文件,称为系统文件(使用扩展名 *.sav)。元数据(有关数据的信息),例如变量格式、缺失值以及变量和值标签与数据集一起存储。

值标签

Base R 有一种数据类型可以有效地维护整数和字符标签之间的映射:因子。然而,这不是因子的主要用途:它们被设计为自动为线性模型生成有用的对比。因素在重要方面与其他工具提供的标记值不同:

SPSS 和 SAS 可以标记数字和字符值,而不仅仅是整数值。

缺失值

所有三个工具(SPSS、SAS、Stata)都提供了一个全局“系统缺失值”,显示为.。这大致相当于 R 的NA,尽管 Stata 和 SAS 都不会在数值比较中传播缺失:SAS 将缺失值视为可能的最小数(即 -inf),而 Stata 将其视为可能的最大数(即 inf) .

每个工具还提供了一种记录多种缺失类型的机制:

  • Stata 具有“扩展”缺失值,从 .A 到 .Z。
  • SAS 有“特殊”缺失值,.A 到 .Z 加上 ._。
  • SPSS 具有每列“用户”缺失值。每列最多可以声明三个不同的值或应该被视为缺失的一系列值(加上一个不同的值)。

用户定义的缺失值

SPSS 的用户定义值的工作方式与 SAS 和 Stata 不同。每列最多可以有三个不同的值,这些值被视为缺失或一个范围。 Haven 提供 labelled_spss() 作为 labelled() 的子类来模拟这些额外的用户定义的缺失。

x1 <- labelled_spss(c(1:10, 99), c(Missing = 99), na_value = 99)
x2 <- labelled_spss(c(1:10, 99), c(Missing = 99), na_range = c(90, Inf))

x1
#> <Labelled SPSS double>
#>  [1]  1  2  3  4  5  6  7  8  9 10 99
#> Missing values: 99
#> 
#> Labels:
#>  value   label
#>     99 Missing
x2
#> <Labelled SPSS double>
#>  [1]  1  2  3  4  5  6  7  8  9 10 99
#> Missing range:  [90, Inf]
#> 
#> Labels:
#>  value   label
#>     99 Missing

标记缺失值

为了支持 Stata 的扩展和 SAS 的特殊缺失值,have 实现了一个标记的 NA。它通过利用浮点 NA 的内部结构来做到这一点。这允许这些值在常规 R 操作中的行为与 NA 相同,同时仍保留标记的值。

使用标记NAs 创建的 R 界面有点笨拙,因为通常它们会由 Haven 为您创建。但是您可以使用 tagged_na() 创建自己的:

重要:

请注意,这些标记的 NA 的行为与常规 NA 相同,即使在打印时也是如此。要查看他们的标签,请使用 print_tagged_na():

因此:

    library(haven)
    library(labelled)
    v1<-labelled(c(1,2,2,2,5,6), c(agree=1, disagree=2, dk=5, refused=6))
    v2<-labelled(c(1,2,2,2,5,6), c(agree=1, disagree=2, dk=tagged_na("5"), refused= tagged_na("6")))
    v3<-data.frame(v1 = v1, v2 = v2)
    v3
    lapply(v3, val_labels)

> v3
  x x.1
1 1   1
2 2   2
3 2   2
4 2   2
5 5   5
6 6   6
> lapply(v3, val_labels)
$x
   agree disagree       dk  refused 
       1        2        5        6 

$x.1
   agree disagree       dk  refused 
       1        2       NA       NA 

注意事项:

SPSS 的用户定义值的工作方式与 SAS 和 Stata 不同。每列最多可以有三个被视为缺失的不同值或一个范围。 Haven 提供 labelled_spss() 作为 labelled() 的子类来模拟这些额外的用户定义的缺失。

希望以上对你有帮助

保重 T.

参考资料:

使用公共数据的附录示例...

SPSS 缺失值示例使用 SPPS 数据文件 {hospital.sav}

首先,让我们确保我们强调这一点

  • 系统缺失值 - 数据中完全缺失的值
  • 用户缺失值是存在于数据中但必须从计算中排除的值。

SPSS 数据视图...

让我们回顾一下图像和数据...变量视图中显示的 SPSS 数据显示每一行都有一个 Label [Column5],我们注意到第 10 到第 14 行具有特定的属性值给他们 [1..6] [Column 6] 具有名称属性并且没有值被指定为 Missing [Column 7]。

现在让我们看看 SPSS 数据视图:

在这里我们可以注意到缺少数据...(请参阅突出显示的“.”)。关键是我们有缺失数据,但目前没有“缺失用户价值”

现在让我们转向 R,并将数据加载到 R

hospital_url <- "https://www.spss-tutorials.com/downloads/hospital.sav"
hospital <- read_sav(hospital_url, 
                     user_na = FALSE)
head(hospital,5)

# We're interested in columns 10 through 14...
head(hospital[10:14],5)

结果

> hospital_url <- "https://www.spss-tutorials.com/downloads/hospital.sav"
> hospital <- read_sav(hospital_url, 
+                      user_na = FALSE)
> head(hospital,5)
# A tibble: 5 × 14
  visit_id patient_id first_name surname_prefix last_name    gender entry_date entry_time
     <dbl>      <dbl>      <chr>          <chr>     <chr> <dbl+lbl>     <date>     <time>
1    32943      23176    JEFFREY                 DIJKSTRA         1 2013-01-08   16:56:10
2    32944      20754       MARK        VAN DER      BERG         1 2013-02-01   14:24:45
3    32945      25419     WILLEM                VERMEULEN         1 2013-02-02   10:01:43
4    32946      21139      LINDA                  JANSSEN         0 2013-02-10   10:24:39
5    32947      25419     WILLEM                VERMEULEN         1 2013-02-10   18:05:59
# ... with 6 more variables: exit_moment <dttm>, doctor_rating <dbl+lbl>, nurse_rating <dbl+lbl>,
#   room_rating <dbl+lbl>, food_rating <dbl+lbl>, facilities_rating <dbl+lbl>

第 10 到 14 列包含值

1="Very Dissatisfied"
2="Dissatisfied"
3="Neutral"
4="Satisfied"
5="Very Satisfied"
6="Not applicable or don't want to answer"

因此:

> head(hospital[10:14],5)
# A tibble: 5 × 5
  doctor_rating nurse_rating room_rating food_rating facilities_rating
      <dbl+lbl>    <dbl+lbl>   <dbl+lbl>   <dbl+lbl>         <dbl+lbl>
1             5            5           4           2                 3
2             4            5           4           3                 3
3             5            6           4           5                 4
4             4            5           5           4                 4
5             5            5           6           6                 6

SPSS 值标签

> lapply(hospital[10], val_labels)
$doctor_rating
                     Very dissatisfied                           Dissatisfied 
                                     1                                      2 
                               Neutral                              Satisfied 
                                     3                                      4 
                        Very satisfied Not applicable or don't want to answer 
                                     5                                      6

好的,注意上面我们可以确认我们已经导入了值标签。

从调查数据中删除不适用的数据

我们的目标是现在删除 “不适用或不想回答” 数据条目,方法是将它们设置为 “用户 NA 值”,即 SPSS 缺失值

解决方案 - 第 1 步 - 单列

我们希望在数据中的多列中设置缺失值属性...让我们首先为一列执行此操作...

请注意,我们使用 add_value_labels 而不是 set_value_labels,因为我们希望附加一个新标签,而不是完全覆盖现有标签...

d <- hospital
mean(d$doctor_rating, na.rm = TRUE)

d <- hospital %>% 
  add_value_labels( doctor_rating = c( "Not applicable or don't want to answer" 
                                       = tagged_na("6") )) %>%
  set_na_values(doctor_rating = 5)

val_labels(d$doctor_rating)
mean(d$doctor_rating, na.rm = TRUE)

> d <- hospital
> mean(d$doctor_rating, na.rm = TRUE)
[1] 4.322368
> d <- hospital %>% 
+   add_value_labels( doctor_rating = c( "Not applicable or don't want to answer" 
+                                        = tagged_na("6") )) %>%
+   set_na_values(doctor_rating = 6)
> val_labels(d$doctor_rating)
                     Very dissatisfied                           Dissatisfied 
                                     1                                      2 
                               Neutral                              Satisfied 
                                     3                                      4 
                        Very satisfied Not applicable or don't want to answer 
                                     5                                      6 
Not applicable or don't want to answer 
                                    NA 
> mean(d$doctor_rating, na.rm = TRUE)
[1] 4.097015

解决方案 - 第 2 步 - 现在应用于多个列...

mean(hospital$nurse_rating)
mean(hospital$nurse_rating, na.rm = TRUE)
d <- hospital %>% 
  add_value_labels( doctor_rating = c( "Not applicable or don't want to answer" 
                                       = tagged_na("6") )) %>%
  set_na_values(doctor_rating = 6) %>%
  add_value_labels( nurse_rating = c( "Not applicable or don't want to answer" 
                                     = tagged_na("6") )) %>%
  set_na_values(nurse_rating = 6)
mean(d$nurse_rating, na.rm = TRUE)

结果

请注意,nurse_rating 包含“NaN”值 NA 标记值。 第一个 mean() 调用失败,第二个调用成功,但在删除“不适用...”的过滤器后包含“不适用...”...

> mean(hospital$nurse_rating)
[1] NaN
> mean(hospital$nurse_rating, na.rm = TRUE)
[1] 4.471429
> d <- hospital %>% 
+   add_value_labels( doctor_rating = c( "Not applicable or don't want to answer" 
+                                        = tagged_na("6") )) %>%
+   set_na_values(doctor_rating = 6) %>%
+   add_value_labels( nurse_rating = c( "Not applicable or don't want to answer" 
+                                      = tagged_na("6") )) %>%
+   set_na_values(nurse_rating = 6)
> mean(d$nurse_rating, na.rm = TRUE)
[1] 4.341085

将标记的 NA 转换为 R NA

这里我们取上面标记的 NA 并转换为 R NA 值。

d <- d %>% remove_labels(user_na_to_na = TRUE)

【讨论】:

  • 这个很彻底,但我要解决的核心就在这里:df &lt;- data_frame(s1 = c(1,2,2,2,5,6), s2 = c(1,2,2,2,5,6)) %&gt;% set_value_labels(s1 = c(agree=1, disagree=2, dk=5, refused=6), s2 = c(agree=1, disagree=2, dk = tagged_na("5"), refused = tagged_na("6"))) %&gt;% #Am I going to have to set these values for each variable, line by line; #I'd like a way to do this for multiple variables at once. Something like #lapply(x, function(x) set_na_values(x=c(5,6)) or #set_na_values(s1:s2=c(5,6)) set_na_values(s2 = c(5,6))
  • @spindoctor - 也许我在这里遗漏了一些东西。我假设您正在通过 Haven 导入带有标签的 SPSS 数据文件。 正确吗? 如果是,可以使用导入的标签数据设置tagged_na(),然后使用remove_labels(x, user_na_to_na = TRUE)。你能提供一个数据样本吗?仅供参考:PURR::map() 函数是 lapply 更好的类型安全等价物 - 然后两者都可以用于迭代数据集。
  • @spindoctor - 您是在导入文件后添加标签,还是在读入内存后操作文件中已存在的标签。
  • 我在做后者!
  • 非常感谢技术恐惧症;我真的很感激时间!
【解决方案2】:

不太确定这是否是您要查找的内容:

v1 <- labelled(c(1, 2, 2, 2, 5, 6), c(agree = 1, disagree = 2, dk = 5, refused = 6))
v2 <- labelled(c(1, 2, 2, 2, 5, 6), c(agree = 1, disagree = 2, dk = 5, refused = 6))
v3 <- data_frame(v1 = v1, v2 = v2)

lapply(names(v3), FUN = function(x) {
  na_values(v3[[x]]) <<- 5:6
})

lapply(v3, na_values)

最后一行返回

$v1
[1] 5 6

$v2
[1] 5 6

验证缺失值

is.na(v3$v1)
[1] FALSE FALSE FALSE FALSE  TRUE  TRUE

【讨论】:

  • 我可以做到这一点,但实际上并没有设置缺失值
  • 如何不将 5 和 6 定义为 NA 值?你期待什么?
【解决方案3】:

定义 SPSS 样式的用户定义缺失值

主要功能

labelled 包中用于处理 SPSS 样式的用户定义缺失值的两个主要函数是 na_valuesna_range

library(labelled)
v1 <-c(1,2,2,2,5,6)
val_labels(v1) <- c(agree=1, disagree=2, dk=5, refused=6)
na_values(v1) <- 5:6
v1

<Labelled SPSS double>
[1] 1 2 2 2 5 6
Missing values: 5, 6

Labels:
 value    label
     1    agree
     2 disagree
     5       dk
     6  refused

set_* 函数

labelled 中的 set_* 函数旨在与 magrittr / dplyr 一起使用。

library(dplyr)
d <- tibble(v1 = c(1, 2, 2, 2, 5, 6), v2 = c(1:3, 1:3))
d <- d %>%
  set_value_labels(v1 = c(agree=1, disagree=2, dk=5, refused=6)) %>%
  set_na_values(v1 = 5:6)
d$v1

<Labelled SPSS double>
[1] 1 2 2 2 5 6
Missing values: 5, 6

Labels:
 value    label
     1    agree
     2 disagree
     5       dk
     6  refused

什么是用户定义的缺失值?

用户定义的缺失值只是元信息。它不会改变向量中的值。这只是向用户说明这些值在某些情况下可以/应该被视为缺失值的一种方式。这意味着如果您从向量计算某些东西(例如均值),这些值仍将被考虑在内。

mean(v1)
[1] 3

您可以使用user_na_to_na 轻松地将用户定义的缺失值转换为正确的NA

mean(user_na_to_na(v1), na.rm = TRUE)
[1] 1.75

很少有函数会考虑这些元信息。例如,参见 questionr 包中的 freq 函数。

library(questionr)
freq(v1)
             n    % val%
[1] agree    1 16.7   25
[2] disagree 3 50.0   75
[5] dk       1 16.7   NA
[6] refused  1 16.7   NA
NA           0  0.0   NA

标记的 NA 有什么区别?

haven 引入的标记 NA 的目的是重现 Stata 管理缺失值的方式。 R 在内部将所有标记的 NA 视为NA

【讨论】:

    【解决方案4】:

    这是正确的吗?

    #Using replace to substitute 5 and 6 in v3 with NA
    data.frame(lapply(v3, function(a) replace(x = a, list = a %in% c(5,6), values = NA)))
    #   x x.1
    #1  1   1
    #2  2   2
    #3  2   2
    #4  2   2
    #5 NA  NA
    #6 NA  NA
    

    我知道 labelled_spss 允许您指定 na_range 甚至是 na_values 的向量

    #DATA
    v11 = labelled_spss(x = c(1,2,2,2,5,6),
                        labels = c(agree=1, disagree=2, dk=5, refused=6),
                        na_range = 5:6)
    
    #Check if v11 has NA values
    is.na(v11)
    #[1] FALSE FALSE FALSE FALSE  TRUE  TRUE
    
    v22 = labelled_spss(x = c(1,2,2,2,5,6),
                        labels = c(agree=1, disagree=2, dk=5, refused=6),
                        na_range = 5:6)
    
    #Put v11 and v22 in a list
    v33 = list(v11, v22)
    
    #Use replace like above
    data.frame(lapply(X = v33, FUN = function(a) replace(x = a, list = is.na(a), values = NA)))
    #   x x.1
    #1  1   1
    #2  2   2
    #3  2   2
    #4  2   2
    #5 NA  NA
    #6 NA  NA
    

    【讨论】:

    • 这很接近,但我希望有一种方法可以使用标记包中提供的命令来执行此类操作。
    • @spindoctor - 您能澄清一下您是否只想使用 SPSS 标签将值转换为 R NA 值。即用 R NA 搜索标签替换值并擦除基础数据。对吗?
    • 查看更新的答案 - 您可以使用 remove_labels(df, user_na_to_na = TRUE) 这会去除标记为 NA 的标签,并用 R 定义的 NA 值替换它们。
    【解决方案5】:

    set_na_values 的第一个参数是数据框,而不是向量/列,这就是您的 lapply 命令不起作用的原因。您可以为数据框中任意数量的列构建set_na_values 的参数列表,然后使用do.call 调用它,如下所示...

    v1<-labelled(c(1,2,2,2,5,6), c(agree=1, disagree=2, dk=5, refused=6))
    v2<-labelled(c(1,2,2,2,5,6), c(agree=1, disagree=2, dk=5, refused=6))
    v3<-data.frame(v1=v1, v2=v2)
    na_values(v3)
    
    args <- c(list(.data = v3), setNames(lapply(names(v3), function(x) c(5,6)), names(v3)))
    v3 <- do.call(set_na_values, args)
    na_values(v3)
    

    更新:您还可以在lapply 语句中使用na_values 函数的赋值形式,因为它接受向量作为第一个参数,而不是像set_na_values 这样的数据框...

    library(haven)
    library(labelled)
    v1<-labelled(c(1,2,2,2,5,6), c(agree=1, disagree=2, dk=5, refused=6))
    v2<-labelled(c(1,2,2,2,5,6), c(agree=1, disagree=2, dk=5, refused=6))
    v3<-data.frame(v1=v1, v2=v2)
    lapply(v3, val_labels)
    na_values(v3)
    
    v3[] <- lapply(v3, function(x) `na_values<-`(x, c(5,6)))
    na_values(v3)
    

    甚至在lapply 命令中使用普通版本的na_values,只要确保返回“固定”向量...

    library(haven)
    library(labelled)
    v1<-labelled(c(1,2,2,2,5,6), c(agree=1, disagree=2, dk=5, refused=6))
    v2<-labelled(c(1,2,2,2,5,6), c(agree=1, disagree=2, dk=5, refused=6))
    v3<-data.frame(v1=v1, v2=v2)
    lapply(v3, val_labels)
    na_values(v3)
    
    v3[] <- lapply(v3, function(x) { na_values(x) <- c(5,6); x } )
    na_values(v3)
    

    这个想法也可以在dplyr 链中使用,既可以应用于所有变量,也可以应用于使用dplyr 的选择工具选择的任何列...

    library(haven)
    library(labelled)
    library(dplyr)
    v1<-labelled(c(1,2,2,2,5,6), c(agree=1, disagree=2, dk=5, refused=6))
    v2<-labelled(c(1,2,2,2,5,6), c(agree=1, disagree=2, dk=5, refused=6))
    v3<-data.frame(v1=v1, v2=v2)
    lapply(v3, val_labels)
    na_values(v3)
    
    v4 <- v3 %>% mutate_all(funs(`na_values<-`(., c(5,6))))
    na_values(v4)
    
    v5 <- v3 %>% mutate_each(funs(`na_values<-`(., c(5,6))), x)
    na_values(v5)
    

    【讨论】:

      【解决方案6】:

      您可以使用一个非常简单的解决方案来使用base R:

      v3[v3 == 5 ] <- NA
      v3[v3 == 6 ] <- NA
      

      但如果您正在寻找一个真正快速的解决方案,您可以使用data.table 方法。

      library(data.table)
      
      setDT(v3)
      
      for(j in seq_along(v3)) { 
                  set(v3, i=which(v3[[j]] %in% c(5,6)), j=j, value=NA) 
                  }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多