Chi-Square Goodness-of-Fit Test in R: Observed vs Expected Proportions

Test whether one categorical variable’s proportions match a theoretical distribution — equal or unequal expected proportions, with pairwise post-hoc

Biostatistics

Learn to run the chi-square goodness-of-fit test in R — compare the observed proportions of one categorical variable to expected (theoretical) proportions, equal or unequal. Use rstatix chisq_test() with a p argument, follow up with pairwise post-hoc comparisons, and use the exact multinomial test for small samples.

Published

June 23, 2026

Modified

July 7, 2026

TipKey takeaways
  • The chi-square goodness-of-fit test compares the observed proportions of one categorical variable to expected (theoretical) proportions — does the data fit the expected distribution?
  • Run it with rstatix::chisq_test() (or base chisq.test()): for equal expected proportions pass just the counts; for unequal expected proportions add p = c(...).
  • A significant result means the observed proportions differ from expected.
  • Follow up with pairwise comparisons (pairwise_chisq_gof_test()) to see which categories differ.
  • For small samples (expected counts < 5), use the exact multinomial test (multinom_test()).

Introduction

The chi-square goodness-of-fit test works on a single categorical variable: it asks whether its observed category proportions match a set of expected proportions. Unlike the test of independence (which needs two variables and a two-way table), goodness-of-fit compares one variable’s distribution to a theoretical one.

The classic scenario: researchers collected wild tulips — 81 red, 50 yellow, 27 white — and ask whether the colours are equally distributed, or whether they fit an expected ratio. This lesson runs both cases with rstatix.

Note

Goodness-of-fit hypotheses. H₀: the observed proportions equal the expected proportions (the data fit the distribution). Hₐ: they differ. A significant result rejects the expected distribution.

The data: tulip colours

library(rstatix)

tulip <- c(red = 81, yellow = 50, white = 27)
tulip
   red yellow  white 
    81     50     27 

Equal expected proportions

Are the three colours equally frequent? If so, each expected proportion is 1/3. Pass just the counts — chisq_test() defaults to equal proportions:

library(rstatix)

tulip <- c(red = 81, yellow = 50, white = 27)
tulip %>% chisq_test()
# A tibble: 1 × 6
      n statistic          p    df method          p.signif
* <int>     <dbl>      <dbl> <dbl> <chr>           <chr>   
1     3      27.9 0.00000088     2 Chi-square test ****    

The colours are not equally distributed, χ²(2) = 27.9, p < 0.0001 — red is far more common than white. The same test in base R gives the identical statistic and p-value:

tulip <- c(red = 81, yellow = 50, white = 27)
chisq.test(tulip)                       # base R: equal proportions by default

    Chi-squared test for given probabilities

data:  tulip
X-squared = 27.886, df = 2, p-value = 8.803e-07

Unequal expected proportions

Do the colours fit a known 3 : 2 : 1 ratio (red : yellow : white)? That ratio gives expected proportions of 1/2, 1/3, 1/6. Pass them with p:

library(rstatix)

tulip <- c(red = 81, yellow = 50, white = 27)
tulip %>% chisq_test(p = c(1/2, 1/3, 1/6))
# A tibble: 1 × 6
      n statistic     p    df method          p.signif
* <int>     <dbl> <dbl> <dbl> <chr>           <chr>   
1     3     0.203 0.904     2 Chi-square test ns      

Here χ²(2) = 0.20, p = 0.90 — not significant, so the observed counts fit the 3 : 2 : 1 ratio well. (The order of p must match the order of the categories.)

Post-hoc: which categories differ?

When the equal-proportions test is significant, pairwise comparisons show which colours differ:

library(rstatix)

tulip <- c(red = 81, yellow = 50, white = 27)
pairwise_chisq_gof_test(tulip)
# A tibble: 3 × 8
      n group1 group2 statistic           p    df       p.adj p.adj.signif
* <int> <chr>  <chr>      <dbl>       <dbl> <dbl>       <dbl> <chr>       
1     2 red    yellow      7.34 0.00676         1 0.0135      *           
2     2 red    white      27    0.000000203     1 0.000000609 ****        
3     2 yellow white       6.87 0.00876         1 0.0135      *           

All three colours differ significantly from each other (p.adj < 0.05) — red vs white most strongly.

Small samples: the exact multinomial test

The chi-square approximation needs expected counts ≥ 5. For small samples, the exact multinomial test makes no approximation:

library(rstatix)

tulip <- c(red = 81, yellow = 50, white = 27)
multinom_test(tulip)
# A tibble: 1 × 2
            p p.signif
*       <dbl> <chr>   
1 0.000000711 ****    

(With these large counts it agrees with chi-square; the exact test matters when counts are small.)

Plot it

library(rstatix)
library(ggpubr)

tulip <- c(red = 81, yellow = 50, white = 27)
df <- data.frame(color = names(tulip), count = as.numeric(tulip))
stat.test <- chisq_test(tulip)

ggbarplot(df, x = "color", y = "count", fill = "color", palette = "jco",
          label = TRUE, ylab = "Count", xlab = "Tulip colour") +
  labs(subtitle = get_test_label(stat.test, detailed = TRUE))

A bar chart of observed tulip-colour counts — red 81, yellow 50, white 27 — with the goodness-of-fit chi-square p-value in the subtitle.

Report

A chi-square goodness-of-fit test showed that the tulip colours were not equally distributed, χ²(2) = 27.9, p < 0.001; pairwise comparisons found all three colours differed. However, the counts were consistent with the expected 3 : 2 : 1 ratio, χ²(2) = 0.20, p = 0.90.

Goodness-of-fit uses the same statistic as the independence test, \(\chi^2 = \sum_i \dfrac{(O_i - E_i)^2}{E_i}\), but the expected count for each category is \(E_i = N \times p_i\) (total × the hypothesized proportion), and the degrees of freedom are \(k - 1\) for \(k\) categories (one fewer than the number of categories, since the counts sum to \(N\)).

Try it live

Test a die for fairness — are the six faces equally likely? Edit the counts and re-run; the sandbox boots on first Run.

🟢 With an AI agent

Ask Prova “I have counts in categories and expected proportions — run a chi-square goodness-of-fit test and tell me whether my data fits” — it answers with rstatix code you can run on your own counts. The runtime is the judge. Ask Prova →

Common issues

You mixed up the order of p. The expected proportions in p = c(...) must be in the same order as the counts/categories — a mismatch tests the wrong hypothesis.

Expected counts are below 5. The chi-square approximation is unreliable — use the exact multinomial test (multinom_test()).

You have two variables, not one. Goodness-of-fit is for one categorical variable vs expected proportions. To test whether two variables are associated, use the chi-square test of independence.

Frequently asked questions

It tests whether the observed proportions of a single categorical variable match a set of expected (theoretical) proportions — for example, whether tulip colours are equally frequent, or fit a known ratio. A significant result means the data do not fit the expected distribution.

Goodness-of-fit uses one categorical variable and compares it to expected proportions. Independence uses two categorical variables and tests whether they are associated (from a two-way table). Same statistic, different setup.

Pass them to the p argument as a vector that sums to 1, in the same order as the categories — e.g. chisq_test(counts, p = c(1/2, 1/3, 1/6)) for a 3 : 2 : 1 ratio. With no p, the test assumes equal proportions.

Test your understanding

  1. Run it. In the live cell, test whether a die is fair (each face 1/6). Fill the blank. Is it fair?
  2. Conceptual. Your goodness-of-fit test against equal proportions is significant. What follow-up tells you which categories are over- or under-represented?

Fill the blank with chisq_test (no p = equal proportions). For question 2, recall the pairwise follow-up.

die <- c(8, 12, 9, 11, 6, 14)
chisq_test(die)   #> typically not significant for these counts — consistent with a fair die.

For question 2: run pairwise comparisons (pairwise_chisq_gof_test()) to see which categories differ, or inspect the standardized residuals to see which are over- (positive) or under-represented (negative).

TipWhich test when?
  • One categorical variable vs expected proportionschi-square goodness-of-fit (this lesson).
  • One variable, small counts → the exact multinomial test (multinom_test()).
  • Two categorical variables associated?chi-square test of independence.
  • One proportion vs an expected value → the proportion z-test / exact binomial.

Conclusion

You can now run the chi-square goodness-of-fit test in R: pass the counts to chisq_test() for equal expected proportions, add p = c(...) for unequal ones, follow a significant result with pairwise_chisq_gof_test(), and switch to the exact multinomial test for small samples. It is the test for checking whether one categorical variable fits a theoretical distribution.

Reuse

Citation

BibTeX citation:
@online{2026,
  author = {},
  title = {Chi-Square {Goodness-of-Fit} {Test} in {R:} {Observed} Vs
    {Expected} {Proportions}},
  date = {2026-06-23},
  url = {https://www.datanovia.com/learn/biostatistics/categorical/chi-square-goodness-of-fit-test-in-r},
  langid = {en}
}
For attribution, please cite this work as:
“Chi-Square Goodness-of-Fit Test in R: Observed Vs Expected Proportions.” 2026. June 23. https://www.datanovia.com/learn/biostatistics/categorical/chi-square-goodness-of-fit-test-in-r.