Fitting a finite mixture means picking the number of components, , and then defending it. The Dirichlet process (DP) mixture offers a way out: put a prior on mixtures with an unbounded number of components and let the data decide how many get used.
I tested that promise in a worked example on the enzyme data of Richardson & Green (1997): the activity of an enzyme that metabolises carcinogenic substances, measured in the blood of 245 unrelated people. The biological question is whether the population splits into slow and fast metabolisers. The fit answered that question clearly. It did not answer “how many clusters,” because that phrase turned out to name three different quantities, and the prior set the one people usually mean.
Two groups, but not two bell curves
About two thirds of the people sit in a tight pile near 0.2. The rest spread from about 0.6 to 2.9 in a wide, right-skewed hump. A mixture of normals needs more than two components to draw that shape, which is the first thing to hold on to: a mixture component is a kernel, not a population.

The enzyme data (grey histogram) with the density estimate from the fit described below. Figure from the worked example.
Stick-breaking: infinitely many weights from one parameter
A DP mixture says there are infinitely many components, with weights that sum to one. The weights come from breaking a stick of length 1. Break off a fraction , and that piece is . Break a fraction off what is left, and that is . Continue without end:
Each component draws its own parameters from a base distribution . The single concentration parameter sets how fast the stick is used up: small gives a few big weights, large gives many small ones.
That makes a prior on the number of clusters. With observations, the number of occupied components has a prior that depends only on and :
For the 245 people, expects about two clusters and about twenty. The popular hyperprior is not neutral either: it spreads the prior over 2 to 21 occupied clusters, with a median of 9. The expected count also keeps growing with . That is a modelling assumption (new data keep revealing new kinds), and not an obvious one for a genetic polymorphism.
The truncation that makes it a PyMC model
NUTS cannot sample infinitely many sticks, so the model truncates at components and gives the last weight the rest of the stick. The prior mass beyond averages , which is about for and . The truncated model is
T = 20
with pm.Model() as dp:
alpha = pm.Gamma("alpha", 2.0, 1.0)
w = pm.StickBreakingWeights("w", alpha=alpha, K=T - 1)
mu = pm.Normal("mu", y.mean(), 2 * y.std(), shape=T)
sigma = pm.InverseGamma("sigma", 2.0, 0.1, shape=T)
pm.NormalMixture("y", w=w, mu=mu, sigma=sigma, observed=y)
pm.StickBreakingWeights takes K as the number of breaks, so K=T - 1 returns weights. pm.NormalMixture marginalises the discrete assignments, which leaves a continuous model that NUTS can sample. Everything else depends on that. The inverse-gamma prior on matters too: a normal mixture’s likelihood is unbounded when one component shrinks onto a single point, and this prior puts no mass near zero.
The fit took 17 seconds with no divergences. Truncation needs its own check, because if were too small the last weight would be visibly positive. Here it averages , and the 99th percentile of the total weight in the last five components is 1.5%. Twenty components were enough.
Labels switch, so the usual table fails
The standard summary table for this fit looks alarming: up to 1.36 for the component means, and bulk effective sample sizes below 10 for some of them. This is not a sampling failure. The same density can be written with the components in a different order, so "" has no meaning of its own across chains. Stick-breaking tempers the problem, since component 1 tends to be the biggest, but does not remove it.
The repair is to diagnose quantities that do not care what the components are called: , the log density, and the fitted density at a grid of points.
The same rule decides what can be reported. The mean of component 3 cannot. Three things can: the density with its uncertainty band, the probability that two people share a cluster, and a single partition chosen by a stated loss.
Three numbers of clusters
Three quantities could be called the number of clusters:
- The number of components: by construction, and infinite for the untruncated process.
- The number of occupied clusters, : the number of distinct labels when each person is allocated to a component. The assignments were marginalised out, so they are drawn back afterwards, with person going to component with probability proportional to .
- The number of components with non-negligible weight, say more than 0.02.
They disagree. has a median of 7 and a long right tail, because under the DP prior a new cluster is always possible and a single outlying value is cheaply explained by one. Counting only components with more than 2% of the weight, or clusters with at least 5 people, gives 4 or 5. None of them is 2, the number of biological groups: the fast group is skewed, and normal kernels need two or three to draw it.
Compared with the prior, the data ruled out one to three clusters and very many clusters, but the posterior of stayed wide. Miller & Harrison (2014) showed this is not a small-sample accident: the DP mixture posterior of the number of clusters is inconsistent, and does not concentrate on the true number even with infinitely many observations from a finite mixture.
Who belongs with whom
Labels switch, but pairs do not. The posterior similarity matrix , the probability that persons and are in the same cluster, is label-invariant.

The co-clustering matrix from the worked example. People are sorted by activity, so clusters show up as blocks on the diagonal.
Nobody below about 0.45 is ever clustered with anybody above 0.6, so the slow/fast split is certain. The fast group is much less coherent: it is carved into two or three kernels in varying ways, and its pairwise probabilities sit between 0.3 and 0.8.
A report usually wants one partition, and the Bayesian answer is the one that minimises the posterior expected loss. Two losses are standard (Wade & Ghahramani 2018), and here they disagree. The partition that minimises the variation of information is 151 slow metabolisers, with activity up to 0.41, and 94 fast ones, from 0.47. Binder’s loss keeps five clusters, splitting both groups and isolating two people around 0.5. Neither is “the” answer.
The prior on the concentration decides the count
Refitting with fixed at 0.2 and at 5, and once more with the truncation cut to , changes almost nothing about the density: the four posterior median densities are nearly indistinguishable. The count is another matter.
The median of runs from 4 to 14. With 245 observations the prior on still decides the count, and learning only moves the choice to the hyperprior.
The same refits expose two truncation failures. With the last of 20 components carries 1.7% of the mass on average. With the last weight averages 0.23 and piles against the ceiling. Neither failure shows in the density or in the sampler diagnostics. Only the last-weight check catches it.
Where it breaks, and where it earns its keep
Mixing between orderings. The posterior has many unequal modes, one per plausible ordering of the components, and NUTS moves between them rarely. In the fit, one chain almost never visited while the others did in 35% to 50% of their draws, and the density at 0.15 had with a bulk effective sample size of 19. Samplers that update the assignments directly handle this. NUTS on the marginalised model cannot.
Counting. If the count is the goal, a sparse finite mixture (Malsiner-Walli, Frühwirth-Schnatter & Grün 2016) is better behaved: ten components and a symmetric Dirichlet prior with a tiny parameter , so superfluous components empty out. The recommended defeated NUTS, in one parameterisation with 70 divergences and in another with three chains that reported no divergences while frozen at step sizes of . With the sampler was healthy and the posterior put 75% on 4 or 5 occupied clusters. Its variation-of-information partition was the same 151 and 94.
Prediction does not pick a count either. By PSIS-LOO, two normal components are about 8 elpd behind the best model. From four components on, the finite mixtures, the DP and the sparse mixture are within 3.2 elpd of each other. That says it takes at least four normal kernels to draw a skewed density. It says nothing about how many kinds of people there are.
A flexible distribution inside a bigger model. This is where the DP mixture is most useful. The example’s second dataset is the Mayo Clinic trial in primary biliary cholangitis: 1,945 bilirubin measurements on 312 patients, each with their own slope on the log scale. Replacing the Normal distribution of slopes with a DP mixture gave a sharp peak of near-stable patients, a shoulder, and a long right tail of progressors. The Normal, forced to be symmetric, put 18% of patients on a falling trajectory. The DP put 14% there, and 18% against 15% in the tail where bilirubin doubles within two years. The two models’ intervals overlap, and the LOO difference is 6.7 elpd with a standard error of 7.4, so this is a shift of emphasis and not a decisive win.
What to do in practice
- Simulate the prior on the occupied count before fitting. and fix it, and with 245 observations it still decided the posterior count.
- Truncate, then check the last weight. Neither the density nor the sampler diagnostics warn when is too small.
- Diagnose label-free quantities. Use the density on a grid, the log density and . Ignore of the component parameters.
- Say which count you mean. Components, occupied clusters and clusters with non-negligible weight are different numbers, and none of them is the number of populations when the kernel has the wrong shape.
- Report the similarity matrix with any single partition, and name the loss that chose it.
- Use the DP for densities and for flexible distributions inside bigger models. That is where it is reliable.
The DP mixture kept its promise in the narrow sense: I never chose . What it gave back was a density, a certain slow/fast split, and a count that depends on a definition and a prior, both of which I still have to state.
Further reading: the full worked example has the data, every fit and the diagnostics. All empirical numbers above come from it. References, as cited there: Richardson & Green (1997), “On Bayesian analysis of mixtures with an unknown number of components,” JRSS B 59:731; Miller & Harrison (2014); Wade & Ghahramani (2018); Malsiner-Walli, Frühwirth-Schnatter & Grün (2016).