Written and maintained by CASRAI Editorial Board
Last updated
A contingency table counts how many observations fall into each combination of two categorical variables. To read one, work from the outside in: identify the rows and columns, check the totals along the margins, choose the percentage that matches your question, and then compare observed counts with the counts you would expect if the two variables were unrelated.
What is a contingency table?
A contingency table (also called a cross-tabulation or two-way table) is an array of r rows and c columns in which each of the r × c cells holds the number of observations with that particular pair of categories. The NIST/SEMATECH e-Handbook of Statistical Methods describes it as a cross-classification of two attributes; OpenStax defines it as a table that displays sample values for two factors that may be dependent, or contingent, on one another. Its purpose is to show whether the two classifications are independent or associated.
How to read a contingency table
Step 1: Name the rows, columns and cells
Rows are the categories of one variable and columns are the categories of the other. Each cell is one row-column combination. A cell count of 60 in the row “Trained” and the column “Passed” means 60 observations were both trained and passed.
Step 2: Check the margins and the grand total
Row totals and column totals sit on the edges (the marginal totals); the bottom-right number is the grand total n. Every row total must add to n across, and every column total must add to n down. If they do not, the table has a counting error. Marginals alone describe each variable separately; see how to calculate marginal probability for turning them into probabilities.
Step 3: Choose the percentage that answers your question
The same four counts support three different percentages, and they answer different questions:
- Row percentage (cell ÷ row total): of the people in this row, what share fall in each column?
- Column percentage (cell ÷ column total): of the people in this column, what share fall in each row?
- Total percentage (cell ÷ grand total): what share of everyone falls in this cell?
Put the variable you treat as the explanation on the rows, and read row percentages across to compare groups.
Step 4: Compare observed with expected counts
If the variables were independent, the expected count for a cell would be (row total × column total) ÷ grand total. This is the formula given by both NIST and OpenStax. Cells whose observed count is far from the expected count are where an association shows up.
How to make (construct) a contingency table
- Start with one record per observation, with two categorical fields (for example, group and outcome).
- List the categories of the first variable as rows and of the second as columns.
- Tally each record into the single cell that matches its pair of categories.
- Add row totals, column totals and the grand total, and confirm both sets of totals sum to n.
- Add the percentage type you need (row, column or total) beside or under the counts, and label which one it is.
Software does the tally for you: see crosstabs in Excel and crosstabs in R for tool-specific walk-throughs.
Worked example 1: a 2 × 2 table (illustrative counts)
The counts below are invented for illustration; they are not data from any study. Suppose 200 participants are classified by whether they completed a training session and whether they passed an assessment.
| Passed | Failed | Row total | |
|---|---|---|---|
| Trained | 60 | 40 | 100 |
| Not trained | 45 | 55 | 100 |
| Column total | 105 | 95 | 200 |
Reading it. Row percentages: 60% of the trained group passed, against 45% of the untrained group. Column percentages answer a different question: 60 ÷ 105 = 57.1% of those who passed were trained. Total percentages: 60 ÷ 200 = 30% of all participants were trained and passed. The two figures describe different groups, so always state which percentage you are using.
Expected counts. Trained and passed: 100 × 105 ÷ 200 = 52.5. Trained and failed: 100 × 95 ÷ 200 = 47.5. The untrained row has the same expected counts (52.5 and 47.5).
Chi-square statistic. χ² = Σ (observed − expected)² ÷ expected. Each cell is 7.5 away from expected, so χ² = 7.5² × (1/52.5 + 1/47.5 + 1/52.5 + 1/47.5) = 56.25 × 0.0802 = 4.51. With df = (2 − 1)(2 − 1) = 1, the 0.05 critical value is 3.841 and the 0.01 value is 6.635 (table below). Because 4.51 is greater than 3.841 but less than 6.635, the association is significant at the 0.05 level but not at the 0.01 level.
Worked example 2: published designs from NIST and OpenStax
NIST wafer defects (4 × 3). The NIST/SEMATECH e-Handbook section 7.4.5 classifies 309 wafer defects by four defect types (A to D) across three shifts. The degrees of freedom are (3 − 1)(4 − 1) = 6. The handbook reports χ² = 19.18, which exceeds the critical value 12.5916 at the 0.05 level, so it rejects independence (p = 0.00387): defect type and shift are associated.
OpenStax volunteer hours (3 × 3). OpenStax Introductory Statistics, section 11.4, classifies 839 volunteers by hours volunteered and type of volunteering. The top-left expected count is (255)(298) ÷ 839 = 90.57. The test gives χ² = 12.99 with df = 4 and p = 0.0113, so independence is rejected at α = 0.05. OpenStax also states that the expected value for each cell needs to be at least five.
Degrees of freedom and the critical-value table
Degrees of freedom for an r × c table are df = (r − 1)(c − 1). A 2 × 2 table has df 1, a 2 × 3 table df 2, a 3 × 3 table df 4, a 3 × 4 table df 6 and a 4 × 4 table df 9. Reject independence when your χ² statistic is greater than the tabled value.
| df | α = 0.05 | α = 0.01 |
|---|---|---|
| 1 | 3.841 | 6.635 |
| 2 | 5.991 | 9.210 |
| 3 | 7.815 | 11.345 |
| 4 | 9.488 | 13.277 |
| 5 | 11.070 | 15.086 |
| 6 | 12.592 | 16.812 |
| 7 | 14.067 | 18.475 |
| 8 | 15.507 | 20.090 |
| 9 | 16.919 | 21.666 |
| 10 | 18.307 | 23.209 |
Source: NIST/SEMATECH e-Handbook of Statistical Methods, chi-square critical values table (upper tail, columns 0.95 and 0.99), itl.nist.gov/div898/handbook/eda/section3/eda3674.htm, date checked 2026-10-29. Values are rounded to three decimal places as published; the handbook example quotes the df 6 value as 12.5916.
Compute it in Excel, R or Python
Each snippet reproduces worked example 1.
Excel. Build the observed counts, then in an expected-count cell enter =row_total*column_total/grand_total (using cell references) and fill across the table. =CHISQ.TEST(observed_range, expected_range) returns the p-value; with the example it gives about 0.0337.
R.
tab <- matrix(c(60, 45, 40, 55), nrow = 2,
dimnames = list(c("Trained", "Not trained"), c("Passed", "Failed")))
chisq.test(tab, correct = FALSE) # X-squared = 4.5113, df = 1
chisq.test(tab, correct = FALSE)$expected
Python.
from scipy.stats import chi2_contingency
obs = [[60, 40], [45, 55]]
chi2, p, dof, expected = chi2_contingency(obs, correction=False)
# chi2 = 4.511, p = 0.0337, dof = 1, expected = [[52.5, 47.5], [52.5, 47.5]]
These calls turn off the continuity correction so the output matches the hand calculation. SciPy applies a continuity correction to 2 × 2 tables by default; with it, this example gives χ² = 3.93 and p = 0.047. For the full test, see the chi-square test guide.
After the test: which cells drive the result?
A significant χ² says the variables are associated, not where. Compare observed with expected cell by cell, or use standardized residuals, and report strength of association with a measure such as Cramér’s V.
Frequently asked questions
What is a contingency table?
A table of counts for the combinations of two categorical variables, with rows for one variable, columns for the other, and totals along the margins.
How do I read a contingency table?
Identify rows and columns, check that margins add to the grand total, pick the right percentage (row, column or total) for your question, and compare observed counts with expected counts.
How do I make a contingency table?
Tally each record into the cell matching its pair of categories, add row, column and grand totals, and verify both sets of totals equal n. Software such as a PivotTable or a crosstab command does the tally.
How do I construct a contingency table by hand?
Follow the five steps above: set up the categories, tally, total, check, and add labelled percentages.
Sources
Date checked for all sources: 2026-10-29. Example 1 uses illustrative counts and is not drawn from a study.
- NIST/SEMATECH e-Handbook of Statistical Methods, 7.4.5 Contingency tables and the chi-square test: https://www.itl.nist.gov/div898/handbook/prc/section4/prc45.htm
- NIST/SEMATECH e-Handbook, chi-square critical values table: https://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm
- OpenStax Introductory Statistics, 11.4 Test of Independence (LibreTexts): https://stats.libretexts.org/Bookshelves/Introductory_Statistics/Introductory_Statistics_(OpenStax)/11%3A_The_Chi-Square_Distribution/11.04%3A_Test_of_Independence








