table() in RStudio: How It Counts and Displays Data

How-to basics · Updated November 11, 2026

In RStudio, table() is an R function that counts how often each value or combination of values appears in one or more vectors. For example, run table(df$category) in the Console to get a frequency count; RStudio is the interface, while table() belongs to base R.

What table() does

The function summarizes categorical data by tallying observations. Given a vector of labels, it returns the number of times each distinct value occurs. With two or more vectors, it builds a cross-tabulation: counts for each combination of categories. Its result is an object of class table, represented as an array with dimensions and, usually, category names attached.

For instance, if a survey records each respondent’s preferred transport, this code counts responses:

transport <- c('bus', 'bike', 'bus', 'walk', 'bike', 'bus')
table(transport)

The result reports bus 3, bike 2, and walk 1. It is a compact summary, not a modified version of the original data: the input vector remains unchanged. The counts are useful for checking category balance, summarizing survey answers, and preparing contingency tables for further analysis.

Basic syntax and a first example

The common form is table(...), where the dots represent one or more vectors or factors. A one-variable table gives frequency counts; multiple arguments produce a multi-way table. When supplying multiple vectors, use vectors of the same length so each position describes the same observation.

Here is an example with a small data frame. The first vector identifies a department and the second records a response:

department <- c('Sales', 'Support', 'Sales', 'Support', 'Sales')
response <- c('Yes', 'No', 'Yes', 'Yes', 'No')
table(department)

The one-variable result has two department counts: Sales appears three times and Support appears twice. You can also type an expression directly at the Console prompt, or run the same code from an R script by selecting it and choosing Run. In either case, the printed result appears in the Console.

For actual data, a typical call is table(df$department). The dollar sign selects the named column from a data frame. If the column name contains spaces or punctuation, use backticks, as in table(df$`service type`). You can also pass a vector stored in a separate variable.

Missing values and unused categories

By default, missing values are not displayed as a category in the output. Use the useNA argument when you want them counted. Its options are 'no' (the default), 'ifany' (show a missing-value count when missing values occur), and 'always' (include that category even if its count is zero).

rating <- c('good', 'poor', NA, 'good')
table(rating, useNA = 'ifany')

This table shows good with a count of 2, poor with a count of 1, and an NA category with a count of 1. Counting missing entries is often important: a table that silently omits them can make the displayed category totals seem smaller than the number of rows in the source data. If missingness itself is meaningful, report it rather than treating it as an ordinary response without explanation.

Factors introduce another detail. A factor can have defined levels that do not occur in the observed data. Those levels can still appear in a table with a zero count. This is useful when you need consistent categories across reports or samples, but it can surprise readers who expect only observed values. A character vector generally displays the values present in that vector.

Reading two-way and multi-way tables

Pass two variables to compare their categories. Each cell in the result is the number of rows where both categories occur together:

table(department, response)

The first variable forms the rows and the second forms the columns. The row and column labels identify categories, and the cell values are counts. This is useful for questions such as “How many Sales respondents said Yes?” or “How many Support respondents said No?” The table does not, by itself, say whether a difference is statistically meaningful.

You can add a third variable, but the result then has three dimensions, and further variables add more. Multi-way tables can be useful for stratified summaries, but are harder to read in the Console. For a quick view of a two-variable table, use named columns:

table(df$department, df$response)

To give the dimensions clearer labels, set dnn, which supplies dimension names:

table(df$department, df$response,
      dnn = c('Department', 'Response'))

For a standard frequency table, the total of all cells equals the number of included observations. It can be lower than the number of source rows when missing values are excluded by default. If you are checking a data-cleaning step, compare the table’s total with the expected row count and decide explicitly how missing entries should be handled.

Getting proportions and margins

Counts are not always the easiest way to compare groups of different sizes. The base R function prop.table() converts a table to proportions. With one variable, it divides each count by the overall total:

counts <- table(transport)
prop.table(counts)

The proportions sum to 1, subject to normal floating-point display. Multiply by 100 if you want percentages. For a two-way table, use the margin argument to calculate proportions within a dimension. With margin = 1, values are divided by their row totals; with margin = 2, they are divided by column totals. State which denominator you used, because row percentages and column percentages answer different questions.

To add row and column totals to a two-way table, use addmargins():

addmargins(table(department, response))

The added totals make it easier to check group sizes and grand totals. They do not replace the original cell counts; they add summary margins to the printed table.

Viewing or exporting the result in RStudio

For small tables, the Console output is often the clearest option. If you want a rectangular data-frame form for sorting, exporting, or viewing, wrap the table in as.data.frame():

result <- as.data.frame(table(department, response))
View(result)

The resulting data frame has columns for the categories and a Freq column for the count. In RStudio, View() opens the data viewer for this rectangular result. You can also export it with base R’s write.csv(), or use RStudio’s Export control in the data viewer where available. The original table and the converted data frame are different representations of the same counts; converting does not create additional observations.

Common problems and useful checks

  • Unexpectedly small totals: Missing values are omitted by default. Try useNA = 'ifany' and compare the result with the number of source rows.
  • Zero-count categories: Check whether an input is a factor with levels that are not observed. Such levels can be retained with zero counts.
  • Misaligned variables: When two vectors come from different sources, confirm that their values still correspond row by row and have compatible lengths before tabulating them.
  • Hard-to-read output: A table with many categories or dimensions can be unwieldy. Filter or recode categories for a focused summary, or convert the result to a data frame.
  • Confusing counts with percentages: table() returns counts. Use prop.table() for proportions and identify whether percentages use the whole sample, rows, or columns as the denominator.

A frequent misconception is that table() makes a spreadsheet-style table that can be edited in place. It produces a summary object in R. If you need a formatted report, you can convert it to a data frame and then use reporting or plotting tools; formatting is separate from counting.

How table() compares with related R tools

table() is a good choice for quick counts and cross-tabulations using base R, so it does not require an additional package. xtabs() is another base R option that uses a formula interface. For example, xtabs(~ department + response, data = df) expresses a two-way count in terms of columns in a data frame. It can feel natural when the variables already live in a data frame, while table(df$department, df$response) is direct and familiar for simple cases.

In a data-manipulation workflow, functions such as dplyr::count() can count combinations and return a data frame, but they require the dplyr package. Choose based on the output you need: use table() for a compact array of counts, as.data.frame(table(...)) for a base-R rectangular result, or a package workflow when you need to combine counting with other data transformations. These approaches summarize data; none automatically establishes that observed group differences have a particular cause.

Frequently Asked Questions

Is table() part of RStudio or R?
table() is part of base R, not a special RStudio-only feature. RStudio provides an interface for writing and running R code, so the same function also works in other R environments.

How do I count values in a column in RStudio?
Use table(data$column), replacing data and column with your data-frame and column names. For example, table(df$category) prints the count for each category in the Console.

How do I include NA values in table()?
Set useNA = 'ifany' to display a missing-value count when missing values are present, or useNA = 'always' to include the NA category even when its count is zero. The default is to omit missing values from the displayed counts.

How do I make a two-way frequency table?
Pass both variables to the function, such as table(df$group, df$outcome). Rows correspond to the first variable and columns to the second; each cell counts observations with that combination.

How do I turn a table into a data frame?
Use as.data.frame(table(df$group, df$outcome)). The result has category columns and a Freq column, which is often easier to export or inspect with RStudio’s View().

Related

Try it: the Date Difference Calculator: days, months, years and weekdays between two dates, with the DATEDIF formulas.

More in How-to basics

Sources: Function behaviour and menu paths: Microsoft Support documentation for Excel. Method & sources

Spotted an error in this guide? Let us know.