How to Calculate Standard Deviation in R

Standard deviation quantifies how spread out your data is from the mean. A low standard deviation means data points cluster tightly around the average, while a high standard deviation indicates...

Key Insights

  • R’s built-in sd() function calculates sample standard deviation (dividing by n-1), not population standard deviation—know which one your analysis requires
  • Always use na.rm = TRUE when working with real-world data that may contain missing values, or your result will be NA
  • For grouped calculations, dplyr’s group_by() %>% summarise() pattern is cleaner and more readable than base R alternatives for most use cases

Introduction

Standard deviation quantifies how spread out your data is from the mean. A low standard deviation means data points cluster tightly around the average, while a high standard deviation indicates they’re scattered across a wider range. This single number tells you whether your data is consistent or variable.

In practical terms, standard deviation helps you understand data quality, identify outliers, compare distributions, and make statistical inferences. If you’re analyzing test scores, stock returns, manufacturing tolerances, or any quantitative data, standard deviation is fundamental to your analysis.

R makes calculating standard deviation straightforward, but there’s a critical distinction you need to understand first: the difference between population and sample standard deviation. Getting this wrong will skew your results.

Population vs. Sample Standard Deviation

The formulas for population and sample standard deviation look nearly identical, but they produce different results:

Population standard deviation (σ): Used when you have data for an entire population. Divides by N (total count).

Sample standard deviation (s): Used when your data represents a sample from a larger population. Divides by n-1 (Bessel’s correction).

The n-1 denominator in sample standard deviation corrects for bias. When you’re estimating population variability from a sample, dividing by n would systematically underestimate the true spread. The n-1 adjustment compensates for this.

Here’s the critical point: R’s sd() function uses the sample formula (n-1) by default. There’s no built-in function for population standard deviation. If you need population SD, you’ll have to calculate it manually.

# Sample data
data <- c(4, 8, 6, 5, 3, 8, 9, 2, 7, 6)
n <- length(data)
mean_val <- mean(data)

# Manual calculation: Sample standard deviation (n-1 denominator)
sample_sd <- sqrt(sum((data - mean_val)^2) / (n - 1))
print(paste("Sample SD (manual):", round(sample_sd, 4)))
# [1] "Sample SD (manual): 2.2706"

# Manual calculation: Population standard deviation (n denominator)
pop_sd <- sqrt(sum((data - mean_val)^2) / n)
print(paste("Population SD (manual):", round(pop_sd, 4)))
# [1] "Population SD (manual): 2.1541"

# Verify R's sd() matches sample formula
print(paste("R's sd() function:", round(sd(data), 4)))
# [1] "R's sd() function: 2.2706"

When should you use each? Use population standard deviation when your dataset includes every member of the group you’re studying—all employees in a company, all products in inventory, or all students in a specific class. Use sample standard deviation (R’s default) when your data represents a subset drawn from a larger population you’re trying to understand.

In practice, sample standard deviation is more common because we rarely have access to complete population data.

Using the Built-in sd() Function

The sd() function is your primary tool for standard deviation calculations in R. Its syntax is simple:

# Basic syntax
sd(x, na.rm = FALSE)

The x argument is your numeric vector, and na.rm controls how missing values are handled.

Basic Usage

# Simple numeric vector
scores <- c(78, 85, 92, 88, 76, 95, 82, 89, 91, 84)
sd(scores)
# [1] 6.055301

# Works with any numeric vector
temperatures <- c(72.5, 68.3, 75.1, 71.8, 69.4)
sd(temperatures)
# [1] 2.669728

Handling Missing Values

Real-world data almost always contains missing values. By default, sd() returns NA if any value is missing:

# Data with missing values
sales <- c(150, 200, NA, 175, 225, 180, NA, 195)

# Default behavior: returns NA
sd(sales)
# [1] NA

# Use na.rm = TRUE to ignore missing values
sd(sales, na.rm = TRUE)
# [1] 26.22975

Always use na.rm = TRUE when working with datasets that might contain missing values. It’s a good habit to include it by default in production code.

Working with Data Frame Columns

Calculating standard deviation for data frame columns works the same way:

# Create a sample data frame
df <- data.frame(
  product = c("A", "B", "C", "D", "E"),
  price = c(29.99, 34.50, 27.00, 31.25, 33.75),
  quantity = c(150, 89, 210, 175, 122)
)

# SD for a single column
sd(df$price)
# [1] 3.013496

sd(df$quantity)
# [1] 45.87592

# Using with dplyr's pull()
library(dplyr)
df %>% pull(price) %>% sd()
# [1] 3.013496

Calculating Population Standard Deviation

Since R lacks a built-in population standard deviation function, you have two options: create a custom function or apply a conversion formula to sd().

Method 1: Conversion Formula

The relationship between sample and population SD allows a simple conversion:

# Population SD from sample SD
pop_sd <- function(x, na.rm = FALSE) {
  if (na.rm) x <- x[!is.na(x)]
  n <- length(x)
  sd(x, na.rm = FALSE) * sqrt((n - 1) / n)
}

# Test it
data <- c(4, 8, 6, 5, 3, 8, 9, 2, 7, 6)
pop_sd(data)
# [1] 2.154066

# With missing values
data_na <- c(4, 8, NA, 6, 5, 3, 8, 9, NA, 2, 7, 6)
pop_sd(data_na, na.rm = TRUE)
# [1] 2.154066

Method 2: Direct Calculation

For clarity, you might prefer calculating from first principles:

pop_sd_direct <- function(x, na.rm = FALSE) {
  if (na.rm) x <- x[!is.na(x)]
  sqrt(mean((x - mean(x))^2))
}

data <- c(4, 8, 6, 5, 3, 8, 9, 2, 7, 6)
pop_sd_direct(data)
# [1] 2.154066

Both methods produce identical results. Use whichever makes your code more readable for your team.

Standard Deviation Across Groups

Calculating standard deviation by group is essential for comparative analysis. Both base R and dplyr handle this well.

Base R with aggregate()

# Sample dataset
sales_data <- data.frame(
  region = c("North", "North", "South", "South", "East", "East", "West", "West"),
  quarter = c("Q1", "Q2", "Q1", "Q2", "Q1", "Q2", "Q1", "Q2"),
  revenue = c(45000, 52000, 38000, 41000, 55000, 58000, 42000, 47000)
)

# SD by region using aggregate()
aggregate(revenue ~ region, data = sales_data, FUN = sd)
#   region  revenue
# 1   East 2121.320
# 2  North 4949.747
# 3  South 2121.320
# 4   West 3535.534

dplyr Approach

The dplyr approach is more flexible and readable, especially for multiple calculations:

library(dplyr)

# SD by region
sales_data %>%
  group_by(region) %>%
  summarise(
    revenue_sd = sd(revenue),
    .groups = "drop"
  )
# # A tibble: 4 × 2
#   region revenue_sd
#   <chr>       <dbl>
# 1 East        2121.
# 2 North       4950.
# 3 South       2121.
# 4 West        3536.

# Multiple grouping variables
sales_data %>%
  group_by(quarter) %>%
  summarise(
    mean_revenue = mean(revenue),
    sd_revenue = sd(revenue),
    .groups = "drop"
  )

Practical Application: Descriptive Statistics Summary

In real analysis, you rarely calculate standard deviation in isolation. Here’s how to build comprehensive summary statistics:

Using dplyr

library(dplyr)

# Comprehensive summary statistics
mtcars %>%
  summarise(
    n = n(),
    mean_mpg = mean(mpg),
    sd_mpg = sd(mpg),
    median_mpg = median(mpg),
    min_mpg = min(mpg),
    max_mpg = max(mpg),
    cv_mpg = sd(mpg) / mean(mpg) * 100  # Coefficient of variation
  )
#    n mean_mpg   sd_mpg median_mpg min_mpg max_mpg   cv_mpg
# 1 32 20.09062 6.026948       19.2    10.4    33.9 30.00285

# Grouped summary
mtcars %>%
  group_by(cyl) %>%
  summarise(
    n = n(),
    mean_mpg = round(mean(mpg), 2),
    sd_mpg = round(sd(mpg), 2),
    .groups = "drop"
  )

Using the psych Package

The psych package provides excellent summary statistics with minimal code:

library(psych)

# Quick descriptive statistics
describe(mtcars[, c("mpg", "hp", "wt")])
#     vars  n   mean    sd median trimmed   mad   min    max range  skew kurtosis   se
# mpg    1 32  20.09  6.03  19.20   19.70  5.41 10.40  33.90 23.50  0.61    -0.37 1.07
# hp     2 32 146.69 68.56 123.00  141.19 77.10 52.00 335.00 283.00  0.73    -0.14 12.12
# wt     3 32   3.22  0.98   3.33    3.15  0.77  1.51   5.42  3.91  0.42    -0.02 0.17

# Grouped statistics
describeBy(mtcars$mpg, group = mtcars$cyl)

Conclusion

Calculating standard deviation in R is straightforward once you understand the key distinctions. Use sd() for sample standard deviation, which covers most analytical scenarios. Apply the conversion formula or a custom function when you need population standard deviation. Always include na.rm = TRUE when working with real data.

For grouped calculations, dplyr’s group_by() %>% summarise() pattern offers the cleanest syntax. Combine standard deviation with other descriptive statistics to build complete data summaries that tell the full story of your data’s distribution.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.