-
Notifications
You must be signed in to change notification settings - Fork 116
Expand file tree
/
Copy pathsemantics.Rmd
More file actions
172 lines (117 loc) · 6.29 KB
/
Copy pathsemantics.Rmd
File metadata and controls
172 lines (117 loc) · 6.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
---
title: "Conversion semantics"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Conversion semantics}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
library(haven)
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
```
There are some differences between the way that R, SAS, SPSS, and Stata represented labelled data and missing values. While SAS, SPSS, and Stata share some obvious similarities, R is little different. This vignette explores the differences, and shows you how haven bridges the gap.
## Value labels
Base R has one data type that effectively maintains a mapping between integers and character labels: the factor. This however, is not the primary use of factors: they are instead designed to automatically generate useful contrasts for linear models. Factors differ from the labelled values provided by the other tools in important ways:
* SPSS and SAS can label numeric and character values, not just
integer values.
* The value do not need to be exhaustive. It is common to label the
special missing values (e.g. `.D` = did not respond, `.N` =
not applicable), while leaving other values as is.
Value labels in SAS are a little different again. In SAS, labels are just special case of general formats. Formats include currencies and dates, but user-defined just assigns labels to individual values (including special missings value). Formats have names and existing independently of the variables they are associated with. You create a named format with `PROC FORMAT` and then associated with variables in a `DATA` step (the names of character formats thealways start with `$`).
### `labelled()`
To allow you to import labelled vectors into R, haven provides the S3 labelled class, created with `labelled()`. This class allows you to associated arbitrary labels with numeric or character vectors:
```{r}
x1 <- labelled(
sample(1:5),
c(Good = 1, Bad = 5)
)
x1
x2 <- labelled(
c("M", "F", "F", "F", "M"),
c(Male = "M", Female = "F")
)
x2
```
The goal of haven is not to provide a labelled vector that you can use everywhere in your analysis. The goal is to provide an intermediate datastructure that you can convert into a regular R data frame. You can do this by either converting to a factor or stripping the labels:
```{r}
as_factor(x1)
zap_labels(x1)
as_factor(x2)
zap_labels(x2)
```
See the documentation for `as_factor()` for more options to control exactly what the factor uses for levels.
Both `as_factor()` and `zap_labels()` have data frame methods if you want to apply the same strategy to every column in a data frame:
```{r}
df <- tibble::data_frame(x1, x2, z = 1:5)
df
zap_labels(df)
as_factor(df)
```
## Missing values
All three tools provide a global "system missing value" which is displayed as `.`. This is roughly equivalent to R's `NA`, although neither Stata nor SAS propagate missingness in numeric comparisons: SAS treats the missing value as the smallest possible number (i.e. `-inf`), and Stata treats it as the largest possible number (i.e. `inf`).
Each tool also provides a mechanism for recording multiple types of missingness:
* Stata has "extended" missing values, `.A` through `.Z`.
* SAS has "special" missing values, `.A` through `.Z` plus `._`.
* SPSS has per-column "user" missing values. Each column can declare
up to three distinct values or a range of values (plus one distinct
value) that should be treated as missing.
Stata and SAS only support tagged missing values for numeric columns. SPSS supports up to three distinct values for character columns. Generally, operations involving a user-missing type return a system missing value.
Haven models these missing values in two different ways:
* For SAS and Stata, haven provides "tagged" missing values which extend R's
regular `NA` to add a single character label.
* For SPSS, haven provides a subclass of `labelled` that also provides
user defined values and ranges.
### Tagged missing values
To support Stata's extended and SAS's special missing value, haven implements a tagged NA. It does this by taking advantage of the internal structure of a floating point NA. That allows these values to behave identical to NA in regular R operations, while still preserving the value of the tag.
The R interface for creating with tagged NAs is a little clunky because generally they'll be created by haven for you. But you can create your own with `tagged_na()`:
```{r}
x <- c(1:3, tagged_na("a", "z"), 3:1)
x
```
Note these tagged NAs behave identically to regular NAs, even when printing. To see their tags, use `print_tagged_na()`:
```{r}
print_tagged_na(x)
```
To test if a value is a tagged NA, use `is_tagged_na()`, and to extract the value of the tag, use `na_tag()`:
```{r}
is_tagged_na(x)
is_tagged_na(x, "a")
na_tag(x)
```
My expectation is that tagged missings are most often used in conjuction with labels (described below), so labelled vectors print the tags for you, and `as_factor()` knows how to relabel:
```{r}
y <- labelled(x, c("Not home" = tagged_na("a"), "Refused" = tagged_na("z")))
y
as_factor(y)
```
### User defined missing values
SPSS's user-defined values work differently to SAS and Stata. Each column can have either up to three distinct values that are considered as missing, or a range. Haven provides `labelled_spss()` as a subclass of `labelled()` to model these additional user-defined missings.
```{r}
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
x2
```
These objects are somewhat dangerous to work with in R because most R functions don't know those values are missing:
```{r}
mean(x1)
```
Because of that danger, the default behaviour of `read_spss()` is to return regular labelled objects where user-defined missing values have been converted to `NA`s. To get `read_spss()` to return `labelled_spss()` objects, you'll need to set `user_na = TRUE`.
I've defined an `is.na()` method so you can find them yourself:
```{r}
is.na(x1)
```
And the presence of that method does mean many functions with an `na.rm` argument will work correctly:
```{r}
mean(x1, na.rm = TRUE)
```
But generally you should either convert to a factor, convert to regular missing vaues, or strip the all the labels:
```{r}
as_factor(x1)
zap_missing(x1)
zap_labels(x1)
```