Quick summary
Summarize this blog with AI
Introduction
colSums() produces one value per column. It is equivalent in purpose to apply(x, 2, sum) for an ordinary numeric matrix, not apply(x, 1, sum), which sums rows. Use a non-square, labelled fixture to make an axis mistake visible.
Tested with R 4.5.1. Run the following code blocks in order. All inputs are included.
Compare column and row totals
Rows are stores and columns are days. Each selected value is measured in the same units.
sales <- matrix(c(10, 20, 30, 40, 50, 60), nrow = 2, byrow = TRUE,
dimnames = list(c("A", "B"), c("Mon", "Tue", "Wed")))
print(colSums(sales)) # Mon 50; Tue 70; Wed 90
print(rowSums(sales)) # A 60; B 150
stopifnot(identical(unname(colSums(sales)), c(50, 70, 90)))
stopifnot(isTRUE(all.equal(colSums(sales), apply(sales, 2, sum))))
stopifnot(length(colSums(sales)) == ncol(sales))
Changing a row operation to colSums changes the question. Compare equivalent operations before discussing speed. Never use a function name such as rowSums for a result variable.
Select measurement columns and handle missingness
A data frame may also contain numeric IDs or text labels. Select intended measurements rather than every numeric column.
records <- data.frame(store_id = c(101, 102), mon = c(10, 40),
tue = c(NA_real_, 50), wed = c(NA_real_, NA_real_))
amounts <- as.matrix(records[c("mon", "tue", "wed")])
complete <- colSums(amounts)
observed <- colSums(amounts, na.rm = TRUE)
observed_count <- colSums(!is.na(amounts))
observed[observed_count == 0] <- NA_real_
print(complete) # 50 NA NA
print(observed) # 50 50 NA
stopifnot(identical(unname(observed), c(50, 50, NA_real_)))
Tuesday has an observed subtotal of 50 but an unknown complete total. The explicit all-missing policy distinguishes “no observations” from zero. Counts make the difference reviewable.
Distinguish counts from amounts
Summing TRUE/FALSE conditions counts matches. To add qualifying amounts, retain their original values.
positive <- matrix(c(-1, 2, 3, 4, -5, 6), nrow = 2, byrow = TRUE)
count <- colSums(positive > 0)
selected <- positive
selected[selected <= 0] <- 0
amount <- colSums(selected)
print(count) # 1 1 2
print(amount) # 4 2 9
stopifnot(identical(count, c(1, 1, 2)), identical(amount, c(4, 2, 9)))
This fixture has no missing values. With missing measurements, define whether the output is an observed subtotal or a complete total before adding na.rm.
Preserve matrix shape and test emptiness
Selecting one column normally drops a matrix dimension; drop = FALSE preserves the two-dimensional input contract.
one_day <- sales[, "Mon", drop = FALSE]
stopifnot(identical(unname(colSums(one_day)), 50))
no_rows <- sales[FALSE, , drop = FALSE]
stopifnot(identical(unname(colSums(no_rows)), c(0, 0, 0)))
no_columns <- sales[, FALSE, drop = FALSE]
stopifnot(length(colSums(no_columns)) == 0)
wrong_type <- tryCatch(colSums(matrix(c("a", "2"), ncol = 1)), error = function(e) e)
stopifnot(inherits(wrong_type, "error"))
Zero-row input gives zero per column by the built-in sum convention; your reporting policy may require an explicit no-data state. Do not coerce arbitrary labels and ignore the resulting failures. The rowSums tutorial covers the complementary axis.