rguides

table()

table(..., exclude = NA, useNA = c("no", "ifany", "always"))

The table() function creates contingency tables, also known as frequency tables. It counts the occurrences of each unique value in one or more vectors or factors, making it essential for exploratory data analysis and categorical data summarization. The function is part of base R and requires no additional packages.

Syntax

table(..., exclude = NA, useNA = c("no", "ifany", "always"))

Parameters

ParameterTypeDefaultDescription
...vectors/factorsrequiredOne or more vectors or factors to cross-tabulate
excludecharacter/NANAValues to exclude from the table; use NA to keep NA counts
useNAcharacter"no"Controls NA handling: "no" (no NA column), "ifany" (include if present), "always" (always include)

Examples

Basic one-way table

colors <- c("red", "blue", "red", "green", "blue", "red")
table(colors)
# colors
# blue green   red 
#    2     1     3 

A one-way table counts each unique value in a single vector, producing a named integer array. The names are the unique values and the counts are the frequencies. This is the simplest form of table() and is useful for summarizing categorical variables like survey responses, factor levels, or discrete outcomes in a dataset.

Two-way cross-tabulation

gender <- c("M", "F", "M", "F", "M", "F", "M", "M")
outcome <- c("pass", "pass", "fail", "pass", "pass", "fail", "pass", "fail")

table(gender, outcome)
#         outcome
# gender  fail pass
#      F     1    2
#      M     2    3

Passing two vectors to table() creates a contingency table — a grid where rows are the levels of the first argument, columns are the levels of the second, and each cell contains the count of observations with that row-column combination. This is the foundation for chi-squared tests and for visualizing relationships between two categorical variables with mosaic plots or heatmaps.

Using useNA

scores <- c(85, 92, NA, 78, NA, 88, 95)
table(scores, useNA = "ifany")
# scores
#   78   85   88   92   95 <NA> 
#     1    1    1    1    1    2 

The useNA argument controls whether missing values appear as a category in the table output. useNA = "ifany" shows an <NA> column only when NA values are present in the data, keeping the output compact. useNA = "always" includes the column even with no missing values, which is helpful when producing tables for automated reports where column structure must be consistent across runs.

Common patterns

Proportions

colors <- c("red", "blue", "red", "green", "blue", "red", "green", "green")
tbl <- table(colors)
prop.table(tbl)
# colors
#  blue green   red 
#  0.25  0.375  0.375 

prop.table() converts raw counts to proportions by dividing every cell by the grand total. Passing margin = 1 computes row proportions (each row sums to 1), while margin = 2 computes column proportions. These are essential for comparing distributions across groups when group sizes differ — raw counts can be misleading, but proportions reveal the underlying pattern.

Margin tables

gender <- c("M", "F", "M", "F", "M", "F")
outcome <- c("pass", "pass", "fail", "pass", "pass", "fail")

addmargins(table(gender, outcome))
#         outcome
# gender   fail pass Sum
#      F      1    2   3
#      M      2    2   4
#      Sum    3    4   7

table() in practice

table() counts the frequency of each unique value in a vector. For a single vector, it returns a 1D table; for two vectors, a 2D contingency table. The result is an integer array with dimnames set to the unique values. as.data.frame(table(x)) converts the result to a data frame with Var1 and Freq columns for easier downstream processing.

Two-way tables are the primary use case: table(df$group, df$outcome) shows how many observations fall in each combination of group and outcome. prop.table(table(x, y)) converts counts to proportions; prop.table(table(x, y), margin = 1) gives row proportions.

table() excludes NA by default, they do not appear as a level in the output. Set useNA = "ifany" to include NA as a category if any missing values exist, or useNA = "always" to always include an NA column even if there are none. This distinction matters when you need to count missing values as part of a frequency distribution.

xtabs() is a formula-based interface to the same functionality: xtabs(~ group + outcome, data = df) is equivalent to table(df$group, df$outcome). For large datasets, dplyr::count() is faster and integrates with the tidyverse pipeline, returning a data frame rather than a table object.

table() produces a contingency table from one or more factors or vectors. Pass two arguments for a cross-tabulation: table(x, y) gives counts for each combination. Wrap in prop.table() to convert counts to proportions. addmargins() adds row and column totals. For large datasets, table() can be slow — dplyr::count() is faster for data frames.

table() counts occurrences of each unique value in one or more vectors. Passing two vectors creates a cross-tabulation (contingency table). The result is a named integer array with class "table".

prop.table(tab) converts counts to proportions across all cells. prop.table(tab, margin = 1) computes row proportions; margin = 2 computes column proportions. addmargins(tab) adds row and column totals to the table.

as.data.frame(tab) converts a table to a tidy data frame with a Freq column, suitable for use with ggplot2. For large datasets, dplyr::count() is faster because it avoids intermediate factor conversion.

table() includes counts for all factor levels even if some are zero. For plain character vectors, only observed values appear. Converting the input to a factor with factor(x, levels = all_levels) ensures zero-count levels appear in the table.

xtabs(~ a + b, data = df) is a formula interface to table() that works directly with data frame columns, equivalent to table(df$a, df$b) but more readable in data analysis pipelines.

See also