Why Count Categorical Values?
Before comparing outcomes or building a predictive model, it is useful to know which categories are present in a data set and how many records fall in each one. For example, a table of policy types can reveal a category that does not appear in a filtered subset, or a region label that is spelled differently from the others. R’s table() function produces a frequency table: a count for each category it includes.
A frequency table is a count, not a percentage. Its main purpose here is to make category membership and counts visible and to help check whether the categories in the data are what the analyst expects. The result depends on both the values in the vector and, for a factor, its defined levels.
table(x) returns the frequency for each category represented in the input vector x, subject to its factor-level and missing-value handling.
Reading a One-Variable Frequency Table
Suppose each row of a data frame represents one policy, and the column type records its policy type. Calling table(type) counts how many rows have each observed value. The output shows category names and the corresponding counts. For a character vector, R generally displays observed values in sorted order; for a factor, it follows the order of the factor’s levels.
A table summarizes the values supplied to it. If the vector comes from a filtered data frame, the counts describe only the retained rows, not necessarily the full original data set. The analyst should therefore confirm what data were passed to table() before interpreting the result.
Worked Example: Counting Policy Types After a Filter
A hypothetical analyst filters a data frame to policies written in one region. The resulting policy-type vector is c("Auto", "Home", "Home", "Life", "Auto", "Travel", "Home", "Auto"). Find the frequency of each observed type and check the total.
Method. Apply table() to the filtered vector. First count each distinct label directly from the eight entries:
- Auto occurs in positions 1, 5, and 8: frequency 3.
- Home occurs in positions 2, 3, and 7: frequency 3.
- Life occurs in position 4: frequency 1.
- Travel occurs in position 6: frequency 1.
The frequency table therefore reports Auto = 3, Home = 3, Life = 1, and Travel = 1. The counts add to \(3+3+1+1=8\), matching the eight vector entries. The result describes the filtered region only.
Check and common mistake avoided. A count of “Home = 3” does not mean there are three distinct policy types; it means three observations have the Home label. It would also be incorrect to report the counts as applying to all policies if the table was built from the filtered subset.
Factors Can Show Categories With No Observations
A factor stores categorical values together with a set of defined levels. This matters because a factor can have a level that is not represented by any current observation. By default, table() includes factor levels even when their frequency is zero. Such a level is not evidence that the category occurs in the data; its zero count says the opposite.
The level order is also informative. A factor table follows the factor’s level order, which may differ from alphabetical order. This can be useful when the categories have a deliberate order, but it means that the table’s display order should not be mistaken for a ranking by frequency.
Worked Example: Finding an Unused Factor Level
A categorical vector contains c("East", "West", "East", "North", "West"). It is converted to a factor with levels c("East", "North", "South", "West"). Determine the table output, including any unused level, and verify the total.
Method. Count the five entries, keeping the factor’s specified level order:
- East occurs in entries 1 and 3, so its frequency is 2.
- North occurs in entry 4, so its frequency is 1.
- South does not occur, so its frequency is 0.
- West occurs in entries 2 and 5, so its frequency is 2.
The table reports East = 2, North = 1, South = 0, and West = 2. These counts sum to \(2+1+0+2=5\), which matches the length of the vector. South is an unused level, not an observed value.
Check and common mistake avoided. It is easy to overlook North because it appears only once, or to treat South as an observation because it is printed in the table. The raw vector confirms North = 1, while the absence of South from the vector confirms South = 0. The total of five is an independent check against either counting error.
Missing Values and the Table Total
By default, table() does not include missing values in its counts. As a result, the sum of the displayed frequencies can be smaller than the number of entries in the input vector. To display missing values when they occur, use useNA = "ifany". The table then shows an additional missing-value category if the input contains missing values.
When a factor is used, the table can show both zero-count levels and a missing-value count. These represent different situations: a zero-count level is an available category with no observations, while a missing value indicates that an observation has no recorded category. If values are converted to a factor using a restricted set of levels, a value not among those levels becomes missing. That conversion can therefore change what the table appears to say.
Worked Example: Reconciling Missing and Unused Categories
A hypothetical operations team records the factor c("New", "Renewal", NA, "New", "Transfer", NA, "Renewal", "New") with levels c("New", "Renewal", "Transfer", "Lapsed"). Find the default table and the table using useNA = "ifany". Explain why their totals differ.
Method. Count the nonmissing category values first. New occurs in entries 1, 4, and 8, giving 3. Renewal occurs in entries 2 and 7, giving 2. Transfer occurs in entry 5, giving 1. Lapsed is a defined level but does not occur, giving 0. There are also missing entries in positions 3 and 6, giving 2 missing values.
With the default missing-value handling, the table shows New = 3, Renewal = 2, Transfer = 1, and Lapsed = 0. Its displayed counts sum to \(3+2+1+0=6\), because the two missing entries are omitted.
With useNA = "ifany", the table also shows a missing-value count of 2. The full total is then \(3+2+1+0+2=8\), matching the vector length. The zero-count Lapsed level remains distinct from the missing-value category.
Check and common mistake avoided. Do not interpret the default total of six as proof that the vector has only six entries. First check whether missing values were omitted. Conversely, do not call the two missing entries Lapsed: no such label was recorded for them.
Why the Counts Must Reconcile
The total-count check follows directly from what a frequency represents. Suppose every nonmissing observation in a vector is assigned to exactly one category. For each category, its count is the number of observations assigned to it. Adding the category counts counts every included observation once: each observation contributes one to its own category and zero to every other category.
Thus, if the table includes all nonmissing categories and excludes missing values, the sum of its frequencies equals the number of nonmissing observations. If missing values are included as their own table entry, the sum equals the total number of vector entries. This check can reveal a missed category, a mistaken count, or an unrecognized missing-value issue.
Here \(C\) is the set of categories represented in the table, \(n_c\) is the count for category \(c\), \(N\) is the number of vector entries, and the indicator equals 1 when entry \(j\) is included under the chosen missing-value rule and 0 otherwise. The equality holds because each included entry belongs to exactly one counted category. It assumes the input represents one categorical value per observation; it does not apply unchanged if the data structure or counting rule allows an observation to be counted more than once.
Practical Checks Before Interpreting a Table
A frequency table is most useful when its scope and category definitions are clear. Before drawing a conclusion, check the input vector, factor levels, and missing-value setting. In particular, confirm that the vector was selected from the intended rows and that category labels have not been unintentionally split by inconsistent spelling or encoding. For example, “North” and “north” are distinct character values.
Use the following sequence when inspecting a one-variable table:
Confirm which column or vector is being counted and whether it comes from a filtered subset.
For a character vector, note the observed labels; for a factor, also check the defined levels and their order.
Know that the default omits them; use
useNA = "ifany" when the missing count should be visible.Compare the sum of table counts with the expected number of included observations.
A zero frequency is useful information when the category set is intentional: it shows that a defined category did not appear in the subset. It should not be confused with a missing value, an invalid label, or proof that the category can never occur. Frequency tables describe the data supplied to them; they do not by themselves establish why a category is absent.
Common Mistakes
- Forgetting that factor levels can have zero counts. A printed level may be unused; check its frequency rather than assuming it occurred.
- Omitting a value while counting by hand. Recheck each occurrence in the vector, then verify that the table counts reconcile with the relevant number of records.
- Assuming the default table includes missing values. By default, missing values are left out, so the displayed total may be smaller than the vector length.
- Confusing a factor level with a recorded observation. Levels define allowed categories; the data values determine which categories actually occur.
- Ignoring the analysis subset. A table built after filtering describes only the filtered rows.
table() to count categorical observations, inspect zero-frequency factor levels, and check how missing values affect the result. Always interpret the counts in relation to the exact vector and verify that the table total matches the intended inclusion rule.
Check Your Understanding
For each problem, determine the frequency counts and use the stated inclusion rule. Treat each scenario as a hypothetical data set.
- A vector of route labels is
c("Coastal", "Hill", "Coastal", "Valley", "Hill", "Coastal"). Give the table counts and their total. (Answer: Coastal = 3, Hill = 2, Valley = 1; total = 6.) - A factor has values
c("Basic", "Plus", "Basic", "Elite")and levelsc("Elite", "Basic", "Plus", "Legacy"). Give the table in factor-level order, including unused levels. (Answer: Elite = 1, Basic = 2, Plus = 1, Legacy = 0; total = 4.) - A vector is
c("A", "B", NA, "A", "C", NA, "B"). What counts appear by default, and what are the counts withuseNA = "ifany"? (Answer: Default: A = 2, B = 2, C = 1; total = 5. With missing values: A = 2, B = 2, C = 1, NA = 2; total = 7.) - The character values
c("A", "C", "B", "C")are converted to a factor with levelsc("A", "B"). What does the default table show, and how should the two C values be interpreted? (Answer: A = 1, B = 1; the two C values become missing during conversion and are omitted by default. WithuseNA = "ifany", NA = 2 is also shown, for a total of 4.) - A factor has values
c("Open", "Closed", NA, "Open", "Pending", "Closed", NA, "Open")and levelsc("Open", "Pending", "Closed", "Not started"). State the counts with missing values included, identify the unused level, and check the total. (Answer: Open = 3, Pending = 1, Closed = 2, Not started = 0, NA = 2; total = 8. Not started is the unused level.)