This tutorial follows a small survey from inspection to tabulation, grouped summaries and new variables. Results are ordinary R objects that you can save, filter and join. Install tidystata as shown on the package homepage before starting.
Load data and inspect variables
library(tidystata)
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
survey <- tibble(
id = 1:6,
group = c("A", "A", "A", "B", "B", NA),
response = c("yes", "no", NA, "yes", "yes", "no"),
score1 = c(2, 4, NA, 6, 8, 10),
score2 = c(4, NA, NA, 8, 10, 12)
)
attr(survey$score1, "label") <- "First assessment score"
codebook(survey)
#> # A tibble: 5 x 5
#> variable label type n unique
#> <chr> <chr> <chr> <int> <int>
#> 1 id NA integer 6 6
#> 2 group NA character 5 2
#> 3 response NA character 5 2
#> 4 score1 First assessment score double 5 5
#> 5 score2 NA double 4 4
variables(survey)
#> # A tibble: 5 x 2
#> name label
#> <chr> <chr>
#> 1 id NA
#> 2 group NA
#> 3 response NA
#> 4 score1 First assessment score
#> 5 score2 NA
ds(survey, starts_with("score"))
#> [1] "score1" "score2"codebook() gives one row per variable.
variables() reports names and variable labels, while
ds() returns a character vector of names.
variables_search() searches names and labels, so it can
find a variable from its description:
variables_search(survey, "assessment")
#> # A tibble: 1 x 2
#> name label
#> <chr> <chr>
#> 1 score1 First assessment score
select_or_transmute(survey, id, starts_with("score"))
#> # A tibble: 6 x 3
#> id score1 score2
#> <int> <dbl> <dbl>
#> 1 1 2 4
#> 2 2 4 NA
#> 3 3 NA NA
#> 4 4 6 8
#> 5 5 8 10
#> 6 6 10 12Count categories
counts <- tab(survey, response)
counts
#> # A tibble: 3 x 6
#> response n percent cum valid valid_cum
#> <chr> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 no 2 33.3 33.3 40 40
#> 2 yes 3 50 83.3 60 100
#> 3 NA 1 16.7 100 NA NA
sum(counts$n)
#> [1] 6
fre(survey, response)
#> # A tibble: 5 x 6
#> response n percent cum valid valid_cum
#> <chr> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 no 2 33.3 33.3 40 40
#> 2 yes 3 50 83.3 60 100
#> 3 Valid Total 5 83.3 NA 100 NA
#> 4 NA 1 16.7 100 NA NA
#> 5 Total 6 100 NA NA NA
tab2(survey, group, response)
#> # A tibble: 3 x 4
#> `group \\ response` no yes `NA`
#> <chr> <int> <int> <int>
#> 1 A 1 1 1
#> 2 B 0 2 0
#> 3 NA 1 0 0The counts sum to six, including the missing response.
tab() returns observed categories with counts and
percentages. fre() adds total rows; tab2()
puts the categories of its second variable into columns. Use
tab1() when you want separate one-way tables instead of a
joint table:
tables <- tab1(survey, group, response)
tables$response
#> # A tibble: 3 x 6
#> value n percent cum valid valid_cum
#> <chr> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 no 2 33.3 33.3 40 40
#> 2 yes 3 50 83.3 60 100
#> 3 NA 1 16.7 100 NA NAThe returned objects contain data. For example, select categories occurring more than once with an ordinary pipeline:
Summarise measurements
Select numeric measurements deliberately. Codes attached to categories are not automatically meaningful measurements.
summ(survey, score1, score2, .stat = c("n", "mean", "sd", "missing"))
#> # A tibble: 2 x 6
#> name type n mean sd missing
#> <chr> <chr> <int> <dbl> <dbl> <int>
#> 1 score1 dbl 5 6 3.16 1
#> 2 score2 dbl 4 8.5 3.42 2
tabstat(survey, score1, score2, .by = group)
#> # A tibble: 6 x 4
#> group name type mean
#> <chr> <chr> <chr> <dbl>
#> 1 A score1 dbl 3
#> 2 A score2 dbl 4
#> 3 B score1 dbl 7
#> 4 B score2 dbl 9
#> 5 NA score1 dbl 10
#> 6 NA score2 dbl 12
mean(survey$score1, na.rm = TRUE)
#> [1] 6summ() can return several statistics.
tabstat() selects means by default. Both omit missing
measured values from numeric calculations. Missing grouping keys remain
as a group unless you request .missing = FALSE:
summ(survey, score1, .by = group, .missing = FALSE, .stat = c("n", "mean"))
#> # A tibble: 2 x 4
#> group type n mean
#> <chr> <chr> <int> <dbl>
#> 1 A dbl 2 3
#> 2 B dbl 2 7Numeric-only summaries return a table. When numeric and calendar
measurements are combined, the default can be a list of tables. The
companion
vignette("containers-and-missing-values", package = "tidystata")
explains those types and their missing-value conventions.
Prepare new variables
Row helpers return vectors, making them convenient in
mutate(). pick() makes the columns being
combined explicit.
prepared <- survey |>
mutate(
average = row_mean(pick(score1, score2)),
observed = row_non_miss(pick(score1, score2))
)
prepared
#> # A tibble: 6 x 7
#> id group response score1 score2 average observed
#> <int> <chr> <chr> <dbl> <dbl> <dbl> <int>
#> 1 1 A yes 2 4 3 2
#> 2 2 A no 4 NA 4 1
#> 3 3 A NA NA NA NA 0
#> 4 4 B yes 6 8 7 2
#> 5 5 B yes 8 10 9 2
#> 6 6 NA no 10 12 11 2The third row has no observed score, so its average is
NA and its observed count is zero. Row sums use zero for an
all-missing row, while column summaries use NA for a sum
with no observations.
Character measurements can be parsed explicitly, and sentinel values can be replaced before calculations:
destring(c("1,200", "2,500", NA))
#> [1] 1200 2500 NA
mvdecode(c(1, -99, 3), -99)
#> [1] 1 NA 3
xtile(1:8, 4)
#> [1] [1,2.5] [1,2.5] (2.5,4.5] (2.5,4.5] (4.5,6.5] (4.5,6.5] (6.5,8]
#> [8] (6.5,8]
#> Levels: [1,2.5] < (2.5,4.5] < (4.5,6.5] < (6.5,8]destring() reads numbers with currency symbols,
thousands separators and percent signs; other text, such as
"10 kg", becomes NA with a warning unless you
remove it with ignore = "kg". xtile() uses
Stata’s percentile cutpoints and keeps tied values together; ties can
leave some groups empty.
Next steps
Read Containers and missing values for output conventions, or Coming from Stata to find the R equivalent of a Stata command.