library(tidyverse)
library(tidystata)9 Summarize data
- Describe variables with
summ()and compare groups withtabstat(). - Interpret the mean, median, quartiles, and standard deviation.
- Identify the observations contributing to each summary and check whether its units and variable type make it meaningful.
- Compare schooling and family income across educational groups in the 2024 General Social Survey.
What does a typical value look like, and how widely do values vary? Numerical summaries describe quantities such as age, years of schooling, and income through measures of center and spread. As with the percentages in Chapter 8, we must identify which observations contribute to each calculation.
summ() from tidystata produces common statistics for one or more variables. Its companion tabstat() compares selected statistics across groups. We use individual calculations to explain the statistics, then briefly show summarise() for a custom summary.
This chapter builds on inspecting a dataset and reading frequency tables in Chapter 8. We continue with the same 2024 GSS respondents. As in tab(), the dataset is the first argument and column names follow without quotation marks: summ(gss24, age). Commas separate arguments. A named argument, such as .by = degree, sets an option for that call. Each new summary statistic is explained before we interpret it.
After installing the packages (see Section 1.3), load them at the start of your session:
Use Appendix A to create the gss24 extract and save it as gss24.rds. If that file is in your working directory, load it with:
gss24 <- readRDS("gss24.rds")readRDS() restores the saved R object, and <- stores it under the name gss24. You only need to load it once per session.
gss24 comes from the 2024 General Social Survey (GSS), which has interviewed adults in the United States since 1972 about their families, work, education, and attitudes. Our extract has 3,986 respondents and 25 variables. Its degree categories are already prepared; factor construction is not required here. We begin with age, recorded in years, before comparing schooling, working hours, and family income.
9.1 Summarize variables with summ()
In Section 7.3, summ() described complete columns without requiring us to inspect every record. We now use its output to ask what a typical value looks like and how widely values differ. Supply a table and the numeric variable to summarize:
# The type, number of non-missing values, number of distinct values,
# minimum, mean, standard deviation, and maximum of age
summ(gss24, age)# A tibble: 1 × 7
type n unique min mean sd max
<chr> <int> <int> <dbl> <dbl> <dbl> <dbl>
1 dbl 3859 72 18 49.0 17.7 89
Read its columns as follows:
type: the type of the variable (or its unit, if the variable has one).n: the number of non-missing values.unique: the number of distinct non-missing values, which cannot exceedn.min: the minimum.mean: the arithmetic mean, the sum of observed values divided by their number.sd: the standard deviation.max: the maximum.
Here, 3859 respondents share 72 distinct recorded ages. Repeated ages are not missing observations. Finer measurement, such as recording age in months rather than whole years, allows more distinct values, but the observed count also depends on the sample. A high unique does not by itself make a variable continuous or substantively informative: even an identifier can be unique for every case.
The statistics describe recorded ages in years, with each respondent contributing equally. These are unweighted sample summaries, rather than estimates adjusted for how the sample represents the population. The n column makes the number of contributing respondents explicit. This one-variable result does not need a name column.
9.1.1 Extract a column
Chapter 7 explains observations, variables, tidy data, and table previews. Recall that a tibble is a form of data frame: it stores columns together, but its class alone does not determine what an observation means. Each row of gss24 represents a respondent.
The $ operator extracts a column by name. Put the table on the left and the column name on the right. length() counts entries, including missing values; var_type() identifies the object’s type:
age_vector <- gss24$agelength(age_vector)[1] 3986
var_type(age_vector)[1] "double"
The extracted vector has 3,986 entries, stored as doubles, in the same respondent order as the table. Its length includes missing ages. Extracting a column does not remove it from its source.
9.2 Understand center and spread
9.2.1 Ordered positions
The mean and standard deviation introduced in Section 7.3.1 describe average level and spread. A median or quartile answers a different question: where does a value lie among the ordered observations? We first use abstract numbers so every position is visible. c() combines the values in their written order.
sort() takes a vector and returns its values in increasing order. It does not change the saved input:
x <- c(2, 4, 6, 8, 10)
sort(x)[1] 2 4 6 8 10
These five values are already ordered. If their written order changed, sort() would still return the same increasing sequence.
median() returns the middle value after ordering the observations:
median(x)[1] 6
The median is 6: two entries lie below it and two above it. With an even number of values, two occupy the middle. median() averages them:
four_values <- c(2, 4, 6, 8)
median(four_values)[1] 5
The two middle values are 4 and 6, so the median is (4 + 6) / 2, or 5. Here + adds the middle values, parentheses make that addition happen first, and / 2 divides the sum by two. A median need not be an input value.
quantile() takes a numerical vector and probabilities specifying positions. Probabilities run from zero to one; .25 requests the lower quartile and .75 the upper quartile. Supply both in one c() value:
quantile(x, c(.25, .75))25% 75%
4 8
The 25% and 75% headings identify the requested positions. Here the quartiles are 4 and 8, the second and fourth values. Quartiles locate one quarter and three quarters of the way through an ordered distribution. They are not 25% and 75% of the largest number. With other inputs, interpolation can place a cutpoint between recorded values.
9.2.2 Summarize a recorded column
The familiar extraction gss24$age supplies all recorded and missing ages. As with mean() and sd() in Section 7.3.1, an unavailable input requires a decision about the calculation. median() supports its own na.rm argument. Its default is FALSE; TRUE excludes missing ages from this calculation:
median(gss24$age)[1] NA
median(gss24$age, na.rm = TRUE)[1] 48
The first result is NA; the second locates the middle among the 3,859 recorded ages. Neither command fills missing ages or changes gss24.
quantile() also supports na.rm = TRUE. Without it, missing inputs produce an error rather than defined quartiles. Apply the explicit exclusion when requesting the two age cutpoints:
quantile(gss24$age, c(.25, .75), na.rm = TRUE)25% 75%
34 64
The 25% and 75% cutpoints are both in years. Recall that sd() uses the same unit as the original measurements:
sd(gss24$age, na.rm = TRUE)[1] 17.66311
Recorded age ranges from 18 to 89 years. Its median is 48 years, and its standard deviation is about 17.7 years. These describe location and spread among the same respondents with recorded ages. summ() excludes missing measured values automatically and reports the contributing count.
9.3 Summarize several variables
When we summarize several variables, name identifies the variable in each output row:
# Summarize several variables at once
summ(gss24, age, educ, hrs1:realinc)# A tibble: 4 × 8
name type n unique min mean sd max
<chr> <chr> <int> <int> <dbl> <dbl> <dbl> <dbl>
1 age dbl 3859 72 18 49.0 17.7 89
2 educ dbl 3952 21 0 14.2 2.92 20
3 hrs1 dbl 2201 76 0 39.3 13.6 89
4 realinc dbl 3563 26 182. 34252. 34697. 117565.
Using the tidyselect range introduced in Section 3.3, hrs1:realinc selects the columns from hrs1 through realinc in their dataset order. Here those two columns are adjacent. It is a column range, not a sequence of numeric values. age and educ are selected separately. Each output row describes one variable, not one respondent.
educ records completed years of schooling; hrs1 records hours worked last week and is generally available for respondents who were working. realinc measures family income in constant 1986 dollars, adjusted to the purchasing power of dollars in 1986. It is based on income brackets rather than exact amounts. Compare statistics only with their units in mind: a standard deviation in dollars is not directly comparable with one in years or hours.
summ() calculates these statistics from non-missing values. The n column reveals that working hours are missing for 1,785 respondents and family income for 423. Their means therefore describe different sets of respondents. A missing work-hours value need not mean a refusal: some respondents were not asked because they were not working. The coverage diagnostics in Section 10.2 will examine these different contributing samples more closely.
The income example also shows why we compare a mean with a median. Some families have much higher recorded incomes than others. The .stat argument requests statistics by name. c() combines the quoted names into one argument value; here c("n", "mean", "median") asks for the observed count and two measures of center on the same recorded incomes:
summ(gss24, realinc, .stat = c("n", "mean", "median"))# A tibble: 1 × 4
type n mean median
<chr> <int> <dbl> <dbl>
1 dbl 3563 34252. 24502.
The mean of $34,252 exceeds the median of $24,502: high family incomes pull the mean upward. The median locates the middle recorded income among respondents who reported it. Both statistics are in constant 1986 dollars and give each respondent equal influence. Neither describes individual earnings or a survey-weighted population estimate.
9.3.1 Optional reference: detailed statistics
You can skip the detailed output on a first reading. The argument .detail = TRUE requests the full set of available statistics. print(width = Inf) allows all columns to be printed, even when they need several blocks of output:
# Show all available statistics
detailed_gss <- summ(gss24, age, educ, hrs1:realinc, .detail = TRUE)
print(detailed_gss, width = Inf)# A tibble: 4 × 19
name type n unique miss_n valid_pct min q1 median mean
<chr> <chr> <int> <int> <int> <dbl> <dbl> <dbl> <dbl> <dbl>
1 age dbl 3859 72 127 0.968 18 34 48 49.0
2 educ dbl 3952 21 34 0.991 0 12 14 14.2
3 hrs1 dbl 2201 76 1785 0.552 0 35 40 39.3
4 realinc dbl 3563 26 423 0.894 182. 9982. 24502. 34252.
mad sd q3 max iqr skew kurtosis se sum
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 22.2 17.7 64 89 30 0.158 2.00 0.284 189223
2 2.97 2.92 16 20 4 -0.655 5.38 0.0464 56270
3 7.41 13.6 45 89 10 -0.0694 4.69 0.289 86468
4 21527. 34697. 43560 117565. 33578. 1.55 4.30 581. 122040909.
Alongside the familiar columns, miss_n counts missing values and valid_pct gives the non-missing proportion. q1, median, and q3 locate the quartiles; iqr is q3 - q1, the width of the middle half of the distribution. mad is a scaled median absolute deviation, another measure of spread. skew describes asymmetry, while kurtosis describes tail weight relative to the distribution’s spread. se is the standard error of the mean, discussed below, and sum adds the observed values. Not every available statistic is useful for every variable: summing respondents’ ages, for example, does not describe a typical respondent.
summ() uses Stata’s percentile rule, so its quartiles can differ from those produced by R’s default quantile(). A small difference in q1 between the two outputs need not be a calculation error. Two patterns in the detailed output help us describe the distributions:
- For
realinc, the mean ($34,252) is above the median ($24,502), and skewness (skew) is positive. High recorded family incomes extend the upper tail and pull the mean upward. The median locates the middle recorded income, following the comparison in the core lesson. - For
educ, skewness is negative: respondents cluster around high-school and college schooling, with a longer tail toward few completed years. Its observed range is 0 to 20 years. Skewness describes the distribution’s asymmetry; it does not judge the people whose answers produce it.
9.3.2 Choose statistics
Instead of displaying every statistic, .stat requests a few by name. c("n", "valid_pct", "mean") combines three names into a character vector. The name and type columns still identify each variable:
# Choose summaries that show how many respondents contribute
summ(gss24, age, educ, hrs1:realinc, .stat = c("n", "valid_pct", "mean"))# A tibble: 4 × 5
name type n valid_pct mean
<chr> <chr> <int> <dbl> <dbl>
1 age dbl 3859 0.968 49.0
2 educ dbl 3952 0.991 14.2
3 hrs1 dbl 2201 0.552 39.3
4 realinc dbl 3563 0.894 34252.
n counts the non-missing observations. Despite its name, valid_pct is displayed on a 0–1 scale: 1 means complete coverage, and 0.894 means about 89.4% of respondents have a recorded family income. The income mean describes those respondents, not all 3,986 respondents.
Counts alone do not explain why answers are missing. Section 10.2 compares income coverage across degree categories before we use the joint availability of measurements in graphs.
9.3.3 Optional reference: custom calculations
For a custom calculation, first identify the quantities it needs. min() receives a numeric vector and returns its smallest value. Its own na.rm = TRUE option excludes missing inputs from this calculation:
min(gss24$age, na.rm = TRUE)[1] 18
This is the youngest recorded age, in years. max() instead returns the largest value, with its own missing-input instruction:
max(gss24$age, na.rm = TRUE)[1] 89
This is the oldest recorded age. The range is the distance between these extremes: subtract the minimum from the maximum. It differs from standard deviation, which uses distances around the mean.
For a calculation outside the standard summary choices, dplyr’s summarise() lets us write each expression ourselves. It takes a table and returns summary columns. Here we request the mean and range. In the range expression, - subtracts the smallest recorded age from the largest:
summarise(
gss24,
mean_age = mean(age, na.rm = TRUE),
range_years = max(age, na.rm = TRUE) - min(age, na.rm = TRUE)
)# A tibble: 1 × 2
mean_age range_years
<dbl> <dbl>
1 49.0 71
The output has one row and two columns, both in years. A name on the left of = supplies the output heading; the expression on the right supplies its value. The mean agrees with summ(), while the range describes the distance between the extremes rather than the spread around the mean. Neither command changes gss24. All calculations use respondents with recorded ages.
9.3.4 Optional reference: standard error
You can skip this section and continue to the application. Standard deviation describes differences among observations. A standard error concerns how an estimated mean would vary across repeated samples: imagine repeatedly drawing independent random samples of the same size from one population and calculating a mean each time. Those sample means would differ. Their standard deviation is the standard error of the mean.
Under the simple independent-sampling model, we estimate it as sd / sqrt(n), where n counts non-missing observations and sqrt() takes the square root. A larger sample can make a mean more stable without making individual observations less spread out. To inspect that calculation in the teaching data, request n, sd, and se together:
summ(gss24, age, .stat = c("n", "sd", "se"))# A tibble: 1 × 4
type n sd se
<chr> <int> <dbl> <dbl>
1 dbl 3859 17.7 0.284
The two spread measures have the variable’s units, here years, but describe different things. The printed se uses the simple formula above. It does not account for the GSS sampling design and weights or correct systematic missingness, so it should not be treated as a design-adjusted measure of population precision. Inference and survey-design methods require later teaching beyond this chapter.
9.3.5 Check whether a summary is meaningful
With only the dataset argument, summ() examines all columns rather than requiring us to name them individually. This is convenient, but it makes us responsible for deciding which results make sense:
# Select all variables at once
summ(gss24)sex, race, ... are factors (**).
# A tibble: 25 × 8
name type n unique min mean sd max
<chr> <chr> <int> <int> <dbl> <dbl> <dbl> <dbl>
1 id int 3986 3986 1 1994. 1151. 3986
2 age dbl 3859 72 18 49.0 17.7 89
3 **sex fct 3956 2 1 1.55 0.498 2
4 **race fct 3906 3 1 1.54 0.764 3
5 **region fct 3986 4 1 2.68 0.987 4
6 **degree fct 3979 5 1 2.87 1.27 5
7 educ dbl 3952 21 0 14.2 2.92 20
8 paeduc dbl 2866 21 0 12.5 3.88 20
9 maeduc dbl 3445 21 0 12.5 3.48 20
10 **marital fct 3971 5 1 2.80 1.75 5
# ℹ 15 more rows
summ() marks categorical factors with ** and prints a message. Their numerical summaries describe internal category codes, not meaningful measurements; use tab() to describe the categories. It also summarizes id, whose numbers identify respondents. Its mean of 1993.5 is mathematically defined but has no substantive interpretation: assigning different ID numbers would change it without changing anyone’s age, schooling, or income. Numeric storage alone does not make an average meaningful.
9.4 Application: age, schooling, and income in the 2024 GSS
How old are the respondents, how much schooling have they completed, and how many children have they had? How does family income vary? These quantities use different units, so first identify the variables:
ageis in years.educis the number of years of schooling completed.childsis the number of children the respondent has ever had.realincis family income in constant 1986 dollars: amounts adjusted for price changes to the purchasing power of dollars in 1986. The GSS asks about income in brackets, meaning ranges of amounts rather than exact dollar answers, sorealinchas only 26 distinct recorded values.
Use summ() to inspect the distributions together:
summ(gss24, age, educ, childs, realinc)# A tibble: 4 × 8
name type n unique min mean sd max
<chr> <chr> <int> <int> <dbl> <dbl> <dbl> <dbl>
1 age dbl 3859 72 18 49.0 17.7 89
2 educ dbl 3952 21 0 14.2 2.92 20
3 childs dbl 3944 9 0 1.72 1.65 8
4 realinc dbl 3563 26 182. 34252. 34697. 117565.
Each output row describes one variable and uses its non-missing observations, so n differs across rows. For example, the mean of childs can be fractional even though each observed number of children is a whole number: an average need not be an observed value. The mean of realinc is in 1986 dollars, not current dollars or individual earnings. A common output layout does not make the variables interchangeable.
tabstat() returns a table of statistics for a numerical variable. Begin with the table and variable, before choosing options:
tabstat(gss24, realinc)# A tibble: 1 × 2
type mean
<chr> <dbl>
1 dbl 34252.
This describes the recorded family incomes together. Unlike summ()’s variable-oriented layout, tabstat() arranges selected statistics in columns, making it convenient for the group comparison that follows. The source remains unchanged.
Its .stat option requests statistics by quoted names, as in summ(). First choose the non-missing count, mean, and median without dividing the data into groups:
tabstat(gss24, realinc, .stat = c("n", "mean", "median"))# A tibble: 1 × 4
type n mean median
<chr> <int> <dbl> <dbl>
1 dbl 3563 34252. 24502.
Those measures use the same recorded incomes. Now add .by = degree to request a separate row for each degree category:
tabstat(gss24, realinc, .stat = c("n", "mean", "median"), .by = degree)# A tibble: 6 × 5
degree type n mean median
<fct> <chr> <int> <dbl> <dbl>
1 less than high school dbl 287 13210. 7714.
2 high school dbl 1621 24308. 16335
3 associate/junior college dbl 336 31510. 19965
4 bachelor's dbl 783 46575. 36300
5 graduate dbl 530 59746. 43560
6 NA dbl 6 20842. 18513
A missing degree is included as a separate group by default. That group’s summary describes respondents whose degree is unavailable. To compare only the five recorded categories, add .missing = FALSE. This excludes the unknown-degree group; missing income is still excluded separately within every remaining group:
tabstat(gss24, realinc, .by = degree,
.stat = c("n", "mean", "median"), .missing = FALSE)# A tibble: 5 × 5
degree type n mean median
<fct> <chr> <int> <dbl> <dbl>
1 less than high school dbl 287 13210. 7714.
2 high school dbl 1621 24308. 16335
3 associate/junior college dbl 336 31510. 19965
4 bachelor's dbl 783 46575. 36300
5 graduate dbl 530 59746. 43560
Each row now describes an educational group rather than a variable. Its n counts group members with reported income; a recorded degree does not guarantee a recorded income. Median family income rises at every step of education, from $7,714 among respondents without a high school diploma to $43,560 among those with a graduate degree. In every group, the mean is above the median, consistent with high incomes pulling the mean upward. Comparing the two measures helps show why a mean alone can give an incomplete description.
Two cautions apply to these descriptions:
- They describe associations in a sample. They do not show that a degree causes higher income.
- They are unweighted sample statistics: each respondent contributes equally. A survey weight changes a record’s contribution to account for how the sample represents the population. The GSS provides such weights; using them is beyond this edition and planned for later teaching.
9.5 Chapter review
summ()describes variables and reports the number of observed values contributing to each calculation.mean()andmedian()describe center, whilesd()describes spread in the variable’s units.min()andmax()identify extremes;quantile()locates positions such as the lower and upper quartiles.- Check each statistic’s contributing count; excluding missing inputs changes a calculation without replacing missing answers.
tabstat()compares selected statistics across groups;.byidentifies groups and.statchooses the reported statistics.- Optional:
summarise()defines custom calculations;.detailrequests additional statistics fromsumm(), andprint()can widen the display.
Check units, column types, and non-missing counts before interpreting these unweighted sample summaries. Next, Chapter 10 examines item and joint coverage. Graphs in Chapter 11 then explore distributions and relationships.
9.6 Exercises
- Explain why
mean(gss24$age)returnsNA, correct the calculation, and compare its result and contributing sample withsumm(gss24, age). Then compare mean and median family income. Choose a measure for a description of typical income, and report its count, units, and interpretation. - Use the abstract values
c(2, 4, 6, 8)to predict the median before running the code. Find the lower and upper quartiles separately from the median. Why need a cutpoint not equal a recorded value? Then calculate the median and quartiles of recorded GSS schooling in years. - Compare family income across recorded marital-status categories with
tabstat(). Report counts, means, and medians. Explain why a row counts income reporters within a category rather than all its members, and why neither the largest mean nor the largest count establishes a causal relationship. - A researcher compares the standard deviation of age with the standard deviation of income and concludes that income is more variable because its number is larger. Another averages
idto describe a typical respondent. Diagnose both claims using the meanings of the columns. If you read the optional standard-error section, explain why a smaller standard error does not imply that respondents have more similar ages.
Worked answers
Start a fresh session, load the packages, and restore the prepared GSS extract:
library(tidyverse)
library(tidystata)
answer_gss24 <- readRDS("gss24.rds")mean(answer_gss24$age)[1] NA
mean(answer_gss24$age, na.rm = TRUE)[1] 49.03421
summ(answer_gss24, age)# A tibble: 1 × 7
type n unique min mean sd max
<chr> <int> <int> <dbl> <dbl> <dbl> <dbl>
1 dbl 3859 72 18 49.0 17.7 89
summ(answer_gss24, realinc, .stat = c("n", "mean", "median"))# A tibble: 1 × 4
type n mean median
<chr> <int> <dbl> <dbl>
1 dbl 3563 34252. 24502.
Missing ages make the first mean NA. The corrected mean agrees with summ() and uses its reported non-missing count; neither replaces missing ages. For income, the median describes the middle recorded family income because high values pull the mean upward. Among 3,563 income reporters, the median is $24,502, compared with the mean of $34,252. Both use constant 1986 dollars and describe unweighted reported family income. A mean remains appropriate when the question explicitly concerns the arithmetic average.
Start a fresh session, load the packages, and restore the prepared GSS extract:
library(tidyverse)
library(tidystata)
answer_gss24 <- readRDS("gss24.rds")answer_values <- c(2, 4, 6, 8)
sort(answer_values)[1] 2 4 6 8
median(answer_values)[1] 5
quantile(answer_values, c(.25, .75))25% 75%
3.5 6.5
median(answer_gss24$educ, na.rm = TRUE)[1] 14
quantile(answer_gss24$educ, c(.25, .75), na.rm = TRUE)25% 75%
12 16
summ(answer_gss24, educ)# A tibble: 1 × 7
type n unique min mean sd max
<chr> <int> <int> <dbl> <dbl> <dbl> <dbl>
1 dbl 3952 21 0 14.2 2.92 20
The abstract median is 5, midway between 4 and 6. The quartiles are 3.5 and 6.5 under R’s default interpolation, so neither needs to be an input value. GSS schooling cutpoints describe positions among respondents with recorded completed years; n supplies their contributing count. A lower quartile of schooling is a position in that distribution, not one quarter of its largest recorded years. Default quantile() cutpoints can differ from summ()’s Stata percentile rule.
Start a fresh session, load the packages, and restore the prepared GSS extract:
library(tidyverse)
library(tidystata)
answer_gss24 <- readRDS("gss24.rds")tabstat(answer_gss24, realinc, .by = marital,
.stat = c("n", "mean", "median"), .missing = FALSE)# A tibble: 5 × 5
marital type n mean median
<fct> <chr> <int> <dbl> <dbl>
1 married dbl 1492 49107. 36300
2 widowed dbl 227 21084. 13612.
3 divorced dbl 537 23857. 16335
4 separated dbl 118 21644. 14974.
5 never married dbl 1181 23974. 16335
tab(answer_gss24, marital)# A tibble: 6 × 6
marital n percent cum valid valid_cum
<fct> <int> <dbl> <dbl> <dbl> <dbl>
1 married 1659 41.6 41.6 41.8 41.8
2 widowed 269 6.75 48.4 6.77 48.6
3 divorced 579 14.5 62.9 14.6 63.1
4 separated 130 3.26 66.2 3.27 66.4
5 never married 1334 33.5 99.6 33.6 100
6 NA 15 0.376 100 NA NA
Each summary row identifies a recorded marital-status category. Its n counts people in that category who also reported income; the frequency table counts everyone in the category. Income means and medians are in constant 1986 dollars. Group size describes how many observations contribute, and the summaries describe the observed distribution. Neither establishes that changing marital status would cause income to change. Differences in ages and other circumstances may also matter.
Start a fresh session, load the packages, and restore the prepared GSS extract:
library(tidyverse)
library(tidystata)
answer_gss24 <- readRDS("gss24.rds")summ(answer_gss24, age, realinc)# A tibble: 2 × 8
name type n unique min mean sd max
<chr> <chr> <int> <int> <dbl> <dbl> <dbl> <dbl>
1 age dbl 3859 72 18 49.0 17.7 89
2 realinc dbl 3563 26 182. 34252. 34697. 117565.
summ(answer_gss24, id)# A tibble: 1 × 7
type n unique min mean sd max
<chr> <int> <int> <dbl> <dbl> <dbl> <dbl>
1 int 3986 3986 1 1994. 1151. 3986
Age is measured in years and income in constant 1986 dollars. Their standard deviations retain those units, so comparing the printed magnitudes does not give a unit-free ranking of variation. An ID identifies a record; its arithmetic average changes if we renumber respondents and has no substantive interpretation. In the optional section, standard deviation describes differences among individual ages, while standard error describes the sampling variability of an estimated mean under a stated model. More observations can reduce standard error while leaving individual variation unchanged.