Quick summary
Summarize this blog with AI
Choose a kurtosis definition before comparing values
Kurtosis summarizes a fourth standardized moment. Two scales are common: Pearson kurtosis has a normal-population reference value of 3, while excess kurtosis subtracts 3 and has reference value 0. The e1071::kurtosis() function returns an excess-kurtosis estimator. Subtracting 3 from its result again is an error.
This tutorial uses a fixed vector, R 4.5.1 and e1071 1.7-17. Install e1071 if it is not already available. All code blocks run in order. The calculations are descriptive examples, not a normality test or a financial risk forecast.
Calculate moment-based Pearson and excess kurtosis
Let m2 = mean((x - mean(x))^2) and m4 = mean((x - mean(x))^4). Using the same denominator n for both moments gives Pearson kurtosis m4 / m2^2 and moment excess kurtosis g2 = m4 / m2^2 - 3. A zero variance makes this ratio undefined.
moment_kurtosis <- function(x) {
if (!is.numeric(x) || anyNA(x) || any(!is.finite(x)) || length(x) < 2) {
stop("x must contain at least two finite numeric values without NA")
}
# A common change of units leaves standardized kurtosis unchanged.
magnitude <- max(abs(x))
if (magnitude == 0) stop("positive variance is required")
unit_x <- x / magnitude
deviations <- unit_x - mean(unit_x)
m2 <- mean(deviations^2)
m4 <- mean(deviations^4)
if (!is.finite(m2) || !is.finite(m4) || m2 <= 0) {
stop("finite positive variance and finite fourth moment are required")
}
pearson <- m4 / m2^2
if (!is.finite(pearson)) stop("standardized fourth moment is not finite")
c(pearson = pearson, excess = pearson - 3)
}
x <- 1:10
moments <- moment_kurtosis(x)
print(round(moments, 6)) # pearson 1.775758; excess -1.224242
stopifnot(isTRUE(all.equal(unname(moments), c(293 / 165, -202 / 165))))
Match the e1071 estimator explicitly
types <- vapply(1:3, function(t) e1071::kurtosis(x, type = t), numeric(1))
names(types) <- c("type1", "type2", "type3")
print(round(types, 6)) # -1.224242 -1.200000 -1.561636
n <- length(x)
g2 <- unname(moments["excess"])
expected <- c(
g2,
((n + 1) * g2 + 6) * (n - 1) / ((n - 2) * (n - 3)),
(g2 + 3) * (1 - 1 / n)^2 - 3
)
stopifnot(isTRUE(all.equal(unname(types), expected)))
stopifnot(isTRUE(all.equal(e1071::kurtosis(x), unname(types[3]))))
Type 1 is the moment excess coefficient above. Type 2 applies the displayed finite-sample adjustment and requires more than three observations. Type 3 is the package default and uses the sample-variance convention. All three results are already on an excess scale. When comparing packages or published numbers, report the estimator type and sample size; the word “kurtosis” alone is insufficient.
Handle missing and degenerate inputs
with_missing <- c(x, NA_real_)
stopifnot(is.na(e1071::kurtosis(with_missing, type = 1)))
stopifnot(isTRUE(all.equal(e1071::kurtosis(with_missing, type = 1,
na.rm = TRUE), g2)))
constant_error <- tryCatch(moment_kurtosis(rep(7, 10)), error = function(e) e)
missing_error <- tryCatch(moment_kurtosis(with_missing), error = function(e) e)
stopifnot(inherits(constant_error, "error"), inherits(missing_error, "error"))
stopifnot(isTRUE(all.equal(unname(moment_kurtosis(c(1e-100, 2e-100))),
c(1, -2))))
stopifnot(isTRUE(all.equal(unname(moment_kurtosis(c(-1e308, 1e308))),
c(1, -2))))
Removing NA values changes the analyzed sample. Report the number removed and whether the missingness could be informative. Do not assign a constant vector a kurtosis of zero: zero excess is a reference value on a defined scale, while zero variance makes standardized kurtosis undefined. Taking fourth powers in the original units can overflow or underflow. The helper first divides all observations by the same largest absolute magnitude, then centers and calculates moments. This common change of units leaves kurtosis unchanged; the tiny and huge two-value fixtures both give Pearson kurtosis 1 and excess -2. Any remaining nonfinite result is rejected explicitly.
Interpret the value without overclaiming
For the fixed vector 1 through 10, the type-1 excess coefficient is about -1.224. This is a property of that sample, not proof that a population has no extreme outcomes. A positive or negative sample excess value alone does not establish normality, estimate an outlier probability or predict a future event. Fourth powers make the estimate sensitive to unusual observations and sample size.
Check how one changed observation affects the result
changed <- c(1:9, 100)
comparison <- c(original = moment_kurtosis(x)[["excess"]],
changed = moment_kurtosis(changed)[["excess"]])
print(round(comparison, 6))
stopifnot(comparison[["changed"]] > comparison[["original"]])
This controlled comparison changes one observation and holds the sample size fixed. It illustrates sensitivity; it is not permission to delete that observation. Investigate whether an unusual value is an error or a legitimate measurement, and examine the data distribution alongside the numerical summary.