HOWTO · R
The table() Function in R
Learn to use table() and prop.table() in R to count categories, calculate proportions, and summarize combinations of two variables.
On this page
In R, table() counts how often each value or combination of values occurs. Use it for categorical summaries such as survey responses, product types, or groups. Wrap the resulting counts in prop.table() when you want proportions instead. The examples below use one small data frame to show both operations and how to read their results.
Create a frequency table with table()
For one variable, pass its vector to table(x). The result is a named table: each category is a name, and its associated number is the count. With a data frame, select a column using $, as in responses$channel.
The example uses five observations. The channel count records how many responses came from each channel. For a factor input, table() follows the factor-level order, which can be custom. Character values are converted to factors and sorted, so their displayed order need not match the order in which values first appeared in the data.
Run this example:
responses <- data.frame(
channel = c("web", "store", "web", "phone", "store"),
outcome = c("yes", "no", "no", "yes", "yes")
)
channel_counts <- table(responses$channel)
channel_share <- prop.table(channel_counts)
joint_counts <- table(responses$channel, responses$outcome)
joint_share <- prop.table(joint_counts)
within_channel_share <- prop.table(joint_counts, margin = 1)
print(channel_counts)
print(channel_share)
print(joint_counts)
print(joint_share)
print(within_channel_share)
Calculate proportions with prop.table()
prop.table(channel_counts) divides every count by the sum of the table, so the proportions across all channels add up to 1 (apart from display rounding). Multiply by 100 if you want percentages. In this sample, a channel with two responses represents two of the five observations, or 40% of this sample—not 40% of all users or responses in a wider population.
The denominator depends on the table you pass in. For a one-variable table, it is the total number of observations represented by that table. This makes the default useful when you want each category’s share of the whole sample.
Count combinations across two variables
Pass two vectors to table(x, y) to count their combinations. In joint_counts, rows correspond to channels and columns correspond to outcomes. Each cell gives the number of observations with that channel and outcome together. A zero means that no matching observation appears in this sample; it does not establish that the combination is impossible.
Applying prop.table() to this two-dimensional table divides every cell by the grand total, so joint_share describes each combination’s share of all observations. To instead calculate the outcome mix separately within each channel, use prop.table(joint_counts, margin = 1). Here, margin = 1 normalizes each row to 1. Use margin = 2 to normalize each column. These conditional proportions answer a different question from the overall shares.
Interpret and verify the results
Read the table labels before interpreting a number: identify the category or row-and-column pair it belongs to, then check which denominator was used. The one-variable proportions and the overall two-variable cell proportions each sum to 1. Row-normalized proportions sum to 1 within each row, while column-normalized proportions sum to 1 within each column. Small differences in printed totals can result from rounding.
A table() result only summarizes the values supplied to it. By default, missing values are not counted as a category; when missingness should be visible, consult the function’s useNA argument. Also check that the selected columns contain the categories you intend to compare. In this example, the sample contains five responses, so every count and proportion describes those five rows only. The code keeps the counts and each kind of proportion in separate objects, making their different meanings easier to inspect and reuse.