Written and maintained by CASRAI Editorial Board
Last updated
A polychoric correlation estimates the correlation between two unobserved, continuous variables from their ordered-category versions, such as two Likert items. It is the ordinal-data counterpart of the tetrachoric correlation (two binary variables), and it is the input that ordinal factor analysis and structural equation models are built on. In the worked example below, the same 200 responses give r = .53 with Pearson on the integer codes and a polychoric estimate of about .64.
What is a polychoric correlation? (definition)
A polychoric correlation is the estimated Pearson correlation between two latent continuous variables, inferred from a table of counts in which each variable has been cut into ordered categories. The R package polycor states the assumption directly: the two ordinal variables are taken to “dissect continuous latent variables that are bivariate normal.” The psych package manual describes the result as the “(inferred) latent Pearson correlation” and says the polychoric case generalizes the tetrachoric to an n × m table.
The reason to use it: when a five-point item is scored 1 to 5 and fed to Pearson’s r, the coarse categories attenuate the correlation. The CASRAI guides on exploratory factor analysis and confirmatory factor analysis note the related consequence for factor models, where Pearson on coarse items can create spurious “difficulty factors” that group items by response distribution.
Polychoric correlation formula, with every symbol explained
Assume two latent variables ξ and η, each standard normal, with correlation ρ. The observed ordinal variables are cut points of these latent variables:
- X = i when τi−1 < ξ ≤ τi, where τ0 = −∞ and τr = +∞ for r row categories.
- Y = j when υj−1 < η ≤ υj, defined the same way for c column categories.
- Φ2(a, b; ρ) is the standard bivariate normal cumulative distribution function.
The probability of cell (i, j) is the rectangle probability
πij = Φ2(τi, υj; ρ) − Φ2(τi−1, υj; ρ) − Φ2(τi, υj−1; ρ) + Φ2(τi−1, υj−1; ρ)
and the log-likelihood is Σij nij ln πij, where nij is the observed count in the cell. The polychoric correlation is the ρ that maximizes it. This is the model of Olsson (1979, Psychometrika 44:443-460).
Two-step estimate vs. full maximum likelihood
The polycor::polychor manual describes two options. The default (ML = FALSE) is a quicker two-step approximation: thresholds are estimated first from each variable’s marginal distribution, then a one-dimensional likelihood is maximized for the correlation. Thresholds come from the cumulative marginal proportions through the inverse normal CDF, for example τi = Φ−1(cumulative row proportion). With ML = TRUE the thresholds and the correlation are estimated jointly. The manual adds that the standard error from the two-step route “treats the thresholds as fixed.”
Worked example: a 3 × 3 table of 200 responses
Two three-category items (1 = low, 2 = middle, 3 = high), 200 respondents. Counts, with rows for item X and columns for item Y:
| Y = 1 | Y = 2 | Y = 3 | Row total | |
|---|---|---|---|---|
| X = 1 | 40 | 20 | 5 | 65 |
| X = 2 | 18 | 40 | 17 | 75 |
| X = 3 | 4 | 22 | 34 | 60 |
| Column total | 62 | 82 | 56 | 200 |
Step 1, thresholds from the margins. Cumulative row proportions are 65/200 = .325 and (65 + 75)/200 = .700, so τ1 = Φ−1(.325) = −0.454 and τ2 = Φ−1(.700) = 0.524. Cumulative column proportions are 62/200 = .310 and 144/200 = .720, so υ1 = Φ−1(.310) = −0.496 and υ2 = Φ−1(.720) = 0.583.
Step 2, the correlation. With the four thresholds fixed, compute the nine cell probabilities πij for a trial ρ and add up each cell’s count times the log of its probability. The log-likelihood is −414.88 at ρ = .30, −409.84 at .40, −405.94 at .50, −403.64 at .60 and −404.08 at .70, so the maximum lies between .60 and .70. A one-dimensional search puts it at ρ = .641 (log-likelihood −403.38). At that value the model’s expected counts (38.8, 22.4, 3.9 / 19.2, 37.9, 17.8 / 4.0, 21.7, 34.3) sit close to the observed table.
Comparison with Pearson on integer codes. Coding the categories 1, 2, 3 gives means of 1.975 (X) and 1.970 (Y), a cross-product sum of 64.85, and sums of squares of 124.875 (X) and 117.82 (Y). So r = 64.85 / √(124.875 × 117.82) = 64.85 / 121.30 = .535. The polychoric estimate (.64) is larger because it estimates the correlation of the underlying continuous variables rather than of the three-step codes.
How to interpret a polychoric correlation
Read the sign and size as you would for Pearson’s r (see the correlation coefficient guide), but remember it describes the latent variables, not the category codes. The table below covers situations that come up when you use the estimate; each row rests on the package manuals or the example above.
| What you see | What it means | Basis |
|---|---|---|
| Polychoric is larger than Pearson on the same items | Expected: coarse categories attenuate Pearson. In the example, .64 vs. .53. | Worked example; CASRAI EFA and CFA guides |
| Cells with zero counts | The psych manual documents a continuity correction (correct) that replaces zero-count cells with .5 in the tetrachoric case; check the manual for your function version before relying on it for larger tables |
psych manual |
| Items have different numbers of response options | Use global = FALSE in polychoric, which the manual describes as equivalent to the polycor solution, one correlation at a time |
psych manual |
| The matrix is not positive definite | The smooth option in psych is meant for this case |
psych manual |
| Standard error from the two-step method | Treats thresholds as fixed, so it does not carry threshold uncertainty | polycor manual |
Assumptions and common mistakes
- Bivariate normality of the latent variables. This is the stated model in
polycor. If the latent pair is not bivariate normal, the estimate is a model-based quantity, not a free-standing descriptive statistic. - Ordered categories. The method needs ordinal variables. For unordered categories use an association measure such as Cramér’s V; for two binary variables see the phi coefficient and note that the tetrachoric is the binary latent-variable analogue.
- Treating it as a description of the responses. It estimates a latent correlation. Report raw category distributions alongside it.
- Forgetting that the measurement level matters. Check whether the items are genuinely ordinal; see levels of measurement and the notes on analysing Likert items.
How to report a polychoric correlation (APA style)
State the estimator, the sample size and the number of categories, and use italic statistical symbols with no leading zero for values that cannot exceed 1. A template: “The polychoric correlation between item 1 and item 2 (three response categories each) was rpc = .64 (two-step estimate, N = 200).” Name the software and package with their versions in the methods section, and cite Olsson (1979) for the estimator. If you report Pearson for comparison, label it as computed on integer codes. For how to present a whole matrix, see how to read a correlation matrix.
Polychoric correlation in R, Python and Excel
R
Two documented routes. polycor::polychor accepts a contingency table or two ordered factors and returns the correlation with a standard error:
library(polycor)
tab <- matrix(c(40,18,4, 20,40,22, 5,17,34), nrow = 3) # columns = Y levels
polychor(tab, std.err = TRUE) # two-step (default)
polychor(tab, ML = TRUE, std.err = TRUE) # joint ML
For a data frame of items, psych::polychoric(df) returns the whole matrix, with the correct, smooth and global options described above. In lavaan, declare items with ordered=; the lavaan tutorial states that lavaan then switches to the WLSMV estimator, which uses diagonally weighted least squares, so you do not build the matrix by hand for a factor model.
Python
The same two-step logic in a few lines of NumPy and SciPy: thresholds from norm.ppf of the cumulative proportions, cell probabilities from bivariate normal CDF rectangles, then a bounded one-dimensional search over ρ. On the table above this reproduces .64.
import numpy as np
from scipy.stats import norm, multivariate_normal
from scipy.optimize import minimize_scalar
def polychoric(table):
t = np.asarray(table, float); n = t.sum()
ex = np.r_[-8, norm.ppf(np.cumsum(t.sum(1))[:-1] / n), 8]
ey = np.r_[-8, norm.ppf(np.cumsum(t.sum(0))[:-1] / n), 8]
def negll(r):
mv = multivariate_normal([0, 0], [[1, r], [r, 1]])
F = np.array([[mv.cdf([a, b]) for b in ey] for a in ex])
p = F[1:, 1:] - F[:-1, 1:] - F[1:, :-1] + F[:-1, :-1]
return -(t * np.log(p)).sum()
return minimize_scalar(negll, bounds=(-.99, .99), method='bounded').x
print(round(polychoric([[40,20,5],[18,40,17],[4,22,34]]), 2)) # 0.64
Excel
Step 1 is easy in a spreadsheet: cumulative marginal proportions passed to NORM.S.INV give the thresholds (for example =NORM.S.INV(0.325) returns −0.454). Step 2 needs the bivariate normal likelihood maximized over ρ, which is impractical with built-in worksheet functions, so use R or Python for the final estimate and Excel only to check the thresholds.
Frequently asked questions
What is the difference between polychoric and tetrachoric correlation?
The tetrachoric correlation is the two-category case; the polychoric generalizes it to tables with more rows and columns (psych manual).
When should I use polychoric instead of Pearson or Spearman?
Use it when the variables are ordered categories believed to come from underlying continuous variables, especially for factor analysis of Likert items. Spearman is a rank correlation of the observed codes and does not estimate a latent correlation.
Does lavaan use polychoric correlations?
The lavaan tutorial describes ordered data handling through ordered= and the WLSMV (DWLS) estimator, and notes that full information maximum likelihood is not supported for it. The CASRAI CFA guide explains how WLSMV relates to polychoric correlations.
Is the two-step estimate good enough?
The two-step method is the quicker approximation; polycor offers full ML for jointly estimated thresholds and correlation. In the example the thresholds come from the margins, so the correlation is estimated conditional on them.
Sources
polycorpackage: polychor reference manual, CRAN. search.r-project.org/CRAN/refmans/polycor/html/polychor.html (checked 2026-10-29). Lists Drasgow (1986), “Polychoric and polyserial correlations,” and Olsson (1979).psychpackage: tetrachoric and polychoric reference manual, CRAN. search.r-project.org/CRAN/refmans/psych/html/tetrachor.html (checked 2026-10-29).- lavaan tutorial, “Categorical data.” lavaan.ugent.be/tutorial/cat.html (checked 2026-10-29).
- Olsson, U. (1979). Maximum likelihood estimation of the polychoric correlation coefficient. Psychometrika, 44, 443-460.








