Espartaco

“Is that to say we are against Free Trade? No, we are for Free Trade, because by Free Trade all economical laws, with their most astounding contradictions, will act upon a larger scale, upon the territory of the whole earth; and because from the uniting of all these contradictions in a single group, where they will stand face to face, will result the struggle which will itself eventuate in the emancipation of the proletariat.”

Karl Heinrich Marx · Marx-Engels Collected Works, Vol. VI, p. 290

28,022 views since December 2020

28,022 visitas desde diciembre de 2020

EnglishEspañol

Category: GitHub

  • GENERAL PRINCIPLES OF PROGRAMMING IN RSTUDIO: A STATISTICAL-MATHEMATICAL APPROACH

    GENERAL PRINCIPLES OF PROGRAMMING IN RSTUDIO: A STATISTICAL-MATHEMATICAL APPROACH

    Statistical notebooks · Mathematics and programming

    R Programming Fundamentals

    A Statistical and Mathematical Approach

    JOSÉ MAURICIO GÓMEZ JULIÁN

    Originally published: September 11, 2021
    Revised and expanded edition: November 2, 2026

    A program organizes transformations of representations. Understanding it requires connecting the form of the data, the meaning of the operations, and the conditions under which a result can become knowledge.

    This chapter integrates the original document’s contents into a self-contained, revised, and expanded exposition: from operators to data structures; from functions to recursion; from a rule on integers to the analysis of economic information. Code examples are provided as copyable text, with mathematical distinctions accompanying the practical work.

    Starting point

    1. Programming as a Way of Knowing

    Programming in R means representing data, defining operations on those data, and organizing their execution so that the result can be inspected and reproduced. A correct instruction must satisfy two requirements: express the intended calculation and preserve the meaning of the information being processed. The first requirement is computational; the second concerns the scientific problem, its concepts, and the way observations were produced.

    This chapter connects operators, vectors, lists, matrices, statistical variables, and data frames with functions, conditions, iteration, and recursion. Applications range from student grades to Collatz dynamics and the treatment of gross domestic product growth rates. The discussion addresses both how to write instructions and why those instructions represent particular mathematical operations, together with the limits of their interpretation.

    R, RStudio, and working files

    R is the language and computing environment. RStudio is an integrated development environment that helps users edit programs, execute instructions, inspect objects, and display plots. R can be used without RStudio; the functions introduced here belong to R or to explicitly identified packages. R supports functional programming and several systems of object-oriented programming. You do not need to master the latter before beginning to calculate and analyze data.

    An .R file stores instructions; a data file stores observations. The console evaluates expressions and displays results. The editor allows you to construct a program that can be executed again in the same order. The object pane helps inspect what has been created, but the program must preserve the evidence of how each object was obtained. A useful practice is therefore to begin with a fresh session and execute the code from the beginning.

    R / 01 An instruction, an object, and a result
    # Lines starting with # are comments.
    v1 <- 10
    v3 <- 150 / v1
    v3
    # [1] 15
    
    getwd()           # Current working directory.
    R.version.string  # R version used to run the program.

    The <- symbol binds a name to a value in an environment. After the first instruction, v1 retrieves the number 10; after the second, v3 retrieves the result 15. An identifier must be distinguished from a character string: v1 looks up an object, whereas "v1" represents text. R is case-sensitive: Data and data are different names.

    From the question to the result

    01Define

    Question, unit of analysis, and variables.

    02Represent

    Objects, types, dimensions, and missingness.

    03Calculate

    Functions, conditions, and algorithms.

    04Interpret

    Result, assumptions, and limitations.

    These stages inform and correct one another. An import anomaly may reveal a formatting problem; an inadmissible operation may expose confusion between categories and quantities; a numerically valid result may answer a different question from the one under investigation. Statistical programming requires keeping the relationship between representation, procedure, and interpretation visible.

    Documentation and references: [1] · [2]

    Syntax and meaning

    2. Operators, Comparisons, and Logic

    Assignment, equality, and separators

    R / 02 Assigning, comparing, and distinguishing a name from its contents
    x <- 12
    12 -> y
    z = 12
    identical(x, y)
    # [1] TRUE
    
    x == z  # Compares values; does not assign a new value.
    # [1] TRUE
    x <- 12; x  # Two expressions on the same line.
    # [1] 12
    
    hello_value <- 5
    greeting <- "hello"
    greeting
    # [1] "hello"

    Rightward assignment -> exists, although this chapter generally uses <- for a consistent reading direction. The = sign also permits assignment in some contexts; inside a call, it usually matches arguments to names. In mean(x = 1:3), x = identifies the function argument. The == operator compares values. The <= operator means “less than or equal to” and must not be confused with assignment.

    A semicolon ; separates expressions on the same line. In the console, evaluating a name may display its value through automatic printing; the semicolon is not a printing function. A comma , separates arguments in calls and index positions in multidimensional structures. In an R numeric constant, a period marks the decimal part: 1.65. A decimal comma in an imported file is specified through reading options and is a separate matter.

    Basic punctuation in the language
    ElementPurposeExample
    <-Assign a value to a namex <- 3
    == / !=Compare equality / inequalityx != 4
    ;Separate expressionsx <- 3; x
    ,Separate arguments or indicessum(1, 2); M[2, 3]
    ()Calls and groupingmean(x); (2 + 3) * 4
    {}Group instructionsThe body of a function or loop
    [] / [[]]Select subsets / extract componentsx[2]; L[[2]]

    Sequences and arithmetic operations

    R / 03 Finite sequences and arithmetic
    2:5
    # [1] 2 3 4 5
    5:2
    # [1] 5 4 3 2
    
    a <- 17
    b <- 5
    c(addition = a + b, subtraction = a - b, product = a * b,
      division = a / b, quotient = a %/% b, remainder = a %% b,
      power = b^2)
    # addition 22; subtraction 12; product 85; division 3.4;
    # quotient 3; remainder 2; power 25.
    
    typeof(8)
    # [1] "double"
    typeof(8L)
    # [1] "integer"

    For numeric arguments, a:b generates a sequence with increments of 1 or −1. 2:5 is a vector of four ordered values. It does not represent the closed real interval [2, 5], which contains infinitely many real numbers. A vector must also be distinguished from a set: it preserves positions and may contain repetitions. If the endpoints do not differ by an integer, the final endpoint may not appear in the sequence.

    a = b⌊a/b⌋ + (a mod b)
    For a nonzero divisor, and within the limitations of machine arithmetic, %/% and %% connect quotient and remainder. For 17 and 5: 17 = 5 × 3 + 2.

    The L suffix in 8L requests an integer constant; it does not perform an arithmetic operation. Powers are written with ^; R also accepts **. Parentheses make the intended order explicit. For example, -2^2 is interpreted as -(2^2), whereas (-2)^2 returns 4.

    Elementwise comparisons

    R / 04 Exact comparison and numerical approximation
    v <- c(2, 5.5, 6, 9)
    t <- c(8, 2.5, 14, 9)
    v > t
    # [1] FALSE TRUE FALSE FALSE
    v == t
    # [1] FALSE FALSE FALSE TRUE
    v != t
    # [1] TRUE TRUE TRUE FALSE
    
    c(0.1 + 0.2 == 0.3,
      isTRUE(all.equal(0.1 + 0.2, 0.3)))
    # [1] FALSE TRUE

    The operators <, >, <=, >=, ==, and != return logical values. For compatible vectors, comparison takes place by position. Use identical() for complete object identity; all.equal() may be more suitable for comparing numerical quantities within a tolerance. The decimal example illustrates a limitation of binary floating-point representation, not a contradiction in arithmetic.

    Conjunction, disjunction, negation, and membership

    Truth table for known logical values
    pqp & qp | q!p
    FALSEFALSEFALSEFALSETRUE
    FALSETRUEFALSETRUETRUE
    TRUEFALSEFALSETRUEFALSE
    TRUETRUETRUETRUEFALSE
    R / 05 Logical filters and membership tests
    u <- c(TRUE, FALSE, TRUE, FALSE)
    w <- c(TRUE, TRUE, FALSE, FALSE)
    u & w
    # [1] TRUE FALSE FALSE FALSE
    u | w
    # [1] TRUE TRUE TRUE FALSE
    !u
    # [1] FALSE TRUE FALSE TRUE
    c(3, 8, 3) %in% c(1, 3, 5)
    # [1] TRUE FALSE TRUE
    
    x <- c(2, 5, 8, NA_real_)
    x[!is.na(x) & x > 3 & x < 8]
    # [1] 5
    any(u)
    # [1] TRUE
    all(u)
    # [1] FALSE

    Conjunction & requires both conditions to hold. Disjunction | is inclusive: it is also true when both conditions are true. Negation ! reverses known logical values. Mathematically, it is an involution because applying it twice recovers the initial value; describing it as an isomorphism requires specifying the structure being preserved. %in% checks whether each element of its left argument appears in its right argument.

    R / 06 Short-circuit evaluation and scalar conditions
    divide_if_valid <- function(x, y) {
      if (length(y) == 1L && is.numeric(y) &&
          !is.na(y) && is.finite(y) && y != 0) {
        x / y
      } else {
        stop("The divisor must be a finite, scalar, nonzero number.")
      }
    }
    divide_if_valid(10, 2)
    # [1] 5

    The operators && and || evaluate from left to right and stop once the result is determined. In the example, the function checks the length and type of y before evaluating value-based scalar conditions. These operators require length-one operands in current R versions. To summarize a logical vector, explicitly choose any() or all(), depending on the question.

    Documentation and references: [1] · [3] · [4] · [7]

    The elementary structure

    3. Vectors and Sequences

    A vector stores elements in ordered positions. Atomic vectors have a single basic storage type; lists can combine components of different types and sizes. Atomic types include logical, integer, double, complex, character, and raw. The word “numeric” may be used broadly to include integers and doubles, so inspect the type with typeof() when the distinction matters.

    A numeric vector of length n can represent an element of ℝn. This interpretation is useful for algebraic operations, but it does not characterize every R vector: a list or a vector of words requires other interpretations. A simple vector also has no row or column orientation by default; dimensions make that orientation explicit when a matrix is used.

    Constructing vectors, allocating space, and assigning positions

    R / 07 Concatenation and preallocation
    v1 <- c(3, 1, 5, 6, 7)
    v2 <- c(TRUE, FALSE, TRUE)
    v3 <- c("ONE", "Monday", "Team")
    integer_vector <- c(2L, 4L, 6L)
    complex_vector <- c(1 + 2i, 3 - 1i)
    
    a <- vector(mode = "numeric", length = 4)
    a
    # [1] 0 0 0 0
    a[1:4] <- 1
    a
    # [1] 1 1 1 1
    
    vector("character", 3)
    # [1] "" "" ""
    vector()
    # logical(0)

    c() combines values. vector() allocates a structure of a specified type and length and initializes its positions. By default, it creates an empty logical vector; filling with zeros applies to numeric mode. The assignment a[1:4] <- 1 changes positions in an existing object. In algorithms that produce many results, allocating space before iteration avoids repeatedly rebuilding the vector.

    Selecting positions, excluding them, and preserving names

    R / 08 Extracting subsets
    v1[2:3]
    # [1] 1 5
    v1[c(1, 5)]
    # [1] 3 7
    v1[-c(2, 4)]
    # [1] 3 5 7
    v1[v1 >= 5]
    # [1] 5 6 7
    
    ages <- c(Ana = 20, Luis = 24, Eva = 21)
    ages[c("Eva", "Ana")]
    # Eva Ana
    #  21  20

    Positive indices select positions; negative indices exclude them; logical indices select where a condition is true; names refer to identified elements. Positive and negative indices cannot be mixed in one selection, apart from zeros. Indexing starts at 1. Extraction can repeat and reorder elements, so it is not generally a subset operation in the strict set-theoretic sense, and still less a selection of “subgroups” in the algebraic sense.

    seq(), rep(), and sequence(): different ways to generate sequences

    R / 09 Sequences with controlled steps, repetitions, and lengths
    seq(from = 1, to = 10, by = 2)
    # [1] 1 3 5 7 9
    seq(from = 10, to = -8, by = -3)
    # [1] 10 7 4 1 -2 -5 -8
    seq(from = 0, to = 1, length.out = 5)
    # [1] 0.00 0.25 0.50 0.75 1.00
    
    rep(3, times = 7)
    # [1] 3 3 3 3 3 3 3
    rep(c("A", "B"), times = 2)
    # [1] "A" "B" "A" "B"
    rep(c("A", "B"), each = 2)
    # [1] "A" "A" "B" "B"
    
    sequence(c(1, 2, 3))
    # [1] 1 1 2 1 2 3
    sequence(c(1, 3))
    # [1] 1 1 2 3
    
    seq_len(0)
    # integer(0)
    1:0
    # [1] 1 0
    seq_along(v1)
    # [1] 1 2 3 4 5

    seq() supports noninteger increments or a prescribed number of positions. rep() distinguishes repeating a complete vector from repeating each element. In these examples, sequence() concatenates sequences beginning at 1 with the specified lengths. Its function name is lowercase. For loop indices, seq_len(n) and seq_along(x) avoid the problem created by 1:n when n is zero.

    Vectorization and recycling

    R / 10 Positionwise operations and repeated operands
    x <- c(1, 2, 3, 4)
    x * 2
    # [1] 2 4 6 8
    x + c(10, 20)
    # [1] 11 22 13 24
    
    # Avoid accidental recycling: check lengths where appropriate.
    y <- c(10, 20, 30, 40)
    stopifnot(length(x) == length(y))
    x + y
    # [1] 11 22 33 44

    Vectorization expresses an operation over positions without an explicit loop. When lengths differ, many operations recycle the shorter vector. R usually warns when the longer length is not a multiple of the shorter one, but exact recycling can occur without warning and still make no sense for the data. If an operation represents paired measurements, verify the correspondence between observations as well as their lengths.

    Summaries and locating results

    R / 11 Summary functions and vector manipulation
    base_vector <- seq(1, 9, by = 2)
    c(length_value = length(base_vector), addition = sum(base_vector),
      mean_value = mean(base_vector), product = prod(base_vector),
      minimum = min(base_vector), maximum = max(base_vector),
      variance = var(base_vector), standard_deviation = sd(base_vector))
    # length 5; sum 25; mean 5; product 945;
    # minimum 1; maximum 9; variance 10; standard deviation 3.162278.
    range(base_vector)
    # [1] 1 9
    diff(range(base_vector))
    # [1] 8
    cumsum(base_vector)
    # [1] 1 4 9 16 25
    
    vector02 <- c(10, 11, 12, 13, 14, 15, 2, 3, 4, 6, 5, 6,
                  6, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11)
    sort(vector02, decreasing = FALSE)
    sort(vector02, decreasing = TRUE)
    order(vector02)       # Ordering positions, rather than the values.
    which.max(vector02)   # First position of the maximum: 6.
    which.min(vector02)   # First position of the minimum: 7.
    unique(vector02)      # Distinct values in order of first appearance.
    letters[1:10]
    LETTERS[1:10]

    range() returns the minimum and maximum; their difference is the range width. which.max() and which.min() return the first position of an extreme, whereas max() and min() return its value. unique() removes repetitions and does not calculate eigenvalues. Eigenvalues concern linear operators and square matrices through a different mathematical question. letters and LETTERS are built-in vectors of the 26 basic Latin letters in lowercase and uppercase; they do not themselves include the full alphabet of every language.

    x̄ = (1/n) ∑i=1n xi
    s2 = [1/(n − 1)] ∑i=1n (xi − x̄)2
    s = √s2
    The mean summarizes level. Variance accumulates squared deviations from the mean; var() uses the divisor n − 1. sd() returns its square root in the original units. Sample variance is undefined with fewer than two available values.

    The divisor n − 1 reflects the loss of one degree of freedom when the mean is estimated from the same sample. Under independent sampling with finite variance, this correction gives an unbiased estimator of population variance. To describe the dispersion of an entire finite population using its average squared deviation, the appropriate divisor may instead be n. The choice depends on the statistical object and should be explicit.

    Pseudorandom sampling and reproducibility

    R / 12 Seed, replacement, and generating distribution
    set.seed(20210911)
    sample_without_replacement <- sample(1:20, size = 5, replace = FALSE)
    set.seed(20210911)
    repeat_sample <- sample(1:20, size = 5, replace = FALSE)
    stopifnot(identical(sample_without_replacement, repeat_sample))
    
    set.seed(20210911)
    sample_with_replacement <- sample(1:3, size = 10, replace = TRUE)
    simulated_normal <- rnorm(100, mean = 0, sd = 1)

    sample() selects from a specified population; replace = TRUE permits repeated selection of the same source position. rnorm() generates values according to a normal distribution using a pseudorandom generator. A seed fixes a state of that generator: it does not make a sample representative or, on its own, guarantee identical results across every R version and configuration. For exact reproduction, also record the version and, where necessary, RNGkind().

    Documentation and references: [6] · [11] · [13]

    Unknown information

    4. Missing Values and Statistical Decisions

    A missing value indicates absent information at a position belonging to the data structure. NA abbreviates Not Available. Depending on the database design, a cell may be missing because of nonresponse, recording error, or structural absence; these causes have different meanings. Code identifies missing-value markers, but explaining why data are missing requires knowledge of the observation process.

    Distinguishing missingness from other special results
    ObjectBasic meaningCheck
    NAA missing-value indicator, with typed variants.is.na(x)
    NaNA numerical result that is not a number, such as 0/0.is.nan(x)
    NULLThe null object; may indicate the absence of a component.is.null(x)
    "NA"A two-letter string unless explicitly converted during import.is.character(x)
    InfFloating-point infinity; not a missing observation.is.infinite(x)
    R / 13 Detecting missingness and counting available observations
    vector03 <- c(10, 11, 12, 13, NA_real_, NA_real_)
    is.na(vector03)
    # [1] FALSE FALSE FALSE FALSE TRUE TRUE
    sum(vector03)
    # [1] NA
    sum(vector03, na.rm = TRUE)
    # [1] 46
    length(vector03)
    # [1] 6
    sum(!is.na(vector03))
    # [1] 4
    
    is.na(c(NA_real_, NaN, Inf))
    # [1] TRUE TRUE FALSE
    is.nan(c(NA_real_, NaN, Inf))
    # [1] FALSE TRUE FALSE

    In many operations, an unknown input produces an unknown result. This is not a question of adding objects with incompatible units: if an addend is unknown, the sum may also be unknown. is.na() returns the missingness mask; x == NA is not the correct detection procedure. A string such as "NA" remains text until explicitly translated into a missing-value marker.

    Excluding from a calculation, removing from an object, and replacing

    R / 14 Three interventions with different effects
    # 1. Exclude from the calculation: the original object is unchanged.
    observed_mean <- mean(vector03, na.rm = TRUE)
    observed_mean
    # [1] 11.5
    
    # 2. Create another vector without the missing positions.
    without_na <- vector03[!is.na(vector03)]
    without_na_alternative <- as.vector(na.omit(vector03))
    stopifnot(identical(without_na, without_na_alternative))
    
    # 3. Explicit replacement: demonstrates an operation, not an imputation recommendation.
    vector04 <- replace(vector03, which(is.na(vector03)), 0)
    mean(vector04)
    # [1] 7.666667
    
    fruit <- c("apple", "orange", "grape", "banana")
    replace(fruit, list = 2, values = "blueberry")
    # [1] "apple" "blueberry" "grape" "banana"

    The na.rm = TRUE option changes which inputs a function considers without rewriting the vector. The selection x[!is.na(x)] constructs another vector and reduces its length. na.omit() may add attributes recording omitted positions. replace() returns a modified object, which must be assigned to retain it. which() turns a logical condition into the positions where it is true; many replacements can also use a logical mask directly.

    Replacing missing values with zero changes both the mean and the meaning of the information: it turns “unknown” into “zero.” The example demonstrates this numerically. Statistical imputation requires justification of the missingness model and procedure; excluding cases can also introduce bias. Report how many values were used and consider whether missingness relates to the variable of interest.

    R / 15 A function that reports the effective number of observations
    all_missing <- c(NA_real_, NA_real_)
    sum(all_missing, na.rm = TRUE)
    # [1] 0
    mean(all_missing, na.rm = TRUE)
    # [1] NaN
    
    available_summary <- function(x) {
      if (!is.numeric(x)) stop("A numeric vector is required.")
      if (any(!is.na(x) & !is.finite(x))) {
        stop("Infinite values were found.")
      }
      observed <- x[!is.na(x)]
      n <- length(observed)
      c(n_total = length(x), n_observed = n,
        n_missing = sum(is.na(x)),
        mean_value = if (n > 0L) mean(observed) else NA_real_,
        median_value = if (n > 0L) median(observed) else NA_real_,
        variance = if (n > 1L) var(observed) else NA_real_,
        standard_deviation = if (n > 1L) sd(observed) else NA_real_,
        minimum = if (n > 0L) min(observed) else NA_real_,
        maximum = if (n > 0L) max(observed) else NA_real_)
    }
    available_summary(vector03)

    Logic with unknown information

    R / 16 Determinate and indeterminate results
    FALSE & NA
    # [1] FALSE
    TRUE | NA
    # [1] TRUE
    TRUE & NA
    # [1] NA
    FALSE | NA
    # [1] NA
    !NA
    # [1] NA

    A conjunction is already false if one term is false, whatever the unknown value of the other term. A disjunction is already true if one term is true. In the other two cases, the available information is insufficient. This extension explains why a table containing only TRUE and FALSE does not describe the full logical behavior of R.

    Documentation and references: [5]

    Heterogeneous information

    5. Coercion, Lists, and Components

    Implicit coercion and explicit conversion

    Coercion changes an object’s representation from one type to another. When combining ordinary atomic vectors, R seeks a common type; among the types used here, a useful hierarchy is logical → integer → double → complex → character. This hierarchy describes a combination rule; it does not prohibit explicit conversions in the opposite direction. Some conversions are possible, while others produce missing values or lose information.

    R / 17 Conversion, a common type, and information loss
    typeof(c(TRUE, 2L))
    # [1] "integer"
    typeof(c(2L, 3.5))
    # [1] "double"
    c(1, "two", TRUE)
    # [1] "1" "two" "TRUE"
    
    as.numeric(c("12", "3.5"))
    # [1] 12.0 3.5
    as.integer(3.9)
    # [1] 3
    
    # A failed conversion produces a warning and an NA.
    failed_conversion <- tryCatch(
      as.numeric("text"),
      warning = function(w) NA_real_
    )
    stopifnot(is.na(failed_conversion))

    Conversion to integer truncates the fractional part, which can alter a measurement. The text "12" has a numeric interpretation; "text" does not. A conversion warning should be investigated rather than hidden in a real analysis. Here it is caught only to allow the reader to execute the whole block and observe the result defined for that case.

    Lists preserve different components

    R / 18 Containing a component versus extracting its value
    record <- list(
      name = "Ana",
      grades = c(90, 55, 89, 68),
      active = TRUE,
      metadata = list(group = "A", date = as.Date("2021-09-11"))
    )
    record["grades"]       # A one-component list.
    record[["grades"]]     # The numeric vector stored in that component.
    record$grades          # Another way to extract it by name.
    record[["grades"]][2]
    # [1] 55
    record$metadata$group
    # [1] "A"
    length(record)
    # [1] 4
    str(record)

    A list organizes heterogeneous objects without forcing them into one atomic type. It can contain vectors, other lists, matrices, and model results. [ preserves a list of selected components; [[ extracts a component; $ makes extraction by a literal name convenient. For programmatic access using a name stored in a variable, prefer record[[column_name]].

    A named list can serve purposes similar to a dictionary, but its names may be duplicated and access follows R list semantics. Flattening it with unlist() may coerce values to a common type, removing the structural separation that preserved different components. It should therefore not be used as an automatic substitute for extracting a particular component.

    Documentation and references: [6]

    From positions to axes

    6. Matrices, Operations, and Arrays

    Creating a matrix and naming its axes

    R / 19 Dimensions and filling direction
    vm01 <- c(10, 7, 1, 6, 8, 3)
    matrix01 <- matrix(data = vm01, nrow = 2, ncol = 3, byrow = TRUE)
    colnames(matrix01) <- c("Col_1", "Col_2", "Col_3")
    rownames(matrix01) <- c("Row_1", "Row_2")
    matrix01
    #       Col_1 Col_2 Col_3
    # Row_1    10     7     1
    # Row_2     6     8     3
    
    matrix(vm01, nrow = 2, ncol = 3, byrow = FALSE)
    #      [,1] [,2] [,3]
    # [1,]   10    1    8
    # [2,]    7    6    3
    dim(matrix01)
    # [1] 2 3
    length(matrix01)
    # [1] 6

    A matrix has two axes: rows and columns. data supplies the elements, nrow the number of rows, and ncol the number of columns. byrow = TRUE distributes the sequence across rows; FALSE, the default, proceeds down columns. This option determines where the initial sequence is placed; it does not sort values from smallest to largest. Atomic matrices have one common element type; list matrices also exist.

    Combining rows and columns

    R / 20 Binding along compatible axes
    matrix03 <- matrix(c(10, 1, 8, 7, 6, 3), nrow = 2,
                       ncol = 3, byrow = TRUE)
    colnames(matrix03) <- colnames(matrix01)
    rownames(matrix03) <- c("New_1", "New_2")
    row_bound_matrix <- rbind(matrix03, matrix01)
    column_bound_matrix <- cbind(matrix01, matrix03)
    dim(row_bound_matrix)
    # [1] 4 3
    dim(column_bound_matrix)
    # [1] 2 6

    rbind() stacks rows and requires compatible columns; cbind() adds columns and requires compatible rows. When combining data, verify the identity and order of units as well as dimensions. Two matrices with the same number of rows may refer to different people, or to the same people in a different order.

    Inspecting values, constructing masks, and filtering

    R / 21 A condition and the extraction that uses it
    row_bound_matrix[2, 3]
    # [1] 3
    row_bound_matrix[, 3]
    # New_1 New_2 Row_1 Row_2
    #       8       3     1     3
    
    mask <- row_bound_matrix[, 3] > 2
    mask
    # [1] TRUE TRUE FALSE TRUE
    row_bound_matrix[mask, , drop = FALSE]
    row_bound_matrix[row_bound_matrix[, 3] > 2, , drop = FALSE]
    
    matrix01[1, , drop = FALSE]  # Preserves a one-row matrix.
    matrix01[1, ]               # Simplifies to a vector.

    The expression M[, 3] > 2 produces a mask indicating which positions satisfy the condition. The expression M[mask, ] uses that mask to extract rows. Distinguishing these operations clarifies what each line returns. An empty index before the comma keeps all rows; an empty index after it keeps all columns. drop = FALSE preserves two dimensions when extraction selects a single row or column.

    Axis totals, sorting, and transposition

    R / 22 MARGIN = 1 for rows and MARGIN = 2 for columns
    apply(X = row_bound_matrix, MARGIN = 2, FUN = sum)
    # Col_1 Col_2 Col_3
    #    33    22    15
    apply(X = row_bound_matrix, MARGIN = 1, FUN = sum)
    # New_1 New_2 Row_1 Row_2
    #      19      16    18    17
    
    stopifnot(
      isTRUE(all.equal(colSums(row_bound_matrix),
                       apply(row_bound_matrix, 2, sum))),
      isTRUE(all.equal(rowSums(row_bound_matrix),
                       apply(row_bound_matrix, 1, sum)))
    )
    apply(row_bound_matrix, 2, sort)
    apply(row_bound_matrix, 1, sort)
    t(row_bound_matrix)

    apply() receives an object, the axis or axes organizing the calculations, and the function to apply. For sums, rowSums() and colSums() express the intention directly. If the applied function returns several values, apply() may reorganize the result’s dimensions; for example, when sorting by row, it may place each result in a column. Inspect dim() to confirm the resulting structure.

    Sorting each column separately breaks the original relationships between variables in a row. If each row represents a person, this transformation can combine ages, heights, and measurements from different people. To sort observations while preserving rows, construct a single index with order() and apply it to every column. Transposition with t() exchanges axes; it does not sort values.

    Elementwise multiplication and matrix multiplication

    R / 23 Hadamard product, inner product, and matrix multiplication
    matrix01 * matrix03
    #       Col_1 Col_2 Col_3
    # Row_1   100     7     8
    # Row_2    42    48     9
    
    matrix04 <- t(matrix03)
    matrix01 %*% matrix04
    #       New_1 New_2
    # Row_1     115      115
    # Row_2      92       99
    
    u <- c(1, 2, 3)
    v <- c(4, 5, 6)
    u * v
    # [1] 4 10 18
    sum(u * v)
    # [1] 32
    drop(crossprod(u, v))
    # [1] 32
    (A ⊙ B)ij = aijbij
    (AB)ij = ∑k=1p aikbkj
    Elementwise multiplication requires compatible shapes. For matrix multiplication, A is m × p and B is p × n; the result is m × n. Each entry combines a row of A with a column of B.

    The symbol * denotes elementwise multiplication, also called the Hadamard product for matrices of the same size. %*% denotes matrix multiplication. For numeric vectors, the Euclidean inner product sums products of corresponding positions. Distinguishing these three operations avoids assigning the ambiguous label “vector multiplication” to a single expression.

    Arrays and their relationship to tensors

    R / 24 An array with three axes
    A <- array(1:12, dim = c(2, 3, 2),
               dimnames = list(row = c("f1", "f2"),
                               column = c("c1", "c2", "c3"),
                               slice = c("t1", "t2")))
    A[, , "t1"]
    dim(A)
    # [1] 2 3 2
    length(A)
    # [1] 12

    In R, an array organizes a vector through a dimension attribute. It can have one, two, or more axes. A matrix is the two-axis case. Creating an array does not amount to performing a tensor product: it constructs a computational representation. A numeric array can store tensor components relative to chosen bases, but an abstract tensor and the container holding its components are not conceptually identical.

    Documentation and references: [8] · [9]

    What is measured and how it is encoded

    7. Statistical Variables and Factors

    Statistical classification describes a variable’s nature and the operations that make sense on its values. Computational classification describes storage and behavior in the language. The two are connected through encoding decisions; neither determines the other on its own. A number used as a person’s identifier does not become a quantity suitable for averaging merely because it is stored numerically.

    Variable nature and encoding structure
    Statistical natureExamplePossible representation in RInterpretive criterion
    Discrete numericNumber of visitsinteger or doubleSeparated values, often counts.
    Continuous numericHeightdoubleA magnitude modeled on a continuum; measurement and storage have finite precision.
    Nominal categoricalType of clothingfactor or characterCategories without intrinsic order.
    Ordinal categoricalRating: poor, fair, goodordered / factorMeaningful order; distances between categories need not be equal.
    R / 25 Factor levels, order, and internal codes
    Clothing <- factor(c("Shirt", "Shoes", "Coat", "Socks"))
    levels(Clothing)
    # [1] "Coat" "Shirt" "Shoes" "Socks"
    
    Response <- factor(c("Good", "Fair", "Fair", "Poor"),
                        levels = c("Poor", "Fair", "Good"),
                        ordered = TRUE)
    Response
    # [1] Good Fair Fair Poor
    # Levels: Poor < Fair < Good
    Response > "Fair"
    # [1] TRUE FALSE FALSE FALSE
    
    encoded_measurement <- factor(c("10", "20", "10"))
    as.numeric(encoded_measurement)
    # [1] 1 2 1
    as.numeric(as.character(encoded_measurement))
    # [1] 10 20 10

    A factor represents categories through integer codes and labels called levels. Alphabetical order in a nominal factor’s levels does not create a substantive hierarchy. In an ordered factor, order is deliberately specified using levels and ordered = TRUE. Even then, the distance between “Poor” and “Fair” need not equal that between “Fair” and “Good.”

    If the labels represent numbers, first recover them as text and then convert them. If they represent genuine categories, converting their internal codes to numbers does not justify averaging the categories. Decisions about measurement scales precede function calls. Models that accept factors may construct contrasts from their levels; these contrasts also depend on ordering and the chosen configuration.

    Documentation and references: [11]

    Tables with statistical structure

    8. Data Frames: Observations and Variables

    A data.frame brings together variables with the same number of rows. In the usual arrangement of individual-level data, each row corresponds to an observational unit and each column to a variable. A cell contains that variable’s observed value for that unit. In longitudinal data, a row may represent a combination of unit and time, so specify what identifies a record before analyzing it.

    R / 26 Constructing columns inside or outside the call
    Data_01 <- data.frame(
      Height = c(1.65, 1.70, 1.89),
      Hair_length = c("Short", "Medium", "Long"),
      stringsAsFactors = FALSE
    )
    Height <- c(1.65, 1.70, 1.89)
    Hair_length <- c("Short", "Medium", "Long")
    Data_02 <- data.frame(Height, Hair_length,
                           stringsAsFactors = FALSE)
    stopifnot(identical(Data_01, Data_02))
    
    Data_03 <- data.frame(Height = Height, Hair = Hair_length,
                           stringsAsFactors = FALSE)
    Data_03
    #   Height  Hair
    # 1   1.65 Short
    # 2   1.70 Medium
    # 3   1.89 Long

    The first two constructions produce the same table through different ways of organizing the program. The third provides column names explicitly. Since R 4.0.0, data.frame() uses stringsAsFactors = FALSE by default; it is written here to make the decision visible. When a variable is categorical, deliberately construct a factor with appropriate levels.

    Related structures with different properties
    FeatureAtomic matrixData frame
    OrganizationTwo axes; one common atomic type.Columns that may have different types.
    Frequent useAlgebraic computation and homogeneous arrays.Tables of observations and variables.
    Inspectiondim(), typeof(), str().str(), names(), and column classes.
    SelectionM[i, j].d[i, j], d[[j]], d$variable.
    Conversionas.data.frame(M).as.matrix(d), checking coercion.

    A data frame is a list with class data.frame and shared-row constraints; its columns can include logical and numeric types, text, factors, dates, and more complex structures supported by its methods. There is no general guarantee that converting a matrix into a data frame makes every filter or calculation faster. The appropriate structure depends on the information, the operations, and actual performance measurements.

    Extracting columns, rows, and subsets

    R / 27 Selecting while preserving meaning and result shape
    matrix_df <- as.data.frame(matrix01)
    column1 <- matrix_df$Col_1
    stopifnot(identical(column1, matrix_df[["Col_1"]]))
    matrix_df[, 1]                 # A vector.
    matrix_df[1]                   # A one-column data frame.
    matrix_df[, 1, drop = FALSE]    # Preserves the tabular structure.
    matrix_df[2, , drop = FALSE]
    matrix_df[, c("Col_1", "Col_2"), drop = FALSE]
    matrix_df[, c(1, 2), drop = FALSE]
    
    Data_01[Data_01$Height >= 1.70, , drop = FALSE]
    names(Data_03) <- c("Height_m", "Hair")

    Selecting one column may return a vector or a table depending on the form of indexing. The distinction matters when writing functions expecting a particular structure. Col_1 and col_1 are different names. When a name is stored in a variable, [[ extracts that exact column and helps distinguish the object’s name from its textual contents.

    Inspection before analysis

    R / 28 Structure, classes, and missing values by column
    head(Data_01)
    tail(Data_01)
    str(Data_01)
    summary(Data_01)
    dim(Data_01)
    nrow(Data_01)
    ncol(Data_01)
    vapply(Data_01, typeof, character(1))
    colSums(is.na(Data_01))

    head() shows the first six rows by default and tail() the last six; when fewer exist, they show those available. str() describes structure and types. summary() produces summaries depending on each column’s class. dim() returns rows and columns; nrow() and ncol() inspect them separately. View() opens an interactive viewer when the environment supports it, but reproducible checks must be preserved as instructions.

    Documentation and references: [10]

    From file to object

    9. Importing, Exporting, and Preserving Data

    Importing means interpreting a file’s bytes and constructing R objects. Exporting means writing a representation of those objects in another format. The extension provides a clue, but the procedure must match the actual contents, separators, header, decimal symbol, and missing-value markers. A .txt file may have a header or lack one; a CSV may use commas or, under regional conventions, semicolons. None of these properties is determined by the filename alone.

    Paths, directories, and projects

    R / 29 Portable paths and existence checks
    # A temporary directory makes the example independent of existing files.
    example_directory <- tempfile("r_chapter_")
    dir.create(example_directory)
    csv_path <- file.path(example_directory, "data.csv")
    file.exists(csv_path)
    # [1] FALSE: the file has not been written yet.
    
    # Example paths stored as text; the files need not exist.
    relative_path <- file.path("data", "Data01.xlsx")
    windows_path <- "C:/Users/User/Downloads/Data01.xlsx"

    An absolute path identifies a file from the filesystem root. A relative path is interpreted from the working directory. A relative path can be sufficient when the file is in the appropriate location; writing a full path is not always necessary. file.path() constructs paths with suitable separators. Forward slashes / can be used on Windows; a literal backslash inside a string must be escaped. Hierarchical folders provide a useful visual analogy, but are not themselves inheritance or encapsulation in object-oriented programming.

    Text, CSV, and regional conventions

    R / 30 Verified writing and reading with base R
    write.csv(Data_01, file = csv_path, row.names = FALSE,
              na = "", fileEncoding = "UTF-8")
    reimported <- read.csv(csv_path, header = TRUE, sep = ",", dec = ".",
                            na.strings = c("", "NA"),
                            stringsAsFactors = FALSE,
                            fileEncoding = "UTF-8")
    stopifnot(isTRUE(all.equal(Data_01, reimported)))
    
    text_path <- file.path(example_directory, "data.txt")
    write.table(Data_01, file = text_path, sep = "\t", dec = ".",
                row.names = FALSE, col.names = TRUE, quote = TRUE,
                na = "", fileEncoding = "UTF-8")
    from_text <- read.table(text_path, header = TRUE, sep = "\t", dec = ".",
                            na.strings = c("", "NA"),
                            stringsAsFactors = FALSE,
                            fileEncoding = "UTF-8")
    
    regional_path <- file.path(example_directory, "regional_data.csv")
    write.csv2(Data_01, file = regional_path, row.names = FALSE)
    from_regional <- read.csv2(regional_path, stringsAsFactors = FALSE)
    stopifnot(isTRUE(all.equal(Data_01, from_text)),
              isTRUE(all.equal(Data_01, from_regional)))
    Declare the layout instead of inferring it from the extension
    OptionQuestion it answers
    header / col.namesDoes the first row contain column names when reading / will those names be written?
    sepWhich character separates fields?
    decWhich symbol marks the decimal part?
    na.strings / naWhich strings will be read as missing / how will missing values be written?
    skipHow many initial lines will be skipped before interpreting the data?
    row.namesAre additional row identifiers interpreted or written?
    fileEncodingWhich character encoding is used?

    read.csv() defaults to a comma separator and decimal point; read.csv2() uses a semicolon separator and decimal comma. The option must match the file. For text without a header, use header = FALSE and assign names afterward. On export, row.names = FALSE avoids adding an index column that often reappears as an unwanted variable on reimport.

    Excel: sheets, formats, and preliminary rows

    R / 31 One sheet, multiple sheets, and separate files
    # Install these packages once if needed:
    # install.packages(c("readxl", "writexl"))
    
    if (requireNamespace("readxl", quietly = TRUE) &&
        requireNamespace("writexl", quietly = TRUE)) {
      excel_path <- file.path(example_directory, "All_Data.xlsx")
      writexl::write_xlsx(
        list(Datos01 = Data_01, Datos02 = Data_03), path = excel_path
      )
      IMP_01 <- readxl::read_excel(excel_path, sheet = "Data01")
      IMP_02 <- readxl::read_excel(excel_path, sheet = "Data02")
      IMP_first <- readxl::read_excel(excel_path)
    
      # One table per file:
      writexl::write_xlsx(IMP_01,
                         path = file.path(example_directory, "Export1.xlsx"))
      writexl::write_xlsx(IMP_02,
                         path = file.path(example_directory, "Export2.xlsx"))
      stopifnot(nrow(IMP_01) == nrow(Data_01))
    } else {
      message("The Excel example requires the readxl and writexl packages.")
    }

    read_excel() accepts .xls and .xlsx and attempts to detect the format. read_xls() and read_xlsx() specify it directly. The file must exist at the declared path and its actual format must match the reading procedure. Omitting sheet uses the first sheet even when the workbook contains others. readxl returns a tibble, a tabular class inheriting from data.frame, with printing and indexing differences from a base data frame.

    skip = 3 is appropriate when the first three rows contain preliminary material and the header follows. Determine that number by inspecting the particular file. When an explicit range is specified, its rules take precedence over other sheet-geometry options. To export several sheets with writexl, provide a named list, one component per sheet. This approach avoids requiring Java, although other valid alternatives exist for more complex Excel editing and formatting tasks.

    From an import wizard to a reproducible procedure
    Operation in a graphical interfaceInstruction that must be recorded
    Select a filePath and verification with file.exists().
    Select a sheetThe sheet argument or a declared range.
    Skip preliminary rowsskip, based on inspection of the document.
    Declare headers and missingnesscol_names, header, na, or na.strings.
    Confirm the importstr(), dim(), names, types, and missing-value counts.

    RStudio’s import wizards can help configure a reading operation. To preserve the procedure, copy the generated code into the working script and inspect its arguments. The pane does not replace a record of the operation. An RDS file provides another option: it preserves an R object with its classes and attributes, whereas CSV preserves a textual table and may lose those properties.

    R / 32 Preserving an object with its attributes
    rds_path <- file.path(example_directory, "data.rds")
    saveRDS(Data_01, file = rds_path)
    restored_data <- readRDS(rds_path)
    stopifnot(identical(Data_01, restored_data))

    Documentation and references: [18] · [19]

    Connections with mathematics

    10. Functions, Composition, and Functionals

    A function and its call

    An R function receives arguments, evaluates instructions in an environment, and returns a value. It may also produce effects such as displaying text or writing a file. The returned result and those effects are distinguishable. In mathematics, a function assigns each element of a domain to exactly one element of a codomain; a particular computational function may fail, fail to terminate, or depend on external state. Identifying it with a mathematical mapping therefore requires specifying its domain, conditions, and effects.

    R / 33 Definition, formal argument, and evaluation
    square <- function(x) {
      x^2
    }
    square(3)
    # [1] 9
    square(c(1, 2, 3))
    # [1] 1 4 9
    is.function(square)
    # [1] TRUE
    formals(square)
    body(square)

    In function(x), x is a formal argument. In square(3), the supplied value is 3. The function is vectorized because the ^ operation in its body accepts vectors. A user-defined function may be short even if built-in functions perform related tasks; defining one helps express an intention, validate inputs, and automate a repeatable procedure.

    Composing calls is different from passing functions

    R / 34 Results as arguments and functions as arguments
    sum(c(1:50))
    # [1] 1275
    
    f <- function(x) 2 * x
    g <- function(x) x + 1
    f(g(3))
    # [1] 8
    
    apply_to <- function(x, fun) {
      fun(x)
    }
    apply_to(1:5, mean)
    # [1] 3
    apply_to(1:5, sum)
    # [1] 15

    In sum(c(1:50)), a vector is constructed and its elements are then summed. sum() receives the resulting vector, not the function c as an object. This structure illustrates nested calls and composition of operations. By contrast, apply_to(1:5, mean) supplies the function mean itself as an argument. The second structure is an example of a higher-order function.

    (f ∘ g)(x) = f(g(x))
    ∑i=1n i = n(n + 1)/2
    The first identity evaluates g and then f, assuming compatible domains. In the second, pairing the first and last summands gives n + 1; the argument yields n(n + 1)/2 and verifies 1275 when n = 50.
    Classification by properties rather than code length
    ConceptDistinguishing propertyExample
    Nested callsOne call appears inside another.sum(c(1, 2, 3))
    Inner functionIts definition occurs inside another function.A function created by make_offset().
    Higher-order functionReceives a function or returns a function.apply_to(x, mean).
    Functional in R programmingReceives a function and produces results by applying it.lapply(list_object, mean).
    Mathematical functionalA scalar-valued mapping on a suitable function space or other appropriate objects.A function is mapped to its definite integral.
    Control-flow constructDetermines which instructions execute and when.if, for, while.

    Mathematical functionals and numerical evaluation

    𝓘(f) = ∫01 f(t) dt
    𝓘(t ↦ t2) = 1/3
    The argument is a whole function; the result is a scalar. On a suitable class, such as continuous real-valued functions on [0, 1], this mapping is well defined. The integration variable t ranges over the interval and is not a free parameter of the result.
    R / 35 An integral and two functionals applied to data
    integrand <- function(t) t^2
    integral_result <- integrate(integrand, lower = 0, upper = 1)
    integral_result$value
    # [1] 0.3333333
    integral_result$abs.error
    stopifnot(abs(integral_result$value - 1/3) < 1e-12)
    
    groups <- list(A = c(1, 2, 3), B = c(4, 5, 6))
    lapply(groups, mean)
    vapply(groups, mean, numeric(1))
    # A B
    # 2 5

    The mathematical integral and its approximation algorithm are different objects: integrate() returns a list containing an approximate value, an estimated absolute error, and other components. lapply() applies a function to each component and preserves a list of results. vapply() adds an expected output type and shape; here each group must produce a numeric scalar. apply(), already used with matrices, also receives a function through FUN.

    The uses of “functional” in programming and functional analysis are related but not universally identical. The computational terminology allows vector-valued outputs and data objects that require further specification to be interpreted within a mathematical function space. A precise connection identifies domain, operation, and codomain in each case, rather than inferring equivalence merely because one instruction contains another.

    Operators and primitive functions

    Many operators can be called as functions, for example `+`(2, 3). This does not make every built-in function an operator or every operator a low-level primitive function. is.primitive() inspects that technical property. An operator can be unary, binary, or user-defined, and may act on objects other than numbers. Its meaning depends on the operation and, when dispatch applies, on the classes of its arguments.

    R / 36 Being primitive is a technical property, not a measure of simplicity
    `+`(2, 3)
    # [1] 5
    is.primitive(sum)
    # [1] TRUE
    is.primitive(mean)
    # [1] FALSE

    Documentation and references: [14] · [15]

    Organizing execution

    11. Conditions, Loops, and Student Grades

    Scalar decisions and vectorized ifelse()

    R / 37 An explicit set of rules
    grades <- c(90, 55, 89, 68)
    average_grade <- mean(grades)
    average_grade
    # [1] 75.5
    
    if (average_grade >= 70) {
      cat("Passed the course\n")
    } else if (average_grade >= 60) {
      cat("Resit examination\n")
    } else {
      cat("Failed the course\n")
    }
    
    demo_averages <- c(75.5, 66.75, 47.25, NA_real_)
    ifelse(is.na(demo_averages), "No information",
           ifelse(demo_averages >= 70, "Passed",
                  ifelse(demo_averages >= 60, "Resit", "Failed")))
    # [1] "Passed" "Resit" "Failed" "No information"

    This illustration assumes a grading scheme in which 70 or above is a pass, 60 to below 70 allows a resit examination, and below 60 is a fail. Adapt these thresholds to the applicable regulations. Once the first condition has excluded values of 70 or above, the second need only ask whether 60 has been reached. Write a closing brace and else together as } else { to avoid parsing problems when running the code.

    if requires a nonmissing condition of length one. ifelse() selects elements over a vector of conditions and returns an object with its shape. It is not a universal replacement for if: output types and attributes require inspection, and evaluated branches may calculate values for positions that are not ultimately selected. Scalar logic and side effects normally call for if.

    What cat(), print(), and return() mean

    Displaying and returning are different operations
    InstructionPurposeRelevant result
    cat(..., sep = " ", file = "")Concatenate and write a textual representation.Normally returns NULL invisibly; produces output.
    print(x)Display an object through the method appropriate to its class.Usually returns the object invisibly.
    return(x)End a function’s evaluation and return a value.The caller can use the returned value.
    Last expression in the bodyImplicit return when no earlier return is used.Its value becomes the function result.

    In its usual uses, cat() accepts atomic arguments and names; a nonempty list needs a suitable representation or a function such as print(). The options sep, fill, labels, file, and append control separators, line filling, and destination. Writing a message does not automatically construct a reusable result. An analysis function commonly returns a table or list and leaves presentation to another instruction.

    Iterating over known values

    R / 38 A loop, its vectorized counterpart, and an accumulator
    for (x in seq_len(5)) {
      print(round(sqrt(x), digits = 2))
    }
    # [1] 1
    # [1] 1.41
    # [1] 1.73
    # [1] 2
    # [1] 2.24
    
    round(sqrt(seq_len(5)), digits = 2)
    # [1] 1.00 1.41 1.73 2.00 2.24
    
    Accumulator <- 0
    for (i in seq_len(5)) {
      if (i %% 2 == 0) {
        Accumulator <- Accumulator + i
      }
    }
    Accumulator
    # [1] 6

    A for loop assigns successive elements of a collection to the iteration variable and executes its body. The first example computes square roots of independent values: it is not recursion and does not use the previous root as the next input. Rounding to two decimal places controls numerical presentation; it does not achieve “infinitesimal precision.” The sum accumulator starts at zero because zero is the additive identity, not because the first even number is unknown.

    Three students, four assessments, and a tabular result

    R / 39 One row per student and one column per assessment
    grades1 <- c(90, 55, 89, 68)
    grades2 <- c(70, 65, 65, 67)
    grades3 <- c(40, 59, 70, 20)
    grade_matrix <- rbind(Ana = grades1, Bruno = grades2, Carla = grades3)
    colnames(grade_matrix) <- c("Exam_1", "Exam_2", "Exam_3", "Project")
    grade_averages <- rowMeans(grade_matrix)
    grade_averages
    #   Ana Bruno Carla
    # 75.50 66.75 47.25
    
    status <- character(length(grade_averages))
    for (j in seq_along(grade_averages)) {
      current_average <- grade_averages[j]
      if (is.na(current_average)) {
        status[j] <- "No information"
      } else if (current_average >= 70) {
        status[j] <- "Passed"
      } else if (current_average >= 60) {
        status[j] <- "Resit"
      } else {
        status[j] <- "Failed"
      }
    }
    course_results <- data.frame(
      student = names(grade_averages), average = unname(grade_averages), status,
      stringsAsFactors = FALSE
    )
    course_results

    Each row identifies a student, so averages are calculated by row. If the interpretation changed so that each row represented a subject, the meaning of averaging that row would need review. Displaying three vectors separated by semicolons does not construct a matrix; rbind() does. Equal weights are an assumption: when assessments have different weights, use a weighted mean with coherent weights and a positive total weight.

    x̄w = [∑j=1k wjxj] / [∑j=1k wj]
    Each grade x_j is multiplied by its weight w_j. Equal positive weights recover the simple mean. Weights represent the agreed contribution of each assessment.
    R / 40 A differently weighted project changes the average
    weighted.mean(grades1, w = c(0.20, 0.20, 0.20, 0.40))
    # [1] 74

    while, repeat, break, and next

    R / 41 Choose the structure according to the repetition criterion
    # while: check before each execution.
    counter <- 0L
    while (counter < 3L) {
      counter <- counter + 1L
    }
    stopifnot(counter == 3L)
    
    # repeat: an explicit exit using break.
    counter <- 0L
    repeat {
      counter <- counter + 1L
      if (counter >= 3L) break
    }
    
    # next: skip the rest of the current iteration.
    even_values <- integer(0)
    for (i in seq_len(6)) {
      if (i %% 2 != 0) next
      even_values <- c(even_values, i)
    }
    even_values
    # [1] 2 4 6

    while may never execute its body if its initial condition is false. repeat needs an explicit exit or may continue indefinitely. break exits the innermost loop; next advances to its next iteration. These are language control-flow constructs, not object classes. Clarity improves when the chosen structure expresses the actual repetition criterion and unnecessary nested conditions are reduced.

    Documentation and references: [4] · [12]

    Automating without hiding assumptions

    12. Custom Functions and Validation

    Four decisions when defining a function

    Choose a name expressing the task; declare arguments and defaults; build the calculation and its checks; and return an object with a useful structure. Braces delimit the body. return() may appear before the last line to end evaluation early; it is neither a compulsory typographical closing marker nor a printing instruction. Describe accepted inputs and the conditions that produce errors.

    R / 42 A complete function summing even numbers from 1 through n
    validate_integer <- function(x, minimum = 0, name = "x", maximum = 1e6) {
      if (length(x) != 1L || !is.numeric(x) || is.na(x) ||
          !is.finite(x) || x != floor(x) || x < minimum || x > maximum) {
        stop(name, " must be a scalar integer between ", minimum, " and ", maximum, ".")
      }
      invisible(TRUE)
    }
    
    sum_even <- function(n) {
      validate_integer(n, minimum = 0, name = "n")
      accumulator <- 0
      for (i in seq_len(n)) {
        if (i %% 2 == 0) accumulator <- accumulator + i
      }
      accumulator
    }
    sum_even(50)
    # [1] 650
    sum_even(0)
    # [1] 0

    The one-million limit keeps this iterative exercise manageable; it is not a mathematical boundary of the problem. Negative, fractional, infinite, missing, and incorrectly sized inputs are rejected. seq_len(0) creates an empty traversal and correctly preserves a zero sum. The function returns a number that can be used in further calculations.

    m = ⌊n/2⌋
    2 + 4 + ⋯ + 2m = 2∑j=1mj = m(m + 1)
    There are m positive even integers through n. Each is twice an integer from 1 to m. Applying the sum formula gives m(m + 1), independently checking the loop result.
    R / 43 Comparing methods and returning reusable classifications
    sum_even_formula <- function(n) {
      validate_integer(n, minimum = 0, name = "n")
      m <- floor(n / 2)
      m * (m + 1)
    }
    stopifnot(sum_even(50) == sum_even_formula(50),
              sum_even(0) == sum_even_formula(0),
              sum_even(5) == sum_even_formula(5))
    
    parity <- function(x) {
      if (!is.numeric(x) || anyNA(x) || any(!is.finite(x)) ||
          any(x != floor(x)) || any(abs(x) > 2^53 - 1)) {
        stop("Finite integers exactly representable as double are required.")
      }
      ifelse(x %% 2 == 0, "Even", "Odd")
    }
    parity(1:6)
    # [1] "Odd" "Even" "Odd" "Even" "Odd" "Even"
    parity(50)
    # [1] "Even"

    The parity function accepts a vector or a single value. Traversing 1:n answers “What is the parity of each integer in this traversal?”; evaluating only n answers “What is the parity of this value?” The vectorized function preserves the distinction through its input. The numerical bound avoids claiming exact parity for values whose representation no longer guarantees all consecutive integers.

    Documentation and references: [1] · [16]

    A mathematical laboratory

    13. Collatz: Orbits, Iteration, and Recursion

    The Collatz problem allows us to study an elementary rule whose evolution can produce long trajectories. For positive integers, the transformation divides an even value by two and replaces an odd value with three times that value plus one. The assertion of eventual arrival at 1 concerns every positive seed. Traversing particular examples verifies those examples and explores patterns; it does not turn exploration into a universal proof.

    T(n) = n/2, if n is even3n + 1, if n is odd
    n0 = a,   nk+1 = T(nk)
    The seed a is a positive integer. Each application of T produces one transition. The observed trajectory is the sequence n_0, n_1, … . The standard rule sends 1 to 4; our exploration algorithms stop upon reaching 1.

    One transition and a trajectory algorithm

    R / 44 Local state, preallocation, and explicit stopping
    exact_integer_limit <- 2^53 - 1
    
    validate_collatz <- function(n) {
      if (length(n) != 1L || !is.numeric(n) || is.na(n) ||
          !is.finite(n) || n < 1 || n != floor(n) ||
          n > exact_integer_limit) {
        stop("The seed must be an exactly representable positive integer.")
      }
      invisible(TRUE)
    }
    
    collatz_step <- function(n) {
      validate_collatz(n)
      if (n %% 2 == 0) return(n / 2)
      if (n > floor((exact_integer_limit - 1) / 3)) {
        stop("3*n + 1 would exceed the chosen range of exact integers.")
      }
      3 * n + 1
    }
    
    collatz_iterative <- function(n, max_steps = 10000L) {
      validate_collatz(n)
      validate_integer(max_steps, minimum = 0, name = "max_steps", maximum = 1e6)
      trajectory <- numeric(max_steps + 1)
      trajectory[1] <- n
      steps <- 0L
      current <- as.double(n)
      while (current != 1 && steps < max_steps) {
        current <- collatz_step(current)
        steps <- steps + 1L
        trajectory[steps + 1L] <- current
      }
      trajectory <- trajectory[seq_len(steps + 1L)]
      list(seed = n, trajectory = trajectory,
           iterations = steps, terms = length(trajectory),
           maximum = max(trajectory), reached_one = current == 1,
           status = if (current == 1) "Reached 1" else "Step limit reached")
    }
    
    c8 <- collatz_iterative(8)
    c27 <- collatz_iterative(27)
    c8$trajectory
    # [1] 8 4 2 1
    c(c27$iterations, c27$terms, c27$maximum)
    # [1] 111 112 9232
    stopifnot(c8$iterations == 3L, c8$terms == 4L,
              c27$iterations == 111L, c27$terms == 112L,
              c27$maximum == 9232, c27$reached_one)

    The transition function performs one transformation. The iterative function organizes transformations and returns a list with results and termination status. Each call creates its own trajectory, independent of a global vector defined in another session. The seed occupies the first position, followed by each new state. This decision removes counting ambiguities.

    Observed states and applications of the transformation
    SeedTrajectory or summaryTerms, including start and endTransitions to 1Maximum
    1The stopping destination has already been reached.101
    88, 4, 2, 1438
    27Full trajectory stored in c27$trajectory.1121119232

    A stopped trajectory with L terms has L − 1 transitions. The distinction also matters for a = 1: one term is recorded and no transition is performed. c(a, v) concatenates values and does not remove duplicates; if v already contains a, that value appears twice unless the algorithm prevents it. This implementation uses a single trajectory with explicit positions.

    R / 45 The time axis starts at zero; the line joins discrete states
    unfinished <- collatz_iterative(27, max_steps = 10)
    unfinished$status
    # [1] "Step limit reached"
    stopifnot(!unfinished$reached_one, unfinished$iterations == 10L)
    
    seed_one <- collatz_iterative(1, max_steps = 0)
    stopifnot(seed_one$reached_one, seed_one$iterations == 0L)
    
    plot(seq_along(c27$trajectory) - 1L, c27$trajectory,
         type = "l", col = "#8b4513", lwd = 1.5,
         xlab = "Iteration k", ylab = "State n[k]",
         main = "Collatz orbit starting at 27")
    Collatz orbit starting at 27111 transitions to 1; 112 terms including the initial value; maximum 9232 at iteration 77. Points represent discrete states. image/svg+xml Matplotlib v3.10.8, https://matplotlib.org/ 0 20 40 60 80 100 111 Iteration k 0 2000 4000 6000 8000 10000 State nₖ Maximum: 9232 27 → 1 · 111 transitions · 112 terms
    Figure 1 Trajectory calculated exactly for this seed. The initial segment and oscillations belong to the traversal before reaching 1.

    The line makes the plot easier to follow but does not turn its states into a continuous flow. Starting at 27, the trajectory reaches values far above its initial value before descending to 1. A step bound stops an exploration; reaching it reports a limitation of the executed traversal, not a counterexample. Exploring arbitrarily large integers would require exact arbitrary-precision arithmetic and a suitably adapted implementation.

    The recursive version: a base case and a call to itself

    R / 46 The same rule with a different organization of execution
    collatz_recursive <- function(n, max_steps = 300L) {
      validate_collatz(n)
      validate_integer(max_steps, minimum = 0, name = "max_steps", maximum = 300)
      walk <- function(current, remaining, accumulator) {
        accumulator <- c(accumulator, current)
        if (current == 1 || remaining == 0L) return(accumulator)
        walk(collatz_step(current), remaining - 1L, accumulator)
      }
      trajectory <- walk(as.double(n), max_steps, numeric(0))
      reached <- tail(trajectory, 1) == 1
      list(seed = n, trajectory = trajectory,
           iterations = length(trajectory) - 1L,
           terms = length(trajectory), maximum = max(trajectory),
           reached_one = reached,
           status = if (reached) "Reached 1" else "Step limit reached")
    }
    
    r8 <- collatz_recursive(8)
    r27 <- collatz_recursive(27)
    stopifnot(identical(c8$trajectory, r8$trajectory),
              identical(c27$trajectory, r27$trajectory))

    The inner function walk() records the state and checks a stopping case. To continue, it calls itself with the next state, one fewer available step, and the accumulated trajectory. The object returned by the inner call becomes the outer call’s result. An argument need not be renamed simply because another function uses the same name: every call creates its own evaluation environment.

    The recursive example deliberately uses a lower limit. Available depth depends on settings and environmental resources, so a step limit does not guarantee that every R implementation permits that call depth. R does not guarantee tail-call optimization. For long trajectories, the iterative version avoids accumulating recursive frames and is usually more appropriate.

    Pending calls and return in recursive CollatzCalls for states 8, 4, 2, and 1 are stacked. Resolving state 1 returns the full trajectory through the outer calls in reverse order.Pending callsResolution and returnwalk(8, …)walk(4, …)walk(2, …)walk(1, …)Base case: state 1.Accumulated trajectory:(8, 4, 2, 1).Last call: firstto resolve (LIFO).4 terms · 3 transitions
    Figure 2 Inner-function calls, with auxiliary parameters abbreviated. The same trajectory object is returned through the pending calls.

    What the call stack shows

    Starting at 8 leaves calls pending for states 8, 4, and 2 before the state-1 base case is resolved. Return resolves the most recent call first and earlier calls afterward: this is LIFO, last in, first out, also called FILO. A stack-of-plates analogy helps visualize the order, but does not establish that recursion was designed to optimize cache memory. Call stacks and cache policies are different concepts.

    A program that executes A1, then A2, and then calls itself again without a stopping condition leaves any subsequent instruction pending. Thus a final instruction to print “Hello, Fernanda” is not reached in an idealized execution continuing indefinitely. A real system may instead fail earlier through excessive call depth or resource use. A useful algorithm needs a base case or an explicit exit.

    Formalizing the trajectory without assuming termination

    τ(a) = min{k ≥ 0 : Tk(a) = 1}
    𝒪(a) = (a, T(a), …, Tτ(a)(a))
    If the orbit reaches 1, τ(a) is the first hitting index and the trajectory contains τ(a) + 1 terms. Otherwise no such finite minimum exists; one may adopt the convention τ(a) = infinity. This conditional definition does not prove finite hitting time for every seed.

    A recursive formula saying only “keep calling until 1 is reached” describes a procedure, but termination must be established for it to represent a total function on every accepted seed. Moreover, returning only the scalar 1 does not preserve the trajectory, which requires another codomain, such as finite sequences or lists. Separating the transformation T, hitting time, and trajectory avoids conflating different objects in one notation.

    Documentation and references: [1] · [16]

    A closer look at the language

    14. Environments, Classes, and Expressions

    The advanced concepts in this section clarify what R does when it evaluates a function, selects a method, or constructs an expression. They follow the first examples so that they can be linked to familiar operations. These are different explanatory levels: a storage type, a class, a reserved word, and a call are not interchangeable, although they may participate in one instruction.

    Environments and lexical scope

    R / 47 An inner function retains access to its creation environment
    offset <- 100
    make_offset <- function(delta) {
      force(delta)
      function(x) x + delta
    }
    add_two <- make_offset(2)
    add_two(5)
    # [1] 7
    offset
    # [1] 100

    The returned function can look up delta in the environment where it was created. This combination of function and environment is called a closure. Ordinary assignment inside a function establishes names in the call environment; looking up a name absent there proceeds through enclosing environments according to lexical scope. Not every object accessible outside a function belongs to the global environment: there may be enclosing function environments and package environments.

    force(delta) explicitly evaluates the argument before constructing the inner function; R uses lazy argument evaluation through promises. The example illustrates a function factory: it receives a number and returns a function. Its output makes it a higher-order function. Local objects from different calls do not conflict merely because they share a name.

    Types, classes, and methods

    R / 48 Class affects behavior in addition to storage
    date <- as.Date("2021-09-11")
    c(type = typeof(date), mode_value = mode(date), class_value = class(date))
    # type: "double"; mode: "numeric"; class: "Date".
    
    measurement <- structure(1.65, class = "height_m")
    print.height_m <- function(x, ...) {
      cat(format(unclass(x)), "m\n")
      invisible(x)
    }
    print(measurement)
    # 1.65 m
    inherits(measurement, "height_m")
    # [1] TRUE

    typeof() identifies the internal type. mode() supplies a historical classification that can group types; for example, integers and doubles have numeric mode. class() participates in method behavior. The date example stores numeric information, but its class allows it to be printed and interpreted as a date. In the height example, an S3 print() method defines how the object is displayed.

    A generic function selects a method through a dispatch mechanism. In S3, many generics use UseMethod(); S4 has formal definitions of classes, methods, and inheritance, with dispatch that may depend on several arguments. A description of one system should not be projected onto all the others. methods::extends() examines inheritance relationships in the formal system, and genericFunction is an S4 class, not the universal definition of every generic call in R.

    The image of a class as a blueprint or prototype for objects is a teaching aid suited to certain object-oriented models. R contains systems with different properties. A computational class is also not identical to a set-theoretic class: the latter is not simply a set of transfinite cardinality. In theories distinguishing sets and proper classes, many sets are already infinite or have transfinite cardinality without being proper classes.

    Reserved words and names that mask functions

    Reserved words include if, else, repeat, while, function, for, in, next, and break. Reserved constants include TRUE, FALSE, NULL, Inf, NaN, and typed variants of NA, alongside special names associated with .... Natural numbers are not a collection of reserved words. Ordinary function names are not all reserved: other assignments can mask them, often without an automatic warning preventing that design mistake.

    R / 49 The :: operator identifies the originating package
    # Avoid giving an object the name of a frequently used function.
    my_data <- c(1, 2, 3)
    base::sum(my_data)
    # [1] 6
    stats::median(my_data)
    # [1] 2

    A package contains functions, data, and documentation. An R library is a directory where packages are installed; a repository, such as CRAN, distributes packages. install.packages() installs; library() loads and attaches a package; requireNamespace() checks availability and loads its namespace without attaching it. Distinguishing these concepts prevents confusion between a distribution network, packages, and installed directories.

    The … argument and construction of names

    R / 50 Passing additional arguments explicitly
    sum_with_options <- function(x, ...) {
      sum(x, ...)
    }
    sum_with_options(c(1, NA_real_, 3), na.rm = TRUE)
    # [1] 4
    
    named_table <- data.frame(height_m = c(1.65, 1.70))
    names(named_table)
    # [1] "height_m"

    ... receives or forwards additional arguments. In data.frame(), column arguments may take the form name = value; when a name is omitted, the function can construct one from the supplied expression and adjust its syntax. Explicit column names clarify the structure and avoid dependence on name-transformation rules.

    Calls, expressions, and deparse()

    R / 51 Constructing a call and evaluating it are different operations
    call_object <- call("sum", c(1, 2, 3))
    typeof(call_object)
    # [1] "language"
    eval(call_object)
    # [1] 6
    
    expression_object <- expression(1 + 2, sqrt(9))
    typeof(expression_object)
    # [1] "expression"
    eval(expression_object)
    # [1] 3
    
    call_text <- deparse(call_object)
    call_text
    # [1] "sum(c(1, 2, 3))"

    call() constructs a call object whose first argument identifies the function and whose remaining arguments are incorporated into the call. Its arguments are evaluated while constructing it, but the resulting call has not yet been executed. eval() evaluates it in an environment. Calling an already defined function and defining a function are different operations: call() does not replace a definition using function().

    expression() stores expressions without evaluating them; evaluating the entire vector returns the value of the last expression. deparse() produces a textual representation of an expression. parse() turns text into language expressions, but converting text to code and evaluating it requires control over its provenance. These are metaprogramming tools, not prerequisites for creating your first data tables.

    Documentation and references: [1] · [16] · [17]

    From economic data to analysis

    15. Application: GDP Growth in Central America

    This application uses the World Bank indicator NY.GDP.MKTP.KD.ZG, annual GDP growth expressed as a percentage. The objective is to select Belize, Guatemala, El Salvador, Honduras, Nicaragua, Costa Rica, and Panama; preserve observations by year; inspect missingness; and construct summaries with explicit meanings. A download’s size and time coverage depend on the particular file and are not permanent properties of the indicator.

    An export may combine countries, other economies, and regional or income-group aggregates. Accordingly, each row is not interpreted as a country, and a row count is not treated as the number of countries in the world. The usual layout also includes metadata columns before the year columns. These text columns are not errors to be removed indiscriminately: they identify information and allow the correct units to be selected.

    A complete teaching sample with explicit provenance

    R / 52 Seven units and five years: exclusively illustrative data
    ca_codes <- c("BLZ", "GTM", "SLV", "HND", "NIC", "CRI", "PAN")
    ca_names <- c("Belize", "Guatemala", "El Salvador", "Honduras",
                    "Nicaragua", "Costa Rica", "Panama")
    
    demo_wb <- data.frame(
      "Country Name" = ca_names,
      "Country Code" = ca_codes,
      "Indicator Name" = "GDP growth (annual %)",
      "Indicator Code" = "NY.GDP.MKTP.KD.ZG",
      "2018" = c(2, 3, 2, 4, -4, 3, 4),
      "2019" = c(1, 4, 3, 3, -3, 2, 3),
      "2020" = c(-12, -2, -8, -9, -2, -4, -16),
      "2021" = c(10, 8, 10, 12, 10, 8, 15),
      "2022" = c(7, 4, 3, 4, NA_real_, 5, 11),
      check.names = FALSE, stringsAsFactors = FALSE
    )
    stopifnot(nrow(demo_wb) == 7L, ncol(demo_wb) == 9L)

    Three-letter codes provide a selection stable against changes in row order. The table uses check.names = FALSE to preserve year headings as written. If a reading procedure changes 2020 into X2020, explicitly correct or interpret that transformation. The procedure below expects four-digit year headings and reports an error if none are found.

    Validating a download and separating metadata from years

    R / 53 Selecting by codes and year names, never by row numbers
    convert_wb_column <- function(x) {
      text_value <- trimws(as.character(x))
      missing_value <- is.na(x) | text_value %in% c("", "..", "NA", "N/A")
      text_value[missing_value] <- NA_character_
      number <- suppressWarnings(as.numeric(text_value))
      if (any(!missing_value & is.na(number))) {
        stop("A year column contains text not recognized as a number or a missing value.")
      }
      if (any(!is.na(number) & !is.finite(number))) {
        stop("A year column contains nonfinite values.")
      }
      number
    }
    
    prepare_growth <- function(input_data, codes = ca_codes,
                                    start_year = NULL, end_year = NULL) {
      d <- as.data.frame(input_data, check.names = FALSE,
                         stringsAsFactors = FALSE)
      required_columns <- c("Country Name", "Country Code", "Indicator Code")
      if (!all(required_columns %in% names(d))) {
        stop("Required identification columns are missing.")
      }
      if (anyDuplicated(names(d))) stop("Column names are duplicated.")
      if (anyNA(codes) || anyDuplicated(codes) || length(codes) == 0L) {
        stop("Requested codes must be unique, nonmissing, and nonempty.")
      }
      selection <- !is.na(d[["Indicator Code"]]) &
        d[["Indicator Code"]] == "NY.GDP.MKTP.KD.ZG" &
        d[["Country Code"]] %in% codes
      d <- d[selection, , drop = FALSE]
      if (anyDuplicated(d[["Country Code"]])) {
        stop("There is more than one row per code for the selected indicator.")
      }
      position <- match(codes, d[["Country Code"]])
      if (anyNA(position)) {
        stop("Not all requested codes were found.")
      }
      d <- d[position, , drop = FALSE]
      year_names <- names(d)[grepl("^[0-9]{4}$", names(d))]
      if (length(year_names) == 0L) stop("No year columns were found.")
      years <- as.integer(year_names)
      sort_index <- order(years)
      years <- years[sort_index]
      year_names <- year_names[sort_index]
      if (!is.null(start_year)) {
        validate_integer(start_year, minimum = 0, name = "start_year", maximum = 9999)
      }
      if (!is.null(end_year)) {
        validate_integer(end_year, minimum = 0, name = "end_year", maximum = 9999)
      }
      if (!is.null(start_year) && !is.null(end_year) && start_year > end_year) {
        stop("The start year cannot be later than the end year.")
      }
      keep <- rep(TRUE, length(years))
      if (!is.null(start_year)) keep <- keep & years >= start_year
      if (!is.null(end_year)) keep <- keep & years <= end_year
      years <- years[keep]
      year_names <- year_names[keep]
      if (length(years) == 0L) stop("The interval contains no available years.")
      values <- lapply(d[year_names], convert_wb_column)
      M <- as.matrix(as.data.frame(values, check.names = FALSE))
      dimnames(M) <- list(codes, as.character(years))
      metadata <- d[, required_columns, drop = FALSE]
      rownames(metadata) <- NULL
      long_data <- data.frame(
        country = rep(d[["Country Name"]], each = length(years)),
        code = rep(codes, each = length(years)),
        year = rep(years, times = length(codes)),
        growth_pct = as.vector(t(M)),
        stringsAsFactors = FALSE
      )
      list(metadata = metadata, years = years,
           matriz = M, long_data = long_data)
    }
    
    ca <- prepare_growth(demo_wb, start_year = 2018, end_year = 2022)
    dim(ca$matriz)
    # [1] 7 5
    head(ca$long_data)
    stopifnot(identical(rownames(ca$matriz), ca_codes),
              nrow(ca$long_data) == 35L, sum(is.na(ca$matriz)) == 1L)

    The function converts only annual columns and checks for unrecognized strings. Suppressing conversion warnings is paired with a check that stops the analysis when unexpected losses appear: the problem is not hidden. Metadata are retained separately. Long format has one row per country-year; the matrix preserves one row per country and one column per year. Both layouts preserve each measurement’s identity.

    The transposition used to build long format organizes the order in which cells are traversed. It does not “fix” year names, nor is it needed because a named vector cannot be averaged. Names are attributes compatible with numeric computation. To extract a series from the matrix, select its row; remove names only when another operation requires it.

    R / 54 Mean, median, dispersion, extremes, missingness, and empirical modes
    Guatemala <- ca$matriz["GTM", ]
    mean(Guatemala)
    # [1] 3.4
    unname(Guatemala)
    # [1] 3 4 -2 8 4
    
    ca_summary <- t(vapply(seq_len(nrow(ca$matriz)),
                           function(i) available_summary(ca$matriz[i, ]),
                           numeric(9)))
    rownames(ca_summary) <- rownames(ca$matriz)
    ca_summary
    
    empirical_mode <- function(x) {
      x <- x[!is.na(x)]
      if (length(x) == 0L) return(numeric(0))
      frequencies <- table(x)
      as.numeric(names(frequencies)[frequencies == max(frequencies)])
    }
    lapply(seq_len(nrow(ca$matriz)),
           function(i) empirical_mode(ca$matriz[i, ]))

    The mean and median summarize different aspects of the level of rates; variance and standard deviation describe dispersion over the period. Their interpretation does not make years independent observations by definition. The empirical mode function returns every value tied for highest frequency, so several may be returned. With continuous measurements containing many distinct values, this mode may be uninformative and highly sensitive to rounding. R’s mode() inspects a computational mode; it does not calculate the statistical mode.

    Four economic questions, four operations

    The calculation must answer the economic question
    QuestionAppropriate operationInterpretive limitation
    What was a country’s observed average annual rate?Arithmetic mean of available rates.Report period and coverage; this is not cumulative growth.
    How much did it grow cumulatively across consecutive years?Compound growth factors.Requires the rates for every transition in the period.
    What equivalent constant annual compound rate gives the same result?Geometric mean of the factors, minus one.Requires a defined number of transitions and valid factors.
    What was the region’s GDP growth?Aggregation using coherent levels and weights.Not obtained simply by adding national rates; follow the aggregate’s methodology.
    G = ∏t=1T(1 + gt/100) − 1
    geq = 100[(1 + G)1/T − 1]
    g_t is an annual percentage rate. G is cumulative growth as a proportion; 100G expresses it as a percentage. The second formula spreads the total factor over T identical factors. A complete temporal chain of transitions is required.
    R / 55 Compounding rates with explicit time coverage
    compound_growth <- function(rates, years) {
      if (!is.numeric(rates) || !is.numeric(years) ||
          length(rates) != length(years) || length(rates) == 0L ||
          anyNA(years) || any(!is.finite(years)) || any(years != floor(years))) {
        stop("Rates and their years must be compatible numeric vectors.")
      }
      if (length(years) > 1L && any(diff(years) != 1)) {
        stop("Years must be ordered and consecutive.")
      }
      if (anyNA(rates)) {
        return(c(cumulative_pct = NA_real_, equivalent_annual_pct = NA_real_))
      }
      if (any(!is.finite(rates)) || any(rates <= -100)) {
        stop("This example requires finite rates greater than -100%.")
      }
      log_sum <- sum(log1p(rates / 100))
      c(cumulative_pct = 100 * expm1(log_sum),
        equivalent_annual_pct = 100 * expm1(log_sum / length(rates)))
    }
    compound_growth(c(10, -10), c(2020, 2021))
    # cumulative_pct: -1; equivalent_annual_pct: approximately -0.5012563.
    compound_growth(ca$matriz["GTM", ], ca$years)
    compound_growth(ca$matriz["NIC", ], ca$years)
    # Both compounded summaries are NA for Nicaragua because one rate is missing.

    An increase of 10% followed by a decrease of 10% leaves a factor of 1.10 × 0.90 = 0.99: cumulative change is −1%, although the sum and arithmetic mean of the rates are zero. The implementation uses log1p() and expm1() to express compounding with improved precision near zero. If rates labeled 2018–2022 describe changes from the preceding year, compounding them compares the end-of-2022 level with the level preceding the first transition, 2017. Interpret time labels according to the indicator definition.

    Dropping a missing rate and compounding the rest can join segments that do not reconstruct growth over the full interval. The function therefore returns missingness rather than presenting a partial accumulation as complete. Nor is the annual sum of different countries’ rates the region’s growth: GDP aggregation requires comparable levels and consistent weights, while avoiding the inclusion of both an aggregate and its members, which would double-count information.

    Using an actual World Bank file

    R / 56 An explicit template separating demonstration from actual data
    # Template for an XLS/XLSX download: adjust the path, sheet, and initial rows.
    # This block does not download data or replace inspection of the file.
    wb_path <- file.path("data", "world_bank_gdp_growth.xlsx")
    if (file.exists(wb_path) && requireNamespace("readxl", quietly = TRUE)) {
      sheets <- readxl::excel_sheets(wb_path)
      print(sheets)
      if (!"Data" %in% sheets) {
        stop("Check the data sheet; this example expected Data.")
      }
      preview <- readxl::read_excel(wb_path, sheet = "Data",
                                        col_names = FALSE, n_max = 8)
      print(preview)
      # skip = 3 fits the layout with column headings on the fourth row.
      wb_input <- readxl::read_excel(wb_path, sheet = "Data", skip = 3,
                                     col_types = "text", na = c("", ".."))
      ca_actual <- prepare_growth(wb_input, start_year = 2000, end_year = 2020)
      print(dim(ca_actual$matriz))
    } else {
      message("The empirical application requires the specified file and readxl.")
    }

    If the sheet is named “Datos” or something else, change the argument after inspecting the workbook. skip = 3 is not a law of the Excel format. For a ZIP download containing CSV, first extract the data file and inspect its preliminary lines; then configure read.csv(), preserving headings with check.names = FALSE. The example years 2000–2020 are an analytical choice, not a promise of complete availability.

    • Record the indicator address, retrieval date, original file, and sheet or table.
    • Confirm country codes, indicator code, and percentage units.
    • Report dimensions after selection without counting metadata as years.
    • Inspect missingness by country and year; do not interpret blank cells as zeros.
    • Preserve the original file and export results under names identifying the period and procedure.
    R / 57 Exports explicitly identified as demonstrations
    write.csv(ca$long_data,
              file = file.path(example_directory, "central_america_demo_long_format.csv"),
              row.names = FALSE, na = "", fileEncoding = "UTF-8")
    write.csv(ca_summary,
              file = file.path(example_directory, "central_america_demo_summary.csv"),
              row.names = TRUE, na = "", fileEncoding = "UTF-8")

    Documentation and references: [20] · [21] · [22]

    Understand, check, and reproduce

    16. Working Principles and Exercises

    A statistical program should make it possible to reconstruct the decisions separating source from result. Data size, file format, and function complexity do not alter that requirement. We need to know what each object represents, how observations were selected, which were excluded, and what the applied operation means. Visual inspection and automatic checks complement one another.

    Validity has computational, mathematical, and substantive components
    Before calculatingDuring calculationWhen interpreting
    Identify unit, variable, scale, and provenance.Check types, lengths, and dimensions.Connect the output to the original question.
    Distinguish observations from metadata.Verify selection by names and codes.Report coverage and missing-value treatment.
    Preserve original data and program.Validate assumptions and boundary cases.Distinguish approximation, verification, and proof.
    Record version and dependencies.Separate returned results from effects.Preserve reproducible output with units and labels.

    Errors worth recognizing

    • Unnoticed reassignment: a later assignment changes the value bound to a name in that environment. Keeping a separate name for original data makes transformations easier to review.
    • Misread equality: <- assigns, == compares, and <= orders. Approximate numerical equality requires an appropriate tolerance.
    • A mask containing missing values: a condition can contain NA. Decide whether to preserve these as unknown information or explicitly exclude them before selection.
    • Identity and order: equal length does not establish that vectors are correctly paired. Use stable identifiers.
    • Mixed types: a label column can turn an entire matrix into text. Calculate summaries on the relevant numeric columns.
    • Incompatible units: adding quantities with different units is a dimensional issue; converting text to numbers is a representation issue. They can coexist but are not identical.
    • Excessive interpretation: numeric output does not establish temporal independence, sample representativeness, causality, or the universal validity of a conjecture.

    Exercises with checkable results

    Apply an operation and explain its meaning
    ExerciseResult or checking criterion
    Construct a sequence from 0 to 1 with five positions.0, 0.25, 0.5, 0.75, 1.
    Extract values strictly between 3 and 8 from c(2, 5, 8, NA).The result is 5; the condition must explicitly handle missingness.
    Sum positive even integers no greater than 50 using a loop and a formula.Both methods give 650.
    Calculate the three students’ average grades.75.50, 66.75, and 47.25; pass, resit, and fail.
    Distinguish elementwise and matrix multiplication for the chapter’s matrices.The former preserves 2 × 3; matrix01 %*% t(matrix03) produces 2 × 2.
    Compare Collatz starting at 8 and at 27.3 and 111 transitions; 4 and 112 terms. From 27 the maximum is 9232.
    Compound a 10% increase and a 10% decrease.Cumulative change is −1%.
    Calculate a mean when every value is missing.Identify that no observations are available; do not interpret the result as a zero mean.

    A minimal reproducibility record

    R / 58 Execution should provide evidence of its conditions
    # Information to accompany a shared analysis.
    sessionInfo()
    RNGkind()
    
    # Brief checks of the integrated applications.
    stopifnot(
      sum_even(50) == 650,
      isTRUE(all.equal(unname(grade_averages), c(75.5, 66.75, 47.25))),
      c27$iterations + 1L == length(c27$trajectory),
      nrow(ca$long_data) == nrow(ca$matriz) * ncol(ca$matriz),
      abs(compound_growth(c(10, -10), c(2020, 2021))[1] + 1) < 1e-10
    )

    The chosen checks contrast identities and results verifiable through another route: a formula against a loop, counts against structures, and compounded rates with a known result. They do not replace reading code or examining data. They make changes that alter important behavior visible when the program is modified.

    Understanding an instruction requires knowing the object it receives, the transformation it performs, and the conclusion it supports.

    A reading principle for this chapter

    Documentation and references: [2]

    Documentation and traceability

    17. References and Provenance

    Definitions of the language and its functions rely primarily on R and package documentation. Links to R-devel may describe behavior newer than a local installation; installed help, accessed with ?name, documents the version being used. This chapter makes relevant behavior explicit, including scalar conditions and the default value of stringsAsFactors.

    1. R Core Team. R Language Definition: objects, evaluation, functions, indexing, and syntax.
    2. R Core Team. An Introduction to R: vectors, structures, and programming.
    3. R Core Team. Logical Operators: elementwise logic and short-circuit evaluation.
    4. R Core Team. Control Flow: if, for, while, repeat, break, and next.
    5. R Core Team. Not Available / Missing Values.
    6. R Core Team. Vectors: Creation, Coercion, etc.
    7. R Core Team. Colon Operator and sequence generation.
    8. R Core Team. Apply Functions Over Array Margins.
    9. R Core Team. Multi-way Arrays.
    10. R Core Team. Data Frames.
    11. R Core Team. Numeric Vectors: types and factor conversion.
    12. R Core Team. Conditional Element Selection: ifelse().
    13. R Core Team. Correlation, Variance and Covariance.
    14. R Core Team. Integration of One-Dimensional Functions.
    15. Wickham, H. Advanced R, second edition: functionals.
    16. Wickham, H. Advanced R: functions and lexical scope.
    17. Wickham, H. Advanced R: object-oriented systems.
    18. The readxl team. Read xls and xlsx files.
    19. The writexl team / rOpenSci. Export to xlsx.
    20. World Bank. GDP growth (annual %), NY.GDP.MKTP.KD.ZG.
    21. World Bank. Methodologies: aggregation rules and methods.
    22. World Bank. Aggregate API Queries: economies and aggregates.

    Sources used in the original document

    The 2021 version drew on A. Ballestero’s materials on operators and functions in RStudio, Kolmogorov and Fomin’s text on function theory and functional analysis, and tutorials, documentation, and technical discussions. That bibliographical provenance is retained. Analogies and formulations revised in this edition should be assessed through the definitions and explanations developed in the chapter.

    Provenance bibliography and original links
    1. Ballestero, A. (2020). Fundamentos de programación en R Studio. Operadores, vectores, listas, matrices, variables y data frame.
    2. Ballestero, A. (2021). Fundamentos de programación en R Studio. Funciones en R, Parte I.
    3. Banco Mundial (2021). Crecimiento del PIB (% anual), source for the original application.
    4. Codecademy (2021). What is function call?
    5. DataMentor (2021). R while Loop.
    6. R Documentation, originally cited as EHZ/ETH Zürich (2020). Unevaluated Expressions.
    7. Estadísticamente (2021). Sucesiones, repeticiones y factores en R.
    8. R Documentation, originally cited as ETH Zürich (2020). Generic Function Objects.
    9. R Documentation, originally cited as ETH Zürich (2020). Logical Vectors.
    10. R Documentation, originally cited as ETH Zürich (2021). Function Calls.
    11. GeeksforGeeks (2020). Matrix vs Dataframe in R.
    12. JournalDev (2021). How to replace values using replace() in R.
    13. Langohr, K. (2010). Introducción a R. Universitat Politècnica de Catalunya.
    14. Mendoza Vega, J. B. (2021). R para principiantes: coerción.
    15. Mendoza Vega, J. B. (2021). R para principiantes: if, else.
    16. Moore, K., and collaborators (2021). Classes (OOP). Brilliant.
    17. Stack Overflow (2013). Confused about R terminology: Attributes, parameters, and arguments.
    18. RDocumentation.org (2021). Expression Deparsing.
    19. Real Academia Española (2021). Anidar.
    20. Sánchez, G. (2020). Character Strings in R.
    21. Simpson, G. (2011). What evaluates to True/False in R?
    22. Stack Overflow (2016). What is the difference between mode and class in R?
    23. Stackoverrun (2021). Global and local variables in R.
    24. Wickham, H., cited in 2019. Functionals, first edition of Advanced R.
    25. Wickham, H., cited in 2019. Functions / Primitive functions, first edition of Advanced R.
    26. Wikipedia, cited in 2020. Implementation.
    27. Wikipedia, cited in 2020. Stack (abstract data type).
    28. TechTerms (2014). FILO, cited in the original note on stacks.
    29. Kolmogórov, A. N., y Fomin, S. V. (1978). Elementos de la teoría de funciones y del análisis funcional, third edition. Moscow: MIR. Originally cited at p. 121.
    30. Huamaní Ninahuanca (2019), mentioned in an original note supporting integer-vector terminology; the PDF does not provide a complete bibliographic reference.
    31. Stack Overflow (2015), a discussion cited in the original note on cat() and print(); the PDF provides no identifiable link.
    32. Study.com (2021), cited in an original note on the class-as-blueprint analogy; the PDF does not include the complete reference.

    Years and titles in this list retain the identification used in the source document; they do not imply that every older resource remains available or replace primary documentation supporting this edition’s corrections. Original Spanish bibliographic titles are retained where appropriate. The Stroustrup quotation cited through Wickham motivated attention to clear control flow; that concern is developed through examples here without extensively reproducing the quotation.

  • INTRODUCTION TO CLINICAL TRIALS FROM STATISTICAL THEORY AND RSTUDIO: ASSOCIATION AND CORRELATION OF PEARSON, SPEARMAN AND KENDALL

    INTRODUCTION TO CLINICAL TRIALS FROM STATISTICAL THEORY AND RSTUDIO: ASSOCIATION AND CORRELATION OF PEARSON, SPEARMAN AND KENDALL

    Mathematical statistics · Theory and practice

    Association and Correlation in Statistics

    Pearson, Spearman and Kendall: foundations, inference, and an introduction to clinical trials with R
    Originally published: September 11, 2021 · Expanded revision: November 2, 2026

    Studying association means asking how the variations, orderings or frequencies of two characteristics relate to one another. The answer depends on what we observe, how we measure it and how the data were obtained: a linear relationship between temperature and industrial conversion is not quite the same problem as comparing heart attacks across two arms of a clinical trial.

    This article develops those distinctions through definitions, explained equations and reproducible calculations in R. It moves from covariance to Pearson, Spearman and Kendall correlations, then to contingency tables, association tests and effect measures for a binary outcome. At each stage, description of the data, uncertainty in estimation and the scientific scope of a conclusion remain distinct.

    01 · The scientific question comes before the coefficient

    A statistical association is a regularity in the joint distribution of variables. Correlation summarizes particular aspects of that regularity. Causation additionally requires an account of what would happen to an outcome if an intervention changed the exposure or treatment. Selecting a coefficient does not, by itself, answer that third question.

    Before calculating, identify the observational unit, measurement scale, target population, observation period and study design. Two measurements on one person form a pair; twenty measurements on the same person are not equivalent to twenty independent people. Ordered categories, numerical magnitudes and nominal categories also call for different analyses.

    QuestionInitial toolLimit of the answer
    Is there a linear association between numerical quantities?Pearson and a scatterplotCurved relationships may be missed.
    Do values tend to preserve an increasing or decreasing order?Spearman or KendallA rank summary may miss nonmonotonic patterns.
    Do event frequencies differ between groups?Contingency table, risks and comparative measuresThe design determines which risks and effects are identifiable.
    Does treatment cause a difference?Appropriate causal design and analysisAssociation or a p-value alone is insufficient.

    The estimand is the population quantity of interest; an estimator is a rule for calculating it from a sample; an estimate is the resulting value. A test statistic is constructed to evaluate compatibility with a hypothesis. For example, ρ is a population correlation, r is its estimator and t is the statistic used in the classical zero-correlation test.

    02 · Covariance and Pearson: from joint variation to standardization

    sXY = ∑i=1n (xi − x̄)(yi − ȳ)n − 1(1)

    Centering each observation around its mean identifies whether it lies above or below average. The product of the two deviations is positive when their signs agree and negative when their signs differ. The sum combines these contributions. Dividing by n − 1 gives the usual sample covariance, unbiased under independent, identically distributed sampling with finite moments.

    Covariance retains units: when X is measured in degrees and Y in percentage points, the units are their product. Its magnitude therefore does not supply a universal scale of association strength.

    r = sXYsXsY = ∑ (xi − x̄)(yi − ȳ)√[∑ (xi − x̄)² ∑ (yi − ȳ)²](2)

    Standardization removes units and places r between −1 and 1. Its sign indicates direction, while its absolute value summarizes linear alignment. The endpoints correspond to a perfect affine relationship in the sample. A constant variable has zero standard deviation, making the coefficient undefined.

    Pearson can be used with quantitative interval or ratio variables; calculating it does not require normality. It is not a proportion of people or a general percentage of shared variation. In simple linear regression with an intercept and the same observations, R² = r², but that identity has this specific context.

    Cov(a + bX, c + dY) = bd Cov(X,Y)
    Cor(a + bX, c + dY) = sign(bd) Cor(X,Y),   b,d ≠ 0(3)

    Adding constants does not change association. Multiplication by positive factors changes covariance but preserves correlation; reversing the sign of one variable reverses Pearson’s sign. Invariance to changes in measurement units is therefore distinct from invariance to arbitrary transformations.

    03 · Zero correlation, dependence and geometry

    If two variables are independent and have finite, positive variances, their population correlation is zero. The converse is false. Let X be uniform on [−1,1] and Y = X²: by symmetry, E[X] = E[X³] = 0, and hence Cov(X,Y) = 0, although knowing X determines Y exactly. Absence of linear association does not remove dependence.

    Geometrically, r is the cosine of the angle between the centered sample vectors. If r = 0, these vectors are orthogonal. Sample orthogonality is not probabilistic independence and should not be confused with linear independence of vectors. Under a jointly bivariate normal model, zero population correlation does imply independence, but separately normal marginal distributions do not suffice.

    [12]
    2026-11-02T13:35:34.701199 image/svg+xml Matplotlib v3.10.8, https://matplotlib.org/ 0 2 4 6 8 10 X 0 2 4 6 8 10 12 Y r = 0.997 ρs = 1.000 A · Linear relationship 0 2 4 6 8 10 X −25 0 25 50 75 100 125 150 175 Y r = 0.865 ρs = 1.000 B · Monotonic and curved −6 −4 −2 0 2 4 6 X −5 0 5 10 15 20 25 30 Y r = -0.000 ρs = 0.000 C · Dependence without linear correlation 0 5 10 15 20 X −2 0 2 4 6 8 10 12 Y r = -0.000 ρs = 0.500 D · One influential point
    Figure 1. Constructed data illustrate contrasting patterns. In C, Y = X²: the U shape reveals dependence although Pearson is zero. In D, one point changes a previously perfect relationship. Coefficients summarize these points; they do not establish causation.

    Inspect the points before choosing a coefficient: linearity, monotonicity, influential values, clusters and restricted ranges can lead to different interpretations. None of the three coefficients discussed here is a universal detector of dependence.

    04 · Spearman: correlation between ranks

    rs = Cor(RX, RY)(4)

    Spearman replaces magnitudes with ordered positions. A strictly increasing relationship preserves the orderings and reaches +1 when the ranks agree perfectly; a decreasing relationship reaches −1 when they are perfectly reversed. Because it uses order, a strictly increasing transformation of a variable preserves the coefficient. A monotonic association can be curved: increasing X may accompany increasingly large increments in Y.

    rs = 1 − 6∑ di²n(n² − 1)   (no ties)(5)

    In the shortcut formula, di is the difference between the two ranks of observation i. Ordering discrepancies reduce the coefficient from its maximum. With ties, assign average ranks and calculate Pearson on those ranks; do not automatically apply the shortcut. Rank methods reduce the influence of extreme magnitudes, but do not eliminate every problem of data, design or interpretation.

    [1]

    05 · Kendall: concordance, discordance and ties

    With n observations there are n(n − 1)/2 pairs of observations. A pair is concordant when the observation higher on X is also higher on Y, and discordant when this ordering is reversed. Equality requires specific treatment.

    τa = C − Dn(n − 1)/2   (no ties)
    τb = C − D√[(C + D + TX)(C + D + TY)](6)

    C and D count concordant and discordant pairs; TX counts pairs tied only on X, and TY those tied only on Y. Pairs tied on both variables contribute to none of these four counts. Without ties, τ can be interpreted as the difference between the probabilities of concordance and discordance for a random pair. The τb version normalizes for ties and is calculated by cor(..., method = "kendall").

    A minimal example with a tie on X
    ObservationXYRank XRank Y
    11111
    2232.53
    3222.52
    44444

    Here C = 5, D = 0, TX = 1 and TY = 0. Thus τb = 5/√30 ≈ 0.912871 and Spearman ≈ 0.948683. The numbers differ because the coefficients summarize different objects; applying identical strength thresholds to both requires justification.

    [3]

    06 · Choosing a measure and specifying its assumptions

    MeasureWhat it summarizesWhat to inspect
    PearsonLinear association between magnitudesScatterplot, influential points, observed range and inferential model.
    SpearmanRank association, sensitive to monotonicityMeaningful ordering, ties and p-value calculation.
    KendallBalance of concordance and discordanceTies, the version of τ and the test approximation.

    The decision should follow the substantive question and anticipated pattern. An isolated normality test does not automatically determine which coefficient makes sense. Pearson remains a calculable linear summary with nonnormal data; what may fail is classical inference used without regard to its model.

    The exact Pearson t test under zero correlation is justified for independent pairs from the bivariate normal model. In the equivalent fixed-X regression formulation, the model uses independent normal errors with constant variance. These are related frameworks; neither reduces to checking the normality of Y alone. Rank methods also require a sampling framework and independent observational units for their ordinary tests.

    Repeated measurements, clusters, time series and complex selection require procedures that respect that structure. A coefficient calculated while ignoring it may describe the points without making its ordinary p-value valid.

    07 · Complete data and manual calculation in R

    Two teaching contexts are retained: conditions in a vinylation process and association between animal activity and metabolism. The industrial question concerns how temperature and pressure relate to percentage conversion; those associations alone do not identify optimal settings for a multivariable process.

    # Illustrative data, not the missing original CSV files.
    vinylation <- data.frame(
      temperature = c(60,65,70,75,80,85,90,95),
      pressure = c(2,4,3,5,4,6,5,7),
      conversion = c(22,29,27,38,43,41,52,56)
    )
    activity <- data.frame(
      activity_index = c(2,3,3,4,5,5,6,7,7,8,9,9,10,11,11,12,13,14,14,15),
      metabolic_rate = c(12,14,13,15,18,16,19,21,20,22,25,23,26,29,27,30,31,34,32,35)
    )
    
    # Retain complete, finite pairs; record what was removed.
    prepare_pairs <- function(x, y) {
      stopifnot(is.numeric(x), is.numeric(y), length(x) == length(y))
      ok <- is.finite(x) & is.finite(y)
      x <- x[ok]; y <- y[ok]
      if (length(x) < 4L) stop("At least four complete pairs required.")
      if (sd(x) == 0 || sd(y) == 0) stop("A variable is constant.")
      list(x = x, y = y, n = length(x), removed = sum(!ok))
    }
    pairs_xy <- prepare_pairs(vinylation$temperature, vinylation$conversion)
    x <- pairs_xy$x; y <- pairs_xy$y; n <- pairs_xy$n
    pairs_xy[c("n", "removed")]
    

    The sample size n is obtained after selecting valid pairs. The function reports excluded observations and prevents correlations involving constant variables. Complete-case deletion is an analytical decision, not a general remedy for missing-data bias: why observations are missing still matters.

    s_xy <- sum((x - mean(x)) * (y - mean(y))) / (n - 1)
    r <- s_xy / (sd(x) * sd(y))
    c(covariance = s_xy, pearson = r)
    stopifnot(isTRUE(all.equal(s_xy, cov(x, y))),
              isTRUE(all.equal(r, cor(x, y))))
    
    S <- cov(vinylation)
    S                         # Variances on the diagonal.
    cor(vinylation)            # Pearson by default.
    cor(vinylation, method = "spearman")
    cor(vinylation, method = "kendall")
    stopifnot(isTRUE(all.equal(cov2cor(S), cor(vinylation))))
    

    In the industrial example, sXY = 142.857143 and r = 0.971057. The eight illustrative points display pronounced positive alignment; the small sample limits assessment of assumptions. In a covariance matrix, variances occupy the diagonal and covariances the off-diagonal positions. A correlation matrix has diagonal entries of 1 for nonconstant variables.

    When comparing matrices with missing values, prefer a common, documented sample. Pairwise deletion may use different observations for each entry and yield a matrix that is not positive semidefinite.

    rx <- rank(x, ties.method = "average")
    ry <- rank(y, ties.method = "average")
    rho_s <- cor(rx, ry)
    stopifnot(isTRUE(all.equal(rho_s, cor(x, y, method = "spearman"))))
    
    # Formula without ties: use only when neither variable has ties.
    if (!anyDuplicated(x) && !anyDuplicated(y)) {
      rho_short <- 1 - 6 * sum((rx - ry)^2) / (n * (n^2 - 1))
      stopifnot(isTRUE(all.equal(rho_s, rho_short)))
    }
    
    kendall_manual <- function(x, y) {
      ij <- combn(seq_along(x), 2)
      sx <- sign(x[ij[1, ]] - x[ij[2, ]])
      sy <- sign(y[ij[1, ]] - y[ij[2, ]])
      C <- sum(sx * sy > 0); D <- sum(sx * sy < 0)
      Tx <- sum(sx == 0 & sy != 0)  # Tied only on x.
      Ty <- sum(sy == 0 & sx != 0)  # Tied only on y.
      den <- sqrt((C + D + Tx) * (C + D + Ty))
      if (den == 0) return(NA_real_)
      (C - D) / den
    }
    tau_b <- kendall_manual(x, y)
    stopifnot(isTRUE(all.equal(tau_b, cor(x, y, method = "kendall"))))
    c(pearson = r, spearman = rho_s, kendall = tau_b)
    
    # Small, tied example: average ranks and pair counts are inspectable.
    u <- c(1, 2, 2, 4); v <- c(1, 3, 2, 4)
    data.frame(u, v, rank_u = rank(u), rank_v = rank(v))
    c(spearman = cor(u, v, method = "spearman"),
      kendall = kendall_manual(u, v))
    

    The same eight pairs yield Spearman = 0.952381 and Kendall = 0.857143. The code makes ranks and pair counts explicit and checks manual formulas against R functions. Automatic calculations do not replace understanding of the estimated object.

    08 · Inference: hypotheses, p-values and decisions

    H0: ρ = 0   versus   H1: ρ ≠ 0
    t = r √[n − 21 − r²],   df = n − 2(7)

    The sample supplies r; the test evaluates a statement about ρ. Under the classical assumptions and H0, the statistic follows Student’s t distribution with n − 2 degrees of freedom. A two-sided alternative combines both tail probabilities, which is why the code uses the absolute value of t.

    The p-value is the probability, under the specified null model, of obtaining a statistic at least as extreme as the observed one. It is not P(H0 | data), the probability that the result is due to chance, or the effect size. A significance level α is fixed as part of the procedure: a Type I error is rejection of a true null hypothesis. The observed p-value is not a personalized probability of having made that error.

    If the prespecified criterion is p ≤ α, reject H0 when it holds. Otherwise, do not reject: insufficient evidence does not prove an exactly zero correlation. Nor does a significant result demonstrate a strong association, clinical importance or causality.

    [5]

    Rank tests distinguish exact procedures from approximations. Ties affect which calculations are available; exact = FALSE explicitly requests an approximation in examples containing ties. A t transformation of Spearman should not be presented as exact for every sample. In R, even the option named exact for Spearman can use approximations depending on sample size.

    [2]

    09 · Confidence intervals: Fisher’s transformation

    zr = atanh(r) = ½ ln[1 + r1 − r]
    SE(zr) ≈ 1√(n − 3)(8)

    The coefficient r is bounded and its sampling distribution may be asymmetric, particularly near ±1. Fisher maps it to an unbounded scale with an approximately normal distribution and a variance approximately stable across values of ρ. The standard error shown belongs to that transformed scale.

    CIz = zr ± z1−α/2√(n − 3)
    CIρ = [tanh(Lz), tanh(Uz)](9)

    Transform first, construct the interval second, and invert its endpoints third. The bounds then return to the correlation scale. The approximation requires n > 3 and |r| < 1 and remains sensitive to the model and sample size. A 95% interval is interpreted through the procedure’s coverage across repeated samples, rather than a 95% frequentist probability assigned to the fixed parameter after observing the data.

    [11]
    if (abs(r) >= 1) stop("Perfect correlation: inspect the data separately.")
    t_stat <- r * sqrt((n - 2) / (1 - r^2))
    p_two <- 2 * pt(-abs(t_stat), df = n - 2)
    alpha <- 0.05
    z_r <- atanh(r)
    ci_r <- tanh(z_r + c(-1, 1) * qnorm(1 - alpha/2) / sqrt(n - 3))
    pearson_test <- cor.test(x, y, method = "pearson", conf.level = 1-alpha)
    c(t = t_stat, df = n-2, p = p_two, lower = ci_r[1], upper = ci_r[2])
    stopifnot(isTRUE(all.equal(p_two, pearson_test$p.value)),
              isTRUE(all.equal(as.numeric(ci_r), as.numeric(pearson_test$conf.int))))
    
    # Ties: explicitly request approximations rather than exact p-values.
    cor.test(activity$activity_index, activity$metabolic_rate,
             method = "pearson")
    cor.test(activity$activity_index, activity$metabolic_rate,
             method = "spearman", exact = FALSE)
    cor.test(activity$activity_index, activity$metabolic_rate,
             method = "kendall", exact = FALSE)
    
    # Quantile comparison, not a correlation-confidence-interval recipe.
    cbind(df = c(6, 30, 1000), t975 = qt(.975, c(6, 30, 1000)),
          normal975 = qnorm(.975))
    

    For the industrial data: t = 9.958592, df = 6, p ≈ 0.00005931 and Fisher’s approximate 95% CI ≈ [0.843728, 0.994925]. These are illustrative results under the stated model. The code compares the manual calculation with cor.test(); its ordinary intervals are supplied for Pearson, not automatically for the other two measures.

    The biological example yields Pearson ≈ 0.994995, Spearman ≈ 0.997742 and Kendall ≈ 0.984084. It is a constructed series with pronounced association, not an estimate about actual animals. Convergence of t quantiles toward normal quantiles as degrees of freedom grow is a separate fact: it does not justify constructing an interval for ρ as r ± a quantile times the denominator of the t statistic.

    10 · Scatterplots, regression and diagnostics

    old_par <- par(mfrow = c(2, 2))
    plot(conversion ~ temperature, data = vinylation,
         xlab = "Temperature (illustrative units)", ylab = "Conversion (%)", pch = 19)
    fit_temp <- lm(conversion ~ temperature, data = vinylation)
    abline(fit_temp, col = "#8b4513", lwd = 2)
    plot(conversion ~ pressure, data = vinylation,
         xlab = "Pressure (illustrative units)", ylab = "Conversion (%)", pch = 19)
    fit_pressure <- lm(conversion ~ pressure, data = vinylation)
    abline(fit_pressure, col = "#8b4513", lwd = 2)
    plot(fitted(fit_temp), residuals(fit_temp),
         xlab = "Fitted conversion", ylab = "Residual", pch = 19)
    abline(h = 0, lty = 2)
    qqnorm(residuals(fit_temp)); qqline(residuals(fit_temp))
    par(old_par)
    
    # y = x^2: strong dependence, zero sample Pearson on a symmetric grid.
    grid_x <- seq(-2, 2, length.out = 101)
    c(pearson = cor(grid_x, grid_x^2),
      spearman = cor(grid_x, grid_x^2, method = "spearman"))
    

    The formula conversion ~ temperature places conversion on the response side; the model and plot preserve that orientation. Pearson is symmetric in X and Y, whereas regression is not: exchanging the response changes the question and generally the fitted line.

    A simple linear model is Yi = β0 + β1Xi + εi. The intercept is included by default; removing it requires justification and changes identities such as R² = r². More generally, “linear” refers to parameters: lm(y ~ x + I(x^2)) remains linear in its coefficients although it describes a curve.

    OLS: minimize ∑ ei²
    WLS: minimize ∑ wiei²(10)

    Residuals ei are differences between observed and fitted responses. Weighted least squares uses supplied weights; it does not automatically estimate those weights as part of this minimization. When variances are known up to a common factor, inverse-variance weights are commonly used. Integer frequency weights or grouped means require care with within-group variation and degrees of freedom.

    lm() also accepts a response matrix and fits each column; this does not turn weights into independent replicates. Residual inspection, design and substantive knowledge matter more than the mere presence of a straight line drawn over the data.

    [10]

    11 · From magnitudes to categories: contingency tables

    A contingency table organizes counts of units across categories. For a two-arm trial with a binary outcome, use rows for treatment and columns for event status. Each participant contributes to one cell, and follow-up remains comparable.

    Table 1. Hypothetical trial: these counts are not from the historical study
    GroupEventNo eventTotal
    Illustrative aspirin201,9802,000
    Illustrative placebo401,9602,000
    Total603,9404,000

    With this orientation, prop.table(trial, 1) calculates outcome proportions within each arm. By contrast, prop.table(trial, 2) calculates group composition within each outcome. With no margin argument it gives joint proportions. None of these instructions alone calculates a relative risk.

    The observed frequencies Oij are the data. Under independence, expected frequencies follow from the margins:

    Eij = ni+n+jN(11)

    ni+ is row i’s total, n+j is column j’s total, and N is the overall total. The formula applies the marginal outcome proportion to each row. Here H0 implies 30 events and 1,970 non-events per arm. Expected counts can be fractional: they are theoretical averages, not additional participants.

    12 · Chi-square, Yates and Fisher: what is being tested?

    X² = ∑i,j (Oij − Eij)²Eij
    df = (F − 1)(C − 1)(12)

    Squaring prevents positive and negative discrepancies from cancelling; division by Eij scales each discrepancy relative to its expected count. F and C denote the numbers of rows and columns. Under H0 and suitable approximation conditions, X² is compared with a chi-square distribution. This is not a correlation between numerical codes assigned to categories.

    The χ²ν distribution is the distribution of a sum of squared independent standard normal variables. It appears in goodness-of-fit tests, independence tests and, under normality, variance inference. For the table, the statistic has an approximate chi-square distribution; the distribution’s definition does not make every sum of squares exactly chi-square.

    # Hypothetical trial: 2,000 participants per arm, same follow-up.
    # These counts are not results of the historical aspirin trial.
    trial <- matrix(c(20, 1980, 40, 1960), nrow = 2, byrow = TRUE,
      dimnames = list(group = c("Aspirin", "Placebo"),
                      outcome = c("Event", "No_event")))
    addmargins(trial)
    prop.table(trial, margin = 1) # Outcome proportions within each arm.
    prop.table(trial, margin = 2) # Arm composition within each outcome.
    addmargins(prop.table(trial)) # Joint proportions and marginal totals.
    
    # Equivalent representation with aggregated frequencies.
    trial_long <- data.frame(
      group = factor(c("Aspirin", "Aspirin", "Placebo", "Placebo"),
                     levels = c("Aspirin", "Placebo")),
      outcome = factor(c("Event", "No_event", "Event", "No_event"),
                       levels = c("Event", "No_event")),
      freq = c(20, 1980, 40, 1960))
    from_long <- xtabs(freq ~ group + outcome, data = trial_long)
    stopifnot(all(from_long == trial))
    
    expected <- outer(rowSums(trial), colSums(trial)) / sum(trial)
    contributions <- (trial - expected)^2 / expected
    X2 <- sum(contributions)
    df_chi <- (nrow(trial)-1) * (ncol(trial)-1)
    p_chi <- pchisq(X2, df = df_chi, lower.tail = FALSE)
    chi_test <- chisq.test(trial, correct = FALSE)
    expected; contributions
    c(X2 = X2, df = df_chi, p = p_chi)
    stopifnot(isTRUE(all.equal(X2, unname(chi_test$statistic))),
              isTRUE(all.equal(p_chi, chi_test$p.value)))
    
    # Sensitivity comparison; choose the primary method before examining p.
    c(pearson = chi_test$p.value,
      yates = chisq.test(trial, correct = TRUE)$p.value,
      fisher = fisher.test(trial)$p.value)
    fisher.test(trial) # Conditional OR estimate and confidence interval.
    

    The example gives X² ≈ 6.768190, df = 1 and p ≈ 0.009280 without correction. Yates gives p ≈ 0.013455 and two-sided Fisher p ≈ 0.012806. All three reject at 0.05 here. The comparison describes procedural sensitivity; it does not license choosing whichever p-value is convenient after seeing the results.

    R applies Yates by default to 2 × 2 tables. Its modification reduces absolute discrepancies before squaring and can make the test conservative. Small expected counts signal a need to inspect the approximation, rather than a universal instruction to apply Yates. Consider the design, table configuration and exact or simulated alternatives.

    [6][13]

    Fisher constructs a conditional distribution given the margins. Under the null hypothesis OR = 1, the first-cell count A follows a hypergeometric distribution:

    P(A = k | margins) = binom(n1, k) binom(n0, m − k)binom(N, m)(13)

    n1 and n0 are arm sizes; m is the event total and N = n1 + n0. “binom(n,k)” counts ways of selecting k elements from n. The numerator allocates m events across the arms, while the denominator counts all compatible allocations. In this parameterization E[A] = n1m/N and Var(A) = n1(m/N)(1 − m/N)(N − n1)/(N − 1).

    The hypergeometric distribution describes allocations without replacement within a finite total; a binomial assumes independent trials with constant probability. A small sampling fraction can make a binomial approximation useful. Conditioning on margins does not mean that both were fixed by design: a trial’s total event count is usually random. Fisher supplies conditional inference, not the only possible inference.

    [7]

    Large sparse tables may use conditional simulation, documenting the number of replicates and random seed. If observations are paired, such as before-and-after outcomes on the same people, ordinary independence tests fail to respect the design; for paired binary outcomes, consider McNemar’s test.

    13 · Risk, odds and effect size

    Write the table as (a,b) in the first row and (c,d) in the second: treatment events and non-events, followed by control events and non-events. Each measure answers a different question.

    MeasureDefinitionInterpretation
    Riskp₁ = a/(a+b); p₀ = c/(c+d)Event probability during the specified follow-up.
    Oddsp/(1−p)Events relative to non-events.
    Relative riskRR = p₁/p₀Multiplicative comparison of risks.
    Odds ratioOR = (a/b)/(c/d) = ad/(bc)Multiplicative comparison of odds.
    Risk differenceRD = p₁−p₀Absolute change in probability.
    Absolute risk reductionARR = p₀−p₁Absolute decrease for an adverse event.
    Relative risk reductionRRR = 1−RRProportional decrease relative to control risk.
    Number needed to treatNNT = 1/ARR, if ARR > 0People treated per adverse event prevented over the period.

    With the illustrative counts, p1 = 1% and p0 = 2%: 10 versus 20 events per thousand participants. RR = 0.50, OR ≈ 0.494949, ARR = 0.01 and RRR = 50%. The absolute reduction is one percentage point, not fifty points. Estimated NNT is 100 over the assumed follow-up, provided a causal interpretation is justified.

    Multiplying odds by one thousand compares events with one thousand non-events; to express events per thousand participants, multiply risk by one thousand. OR approximates RR when risks in both groups are small, but may differ substantially for common outcomes. The odds ratio can be used in frequentist or Bayesian inference; its definition belongs exclusively to neither framework.

    [8]

    An effect size quantitatively reflects the magnitude of a phenomenon relevant to a question. It may be a correlation, difference or ratio. The same p-value can accompany different magnitudes; a similar magnitude can have very different uncertainty depending on study size and quality. Epistemological relevance and the mathematical properties of a measure should be considered together.

    [4]
    CIRR = exp{ln(RR) ± z · √[1/a − 1/n1 + 1/c − 1/n0]}
    CIOR = exp{ln(OR) ± z · √[1/a + 1/b + 1/c + 1/d]}(14)

    These approximate intervals use the logarithmic scale because positive ratios often have asymmetric distributions. For RD, an unpooled Wald approximation is shown, with SE = √[p1(1−p1)/n1 + p0(1−p0)/n0]. These are transparent teaching methods, not universal solutions for sparse counts or zero cells; appropriate alternatives, such as score or exact methods depending on the measure, are needed in those situations.

    a <- trial["Aspirin", "Event"]; b <- trial["Aspirin", "No_event"]
    c0 <- trial["Placebo", "Event"]; d <- trial["Placebo", "No_event"]
    n1 <- a+b; n0 <- c0+d
    p1 <- a/n1; p0 <- c0/n0
    RR <- p1/p0; OR <- (a*d)/(b*c0)
    RD <- p1-p0; ARR <- p0-p1; RRR <- 1-RR
    
    # Large-sample log intervals: all four counts must be positive here.
    stopifnot(all(trial > 0))
    zcrit <- qnorm(.975)
    se_log_RR <- sqrt(1/a - 1/n1 + 1/c0 - 1/n0)
    se_log_OR <- sqrt(1/a + 1/b + 1/c0 + 1/d)
    ci_RR <- exp(log(RR) + c(-1, 1)*zcrit*se_log_RR)
    ci_OR <- exp(log(OR) + c(-1, 1)*zcrit*se_log_OR)
    se_RD <- sqrt(p1*(1-p1)/n1 + p0*(1-p0)/n0)
    ci_RD <- RD + c(-1, 1)*zcrit*se_RD # Unpooled Wald approximation.
    ci_ARR <- -rev(ci_RD)
    NNT <- if (ARR > 0) 1/ARR else NA_real_
    ci_NNT <- if (all(ci_ARR > 0)) 1/rev(ci_ARR) else c(NA_real_, NA_real_)
    rbind(RR = c(RR, ci_RR), OR = c(OR, ci_OR), RD = c(RD, ci_RD))
    c(ARR = ARR, RRR = RRR, NNT = NNT)
    ci_NNT
    
    Table 2. Uncertainty in the constructed trial; methods specified in the code
    MeasureEstimateApproximate 95% CI
    RR0.500000[0.293372, 0.852161]
    Sample OR0.494949[0.288322, 0.849659]
    RD−0.010000[−0.017527, −0.002473]
    NNT100[57.05, 404.43] before rounding

    The NNT interval inverts endpoints of a positive ARR interval; if that interval crosses zero, inversion passes through infinity and should not be reported as an ordinary finite interval. NNT is commonly rounded upward when communicating whole people. The conditional OR estimate and interval returned by fisher.test() use a different procedure and can differ from the sample OR ad/bc and its log interval.

    14 · What a clinical trial actually adds

    Randomization seeks to make treatment assignment independent of prognostic characteristics; allocation concealment protects recruitment and blinding reduces certain subsequent influences. These are distinct mechanisms. The protocol should define population, outcome, follow-up, comparison and analysis before results are observed.

    Intention-to-treat analysis retains people in their assigned group, directing estimation toward the effect of assignment. It does not remove missing-data or adherence problems. Loss to follow-up, competing events, protocol deviations and multiple outcomes require specific attention. A 2 × 2 table can oversimplify a study with differing follow-up times or censoring.

    [9]

    A favorable relative effect must be read alongside baseline risk, absolute difference, adverse events and the studied population. This example teaches statistical analysis of a hypothetical trial; by itself, it does not establish an individual clinical indication or justify automatic extrapolation from one population to another.

    15 · Reporting results without losing their meaning

    A clear report identifies the question and design; describes variables and units; presents the plot or table; states method, effective sample size and handling of ties or missing data; reports the estimate, interval and p-value when appropriate; and delimits interpretation.

    The strength of a conclusion depends on the correspondence between question, design, measure and inference.

    For the industrial series: “Eight illustrative pairs yielded r = 0.9711, with Fisher’s approximate 95% CI [0.8437, 0.9949]; the classical two-sided test gave p ≈ 0.0000593. This describes positive linear association under the specified model and does not identify a causal effect of temperature.”

    For the constructed trial: “Over an assumed common follow-up, the event occurred in 20/2,000 and 40/2,000 participants; RR = 0.50 with approximate log-scale 95% CI [0.2934, 0.8522], and a risk difference of −1 percentage point.” Absolute magnitude remains necessary even when a p-value is reported.

    When exploring many correlations, distinguish exploratory from confirmatory analysis and consider multiplicity. Do not select the coefficient, test direction or sample solely to obtain significance.

    16 · Reproducibility and references

    Run the R blocks in reading order in a fresh session, using base R and the stats package included with a standard installation. No external files, personal paths or additional packages are required. Object names and code labels remain identical across languages to facilitate comparison.

    # Numerical checks of the transparent examples.
    stopifnot(n == 8L, abs(s_xy - 1000/7) < 1e-10,
              abs(RR - 0.5) < 1e-12, abs(ARR - 0.01) < 1e-12,
              abs(X2 - 6.7681895093) < 1e-8,
              abs(kendall_manual(u, v) - 5/sqrt(30)) < 1e-12)
    sessionInfo()
    

    Numerical checks compare manual formulas with built-in functions. sessionInfo() records version and environment, helping document implementation differences. Printed figures are rounded; calculations retain numerical precision.

    The following bibliography supports further study of the measures and verification of R’s behavior. The original post date is retained as an editorial reference; this revision develops and corrects its content.

    References and documentation

    1. R Core Team. Correlation, Variance and Covariance; cor(), cov(), cov2cor().
    2. R Core Team. Test for Association/Correlation Between Paired Samples; cor.test().
    3. Taylor, J. M. G. (1987). Kendall’s and Spearman’s Correlation Coefficients in the Presence of a Blocking Variable. Biometrics, 43, 409–416.
    4. Kelley, K., & Preacher, K. J. (2012). On effect size. Psychological Methods, 17(2), 137–152. DOI: 10.1037/a0028086.
    5. American Statistical Association (2016). Statement on Statistical Significance and P-Values: six principles.
    6. R Core Team. Pearson’s Chi-squared Test for Count Data; chisq.test().
    7. R Core Team. Fisher’s Exact Test for Count Data; fisher.test().
    8. Dicker, R. C. Analyzing and Interpreting Data. The CDC Field Epidemiology Manual, chapter 8.
    9. Steering Committee of the Physicians’ Health Study Research Group (1989). Final report on the aspirin component of the ongoing Physicians’ Health Study. New England Journal of Medicine, 321, 129–135. DOI: 10.1056/NEJM198907203210301.
    10. R Core Team. Fitting Linear Models; lm().
    11. Pennsylvania State University. STAT 509, Lesson 7: Early Phase Studies; correlation and Fisher transformation.
    12. Pennsylvania State University. STAT 414, Lesson 18: The Correlation Coefficient.
    13. Grizzle, J. E. (1967). Continuity Correction in the χ²-Test for 2 × 2 Tables. The American Statistician, 21(4), 28–32. DOI: 10.2307/2682103.
  • THEORETICAL ANALYSIS OF THE QUANTILE FUNCTION IN R STUDIO

    THEORETICAL ANALYSIS OF THE QUANTILE FUNCTION IN R STUDIO

    Mathematical statistics · Theory and practice in R

    The Quantile Function in R

    Theoretical foundations, probabilistic interpretation, and applications

    Originally published on September 5, 2021
    Revised and expanded edition · October 29, 2026
    English translation of the revised Spanish edition

    Knowing a probability and knowing the value associated with it are related but distinct problems. We may ask what proportion of a distribution lies below a particular score; we may also ask which score marks a proportion chosen in advance. The cumulative distribution function answers the first question. The quantile function allows us to answer the second.

    This article develops that relationship from its mathematical definition to its implementation in R. The running example retains a normal model for test scores with mean 72 and standard deviation 15.2, showing why a probability of 0.7850824 yields approximately 84 for the lower tail and 60 for the upper tail. It then examines discrete quantiles, sample quantiles, and the role of critical values in inference.

    1. Two different questions

    Suppose a test score is represented by a random variable X. If we fix a score x, we can calculate the probability that X is less than or equal to it. If we fix a probability p, we can seek the score that reaches that cumulative level. In one case, the threshold is given; in the other, the probability level is given.

    From value to probability and from probability to value
    QuestionNormal-distribution functionReturns
    What probability lies to the left of x?pnorm(x, ...)A probability
    Which value corresponds to cumulative probability p?qnorm(p, ...)A threshold
    What is the density at x?dnorm(x, ...)A density height
    How can observations be generated from this model?rnorm(n, ...)n pseudorandom realizations

    The prefixes d, p, q, and r organize many of the probability-distribution families available in R. For discrete variables, d…() functions return probability masses; for continuous variables, they return densities. A density height is not the probability of observing exactly that value: in the continuous case, probabilities are obtained over intervals. See references [1] and [2].

    2. The cumulative distribution function

    For every real-valued random variable X, the cumulative distribution function is defined as:

    FX(x)=P(X≤x),x∈ℝ

    This function is nondecreasing and right-continuous; its limits are 0 as x tends to −∞ and 1 as x tends to +∞. It may be continuous, have jumps, or combine both features. Its existence does not depend on our knowing an elementary expression that can be evaluated by hand.

    For a standard normal distribution, the cumulative distribution function is denoted by Φ:

    Φ(z)=12π∫−∞ze−t2/2dt

    This integral is well defined, although it cannot be expressed using elementary functions. In R, pnorm() evaluates it numerically and qnorm() provides its quantiles. Quantiles are useful because of the question they answer, not because the cumulative distribution function is somehow absent.

    The cumulative distribution function starts with a value and measures probability; the quantile starts with a probability and locates a value.

    3. The quantile function as a generalized inverse

    To include distributions with jumps or constant stretches, we define the lower quantile as:

    QX(p)=inf{x∈ℝ:FX(x)≥p},0<p<1

    The symbol inf denotes the infimum: the greatest lower bound of the set of values whose cumulative probability reaches or exceeds p. For a real-valued random variable and a level strictly between 0 and 1, this definition selects the first threshold satisfying the condition.

    If F is continuous and strictly increasing, the quantile function coincides with its ordinary inverse and F(Q(p)) = p. With jumps, that equality may fail. The relevant general relationship is:

    F(q−)≤p≤F(q),q=Q(p)

    Here, F(q−) = P(X < q) is the left-hand limit, whereas F(q) = P(X ≤ q) includes any probability mass at the threshold. Thus, in general, a quantile does not leave exactly a proportion p below it: it ensures that the cumulative level is reached or exceeded.

    Properties that aid interpretation

    • Order: as the level p increases, the quantile does not decrease.
    • Units: if X measures scores, incomes, or times, its quantiles retain those units.
    • Position: Q(0.5) is a choice of median; Q(0.25) and Q(0.75) are the quantiles associated with the first and third quartiles.
    • Convention: when a distribution has several medians, the definition above selects one of them. The convention used should be stated.

    The terms percent-point function and inverse cumulative distribution function are also used. A percentile expresses the level p on a scale of one hundred: the 95th percentile corresponds to p = 0.95. For finite samples, its calculation also requires specifying a method.

    4. The test-score example: one probability and two tails

    Suppose test scores follow, as a working model, a normal distribution with mean μ = 72 and standard deviation σ = 15.2:

    X∼N(72,15.22)

    The second parameter in this notation is the variance, σ². By contrast, the R argument sd takes the standard deviation directly. For this family:

    QX(p)=μ+σΦ−1(p),σ>0

    This identity shows how a standard normal quantile is transformed to the original scale by multiplying it by σ and adding μ. The expression qnorm(p) uses μ = 0 and σ = 1 by default; supplying other parameters specifies a different normal distribution rather than the standard normal distribution.

    4.1. Lower tail: the threshold is approximately 84

    We seek a score whose lower-tail cumulative probability is 0.7850824. The call and its verification are:

    R · Lower tail and verification

    target_p <- 0.7850824
    score_mean <- 72
    score_sd <- 15.2
    
    lower_threshold <- qnorm(target_p, mean = score_mean,
                        sd = score_sd)
    lower_threshold
    # Approximately 84.00000012
    
    pnorm(lower_threshold, mean = score_mean, sd = score_sd)
    # Approximately 0.7850824

    Under the model, approximately 78.50824% of scores are less than or equal to 84. The returned value is very close to 84, but it is not identical in exact arithmetic: the supplied decimal probability has been rounded.

    4.2. Upper tail: the threshold is approximately 60

    We now seek a value above which that same probability remains. The tail changes while the requested probability level stays fixed:

    R · Upper tail and verification

    upper_threshold <- qnorm(target_p, mean = score_mean,
                        sd = score_sd, lower.tail = FALSE)
    upper_threshold
    # Approximately 59.99999988
    
    pnorm(upper_threshold, mean = score_mean,
          sd = score_sd, lower.tail = FALSE)
    # Approximately 0.7850824
    
    pnorm(upper_threshold, mean = score_mean, sd = score_sd)
    # Approximately 0.2149176

    The interpretation is that approximately 78.50824% of the distribution lies above 60. Its lower-tail cumulative probability is approximately 0.2149176. For this continuous distribution, including or excluding the exact threshold does not change the probability, because every individual point has zero probability mass.

    The same probability, two thresholdsTwo normal densities. Above, the area to the left of 84 is 0.7850824. Below, the area to the right of 60 is the same. The mean is 72 and the standard deviation is 15.2.A · Lower tail: P(X ≤ 84) ≈ 0.7850824244860728496120Score xDensity f(x) · area represents probabilityB · Upper tail: P(X > 60) ≈ 0.7850824244860728496120Score xDensity f(x) · area represents probability
    Figure 1. The shaded area represents approximately 78.50824% in both panels. The distribution stays the same; the tail and the threshold change. The curve is shown over a finite range for readability.

    For a normal distribution, symmetry explains why both thresholds are equally far from the mean: 84 − 72 = 72 − 60 = 12, up to numerical rounding. This symmetry is a property of this model and should not be assumed for every distribution.

    The same probability p = 0.7850824
    Requested tailThresholdProbability under the model
    Lower≈ 84P(X ≤ 84) ≈ 0.7850824
    Upper≈ 60P(X > 60) ≈ 0.7850824
    Lower tail at the second threshold≈ 60P(X ≤ 60) ≈ 0.2149176
    From probability to threshold on the cumulative distributionThe curve F(x) is increasing. A horizontal line runs from p equal to 0.7850824 to the curve, and a vertical line drops to x equal to 84.00.250.50.7512448728496120F(x) = P(X ≤ x)p = 0.7850824Q(p) ≈ 84Score x
    Figure 2. pnorm() follows the relationship from value to probability; qnorm() allows it to be followed from probability to value.
    The normal quantile curveThe function Q(p) of a normal distribution with mean 72 and standard deviation 15.2. It is increasing and passes through p equal to 0.5 and score 72, and p equal to 0.7850824 and score 84.36486072849610800.250.50.751Q(p): probability → scoreQ(0.5) = 72Q(0.7850824) ≈ 84Cumulative probability p
    Figure 3. The quantile curve takes probabilities on the horizontal axis and returns scores on the vertical axis. For a nondegenerate normal distribution, its limits are −∞ and +∞ as the probability approaches 0 and 1.

    4.3. Is a normal model appropriate for test scores?

    The normal distribution has support on the entire real line. If scores are bounded between 0 and 100, the model assigns some probability to impossible values. With these parameters, the probability above 100 is approximately 3.27%: a discrepancy worth examining, especially when high percentiles are of interest.

    R · Probability outside the 0–100 scale

    p_below_0 <- pnorm(0, mean = score_mean, sd = score_sd)
    p_above_100 <- pnorm(100, mean = score_mean,
                         sd = score_sd, lower.tail = FALSE)
    c(p_below_0 = p_below_0, p_above_100 = p_above_100)

    Its use requires examining the shape of the data and the purpose of the analysis. Depending on the situation, empirical quantiles, a truncated distribution, or a model with bounded support may be more appropriate. The example illustrates the mechanism of inversion; it does not automatically validate normally distributed scores.

    5. Syntax, arguments, and numerical precision in R

    A valid call with all the main arguments specified explicitly is:

    R · Complete call

    qnorm(p = 0.95, mean = 0, sd = 1,
          lower.tail = TRUE, log.p = FALSE)
    # Approximately 1.644854
    Interpreting the arguments
    ArgumentMeaningMain consideration
    pProbability levelUse 0.95, not 95, for 95%
    meanModel meanIf using an estimate, state how it was obtained
    sdModel standard deviationNeither variance nor standard error
    lower.tailSelects the lower or upper tailThe supplied probability refers to the selected tail
    log.pIndicates whether the input is the natural logarithm of the probabilityDoes not request the logarithm of the returned quantile

    An explanatory phrase such as “TRUE or FALSE” should not be copied literally as an argument: a specific logical value must be chosen in each call. It is preferable to spell out TRUE and FALSE. The abbreviations T and F can be reassigned and may become ambiguous during a working session.

    5.1. Complementary tails

    For a normal distribution and an interior probability level p, the upper-tail threshold corresponds to the lower-tail quantile at level 1 − p. However, changing lower.tail returns a different value, not the complementary probability as its output. In extreme tails, it is preferable to supply the probability of the tail of interest directly.

    R · Logarithms and extreme tails

    qnorm(log(0.95), log.p = TRUE)
    # The same quantile as qnorm(0.95)
    
    qnorm(1 - 1e-20)
    # Inf: the subtraction rounds to 1
    
    qnorm(1e-20, lower.tail = FALSE)
    # Approximately 9.26234
    
    qnorm(-1000, lower.tail = FALSE, log.p = TRUE)
    # Finite quantile for upper-tail probability exp(-1000)

    Subtracting a tiny probability from 1 can produce exactly 1 in floating-point representation. Thus, qnorm(1 - 1e-20) may return Inf, whereas the upper-tail call retains a finite threshold. With log.p = TRUE, a probability can be represented by its logarithm even when the ordinary probability is too small to be stored with useful precision.

    5.2. Vectorization and limiting cases

    R · Multiple levels and endpoints

    qnorm(c(0.025, 0.5, 0.975), mean = 72, sd = 15.2)
    # Approximately: 42.20855  72.00000  101.79145
    
    qnorm(c(0, 1))
    # -Inf  Inf

    For the nondegenerate normal interpretation, we assume sd > 0. R also handles sd = 0 as a point mass and flags negative values of sd as invalid. A probability level outside [0, 1] is also invalid when log.p = FALSE. Options and numerical details are documented in [2].

    6. When the distribution has jumps

    Consider the number of successes in four independent Bernoulli trials, each with success probability 0.5. For X ~ Binomial(4, 0.5), we obtain:

    R · Binomial quantile and attained probabilities

    discrete_quantile <- qbinom(0.5, size = 4, prob = 0.5)
    discrete_quantile
    # 2
    
    pbinom(discrete_quantile - 1, size = 4, prob = 0.5)
    # 0.3125
    
    pbinom(discrete_quantile, size = 4, prob = 0.5)
    # 0.6875
    
    qbinom(0.5, size = 4, prob = 0.5, lower.tail = FALSE)
    # 2
    pbinom(2, size = 4, prob = 0.5, lower.tail = FALSE)
    # 0.3125

    No value has a cumulative probability exactly equal to 0.5. Up to 1 success, the accumulated probability is 0.3125; at 2 successes, it reaches 0.6875. The quantile at level 0.5 is therefore 2: it is the smallest value that reaches or exceeds the requested level. This is the operational definition of qbinom(), described in [3].

    The quantile in a distribution with jumpsCumulative distribution of a binomial with four trials and probability 0.5. F(1) is 0.3125 and F(2) is 0.6875. The level 0.5 falls inside a jump and the quantile is 2.00.250.50.75101234X ~ Binomial(4, 0.5)F(2) = 0.6875F(1) = 0.3125p = 0.5Number of successes x
    Figure 4. The first value that reaches or exceeds 0.5 is 2. Filled points indicate the value included in each step; open points indicate the excluded endpoint. No outcome has a cumulative probability exactly equal to 0.5.

    In this example, P(X < 2) = 0.3125, P(X ≤ 2) = 0.6875, P(X > 2) = 0.3125, and P(X ≥ 2) = 0.6875. Including the threshold changes the probability because P(X = 2) = 0.375.

    7. Theoretical quantiles and sample quantiles

    qnorm() calculates quantiles of a specified normal distribution. quantile() works with a sample and a computational method. Even if the data appear normal, these operations involve different constructions: one depends on the model and its parameters; the other depends on the ordered data and the chosen convention.

    Consider six illustrative scores, created solely to demonstrate the computational methods:

    R · Comparing two sample-quantile methods

    scores <- c(58, 65, 70, 72, 80, 91)
    levels <- c(0.25, 0.5, 0.75)
    
    quantile(scores, probs = levels, type = 1)
    # 25% 50% 75%
    #  65  70  80
    
    quantile(scores, probs = levels, type = 7)
    #  25%   50%   75%
    # 66.25 71.00 78.00
    
    empirical_cdf <- ecdf(scores)
    empirical_cdf(70)
    # 0.5
    The same sample, two quantile methods
    Leveltype = 1type = 7
    0.256566.25
    0.507071
    0.758078

    7.1. The empirical distribution and its inverse

    The empirical cumulative distribution function assigns to each threshold the observed proportion of data less than or equal to it:

    Fn(x)=1n∑i=1n𝟙{Xi≤x}

    The expression 𝟙 denotes an indicator function: it equals 1 when the condition holds and 0 otherwise. For 0 < p ≤ 1, the generalized inverse of this function corresponds to type = 1. Writing the ordered observations as x(1), …, x(n), it returns x(⌈np⌉). For example, with six scores and p = 0.5, the index is 3 and the result is 70.

    7.2. Default interpolation

    The default R method is type = 7. For a finite sample of size at least two, it places the level p at position h = 1 + (n − 1)p and interpolates linearly between adjacent observations when that position is not an integer. Thus, p = 0.25 gives h = 2.25 and the result is 65 + 0.25(70 − 65) = 66.25.

    At level 0.5, interpolation falls between 70 and 72 and returns 71. Both methods follow legitimate conventions; they are not resolving an arithmetic disagreement. The interpolated result need not coincide with an observed score.

    R provides nine methods for calculating sample quantiles. It is unnecessary to develop all their algorithms here, but the chosen method should be stated, especially when comparing results across programs. The official documentation presents the nine definitions and their relationship to order statistics; see [4].

    7.3. Missing values and reproducibility

    R · Missing values

    scores_with_na <- c(scores, NA_real_)
    sum(is.na(scores_with_na))
    # 1
    
    quantile(scores_with_na, probs = levels,
             type = 7, na.rm = TRUE)
    # 66.25 71.00 78.00

    Removing missing values allows the calculation to proceed, but does not explain why they are missing. A study should report the sample size used, the policy for handling missing data, the requested probability level, and the quantile method. If sampling weights or a complex design are involved, the procedure must account for them as well.

    8. Quantiles, critical values, and confidence intervals

    Quantile functions appear in many inferential procedures because they locate critical values of a reference distribution. However, calling qnorm() or qt() does not, by itself, construct a confidence interval. One must specify the parameter being estimated, the statistic being used, and the assumptions under which its distribution is known or approximated.

    8.1. An interval for the mean

    If X1, …, Xn are independent observations from the same normal distribution with mean μ and unknown positive variance, the pivot:

    T=X¯−μS/n∼tn−1

    follows a Student t distribution with n − 1 degrees of freedom. Inverting the probability inequality for this pivot gives the two-sided confidence interval:

    X¯±t1−α/2,n−1Sn

    In this formula, the sample mean is the estimator; S/√n is its estimated standard error; and the quantile t1−α/2,n−1 is the critical value calculated by qt(). See [5] and [6].

    R · Student t interval for the mean

    scores <- c(58, 65, 70, 72, 80, 91)
    n <- length(scores)
    confidence <- 0.95
    alpha <- 1 - confidence
    sample_mean <- mean(scores)
    sample_sd <- sd(scores)
    standard_error <- sample_sd / sqrt(n)
    critical_t <- qt(1 - alpha / 2, df = n - 1)
    
    mean_ci <- sample_mean + c(-1, 1) *
      critical_t * standard_error
    mean_ci
    # Approximately: 60.50649  84.82684
    
    automatic_ci <- t.test(scores, conf.level = confidence)$conf.int
    unname(automatic_ci)
    
    stopifnot(isTRUE(all.equal(
      as.numeric(mean_ci), as.numeric(automatic_ci),
      tolerance = 1e-10
    )))

    For these six illustrative scores, the 95% interval is approximately [60.50649, 84.82684]. The calculation demonstrates the procedure; a sample this small requires careful examination of the plausibility of the normality assumption. For nonnormal populations, approximate use of this interval needs justification and can be problematic with pronounced skewness, heavy tails, dependence, or influential outliers.

    8.2. Three objects that should be distinguished

    The object of interest determines the procedure
    ObjectQuestionExample
    Central interval of a distributionBetween which values does a proportion of the model lie?Two normal quantiles at levels 0.025 and 0.975
    Confidence intervalHow can a parameter be estimated with specified coverage?Student t interval for the mean
    Prediction intervalHow can a future observation be bounded with specified coverage?A procedure incorporating individual variation and estimation uncertainty

    R · Central interval of the normal model

    qnorm(c(0.025, 0.975), mean = 72, sd = 15.2)
    # Approximately: 42.20855  101.79145
    # Central interval of the model, not an interval for its mean

    For a known normal distribution with these parameters, the central 95% interval is approximately [42.20855, 101.79145]. It is not a confidence interval for μ. The upper limit also illustrates why caution is necessary when using a normal distribution for scores whose actual maximum is 100.

    8.3. Critical values and p-values

    In a two-sided test concerning a mean, the critical value is obtained by setting a level α before evaluating the statistic. The p-value is calculated from the observed statistic and its distribution under the null hypothesis. The letter p in the arguments of qnorm() or qt() denotes an input probability; it does not automatically mean “the p-value of a test.”

    R · Critical quantile and two-sided p-value

    null_mean <- 72
    observed_t <- (sample_mean - null_mean) / standard_error
    two_sided_p <- 2 * pt(abs(observed_t), df = n - 1,
                          lower.tail = FALSE)
    
    c(observed_t = observed_t,
      critical_t = critical_t, two_sided_p = two_sided_p)
    
    abs(observed_t) > critical_t
    # FALSE
    two_sided_p < alpha
    # FALSE

    In this example, both decisions agree: μ = 72 is not rejected at level 0.05. This does not prove that the null hypothesis is true or that a substantive difference is impossible. The calculation uses a symmetric reference distribution; other tests require the appropriate tail, statistic, and distribution to be established.

    9. Simulation through the quantile function

    The quantile function also allows realizations from a distribution to be constructed from a uniform variable. If U is uniformly distributed on (0, 1), then QX(U) has cumulative distribution function FX. Defining the quantile through a generalized inverse extends this principle to discrete distributions as well.

    R · Inverse transform

    set.seed(2021)
    uniform_values <- runif(1000)
    simulated_scores <- qnorm(uniform_values, mean = 72, sd = 15.2)
    
    mean(simulated_scores)
    sd(simulated_scores)
    # Sample approximations to 72 and 15.2
    
    # Normal generation using the dedicated function:
    set.seed(2021)
    direct_scores <- rnorm(1000, mean = 72, sd = 15.2)
    # Element-by-element equality between the two methods is not expected

    This is the inverse transform principle. A seed makes the sequence reproducible within a compatible random-number-generator configuration; it does not eliminate sampling variation or make the observed mean equal to the exact parameter. From the user’s perspective, rnorm() provides direct normal generation, but this example does not describe every internal algorithm through which R can generate it.

    This application makes the article’s central relationship visible: a collection of probability levels is transformed into values of a variable. Beyond simulation, the same relationship appears in descriptive percentiles, decision thresholds, and diagnostics comparing empirical quantiles with theoretical quantiles.

    10. Reproducing the plots in R

    The plots embedded in this article represent the mathematical functions used in the examples. The following block produces their equivalents using base R tools: two density areas, a cumulative distribution curve, a quantile curve, and a discrete cumulative distribution. No additional packages need to be installed.

    R · Plots with base R

    plot_quantiles <- function() {
      previous_parameters <- par(no.readonly = TRUE)
      on.exit(par(previous_parameters))
    
      paper <- "#fffdf7"
      ink <- "#2c2418"
      copper <- "#8b4513"
      area <- "#e4d1bb"
      mu <- 72
      sigma <- 15.2
      p <- 0.7850824
      q_left <- qnorm(p, mean = mu, sd = sigma)
      q_right <- qnorm(p, mean = mu, sd = sigma,
                     lower.tail = FALSE)
      grid <- seq(12, 132, length.out = 1201)
      density_values <- dnorm(grid, mean = mu, sd = sigma)
    
      par(mfrow = c(2, 1), mar = c(4, 4, 3, 1),
          bg = paper, fg = ink, col.axis = ink,
          col.lab = ink, col.main = ink)
    
      for (tail_choice in c("lower", "upper")) {
        cutoff <- if (tail_choice == "lower") q_left else q_right
        bounds <- if (tail_choice == "lower") {
          c(12, cutoff)
        } else {
          c(cutoff, 132)
        }
        xs <- seq(bounds[1], bounds[2], length.out = 600)
        ys <- dnorm(xs, mean = mu, sd = sigma)
        plot(grid, density_values, type = "n",
             xlab = "Score", ylab = "Density",
             main = paste("Tail", tail_choice, "- probability", p))
        polygon(c(xs[1], xs, tail(xs, 1)), c(0, ys, 0),
                col = area, border = NA)
        lines(grid, density_values, col = copper, lwd = 2)
        segments(cutoff, 0, cutoff,
                 dnorm(cutoff, mean = mu, sd = sigma), col = copper)
        abline(v = mu, col = "#c4956a", lty = 3)
        mtext(sprintf("Threshold: %.2f", cutoff), side = 3,
              line = 0.2, cex = 0.8)
      }
    
      par(mfrow = c(1, 1))
      plot(grid, pnorm(grid, mean = mu, sd = sigma),
           type = "l", col = copper, lwd = 2, ylim = c(0, 1),
           xlab = "Score", ylab = "Cumulative probability",
           main = "From p to threshold: cumulative distribution")
      segments(12, p, q_left, p, lty = 2, col = copper)
      segments(q_left, 0, q_left, p, lty = 2, col = copper)
      points(q_left, p, pch = 19, col = copper)
    
      plot_levels <- seq(0.005, 0.995, length.out = 1000)
      plot(plot_levels,
           qnorm(plot_levels, mean = mu, sd = sigma),
           type = "l", col = copper, lwd = 2,
           xlab = "Cumulative probability p", ylab = "Quantile",
           main = "Normal quantile function")
      points(c(0.5, p), c(mu, q_left), pch = 19, col = copper)
    
      plot(NA_real_, NA_real_, type = "n", xlim = c(-0.5, 4.5),
           ylim = c(0, 1), xlab = "Number of successes",
           ylab = "Cumulative probability",
           main = "Binomial(4, 0.5): cumulative distribution with jumps")
      abline(h = 0.5, lty = 2, col = "#c4956a")
      segments(-0.5, 0, 0, 0, col = copper, lwd = 2)
      for (k in 0:4) {
        cumulative <- pbinom(k, size = 4, prob = 0.5)
        endpoint <- min(k + 1, 4.5)
        segments(k, cumulative, endpoint, cumulative,
                 col = copper, lwd = 2)
        points(k, cumulative, pch = 19, col = copper)
        if (k < 4) {
          points(k + 1, cumulative, pch = 21,
                 col = copper, bg = paper)
        }
      }
      segments(2, 0, 2, pbinom(2, 4, 0.5), lty = 2, col = copper)
      invisible(NULL)
    }
    
    plot_quantiles()
    # In RStudio, use the Plots pane arrows to view the plots.
    # To save all pages in a PDF from R:
    # pdf("quantile_plots.pdf", width = 8, height = 7)
    # plot_quantiles()
    # dev.off()

    Shaded regions under the densities correspond to probabilities; the heights are densities. In the cumulative distribution plot, height represents cumulative probability. In the quantile curve, the roles of the axes change: the probability level becomes the input and the variable’s value becomes the output.

    11. Common mistakes

    A guide to interpretation and review
    MisconceptionCorrect interpretation
    qnorm() returns a probabilityIt returns a threshold associated with a probability
    sd means standard errorIt is the standard deviation of the normal model
    The quantile function is used because the cumulative distribution does not existThe cumulative distribution exists; inversion answers a different question
    Every quantile satisfies exactly F(q) = pJumps can make the attained level exceed the requested one
    The upper tail is simply the same threshold with a different explanationKeeping p fixed while changing the tail generally changes the threshold
    A quantile lists the observations below itIt locates a value; identifying observations requires the data
    A model proportion holds exactly in every sampleObserved frequencies vary across samples
    quantile() and qnorm() are interchangeableThey work with a sample and a specified model, respectively
    A pair of quantiles is always a confidence intervalInferential coverage depends on a procedure and its assumptions
    x <- rnorm generates scoresIt stores a function; the function must be called with arguments

    Interpreting any result requires recording four elements: the variable and its units, the distribution or sample used, the requested probability level, and the computational convention. If the result is used for inference, the parameter, statistic, and assumptions supporting the procedure must also be made explicit.

    A quantile turns a proportion into a position within a distribution. Its interpretation depends on which distribution is being studied and how that position is defined.

    12. Complete script and checks

    The expandable section collects the examples and plots in execution order. It can be copied to an .R file and run in a fresh session. The six test scores are illustrative and do not come from an empirical study. The last printed decimal places may vary with the version and platform.

    Expand the complete R script

    R · Complete file

    # THE QUANTILE FUNCTION IN R
    # JOSÉ MAURICIO GÓMEZ JULIÁN
    # Illustrative data; base R and stats functions.
    
    # LOWER TAIL AND VERIFICATION
    target_p <- 0.7850824
    score_mean <- 72
    score_sd <- 15.2
    
    lower_threshold <- qnorm(target_p, mean = score_mean,
                        sd = score_sd)
    lower_threshold
    # Approximately 84.00000012
    
    pnorm(lower_threshold, mean = score_mean, sd = score_sd)
    # Approximately 0.7850824
    
    # UPPER TAIL AND VERIFICATION
    upper_threshold <- qnorm(target_p, mean = score_mean,
                        sd = score_sd, lower.tail = FALSE)
    upper_threshold
    # Approximately 59.99999988
    
    pnorm(upper_threshold, mean = score_mean,
          sd = score_sd, lower.tail = FALSE)
    # Approximately 0.7850824
    
    pnorm(upper_threshold, mean = score_mean, sd = score_sd)
    # Approximately 0.2149176
    
    # PROBABILITY OUTSIDE THE 0–100 SCALE
    p_below_0 <- pnorm(0, mean = score_mean, sd = score_sd)
    p_above_100 <- pnorm(100, mean = score_mean,
                         sd = score_sd, lower.tail = FALSE)
    c(p_below_0 = p_below_0, p_above_100 = p_above_100)
    
    # COMPLETE CALL
    qnorm(p = 0.95, mean = 0, sd = 1,
          lower.tail = TRUE, log.p = FALSE)
    # Approximately 1.644854
    
    # LOGARITHMS AND EXTREME TAILS
    qnorm(log(0.95), log.p = TRUE)
    # The same quantile as qnorm(0.95)
    
    qnorm(1 - 1e-20)
    # Inf: the subtraction rounds to 1
    
    qnorm(1e-20, lower.tail = FALSE)
    # Approximately 9.26234
    
    qnorm(-1000, lower.tail = FALSE, log.p = TRUE)
    # Finite quantile for upper-tail probability exp(-1000)
    
    # MULTIPLE LEVELS AND ENDPOINTS
    qnorm(c(0.025, 0.5, 0.975), mean = 72, sd = 15.2)
    # Approximately: 42.20855  72.00000  101.79145
    
    qnorm(c(0, 1))
    # -Inf  Inf
    
    # BINOMIAL QUANTILE AND ATTAINED PROBABILITIES
    discrete_quantile <- qbinom(0.5, size = 4, prob = 0.5)
    discrete_quantile
    # 2
    
    pbinom(discrete_quantile - 1, size = 4, prob = 0.5)
    # 0.3125
    
    pbinom(discrete_quantile, size = 4, prob = 0.5)
    # 0.6875
    
    qbinom(0.5, size = 4, prob = 0.5, lower.tail = FALSE)
    # 2
    pbinom(2, size = 4, prob = 0.5, lower.tail = FALSE)
    # 0.3125
    
    # COMPARING TWO SAMPLE-QUANTILE METHODS
    scores <- c(58, 65, 70, 72, 80, 91)
    levels <- c(0.25, 0.5, 0.75)
    
    quantile(scores, probs = levels, type = 1)
    # 25% 50% 75%
    #  65  70  80
    
    quantile(scores, probs = levels, type = 7)
    #  25%   50%   75%
    # 66.25 71.00 78.00
    
    empirical_cdf <- ecdf(scores)
    empirical_cdf(70)
    # 0.5
    
    # MISSING VALUES
    scores_with_na <- c(scores, NA_real_)
    sum(is.na(scores_with_na))
    # 1
    
    quantile(scores_with_na, probs = levels,
             type = 7, na.rm = TRUE)
    # 66.25 71.00 78.00
    
    # STUDENT T INTERVAL FOR THE MEAN
    scores <- c(58, 65, 70, 72, 80, 91)
    n <- length(scores)
    confidence <- 0.95
    alpha <- 1 - confidence
    sample_mean <- mean(scores)
    sample_sd <- sd(scores)
    standard_error <- sample_sd / sqrt(n)
    critical_t <- qt(1 - alpha / 2, df = n - 1)
    
    mean_ci <- sample_mean + c(-1, 1) *
      critical_t * standard_error
    mean_ci
    # Approximately: 60.50649  84.82684
    
    automatic_ci <- t.test(scores, conf.level = confidence)$conf.int
    unname(automatic_ci)
    
    stopifnot(isTRUE(all.equal(
      as.numeric(mean_ci), as.numeric(automatic_ci),
      tolerance = 1e-10
    )))
    
    # CENTRAL INTERVAL OF THE NORMAL MODEL
    qnorm(c(0.025, 0.975), mean = 72, sd = 15.2)
    # Approximately: 42.20855  101.79145
    # Central interval of the model, not an interval for its mean
    
    # CRITICAL QUANTILE AND TWO-SIDED P-VALUE
    null_mean <- 72
    observed_t <- (sample_mean - null_mean) / standard_error
    two_sided_p <- 2 * pt(abs(observed_t), df = n - 1,
                          lower.tail = FALSE)
    
    c(observed_t = observed_t,
      critical_t = critical_t, two_sided_p = two_sided_p)
    
    abs(observed_t) > critical_t
    # FALSE
    two_sided_p < alpha
    # FALSE
    
    # INVERSE TRANSFORM
    set.seed(2021)
    uniform_values <- runif(1000)
    simulated_scores <- qnorm(uniform_values, mean = 72, sd = 15.2)
    
    mean(simulated_scores)
    sd(simulated_scores)
    # Sample approximations to 72 and 15.2
    
    # Normal generation using the dedicated function:
    set.seed(2021)
    direct_scores <- rnorm(1000, mean = 72, sd = 15.2)
    # Element-by-element equality between the two methods is not expected
    
    # PLOTS WITH BASE R
    plot_quantiles <- function() {
      previous_parameters <- par(no.readonly = TRUE)
      on.exit(par(previous_parameters))
    
      paper <- "#fffdf7"
      ink <- "#2c2418"
      copper <- "#8b4513"
      area <- "#e4d1bb"
      mu <- 72
      sigma <- 15.2
      p <- 0.7850824
      q_left <- qnorm(p, mean = mu, sd = sigma)
      q_right <- qnorm(p, mean = mu, sd = sigma,
                     lower.tail = FALSE)
      grid <- seq(12, 132, length.out = 1201)
      density_values <- dnorm(grid, mean = mu, sd = sigma)
    
      par(mfrow = c(2, 1), mar = c(4, 4, 3, 1),
          bg = paper, fg = ink, col.axis = ink,
          col.lab = ink, col.main = ink)
    
      for (tail_choice in c("lower", "upper")) {
        cutoff <- if (tail_choice == "lower") q_left else q_right
        bounds <- if (tail_choice == "lower") {
          c(12, cutoff)
        } else {
          c(cutoff, 132)
        }
        xs <- seq(bounds[1], bounds[2], length.out = 600)
        ys <- dnorm(xs, mean = mu, sd = sigma)
        plot(grid, density_values, type = "n",
             xlab = "Score", ylab = "Density",
             main = paste("Tail", tail_choice, "- probability", p))
        polygon(c(xs[1], xs, tail(xs, 1)), c(0, ys, 0),
                col = area, border = NA)
        lines(grid, density_values, col = copper, lwd = 2)
        segments(cutoff, 0, cutoff,
                 dnorm(cutoff, mean = mu, sd = sigma), col = copper)
        abline(v = mu, col = "#c4956a", lty = 3)
        mtext(sprintf("Threshold: %.2f", cutoff), side = 3,
              line = 0.2, cex = 0.8)
      }
    
      par(mfrow = c(1, 1))
      plot(grid, pnorm(grid, mean = mu, sd = sigma),
           type = "l", col = copper, lwd = 2, ylim = c(0, 1),
           xlab = "Score", ylab = "Cumulative probability",
           main = "From p to threshold: cumulative distribution")
      segments(12, p, q_left, p, lty = 2, col = copper)
      segments(q_left, 0, q_left, p, lty = 2, col = copper)
      points(q_left, p, pch = 19, col = copper)
    
      plot_levels <- seq(0.005, 0.995, length.out = 1000)
      plot(plot_levels,
           qnorm(plot_levels, mean = mu, sd = sigma),
           type = "l", col = copper, lwd = 2,
           xlab = "Cumulative probability p", ylab = "Quantile",
           main = "Normal quantile function")
      points(c(0.5, p), c(mu, q_left), pch = 19, col = copper)
    
      plot(NA_real_, NA_real_, type = "n", xlim = c(-0.5, 4.5),
           ylim = c(0, 1), xlab = "Number of successes",
           ylab = "Cumulative probability",
           main = "Binomial(4, 0.5): cumulative distribution with jumps")
      abline(h = 0.5, lty = 2, col = "#c4956a")
      segments(-0.5, 0, 0, 0, col = copper, lwd = 2)
      for (k in 0:4) {
        cumulative <- pbinom(k, size = 4, prob = 0.5)
        endpoint <- min(k + 1, 4.5)
        segments(k, cumulative, endpoint, cumulative,
                 col = copper, lwd = 2)
        points(k, cumulative, pch = 19, col = copper)
        if (k < 4) {
          points(k + 1, cumulative, pch = 21,
                 col = copper, bg = paper)
        }
      }
      segments(2, 0, 2, pbinom(2, 4, 0.5), lty = 2, col = copper)
      invisible(NULL)
    }
    
    plot_quantiles()
    # In RStudio, use the Plots pane arrows to view the plots.
    # To save all pages in a PDF from R:
    # pdf("quantile_plots.pdf", width = 8, height = 7)
    # plot_quantiles()
    # dev.off()
    
    # Numerical checks with tolerance
    stopifnot(
      abs(lower_threshold - 84) < 1e-5,
      abs(upper_threshold - 60) < 1e-5,
      abs(pnorm(lower_threshold, score_mean, score_sd) -
            target_p) < 1e-10,
      abs(pnorm(upper_threshold, score_mean, score_sd,
                 lower.tail = FALSE) - target_p) < 1e-10,
      discrete_quantile == 2,
      pbinom(discrete_quantile - 1, 4, 0.5) < 0.5,
      pbinom(discrete_quantile, 4, 0.5) >= 0.5,
      isTRUE(all.equal(
        as.numeric(quantile(scores, levels, type = 1)),
        c(65, 70, 80))),
      isTRUE(all.equal(
        as.numeric(quantile(scores, levels, type = 7)),
        c(66.25, 71, 78)))
    )
    
    sessionInfo()
    

    13. References and documentation

    The following sources document the functions, conventions, and procedures used here. The mathematical explanations and examples are developed in the article itself; these links provide access to implementation details.

    1. R Core Team. Distributions in the “stats” Package. Official R manual. The d, p, q, and r families.
    2. R Core Team. The Normal Distribution. Official R manual. dnorm, pnorm, qnorm, and rnorm.
    3. R Core Team. The Binomial Distribution. Official R manual. The binomial distribution and its quantile definition.
    4. R Core Team. Sample Quantiles. Official R manual. quantile and its nine methods. The documentation cites Hyndman, R. J. and Fan, Y. (1996), Sample Quantiles in Statistical Packages, as a reference for this classification.
    5. R Core Team. The Student t Distribution. Official R manual. dt, pt, qt, and rt.
    6. R Core Team. Student’s t-Test. Official R manual. t.test and confidence intervals.

    Back to the top ↑

  • SETS AND COMBINATORICS IN R: A COMPUTATIONAL EXPLORATION OF THE CARTESIAN PRODUCT, THE POWER SET, PERMUTATIONS, AND COMBINATIONS

    SETS AND COMBINATORICS IN R: A COMPUTATIONAL EXPLORATION OF THE CARTESIAN PRODUCT, THE POWER SET, PERMUTATIONS, AND COMBINATIONS

    Marxist Philosophy of Science
    Mathematics · Computing · Method

    Sets and Combinatorics in R

    A computational exploration of Cartesian products, power sets, permutations, and combinations

    JOSÉ MAURICIO GÓMEZ JULIÁN

    Originally published on April 4, 2021 · Revised and expanded edition

    Abstract

    This article brings together definitions, counting arguments, and reproducible experiments in R to study constructions on finite sets. It examines Cartesian products and power sets, ordered selections with and without repetition, combinations, and multiset permutations. The discussion distinguishes mathematical objects from their computational representations, explains the role of order and multiplicity, clarifies the relationship between power sets and σ-algebras, and separates the calculation of cardinalities from exhaustive enumeration.

    Keywords: finite sets; combinatorics; R; RStudio; Cartesian product; power set; σ-algebra; permutations; combinations; reproducibility.

    Counting means establishing which differences make two objects distinct. Before running a function or applying a formula, we need to decide whether order matters, whether an element may appear more than once, and whether we are constructing subsets, sequences, or collections with multiplicities. R provides an accessible laboratory for making these decisions visible and comparing their consequences with results we can inspect.

    The empirical approach proposed here consists of constructing finite examples, comparing representations, and checking identities in specific cases. This activity has exploratory and educational value and helps check implementations. An identity that holds for every size also requires a general argument: running a program for several values does not, by itself, turn an observed regularity into a theorem.

    1. Mathematical objects and their representation in R

    A set is a collection whose defining feature is membership: writing an element more than once or changing the order of presentation does not create a different set. Thus, {a, b, a} and {b, a} represent the same set. By contrast, the sequences (a, b) and (b, a) are different, and the sequence (a, a) has two positions even though it uses only one distinct symbol.

    A multiset records how often each type of element appears, without distinguishing positions. For example, {a, a, b} and {a, b, a} describe the same multiset, which differs from {a, b, b}. Here, braces are used informally to display multiplicities; the context will make clear whether they denote an ordinary set or a multiset.

    Which distinctions each object preserves
    ObjectOrderMultiplicityExample
    SetDoes not distinguish positionsMembership only{a, b} = {b, a, a}
    Sequence or tupleDistinguishes positionsMay include repetitions(a, b) ≠ (b, a)
    MultisetDoes not distinguish positionsPreserves quantities{a, a, b} ≠ {a, b, b}

    An R vector has positions and can contain repeated values; it is therefore not automatically a mathematical set. In this article, we represent sets using finite numeric vectors or character vectors with no duplicates or missing values. The function unique() removes repeated values; setequal() compares membership while ignoring order and duplicates, whereas identical() compares R objects strictly. R’s set operations are documented in [1].

    R code
    x <- c("a", "b", "a")
    A <- unique(x)
    A
    # [1] "a" "b"
    
    setequal(x, c("b", "a"))
    # [1] TRUE
    
    identical(c("a", "b"), c("b", "a"))
    # [1] FALSE

    1.1. Basic operations and verifiable laws

    The union A ∪ B contains elements belonging to at least one of the sets; the intersection A ∩ B retains their common elements; and the difference A ∖ B retains elements belonging to A but not to B. Taking a complement requires a specified universe Ω: Ac = Ω ∖ A. Without an explicit universe, the phrase “everything that is not in A” does not determine a particular set.

    R code
    S <- c("a", "b")
    T <- c("b", "c")
    Omega <- c("a", "b", "c", "d")
    union(S, T)             # "a" "b" "c"
    intersect(S, T)         # "b"
    setdiff(S, T)           # "a"
    setdiff(T, S)           # "c"
    setdiff(Omega, S)       # "c" "d"
    all(S %in% union(S, T)) # TRUE: S is a subset of S union T

    For finite sets, the identity |S ∪ T| = |S| + |T| − |S ∩ T| corrects the double counting of shared elements. De Morgan’s laws describe how complements interact with unions and intersections: (S ∪ T)c = Sc ∩ Tc and (S ∩ T)c = Sc ∪ Tc. These laws follow by examining the membership of an arbitrary element; the script also checks them in this example.

    2. Preparing the environment and reading conventions

    R is the language and computing environment; RStudio is a development interface for working with R. The code can also be run in the R console or with Rscript. The basic examples use R and its standard packages; additional enumerations use gtools, and rje::powerSet() is retained as an alternative way to construct the power set [7].

    R code
    # Run once if not already installed:
    # install.packages(c("gtools", "rje"))
    
    A <- c("a", "b", "c")  # Small, inspectable example
    B <- c(1, 2, 3)
    reference_set <- 1:10        # Reference numerical example
    n <- length(reference_set)
    k <- 3                 # Selection length, where applicable

    Outputs preceded by # are comments indicating expected results, not additional instructions. The following sections contain teaching examples; the complete script assembles the helper functions and checks in executable order. Package names are made explicit with package::function() to identify where each operation comes from.

    The visual representations and tables are built from text and accessible HTML. This allows readers to select symbols, zoom in, and consult formulas without relying on screenshots.

    3. Cartesian products: constructing pairs and tuples

    Given two sets A and B, their Cartesian product A × B is the set of all ordered pairs (a, b) such that a belongs to A and b belongs to B. If both sets are finite, each element of A can be paired with every element of B:

    A × B = {(a, b) : a ∈ A, b ∈ B}
    |A × B| = |A| · |B|

    Vertical bars denote cardinality.

    A Cartesian product preserves coordinate positions. In general, A × B and B × A are different sets, even though they have the same cardinality. If both factors are the same set, pairs such as (a, a) may occur: sharing the same source set does not require the coordinates to be distinct.

    A = {a, b, c}; B = {1, 2, 3}; nine ordered pairs
    A / B123
    a(a, 1)(a, 2)(a, 3)
    b(b, 1)(b, 2)(b, 3)
    c(c, 1)(c, 2)(c, 3)

    In R, expand.grid() produces one row for each combination of coordinates. In the function’s documentation, “combination” refers to a configuration of variable values, not an unordered selection in the combinatorial sense used in later sections. We specify stringsAsFactors = FALSE to keep text as text, and omit auxiliary attributes with KEEP.OUT.ATTRS = FALSE [2].

    R code
    product <- expand.grid(
      A = A, B = B,
      KEEP.OUT.ATTRS = FALSE,
      stringsAsFactors = FALSE
    )
    nrow(product)
    # [1] 9
    
    product_10 <- expand.grid(
      x = reference_set, y = reference_set,
      KEEP.OUT.ATTRS = FALSE,
      stringsAsFactors = FALSE
    )
    nrow(product_10)
    # [1] 100

    For k coordinates chosen from A, we write Ak. If |A| = n, then |Ak| = nk. For k = 0, we adopt A0 = {()}, the set containing the empty tuple. This convention keeps the cardinality equal to 1 even when A is empty.

    4. Power sets: all subsets of a set

    The power set of A, written 𝒫(A), contains all its subsets, including ∅ and A itself. When constructing a subset, each element of A has two possibilities: it is either included or excluded. For n distinct elements, the multiplication principle gives:

    𝒫(A) = {S : S ⊆ A}
    |𝒫(A)| = 2n

    For A = {a, b, c}, the eight subsets can be grouped by size. This arrangement by levels makes the distribution 1, 3, 3, 1 visible; a complete Hasse diagram would also include the covering relations between adjacent levels.

    The power set arranged by cardinality

    We can construct it using standard R by enumerating combinations of every size. The code uses indices to prevent combn() from interpreting a numeric vector of length one as a population size.

    R code
    A <- c("a", "b", "c")
    PA <- list(A[0])  # A list that CONTAINS the empty subset
    for (k in seq_len(length(A))) {
      indices <- utils::combn(seq_along(A), k, simplify = FALSE)
      PA <- c(PA, lapply(indices, function(i) A[i]))
    }
    length(PA)
    # [1] 8
    
    # Alternative using the package from the original version:
    # PA <- rje::powerSet(A)
    # P10 <- rje::powerSet(1:10)
    # length(P10)  # 1024

    4.1. Why the power set is a σ-algebra

    A σ-algebra ℱ on a universe Ω is a family of subsets that contains Ω and is closed under complements relative to Ω and under countable unions. These conditions imply that it also contains ∅. The power set 𝒫(Ω) satisfies these conditions because it contains every possible subset of Ω [6].

    However, “power set” and “σ-algebra” are not synonyms. On Ω = {a, b, c}, both {∅, Ω} and {∅, {a}, {b, c}, Ω} are σ-algebras; neither equals the eight-element family 𝒫(Ω). The first distinguishes only the impossible event from the certain event; the second distinguishes the block {a} from the block {b, c}.

    On a finite universe, every family of subsets is finite. Consequently, checking closure under binary unions, together with the other axioms, establishes closure under countable unions: only finitely many distinct subsets can occur in such a union. The script provides a checker for this finite setting. This procedure cannot be extended to infinite universes without further justification.

    4.2. The connection with combinations

    The subsets of A are partitioned by size. There is exactly one subset of size zero, n subsets of size one, and, in general, a binomial number of subsets of size k. Therefore:

    ∑k=0n C(n, k) = 2n

    This counts every subset once, including the empty set and the full set.

    The correspondence between a subset and its vector of membership indicators also establishes a bijection between 𝒫(A) and {0, 1}n. This is a structural connection with binary sequences, not merely a numerical coincidence.

    5. Ordered selections without repetition

    We choose k distinct elements from n available elements and assign each one a position. There are n possibilities for the first position, n − 1 for the second, and so on. For 0 ≤ k ≤ n:

    P(n, k) = n(n − 1) ··· (n − k + 1) = n!(n − k)!

    These constructions are also called variations, arrangements, or partial permutations without repetition, depending on the terminological tradition. When k = n, we obtain full permutations: P(n, n) = n!. If k > n, there are no possible selections without repetition, and for k = 0 there is a unique empty selection.

    With A = {a, b, c} and k = 2, we obtain six sequences: (a, b), (a, c), (b, a), (b, c), (c, a), and (c, b). The same pair of symbols can appear in two different orders.

    R code
    n <- 10
    k <- 3
    prod(n - seq_len(k) + 1)
    # [1] 720
    
    # Small enumeration; requires gtools:
    # p2 <- gtools::permutations(3, 2, v = A)
    # nrow(p2)  # 6

    The falling product avoids calculating two large factorials separately, although it can also overflow for large sizes. The general function in the script handles k = 0 first, avoiding misleading descending ranges.

    5.1. Summing over different lengths

    Counting all nonempty sequences of lengths 1 through n means summing P(n, k), rather than calculating a single permutation count. For n = 10, the total is 9,864,100; including the empty tuple raises it to 9,864,101. Lengths distinguish objects: a sequence with two positions is never the same as a sequence with three.

    R code
    n <- 10
    counts <- vapply(seq_len(n), function(k) {
      prod(n - seq_len(k) + 1)
    }, numeric(1))
    sum(counts)
    # [1] 9864100

    6. Ordered selections with unrestricted repetition

    If a symbol may reappear in every position, there are always n options, and the available set does not shrink. Sequences of length k form the Cartesian product Ak:

    R(n, k) = nk

    For A = {a, b, c} and k = 2, the nine results are (a, a), (a, b), (a, c), (b, a), (b, b), (b, c), (c, a), (c, b), and (c, c). Compared with the previous section, the pairs containing identical symbols have been added.

    R code
    n <- 10
    k <- 3
    n^k
    # [1] 1000
    
    # With unrestricted repetition, explicitly enabled:
    # r2 <- gtools::permutations(
    #   3, 2, v = A, repeats.allowed = TRUE
    # )
    # nrow(r2)  # 9
    
    # No external packages needed for the two-position case:
    pairs <- expand.grid(x = A, y = A,
                         KEEP.OUT.ATTRS = FALSE,
                         stringsAsFactors = FALSE)
    nrow(pairs)
    # [1] 9

    For n = 10 and k = 10, the count is 10,000,000,000. Here, it is best to calculate the cardinality and stop: exhaustive generation is not a reasonable example for an ordinary computer. The script’s memory safeguard prevents this enumeration.

    7. Combinations without repetition: choosing subsets

    We now choose k distinct elements without distinguishing order. Every subset of size k gives rise to k! ordered sequences. Dividing the preceding count by that number gives:

    C(n, k) = nk = n!k!(n − k)!

    Factorial domain: 0 ≤ k ≤ n. By combinatorial convention, C(n, k) = 0 when k > n.

    With A = {a, b, c} and k = 2, there are three subsets: {a, b}, {a, c}, and {b, c}. A program’s writing {a, b} in the order of the input vector does not give the subset positions.

    R code
    choose(10, 3)
    # [1] 120
    
    A <- c("a", "b", "c")
    indices <- utils::combn(seq_along(A), 2, simplify = FALSE)
    subsets <- lapply(indices, function(i) A[i])
    length(subsets)
    # [1] 3
    
    # Another representation: each column is a combination.
    utils::combn(A, 2)
    #      [,1] [,2] [,3]
    # [1,] "a"  "a"  "b"
    # [2,] "b"  "c"  "c"

    choose() calculates cardinalities; combn() generates selections. By default, the latter arranges results in columns, whereas gtools::combinations() arranges them in rows. Confusing these orientations leads to counting the length of a selection instead of the number of selections [3].

    For a ten-element set, summing combinations of sizes 1 through 10 gives 1,023 nonempty subsets. Adding the empty set yields the 1,024 elements of its power set. The identity C(n, k) = C(n, n − k) also follows from the bijection that assigns each subset its complement.

    8. Combinations with repetition: choosing multiplicities

    There are n available types; we choose a total of k units and allow types to repeat, without distinguishing positions. An outcome can be described by nonnegative integer multiplicities x₁, …, xₙ satisfying:

    x₁ + x₂ + ··· + xₙ = k,   xᵢ ≥ 0

    For n ≥ 1, the stars and bars method represents the k units as stars and separates the n categories with n − 1 bars. Choosing the positions of the stars among k + n − 1 places is equivalent to choosing the multiplicities:

    M(n, k) = C(n + k − 1, k) = (n + k − 1)!k!(n − 1)!

    With A = {a, b, c} and k = 2, the six selections are {a, a}, {a, b}, {a, c}, {b, b}, {b, c}, and {c, c}. In particular, {a, b} and {b, a} represent the same outcome. For n = 10 and k = 3, the count is 220.

    R code
    n <- 10
    k <- 3
    choose(n + k - 1, k)
    # [1] 220
    
    # Small enumeration; requires gtools:
    # m2 <- gtools::combinations(
    #   3, 2, v = A, repeats.allowed = TRUE
    # )
    # nrow(m2)  # 6

    As an additional visual example, with the four symbols {1, 2, 3, 4} and size 2, the selections are 11, 12, 13, 14, 22, 23, 24, 33, 34, and 44. These are ten combinations with repetition; there would be sixteen ordered sequences because they would distinguish, for example, 12 from 21. A tree enumerating only those ten branches belongs in this section.

    9. APPLE: permuting a fixed multiset

    The word APPLE has five positions but four types of letters: A, P, L, and E. The letter P occurs twice. Swapping those two Ps does not change the visible word. Its distinct rearrangements therefore number neither 5! nor the unrestricted five-letter sequences drawn from four symbols.

    When n objects have fixed multiplicities m₁, …, mᵣ summing to n, each visible sequence is counted m₁! ··· mᵣ! times if we artificially distinguish every object. Removing that distinction gives:

    N = n!m₁! m₂! ··· mᵣ!
    NAPPLE = 5!1! · 2! · 1! · 1! = 60
    R code
    letters_in_word <- strsplit("APPLE", "", fixed = TRUE)[[1]]
    multiplicities <- as.numeric(table(letters_in_word))
    factorial(length(letters_in_word)) / prod(factorial(multiplicities))
    # [1] 60

    The distinction has two parts: combinations with repetition choose the multiplicities; multiset permutations take the multiplicities as fixed and count the possible orders. The script checks the APPLE example through a small enumeration: it permutes the five positions and removes duplicate words. This strategy is educational; it is inefficient for large multisets because it generates duplicates before discarding them.

    The correct formula depends on which distinctions are preserved by the object we want to count.

    10. Comparison tables and totals by length

    The following formulas answer different questions. For n = 10 and k = 3, comparing them shows how much the count changes when we alter the importance of order or the possibility of repetition.

    Four selection problems; n = 10 and k = 3
    ConstructionOrderRepetitionCardinalityResult
    Ordered, without repetitionMattersNon! / (n − k)!720
    Ordered, with unrestricted repetitionMattersYesnk1,000
    Combinations without repetitionDoes not matterNoC(n, k)120
    Combinations with repetitionDoes not matterYesC(n + k − 1, k)220

    The two formulas without repetition require k ≤ n to use their factorial expressions. The formula for combinations with repetition applies directly for n ≥ 1. All models include one empty selection when k = 0.

    Counting and enumerating are different operations
    ObjectCountEnumerate in small examples
    Product A × Blength(A) * length(B)expand.grid()
    Power set2^length(A)The script’s power_set(); rje::powerSet()
    Ordered, without repetitioncount_ordered(n, k)gtools::permutations()
    Ordered, with repetitionn^kgtools::permutations(..., repeats.allowed = TRUE)
    Combinations without repetitionchoose(n, k)utils::combn()
    Combinations with repetitionchoose(n + k - 1, k)gtools::combinations(..., repeats.allowed = TRUE)

    10.1. The eleven sizes for n = 10

    The k = 0 row is essential for identities that include the empty object. The table presents counts without generating the corresponding collections.

    n = 10; k from zero to ten
    kOrdered
    without repetition
    Ordered
    with repetition
    Combinations
    without repetition
    Combinations
    with repetition
    01111
    110101010
    2901004555
    37201,000120220
    45,04010,000210715
    530,240100,0002522,002
    6151,2001,000,0002105,005
    7604,80010,000,00012011,440
    81,814,400100,000,0004524,310
    93,628,8001,000,000,0001048,620
    103,628,80010,000,000,000192,378

    10.2. Nonempty totals: k from 1 through 10

    Sums over lengths, with n = 10
    FamilySumTotal
    Ordered, without repetition∑ P(10, k)9,864,100
    Ordered, with unrestricted repetition∑ 10k11,111,111,110
    Combinations without repetition210 − 11,023
    Combinations with repetitionC(20, 10) − 1184,755

    The last result follows from the identity ∑k=0m C(n + k − 1, k) = C(n + m, m), for n ≥ 1. One justification adds an auxiliary category that absorbs the units needed to reach m: choosing up to m units from n types is equivalent to distributing exactly m units among n + 1 types. We subtract one from the final count to exclude the empty selection.

    With unrestricted repetition and at least one available symbol, allowing k to grow without a bound produces infinitely many sequences and infinitely many finite multisets. The range of sizes must therefore be stated whenever a “total” is presented.

    R code
    n <- 10
    ks <- 0:n
    counts <- data.frame(
      k = ks,
      ordered_without = vapply(ks, function(k) {
        if (k == 0) 1 else prod(n - seq_len(k) + 1)
      }, numeric(1)),
      ordered_with = n^ks,
      combinations_without = choose(n, ks),
      combinations_with = choose(n + ks - 1, ks)
    )
    colSums(counts[-1, -1])
    # ordered_without       ordered_with
    #       9864100         11111111110
    # combinations_without   combinations_with
    #             1023              184755

    11. Scalability, limits, and numerical precision

    Knowing a cardinality does not require materializing every object. A hypothetical matrix with 1010 rows, ten columns, and eight bytes per cell would require approximately 800 decimal GB for the data alone, before accounting for auxiliary structures and temporary copies. This estimate applies to that specific representation; actual consumption depends on the data type and algorithm.

    The script therefore sets teaching limits of 100,000 results and 1,000,000 estimated cells before enumeration. These limits do not guarantee that every admitted computation will be fast or use little memory; they prevent accidental execution of the largest examples. For larger tasks, consider lazy generation, partial traversal, or specialized algorithms.

    11.1. Overflow and approximate arithmetic

    R’s double numbers do not represent every arbitrarily large integer exactly. Rounding can occur even when the result is not infinite. A quotient of factorials does not avoid this problem either: intermediate factorials can overflow even when the final result is representable.

    choose() calculates binomial coefficients, and lchoose() obtains their logarithms without first forming the full quantity; lfactorial() serves an analogous purpose for factorials [5]. To estimate an order of magnitude:

    R code
    # Base-10 logarithm of C(2000, 1000):
    log10_binomial <- lchoose(2000, 1000) / log(10)
    log10_binomial
    # Approximately 600.3114
    
    # Logarithm of P(n, k), within the domain 0 <= k <= n:
    n <- 1000
    k <- 500
    log_P <- lfactorial(n) - lfactorial(n - k)

    Working with logarithms reduces numerical range problems; it does not turn a calculation into exact integer arithmetic. Nor should an enormous logarithm automatically be exponentiated, since overflow may reappear. If every digit of a large integer is needed, an appropriate arbitrary-precision tool must be selected and validated for that purpose.

    11.2. What the checks establish

    The script compares counts with small enumerations, checks summation identities, and examines boundary cases. It also avoids confusing an empty list with a list containing the empty selection. These tests detect implementation errors, inconsistent conventions, and regressions. Explanations based on bijections and counting principles justify the results beyond the finite grid that is checked.

    12. Mathematical interpretation and applications

    The preceding constructions are related by specific functions and correspondences. Forgetting the order of a sequence of length k without repetition produces a subset of size k, and every subset has k! preimages. If repetitions are allowed, forgetting order produces a multiset; in this case, the number of sequences associated with each multiset depends on its multiplicities.

    This difference explains why dividing nk by k! does not, in general, give the number of combinations with repetition. For A = {a, b} and k = 2, the sequences aa and bb each have a single visible order, whereas ab and ba are grouped into the same multiset. The groups do not all have the same size.

    12.1. Probability: enumeration does not specify a distribution

    Suppose we make two independent draws with replacement from a mechanism that produces a and b with probability 1/2 each. The sequences aa, ab, ba, and bb each have probability 1/4. After forgetting order, the three multisets {a, a}, {a, b}, and {b, b} have probabilities 1/4, 1/2, and 1/4. Choosing uniformly among these three multisets therefore does not reproduce the same experiment.

    The formula “favorable outcomes / possible outcomes” requires the counted outcomes to be equally probable. Defining the sample space, the selection mechanism, and the distribution is an additional step beyond enumerating objects.

    12.2. Experimental design and data science

    • Cartesian product: constructing every configuration in a parameter grid or every combination of factor levels. If the factors have different sizes, multiply their cardinalities.
    • Power set: describing possible selections of variables. A set of n variables has 2n subsets, including the model with no selected predictors.
    • Ordered selections without repetition: assigning distinct positions to different people, with one candidate per position and no reuse.
    • Combinations without repetition: selecting committees or subsets of observations when selection order is not part of the outcome.
    • Combinations with repetition: distributing indistinguishable units among identified categories, with nonnegative quantities and no upper bounds.

    The assumptions must be reviewed for each application. Incompatible factor combinations, limited inventories, unequal probabilities, or additional constraints change the problem. A table of formulas guides the choice; the definition of the object and mechanism determines which formula is appropriate.

    12.3. Epistemological scope

    A computational representation selects which information to retain. Converting a sequence into a set removes order and multiplicity; converting it into a multiset removes order but preserves frequencies. These transformations show that a mathematical abstraction is more than a change of name: it changes the distinctions organizing the problem and, with them, the counting results.

    Computational experimentation becomes more informative when that selection is explicit. Defining, representing, deducing, executing, and comparing are complementary stages of reproducible inquiry into finite objects.

    13. Complete reproducible script

    The following program brings the article together. Copy its contents into a text file named sets_combinatorics.R, save it as UTF-8, and run it with source("sets_combinatorics.R", encoding = "UTF-8") or Rscript sets_combinatorics.R. External packages are used only if installed; the program reports when it skips those blocks.

    The counting functions validate nonnegative integer sizes. The enumerators represent each selection as a vector inside a list, including empty cases. The size safeguard runs before calling the enumerators. The representation domain is restricted to finite numeric vectors and character vectors without missing values; sets of more complex objects require an appropriate equality relation.

    Open the complete script · functions, examples, and checks
    Complete script · copy from the first line through sessionInfo()
    # SETS AND COMBINATORICS IN R
    # JOSÉ MAURICIO GÓMEZ JULIÁN
    # Article script. Save as sets_combinatorics.R (UTF-8).
    # The basic examples require no external packages.
    # install.packages(c("gtools", "rje")) # Only if the optional blocks are wanted.
    
    # 1. Validation and counting functions -------------------------------
    nonnegative_integer <- function(x, name) {
      if (!is.numeric(x) || is.complex(x) || length(x) != 1L || is.na(x) ||
          !is.finite(x) || x < 0 || x != floor(x)) {
        stop(name, " must be a finite nonnegative integer.")
      }
      invisible(TRUE)
    }
    
    validate_nk <- function(n, k) {
      nonnegative_integer(n, "n")
      nonnegative_integer(k, "k")
    }
    
    normalize_set <- function(x) {
      if ((!is.numeric(x) && !is.character(x)) || is.complex(x) ||
          !is.null(dim(x)) || anyNA(x) ||
          (is.numeric(x) && any(!is.finite(x)))) {
        stop("Use a finite numeric or character vector, with no NA values or dimensions.")
      }
      unique(x)
    }
    
    count_ordered <- function(n, k, repetition = FALSE) {
      validate_nk(n, k)
      if (!is.logical(repetition) || length(repetition) != 1L ||
          is.na(repetition)) stop("repetition must be TRUE or FALSE.")
      if (k == 0) return(1)
      if (repetition) return(n^k)
      if (k > n) return(0)
      # Falling product: avoids forming two factorials separately.
      prod(n - seq_len(k) + 1)
    }
    
    count_combinations <- function(n, k, repetition = FALSE) {
      validate_nk(n, k)
      if (!is.logical(repetition) || length(repetition) != 1L ||
          is.na(repetition)) stop("repetition must be TRUE or FALSE.")
      if (k == 0) return(1)
      if (n == 0) return(0)
      if (repetition) return(choose(n + k - 1, k))
      if (k > n) return(0)
      choose(n, k)
    }
    
    limit_enumeration <- function(rows, columns,
                                    max_rows = 1e5,
                                    max_cells = 1e6) {
      if (!is.finite(rows) || rows > max_rows ||
          rows * max(1, columns) > max_cells) {
        stop("Enumeration stopped by the teaching limit; calculate only the count.")
      }
      invisible(TRUE)
    }
    
    # All enumerations return lists of vectors.
    # For k=0, return list(x[0]): a list containing an empty vector.
    enumerate_ordered <- function(x, k, repetition = FALSE) {
      x <- normalize_set(x)
      n <- length(x)
      total <- count_ordered(n, k, repetition)
      limit_enumeration(total, k)
      if (k == 0) return(list(x[0]))
      if (total == 0) return(list())
      if (!requireNamespace("gtools", quietly = TRUE)) {
        stop("This enumerator requires gtools: install.packages('gtools').")
      }
      m <- gtools::permutations(n, k, v = x,
                                repeats.allowed = repetition)
      lapply(seq_len(nrow(m)), function(i) unname(m[i, ]))
    }
    
    enumerate_combinations <- function(x, k, repetition = FALSE) {
      x <- normalize_set(x)
      n <- length(x)
      total <- count_combinations(n, k, repetition)
      limit_enumeration(total, k)
      if (k == 0) return(list(x[0]))
      if (total == 0) return(list())
      if (!repetition) {
        # Using indices prevents combn(5, 1) from interpreting 5 as 1:5.
        indices <- utils::combn(seq_len(n), k, simplify = FALSE)
        return(lapply(indices, function(i) x[i]))
      }
      if (!requireNamespace("gtools", quietly = TRUE)) {
        stop("This enumerator requires gtools: install.packages('gtools').")
      }
      m <- gtools::combinations(n, k, v = x, repeats.allowed = TRUE)
      lapply(seq_len(nrow(m)), function(i) unname(m[i, ]))
    }
    
    power_set <- function(x) {
      x <- normalize_set(x)
      n <- length(x)
      limit_enumeration(2^n, n)
      result <- list(x[0])
      for (k in seq_len(n)) {
        result <- c(result, enumerate_combinations(x, k))
      }
      result
    }
    
    format_family <- function(F) {
      vapply(F, function(x) {
        if (length(x) == 0) "{}" else paste0("{", paste(x, collapse = ", "), "}")
      }, character(1))
    }
    
    # 2. Sets and Cartesian products -------------------------------
    A <- c("a", "b", "c")
    B <- c(1, 2, 3)
    reference_set <- 1:10
    n <- length(reference_set)
    
    stopifnot(
      setequal(c("a", "b", "a"), c("b", "a")),
      !identical(c("a", "b"), c("b", "a")),
      identical(normalize_set(c(1, 1, 2)), c(1, 2))
    )
    
    product <- expand.grid(A = A, B = B,
                            KEEP.OUT.ATTRS = FALSE, stringsAsFactors = FALSE)
    stopifnot(nrow(product) == length(A) * length(B))
    product_10 <- expand.grid(x = reference_set, y = reference_set,
                               KEEP.OUT.ATTRS = FALSE, stringsAsFactors = FALSE)
    stopifnot(nrow(product_10) == 100)
    print(product)
    
    # Set operations and laws within an explicit universe.
    S <- c("a", "b")
    T <- c("b", "c")
    Omega <- c("a", "b", "c", "d")
    stopifnot(
      length(union(S, T)) == length(S) + length(T) - length(intersect(S, T)),
      setequal(setdiff(Omega, union(S, T)),
               intersect(setdiff(Omega, S), setdiff(Omega, T))),
      setequal(setdiff(Omega, intersect(S, T)),
               union(setdiff(Omega, S), setdiff(Omega, T)))
    )
    
    # 3. Power set and finite sigma-algebra ----------------------
    PA <- power_set(A)
    P10 <- power_set(reference_set)
    stopifnot(length(PA) == 8, length(P10) == 1024,
              sum(lengths(PA) == 0) == 1)
    print(format_family(PA))
    
    contains_set <- function(x, F) {
      any(vapply(F, function(y) setequal(x, y), logical(1)))
    }
    
    is_finite_sigma_algebra <- function(F, omega) {
      if (!is.list(F)) return(FALSE)
      if (!all(vapply(F, function(x) all(x %in% omega), logical(1)))) {
        return(FALSE)
      }
      if (!contains_set(omega[0], F) || !contains_set(omega, F)) return(FALSE)
      for (x in F) {
        if (!contains_set(setdiff(omega, x), F)) return(FALSE)
        for (y in F) if (!contains_set(union(x, y), F)) return(FALSE)
      }
      TRUE
    }
    
    F_min <- list(A[0], A)
    F_intermediate <- list(A[0], "a", c("b", "c"), A)
    stopifnot(is_finite_sigma_algebra(F_min, A),
              is_finite_sigma_algebra(F_intermediate, A),
              is_finite_sigma_algebra(PA, A),
              !is_finite_sigma_algebra(list(A[0], "a", A), A))
    # On a finite universe, closure under binary unions suffices here:
    # any countable union involves only finitely many distinct subsets.
    
    # 4. Small sequence enumeration using base R -----------------------
    pairs <- expand.grid(x = A, y = A,
                         KEEP.OUT.ATTRS = FALSE, stringsAsFactors = FALSE)
    without_repetition <- pairs[pairs$x != pairs$y, , drop = FALSE]
    stopifnot(nrow(pairs) == 9, nrow(without_repetition) == 6,
              length(enumerate_combinations(A, 2)) == 3)
    
    # 5. Count table for n=10, k=0,...,10 ------------------------
    ks <- 0:n
    counts <- data.frame(
      k = ks,
      ordered_without = vapply(ks, function(k) count_ordered(n, k), numeric(1)),
      ordered_with = vapply(ks, function(k) count_ordered(n, k, TRUE), numeric(1)),
      combinations_without = vapply(ks, function(k) count_combinations(n, k), numeric(1)),
      combinations_with = vapply(ks, function(k) count_combinations(n, k, TRUE), numeric(1))
    )
    print(counts, row.names = FALSE)
    stopifnot(sum(counts$combinations_without) == 2^n,
              sum(counts$combinations_without[-1]) == 1023,
              sum(counts$ordered_without[-1]) == 9864100,
              sum(counts$ordered_with[-1]) == 11111111110,
              sum(counts$combinations_with[-1]) == 184755)
    
    # Lists for all lengths are illustrated with n=3.
    # Millions of sequences for n=10 are not enumerated.
    combinations_by_k <- lapply(0:length(A), function(k) {
      enumerate_combinations(A, k)
    })
    stopifnot(sum(lengths(combinations_by_k)) == 8)
    
    # 6. APPLE: permutations of a multiset -----------------------
    letters_in_word <- strsplit("APPLE", "", fixed = TRUE)[[1]]
    multiplicities <- as.numeric(table(letters_in_word))
    apple_count <- factorial(length(letters_in_word)) /
      prod(factorial(multiplicities))
    stopifnot(apple_count == 60)
    
    # 7. Boundary cases and memory safeguards ------------------------
    stopifnot(count_ordered(0, 0) == 1,
              count_ordered(0, 0, TRUE) == 1,
              count_ordered(0, 2, TRUE) == 0,
              count_ordered(2, 3) == 0,
              count_combinations(0, 0, TRUE) == 1,
              count_combinations(0, 2, TRUE) == 0,
              length(power_set(numeric(0))) == 1,
              identical(enumerate_combinations(5, 1), list(5)),
              length(enumerate_combinations(A, 0)) == 1)
    
    expected_failure <- tryCatch({
      limit_enumeration(10^10, 10)
      FALSE
    }, error = function(e) TRUE)
    stopifnot(expected_failure)
    
    # Large counts: logarithmic scales do not provide exact arithmetic.
    log10_binomial <- lchoose(2000, 1000) / log(10)
    print(log10_binomial)
    
    # 8. Optional block: gtools ---------------------------------------
    if (requireNamespace("gtools", quietly = TRUE)) {
      p2 <- enumerate_ordered(A, 2)
      r2 <- enumerate_ordered(A, 2, TRUE)
      c2 <- enumerate_combinations(A, 2)
      m2 <- enumerate_combinations(A, 2, TRUE)
      stopifnot(length(p2) == 6, length(r2) == 9,
                length(c2) == 3, length(m2) == 6)
    
      # Compare generated objects with formulas on a small grid.
      for (nn in 1:4) {
        xx <- letters[seq_len(nn)]
        for (kk in 0:4) {
          for (rr in c(FALSE, TRUE)) {
            stopifnot(
              length(enumerate_ordered(xx, kk, rr)) ==
                count_ordered(nn, kk, rr),
              length(enumerate_combinations(xx, kk, rr)) ==
                count_combinations(nn, kk, rr)
            )
          }
        }
      }
      ordered_by_k <- lapply(0:length(A), function(k) {
        enumerate_ordered(A, k)
      })
      stopifnot(sum(lengths(ordered_by_k)) == 16)
    
      # APPLE: distinguish positions first; then remove duplicate words.
      positions <- gtools::permutations(length(letters_in_word), length(letters_in_word))
      words <- apply(positions, 1, function(i) paste0(letters_in_word[i], collapse = ""))
      apple_words <- unique(words)
      stopifnot(length(apple_words) == apple_count)
      print(head(apple_words))
    } else {
      message("gtools not installed: its examples were skipped; base counts were computed.")
    }
    
    # 9. Optional block: rje ------------------------------------------
    if (requireNamespace("rje", quietly = TRUE)) {
      PA_rje <- rje::powerSet(A)
      stopifnot(length(PA_rje) == 8,
                all(vapply(PA_rje, function(x) contains_set(x, PA), logical(1))))
    } else {
      message("rje not installed: the base-R power-set construction was used.")
    }
    
    cat("Checks completed without errors.\n")
    sessionInfo()
    

    The output of sessionInfo() records the R version, platform, and packages attached or loaded through a namespace. Keep it alongside the results. The absence of gtools or rje does not count as a successful check of its respective block: those examples are skipped with an explicit message.

    14. References and documentation

    The following references support the functions used and the conceptual clarification concerning σ-algebras. The counting arguments are developed in the article through multiplication principles, bijections, and explanations of how outcomes are identified.

    1. R Core Team. Set Operations. Official documentation for the base package. Union, intersection, difference, and set comparison operations. Read the documentation.
    2. R Core Team. Create a Data Frame from All Combinations of Factor Variables. Documentation for expand.grid(). Read the documentation.
    3. R Core Team. Generate All Combinations of n Elements, Taken m at a Time. Documentation for utils::combn(). Read the documentation.
    4. gtools package documentation. Enumerate the Combinations or Permutations of the Elements of a Vector. Reference for combinations(), permutations(), and their arguments. Read the CRAN documentation.
    5. R Core Team. Special Functions of Mathematics. Documentation for choose(), lchoose(), factorial(), and lfactorial(). Read the documentation.
    6. Kehle, Christoph (2025). Introduction to Functional Analysis. Lecture notes for 18.102, Massachusetts Institute of Technology, Section 3.1, Definition 3.1 and example, pp. 25–26. Definition of a σ-algebra and the power-set example with counting measure. Read the lecture notes.
    7. rje package documentation. Miscellaneous Useful Functions for Statistics. Reference index, powerSet entry; also available from R with help("powerSet", package = "rje"). Read the package index.

    JOSÉ MAURICIO GÓMEZ JULIÁN · Marxist Philosophy of Science

    Understanding the object we count is the first step toward understanding the result.

    Consult the R manual
  • ABOUT PRINCIPAL COMPONENT ANALYSIS (PCA)

    ABOUT PRINCIPAL COMPONENT ANALYSIS (PCA)

    Marxist Philosophy of Science · Mathematical statistics

    On Principal Component Analysis (PCA)

    Geometric intuition, algebraic foundations, statistical interpretation, and the epistemological scope of dimensionality reduction

    JOSÉ MAURICIO GÓMEZ JULIÁN

    Representing a phenomenon through many variables allows a rich description, but can also make its principal relationships harder to identify. Principal component analysis—PCA—constructs new coordinates to study the joint variation of those variables. The decisive question is how to reduce a representation’s complexity without losing sight of what is preserved, what is discarded, and under which conditions its interpretation is legitimate.

    Abstract

    This essay develops PCA through four connected perspectives: a geometric transformation of a cloud of observations; a constrained optimization problem; a matrix decomposition; and a tool for scientific exploration and interpretation. It retains the gene-expression and financial-assessment examples, component-retention criteria, graphical diagnostics, Mahalanobis distance, the discussion of NIPALS, and the comparison with linear regression. Mathematical derivation, reproducibility, and the conditions supporting inference are expanded.

    Editorial note. All thematic strands and 51 figures from the 2021 document are preserved. Formulations requiring greater precision are revised in the main discussion; the original images are collected without cropping in the visual archive and retain their attributions. The historical Spanish PDF remains available at the end.

    I. Variables, attributes, and dimensionality

    A dataset organizes observations of particular units of study: people, animals, firms, territories, products, or states of a physical system. Each unit is described by variables. When p numerical variables are recorded for n units, the usual representation is a matrix X of size n × p: rows correspond to observations and columns to variables. Each row can be interpreted as a point in the feature space ℝp.

    The word attribute requires clarification. In data science it may designate a measured feature; in R it also refers to metadata associated with an object, such as its dimensions, names, or class. Adler’s observation about the importance of attributes in R objects is relevant to understanding their computational representation, but such metadata should not automatically be identified with the statistical variables in an investigation. A class attribute can change how R interprets an object without adding a dimension to the measured phenomenon. [1]

    The dimensionality of a representation is the number of coordinates used. Intrinsic dimensionality, by contrast, refers to the degrees of freedom needed to describe the underlying structure under the model being considered. A cloud lying approximately on a plane may have many coordinates while still admitting an approximately two-dimensional representation. With noise, effective dimensionality also depends on the approximation tolerance. There is not always a single minimum number independent of the purpose of the analysis.

    Variable selection and component extraction

    Dimensionality reduction encompasses different strategies. Variable selection retains a subset of the original columns; feature extraction constructs new variables from them. PCA belongs to the second family: a component may combine information from all the initial variables. By definition, it does not identify which columns should be removed or solve a combinatorial search over subsets. Such problems can be connected to PCA, but they have different formulations.

    This distinction also limits the meaning of “essential information.” In ordinary PCA, the operational criterion is the variance represented by a linear subspace. Whether that variation is essential to a biological, economic, or social explanation requires an additional argument about measurement, scale, context, and the research question.

    II. Bellman, Pearson, and the problem of representation

    The expression curse of dimensionality, associated with Richard Bellman, brings together difficulties that arise as the number of dimensions increases: growing computational costs for certain procedures, relative sparsity of observations, reduced effectiveness of some notions of neighborhood, and the need to impose structure on the problem. On a grid with ten positions per axis, systematically exploring p dimensions produces 10p combinations. This illustrates exponential growth; it is not a universal law requiring every statistical method to use exactly that amount of data. [3]

    The difficulty depends on the problem, distribution, regularity, and relationships among variables. It is therefore useful to distinguish feature space from the sample space of probability theory: the former organizes observable coordinates, whereas the latter contains the possible outcomes of an experiment. They can be connected through random variables, but the concepts are not interchangeable.

    In 1901, Karl Pearson studied lines and planes of closest fit to systems of points. His geometric approach approximates a cloud by a line or plane by minimizing the sum of squared orthogonal distances. Hotelling’s later formulation helped establish principal component analysis as a multivariate technique. This genealogy predates the contemporary vocabulary of machine learning. [11] [22]

    Historical context and scientific criteria

    The original document politically characterizes Pearson as a Marxist. The question of the relationship between intellectual biography, the history of statistics, and philosophy of science is retained here, but that classification requires a dedicated historical investigation and is not used as a premise in deriving PCA. The method’s mathematical validity and the historical assessment of its author belong to different levels of argument that must be supported separately.

    Pearson’s line provides a useful intuition: an elongated cloud can be described relatively accurately by a coordinate along its dominant direction. Identifying that direction, however, does not establish a cause of the phenomenon. The fit describes a structure of variation in the measurements; explaining it requires additional theory and evidence.

    III. Definition and optimization of PCA

    Let x be a random vector of p variables, μ its mean, and Σ its covariance matrix, assuming finite second moments. The first principal component is a linear combination of the centered variables with maximum variance, subject to a normalization constraint on its coefficients. In sample analysis, the population covariance is replaced by its estimate S.

    PC1 = a1T(x − μ) = ∑j=1p aj1(xj − μj)
    maximize aTΣa, subject to aTa = 1.

    The vector a₁ defines a direction; the component is the variable obtained by projecting onto it. The unit-length constraint prevents artificially increasing variance by multiplying all coefficients by a constant.

    Without this constraint, multiplying a by an arbitrarily large number would increase the combination’s variance without bound. Normalization makes variation along different directions comparable. The second component maximizes the remaining variance, with a unit coefficient vector orthogonal to the first. The procedure continues successively.

    ak ∈ arg max aTΣa
    subject to aTa = 1 and aTaℓ = 0 for ℓ < k.

    Each new direction is chosen in the orthogonal complement of the preceding ones. This formulation characterizes the solution independently of the algorithm used to compute it.

    The Lagrangian for the first direction is L(a, λ) = aTΣa − λ(aTa − 1). The stationarity condition leads to Σa = λa. The principal directions are therefore eigenvectors of the covariance matrix, and their variances are the corresponding eigenvalues. The first direction is associated with the largest eigenvalue; the remaining directions are ordered in decreasing order. If eigenvalues are repeated, a particular basis for the associated subspace may not be unique.

    The mathematical interpretation has two levels. Eigenvectors specify how to combine the original coordinates; eigenvalues quantify variation along those combinations. Observation scores show where each unit of study lies in the new coordinate system. Keeping these three objects separate avoids confusing weights, observations, and amounts of variation.

    The direction tells us how to look at the data; the score locates an observation in that view; the eigenvalue measures how much variation appears along it.

    IV. Two genes and six mice

    Following Josh Starmer’s teaching example used in the original document, consider six mice described by expression measurements for two genes. The mice are the units of study and the genes are the variables. Transcription is the synthesis of RNA from DNA; it must be distinguished from translation, which produces proteins. The quantities are retained as teaching values: no experimental unit or biological precision undocumented by the example is attributed to them. [13]

    Original example data · transposed to show one observation per row
    MouseGene 1Gene 2
    1106
    2114
    385
    433
    522.8
    611

    In a two-dimensional plot, each mouse occupies the position defined by its two measurements. The first three have relatively high values for both genes, while the remaining mice have lower values. The cloud allows us to ask questions about covariation and grouping, but a figure alone does not establish population differences or causal mechanisms.

    The first step in centered PCA is to subtract each variable’s mean. For these values, the mean of gene 1 is approximately 5.8333 and that of gene 2 is 3.6333. The centroid moves to the origin; differences between pairs of observations remain unchanged.

    xijc = xij − x̄j
    Xc = X − 1x̄T.

    The superscript c denotes centering; 1 is a column vector of n ones. Centering is not the same as dividing by standard deviations.

    Starmer’s original sequence is preserved in full in the visual archive. The new figure shows the same six centered points, with the principal direction computed from the table and the orthogonal residuals involved in the fitting criterion.

    Computed orthogonal projections for the six mice in the original table.
    Figure from the revised edition. Projections computed for the six mice in the table. Centering preserves distances; projection onto PC1 introduces the indicated residuals.

    The example teaches the geometric mechanics. An actual gene-expression study requires attention to measurement, sequencing depth, appropriate transformations, batch effects, replication, and uncertainty. PCA on an unprepared count table may reflect technical magnitudes before biological differences of interest.

    V. Transformations, structure, and preservation

    A transformation is a mapping between spaces. It may be linear, affine, or of another kind. It need not map a domain into the same set: in general, we write f: D → C, and its image is the set of values actually reached in the codomain. For ordinary PCA, translations, orthogonal coordinate changes, and projections onto subspaces are especially important.

    Transformations discussed in the original and their properties
    TransformationCharacterizationImportant distinction
    Translationx ↦ x + bPreserves distances and differences between points; generally not linear because it does not fix the origin.
    Rotationx ↦ Qx, with QTQ = I and det Q = 1Preserves inner products, norms, angles, and distances.
    ReflectionAn orthogonal transformation reversing orientationAlso preserves distances. An orthogonal matrix may combine rotations and reflections.
    Uniform dilation or contractionx ↦ cxMultiplies all distances by |c|; an isometry only when |c| = 1.
    Stretching along one or more axesx ↦ Dx, with D diagonalChanges relative geometry when scale factors differ.
    ShearA linear deformation that shifts coordinates according to other coordinatesMay preserve area or volume in particular cases, but generally alters angles and lengths.
    Orthogonal projectionx ↦ Px, with PT = P and P² = PGenerally not invertible; dimensionality reduction may contract distances and map distinct points onto the same point.

    Isomorphism, homeomorphism, and isometry

    An isomorphism preserves the specified structure: for example, addition and scalar multiplication in vector spaces. A homeomorphism is a continuous bijection with a continuous inverse. An isometry preserves distances; when also surjective, it establishes an equivalence between metric spaces. These notions are related, but none should be used indiscriminately in place of the others.

    Preserving topology does not imply preserving a norm or metric: multiplying all coordinates by two provides an elementary counterexample. The transformation is a homeomorphism of ℝp and changes every nonzero distance. Similarly, a translation preserves distances without being a linear isomorphism under the original vector operations, unless it is the zero translation.

    In an inner-product space, the Euclidean norm is defined by ‖x‖ = √⟨x,x⟩ and the induced distance by d(x,y) = ‖x − y‖. The norm measures vector length; the metric measures distance between points. Hilbert spaces generalize this framework to complete inner-product spaces. These extensions situate PCA within a broader mathematical landscape, but they do not justify transferring its properties to any normed space without proof.

    The philosophical discussion of matter and form

    The original reflection on Aristotle and Plato can be retained as a question about the connection between a system’s elements and the relations organizing them. This philosophical interpretation should nevertheless be distinguished from the mathematical definition of structure. Classifying objects “up to isomorphism” means grouping them according to a specified structural equivalence; it does not mean that they belong to any classification whatsoever or to none at all.

    From a materialist perspective, mathematical abstraction can be examined as an operation that selects properties of concrete phenomena to study particular relations. The critical requirement is to state what is abstracted and what is preserved. In PCA, certain properties are preserved exactly under a complete orthonormal change of basis; others are only approximated when coordinates are discarded. Philosophical analysis becomes more precise when it starts from that distinction.

    VI. Orthogonal projection and reconstruction

    Let a be a unit vector defining a line through the origin, and let z be a centered observation written as a column vector. Its coordinate along that line is t = aTz. Its projection in the original space is ẑ = aaTz. The difference e = z − ẑ is perpendicular to the projection direction.

    z = ẑ + e,   ⟨ẑ,e⟩ = 0
    ‖z‖² = ‖ẑ‖² + ‖e‖².

    The Pythagorean theorem decomposes squared length into a represented part and a residual part.

    For a fixed centered cloud, the sum of ‖z‖² over all observations is constant. Maximizing the sum of squared projected lengths therefore equals minimizing the sum of squared orthogonal residuals. This equivalence requires the same geometry, the same dataset, and comparison of subspaces of equal dimension.

    ∑i ‖zi‖² = ∑i ti² + ∑i ‖ei‖².

    The two formulations do not distinguish an “analytical” PCA from a “numerical” one. They are mathematically equivalent objectives that can be solved using different algorithms.

    Squaring is essential. Minimizing a sum of unsquared distances generally defines a different problem and may lead to another solution. Nor must components be computed through the Minkowski or Cauchy–Schwarz inequalities: these tools help prove properties of norms and inner products, but do not replace a specification of the optimization procedure.

    A projection is a linear transformation that can be defined without choosing coordinates. Once a basis is chosen, it has a precise matrix representation; there is no opposition between a “purely mathematical” projection and a “merely computational” matrix. For a subspace whose orthonormal columns are collected in Vk, the projection matrix is Pk = VkVkT.

    The original discussion of projective geometry is retained as context, with a qualification: the orthogonal projection of PCA belongs here to Euclidean geometry and linear algebra. It need not be conflated with the whole of projective geometry, whose historical development was collective and cannot simply be attributed to Lobachevsky.

    In several dimensions, the criterion is written as minimization of the Frobenius norm of the reconstruction residuals. Approximation by the first k components is optimal among approximations of rank at most k under this quadratic loss. “Optimal” always means optimal for a declared criterion, not for every possible scientific purpose.

    Tk = XcVk,   X̂c,k = TkVkT
    ‖Xc − X̂c,k‖F² = (n − 1) ∑j>k λj.

    This identity uses eigenvalues of the sample covariance with divisor n − 1. To reconstruct the original coordinates, add the mean; if variables were standardized, also reverse the scaling.

    VII. Slope, unit vectors, and components

    The original geometric explanation uses a line with slope 0.25, represented by a displacement of four units along the first axis and one along the second. Its direction vector is (4,1)T. Its length is √17 ≈ 4.1231; dividing its coordinates by that length yields a unit direction.

    a1 = (4,1)T/√17 ≈ (0.9701, 0.2425)T
    a2 = (−1,4)T/√17 ≈ (−0.2425, 0.9701)T.

    The vectors have unit norm and zero inner product. The rounded values 0.97 and 0.242 in the original material illustrate this normalization.

    The line can be written as y = 0.25x; its slope equals the derivative of this linear function. But the coefficients of a principal direction are not generally “derivatives of the phenomenon.” Here, slope is the ratio of the direction vector’s coordinates. The unit eigenvector contains a normalized representation of that direction and is determined by the covariance structure of the data.

    Normalizing (4,1) does not standardize each observation or transform the data into a normal distribution. Nor does it make a cloud’s mean zero. It produces a unit-length vector. With that direction, an observation’s first centered coordinate would be approximately t1 = 0.9701x1c + 0.2425x2c; the second would be t2 = −0.2425x1c + 0.9701x2c.

    Illustration and exact calculation

    The direction (4,1) belongs to the original graphical explanation. Axes computed exactly from the six-mouse table may differ from that teaching slope. For traceability, the new figure and numerical table in this edition use the SVD of the transcribed values, while the visual archive preserves the original construction.

    The Cauchy–Schwarz inequality guarantees that |⟨x,a⟩| ≤ ‖x‖‖a‖ and helps bound the length of a projection. The Minkowski inequality expresses the triangle inequality for Lp norms, with p ≥ 1. Pythagoras provides an exact identity when components are orthogonal in an inner-product space. These relationships serve different purposes; they are not three alternative algorithms for “finding” components.

    VIII. Eigenvalues, eigenvectors, and SVD

    For the centered matrix Xc, with observations in rows, sample covariance is S = XcTXc/(n − 1). This matrix is real, symmetric, and positive semidefinite. By the spectral theorem, it admits an orthonormal basis of eigenvectors collected as columns of V. Its eigenvalues are real and nonnegative.

    S = VΛVT,   VTV = I
    Λ = diag(λ1, …, λp),   λ1 ≥ ⋯ ≥ λp ≥ 0
    T = XcV,   Cov(T) = Λ.

    V describes the directions and T contains the scores. The diagonal matrix Λ contains the variances and expresses sample uncorrelatedness between different components.

    The singular value decomposition of the data matrix provides the same centered PCA solution. In reduced form, if the rank is r, we write Xc = UΣsVrT, with U of size n × r, Vr of size p × r, and diagonal Σs of size r × r. The subscript distinguishes singular values from population covariance.

    Xc = UΣsVrT,   Tr = UΣs
    λj = σj²/(n − 1),   σj = √((n − 1)λj).

    The singular values of Xc are square roots of the eigenvalues of XcᵀXc. To obtain component variances, the divisor n − 1 must be included.

    What distinguishes the two decompositions

    An SVD exists for any real rectangular or square matrix. Diagonalization by similarity requires a square matrix and is not always possible; for general matrices, eigenvalues may be complex and an eigenvector basis need not be orthogonal. These general qualifications, however, do not describe a difficulty of the covariance matrix in PCA: symmetry guarantees orthogonal diagonalization, and positive semidefiniteness guarantees nonnegative eigenvalues.

    In a general diagonalization A = PDP−1, the outer matrices are inverses. In an SVD A = UΣVT, the matrices U and V have different roles and are not generally each other’s inverses. If rectangular, they do not even admit a two-sided inverse. In their complete square versions they represent orthogonal transformations; a reduced SVD contains orthonormal bases for subspaces.

    The connection between eigenvectors and singular vectors is particularly direct in PCA, because the right singular vectors of Xc are eigenvectors of S. These are not two incompatible theories, but connected formulations for computing the same representation under the same centering and scaling conventions. [2] [4]

    Explained variance and discarded variance

    ρj = λj/∑ℓ=1p λℓ
    Ck = ∑j=1k ρj,   Rk = 1 − Ck.

    ρj is the proportion explained by component j; Ck is the cumulative proportion in the first k; Rk is the fraction of variance excluded from that representation. Positive total variance is assumed.

    The eigenvalue of a retained component represents variation preserved along that axis, not “lost information.” Reconstruction loss is related to the sum of discarded eigenvalues. This quadratic relationship does not turn variance into a universal measure of meaning, causality, or information in Shannon’s sense.

    IX. Centering, standardization, and matrix choice

    Centering subtracts the mean. Standardization by z-scores subtracts the mean and divides by each variable’s standard deviation. Vector normalization, in the sense used above, divides by a norm. These are different operations, although all may informally be described as changes of scale.

    zij = (xij − x̄j)/sj,   sj > 0
    R = ZTZ/(n − 1).

    If sj is computed using divisor n − 1, the sample covariance of Z is the original variables’ correlation matrix R. Constant columns require prior treatment.

    The z transformation produces mean zero and unit variance under the convention used. It does not impose normality: a skewed distribution remains skewed after an affine transformation with positive scale. Descriptive PCA can be computed without assuming multivariate normality; certain tests, intervals, or control limits do require additional assumptions.

    Two legitimate decisions with different objectives
    Basis of analysisInterpretationSubstantive decision
    CovariancesCentered variables are analyzed in their measurement scales.May be appropriate when units are comparable and variance differences are meaningful.
    CorrelationsThe covariance of standardized variables is analyzed.Gives each variable the same initial variance; often relevant with heterogeneous units, although it may also amplify unreliable variables.

    A variable measured in thousands may dominate one measured in fractions if changing units increases its variance. But the magnitude of a variable’s mean does not, by itself, determine its variance. A scaling decision requires examination of dispersion, units, and purpose, rather than merely large or small absolute values.

    Standardization changes the geometric problem by weighting the original coordinates differently. It should not be presented as a neutral operation that always improves analysis. It also matters whether standard deviation uses n or n − 1. In the same sample, consistently applying either divisor to all variables introduces only a common factor between the two standardizations; directions and explained-variance proportions coincide, but score magnitudes and variances change.

    The connection to the same author’s essay on rescaling remains relevant. The web edition retains that link and emphasizes these distinctions so that both texts support each other without reproducing the confusion between standardization and transformation to a normal distribution. [9]

    X. Orthogonality, uncorrelatedness, and rank

    Two nonzero vectors with zero inner product are orthogonal and therefore linearly independent. The converse is false: two vectors may be linearly independent without being perpendicular. “Orthogonal” and “perpendicular” do not designate properties reserved for three and two dimensions respectively; orthogonality applies in any dimension within an inner-product framework.

    For the coefficients of distinct components, ajTak = 0. For the principal variables, covariance is ajTΣak = 0. These identities are connected by the eigenvalue equation. They must not be confused with statistical independence, which requires factorization of the joint distribution.

    A counterexample to independence

    If U is uniformly distributed on [−1,1], the variables U and U² have zero covariance by symmetry, but U² is completely determined by U. The absence of linear correlation does not eliminate dependence. Under a joint normal distribution, however, uncorrelatedness between components does imply independence.

    The number of components with positive variance equals the rank of the centered matrix. For n observations and p variables, that rank cannot exceed min(n − 1, p). A complete covariance decomposition can provide p eigenvectors, but some eigenvalues will be zero when there is linear dependence or insufficient sample rank.

    Retaining a complete orthonormal basis of ℝp preserves Euclidean distances under the coordinate change. Retaining all positive-variance components also allows exact reconstruction of the observed centered cloud. With fewer components, the approximation generally leaves residuals; these vanish only when the data already lie in the retained subspace.

    Axis order is fixed by eigenvalues, except for ties; sign orientation is arbitrary. Simultaneously replacing aj with −aj and reversing its scores describes the same solution. Isolated signs should therefore not be compared across programs without first aligning conventions.

    XI. How many components should be retained?

    Component retention is a methodological decision. It depends on the objective, measurement quality, and cost of discarding variation. A plane may be useful for visualization and insufficient for another task. The original document brings together Kaiser, Cattell, MAP, Bartlett, Horn, and Lawley; all are retained here, with their respective scopes clarified. [15]

    Cumulative variance and reconstruction error

    One may choose the smallest k reaching a previously justified variance proportion. Thresholds such as 80%, 90%, or 95% are conventional decisions, not guarantees of scientific sufficiency. In prediction, selection should be assessed against out-of-sample performance; in reconstruction, it can be justified through a tolerable error in the relevant units.

    Kaiser (1960): eigenvalues greater than one

    In correlation-based PCA, each standardized variable contributes variance one, and the mean of the p eigenvalues is one. The rule retains components with λ > 1, meaning those explaining more variation than an average standardized variable. This rationale does not automatically transfer to a covariance matrix with arbitrary units. The rule may retain too many or too few components and should not operate as an automatic decision.

    Cattell (1966): the scree plot

    The scree test looks for a change in slope between the first eigenvalues and a flatter tail. It offers a visual reading of diminishing returns. An “elbow” may be ambiguous; different readers may identify different cutoffs, and some spectra show no clear bend. The original suggestion to examine a baseline along the tail can be retained as a graphical aid, while making its interpretive element explicit.

    Velicer (1976): minimum average partial (MAP)

    The MAP criterion examines residual correlations among variables after successively removing components and normalizing residuals to obtain partial correlations. It seeks a minimum in the average of squared off-diagonal values. The intuition is to stop when removing further components no longer usefully reduces residual association and begins extracting specific structure or noise.

    MAP should not be confused with choosing a percentage of variance or with a universal test of latent causality. The original source’s recommendation to have several high-weight variables can be useful for stable interpretation, but it is not a mathematical identity defining the criterion. Accessible implementations exist, including those documented in psych, so the practical claim that the method is difficult to access needs updating. [24]

    Bartlett (1950, 1951): testing remaining eigenvalues

    The family of tests mentioned in the original evaluates hypotheses about the structure of remaining eigenvalues, such as their equality, under specific distributional assumptions and approximations. It is not the same as the familiar sphericity test applied to the complete correlation matrix. When used sequentially, the exact hypothesis, degrees of freedom, sample size, and stopping criterion must be stated.

    Failure to reject a hypothesis does not prove it true: it may also reflect low power. Nor does every statistically significant result require adding a useful dimension. Interpretation must distinguish testing population structure from making a practical decision about representation.

    Horn (1965): parallel analysis

    Parallel analysis compares observed eigenvalues with those obtained under a random reference of the same size n × p. Simulation or permutation reproduces a setting of absent association according to an explicit mechanism. Components whose eigenvalues exceed the corresponding reference values are retained. It is necessary to state whether comparison uses means or a quantile, such as the 95th percentile, and how many replicates are generated.

    The logic addresses a limitation of Kaiser: even when the population correlation matrix is the identity, sample eigenvalues fluctuate and will not all equal one exactly. One must also specify whether parallel analysis concerns components or common factors, because the matrices and comparisons are not identical. [25]

    Lawley (1940): likelihood ratios and factor analysis

    Likelihood-ratio procedures associated with maximum-likelihood factor analysis assess whether a given number of factors adequately reproduces a covariance or correlation structure under a probabilistic model. They are retained here as part of the original overview, but are not presented as an automatic property of descriptive PCA.

    A common-factor model distinguishes shared variation from specific variation; PCA decomposes the total variation in the analyzed variables. Confusing components with factors may lead to criteria or interpretations being applied outside their framework. When software offers both procedures, it is important to check which has actually been run.

    A documented decision

    Report the primary criterion, compare it with another of a different kind, examine stability, and justify discrepancies. No rule replaces scientific judgment. If the objective changes from visualization to prediction or a latent-factor model, the appropriate choice of dimensions may also change.

    XII. Interpreting the financial example

    The document uses Minitab output based on eight variables: income, education, age, residence, employment, savings, debt, and credit cards. The eigenvalue table and complete coefficient matrix from that output are reproduced below. The labels require checking how each variable was measured or coded; “residence,” for example, does not by itself identify whether the measurement concerns duration, housing status, or something else. [6]

    Eigenvalues, proportions, and cumulative variance · Minitab financial example
    PCEigenvalueProportionCumulative
    PC13.54760.4430.443
    PC22.13200.2660.710
    PC31.04470.1310.841
    PC40.53150.0660.907
    PC50.41120.0510.958
    PC60.16650.0210.979
    PC70.12540.0160.995
    PC80.04110.0051.000
    Complete coefficient matrix · rounding from the original output
    VariablePC1PC2PC3PC4PC5PC6PC7PC8
    Income0.3140.145-0.676-0.347-0.2410.4940.018-0.030
    Education0.2370.444-0.4010.2400.622-0.3570.1030.057
    Age0.484-0.135-0.004-0.212-0.175-0.487-0.657-0.052
    Residence0.466-0.2770.0910.116-0.035-0.0850.487-0.662
    Employment0.459-0.3040.122-0.017-0.014-0.0230.3680.739
    Savings0.4040.2190.3660.4360.1430.568-0.348-0.017
    Debt-0.067-0.585-0.078-0.2810.6810.245-0.196-0.075
    Credit cards-0.123-0.452-0.4680.703-0.195-0.022-0.1580.058

    The first three components have eigenvalues greater than one and account for approximately 84.1% of variation. The first two account for around 71.0%. Representing only PC1 and PC2 therefore leaves about 29% of total variation out of view: a two-dimensional plot is informative, but does not exhaust a three-component solution.

    The relatively large positive coefficients for age, residence, employment, and savings in PC1 motivate the interpretation of long-term financial stability presented in the original. Among other contributions, PC2 contrasts education and savings with debt and credit cards. Under the chosen sign convention, the latter have negative coefficients; reversing the axis preserves the same structure. PC3 captures a different contrast, with substantial negative coefficients for income, education, and credit cards.

    Names such as “stability,” “credit history,” or “academic and income qualifications” are interpretive summaries, not directly observed variables or automatically validated constructs. Supporting them requires examining all loadings, coding, and domain knowledge. A component does not become a valid indicator of risk, solvency, or behavior merely by receiving a plausible label.

    How to calculate a score

    Because the example uses a correlation matrix, its coefficients are applied to standardized variables. For a particular observation, the first score is obtained as follows:

    t1 = 0.314zIncome + 0.237zEducation
    + 0.484zAge + 0.466zResidence
    + 0.459zEmployment + 0.404zSavings
    − 0.067zDebt − 0.123zCredit cards.

    The formula uses coefficients published to three decimal places; small differences from the program’s higher-precision internal calculation may occur.

    As an additional teaching check, a hypothetical observation one standard deviation above the mean in age, residence, employment, and savings, and exactly at the mean of the other variables, would have an approximate score of 0.484 + 0.466 + 0.459 + 0.404 = 1.813. This is not an additional observation from the Minitab dataset: it illustrates substituting values into the linear combination.

    Scores belong to observations; coefficients belong to directions. They are not entries of the same matrix under different names. If directions occupy the columns of V, the transformation for all observations is T = ZV. The principal space is the subspace spanned by retained directions; it should not be loosely identified with a particular matrix representing it.

    XIII. Mahalanobis distance

    An observation may be unremarkable in each variable separately yet unusual because of the combination of its values. If two variables usually increase together, a point high on one and low on the other may depart from the joint pattern even when neither marginal value is extreme. This is the intuition behind the original example.

    DM²(x) = (x − μ)TΣ−1(x − μ)
    DM(x) = √DM²(x).

    The formula assumes invertible covariance. For sample data, μ and Σ are replaced by estimates, and threshold interpretation depends on how those estimates were obtained.

    Mahalanobis distance weights departure from the center while accounting for scale and covariation. In principal coordinates, if all eigenvalues are positive, squared distance is the sum of squared scores divided by their variances.

    DM²(x) = ∑j=1p tj²/λj.

    A modest deviation along a very low-variance direction may be more unusual than a larger deviation along a high-variance direction.

    If covariance is singular, the ordinary inverse does not exist. A pseudoinverse, regularization, or a subspace model may be considered, but each option changes which departures the diagnostic detects. The pseudoinverse ignores null-space directions; consequently, a subspace-restricted diagnostic should be complemented by residual inspection when points outside that subspace are of interest.

    Classical distance uses mean and covariance estimates that can themselves be affected by the outliers one intends to detect. It is not universally “more powerful” under every form of contamination or distribution. Its conceptual advantage over marginal inspection is incorporating the multivariate pattern, but performance depends on the estimator and model.

    With known population parameters and multivariate normality, squared distance follows a chi-squared distribution with degrees of freedom equal to dimensionality. When parameters are estimated from the same sample, appropriate limits may require different corrections. A cutoff line should not be applied without stating its rationale.

    XIV. The scree plot

    A scree plot orders eigenvalues from largest to smallest and plots them against component number. It represents the spectrum of the covariance or correlation matrix being analyzed. The vertical axis should specify whether it shows eigenvalues, variance proportions, or a transformation of those quantities.

    Eigenvalues of the financial example and the Kaiser reference.
    Figure from the revised edition. Eigenvalues from the original financial table; the dashed line marks Kaiser’s reference for correlation-based analysis.

    The financial example shows a substantial decline across the first components and a later tail of smaller contributions. The original interpretation of retaining three components is consistent with Kaiser and a cumulative proportion near 84.1%. It does not show that the remaining five components have no possible relevance: they may contain contrasts important to a particular objective.

    An approximately uniform spectrum suggests that no direction concentrates much variation; in that case, strong reduction loses a substantial fraction. A dominant eigenvalue suggests a direction of common variation, but does not by itself establish a single cause. Spectrum shape also depends on scaling and noise.

    XV. The score plot

    A score plot represents observations in the coordinates of two or more components. In the PC1–PC2 plane, each point is a unit of study, not an original variable. Axes should show the proportion of variation they represent, allowing readers to see how much of the cloud is visible.

    The plot is examined for groupings, gradients, separations, extreme positions, and observations near the center. The original figure identifies a point in a lower corner as a candidate for investigation. Proximity to the edge of the plane does not automatically establish an error, a new group, or a statistically significant outlier.

    Distribution around the origin follows from centering the scores. It is not a test of normality and does not imply a circular cloud. Each component has its own variance; unequal graphical scales can visually alter angles and distances. When the figure’s geometry matters, axis scales should be stated and an appropriate aspect ratio preferably maintained.

    If two points are separated in the plane, they also differ in the full space. If they appear close, they may still differ in unrepresented components. Likewise, a projected grouping may be useful for exploration without establishing stable clusters, natural categories, or population differences.

    Coloring observations by known experimental labels may help interpret the representation, but those labels do not enter the ordinary PCA calculation. If the explicit objective is to separate known groups, an appropriate supervised technique should be considered and validated out of sample.

    XVI. The loading or “influence” plot

    The document’s “influence” plot corresponds to a loading plot: it represents coordinates associated with variables on two components. Accompanying the traditional label with “loading plot” helps avoid confusion with observation-influence diagnostics in regression.

    The term loading has no universal convention. Some programs display the unit eigenvector coefficients; others display coefficients multiplied by the square root of the eigenvalue, or variable–component correlations. The representation type must be stated before comparing magnitudes, interpreting lengths, or applying thresholds.

    Corr(xℓ, PCj) = √λj aℓj/sℓ
    For standardized variables: Corr(zℓ, PCj) = √λj aℓj.

    These identities use an unwhitened component and consistent covariance and scaling conventions.

    A unit-eigenvector coefficient near zero indicates a small contribution to that particular linear combination; it does not declare the variable unimportant throughout the analysis. The variable may contribute to other components or to a question not captured by principal variance. Eigenvector coefficients lie between −1 and 1, but in many dimensions all may be far smaller than one without the component being irrelevant.

    In the financial example, age, residence, and employment point toward PC1; debt and credit cards have substantial weight in the negative PC2 direction. Interpretation concerns the joint pattern of signs and magnitudes, including smaller loadings when they contribute a relevant contrast. Interpretive consistency should be examined under reasonable changes to observations, scaling, or variable selection.

    XVII. The biplot

    A biplot brings together markers for observations and variables in one figure. It is grounded in a factorization of the data matrix’s reduced-rank approximation, not an arbitrary overlay of two plots with different units. Starting from X̂c,k = UkΣs,kVkT, singular values can be distributed between the two sets of markers in different ways.

    G = UkΣs,kα,   H = VkΣs,k1−α
    X̂c,k = GHT.

    The choice of α changes the scaling of observations and variables. Consult the software’s convention before interpreting distances or angles.

    Observation positions help identify patterns; variable vectors or markers help relate them to the original measurements. Under appropriate scaling, inner products allow approximate reconstruction of centered values; certain variable orientations may approximate correlations. These readings depend on normalization and approximation quality in the displayed axes.

    The financial figure retains the original readings of PC1 and PC2 and the suggestion to investigate the isolated point. The additional clarification is that PC3 also contributes appreciably and is absent from the plane. Apparent closeness between an arrow and a point should not be interpreted as an ordinary distance between objects of the same kind.

    A biplot should report the input matrix, preprocessing, displayed components, represented variance, and marker scaling. Without this information, a visually convincing figure can support incompatible interpretations.

    XVIII. The outlier plot

    The original plot shows a Mahalanobis distance for each observation and a detection reference. No point exceeds the displayed limit. This specific result is retained without turning it into proof that the dataset contains no possible anomaly: the diagnostic concerns a particular metric and threshold.

    An observation above a reference line is a signal to investigate. It may reflect a recording error, measurement failure, a unit from another population, an exceptional condition, or a real and scientifically important event. Automatic removal can erase precisely the variation requiring explanation.

    It is useful to distinguish two positions: far from the center within the retained subspace, and far from the subspace itself. The first can be examined through a weighted score distance; the second through a reconstruction residual, frequently summarized by a residual sum of squares.

    Dk²(x) = ∑j=1k tj²/λj,   Q(x) = ‖xc − VkVkTxc‖².

    A limit for Dk² does not replace a limit for Q. Their reference distributions and corrections depend on the model and application.

    A defensible practice is to verify data provenance, correct demonstrated errors, document decisions, and compare results with and without disputed cases when appropriate. When many observations are examined, it also matters that a series of marginal thresholds can accumulate false alarms.

    XIX. Numerical computation and NIPALS

    The variational formulation defines the solution sought; the algorithm determines how it is computed. PCA can be obtained through eigendecomposition of covariance, SVD of the prepared matrix, iterative methods, or approximations designed for large matrices. Gradient descent is a possibility for certain formulations, not the mandatory mechanism “orchestrating” every PCA.

    Computational approaches and implementation decisions
    ProcedureIdeaMain consideration
    EigendecompositionDiagonalize S or R.Forming XTX can amplify conditioning problems; useful when the variable matrix has manageable size.
    Direct SVDDecompose the centered or standardized matrix.Avoids explicitly forming covariance and directly connects directions, scores, and reconstruction.
    Truncated iterative methodsApproximate only the leading components.Require tolerances and convergence checks; useful when few axes are wanted.
    Randomized SVDApproximate the dominant subspace through projections and refinement.Accuracy depends on the spectrum and parameters such as oversampling and iterations; fix the seed when repeatability is required.
    NIPALSExtract components successively through alternating updates and deflation.Implementations may incorporate missing-value handling; convergence and assumptions must be stated.
    Incremental PCAUpdate an approximation in batches.Reduces memory requirements, but algorithmic and batch details affect the approximation.

    NIPALS, step by step

    NIPALS stands for Non-linear Iterative Partial Least Squares. The algorithm’s historical name does not automatically make the resulting representation a nonlinear PCA. In its PCA application, it successively seeks rank-one approximations to the residual matrix. The original discussion remains useful for understanding loadings and scores. [4]

    1. Prepare the data and define an initial residual matrix E = Xc, or E = Z if variables were standardized.
    2. Choose a nonzero column of E to initialize a score vector t.
    3. Update the direction using p = ETt/(tTt), and normalize p to unit length.
    4. Update scores using t = Ep and repeat until a declared tolerance is met.
    5. Store t and p, remove their contribution using E ← E − tpT, and continue with another component if appropriate.

    This sequence describes the complete-data case. Some implementations adjust products and updates over observed values to handle missing entries. Replacing missing values with zeros is not sufficient, nor can every missingness pattern be assumed harmless. Identifiability, the amount of available information, and the missingness mechanism affect the result.

    Sequential extraction allows computation to stop once enough components have been obtained. Deflation and rounding may also accumulate numerical errors; orthogonality, convergence, and reconstruction residuals should therefore be monitored. The claim that “most programs use NIPALS” should be treated as a historical observation from the cited source, not a general property of current software.

    A reproducible report states the algorithm actually used. Saying only that “PCA was performed in Python” or “in R” does not identify the input matrix, centering, scaling, missing-value treatment, or retention criterion. Software facilitates computation but does not itself resolve design decisions.

    XX. PCA and linear regression

    The original comparison with linear regression clarifies two meanings of “best-fitting line.” In ordinary regression of y on x, coefficients minimize errors in the response variable, measured vertically in the usual plot. In two-dimensional PCA, the line minimizes the sum of squared orthogonal distances, treating the coordinates jointly under the chosen metric.

    Regression of y on x: minβ₀,β₁ ∑i (yi − β₀ − β₁xi)²
    Rank-one PCA: min‖a‖=1 ∑i ‖zi − aaTzi‖².

    The first formulation selects a response. The second approximates a centered cloud without designating a dependent variable.

    The distinction is therefore not that regression “lacks any orthogonality.” In least squares, the residual vector is orthogonal to the space spanned by the design columns under the usual conditions. This algebraic property is not equivalent to measuring geometrically perpendicular distances from each point to a line in variable space.

    PCA can be related to orthogonal regression or total least squares, but interpreting errors in all variables requires assumptions about their scales and measurement precision. Symmetry of the criterion does not make units irrelevant: changing one axis’s scale can change the principal line.

    Principal component regression also exists, using scores as predictors. Selecting components solely by their variance can discard a low-variation direction informative about the response. The number of components should be chosen with the predictive task and independent validation in view.

    Geometric association does not establish causality either. The distinction between description, prediction, and explanation must remain explicit even when both techniques share algebraic tools or representations through lines and planes.

    XXI. Three genes and higher dimensions

    The original example adds a third gene while retaining the same six mice. The first three-dimensional table contains the following data. The third coordinate shows how a structure barely visible in one plane may become clearer when another variable is examined.

    Three-dimensional extension of the gene-expression example
    MouseGene 1Gene 2Gene 3
    110612
    21149
    38510
    4332.5
    522.81.3
    6112

    Each unit now occupies a point in ℝ³. PC1 identifies the direction of greatest variation; PC2 is chosen in its orthogonal complement, and PC3 completes an orthonormal basis. The plane formed by PC1 and PC2 provides the best two-dimensional approximation under the Euclidean quadratic criterion applied to the prepared data.

    Mice 1, 2, and 3 have higher third-gene values than mice 4, 5, and 6. This pattern may contribute to separation in principal coordinates. But a component combines all three variables: it need not coincide with a particular gene’s axis. Examining its coefficients describes how much each measurement contributes to the resulting direction.

    The final Starmer figures included in the document also show illustrative variants in which third-gene values change. These variants are preserved in the visual archive. Percentages labeled in them—for example, 91% and 4%, or 79%, 15%, and 6%—belong to the teaching scenes in which they appear; they should not be transferred to another table or preprocessing scheme as universal results.

    To avoid that mixture, the numerical results added here are recalculated exclusively from the transcribed tables using covariance-based PCA: centering without standardization. The code below makes it possible to check how the results differ if correlations are used instead.

    Recalculated results · centered data without standardization
    DataPCEigenvalueVariance (%)Cumulative (%)
    Two genesPC121.28401296.336896.3368
    Two genesPC20.8093213.6632100.0000
    Three genesPC142.45597095.750995.7509
    Three genesPC21.3576643.061998.8129
    Three genesPC30.5263661.1871100.0000
    Recalculated scores · two genes, covariance-based PCA
    MousePC1PC2
    14.719998-0.826949
    24.9888611.392896
    32.500152-0.558086
    4-2.881249-0.356784
    5-3.890244-0.504866
    6-5.4375180.853790

    Sign convention: the first coefficient of each direction is nonnegative. For two genes, a₁ ≈ (0.941711, 0.336424)ᵀ and a₂ ≈ (0.336424, −0.941711)ᵀ. Reversing a direction and its scores preserves the result.

    The same argument generalizes to p genes, even when the original space cannot be drawn. A table with thousands of variables and few observations admits a PCA calculation, but its sample rank is limited by n − 1. The existence of computable components does not guarantee that estimated directions are stable or faithfully reproduce those of a population.

    Observed separations may be consistent with biological differences, but also with technical or preparation differences. Identifying gene 3 as “responsible” for a grouping requires distinguishing geometric contribution from causal mechanism. PCA helps discover questions and hypotheses; design and validation determine which explanations are supported.

    XXII. Scientific scope and multidimensional interpretation

    The interest of PCA extends beyond a data-reduction recipe. It allows us to study how multiple measurements can be organized through relations that do not coincide with isolated variables. This move from original coordinates to new combinations can be philosophically fruitful, provided that a statistical construction is not turned into a demonstrated ontological essence.

    Relations, totality, and mediation

    From a materialist reading, variables are measurements produced through concrete practices, and data express aspects of natural or social processes. Analysis of their covariation may reveal regularities that marginal inspection misses. Yet the totality of a phenomenon is not exhausted by a covariance matrix: nonlinear relationships, temporality, heterogeneity, historical conditions, and interaction mechanisms may also matter.

    The idea of “essential relations” should be treated as an interpretive hypothesis requiring investigation. An eigenvector summarizes a mathematical structure of variation; attributing to it the status of a fundamental relation of the system requires connecting that structure with substantive theory and independent evidence. This separation strengthens philosophical discussion by making its mediations explicit.

    Six dimensions of assessment

    Assessing the method at complementary levels
    DimensionGuiding question
    MathematicalWhat objective is optimized, under which constraints and metric?
    GeometricAre coordinates being changed or directions discarded? Which distances are preserved?
    StatisticalDo the sample, measurements, and stability support the observed regularities?
    ComputationalWhich algorithm, tolerances, and data treatment produce the solution?
    SubstantiveDoes the component interpretation make sense in biology, economics, or the domain studied?
    EpistemologicalIs the aim description, prediction, or explanation? Which inferential step needs additional evidence?

    Applications and limitations

    In biology, PCA can explore joint patterns of expression or morphology. In economics, it can summarize related series or indicators. In industrial processes, it can help monitor multivariate behavior. In images, it can approximate variation through a lower-dimensional basis. In every case, usefulness depends on the objective, data preparation, and component interpretation.

    Major limitations include sensitivity to extreme observations, dependence on units and scaling, the inability of a linear model to fully describe curved structures, and the absence of an explicit class-separation objective. Ordinary PCA also does not automatically distinguish signal from noise or identify causal variables or latent constructs on its own.

    Validation may include sensitivity to preprocessing, resampling to examine stability, comparisons between subsamples, residual inspection, and out-of-sample evaluation. When eigenvalues are close, individual directions may change substantially under small perturbations while the joint subspace remains more stable. In that case, evaluate the subspace rather than attaching excessive significance to the name of an isolated axis.

    Finally, a low-dimensional representation should not be used to naturalize classifications of people or groups. The financial example illustrates a statistical operation; any decision-making use requires validation appropriate to the context, the meaning of the variables, and the consequences of the decisions.

    Scope of the result

    PCA identifies the directions of greatest linear variation in a chosen representation. Scientific knowledge additionally requires explaining why that representation is relevant, which relations it can capture, and what evidence supports interpretation of its patterns.

    XXIII. Reproducible implementation in Python and R

    Reproducibility requires preserving data, matrix orientation, transformations, algorithm, and conventions. The following examples recalculate the gene-table results. Python uses NumPy and direct SVD; R uses prcomp. Neither assumes group labels or uses screenshot percentages as inputs.

    Both distinguish centering without scaling from centering with sample standardization. The covariance divisor is n − 1. Component signs may differ between implementations; directions should be aligned before comparing coordinates. With spectral ties, compare the subspace rather than necessarily comparing individual columns.

    Python: computation and verification

    """PCA for the six-mouse example. Requires Python and NumPy."""
    import numpy as np
    
    X = np.array([
        [10, 6], [11, 4], [8, 5],
        [3, 3], [2, 2.8], [1, 1]
    ], dtype=float)
    X3 = np.column_stack((X, [12, 9, 10, 2.5, 1.3, 2]))
    
    def pca_svd(data, standardize=False):
        """Rows: observations. Sample scaling uses ddof=1."""
        x = np.asarray(data, dtype=float)
        if x.ndim != 2 or x.shape[0] < 2:
            raise ValueError("At least two observations are required.")
        if not np.isfinite(x).all():
            raise ValueError("Resolve missing/nonfinite values before PCA.")
        mean = x.mean(axis=0)
        scale = x.std(axis=0, ddof=1) if standardize else np.ones(x.shape[1])
        if np.any(scale == 0):
            raise ValueError("Do not standardize constant columns.")
        z = (x - mean) / scale
        _, singular, vt = np.linalg.svd(z, full_matrices=False)
        directions = vt.T.copy()
        # Reproducible sign convention: nonnegative first coefficient.
        signs = np.where(directions[0, :] < 0, -1.0, 1.0)
        directions *= signs
        scores = z @ directions
        eigenvalues = singular**2 / (x.shape[0] - 1)
        total = eigenvalues.sum()
        if total <= 0:
            raise ValueError("Total variation must be positive.")
        proportions = eigenvalues / total
        return dict(z=z, mean=mean, scale=scale,
                    directions=directions, scores=scores,
                    eigenvalues=eigenvalues, proportions=proportions)
    
    if __name__ == "__main__":
        np.set_printoptions(precision=6, suppress=True)
        for label, data in [("Two genes", X), ("Three genes", X3)]:
            for scaling in (False, True):
                r = pca_svd(data, standardize=scaling)
                v, t, lam = r["directions"], r["scores"], r["eigenvalues"]
                print(label, "correlations" if scaling else "covariances")
                print("Eigenvalues:", lam)
                print("Explained variance (%):", 100 * r["proportions"])
                print("Directions (columns):\n", v)
                print("Scores:\n", t)
                # Independent properties checking the published result.
                assert np.allclose(v.T @ v, np.eye(v.shape[1]))
                assert np.allclose(np.cov(t, rowvar=False), np.diag(lam))
                assert np.allclose(t @ v.T, r["z"])
                assert np.isclose(lam.sum(), np.var(r["z"], axis=0, ddof=1).sum())
                for k in range(1, v.shape[1] + 1):
                    approximation = t[:, :k] @ v[:, :k].T
                    error = np.sum((r["z"] - approximation)**2)
                    assert np.isclose(error, (data.shape[0] - 1) * lam[k:].sum())
        # Uncorrelatedness and independence are different:
        # if U is uniform on [-1,1], Cov(U,U²)=0 by symmetry,
        # although U² is determined by U; no simulated test is needed.
    

    The checks verify orthonormal directions, diagonal score covariance, full-component reconstruction, and agreement between discarded variation and squared error. These identities detect transposition or scaling errors that a plausible figure may conceal.

    R: the same sample convention

    # PCA of the same data. Requires base R.
    X <- matrix(c(10,6, 11,4, 8,5, 3,3, 2,2.8, 1,1),
                ncol = 2, byrow = TRUE)
    colnames(X) <- c("Gene1", "Gene2")
    rownames(X) <- paste0("Mouse", 1:6)
    X3 <- cbind(X, Gene3 = c(12, 9, 10, 2.5, 1.3, 2))
    for (data in list(X, X3)) {
      for (scaling in c(FALSE, TRUE)) {
        fit <- prcomp(data, center = TRUE, scale. = scaling)
        print(fit$sdev^2)                  # sample eigenvalues
        print(100 * fit$sdev^2 / sum(fit$sdev^2))
        print(fit$rotation)                # directions
        print(fit$x)                       # scores
        reconstruction <- fit$x %*% t(fit$rotation)
        stopifnot(max(abs(reconstruction -
                          scale(data, center=TRUE, scale=scaling))) < 1e-8)
      }
    }
    # Signs may differ from Python without changing the solution.
    # For parallel analysis, install psych if desired:
    # set.seed(2026)
    # psych::fa.parallel(X3, fa="pc", n.iter=1000, quant=.95)
    # With six observations, this is an illustration, not an empirical recommendation.
    

    The parallel-analysis example remains commented out because it requires the additional psych package. The function explicitly specifies components through fa="pc". A teaching sample of six units should not support recommendations about stable empirical groupings or population dimensionality.

    Applying the model to new observations

    A new observation is transformed using the means, scales, and directions fitted on training data. They are not recalculated using the test set. If PCA belongs to a predictive pipeline, its fit and all learned preprocessing must occur within each training partition of cross-validation. Otherwise information leaks from the evaluation set.

    Tnew = ((Xnew − 1x̄trainingT)Dtraining−1)Vk.

    D contains the standard deviations used when fitting the model. Without standardization, D is the identity.

    In implementations such as sklearn.decomposition.PCA, centering and scaling are separate operations: PCA should not be assumed to standardize automatically. If StandardScaler is used, its default variance convention employs divisor n; state this when comparing exact magnitudes with R or the code above. Whitening is another transformation: it divides principal coordinates by a measure of their dispersion and changes output variances.

    XXIV. Complete archive of the 51 original figures

    This archive preserves every graphical element extracted from the PDF’s 49 pages, in order of appearance and without cropping their content. It includes Pearson’s historical figure, the transformation table, Starmer’s sequence, Dunn’s diagrams, and the results and plots from Minitab and Universidad Carlos III de Madrid. A visual filter integrates the color presentation into the article; each figure’s link opens the original PDF page in color.

    Reading the historical material

    The screenshots retain the labels and conventions of the original document, including the source languages. Where they call a sum of squares an “eigenvalue,” distinguish the eigenvalue of the cross-product matrix from a component’s variance. Where dividing by the hypotenuse is described as “standardizing,” that step concerns vector normalization. The clarifications in the main text govern interpretation of the notation. Percentages from different scenes should not be combined.

    A. Pearson and geometric transformations · figures 1–5
    Original figure 1. Orthogonal approximation line in Pearson’s historical illustration.
    Original figure 01 · PDF, page 3. Orthogonal approximation line in Pearson’s historical illustration. Source: Pearson (1901); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 2. Expression of gene 1 in the six mice and calculation of its mean.
    Original figure 02 · PDF, page 6. Expression of gene 1 in the six mice and calculation of its mean. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 3. Expression of gene 2 and calculation of its mean.
    Original figure 03 · PDF, page 6. Expression of gene 2 and calculation of its mean. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 4. Cloud center obtained from the two means.
    Original figure 04 · PDF, page 7. Cloud center obtained from the two means. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 5. Original table of geometric transformations and their characteristics.
    Original figure 05 · PDF, page 7. Original table of geometric transformations and their characteristics. Source: Weisstein, Transformation (2021); reproduced from Isadore Nabi’s document.
    View the original page in color
    B. Centering, fitting, and projection · figures 6–15
    Original figure 6. Translation of the cloud to the coordinate origin.
    Original figure 06 · PDF, page 8. Translation of the cloud to the coordinate origin. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 7. Preservation of relative positions after centering.
    Original figure 07 · PDF, page 9. Preservation of relative positions after centering. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 8. First candidate line through the origin.
    Original figure 08 · PDF, page 10. First candidate line through the origin. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 9. Rotation of the candidate line against the centered cloud.
    Original figure 09 · PDF, page 11. Rotation of the candidate line against the centered cloud. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 10. Illustration of a projection between planes.
    Original figure 10 · PDF, page 11. Illustration of a projection between planes. Source: Weisstein, Projection (2021); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 11. Two fitting criteria expressed through projections.
    Original figure 11 · PDF, page 12. Two fitting criteria expressed through projections. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 12. Perpendicular residual and projected distance from the origin.
    Original figure 12 · PDF, page 13. Perpendicular residual and projected distance from the origin. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 13. Right triangle connecting the observation, projection, and residual.
    Original figure 13 · PDF, page 14. Right triangle connecting the observation, projection, and residual. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 14. Pythagorean identity: constant total and complementary parts.
    Original figure 14 · PDF, page 14. Pythagorean identity: constant total and complementary parts. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 15. Decomposition of a vector into a score and a residual.
    Original figure 15 · PDF, page 15. Decomposition of a vector into a score and a residual. Source: Dunn (2021); reproduced from Isadore Nabi’s document.
    View the original page in color
    C. Sums of squares and principal direction · figures 16–25
    Original figure 16. Choice of a candidate line for measuring fit.
    Original figure 16 · PDF, page 16. Choice of a candidate line for measuring fit. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 17. Projection of an observation onto the candidate direction.
    Original figure 17 · PDF, page 16. Projection of an observation onto the candidate direction. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 18. Distance from a projection to the origin.
    Original figure 18 · PDF, page 17. Distance from a projection to the origin. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 19. Sum of squared projections for all six mice.
    Original figure 19 · PDF, page 18. Sum of squared projections for all six mice. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 20. Comparison of directions and the maximum sum-of-squares criterion.
    Original figure 20 · PDF, page 18. Comparison of directions and the maximum sum-of-squares criterion. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 21. Direction vector with coordinates 4 and 1 and approximate length 4.12.
    Original figure 21 · PDF, page 20. Direction vector with coordinates 4 and 1 and approximate length 4.12. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 22. Normalization of the direction by dividing by its length.
    Original figure 22 · PDF, page 22. Normalization of the direction by dividing by its length. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 23. Normalized coefficients for the illustrative gene combination.
    Original figure 23 · PDF, page 22. Normalized coefficients for the illustrative gene combination. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 24. Singular vector or eigenvector: principal-direction terminology.
    Original figure 24 · PDF, page 23. Singular vector or eigenvector: principal-direction terminology. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 25. Sum of squares and singular value: the original screenshot’s convention.
    Original figure 25 · PDF, page 24. Sum of squares and singular value: the original screenshot’s convention. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    D. Second component and coordinates · figures 26–35
    Original figure 26. Second direction perpendicular to PC1 in two dimensions.
    Original figure 26 · PDF, page 24. Second direction perpendicular to PC1 in two dimensions. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 27. Illustrative linear combination defining PC2.
    Original figure 27 · PDF, page 25. Illustrative linear combination defining PC2. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 28. Normalization of the second direction’s vector.
    Original figure 28 · PDF, page 25. Normalization of the second direction’s vector. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 29. PC2 coefficients compared with those of PC1.
    Original figure 29 · PDF, page 26. PC2 coefficients compared with those of PC1. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 30. Loadings, scores, and sum of squares in the second direction.
    Original figure 30 · PDF, page 27. Loadings, scores, and sum of squares in the second direction. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 31. Coordinate change for the mice into the PC1–PC2 plane.
    Original figure 31 · PDF, page 28. Coordinate change for the mice into the PC1–PC2 plane. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 32. Position of each observation according to its principal scores.
    Original figure 32 · PDF, page 29. Position of each observation according to its principal scores. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 33. Conversion of sums of squares to variances using n − 1.
    Original figure 33 · PDF, page 29. Conversion of sums of squares to variances using n − 1. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 34. Illustrative explained variance and scree plot.
    Original figure 34 · PDF, page 30. Illustrative explained variance and scree plot. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 35. Geometric diagram of components and approximation in several dimensions.
    Original figure 35 · PDF, page 31. Geometric diagram of components and approximation in several dimensions. Source: Dunn (2021); reproduced from Isadore Nabi’s document.
    View the original page in color
    E. Financial example and diagnostics · figures 36–43
    Original figure 36. Complete financial-analysis output in Minitab.
    Original figure 36 · PDF, page 32. Complete financial-analysis output in Minitab. Source: Minitab (2019); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 37. Matrix expression of linear combinations of variables.
    Original figure 37 · PDF, page 35. Matrix expression of linear combinations of variables. Source: Universidad Carlos III de Madrid (2006); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 38. A marginally ordinary point that is unusual in the joint pattern.
    Original figure 38 · PDF, page 36. A marginally ordinary point that is unusual in the joint pattern. Source: Minitab (2019); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 39. Scree plot for the financial example.
    Original figure 39 · PDF, page 37. Scree plot for the financial example. Source: Minitab (2019); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 40. Score plot for the financial example.
    Original figure 40 · PDF, page 38. Score plot for the financial example. Source: Minitab (2019); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 41. Loading plot, called an influence plot in the original.
    Original figure 41 · PDF, page 39. Loading plot, called an influence plot in the original. Source: Minitab (2019); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 42. Biplot for the financial example.
    Original figure 42 · PDF, page 40. Biplot for the financial example. Source: Minitab (2019); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 43. Distances and reference limit in the outlier plot.
    Original figure 43 · PDF, page 41. Distances and reference limit in the outlier plot. Source: Minitab (2019); reproduced from Isadore Nabi’s document.
    View the original page in color
    F. Three genes and geometric extension · figures 44–51
    Original figure 44. Table of three genes measured in six mice.
    Original figure 44 · PDF, page 43. Table of three genes measured in six mice. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 45. Addition of a third axis to feature space.
    Original figure 45 · PDF, page 44. Addition of a third axis to feature space. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 46. High third-gene values for the first three mice.
    Original figure 46 · PDF, page 44. High third-gene values for the first three mice. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 47. Low third-gene values for the last three mice.
    Original figure 47 · PDF, page 45. Low third-gene values for the last three mice. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 48. Illustrative variant with greater separation associated with gene 3.
    Original figure 48 · PDF, page 46. Illustrative variant with greater separation associated with gene 3. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 49. Weights of an illustrative combination in three-dimensional space.
    Original figure 49 · PDF, page 46. Weights of an illustrative combination in three-dimensional space. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 50. Another principal direction and its weights in three dimensions.
    Original figure 50 · PDF, page 47. Another principal direction and its weights in three dimensions. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color
    Original figure 51. Final variant with three explained-variance percentages.
    Original figure 51 · PDF, page 47. Final variant with three explained-variance percentages. Source: Starmer (2018); reproduced from Isadore Nabi’s document.
    View the original page in color

    XXV. Glossary and correspondence with the document

    Vocabulary that keeps analytical levels separate
    TermDefinition in this edition
    ObservationA unit of study represented by a row of X and, after transformation, by a row of T.
    Original variableA measurement corresponding to a column of X.
    Principal directionA unit eigenvector of the chosen covariance or correlation matrix.
    Principal componentA variable obtained as a centered linear combination along a principal direction.
    ScoreAn observation’s value on a component.
    LoadingA coefficient or variable–component association, according to the declared convention.
    EigenvalueA component’s variance when referring to the corresponding covariance or correlation matrix.
    Singular valueA nonnegative SVD quantity; its square is an eigenvalue of XcTXc.
    Principal subspaceThe space spanned by retained directions.
    ProjectionAn observation’s orthogonal approximation within the subspace.
    ResidualThe difference between the prepared observation and its reconstruction.
    Effective dimensionalityThe number of directions considered sufficient under a declared approximation criterion.
    Thematic correspondence · no original sections omitted
    Content of the 2021 PDFDevelopment in this edition
    I. General conceptual and mathematical foundationsI–III, VIII–X: attributes, dimensionality, Bellman, Pearson, linear combinations, constraints, and spectrum.
    II. Geometric intuition and conceptual frameworkIV–X: genes, centering, transformations, structure, topology, projections, norms, slopes, SVD, and orthogonality.
    III.I–III.III. Results, eigenvalues, criteria, and componentsXI–XII: all original criteria, the complete financial table, loadings, and scores.
    III.IV. Mahalanobis distanceXIII: definition, geometry, spectral equivalence, singularity, and threshold assumptions.
    III.V–III.IX. Scree, scores, loadings, biplot, and outliersXIV–XVIII: all five diagnostics, their interpretation, and its conditions.
    III.X. Final considerationsXIX–XX: NIPALS, other algorithms, and comparison with linear regression.
    IV. Geometric intuition in ℝ³XXI: three genes, six mice, graphical variants, and higher-dimensional extension.
    V. ReferencesXXVI: all original bibliographic entries, with editorial corrections and technical additions.
    Document figuresXXIV: an archive of all 51 figures, with source page and access to the historical PDF.
    Additional developmentsDerivations, reconstruction, stability, epistemology, code, and traceability in XXII–XXIII and notes throughout preceding sections.

    The discussion maintains the essay’s multidisciplinary ambition: connecting intuition, formalization, computation, and interpretation. Precise distinctions allow these perspectives to complement each other without attributing to a formula results belonging to another level of analysis.

    XXVI. References and complete document

    The original document’s bibliography is preserved. Jolliffe’s name and the editorial presentation of entries have been corrected; encyclopedic sources and forums remain as historical or teaching aids. The additions incorporate technical documentation and further academic work. Inclusion of a reference in the original is not presented as a new verification of its entire contents or as automatic support for every assertion it contains.

    1. Adler, J. (2012). R in a Nutshell (2nd ed.). O’Reilly Media.
    2. Alger, N. (2013, March 4). Intuitively, what is the difference between Eigendecomposition and Singular Value Decomposition? Answer on Mathematics Stack Exchange. Question and answers.
    3. Bellman, R. (1972). Dynamic Programming (printing consulted in the original). Princeton University Press. Originally published in 1957.
    4. Dunn, K. G. (2021). Process Improvement Using Data. Chapter 6: latent-variable methods; section on PCA. Author’s textbook. PDF document.
    5. Jolliffe, I. T. (2002). Principal Component Analysis (2nd ed.). Springer.
    6. Minitab. (2019, reference in the original). Interpret all statistics and graphs for Principal Components Analysis. Spanish documentation. English technical page consulted to cross-check the table.
    7. Massachusetts Institute of Technology. (2011; consulted in 2021 in the original). Linear transformations and their matrices. MIT OpenCourseWare, Linear Algebra. Course and materials.
    8. Nabi, I. (2020). Sobre los estimadores de Bayes, el análisis de grupos y las mixturas gaussianas [On Bayes estimators, cluster analysis, and Gaussian mixtures]. Unpublished document cited in the original edition.
    9. Nabi, I. (2021, April 3). ¿Por qué se realiza un ajuste por re-escalamiento, normalización o estandarización sobre los datos en el contexto del aprendizaje automático? [Why are data rescaled, normalized, or standardized in machine learning?]. Marxist Philosophy of Science. Related article.
    10. Nabi, I. (2021, April 2). Una interpretación multidisciplinaria de los espacios característicos, vectores característicos y valores característicos [A multidisciplinary interpretation of eigenspaces, eigenvectors, and eigenvalues]. Complete document.
    11. Pearson, K. (1901). On lines and planes of closest fit to systems of points in space. The London, Edinburgh, and Dublin Philosophical Magazine and Journal of Science, 2(11), 559–572. https://doi.org/10.1080/14786440109462720.
    12. Mathematics Stack Exchange. (2015, March 13). Understanding proof of isometry implies isomorphism. Discussion cited in the original. Its scope depends on the specified structures and surjectivity assumptions.
    13. Starmer, J. (2018, April 2, according to the original reference). Principal Component Analysis (PCA). StatQuest. Cited video. Author’s material index. Source of the gene-expression teaching sequences.
    14. Universidad Carlos III de Madrid. (2006, November 7, according to the original reference). Análisis de componentes principales [Principal component analysis]. Cited teaching material.
    15. Universitat de Girona. (2002, January 24, according to the original reference). Número de factores a conservar [Number of factors to retain]. Material on factor analysis. Historical address cited. The reference is retained; the revised discussion distinguishes components from common factors.
    16. Weisstein, E. W. (consulted in 2021). Projection. MathWorld, Wolfram Research. Reference article.
    17. Weisstein, E. W. (consulted in 2021). Transformation. MathWorld, Wolfram Research. Reference article.
    18. Wikipedia. (2020, version cited in the original). Curse of dimensionality. Encyclopedic entry.
    19. Wikipedia. (2020, version cited in the original). Isomorfismo [Isomorphism]. Spanish entry.
    20. Wikipedia. (2021, version cited in the original). Isomorphism. English entry.
    21. Wikipedia. (2021, version cited in the original). Transcripción genética [Genetic transcription]. Encyclopedic entry. Transcription synthesizes RNA from DNA; translation produces a polypeptide chain from messenger RNA.
    22. Hotelling, H. (1933). Analysis of a complex of statistical variables into principal components. Journal of Educational Psychology. Additional historical reference.
    23. Jolliffe, I. T., & Cadima, J. (2016). Principal component analysis: a review and recent developments. Philosophical Transactions of the Royal Society A, 374, 20150202. DOI. Open-access full text.
    24. Revelle, W. VSS: Very Simple Structure, MAP, and other criteria. Documentation for the psych package. Author’s documentation. Includes Velicer (1976), Determining the number of components from the matrix of partial correlations, Psychometrika, 41, 321–327.
    25. Revelle, W. fa.parallel: Scree plots of data or correlation matrix compared to random matrices. Documentation for the psych package. Author’s documentation. Includes Horn (1965), A rationale and test for the number of factors in factor analysis, Psychometrika, 30, 179–185.
    26. Scikit-learn developers. PCA. Official documentation. Algorithms, centering, attributes, and conventions.
    27. Scikit-learn developers. StandardScaler. Official documentation. Standardization and variance convention.
    28. R Core Team. prcomp: Principal Components Analysis. R documentation, stats package. SVD computation, scaling, and scores.

    The historical document provides access to the original composition, color screenshots, and discussion as published in 2021. This English web edition expands the exposition and clarifies its mathematical, statistical, and philosophical distinctions while maintaining traceability between versions.

    Read the complete original document · PDF

    JOSÉ MAURICIO GÓMEZ JULIÁN · Original 49-page document in Spanish
    Revised and expanded English web edition

  • AN EMPIRICAL METHODOLOGY FOR DETERMINING THE MAGNITUDE OF SECTORAL INTERRELATIONSHIPS WITHIN THE INPUT-OUTPUT MATRIX FROM PRODUCTION AND USES TABLES FOR THE CASE OF THE UNITED STATES 1997-2019

    AN EMPIRICAL METHODOLOGY FOR DETERMINING THE MAGNITUDE OF SECTORAL INTERRELATIONSHIPS WITHIN THE INPUT-OUTPUT MATRIX FROM PRODUCTION AND USES TABLES FOR THE CASE OF THE UNITED STATES 1997-2019

    Marxist Philosophy of Science
    Political Economy · Quantitative Methods
    Empirical Research · United States

    Measuring Sectoral Interrelationships in the U.S. Economy

    An empirical methodology using make and use tables, 1997–2019

    Social production connects activities that, considered in isolation, appear independent. An industry transforms commodities produced by others; its own products, in turn, enter new production processes. Studying these connections requires linking the analysis of economic structure to instruments that make its relationships observable.

    Abstract

    This article presents a strategy for organizing annual make and use tables into comparable time series and constructing a descriptive indicator of the association between output and inputs. The original study used BEA data for 71 U.S. industries between 1997 and 2019, processing in R, and two levels of weighting applied to absolute correlations. This edition makes the notation explicit, distinguishes commodities from industries, and preserves the published figures as historical results subject to verification.

    1997–2019Study period
    71Industries
    23Annual observations
    per complete series

    01 / MotivationThe economic problem

    From the standpoint of Marxist political economy, the study of branches of production acquires meaning within the reproduction of society as a whole. The division of labor multiplies specializations, but also the material connections required to sustain each activity.

    The question guiding this research is how to develop an empirical approximation to the interconnectedness of production using disaggregated accounting data. Input-output accounts provide a particularly useful basis: they allow us to examine what is produced, what is used as an input, and how the uses of commodities are distributed.

    The central theoretical antecedent is Kuroki’s discussion of the equalization of the rate of profit and technical interdependence in the volume edited by Semmler. The reference is to the 1986 publication of the proceedings of a conference held in 1985.[1] Connecting this discussion to an empirical indicator requires explaining which relationship is observed and how it relates to the model’s categories.

    The empirical task is to make explicit the link between an economic category and the statistical operation used to study it.

    It is therefore useful to distinguish three dimensions: the existence of production flows, their relative importance, and the temporal association between their magnitudes. These dimensions complement one another, but each answers a different question.

    02 / Statistical foundationData and units of analysis

    Reading the files correctly requires identifying the accounting framework. The BEA distinguishes the make and use tables of the make–use framework from the tables of the supply–use framework. In particular, a supply table includes imports and should not automatically be treated as synonymous with domestic production.[2]

    Table 1. Orientation of the make–use framework used in this exposition
    ObjectRowsColumnsContents
    Make table, VIndustries jCommodities kOutput of each commodity by each industry.
    Intermediate use table, UCommodities kIndustries jInputs of each commodity used by each industry.
    Industry output, xOne observation per industryVectorTotal output of all commodities produced by an industry.

    Source: the structure described in the BEA’s input-output accounts manual. Check the orientation of each exported table before using its totals.

    Let vjkt denote the output of commodity k produced by industry j in year t, and let ukjt denote that industry’s intermediate use of commodity k. With this orientation, industry output is obtained by summing the industry’s row across the commodity block:

    xjt = ∑k vjkt (1) Gross output of industry j in year t.

    The bottom totals row of a make table with this orientation reports commodity totals, whereas industry totals are obtained by summing across rows. Selecting a spreadsheet position without checking its meaning can change the variable being analyzed.[3]

    The original screenshots identify tables after redefinitions and uses valued at producers’ prices. A replication must maintain consistent valuation, classification, and treatment of redefinitions across both tables and over time. Updates to a statistical database can revise its historical figures.

    03 / PreparationFrom annual tables to time series

    Data preparation consists of aligning identifiers and observations. A single year provides a cross section; successive years make it possible to construct a dataset with both industry and time dimensions. Each industry then has an output series and a series for each input category.

    1. Select compatible tables.

      Identify the data vintage, monetary units, aggregation level, and treatment of redefinitions. Separate the production blocks from rows and columns containing totals, value added, and final demand.

    2. Obtain output by industry.

      Construct each year’s vector x from verified industry totals. Retain codes as identifiers; names serve as descriptions.

    3. Reorient the use block where useful.

      Transposing U places industries in rows and commodities in columns. This facilitates combining records while preserving the meaning of each flow: it changes its position, not its economic nature.

    4. Stack the years and align the keys.

      Construct a table with one row per industry–year combination. Check that each combination appears only once and that inputs retain a consistent classification.

    5. Construct comparable series.

      For each industry, assemble output and each input over the same years. Record missing values, zeros, classification changes, and constant series before calculating correlations.

    6. Calculate, weight, and retain the outputs.

      Save signed coefficients, their absolute values, the observations used, the weights, and the aggregate indicators. Validation must make each result traceable to its input data.

    Table 2. Working structure, with values represented symbolically
    YearIndustryOutputInput 1Input 2…
    1997jxj,1997u1j,1997u2j,1997…
    1998jxj,1998u1j,1998u2j,1998…
    ⋮⋮⋮⋮⋮⋱
    2019jxj,2019u1j,2019u2j,2019…

    A complete series has 23 observations. A balanced dataset with 71 industries and one row per industry–year contains 1,633 rows; the number of columns depends on the selected input block.

    04 / MeasurementTemporal output–input association

    For each pair consisting of an industry and a commodity used as an input, Pearson’s correlation is calculated between the two series. The time index runs through the comparable years; this is not a correlation across industries within a single year.

    rjk = corrt(xjt, ukjt) (2) Temporal linear association between industry output and use of commodity k.

    The sign distinguishes movements in the same direction from movements in opposite directions. To summarize their intensity alone, we use ajk = |rjk|. Both matrices should be retained: a large negative coefficient contains economic information that disappears when its absolute value is taken.

    Pearson’s correlation is undefined for a constant series. A flow that is zero in every year should likewise not automatically be assigned a correlation of zero. When data are missing, the number of observations underlying each coefficient must be reported, and an explicit comparison rule must be maintained.

    05 / AggregationThe two levels of weighting

    Weighting incorporates the economic importance of flows. The first level summarizes each industry’s association with its inputs; the second aggregates the industry results.

    First level: input composition

    Sj = ∑k wjk |rjk| (3) wjk ≥ 0 and ∑k wjk = 1 for every industry j.

    Because each correlation summarizes several years, the weights must represent an explicitly defined temporal distribution. One can select a reference year, average annual shares, or calculate shares from cumulative flows. These choices can produce different results.

    One possible convention for a new replication is to use shares based on cumulative flows over the period:

    wjk = ∑t ukjt∑t ∑h uhjt (4) Illustrative fixed-weight convention; it is not attributed to the original script. It requires admissible nonnegative flows and a positive denominator.

    When a correlation is undefined and its input has a positive weight, the series must be reviewed, or its exclusion and the renormalization of the remaining weights must be made explicit. Removing missing values without adjusting the denominator changes the indicator.

    Second level: industry shares

    S = ∑j qj Sj (5) qj ≥ 0 and ∑j qj = 1. The economic variable determining each industry’s share must be stated.

    For example, qj could be defined as the industry’s share of cumulative gross output across all included industries. This choice must also be documented and distinguished from weighting by value added.

    The equations above specify a family of indicators. Their exact application depends on the selected weights and data coverage. With normalized nonnegative weights, the sectoral and overall indicators necessarily lie within the interval [0, 1].

    06 / Worked exampleInterpreting the weighting

    Consider an industry with two input categories. The first has a correlation of 0.90 with output and accounts for 80% of its inputs; the second has a correlation of −0.20 and accounts for the remaining 20%.

    SA = 0.80 × |0.90| + 0.20 × |−0.20| = 0.76 A hypothetical example explaining the operation; this is not a BEA estimate.

    The simple mean of the two magnitudes would be 0.55. The weighted result is higher because the stronger association corresponds to the input with the larger share. Weighting expresses an economic choice; an increase in the indicator does not, by itself, improve the evidence.

    If another industry scores 0.40 and the industry shares are 0.60 and 0.40, respectively, the overall indicator is:

    S = 0.60 × 0.76 + 0.40 × 0.40 = 0.616 The result lies between the two industry indicators being aggregated.

    07 / Published evidenceResults and verification note

    The R outputs reproduced in the original publication show the following figures. They are transcribed to preserve documentary traceability; this edition neither recalculates the dataset nor certifies that the results have been reproduced.

    Table 3. Figures reported in the 2021 screenshots
    StageReported valueDocumentary interpretation
    Mean of absolute correlations0.6837338Summary before economic weighting.
    Mean of industry indicators0.7351034Output associated with the first level of weighting.
    Aggregate with industry weighting0.7778624Final reported output.

    Sources: the first output, second output, and final output from the original publication.

    Correlation r must also be distinguished from the coefficient of determination R2. An average of absolute correlations cannot be presented as a proportion of explained variance. The familiar relationship R2 = r2 applies to simple linear regression with an intercept, not to this aggregation of multiple pairs.[4]

    Finally, describing a correlation as “strong” requires a contextual criterion. If 0.70 is chosen as a descriptive reference, its distance from 0.6837338 is 0.0162662 indicator units. Proximity to that threshold neither replaces validation of the calculation nor establishes an economic conclusion.

    08 / DiscussionInterpretation and scope

    What this strategy can investigate

    Once the data and calculations have been validated, the procedure allows comparisons of industry profiles of temporal association and an examination of how their synthesis changes when economic weights are introduced. Its usefulness also lies in making the passage from accounting tables to time series and indicators traceable.

    Checks that would strengthen inference

    • Prices and trends. Examine how much of the association between monetary magnitudes reflects price changes or common trends. Depending on the question, compare levels, deflated series, growth rates, or justified time-series specifications.
    • Temporal dependence and stability. Examine autocorrelation, influential observations, and possible breaks. Statistical tests must respect the temporal structure of the data.
    • Aggregation decisions. Compare weights and coverage, retain sign information, and document the treatment of commodities associated with the industry’s own sector.
    • Structural content. Compare the indicator with measures based on flows and production requirements. Temporal comovement and the technical intensity of a linkage are distinct dimensions.

    These checks are proposed extensions. They are not presented as tests already performed in the 2021 study.

    Correlations between trending series can invite misleading interpretations; time-series techniques help distinguish these movements from more stable relationships.[4] Gross output also incorporates intermediate inputs within an accounting identity. This connection must form part of the economic interpretation of the result.

    The connection to Marxist debate

    Empirical research can contribute to debates on reproduction, competition, and the interconnectedness of branches of production. Connecting this indicator to the dynamics of the rate of profit requires justifying the step from observed association to the technical interdependence relevant to the model.

    The United States provides a relevant setting for investigation. Its analysis alone neither establishes a universal regularity nor verifies every condition of a theoretical result on stability. Comparisons across periods, countries, and structural measures would allow the generality of the hypothesis to be assessed.

    A study becomes stronger when its data, its operations, and the scope of its conclusions can be examined separately.

    The contribution worth developing is a reproducible strategy for organizing production data and examining a hypothesis about economic interconnectedness. Reviewing the published calculation and conducting robustness checks are the next steps toward consolidating that contribution.

    09 / MaterialsReplication and traceability

    The materials archive linked at the end corresponds to the resource provided by the authors in the original post. A complete replication should retain both the downloaded files and the transformations produced by the code.

    1. Record the source: download date, data vintage, tables, units, valuation, and industry and commodity codes.
    2. Verify alignment: unique keys, annual coverage, classification changes, and correspondence between output and uses.
    3. Audit the coefficients: variation in the series, number of observations, missing values, and the range of each correlation.
    4. Audit the weights: temporal definition, positive denominators, nonnegative weights, and a sum of one at each level.
    5. Publish the outputs: signed and absolute-value matrices, weights, sectoral results, and the overall aggregate, together with the versions of R and its packages.

    Range checks are especially useful: an indicator of the form described must remain between zero and one. If an output violates that property, interpretation should pause while the operation generating it is located.

    10 / SourcesReferences and further reading

    Sources for this edition
    1. [1] Kuroki, R. (1986). The equalization of the rate of profit reconsidered. In W. Semmler (Ed.), Competition, instability, and nonlinear cycles (pp. 35–50). Springer. Publisher’s record. Conference held in March 1985; publication year corrected according to the publisher.
    2. [2] U.S. Bureau of Economic Analysis. (n.d.). Guide to the interactive industry input-output accounts tables. Read the guide.
    3. [3] U.S. Bureau of Economic Analysis. (2006). Concepts and methods of the U.S. input-output accounts. Methodological manual.
    4. [4] Hyndman, R. J., & Athanasopoulos, G. (2021). Forecasting: Principles and practice (3rd ed.). OTexts. Chapter 7: Time series regression models. Read the chapter.
    5. [5] U.S. Bureau of Economic Analysis. (n.d.). Input-output. Access to tables and code concordances. Explore the data.
    Debate bibliography and sources listed in the original publication

    Retained as a bibliographic guide to the 2021 article; the works listed are not presented as an exhaustive review of the current state of research. Non-English publication titles are retained in their original language.

    • Cockshott, P., & Cottrell, A. (2005). Robust correlations between prices and labor values. Cambridge Journal of Economics, 309–316.
    • Cockshott, P., Cottrell, A., & Valle Baeza, A. (2014). The empirics of the labour theory of value: Reply to Nitzan and Bichler. Investigación Económica, 115–134.
    • Cockshott, P., Cottrell, A., & Zachariah, D. (2019). Against the Kliman theory. Document linked in the original.
    • Kliman, A. (2002). The law of value and laws of statistics: Sectoral values and prices in the US economy, 1977–97. Cambridge Journal of Economics, 299–311.
    • Kliman, A. (2005). Reply to Cockshott and Cottrell. Cambridge Journal of Economics, 317–323.
    • Kliman, A. (2014). What is spurious correlation? A reply to Díaz and Osuna. Journal of Post Keynesian Economics, 345–356.
    • Mindrila, D., & Balentyne, P. (n.d.). Scatterplots and correlation. University of West Georgia. Notes linked in the original.
    • Nabi, I. (2021). Sobre la metodología del U.S. Bureau of Economic Analysis para la redefinición y reasignación de productos en la matriz insumo-producto de Estados Unidos. Related post.
    • Sánchez, C., & Ferràndez, M. N. (2010). Valores, precios de producción y precios de mercado a partir de los datos de la economía española. Investigación Económica, 87–118.
    • Sánchez, C., & Montibeler, E. E. (2015). La teoría del valor trabajo y los precios en China. Economia e Sociedade, 329–354.
    • U.S. Bureau of Economic Analysis. (2021). The make of commodities by industries, after redefinitions; The use of commodities by industries, after redefinitions (producers’ prices). Tables used in the original study.
    • Zachariah, D. (2006). Labour value and equalisation of profit rates: A multi-country study. Indian Development Review, 4, 1–20.
    Original code and data ↗

    Resource linked by the authors in the 2021 publication.
    Political Economy · Empirical Evidence · Replication

  • WHY PERFORM DATA RESCALING, NORMALIZATION, OR STANDARDIZATION IN THE CONTEXT OF MACHINE LEARNING?

    WHY PERFORM DATA RESCALING, NORMALIZATION, OR STANDARDIZATION IN THE CONTEXT OF MACHINE LEARNING?

    Marxist Philosophy of Science
    Statistics · Methods · Machine Learning

    Rescaling, Normalization and Standardization

    Why transform the scales of data in machine learning?

    ISADORE NABI
    Published on
    Revised and expanded edition ·

    A difference in units can inadvertently become a difference in importance to an algorithm. If one feature is expressed in years and another in thousands of monetary units, their contributions to a numerical distance can be very unequal. This does not necessarily mean that one is more informative; it means that the calculation responds to the numerical representation chosen.

    Rescaling data allows us to intervene in that representation. The appropriate transformation, however, depends on the problem: it may alter the geometry of the observations, facilitate optimization or change the effect of a penalty. Understanding these differences is more useful than applying a universal preprocessing rule.

    01 / ConceptsTransformations worth distinguishing

    Let X be a data matrix: each row represents an observation and each column a feature. We write xij for the value of feature j in observation i. This distinction between rows and columns is essential: operations called “normalization” do not all act on the same object.

    Rescaling: the general term

    In this article, “rescaling” means changing the scale of a numerical representation. Many common transformations applied to individual features have the form x′ij = ajxij + bj, with constants fixed for each column. When the translation is nonzero, the transformation is affine; it is strictly linear only when that translation is zero.

    Converting degrees Celsius to Fahrenheit is an affine example. Expressing income in thousands of monetary units is a purely multiplicative change. Neither adds observations: each changes how they are expressed.

    Min–max scaling

    The min–max transformation maps the values of each feature to the interval [0, 1] in the dataset used to fit it. For a nonconstant column:

    x′ij = (xij − mj) / (Mj − mj) mⱼ: minimum of column j · Mⱼ: maximum of that column · parameters estimated on the training data.

    A value halfway through the range becomes 0.5. The operation preserves order and ratios between differences within a column, but changes comparisons across columns. Subsequent data may fall outside [0, 1] if they exceed the extremes used to fit the scaler; clipping those values is a separate decision.[2]

    Standardization: centering and expressing values in standard deviations

    The usual standardization subtracts each feature’s mean and divides by its standard deviation:

    zij = (xij − μj) / σj μⱼ: training mean · σⱼ: nonzero training standard deviation.

    Thus, z = 2 means that a value lies two standard deviations above the reference mean. The nonconstant columns in the fitting dataset acquire mean zero and variance one, provided the same variance convention is used throughout. StandardScaler uses the divisor n, equivalent to ddof=0.[1]

    Normalization by a norm: transforming each observation

    In another use of the term, normalization means dividing each nonzero observation vector by its norm. For the Euclidean norm:

    ui = xi / ‖xi‖2 xᵢ: vector in row i · ‖xᵢ‖₂ = √(xᵢ₁² + ⋯ + xᵢₚ²) · requires a nonzero norm.

    This operation maps each row to unit length and preserves its direction, discarding the vector’s overall magnitude. It can be useful for text representations and comparisons using cosine similarity. It does not, by itself, put different columns on comparable scales; the norm must also be meaningful for the chosen representation.[3]

    02 / ExampleUnits can change the nearest neighbor

    Consider four fictional observations. Income is expressed in monetary units per month. We will use all four rows as the reference dataset to illustrate the transformations; we are not yet evaluating a predictive model.

    Table 1 · Original data
    ObservationAge (years)Monthly income
    A202,000
    B223,000
    C402,100
    D505,000
    Synthetic data constructed for this example. Combining years and income directly illustrates the problem; it is not proposed as a substantively justified measure of similarity.

    Applying Euclidean distance directly to the original numbers gives a distance of approximately 1,000.002 from A to B, and 101.980 from A to C. C is closer to A despite a twenty-year age difference, because the difference in income dominates the calculation.

    For min–max scaling, the age range is 30 and the income range is 3,000. For standardization, the means are 33 and 3,025; the standard deviations, calculated with divisor 4, are approximately 12.530 and 1,204.938.

    Table 2 · Two transformations of the same columns
    RowAge
    min–max
    Income
    min–max
    Age
    z
    Income
    z
    A0.00000.0000−1.0375−0.8507
    B0.06670.3333−0.8779−0.0207
    C0.66670.03330.5587−0.7677
    D1.00001.00001.35671.6391
    Values rounded to four decimal places. Distances are calculated before rounding.

    After min–max scaling, the distances from A to B and from A to C are:

    d′(A, B) = √[(2/30)² + (1,000/3,000)²]
    ≈ 0.3399
    d′(A, C) = √[(20/30)² + (100/3,000)²]
    ≈ 0.6675
    To D: d′(A, D) ≈ 1.4142. Among the other three observations, B is now the closest to A.

    The nearest neighbor changed from C to B. The people and their original attributes did not change: the numerical comparison rule did. The example demonstrates sensitivity to scale; it does not establish that the second neighborhood is scientifically preferable. That requires justifying what “similar” means in the problem under study.

    03 / FoundationsThree distinct reasons to scale

    Geometry: deciding how proximity is measured

    If each coordinate is transformed as x′j = (xj − bj)/sj, with sj > 0, the squared Euclidean distance between two observations becomes:

    d′(x, y)² = ∑j=1p (xj − yj)² / sj² p: number of features · sⱼ: scale divisor for feature j. The common translation bⱼ cancels when subtracting.

    This identity shows that scaling introduces a weighting for each coordinate: every squared difference receives the factor 1/sj². This affects K-nearest neighbors when it uses a metric sensitive to scale, and K-means with its usual Euclidean objective. Standardization equalizes marginal variances in the fitting dataset; it does not equalize the information supplied by each variable or remove their correlations.[4]

    Optimization: making the path to a solution easier

    In problems fitted using gradient methods, very different scales can produce uneven curvature in the objective function. A learning rate small enough for one direction may make progress slow in another. Appropriate scaling can improve conditioning and help convergence, although it does not eliminate every numerical difficulty or guarantee a particular speedup.[5]

    It is useful to distinguish the model from the algorithm used to fit it: the same family of models can be estimated through different numerical procedures. Consequently, “improves training” and “changes the statistical solution” are not equivalent claims.

    Regularization: units also affect penalties

    In a linear predictor, multiplying a feature by a constant allows its coefficient to be divided by that constant without changing its contribution to the prediction. An L1 or L2 penalty on the coefficients does change under this reparameterization if its form and strength remain fixed. This is why scale matters in Ridge, Lasso and regularized logistic regression.[6]

    The rationale for scaling therefore extends beyond the explicit use of distances. In unpenalized least squares with an intercept and an invertible affine transformation of the features, the space of fitted predictions is preserved in exact arithmetic; numerical behavior may nevertheless change.

    Choosing a scale means choosing how differences are compared.

    04 / A particular casePCA: which variability do we want to preserve?

    Principal component analysis seeks directions of maximum variance through linear combinations of the features. Working with centered data in their original units gives PCA of the covariance matrix; standardizing first, with a consistent variance convention, leads to PCA of the correlation matrix.

    Differences in units can cause a feature with large numerical variance to dominate certain components. But a high mean does not imply high variance: a variable can have large values and vary very little. What matters for PCA is the spread and the structure of covariation.

    Standardizing before PCA is an analytical decision. It may be justified when units are heterogeneous and the aim is to compare the features’ relative variation. If all features share units and their variance differences have substantive meaning, removing those differences may obscure relevant information.[4]

    PCA should not be identified with gradient descent either. It is usually computed through singular value decomposition of the centered data or eigendecomposition of the covariance matrix. In scikit-learn, PCA centers the features but does not automatically standardize them.[7]

    05 / JudgmentHow to choose a transformation

    The starting point should be the model’s mechanism, the observed distribution and the meaning of the variables. The following options are working hypotheses to test, not guarantees of superiority:

    Table 3 · A guide to choosing
    SituationOption to evaluateCaution
    Comparing differences in units of spreadStandardizationThe mean and standard deviation are sensitive to extremes.
    Using a bounded reference intervalMin–max scalingFuture values may exceed the interval; extremes compress the remaining values.
    Reducing the influence of extremes on scaling parametersMedian and interquartile rangeDoes not remove outliers or make the entire model robust.
    Comparing vector direction or compositionNormalization by a normDiscards overall magnitude.
    Trees with splits on individual featuresKeeping the original scaleCheck the requirements of the other processing steps.
    The choice and its hyperparameters should be evaluated through validation, keeping the final test set held out.

    Outliers and robust scaling

    When a few extremes substantially affect the mean, standard deviation or range, a less sensitive reference is worth considering. A common transformation is:

    rij = [xij − median(Xj)] / [Q3,j − Q1,j] Q₁ and Q₃: first and third training quartiles · their difference is the interquartile range, which must be nonzero for this formula.

    RobustScaler applies this criterion by default. Here, “robust” refers to how the center and scale are estimated: an extreme observation can remain far away after transformation and still influence the subsequent estimator.[8]

    Cases where scaling usually adds little

    Conventional trees that split a feature using thresholds preserve, in exact arithmetic, the same possible partitions under affine transformations with positive slope. The ordering of values does not change. Scaling is therefore usually unnecessary for those trees and ensembles based exclusively on them; implementation or numerical precision differences may occur.[11]

    A processing chain that includes distance-based selection, PCA or another step sensitive to scale may require scaling even when its final predictor is a tree. Evaluation should consider the entire procedure.

    06 / ApplicationTransforming without leaking information

    Scaling parameters are learned from data: means, standard deviations, extremes or quantiles. Computing them on the entire dataset before splitting training and test data allows the test set to influence model construction. This data leakage can produce an overly optimistic evaluation.

    The operational rule is simple: fit the scaler only on training data and reuse its parameters on the other data. In cross-validation, fitting must be repeated on the training portion of each fold. A Pipeline allows this procedure to be carried out together with the estimator.[9]

    Reproducible example · Python / scikit-learn
    from sklearn.datasets import load_iris
    from sklearn.model_selection import (
        StratifiedKFold,
        cross_val_score,
        train_test_split,
    )
    from sklearn.neighbors import KNeighborsClassifier
    from sklearn.pipeline import make_pipeline
    from sklearn.preprocessing import StandardScaler
    
    X, y = load_iris(return_X_y=True)
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.25,
        stratify=y, random_state=42,
    )
    
    model = make_pipeline(
        StandardScaler(),
        KNeighborsClassifier(n_neighbors=5),
    )
    cv = StratifiedKFold(
        n_splits=5, shuffle=True, random_state=42,
    )
    
    # The scaler is fitted within each fold.
    scores = cross_val_score(
        model, X_train, y_train,
        cv=cv, scoring="accuracy",
    )
    print(f"Mean CV accuracy: {scores.mean():.3f}")
    
    # Final fitting uses training data only.
    model.fit(X_train, y_train)
    print(f"Test accuracy: {model.score(X_test, y_test):.3f}")
    Iris makes the example executable without downloading an external file. The test set is used at the end; any comparison of scalers or hyperparameters belongs in validation within the training data.

    The example illustrates the proper separation between fitting and evaluation; it does not attempt to show that standardization always improves performance on Iris. For temporal data, observations grouped by person or other dependencies, the splitting strategy must also respect that structure.

    When applying the model to future observations, the learned scaler is retained. Refitting it independently on each batch changes the input coordinates and breaks their correspondence with the trained model.

    07 / ScopeWhat scaling does not resolve

    Scaling is part of preprocessing, but it does not replace cleaning or data quality assessment. It does not correct measurement errors, duplicates, selection biases or incorrect labels; nor does it automatically impute missing values.

    • Constant columns. If the range or standard deviation is zero, the corresponding formulas require specific handling. Such columns should be identified and a decision made about retaining them.
    • Sparse data. Subtracting the mean can turn zeros into nonzero values and substantially increase memory use. StandardScaler(with_mean=False) avoids centering when sparsity must be preserved.[1]
    • Categorical variables. Assigning numbers to categories does not automatically establish a legitimate order or distance. Their encoding requires a separate decision.
    • Interpretation. A coefficient expressed per standard deviation answers a different question from one expressed per year or monetary unit. The chosen scale should be documented.

    The decisive methodological question is which differences in the phenomenon we wish to preserve and which arise solely from its units of representation. Knowledge of the object of study guides that choice; validation allows its predictive consequences to be tested. Understood in this way, scaling is an explicit modeling decision.

    08 / SourcesReferences and notes

    Numbered citations refer to primary documentation for the tools and to university teaching material. The data and calculations in the age and income example were constructed for this article. Documentation consulted on October 26, 2026.

    1. Scikit-learn developers. (n.d.). StandardScaler. Scikit-learn documentation.
    2. Scikit-learn developers. (n.d.). MinMaxScaler. Scikit-learn documentation.
    3. Scikit-learn developers. (n.d.). Normalizer. Scikit-learn documentation.
    4. Scikit-learn developers. (n.d.). Importance of Feature Scaling. Scikit-learn examples.
    5. Scikit-learn developers. (n.d.). Stochastic Gradient Descent. Scikit-learn user guide.
    6. Scikit-learn developers. (n.d.). Linear Models. Scikit-learn user guide. See the sections on Ridge, Lasso and logistic regression.
    7. Scikit-learn developers. (n.d.). PCA. Scikit-learn documentation.
    8. Scikit-learn developers. (n.d.). RobustScaler. Scikit-learn documentation.
    9. Scikit-learn developers. (n.d.). Common pitfalls and recommended practices. Scikit-learn user guide.
    10. The Pennsylvania State University. (n.d.). SLR Model Assumptions. STAT 501: Regression Methods.
    11. Scikit-learn developers. (n.d.). Decision Trees. Scikit-learn user guide.
  • Braking for Humanity: The Political Economy of Silicon Valley’s Proposed AI Slowdown

    Braking for Humanity: The Political Economy of Silicon Valley’s Proposed AI Slowdown

    Political Economy

    Braking “for Humanity”: The Political Economy of Silicon Valley’s Proposed AI Slowdown

    Abstract. On Saturday, September 12, 2026, the chief executive of Anthropic publicly called for slowing the pace at which the capabilities of artificial intelligence models are improved; the chief executive of OpenAI agreed and, in passing, ruled out taking his company public this year; Elon Musk endorsed the proposal; on the following Monday, chipmakers fell on the stock market while the large companies that buy those chips rose. This article argues that braking “for humanity” is better understood as a way of managing capital, and in particular of defending its rate of profit, than as a technical decision: it reduces what has to be financed at a moment when investors are beginning to withdraw, it is presented as prudence, and it leaves intact the premise that sustains valuations, namely that an artificial general intelligence is near. Every claim carries its source, every technical term is defined when it first appears, and the closing section lists the facts that, in the coming weeks, could refute the thesis.

    · · ·

    1. Who Is Making and Who Is Losing Money Today

    It is best to begin with the accounts, because the whole discussion about bubbles and brakes rests on them. Three terms suffice to read them. Revenue is what a company bills for what it sells. An operating loss is what it loses in its ordinary activity, that is, revenue minus the costs of producing and selling, before interest, taxes and accounting adjustments. A net loss is the final result, which also incorporates those other items; among them there may be non-cash accounting charges, losses recorded in the books without any money leaving the till, for example when the value assigned to certain securities issued by the company changes.

    With those definitions, OpenAI’s situation is as follows. According to its audited financial statements for 2025, obtained by the journalist Ed Zitron and independently verified by the Financial Times, the company billed 13.07 billion, had an operating loss of 20.92 billion and a net loss of 38.53 billion; the gap between the two losses is explained mostly by a non-cash charge of 41.55 billion tied to its restructuring as a for-profit company (Zitron, 2026b; Tolomia, 2026). In plain terms: even without the accounting charge, OpenAI loses more than it bills. Nor was this a surprise to its executives: as early as September 2025, internal projections reported by The Information anticipated burning 115 billion of cash through 2029, with more than 17 billion in 2026, 35 billion in 2027 and 45 billion in 2028 (Reuters, 2025).

    Google’s case requires separating two things that are usually conflated: the model race and the company’s business. In the model race, Google is currently behind. In the Artificial Analysis Intelligence Index, a table that combines dozens of standardized tests, no Gemini model appears in the leading group; in the version of the index published this week the best Gemini ranks twenty-first, with 41 points against 53 for Claude Fable 5.1 and GPT-6 Astra, and in the previous version of the same index the best Gemini ranks eighth, with 50.2 points against 58.9 for GPT-5.6 Sol (Artificial Analysis, n.d.; BenchLM, 2026). The two versions disagree on the order of the leaders, but they agree on what matters here. It is reasonable to suppose that Google’s artificial intelligence unit loses money, although this cannot be asserted with certainty, because Alphabet, the parent company, does not publish that result separately. What it does publish shows that it is not out of business: in 2025 it earned 132.17 billion in net income; in the second quarter of 2026 it had an operating income of 40.8 billion and a net income of 112.1 billion, the latter inflated by 99.0 billion of accounting gains on stakes in other companies; its cloud, which is where it sells Gemini to enterprises, grew 82% in the quarter and yielded 8.8 billion of operating income; the Gemini app went from 750 million to 950 million monthly users between the fourth quarter of 2025 and the second quarter of 2026; and the cloud’s backlog of contracted revenue stood at 240 billion in December (Alphabet Inc., 2026a, 2026b; Pichai, 2026; Cabili, 2026). The consequence for the argument is precise: if Gemini loses money, that loss is paid out of the rents of the search business, where rent means the profit that a dominant position allows a company to charge above cost, and not with outside capital. Google can lag in model quality without depending on external financing, and that puts it in a different position from OpenAI and Anthropic.

    The Chinese bet is of another nature. The main Chinese laboratories publish the open weights of their models, that is, the numerical parameters that result from training and that anyone can download and run, and they charge far less for their use than their American competitors. The U.S.-China Economic and Security Review Commission summarizes it thus: Chinese laboratories “charge far less to use high-end products than their global competitors” and do so “backed by sustained state support,” although the Commission itself leaves open whether that strategy “reflects genuine strategic preference or adaptation to necessity” in the face of chip export controls (Luong, 2026). The size of the discount is measured in tokens, the fragments of text in which models count what they read and write: DeepSeek V4-Flash charges 0.14 per million input tokens against 2 for Claude Sonnet 5 (Mann, 2026); in May 2026, Chinese open models accounted for about 61% of the tokens consumed on OpenRouter, an intermediary that routes requests to many models, and Alibaba’s Qwen functions as a customer-acquisition expense for that company’s cloud (Zeoli, 2026). State support includes energy: Nvidia’s chief executive, Jensen Huang, stated in November 2025 that in China “Power is free,” referring to subsidies that cut by up to half the electricity bill of data centers that use domestic chips (Schepkov, 2025; Yildirim, 2025).

    Two clarifications prevent a simplistic reading. The first is that an intention to ruin Silicon Valley is not documented, and the available evidence points to an effect rather than a plan: DeepSeek’s founder, Liang Wenfeng, stated in 2024 that “We didn’t mean to become a catfish” and that his company does not subsidize its prices but sets “just a small profit margin above costs” (ChinaTalk, 2024), and DeepSeek itself published that its inference system would have, at list prices, a theoretical margin of 545% over cost, which speaks of efficiency rather than selling at a loss (DeepSeek-AI, 2025). The second is that Chinese laboratories do care about losing money, because they too depend on capital: Zhipu and MiniMax went public in Hong Kong in January 2026 with net losses of about 330 million and 512 million on revenues of 27 million and 53 million (Lo, 2026), and DeepSeek took its first external funding round in 2026, of 7.4 billion, at a valuation of 52 billion (Caixin Global, 2026). Whether or not they seek to ruin anyone, the effect of their strategy is to destroy the monopoly rent of the model layer, which is precisely where OpenAI and Anthropic live.

    Anthropic is the only frontier company that has begun to show positive numbers, and it must be said exactly which. In the second quarter of 2026 it billed more than 11.5 billion, against 787 million in the same quarter of 2025 and 4.73 billion in the first quarter of 2026, and it recorded for the first time a positive adjusted operating income, according to documents seen by Bloomberg and described as preliminary (Lipschultz & Metz, 2026; Stanciuc, 2026; Park, 2026). “Adjusted” means that the company excludes certain expenses from the calculation, and since Anthropic does not yet report publicly, it is not known which: the Wall Street Journal noted that “it is unclear what accounting methods Anthropic has used to book revenue and costs,” and one critic warns that the cost of rented computing would rise sharply from July onward (Zitron, 2026a). Adjusted operating income is not net income; its cumulative losses remain negative, and its own projections, reported in November 2025, place positive cash flow only in 2028 (Bellan, 2025). Even so, it is the first time a frontier laboratory has shown an operating quarter in the black, and that explains why capital treats it differently.

    Company and periodRevenueResultSource
    OpenAI, 202513.07 billionOperating loss of 20.92 billion; net loss of 38.53 billion, including a non-cash charge of 41.55 billionZitron (2026b); Tolomia (2026)
    Anthropic, second quarter of 2026More than 11.5 billionPositive adjusted operating income, preliminary figureLipschultz and Metz (2026); Stanciuc (2026)
    Alphabet, 2025402.836 billionNet income of 132.17 billionAlphabet Inc. (2026a)
    Alphabet, second quarter of 2026119.8 billionOperating income of 40.8 billion; net income of 112.1 billion, including 99.0 billion of gains on equity stakesAlphabet Inc. (2026b)

    Note. Figures in U.S. dollars.

    2. The Speculative Turn

    There is a major turn of the market toward artificial intelligence, and a significant part of that turn is speculative. To say this precisely, a concept from classical political economy is needed: fictitious capital, the value of securities such as shares or bonds insofar as they capitalize, that is, discount to a present price, future profits that do not yet exist. That value is real as long as the expectation holds and vanishes when the expectation falls. A valuation of 852 billion for OpenAI and 965 billion for Anthropic, companies that lost money in 2025, is fictitious capital in the strictest sense: a price paid today for profits that would only arrive if the technology delivers what was promised (Bellan, 2026a, 2026b).

    On that base a layer of derivatives has been built. A futures contract is an agreement on an organized market by which two parties fix today the price at which an asset will be bought or sold on a future date; the reference assets may be individual stocks, sector indices, which are baskets of stocks, or exchange-traded funds. Such instruments now exist specifically for artificial intelligence: Cboe has listed since December 8, 2025 futures and options on its Magnificent 10 Index, which groups the seven largest technology companies plus AMD, Broadcom and Palantir (Cboe Global Markets, 2025), and CME Group launched on July 27, 2026 seventy-seven single-stock futures contracts, Nvidia and SpaceX included (CME Group, 2026). It is important to understand what they do and do not do. The money in a futures contract does not enter the company: the contract is settled between the two parties and, for one of them, the gain is exactly the other’s loss. What futures do is push the price of the shares, because those who trade them hedge by buying or selling the reference asset, and a high price attracts investment and makes raising capital cheaper. And they amplify in both directions: the same leverage that accelerates the rise accelerates the fall. All of this is extremely speculative in a precise sense: some realize present gains on the expectation that others will obtain much larger future gains, and the chain rests on very high levels of expected profitability, subject to a great many variables that are hard to control.

    The figures give the scale of the risk. The seven largest technology companies account for about one third of the S&P 500 index, with Nvidia alone at 7.7%, and explained about 42% of that index’s total return in 2025 (Greenberg, 2026). The Financial Policy Committee of the Bank of England warned in October 2025 that valuations “appear stretched, particularly for technology companies focused on Artificial Intelligence,” that “the risk of a sharp market correction has increased” and that, because of index concentration, “any AI-led price adjustment would have a high level of pass-through into the returns for investors exposed to the aggregate index,” with a cyclically adjusted price-to-earnings ratio comparable to the peak of the dot-com bubble (Bank of England, 2025). On the real side of the economy, investment in computing infrastructure, that is, data centers, chips and networks, stands at about 1.5% of United States gross domestic product, double its historical level (Juniewicz, 2026); it contributes between 0.5 and 0.7 percentage points to growth in 2025 and 2026 (Subran et al., 2026), and it accounted for nearly 92% of growth in the first half of 2025 (Wells, 2026). That is why, if the artificial intelligence sector of the stock market collapses, the whole stock market collapses with it, or at least a part large enough for a crash to occur. The only qualification is one of form: a correction of expectations compresses the multiples of profitable companies and destroys the capital of loss-making ones; for the fall to become a financial crisis and not merely a stock market one, the credit channel is needed, and that is exactly the channel that has grown the most and is seen the least.

    3. Why It Would Collapse: The Patience of Investors

    Investors are growing more impatient waiting for net profits that do not arrive, and financing is not cut off suddenly or by announcement: it withdraws little by little. This is already visible on the indebted periphery of the sector. A credit default swap is an insurance policy that an investor buys against a borrower’s default; its price is measured in basis points, hundredths of a percentage point, and rises when the market sees more risk. The swaps of CoreWeave, a company that rents out computing for artificial intelligence and finances itself with debt, went from 675 basis points in November 2025 to about 855 in July 2026, which implies close to a 50% probability of default over five years; those of Oracle, the largest non-financial borrower in the high-grade bond index, rose from 108 to more than 215 over the same span, with its 2054 bond yielding 7.8% (Roberts, 2025; Moadel, 2026). The 12.5 billion bond with which Meta financed a data center in El Paso was priced in July at a wider spread than a comparable 2025 project, because, as the account puts it, lenders “are still writing checks, but they are demanding more in return” (MarketScale Newsroom, 2026). Federal Reserve Governor Lisa Cook counted more than 1.5 trillion in data-center plans, financed with bonds by the large players and with private debt and securitizations by the small ones, and warned that “the increasing use of leverage to finance investments in an emerging technology carries risk” (Cook, 2026); J.P. Morgan estimates hyperscaler investment at 697 billion in 2026 alone and acknowledges that this spending has “in many cases, outpaced monetization” (J.P. Morgan, 2026). A hyperscaler is a company that operates computing clouds at planetary scale, such as Alphabet, Microsoft, Amazon, Meta or Oracle.

    The withdrawal is also visible at the core. As early as June, OpenAI was leaning toward pushing back its stock market debut to 2027, as the New York Times reported (Ma, 2026b), and the letter of intent with which Nvidia had announced a 100 billion investment stalled in January over “internal doubts about the size and structure of the transaction and questions about OpenAI’s business discipline” (Duprey, 2026), ending up, in the February round, as 30 billion (Brandom, 2026). Part of that financing is, moreover, circular: Nvidia invests in OpenAI, OpenAI commits to buying computing from Oracle and CoreWeave, and those companies buy chips from Nvidia, a circuit that critics described as “moving money in circles” (Jadhav, 2025); likewise, in November 2025 Nvidia and Microsoft committed up to 10 billion and 5 billion to Anthropic in exchange for Anthropic’s purchase of 30 billion of Microsoft cloud capacity (Microsoft, 2025). What must be acknowledged is that the withdrawal is still partial and uneven: OpenAI closed in March a 122 billion round at a valuation of 852 billion, with 50 billion from Amazon, 30 billion from Nvidia and 30 billion from SoftBank (Brandom, 2026; Bellan, 2026b); Anthropic raised 65 billion in May at 965 billion (Bellan, 2026a) and filed confidentially in June to go public (Korosec, 2026); venture capital to foundational laboratories in the first quarter of 2026 was double that of all of 2025 (Azevedo, 2026). Financing, in short, is bifurcating: it remains abundant at the core and grows more expensive on the leveraged periphery. The direction of change is what matters, and the direction is that of capital demanding more guarantees, more conditions and more return for every new dollar.

    4. The Brake as a Way of Managing Capital

    The frontier companies keep up that frantic pace of development in order to finally make artificial intelligence as profitable as they promised. The promise has a name: artificial general intelligence, a system capable of performing autonomously, and better than an average human, every digitizable task. Such a system would be highly profitable, and it is the only promise that justifies current valuations; OpenAI’s charter defined it in 2018 as highly autonomous systems that outperform humans at most economically valuable work (OpenAI, 2018). The thesis of this article is that, without public financing for computing, since investment is paid for with private capital, debt and the companies’ own profits, and with private capital withdrawing little by little, the most sensible option for those companies is to brake while invoking a concern for humanity rather than declare that the goal is still very far away. The brake reduces what has to be financed, because training less and building fewer data centers costs less; it is presented as prudence; and it calms markets in comparison with the alternative, which would be to admit that the promise is distant and would trigger a massive flight of capital from the industry. That political backing is at its maximum does not change the calculation: the White House action plan orders the government to “dismantle unnecessary regulatory barriers,” sums up its infrastructure policy as “Build, Baby, Build!” and asks that federal funds not be directed to states “with burdensome AI regulations” (The White House, 2025b), and Executive Order 14365 creates a litigation task force “to challenge State AI laws” (The White House, 2025a); but a government that removes obstacles does not put in money, and money is what is lacking.

    Three features of the safety discourse confirm that it functions as an instrument for managing capital. The first is that the regulation the companies ask for is an alibi and a moat, not a cost. In March 2023 OpenAI’s chief executive did not sign the letter calling for a six-month pause in the training of systems more powerful than GPT-4 (Future of Life Institute, 2023; Fung, 2023), but in May he asked the Senate for an agency that could “issue licenses and can take them away” (Goldman, 2023), signed the statement according to which mitigating the risk of extinction from artificial intelligence should be a global priority (Center for AI Safety, 2023), and kept accelerating: venture capital to artificial intelligence companies approached 50 billion that year (Metinko, 2024). Not even a plain admission of a bubble scared capital away: when the same executive said in August 2025 that investors were “overexcited,” the Nasdaq fell 1.4% in a day and the money kept coming in (Shibu, 2025; Nusca, 2025). In 2025 Anthropic endorsed California’s SB 53, which requires publishing risk frameworks and limits nothing, and declared that it prefers a federal standard (Anthropic, 2025). Disclosure-based regulation is easily paid for by the incumbent and cannot be paid for by the entrant or the open-weight model. The second feature is that the discourse of danger does not declare the goal distant: it declares it so near that it must be paced. Lee Vinsel called “criti-hype” the kind of criticism that “both feeds and feeds on hype,” because it retains the picture of extraordinary change and merely flips its sign (Vinsel, 2026). Anthropic’s chief executive wrote in January that a powerful artificial intelligence could be “as little as 1–2 years away” (Amodei, 2026a) and in September that within “6–12 months” a swarm of agents could take over the entire internet (Ma, 2026a). That nearness is what sustains the valuation while monetization is re-dated. The third feature is the most revealing: the company with the worst finances released the brake rather than pressing it when it needed capital most. In April 2026 OpenAI rewrote its principles, and the new version omits the 2018 commitment to “stop competing with and start assisting” an aligned project that came close to the goal first (OpenAI, 2018; Goel, 2026).

    The premise that artificial general intelligence is far away cannot be verified today, and that does not make it illegitimate: it implies a risky forecast that makes it falsifiable over time. If the promised capabilities arrive within the timeframes their own promoters announce, the thesis loses; if the brake “for safety” is prolonged or renewed while the promise does not arrive and valuations are not validated by profits, the thesis wins.

    What happened between June and September 2026 is the test case. In June, OpenAI was leaning toward postponing its stock market debut (Ma, 2026b). Between late June and mid-July, during internal cybersecurity evaluations, some 1,200 OpenAI agents improvised a message board, exchanged more than 70,000 messages, and about 700 of them compromised Hugging Face infrastructure, according to the independent investigation by METR, which describes “genuine goal-directed autonomy” and attempts to falsify their own records (METR, 2026); OpenAI acknowledged that “it should be assumed that such attacks are a credible near-term threat” (Reuters, 2026). In late July, OpenAI’s chief executive said that “We may have to pace the rate of AI development” (Fernholz, 2026), more than a thousand laboratory workers asked for “the option to buy time,” and a reporter observed that, despite the statements, “OpenAI has not actually slowed down the pace” (Cerullo, 2026). On September 12, Anthropic’s essay called on the industry to “slow the pace at which we improve the capabilities of AI models,” clarified that pacing “does not mean halting model training or technical progress,” and promised to do so “without sacrificing commercial advantage or the United States’ lead in AI” (Amodei, 2026b); the same day, OpenAI’s chief executive declared that, “given everything happening with safety, right now would be an ‘ill-advised moment’ to go public” and that the listing would not happen in 2026 (Ma, 2026b; Shontell, 2026); Elon Musk replied “Dario is right,” and the chief executive of Google DeepMind, Demis Hassabis, that “The direction is correct” (Mowshowitz, 2026). On Monday the 14th, the S&P 500 fell 0.5% and the Nasdaq 0.56%, but the fall was concentrated in chipmakers, with the Philadelphia semiconductor index down 6%, while Alphabet rose 2%, Microsoft 1.6% and Meta 1.4% (O’Donnell et al., 2026; Roytburg, 2026). One analyst put it bluntly: “They’ll just all stop building data centers and just digest what they have” (Roytburg, 2026).

    “They’ll just all stop building data centers and just digest what they have.”Gil Luria, Wall Street analyst, quoted in Roytburg (2026)

    That reaction is the signature of the mechanism. Those who sell fixed capital fell and those who stop buying it rose: the market read the brake as a coordinated restraint of investment, and a restraint of investment reduces what has to be financed. The postponement of OpenAI’s stock market debut, decided before the incident and narrated afterward as safety, is the mechanism with a date on it. That the narrative rests on a real incident makes it more effective, not less of a narrative. And Anthropic’s listing remains on track for October, at a valuation the Financial Times puts at 2 trillion (Stanciuc, 2026): whoever can show revenue does not brake its capitalization, and whoever cannot postpones it in the vocabulary of prudence. One contractual detail serves as a thermometer: 35 billion of Amazon’s investment in OpenAI was conditioned on the company either going public or achieving artificial general intelligence by the end of the year (Brandom, 2026); if that condition is renegotiated at no cost, the brake costs no one any capital.

    It helps to name the mechanism by its classical category. The rate of profit is the profit obtained in a period divided by the capital advanced to obtain it, that is, how much each invested dollar yields, and it is the variable that governs accumulation: capital flows to where that rate rises and withdraws from where it falls. The artificial intelligence buildout inflates the denominator at an unprecedented speed, with hyperscaler investment that J.P. Morgan estimates at 697 billion in 2026 alone on equipment that depreciates within a few years (J.P. Morgan, 2026), while the numerator grows more slowly and, in the model layer, is squeezed by the Chinese competition described in the first section. That combination is a fall in the rate of profit on the capital invested in artificial intelligence, and when the rate falls, accumulation slows, whether by coordinated decision of the large capitals or by the withdrawal of credit. To stop enlarging the denominator and to exploit what is already installed is, literally, to “digest what they have.” The apocalypse that OpenAI, Anthropic and Musk are warning about is the form in which that defense of the rate of profit presents itself to the public.

    Two objections deserve an answer. The first is that the brake might be sincere. It might be, and the thesis does not deny it: it claims that its economic function is the one described, whatever the conviction of the person announcing it. Other readers have offered kindred or harsher readings: Ben Thompson considers it “an unrealistic proposal that seems mostly geared to political control of AI” (Thompson, 2026), and Zvi Mowshowitz catalogs the readings of regulatory capture, stock market timing and computing limits, even though he dismisses them (Mowshowitz, 2026). The second objection is Chinese competition: a real brake would hand the lead to DeepSeek and Qwen, which, for the reasons given in the first section, have no reason to brake, and the coordination with China that the essay proposes is, under present conditions, unattainable. The answer is that, to be consistent with that competition, the brake has to be a paper brake, that is, external evaluators and alignment timelines without any measurable limit on the pace of training. A paper brake with maximum narrative effect is exactly what the thesis predicts, and it can be checked by reading the pact when it is signed.

    5. What to Watch in the Coming Weeks

    A thesis is worth what its risks of refutation are worth. Five observable facts in the coming weeks put it to the test.

    1. Anthropic’s public prospectus. If it describes the pacing commitment as immaterial to its revenue, the brake does not touch the till.
    2. Amazon’s conditional tranche in OpenAI. If the 35 billion is released or renegotiated without a listing or an artificial general intelligence, the brake costs no capital.
    3. The capital expenditure guidance of Alphabet, Microsoft, Meta and Amazon in their third-quarter results at the end of October. A coordinated cut would confirm the reading of investment restraint.
    4. The credit default swaps of CoreWeave and Oracle. If they keep widening while the hyperscalers rise, the devaluation begins on the leveraged periphery.
    5. The content of the pact among laboratories. If it sets evaluators rather than verifiable limits on pace, the brake is form without substance.

    6. Conclusion

    What happened this weekend is best understood as an agreement among those who buy chips to run a little slower, expressed in the language of safety: it improves their cash position, it worsens the sales of those who sell chips, and the market read it that way in a single day. The part of the argument that the evidence confirms most strongly is that the discourse of danger sustains the valuation rather than sinking it, because it asserts that the goal is near. The part that remains open is the part that ought to remain open: if artificial general intelligence arrives within the timeframes its promoters announce, this article will have been wrong; if the brake is renewed while the promise recedes, it will not have been a brake for humanity but a defense of the rate of profit, that is, a way of managing capital in retreat.

    · · ·

    References

    Alphabet Inc. (2026a, February 4). Alphabet announces fourth quarter and fiscal year 2025 results [Exhibit 99.1 to Form 8-K]. U.S. Securities and Exchange Commission. https://www.sec.gov/Archives/edgar/data/1652044/000165204426000012/googexhibit991q42025.htm

    Alphabet Inc. (2026b, July 22). Alphabet announces second quarter 2026 results [Exhibit 99.1 to Form 8-K]. U.S. Securities and Exchange Commission. https://www.sec.gov/Archives/edgar/data/1652044/000165204426000066/googexhibit991q22026.htm

    Amodei, D. (2026a, January). The adolescence of technology. https://darioamodei.com/essay/the-adolescence-of-technology

    Amodei, D. (2026b, September). We must pace the frontier. https://darioamodei.com/post/we-must-pace-the-frontier

    Anthropic. (2025, September 8). Anthropic is endorsing SB 53. https://www.anthropic.com/news/anthropic-is-endorsing-sb-53

    Artificial Analysis. (n.d.). LLM leaderboard: Intelligence Index. Retrieved September 14, 2026, from https://artificialanalysis.ai/leaderboards/models

    Azevedo, M. A. (2026, April 2). Sector snapshot: Venture funding to foundational AI startups in Q1 was double all of 2025. Crunchbase News. https://news.crunchbase.com/venture/foundational-ai-startup-funding-doubled-openai-anthropic-xai-q1-2026/

    Bank of England. (2025, October 8). Record of the Financial Policy Committee meeting on 2 October 2025. https://www.bankofengland.co.uk/financial-policy-committee-record/2025/october-2025

    Bellan, R. (2025, November 4). Anthropic projects 70B in revenue by 2028: Report. TechCrunch. https://techcrunch.com/2025/11/04/anthropic-expects-b2b-demand-to-boost-revenue-to-70b-in-2028-report/

    Bellan, R. (2026a, May 28). Anthropic raises 65 billion, nears 1T valuation ahead of IPO. TechCrunch. https://techcrunch.com/2026/05/28/anthropic-raises-65-billion-nears-1t-valuation-ahead-of-ipo/

    Bellan, R. (2026b, March 31). OpenAI, not yet public, raises 3B from retail investors in monster 122B fund raise. TechCrunch. https://techcrunch.com/2026/03/31/openai-not-yet-public-raises-3b-from-retail-investors-in-monster-122b-fund-raise/

    BenchLM. (2026, September 14). Artificial Analysis Intelligence Index leaderboard. https://benchlm.ai/benchmarks/artificialanalysis

    Brandom, R. (2026, February 27). OpenAI raises 110B in one of the largest private funding rounds in history. TechCrunch. https://techcrunch.com/2026/02/27/openai-raises-110b-in-one-of-the-largest-private-funding-rounds-in-history/

    Cabili, C. (2026, July 22). Alphabet Q2 2026 earnings: Revenue up 24%, Cloud surges 82%. Quartz (via Yahoo Finance). https://finance.yahoo.com/markets/stocks/articles/alphabet-q2-2026-earnings-revenue-203058727.html

    Caixin Global. (2026, July 22). DeepSeek reaches 52 billion valuation in round backed by Tencent, CATL. Caixin China Watch. https://caixinchinawatch.substack.com/p/deepseek-reaches-52-billion-valuation

    Cboe Global Markets. (2025, November 18). Cboe to launch trading of Cboe Magnificent 10 Index futures and options on December 8 [Press release]. https://ir.cboe.com/news/news-details/2025/Cboe-to-Launch-Trading-of-Cboe-Magnificent-10-Index-Futures-and-Options-on-December-8/default.aspx

    Center for AI Safety. (2023, May 30). Statement on AI risk. https://www.safe.ai/work/statement-on-ai-risk

    Cerullo, M. (2026, July 29). Workers at leading AI companies call for a slowdown in AI development. CBS News. https://www.cbsnews.com/news/slow-down-ai-development-sam-altman/

    ChinaTalk. (2024, November 27). Deepseek: The quiet giant leading China’s AI race [Translation of a July 2024 interview of Liang Wenfeng by An Yong for 36Kr]. https://www.chinatalk.media/p/deepseek-ceo-interview-with-chinas

    CME Group. (2026, June 30). CME Group to launch single stock futures on July 27 [Press release]. PR Newswire. https://www.prnewswire.com/news-releases/cme-group-to-launch-single-stock-futures-on-july-27-302814053.html

    Cook, L. D. (2026, May 27). The opportunities and risks AI presents for the economy and financial system [Speech]. Board of Governors of the Federal Reserve System. https://www.federalreserve.gov/newsevents/speech/cook20260527a.htm

    DeepSeek-AI. (2025, February). Day 6: One more thing, DeepSeek-V3/R1 inference system overview [Repository document]. GitHub. https://github.com/deepseek-ai/open-infra-index/blob/main/202502OpenSourceWeek/day_6_one_more_thing_deepseekV3R1_inference_system_overview.md

    Duprey, R. (2026, January 31). Is the stalled Nvidia-OpenAI megadeal AI’s first domino to fall? 24/7 Wall St. (via Yahoo Finance). https://finance.yahoo.com/news/stalled-nvidia-openai-megadeal-ai-131959187.html

    Fernholz, T. (2026, July 28). Sam Altman is ready to decelerate. TechCrunch. https://techcrunch.com/2026/07/28/sam-altman-is-ready-to-decelerate/

    Fung, K. (2023, March 29). The massive name in AI noticeably absent from pause letter signed by Musk. Newsweek. https://www.newsweek.com/massive-name-ai-noticeably-absent-pause-letter-signed-musk-1791303

    Future of Life Institute. (2023, March 22). Pause giant AI experiments: An open letter. https://futureoflife.org/open-letter/pause-giant-ai-experiments/

    Goel, S. (2026, April 27). OpenAI just updated its principles. Here’s what changed since the original version, 8 years ago. Business Insider (via AOL). https://www.aol.com/articles/openai-just-updated-principles-heres-041316000.html

    Goldman, S. (2023, May 16). In Senate testimony, OpenAI CEO Sam Altman agrees with calls for an AI regulatory agency. VentureBeat. https://venturebeat.com/ai/in-senate-testimony-openai-ceo-sam-altman-agrees-with-calls-for-an-ai-regulatory-agency

    Greenberg, G. (2026, January 9). Advisors confront Magnificent 7 concentration risk in portfolios. InvestmentNews. https://www.investmentnews.com/equities/mag-7-for-tomorrow/264753

    Jadhav, A. (2025, November 4). Nvidia, OpenAI, and the trillion-dollar loop. The Register. https://www.theregister.com/special-features/2025/11/04/nvidia-openai-and-the-trillion-dollar-loop/353799

    J.P. Morgan. (2026, August 10). Financing AI infrastructure and U.S. data centers. https://www.jpmorgan.com/insights/banking/capital-markets/financing-ai-infrastructure-data-centers

    Juniewicz, I. (2026, June 5). The AI boom has doubled computing infrastructure’s share of US GDP. Epoch AI. https://epoch.ai/data-insights/ai-datacenter-share-gdp

    Korosec, K. (2026, June 1). Anthropic files to go public. TechCrunch. https://techcrunch.com/2026/06/01/anthropic-files-to-go-public/

    Lipschultz, B., & Metz, R. (2026, August 15). Anthropic revenue surges to over 11.5 billion in second quarter. Fortune. https://fortune.com/2026/08/15/anthropic-revenue-q2-11-5-billion-ipo-investors/

    Lo, K. (2026, January 6). Chinese AI unicorns beat Silicon Valley giants in race to go public. Rest of World. https://restofworld.org/2026/zhipu-ai-minimax-ipo/

    Luong, N. (2026, March 23). Two loops: How China’s open AI strategy reinforces its industrial dominance [Research working group paper]. U.S.-China Economic and Security Review Commission. https://www.uscc.gov/sites/default/files/2026-03/Two_Loops–How_Chinas_Open_AI_Strategy_Reinforces_Its_Industrial_Dominance.pdf

    Ma, J. (2026a, September 13). Anthropic CEO Dario Amodei and AI whistleblower Jacob Coxon agree: Something terrifying could happen in a matter of months. Fortune. https://fortune.com/2026/09/13/anthropic-dario-amodei-ai-whistleblower-jacob-coxon-openai-sam-altman-recursive-self-improvement/

    Ma, J. (2026b, September 12). Sam Altman confirms OpenAI won’t go public this year, saying an IPO now would come at an ‘ill-advised moment’ given AI safety concerns. Fortune. https://fortune.com/2026/09/12/sam-altman-openai-ipo-delay-ill-advised-moment-safety-concerns/

    Mann, T. (2026, August 3). China turns up the heat with open model blitz as US model makers panic. The Register. https://www.theregister.com/ai-and-ml/2026/08/03/china-turns-up-the-heat-with-open-model-blitz-as-us-model-makers-panic/5282526

    MarketScale Newsroom. (2026, August 17). AI data center debt is getting more expensive, and Meta’s 12.5 billion El Paso deal proves it. MarketScale. https://www.marketscale.com/industries/energy/ai-data-center-debt-is-getting-more-expensive-and-metas-125-billion-el-paso-deal-proves-it

    Metinko, C. (2024, January 9). Artificial buildup: AI startups were hot in 2023, but this year may be slightly different. Crunchbase News. https://news.crunchbase.com/ai/hot-startups-2023-openai-anthropic-forecast-2024/

    METR. (2026, August 26). Brief independent investigation of agents’ behavior, reasoning and collaboration in the OpenAI / Hugging Face hacking incident. https://metr.org/blog/2026-08-26-openai-hugging-face-incident-investigation/

    Microsoft. (2025, November 18). Microsoft, NVIDIA and Anthropic announce strategic partnerships. Microsoft Corporate Blogs. https://blogs.microsoft.com/blog/2025/11/18/microsoft-nvidia-and-anthropic-announce-strategic-partnerships/

    Moadel, D. (2026, July 29). Nebius drops 10%, CoreWeave sinks 9% as rising credit-swap costs hit the AI cloud trade. 24/7 Wall St. https://247wallst.com/investing/2026/07/29/nebius-drops-10-coreweave-sinks-9-as-rising-credit-swap-costs-hit-the-ai-cloud-trade/

    Mowshowitz, Z. (2026, September 14). We must pace the frontier. Don’t Worry About the Vase. https://thezvi.wordpress.com/2026/09/14/we-must-pace-the-frontier/

    Nusca, A. (2025, August 20). Just don’t call it an AI bubble. Fortune. https://www.fortune.com/2025/08/20/just-dont-call-it-an-ai-bubble

    O’Donnell, G., Hollerith, D., & Ferré, I. (2026, September 14). Stock market today: Dow, S&P 500, Nasdaq slip as chip stocks fall on AI warning. Yahoo Finance. https://finance.yahoo.com/markets/live/stock-market-today-monday-september-14-dow-sp-500-nasdaq-080559558.html

    OpenAI. (2018, April 9). OpenAI charter [Copy hosted by ETO AGORA, Emerging Technology Observatory]. https://agora.eto.tech/instrument/767

    Park, L. (2026, August 21). Anthropic’s Q2 revenue overtook OpenAI for the first time – and reached its first positive operating income. Forkast News (via Yahoo Finance). https://finance.yahoo.com/technology/ai/articles/anthropic-q2-revenue-overtook-openai-015716271.html

    Pichai, S. (2026, February 4). Q4 earnings call: Remarks from our CEO. The Keyword (Google). https://blog.google/company-news/inside-google/message-ceo/alphabet-earnings-q4-2025/

    Reuters. (2025, September 6). Report: OpenAI expects business to burn 115 billion through 2029. Yahoo Finance. https://finance.yahoo.com/news/openai-expects-business-burn-115-022035561.html

    Reuters. (2026, August 26). OpenAI agents hacked Hugging Face in 700-strong swarm, tried to cover tracks, investigations find. NBC News. https://www.nbcnews.com/tech/tech-news/openai-report-says-network-was-hacked-rogue-ai-agents-rcna594590

    Roberts, L. (2025, November 20). Oracle and CoreWeave credit default swap spreads widening: Omen or jitters? Investing.com. https://www.investing.com/analysis/oracle-and-coreweave-credit-default-swap-spreads-widening-omen-or-jitters-200670506

    Roytburg, E. (2026, September 14). Wall Street’s AI doomsday trade is here: Chipmakers sink while hyperscalers gain. Fortune. https://fortune.com/2026/09/14/ai-slowdown-stocks-nvidia-meta/

    Schepkov, V. (2025, November 5). Nvidia’s Huang warns China will win AI race amid energy costs, regulations. Investing.com (via Yahoo Finance). https://finance.yahoo.com/news/nvidia-huang-warns-china-win-223321776.html

    Shibu, S. (2025, August 19). OpenAI CEO Sam Altman thinks we’re in an AI bubble because investors are ‘overexcited’ about artificial intelligence. Entrepreneur. https://www.entrepreneur.com/business-news/openais-sam-altman-warns-overexcited-investors-ai-bubble/496081

    Shontell, A. (2026, September 12). Exclusive: Sam Altman addresses AI doomsday fears in new interview. Fortune. https://fortune.com/2026/09/12/sam-altman-interview-ai-doomsday-safety-models-control-ipo-2027/

    Stanciuc, A.-M. (2026, August 15). Anthropic’s quarterly revenue passed 11.5bn, up more than 14-fold. The Next Web. https://thenextweb.com/news/anthropic-q2-2026-revenue-11-5-billion-operating-income

    Subran, L., Dejean, G., Hirt, A., Utermöhl, K., & Bartosch, M. (2026, March 25). What to watch. Allianz Research. https://www.allianz.com/content/dam/onemarketing/azcom/Allianz_com/economic-research/publications/specials/en/2026/march/2026_03_25_AI.pdf

    The White House. (2025a, December 11). Ensuring a national policy framework for artificial intelligence [Executive Order 14365]. https://www.whitehouse.gov/presidential-actions/2025/12/eliminating-state-law-obstruction-of-national-artificial-intelligence-policy/

    The White House. (2025b, July). Winning the race: America’s AI action plan. https://www.whitehouse.gov/wp-content/uploads/2025/07/Americas-AI-Action-Plan.pdf

    Thompson, B. (2026, September 14). Pacing the frontier, AI’s digital limits, AI commissars. Stratechery. https://stratechery.com/2026/pacing-the-frontier-ais-digital-limits-ai-commissars/

    Tolomia, C. (2026, June 16). OpenAI 2025 financials leaked: 38.5B loss ahead of IPO. Quartz (via Yahoo Finance). https://finance.yahoo.com/markets/stocks/articles/openai-2025-financials-leaked-38-121508294.html

    Vinsel, L. (2026, February 8). You’re doing it wrong: Notes on criticism and technology hype [Repost of a 2021 Medium essay]. Peoples & Things. https://peoples-things.ghost.io/youre-doing-it-wrong-notes-on-criticism-and-technology-hype/

    Wells, M. (2026). Will AI investments pay off? Econ Focus, First/Second Quarter 2026. Federal Reserve Bank of Richmond. https://www.richmondfed.org/publications/research/econ_focus/2026/q1-q2_feature2

    Yildirim, E. (2025, November 6). Nvidia CEO says China ‘will win’ the global AI race as the U.S. falls behind in energy. Gizmodo. https://gizmodo.com/nvidia-ceo-says-china-will-win-the-global-ai-race-as-the-u-s-falls-behind-in-energy-2000682396

    Zeoli, C. (2026, June). China’s open-weight takeover. Wing Venture Capital. https://www.wing.vc/content/chinas-open-weight-takeover

    Zitron, E. (2026a, May 21). Anthropic’s “profitability” swindle. Where’s Your Ed At. https://www.wheresyoured.at/anthropics-profitability-swindle/

    Zitron, E. (2026b, June 15). Exclusive: OpenAI losses increased nearly 8X in 2025, with spending hitting 34 billion. Where’s Your Ed At. https://www.wheresyoured.at/exclusive-openai-financials/

    September 14, 2026
  • Navier-Stokes, LLMs and the Ownership of Ideas

    Navier-Stokes, LLMs and the Ownership of Ideas

    Mathematics · Artificial intelligence · Open science

    Navier–Stokes, LLMs, And The Ownership of Ideas

    What OpenAI claims to have proved, why external forcing does count, and what the dispute reveals about the social future of mathematics.

    One of the most important and unusual mathematical stories in recent years began with a rumor. Two researchers, Tristan Buckmaster and Levent Alpöge, had spent months developing a line of work on singularities in fluid equations. OpenAI learned that something important was happening, deployed thousands of artificial-intelligence agents, and, a few days later, announced a proof of finite-time blowup for the three-dimensional Navier–Stokes equations.

    The story contains every ingredient needed to produce confusion: one of the seven Millennium Prize Problems, a 166-page proof, a Lean formalization, about 10,000 agents, 130 billion tokens, allegations concerning scientific credit and private data, and a conversation that Buckmaster interpreted as pressure against his career. Understanding it requires resisting two simplifications. The first is that a large amount of computation automatically turns a result into scientific understanding. The second is that the presence of an external force makes the proof irrelevant. Neither is correct.

    1. The fundamental question

    The Navier–Stokes equations describe the motion of viscous fluids such as water and air. They do not track molecules one by one. They treat the fluid as a continuous medium and relate its velocity, pressure, viscosity, and external forces:

    ∂u/∂t + (u · ∇)u = −∇p + νΔu + f,    ∇ · u = 0 u is velocity; p, pressure; ν, viscosity; and f, an external force. The second equality says that the fluid is incompressible.

    In three dimensions, we know how to construct global weak solutions, meaning solutions that satisfy the equations in a generalized sense. What we did not know was whether smooth initial data always produce smooth solutions for all time or whether, under some conditions, velocity can grow without bound in finite time. Such a loss of regularity is called a singularity or blowup.

    Viscosity tends to smooth differences in velocity. The nonlinearity represented by (u · ∇)u transports and reorganizes motion. The problem arises from the competition between these effects: is viscous dissipation always enough to prevent infinite concentration, or can three-dimensional dynamics overcome it?

    A physical clarification

    Mathematically infinite velocity does not mean that a real fluid literally reaches that speed. It means that the continuum model loses regularity and ceases to describe the process in the classical way. The question is mathematical, although it is physically motivated.

    2. What Clay actually requires

    The first indispensable correction belongs here. The official statement written by Charles Fefferman offers four routes. Alternatives A and B ask for global existence and smoothness when the external force is zero, respectively in the whole space and on the three-dimensional torus. Alternatives C and D permit a disproof of global regularity using smooth initial data and an external force that is also smooth, on those same two domains.

    AlternativeDomainExternal forceWhat must be shown
    Aℝ³f = 0Global existence and smoothness for every admissible initial datum.
    B𝕋³f = 0The same global regularity in the periodic case.
    Cℝ³smooth fExistence of data and a force for which there is no global classical solution of bounded energy.
    D𝕋³smooth fThe corresponding breakdown in the periodic case.

    Calling smooth forcing a “loophole” foreign to the prize is therefore technically incorrect. It is written into alternatives C and D. If OpenAI’s proof is correct and satisfies the stated hypotheses exactly, it would resolve the official problem through those routes.

    That does not make all results equivalent. Producing a singularity with a specially constructed smooth force is a more limited claim, physically and conceptually, than proving a singularity without external forcing. It resolves the institutional disjunction formulated by Clay, but it does not answer the stronger question of whether unforced dynamics can break down by themselves. Both statements can be true at once, without verbal tricks.

    External forcing does not formally invalidate the result; it precisely limits what the result teaches us about fluids.

    3. What OpenAI claims to have proved

    OpenAI’s paper constructs, for every positive viscosity, velocity and pressure fields that remain smooth up to a critical time, together with a smooth, compactly supported force f(x,t). The fluid starts from rest. As time approaches t = 1, the maximum velocity grows without bound, while total kinetic energy remains uniformly bounded.

    The intuitive picture is a vortex that spins, stretches, and concentrates around an increasingly small region. Velocity can rise indefinitely without total energy blowing up because the volume occupied by the extreme motion contracts quickly enough. An imperfect but useful analogy is to concentrate ever greater intensity inside ever less space.

    It is not enough to draw a singular flow and then define an infinite force to sustain it. That would be trivial and inadmissible. The difficulty is to make acceleration, pressure, nonlinear transport, and viscosity develop large terms that cancel with extraordinary precision, leaving a perfectly smooth external force as the residual. The paper introduces oscillatory pulses whose internal momentum transport corrects the singular parts of that residual.

    Exact scope of the announced theorem
    • Yes: finite-time velocity blowup for three-dimensional Navier–Stokes with positive viscosity and a smooth external force.
    • Yes: uniformly bounded total kinetic energy throughout the evolution.
    • Yes, conditional on the proof being correct: alternatives C and D of the official formulation.
    • No: a proof of blowup for unforced Navier–Stokes.
    • Not yet: stable, independent acceptance by the mathematical community.

    4. How the agents worked

    According to OpenAI, on September 1 it assigned groups of agents to the different alternatives of several Millennium Prize Problems. The agents could consult a cached copy of the internet, run code, divide into groups, and communicate results. Nearly one hundred agents reportedly first produced a solution for unforced Euler. From that result, the company concentrated its resources on Navier–Stokes.

    The decisive group eventually involved on the order of 10,000 concurrent agents. OpenAI reports that the result appeared after about 88 hours; Lean formalization and verification took an additional 17 hours using GPT-6 Astra. The Navier–Stokes effort reportedly involved 2.7 million messages and approximately 130 billion generated tokens.

    These numbers neither prove understanding nor disprove it. They describe a new material organization of intellectual labor: massively parallel search, route selection, circulation of partial results, automated consolidation, and formal verification. Reducing this to “mere brute force” hides the coordination architecture; presenting it as an autonomous mathematical consciousness hides the human, bibliographical, and computational scaffolding that made the search possible.

    5. The mathematical genealogy

    The route did not emerge in a vacuum. Buckmaster attributes the basic idea of the program to Diego Córdoba and Luis Martínez-Zoroa, who had spent years studying forced blowups and mechanisms of amplification across scales. Their work had shown how increasingly concentrated layers of vorticity could amplify while the regularity of the force remained controlled.

    Buckmaster and Alpöge took that program as their starting point. With extensive help from Claude and Codex, they say they pushed constructions with rougher forcing to smooth forcing for incompressible porous media, Boussinesq, and three-dimensional incompressible Euler. According to their statement, they obtained the Boussinesq and Euler results on August 15 and verified the first model-generated proof in Lean on August 22.

    OpenAI’s announced contribution would be different and later: a construction for viscous Navier–Stokes. Even if it was obtained independently, its historical meaning cannot be narrated as if 10,000 agents had discovered both the problem and the strategy from a blank page. In mathematics, the final proof is only one part of discovery. Recognizing which simplification to attack, which literature matters, and which mechanism may survive new constraints is genuine intellectual work.

    6. Timeline of the dispute

    Date in 2026Reported eventSource of the claim
    August 15Buckmaster and Alpöge obtain smooth-forcing blowup for Boussinesq and Euler.Buckmaster’s statement.
    August 22Lean verification of their initial proof is completed.Buckmaster’s statement.
    September 1OpenAI hears rumors and launches agents on the Millennium Prize Problems.OpenAI’s account.
    September 3Buckmaster privately writes to a mathematician at OpenAI to clarify the rumor.Email reproduced by Buckmaster.
    September 5OpenAI’s agents reach the Navier–Stokes construction, about 88 hours after starting.OpenAI’s account.
    September 6OpenAI’s formal verification is completed and two calls with Buckmaster take place.OpenAI and Buckmaster’s statement.
    September 8OpenAI publishes the article, proof, and formalization; Buckmaster makes his statement public.The respective publications.

    The temporal proximity fuels the controversy. Buckmaster says that almost nobody was working on this route and that the sudden focus on the forced problem was a warning sign to him. OpenAI responds that it distributed agents among all four official alternatives, first obtained an unforced Euler result, and only then identified Navier–Stokes as the most promising direction.

    7. What is established and what is disputed

    The public discussion mixes verifiable documents, incompatible accounts, and inferences. They should be separated.

    Evidence map
    • Publicly documented: an analytical manuscript, a Lean formalization, Buckmaster’s statement, and OpenAI’s recognition of Buckmaster and Alpöge’s priority on forced Euler all exist.
    • Claimed by OpenAI: no researcher or agent accessed their specific work before publication; the agents reached different results, and the company does not intend to claim the monetary prize.
    • Acknowledged by OpenAI as a remote possibility: it cannot rule out that de-identified data derived from their product usage helped improve its models.
    • Claimed by Buckmaster: he received no clear answer about training; he was offered an arrangement in which he would present OpenAI’s result alone and Alpöge would be excluded because he works at Anthropic; he also received statements he interpreted as professional threats.
    • Disputed: Sébastien Bubeck rejects Buckmaster’s characterization of the interaction, according to the cited public summary.
    • Not publicly established: that private drafts were used to produce OpenAI’s proof or that deliberate appropriation occurred.

    Buckmaster himself draws the boundary: he has not seen how the model worked, does not know whether his data were used, and does not present that accusation as an established fact. His stated purpose is to record what he was told, when he was told it, and what arrangements were proposed. This caution does not erase the questions; it prevents them from being answered through condemnation without evidence.

    OpenAI’s sentence about de-identified data also does not demonstrate a causal transmission from those drafts to the proof. It does reveal a governance problem: for a researcher placing unpublished work in a tool, an inability to exclude aggregate influence may be scientifically unacceptable even if the handling complied with the service’s contractual terms.

    8. What Lean verifies and what it does not

    Lean is a proof assistant. A formalized proof is decomposed into steps that a small kernel checks against precise logical rules. This sharply reduces the chance that an algebraic leap, forgotten hypothesis, or invalid inference survives. In a manuscript over one hundred pages long, that guarantee is extraordinarily valuable.

    Lean does not, by itself, answer four separate questions:

    1. Correspondence: whether the formal theorem exactly represents the informal mathematical claim being announced.
    2. Interpretation: what physical meaning the construction has and how much it depends on a designed force.
    3. Novelty: which ideas are new and which come from prior work.
    4. Credit: who formulated the route, supplied the decisive mechanisms, directed the search, and should be named as an author.

    A logical certificate does not replace a human exposition. The community still needs to read the manuscript, check that the formalization matches the intended theorem, reconstruct the conceptual mechanism, and place it within the literature. Formal verification strengthens the evidence; it does not exhaust scientific practice.

    9. The Deep Blue–Kasparov moment

    Buckmaster calls the episode a “Deep Blue–Kasparov moment.” The comparison does not mean that mathematics is simply another game with closed rules. It points to a change in the division of labor: a machine can explore, combine, and verify on a timescale inaccessible to a person, but its productivity depends on a search space structured by historically accumulated problems, theories, formal libraries, and strategies.

    In a major problem, the scarce resource is not always writing the last line of a proof. It may be identifying the promising route after years of failed attempts. If revealing that route allows thousands of agents to be mobilized immediately, then selecting the correct intermediate question acquires enormous economic and scientific value.

    When proving becomes massively parallelizable, knowing what should be proved may become the scarcest part of discovery.

    This could make open mathematics more guarded. A seminar, an informal draft, or a query submitted to a platform would cease to be only an act of cooperation and could become an exploitable signal for institutions with incomparable computing capacity. The danger is not that artificial intelligence participates in science, but that openness becomes unilateral: open knowledge from below and closed appropriative capacity from above.

    10. A materialist reading

    The controversy cannot be understood solely as a moral conflict between individuals. It expresses a transformation in the productive forces of science. Models, data centers, multi-agent systems, and proof assistants turn enormous quantities of accumulated mathematical labor into an infrastructure capable of producing new combinations at great speed.

    At the same time, this productive force is concentrated under private ownership. Researchers contribute questions, drafts, corrections, usage, and tacit knowledge; the platform aggregates those interactions, improves closed models, and can return results at a scale no academic group can match. Even if no concrete appropriation occurred in this case, the structural asymmetry is real.

    The decisive question is therefore not whether the machine “really thought,” a metaphysical dispute of little use. We should ask who controlled the means of mathematical production, who chose the direction of the search, which prior labor it depended on, who could audit the process, and how authorship, prestige, and benefits were distributed.

    Computation is not an epistemological sin. It can be a genuine force of discovery. But a new productive force organized through opaque relations of ownership and credit can simultaneously expand human capacity and deepen the subordination of scientific labor.

    11. The rules we need

    The episode suggests a minimum program for AI-assisted research:

    1. Verifiable confidential mode: unpublished drafts should be excluded from training and any later retrieval, with auditable guarantees rather than contractual language alone.
    2. Provenance: every major result should retain a record of models, versions, consulted sources, decisive prompts, human intervention, and circulation among agents.
    3. Compute disclosure: the number of agents, tokens, time, tools, and selection criteria should be treated as part of the method.
    4. Conceptual genealogy: attribution should recognize not only the final proof but also the prior program that made the route visible.
    5. Authorship independent of corporate rivalry: a researcher’s affiliation cannot justify excluding a scientifically relevant contribution.
    6. Open review: the manuscript, formalization, and correspondence between them should remain available for independent examination.
    7. Public capacity: universities and scientific consortia need shared infrastructure so frontier mathematics does not depend exclusively on private laboratories.

    These rules would not settle every priority dispute, but they would make verifiable questions that currently depend on conflicting institutional statements.

    12. Conclusion

    Did OpenAI solve Navier–Stokes? The rigorous answer is conditional. The announced theorem targets precisely alternatives C and D of Clay’s official statement. The use of a smooth external force is not a trick, although it leaves the stronger unforced question untouched. If the analytical proof and its formalization withstand expert review, the mathematical achievement would be historic.

    But the result did not arise outside history. It depends on decades of analysis, on the route opened by Córdoba and Martínez-Zoroa, on the work of Buckmaster and Alpöge, and on extraordinary computational infrastructure. Nor have the questions about data, priority, and institutional pressure been resolved. The most serious allegations should be investigated, neither repeated as facts nor dissolved through public relations.

    The deepest lesson is that artificial intelligence does not abolish the social relations of science. It makes them more visible. The more powerful automated proving becomes, the more important it will be to protect openness, record the provenance of ideas, and distribute credit fairly. The future of mathematics will depend not only on what machines can prove, but on the institutions through which we decide what it means to discover.

    References

    Specified sources

    1. Julien Cadot, public summary of the controversy.https://x.com/juliencdt/status/2097582940863471900
    2. Mehdi, technical, epistemological, and political criticism of the announcement.https://x.com/bettercallmedhi/status/2097472336257863722
    3. Susan Zhang, reproduction of the manuscript and OpenAI’s statement on concurrent work.https://x.com/suchenzang/status/2097376584814432392
    4. Tristan Buckmaster, statement on the results and conversations with OpenAI.https://cims.nyu.edu/~tristanb/statement.pdf
    5. Jasper, mathematical analysis and summary of the dispute.https://x.com/zjasper/status/2097547276755619988

    Primary technical documents

    1. OpenAI, “On the Navier–Stokes Millennium Prize Problem.”https://openai.com/index/navier-stokes-solution/
    2. OpenAI, “Finite Time Blowup for Navier–Stokes.”Analytical manuscript
    3. Charles L. Fefferman, official formulation of the Millennium Prize Problem.Existence and Smoothness of the Navier–Stokes Equation
    4. OpenAI, Lean formalization.https://github.com/openai/NavierStokesAndEuler
    ↑ Back to top
  • GENERALITIES ON THE STATISTICAL THEORY OF SAMPLING SURVEYS

    GENERALITIES ON THE STATISTICAL THEORY OF SAMPLING SURVEYS

    SURVEY THEORY · SAMPLING · INFERENCE

    GENERAL REMARKS ON THE STATISTICAL THEORY OF SAMPLE SURVEYS

    Conceptual and technical notes on sampling design, frames, inclusion probabilities, design-based inference, nonresponse, total survey error, and estimation.

    The statistical theory of sample surveys is often learned as a collection of procedures: how to select a sample, how to estimate a total or a mean, how to calculate a standard error, and how to determine a sample size. Yet behind every procedure lies a prior conceptual question. What, exactly, is the population to which one intends to infer? What entity is selected at each stage? What makes a selection probabilistic? Where does the randomness that allows us to speak of precision come from? What part of survey error can be quantified by the sampling design, and what part arises from coverage, nonresponse, measurement, or processing?

    The notes that follow are organized around problems that appear in the classical sampling literature—in particular Cochran (1977), Kish (1965), and Lohr (2021)—and develop them using tools from contemporary survey theory. The purpose is not to replace the reading of those authors, but to make explicit the conditions under which their statements acquire meaning and the operational consequences that follow from them. When more recent terminology is introduced, the discussion draws especially on Särndal, Swensson, and Wretman (1992), Groves et al. (2009), Biemer and Lyberg (2003), AAPOR’s definitions, and the guidelines of the United Nations, Statistics Canada, and the U.S. Census Bureau.

    · · ·

    1. In a survey, are costs high and administration more complex?

    The answer necessarily depends on the basis of comparison. Relative to a census, a sample survey is usually cheaper and administratively less demanding because it observes only part of the population. Indeed, one of the historical reasons for the development of probability sampling was to obtain sufficiently precise information at a cost far below that of a complete enumeration.

    The comparison changes, however, if the reference point is a smaller-scale qualitative procedure, such as a focus group, an in-depth interview, or intensive observation. A national probability survey may require a frame, sample selection, instrument programming, training, supervision, callbacks, weighting, quality control, variance estimation, and documentation. A focus group may be far less expensive, but it does not perform the same inferential function. The choice should not be made by asking which technique is “cheaper” in the abstract, but which technique can answer the research question with the kind of evidence required.

    A focus group, for example, can be extraordinarily useful for understanding vocabularies, categories of meaning, mechanisms, and problems in questionnaire comprehension. It is not, however, a direct substitute for a probability survey when the objective is to estimate parameters of a defined population. Cost must therefore be judged in relation to the objective, the required precision, and the type of inference one intends to make.

    2. What is a double-barreled question?

    It is a question that asks for a single response concerning two or more propositions that could receive different answers. The problem is not that the respondent has “two response options”; almost every closed question does. The problem is that the wording fuses two objects of judgment and forces the respondent to compress them into a single answer.

    For example:

    Are you satisfied with your salary and with your working conditions?

    A person may be satisfied with the salary and dissatisfied with the conditions, or the reverse. If the scale offers only one response, the resulting datum has no univocal interpretation. The solution is to separate the components whenever each constitutes a substantively distinguishable dimension.

    This problem points to something more general: a questionnaire is not a neutral container into which already constituted variables are simply “placed.” The concrete wording helps determine the cognitive operation performed by the respondent and, consequently, the datum that is ultimately recorded. Instrument design is therefore part of measurement and not a purely editorial task.

    3. When is a questionnaire too long?

    There is no universal number of questions or minutes beyond which a questionnaire becomes too long. Burden depends on mode of administration, the cognitive complexity of the questions, the sensitivity of the topics, the population being interviewed, the need to consult records, the number of skip patterns, and the repetition of scales, among other factors.

    A face-to-face interview may tolerate a different duration from a telephone interview or a self-administered online questionnaire. Likewise, twenty questions that require recalling amounts, dates, or episodes may impose a greater burden than fifty very brief and homogeneous items.

    Pilot testing is important, but it should be distinguished from cognitive pretesting. A pilot reproduces field procedures on a smaller scale and makes it possible to study duration, contact rates, skip patterns, logistics, and implementation problems. A cognitive interview, by contrast, seeks to understand how the respondent interprets the question, retrieves the information, and selects an answer. The U.S. Census Bureau also includes usability testing, behavior coding, debriefing, and split-panel designs among questionnaire evaluation tools.

    The methodological rule, therefore, is not “make it as short as possible,” but retain everything needed to measure the defined concepts and remove what adds burden without adding substantive information. Brevity and quality are not synonyms.

    4. What does it mean for a frame to contain duplicates?

    A sampling frame is the operational device through which sampling units can be identified or reached in order to carry out selection. Sometimes it takes the form of a list; at other times it consists of geographic areas, addresses, administrative registers, potential telephone numbers, or procedures that make it possible to generate selectable units.

    Duplication occurs when the same population unit is represented more than once in the selection mechanism. If records are selected with equal probability and one person appears twice, that person may acquire a higher inclusion probability than another who appears only once. But the problem should not be described by saying that any inequality in probabilities makes the sample “nonrandom.” There are deliberately unequal-probability probability designs.

    What matters is to distinguish between inequality that is designed and known and multiplicity that is accidental or unknown. A frame may suffer, among other problems, from:

    • undercoverage: units of the target population that cannot be reached through the frame;
    • overcoverage: records that do not belong to the target population;
    • duplicates or multiplicity;
    • outdated records;
    • units whose status or location has changed;
    • many-to-one or one-to-many relationships between records and target units.

    The statistical consequence depends on the specific selection mechanism and on whether multiplicity can be measured and corrected in the inclusion probabilities or in weighting.

    5. What does data editing mean, and what should be done with nonresponse?

    In survey work, “editing” should not be understood as deleting observations that look strange. Editing consists of applying explicit validity, consistency, and plausibility rules to identify records that require review. An improbable answer is not automatically false, and an inconsistent answer should not disappear without a trace.

    A reasonable workflow always preserves the raw information and separates the stages:

    1. captured datum;
    2. validation of range, type, and structure;
    3. verification of skips and logical consistency;
    4. possible recontact, when feasible and planned;
    5. documented editing rules;
    6. marking of missing values;
    7. possible imputation;
    8. production of the analytical dataset without destroying the original dataset.

    Suppose that a section of the questionnaire applies only to households that reported a certain economic activity. If answers appear in that section for households that, according to the filter question, do not perform that activity, several possibilities exist: the filter was captured incorrectly, the later response was captured incorrectly, the interviewer failed to follow the skip, the definition was misunderstood, or there is a real situation that the design did not anticipate. The datum must be investigated; it is not enough to declare it “obviously wrong.”

    It is also necessary to distinguish states that are often collapsed under a single missing-value label: Not applicable, Don’t know, No answer, explicit refusal, failure to contact, structural skip, capture error, and value lost during processing. “Not applicable” is not simply another form of DK/NR: it means that the question does not belong to the logical universe of that unit.

    Unit nonresponse and item nonresponse

    Unit nonresponse occurs when no substantive information is obtained from a selected unit; item nonresponse occurs when an interview exists but particular answers are missing. The former is often addressed through weighting adjustments and the latter may require imputation or other methods, although the specific techniques depend on the missingness mechanism and the analytical objective (Kalton and Kasprzyk, 1986).

    A high nonresponse rate does not mechanically imply that a variable is unusable, just as a high response rate does not demonstrate the absence of bias. Nonresponse bias depends on the relationship between response propensity and the variables to be estimated.

    AAPOR distinguishes, among other measures, response rate, cooperation rate, contact rate, and refusal rate. They are not synonyms. The response rate relates completed interviews to the set of eligible cases—with variants concerning the treatment of unknown eligibility and partial interviews; cooperation conditions on contacted units; the contact rate studies the success of contact; and the refusal rate quantifies a specific form of nonresponse.

    Finally, interviewer probing must be standardized. The objective is not to press until “no survey is ever left incomplete,” but to facilitate a valid answer without inducing it and while respecting the participant’s consent.

    6. Efficiency, repeated sampling, and confidence intervals

    Cochran frames sampling theory as a joint problem of precision and cost: to design selection and estimation procedures that produce the required accuracy at the lowest possible cost. The underlying idea remains fundamental. There is no “best” design outside an objective; there is a design that is more or less efficient for particular estimands, constraints, and costs.

    The precision of a procedure is studied through its distribution under hypothetical repetitions of the sampling mechanism. From a design-based perspective, the values of the finite population may be regarded as fixed, while randomness arises from the set of samples that the design could have selected.

    Here it is important to state the interpretation of a confidence interval precisely. If a procedure constructs intervals with a 95% confidence level, that does not mean that 95 out of 100 samples will produce “values similar” to the value observed in our sample. It means that, under the assumptions of the procedure and under hypothetical repetitions of the design, approximately 95% of the intervals constructed in that way would contain the fixed population parameter.

    Once a particular frequentist interval has been observed, the parameter does not thereby become random. The randomness belongs to the procedure that would have produced different intervals under other possible samples.

    Sample size for a proportion

    In an elementary formulation under simple random sampling, if an approximate margin of error \(e\) is desired for a proportion at a level associated with \(z_{1-\alpha/2}\), one may begin with

    \[ n_0=\frac{z_{1-\alpha/2}^2\,p(1-p)}{e^2}. \]

    When there is no useful prior information about \(p\) and a conservative case is sought, \(p=0.5\) is used because the function

    \[ p(1-p) \]

    attains its maximum at \(p=0.5\). That is the mathematical reason. It is not a consequence of the central limit theorem.

    If the population is finite and the sampling fraction is no longer negligible, sample size can be adjusted, within the same simple framework, by

    \[ n=\frac{Nn_0}{N+n_0-1}. \]

    In complex designs, the calculation must incorporate stratification, clustering, unequal probabilities, domain objectives, anticipated nonresponse, and, where appropriate, an expected design effect. Sample size should not be calculated in isolation from the design.

    7. Are there “common sample sizes”?

    Recurring sizes—400, 800, 1,000 interviews, for example—do appear in professional practice because certain combinations of precision, budget, and mode recur. But those numbers are not statistical constants.

    For a proportion near 0.5, a simple random sample of about 1,067 units yields, under a normal approximation and without finite population correction, a sampling margin close to three percentage points at 95%. This accounts for part of the recurrence of sample sizes near one thousand in polls. The calculation, however, does not by itself demonstrate that a survey is nationally representative or that its total error is ±3 percentage points.

    If there is clustering, highly variable weights, small domains, unequal response rates, or incomplete coverage, the same \(n\) may yield very different precision and quality. The proper question is not “what is the traditional size?” but “what precision do I need for which estimand, under what design, and at what cost?”

    8. Two differences between classical theory and survey theory

    8.1. Distributions, models, and design

    A classical observation by Cochran contrasts part of model-based statistical inference with survey theory for finite populations. In many classical problems, observations are assumed to arise from a distribution with a specified mathematical form and its parameters are estimated. In survey sampling, by contrast, a central tradition develops inference from the selection mechanism without requiring a parametric distribution for the population values.

    The expression “few measurements on each unit” does not refer to a sample with fewer than 30 or 50 cases. It refers to the number and type of variables measured on each unit. A large survey may collect many variables with very different distributions; there is no reason to impose the same parametric family on all of them merely because the dataset is large.

    Computing power does not eliminate this conceptual difference. Today at least three perspectives coexist:

    • design-based inference: randomness is attributed to the selection mechanism;
    • model-assisted inference: models are used to improve estimators, calibrate, or exploit auxiliary information while retaining properties justified by the design;
    • model-based inference: inference rests more directly on a probability model for the observed values or mechanisms.

    Särndal, Swensson, and Wretman (1992) systematically develop the model-assisted perspective. The distinction is not a relic from before modern computers; it answers different questions about the source of randomness and the assumptions that support inference.

    8.2. Finite populations and the finite population correction

    A population is finite if it contains \(N<\infty\) units. This does not depend on whether the researcher knows \(N\) exactly. A finite population can exist even when its size is unknown or uncertain.

    Under simple random sampling without replacement, for the population mean \(\bar Y\), if

    \[ S^2=\frac{1}{N-1}\sum_{i=1}^{N}(Y_i-\bar Y)^2, \]

    then

    \[ \operatorname{Var}(\bar y) =\left(1-\frac{n}{N}\right)\frac{S^2}{n}. \]

    The factor

    \[ 1-f,\qquad f=\frac{n}{N}, \]

    is the finite population correction in the variance. When \(f\) is very small, \(1-f\approx1\) and its effect may be negligible. When the sample covers a large proportion of the population, ignoring it can substantially overestimate sampling variance.

    The reason is materially simple: if an increasing proportion of a finite population is observed without replacement, progressively less uncertainty remains about the part that was not observed. When \(n=N\), a census of that population has been conducted and the variance due to the sampling mechanism is zero, although measurement, coverage, processing, and other errors may still exist.

    9. What does it mean to be sensitive to respondents’ expectations?

    The interviewer must maintain a combination that is not always easy: rapport without leading. The interviewer should establish a relationship that facilitates communication, explain the purpose of the interview, listen, and use permitted probes; but should not guide the respondent toward an answer that appears socially desirable or compatible with the researcher’s expectations.

    Empathy does not mean abandoning standardization. An interview can be humane, respectful, and sensitive to context without arbitrarily altering the meaning of the questions. In standardized instruments, an important part of training consists precisely in learning which clarifications are permitted and how to use neutral probes.

    Sensitivity also has an ethical dimension. The interviewer must recognize discomfort, fatigue, privacy, risks, and the right not to answer. Data quality cannot be pursued through coercion.

    The interaction between interviewer and respondent can affect measurement. Tone of voice, the way response options are presented, verbal or gestural reactions, the respondent’s perception of the interviewer’s status, and topic sensitivity can alter the observed response.

    Several mechanisms should be distinguished:

    • social desirability: a tendency to provide an answer perceived as socially acceptable;
    • acquiescence: a tendency to agree with formulations regardless of content;
    • interviewer effect: systematic variation associated with interviewer characteristics or behavior;
    • mode effect: differences associated with face-to-face, telephone, web, paper, or other modes;
    • context and order effect: responses conditioned by preceding questions or by the structure of the instrument.

    Informed consent, however, is not a bias. It is an ethical condition of research involving human participants. A different question is whether the decision to participate may generate self-selection or differential nonresponse. That possible source of bias should be studied as part of the participation process, not presented as a reason to weaken consent.

    11. Survey design and absolute sample size

    Lohr insists on an idea that should be formulated carefully: an enormous sample does not rescue a defective design. The relevant quantity in her discussion is the absolute size of the sample, not the absolute size of the population.

    For large populations, the precision of an estimator under simple random sampling depends primarily on \(n\); \(N\) appears in the finite population correction and has little effect when \(n/N\) is small. Thus, a sample of one thousand people may have a similar sampling error whether the population contains one million or one hundred million people, provided that the selection mechanism and other assumptions are comparable.

    But this statement does not mean that any sample of one thousand units is adequate. A convenience sample of one hundred thousand people may have more selection bias than a probability sample of one thousand. Increasing \(n\) reduces random variability under the assumed mechanism; it does not automatically erase systematic errors of coverage, selection, measurement, or nonresponse.

    Hence a rule that should remain present whenever polls are read:

    \[ \text{large sample}\;\nRightarrow\;\text{representativeness}. \]

    And likewise:

    \[ \text{small sampling margin}\;\nRightarrow\;\text{small total error}. \]

    12. Content, units, extent, and time when defining a population

    A study population must be defined precisely enough to decide who or what belongs to it. It is useful to specify its substantive content, the units that compose it, its spatial extent, and the time or reference period.

    Content is not simply spatiotemporal location; that belongs mainly to extent and time. Content determines what kind of entity and what substantive condition make a unit part of the population. For example: “persons aged 18 or older who are usual residents of private households in Costa Rica during the reference period.” There we find an entity, an age condition, a residence condition, a household delimitation, and a territorial-temporal extent.

    The different units

    The word “unit” has a general meaning—something that may be considered an entity for a given analysis—but in surveys it acquires different technical functions. They should not be confused.

    Element or target unit. The population entity about which information is to be produced: a person, a household, a firm, a plot of land, a school.

    Unit of analysis or study unit. The entity to which the analyzed variables are attributed. It may coincide with the target element, though not always.

    Observation unit. The entity on which observation or measurement is directly performed.

    Reporting unit. The person or entity that provides the information. In a household survey, one adult may report on other members; the reporting unit and the analytical unit are then not identical.

    Sampling unit. A selectable unit at a particular stage of the design. At a first stage it may be a geographic segment; at a second, a dwelling; at a third, a person.

    Primary sampling unit (PSU). The unit selected at the first stage of a multistage design. It is not defined by being “a place” or by necessarily containing only one final unit.

    Listing unit. A unit identified during a listing operation in order to construct or update the frame for a later stage. Its function is operational.

    The same entity may perform several functions. If a complete list of students is available and students are directly selected and asked about themselves, a student may simultaneously be the element, unit of analysis, observation unit, reporting unit, and sampling unit. But this empirical coincidence does not make the concepts synonymous.

    13. Domain, stratum, class, and subclass

    An estimation domain is a subpopulation for which separate estimates are desired. It may be defined by region, sex, age, economic sector, or another relevant condition.

    A stratum, by contrast, is a partition used by the sampling design before the sample is selected. Strata are generally intended to control composition, improve efficiency, or ensure sufficient representation of relevant groups.

    A domain may coincide with a stratum, but it need not. It may cut across several strata or be defined using information obtained after selection. For example, the population may be stratified by province and, later, a domain may be estimated consisting of persons employed in a particular economic industry across all provinces.

    The notion of a class as an interval in a frequency distribution belongs to a different problem. It is not necessary for defining an estimation domain. Likewise, “subclass” should not be used as a general synonym for domain unless a source explicitly defines that terminology for a particular scheme.

    14. What does \(Y_i\) represent?

    If the finite population is

    \[ U=\{1,2,\ldots,N\}, \]

    and \(Y\) is a study variable, then \(Y_i\) represents the value taken by that variable for unit \(i\). It does not represent “the characteristics” of the unit in the plural, but one particular realization of a defined variable.

    The population total is

    \[ T_Y=\sum_{i=1}^{N}Y_i, \]

    and the population mean is

    \[ \bar Y=\frac{1}{N}\sum_{i=1}^{N}Y_i. \]

    In design-based inference, a particularly important idea is that the vector

    \[ (Y_1,\ldots,Y_N) \]

    may be treated as fixed. The distribution used to study an estimator arises from the sampling design: which samples \(s\) could have been selected and with what probabilities.

    This prevents a common confusion. The use of probability does not require us to think that every material characteristic of the population was “generated” by a known parametric distribution. Probability may reside in the act of selection.

    15. What does MESIP selection mean?

    In terminology used in various Latin American texts, MESIP commonly denotes selection with equal probability of the elements. It should be understood as a particular class of probability design, not as the definition of all probability sampling.

    If \(\pi_i\) is the inclusion probability of unit \(i\),

    \[ \pi_i=P(i\in s), \]

    an equal-probability design satisfies, for comparable units within the design,

    \[ \pi_i=\pi_j. \]

    But a general probability design may have

    \[ \pi_i\neq\pi_j. \]

    The inequality may be deliberate, for example when selection is made with probability proportional to a measure of size.

    16. What does “unrestricted random sampling” mean?

    The expression is used in part of the Spanish-language literature to refer to simple random sampling without replacement: from a population of size \(N\), a fixed-size sample \(n\) is selected so that every possible subset of \(n\) units has the same probability of selection.

    If \(S\) is the set of all possible samples of size \(n\), then for every \(s\in S\),

    \[ P(S=s)=\binom{N}{n}^{-1}. \]

    It follows that every unit has inclusion probability

    \[ \pi_i=\frac{n}{N}. \]

    It is a fundamental design precisely because it permits transparent derivations and serves as a benchmark. But it is not “random sampling as such” in an exhaustive sense: probability sampling includes stratified, systematic, cluster, multistage, and unequal-probability designs.

    17. Mean squared error, variance, and bias

    Let \(\hat\theta\) be an estimator of the parameter \(\theta\). Its mean squared error is

    \[ \operatorname{MSE}(\hat\theta) =E\left[(\hat\theta-\theta)^2\right]. \]

    It can be decomposed as

    \[ \operatorname{MSE}(\hat\theta) =\operatorname{Var}(\hat\theta) +\operatorname{Bias}(\hat\theta)^2, \]

    where

    \[ \operatorname{Bias}(\hat\theta) =E(\hat\theta)-\theta. \]

    The identity can be seen by writing

    \[ \hat\theta-\theta =\bigl(\hat\theta-E(\hat\theta)\bigr) +\bigl(E(\hat\theta)-\theta\bigr), \]

    squaring, and taking expectations. The cross term vanishes because

    \[ E\left[\hat\theta-E(\hat\theta)\right]=0. \]

    Bias is not the difference between one observed realization of \(\hat\theta\) and its expectation. That difference is a random deviation of the realization from the center of its distribution. Bias is a property of the estimation procedure: the difference between the estimator’s expectation and the parameter it is intended to estimate.

    The decomposition is conceptually important because it displays a possible trade-off between variability and bias. An estimator with slightly larger variance may have smaller MSE if it reduces bias enough, and vice versa. “Precision” and “accuracy” should not be collapsed into a single colloquial intuition when the analysis requires their components to be distinguished.

    A cluster in sampling is a grouping of elementary units that may be used as a selection unit. It should not be confused with a cluster produced by a machine-learning algorithm. In clustering, the grouping is usually the result of an algorithmic criterion of similarity or distance; in sampling, the cluster belongs to the design and may be defined by geography, administrative organization, dwellings, schools, establishments, or other structures.

    A PSU is simply the unit selected at the first stage. In many household designs the PSU is a geographic cluster—for example, a census segment—so the two notions coincide empirically. They are not, however, synonymous by definition.

    Design First unit selected Stages Are equal probabilities mandatory?
    Simple random Element 1 Yes, in the classical simple design
    Stratified Element within stratum 1 Not necessarily across strata
    One-stage cluster Cluster 1 No
    Multistage PSU; then secondary units, etc. 2 or more No
    PPS Unit with probability proportional to size 1 or more No

    Clustering matters because units within the same cluster often resemble one another. If there is positive intraclass correlation, interviewing twenty people within the same cluster may provide less information than interviewing twenty people spread across many clusters.

    This relative loss or gain in precision is often summarized through a design effect,

    \[ \operatorname{deff} =\frac{\operatorname{Var}_{\text{design}}(\hat\theta)}{\operatorname{Var}_{\text{SRS}}(\hat\theta)}, \]

    which compares the variance under the actual design with a simple-random-sampling reference of comparable size. A \(\operatorname{deff}>1\) indicates greater variance than the reference; it is not an “error” of an algorithm, but a property that may arise from the design, intraclass correlation, and weight variability.

    19. What does “margin of error” mean?

    In the language of polls, the margin of error is usually the half-width of a confidence interval associated with sampling uncertainty under a particular design and estimation method. It is not “the total amount of error in the survey.”

    For an estimate \(\hat\theta\), a generic form is

    \[ \hat\theta\pm z_{1-\alpha/2}\,\widehat{SE}(\hat\theta), \]

    so that the margin is

    \[ MOE=z_{1-\alpha/2}\,\widehat{SE}(\hat\theta). \]

    In complex designs, \(\widehat{SE}\) must correspond to the design: strata, clusters, unequal probabilities, calibration, or replicate weights, as applicable. Calculating a margin as though the sample had been simple random when it was not can understate or overstate uncertainty.

    Sampling margin and total survey error

    The Total Survey Error paradigm requires separating sampling variability from other sources of error. These include:

    • coverage error;
    • nonresponse error;
    • measurement error;
    • construct or specification error;
    • processing and coding error;
    • editing and imputation error;
    • frame and linkage error;
    • errors arising from models or adjustments.

    Thus a phrase such as “the survey has a margin of error of ±3%” should be understood, unless otherwise specified, as a statement about one component of uncertainty. It does not demonstrate that the true value lies within three points or that every other source of error is small.

    20. Why is probability sampling preferred? Can only probability samples be useful?

    A probability design provides a known selection distribution and therefore permits design-based inferences and estimation of sampling variability under explicit conditions. This is an enormous advantage when the objective is to infer to a defined population.

    But “nonprobability” does not automatically mean “useless.” Convenience, judgment, quota, network, or self-selected samples may be appropriate for exploration, qualitative research, instrument development, mechanism studies, hard-to-reach populations, or questions for which design-based population inference is not the objective.

    The problem arises when such samples are attributed properties that their selection mechanism does not justify. If a researcher studies a localized group because it possesses a rare characteristic, valid knowledge may be produced about that group or about particular mechanisms. One cannot infer without additional assumptions that the prevalence of that characteristic in the country is equal to the prevalence observed in the group.

    The word representativeness should be used cautiously. A probability sample may suffer from undercoverage, differential nonresponse, or measurement error. It is therefore preferable to state exactly what property has been secured: a defined coverage frame, known probabilities, calibration to particular totals, balance on auxiliary variables, or inferential capacity under a specified design.

    Methodological ethics require documenting the selection method and the limits of inference. A nonprobability sample should not be presented as probabilistic; a probability sample should likewise not be presented as free from the remaining sources of survey error.

    21. “Randomization is a physical operation”: what does that mean?

    Kish insists that a probability sample requires more than writing a distribution on paper. There must be an effective selection procedure whose operation is congruent with the specified probability model.

    The word “physical” should not be understood as opposed to computation. An algorithm implemented on a computer can perfectly well form part of the material operation of selection. What Kish contrasts is an actually executed chance mechanism with a mere belief, assumption, or declaration that the units “are as if they had been random.”

    We can separate two levels:

    \[ \text{probability model}\neq\text{randomization procedure}, \]

    but a probability design requires correspondence between them. If the protocol states that each of \(N\) units has equal probability of selection, the actual procedure—a random-number table, an audited pseudorandom algorithm, a draw, or another mechanism—must implement that property.

    This distinction is philosophically interesting. Probability in a probability survey is not merely a formal property of the language used to describe selection; it is linked to an objective, reproducible practice. The theory acquires inferential capacity because a controlled relation exists between the material mechanism and the mathematically defined sample space.

    22. What does it mean that the frame includes “physical lists” and procedures that do not literally list all units?

    Kish uses frame in a broader sense than “a file containing every name.” A physical list may be a register of persons, establishments, addresses, plots, or administrative units. But there may also be procedures that allow units to be selected without constructing an exhaustive list of all final elements.

    An area frame, for example, may divide a territory into selectable segments; dwellings are then listed within the selected segments. In random-digit dialing, the frame may be conceptual or algorithmic: the space of eligible numbers is defined and selections are generated without previously possessing a file of all subscribers.

    “Actually listing” means explicitly enumerating all relevant units in an operational list. Avoiding that effort does not mean replacing the physical by “the computational” in the abstract; it means constructing a coverage and selection mechanism that does not require an exhaustive prior enumeration of all final units.

    23. Why is it so difficult to construct a frame for human populations?

    Because target population, frame, and field location are not the same thing. An electoral register, for example, may be excellent for a population of registered voters and at the same time inadequate for a population defined as “all resident persons” if it excludes minors, unregistered persons, or other groups.

    Human populations also change. People are born, die, migrate, move homes, form or dissolve households, use multiple telephones, lose numbers, share devices, and may reside temporarily in different places. A perfect frame would be costly not because we need to know where every person is at every hour, but because it must maintain a sufficiently good correspondence between target units and selectable units.

    Large statistical systems address this problem through different strategies: address frames, integrated administrative registers, area frames, dwelling listings within PSUs, dual or multiple frames, and periodic updates. Each solution distributes costs and risks of undercoverage, duplication, and obsolescence differently.

    The correct methodological question is not “does a complete list of every inhabitant exist?” but “what frame or combination of frames provides a documented path by which units of the target population have inclusion probabilities that are known or can be modeled in accordance with the design?”

    24. What is multistage sampling?

    It is a design in which selection occurs in two or more stages. In a household survey, for example, one might select:

    1. PSUs or geographic segments;
    2. dwellings within the PSUs;
    3. one person within each dwelling.

    If the selections are conditional, the overall inclusion probability of person \(i\) may be written schematically as the product of the probabilities at each stage:

    \[ \pi_i =\pi_{h}\,\pi_{j\mid h}\,\pi_{i\mid j,h}, \]

    depending on the notation adopted for PSU, dwelling, and person.

    The basic design weight is then

    \[ w_i=\frac{1}{\pi_i}. \]

    In practice, that weight may later be modified for nonresponse, calibration, poststratification, weight trimming, or other procedures. The basic design weight must always be distinguished from final analytical weights.

    Multistage sampling makes it possible to work with enormous populations without first constructing a national list of every final element. Its cost is usually much lower, although geographic concentration can increase variance relative to a simple random sample of the same size.

    25. Multiplicity, unequal probabilities, and Horvitz-Thompson estimation

    Suppose a telephone frame allows a household with two lines to appear twice while another household with one line appears once. If lines are selected with equal probability and households are the final target, household inclusion probabilities may be unequal. If that multiplicity is unknown and ignored, it may produce bias.

    But it does not follow that all random sampling requires equal probability. Survey theory explicitly includes unequal-probability designs. What is fundamental is that the probabilistic mechanism be specified and that the probabilities required for estimation be known or calculable.

    For a population \(U\) and a total

    \[ T_Y=\sum_{i\in U}Y_i, \]

    if each unit has positive inclusion probability \(\pi_i>0\), the Horvitz-Thompson estimator is

    \[ \widehat T_{HT} =\sum_{i\in s}\frac{Y_i}{\pi_i}. \]

    Its logic is immediate: a unit that is difficult to select represents more units of the population than a unit with a higher inclusion probability. Hence the inverse of \(\pi_i\) appears.

    Under the appropriate design,

    \[ E_p\left(\widehat T_{HT}\right)=T_Y, \]

    where \(E_p\) denotes expectation with respect to the sampling design. The historical importance of the Horvitz and Thompson (1952) result lies precisely in showing how samples without replacement and with unequal probabilities can be handled without thereby turning them into “nonrandom” samples.

    This allows us to return to the problem of duplicates with greater precision. If multiplicity changes \(\pi_i\) in a known way and the estimator incorporates it correctly, the inequality can be handled. If multiplicity is unknown, it becomes a frame problem that prevents the probabilities from being properly known and can distort inference.

    · · ·

    Some General Consequences

    Four principles that cut across survey theory can be drawn from the preceding problems.

    First, design and estimation form a unity. There is no estimator whose inferential meaning can be completely separated from the way the sample was produced. Weighting, variance estimation, and the interpretation of intervals depend on the design.

    Second, randomness does not necessarily mean equality. A design may be probabilistic and assign unequal probabilities; what matters is that the mechanism be probabilistically defined and that its relevant probabilities can be incorporated into inference.

    Third, sampling error is not total survey error. A narrow interval may coexist with undercoverage, differential nonresponse, poor measurement, or processing errors. The mathematical precision of one component should not be confused with overall validity.

    Fourth, the statistical operation is also a material operation. Frames, listings, interviews, selection algorithms, questionnaire skips, and editing rules are concrete practices. Theory does not float above them: its assumptions are realized—or violated—in the actual process by which data are produced.

    References

    AAPOR. (2023). Standard Definitions: Final Dispositions of Case Codes and Outcome Rates for Surveys (10th ed.). American Association for Public Opinion Research. https://aapor.org/standards-and-ethics/standard-definitions/

    Biemer, P. P., & Lyberg, L. E. (2003). Introduction to Survey Quality. Wiley. https://doi.org/10.1002/0471458740

    Cochran, W. G. (1977). Sampling Techniques (3rd ed.). Wiley.

    Groves, R. M. (1989). Survey Errors and Survey Costs. Wiley. https://doi.org/10.1002/0471725277

    Groves, R. M., Fowler, F. J. Jr., Couper, M. P., Lepkowski, J. M., Singer, E., & Tourangeau, R. (2009). Survey Methodology (2nd ed.). Wiley.

    Horvitz, D. G., & Thompson, D. J. (1952). A generalization of sampling without replacement from a finite universe. Journal of the American Statistical Association, 47(260), 663-685. https://doi.org/10.1080/01621459.1952.10483446

    Kalton, G., & Kasprzyk, D. (1986). The treatment of missing survey data. Survey Methodology, 12(1), 1-16.

    Kish, L. (1965). Survey Sampling. Wiley.

    Lohr, S. L. (2021). Sampling: Design and Analysis (3rd ed.). CRC Press.

    Särndal, C.-E., Swensson, B., & Wretman, J. (1992). Model Assisted Survey Sampling. Springer.

    Statistics Canada. (2003). Survey Methods and Practices. Catalogue no. 12-587-X. https://www150.statcan.gc.ca/n1/en/pub/12-587-x/12-587-x2003001-eng.pdf

    United Nations Statistics Division. (2008). Designing Household Survey Samples: Practical Guidelines. Studies in Methods, Series F, No. 98. https://unstats.un.org/unsd/publication/seriesf/seriesf_98e.pdf

    U.S. Census Bureau. (2021). Questionnaire Testing and Evaluation Methods for Censuses and Surveys. https://www.census.gov/about/policies/quality/standards/appendixa2.html

    Additional Resources of Interest

    The following resources may be useful for quick orientation, discussion, examples, or bibliographic navigation. They do not replace the methodological sources cited above.