Skip to contents

Fit an univariate mixture of Normals model. stat_distrmix_line() and stat_distrmix_area() add a layer with prediction lines or areas, respectively, while stat_distrmix_eq() adds a layer with textual labels of parameter estimates.

Usage

stat_distrmix_eq(
  mapping = NULL,
  data = NULL,
  geom = "text_npc",
  position = "identity",
  ...,
  orientation = NA,
  method = "normalmixEM",
  method.args = list(),
  n.min = 10L * k,
  level = 0.95,
  k = 2,
  free.mean = TRUE,
  free.sd = TRUE,
  se = FALSE,
  fit.seed = NA,
  fm.values = TRUE,
  components = NULL,
  eq.with.lhs = TRUE,
  eq.digits = 2,
  label.x = "left",
  label.y = "top",
  hstep = 0,
  vstep = NULL,
  output.type = NULL,
  na.rm = FALSE,
  parse = NULL,
  show.legend = NA,
  inherit.aes = TRUE
)

stat_distrmix_line(
  mapping = NULL,
  data = NULL,
  geom = "line",
  position = "identity",
  ...,
  orientation = NA,
  method = "normalmixEM",
  se = NULL,
  quantiles = NA,
  fit.seed = NA,
  fm.values = FALSE,
  n = 500,
  fullrange = TRUE,
  level = 0.95,
  method.args = list(),
  k = 2,
  free.mean = TRUE,
  free.sd = TRUE,
  components = "all",
  n.min = 10L * k,
  na.rm = FALSE,
  show.legend = NA,
  inherit.aes = TRUE
)

stat_distrmix_area(
  mapping = NULL,
  data = NULL,
  geom = "area",
  position = "identity",
  ...,
  orientation = NA,
  method = "normalmixEM",
  se = NULL,
  quantiles = NA,
  fit.seed = NA,
  fm.values = FALSE,
  n = 500,
  fullrange = TRUE,
  level = 0.95,
  method.args = list(),
  k = 2,
  free.mean = TRUE,
  free.sd = TRUE,
  components = "sum",
  n.min = 10L * k,
  na.rm = FALSE,
  show.legend = NA,
  inherit.aes = TRUE
)

Arguments

mapping

The aesthetic mapping, usually constructed with aes(). Only needs to be set at the layer level if you are overriding the plot defaults.

data

A layer specific dataset, only needed if you want to override the plot defaults.

geom

The geometric object to use display the data

position

The position adjustment to use for overlapping points on this layer.

...

other arguments passed on to layer. This can include aesthetics whose values you want to set, not map. See layer for more details.

orientation

character Either "x" or "y" controlling the aesthetic to which the density model is fit. With the default orientation = NA the orientation used is based on the mapping and nearly always correct.

method

function or character If character, "normalmixEM" or the name of a model fit function are accepted, possibly followed by the fit function's method argument separated by a colon. The function must return a model fit object of class "mixEM".

method.args

named list with additional arguments. Not data or weights which are always passed through aesthetic mappings.

n.min

integer Minimum number of distinct values in the variable for fitting to the attempted. The default depends on k.

level

Level of confidence interval to use (0.95 by default).

k

integer Number of mixture components to fit.

free.mean, free.sd

logical If TRUE, allow the fitted mean and/or fitted sd to vary among the component Normal distributions.

se

logical If TRUE standard errors for the parameter estimates are returned in addition to the parameter estimates.

fit.seed

RNG seed argument passed to set.seed(). Defaults to NA, indicating that set.seed() should not be called.

fm.values

logical Add parameter estimates and their standard errors to the returned values (`FALSE` by default.)

components

character One of "all", "sum", or "members" select which densities are returned.

eq.with.lhs

If character the string is pasted to the front of the equation label before parsing or a logical (see note).

eq.digits

integer Number of digits after the decimal point to use for parameters in labels. If Inf, use exponential notation with three decimal places.

label.x, label.y

numeric with range 0..1 "normalized parent coordinates" (npc units) or character if using geom_text_npc() or geom_label_npc(). If using geom_text() or geom_label() numeric in native data units. If too short they will be recycled.

hstep, vstep

numeric in npc units, the horizontal and vertical step used between labels for different groups.

output.type

character One of "expression", "text", "markdown", "marquee", "latex", "latex.eqn", "latex.deqn" or "numeric".

na.rm

a logical indicating whether NA values should be stripped before the computation proceeds.

parse

logical Passed to the geom. If TRUE, the labels will be parsed into expressions and displayed as described in plotmath. Default is TRUE if output.type = "expression" and FALSE otherwise.

show.legend

logical. Should this layer be included in the legends? NA, the default, includes if any aesthetics are mapped. FALSE never includes, and TRUE always includes.

inherit.aes

If FALSE, overrides the default aesthetics, rather than combining with them. This is most useful for helper functions that define both data and aesthetics and shouldn't inherit behaviour from the default plot specification, e.g. borders.

quantiles

numeric The boundaries for quantiles given by P-values in the range 0 to 1, with NULL being equivalent to quantiles = c(0, 1).

n

Number of points at which to predict with the fitted model.

fullrange

logical Should the fit prediction span the full range of the plot, or just the range of the explanatory variable?

Value

The value returned by stat_distrmix_line() and by stat_distrmix_area() is a data frame, with n predictions values or possibly fewer in the case when fullrange = FALSE. Predictions for each component of the mixture plus for their sum are returned in long form.

The value returned by stat_distrmix_eq() is a data frame, with one row of estimates for each group of data in the plot.

Details

stat_distrmix_line() and stat_distrmix_area() are similar to stat_density but they fit the Normal distribution to observations. In addition to a single Normal distribution they can fit a mixture of two or more Normal distributions, using an approach related to clustering. Defaults related to how fitting is done are consistent between stat_distrmix_line(), stat_distrmix_area() and stat_distrmix_eq(). stat_distrmix_eq() can be used to add matched textual annotations, while stat_distrmix_line() and stat_distrmix_area() only differ in their default arguments, including the geom.

If k >= 2 a mixture-of-Normals model is fitted with normalmixEM(), while if k == 1 a single Normal distribution is fitted with function fitdistr(). Only for k == 1 the SE values are exact estimates.

In stat_distrmix_line(), predictions are computed to cover >= 0.999 of the integral in all cases, trimming to the range of the data with fullrange = FALSE is done as the last step in the computations. This ensures correct estimates of the cumulated density (CDF) and of quantiles, whose locations are estimated based on the CDF.

Parameter fit.seed if not NA is used in a call to set.seed() immediately before calling the model fit function. As the fitting procedure makes use of the (pseudo-)random number generator (RNG), convergence can depend on it, and in such cases setting fit.seed to the same value in stat_distrmix_line() and in stat_distrmix_eq() can ensure consistency, and more generally, reproducibility.

The minimum number of observations with distinct values in the data variable can be set through parameter n.min. The default depends on k, the number of components in the mix. Model fits with too few observations are unreliable, thus, using larger values of n.min than the default is wise. The value of n, instead, sets the number of predicted values, which affects the smoothness of the plotted curve and the accuracy with which the location of the quantiles is predicted.

Variables computed by stat_distrmix_line()

Some of the variables can have missing values or depend on orientation and/or method.

component

A factor indexing the components and/or their sum

density

predicted density values

quant.splits

integer, numbering sequentially the regions separated by the the quantiles

.

If fm.values = TRUE is passed then columns with diagnosis and parameters estimates are added, with the same value in each row within a group:

n

numeric the number of x values

.size

numeric the number of density values

fm.class

character the most derived class of the fitted model object

fm.method

character the method, as given by the ft field of the fitted model objects

This provides a simple and robust approach to achieve effects like colouring or hiding annotations by group depending on the outcome of model fitting.

Variables computed by stat_distrmix_eq()

Some of the variables depend on the orientation:

y

the location of text labels

eq.label

character string for equations

n.label

character string for number of observations

method.label

character string for model fit method

lambda

numeric the estimate of the contribution of the component of the mixture towards the joint density

mu

numeric the estimate of the mean

sigma

numeric the estimate of the standard deviation

component

A factor indexing the components of the mixture and/or their sum

If se = TRUE is passed then columns with standard errors for the parameter estimates are also returned:

mu.se

numeric the estimate of the mean

sigma.se

numeric the estimate of the standard deviation

If fm.values = TRUE is passed then columns with diagnosis and parameters estimates are added:

n

numeric the number of x values

.size

numeric the number of density values

fm.class

character the most derived class of the fitted model object

fm.method

character the method, as given by the ft field of the fitted model objects

Output types

The formatting of character strings to be displayed in plots are marked as mathematical equations. Depending on the geom used, the mark-up needs to be encoded differently, or in some cases mark-up not applied.

"expression"

The labels are encoded as character strings to be parsed into R's plotmath expressions.

"LaTeX", "TeX", "tikz", "latex"

The labels are encoded as 'LaTeX' maths equations, without the "fences" for switching in math mode.

"latex.eqn"

Same as "latex" but enclosed in single $, i.e., as in-line maths.

"latex.deqn"

Same as "latex" but enclosed in double $$, i.e., as display maths.

"markdown"

The labels are encoded as character strings using markdown syntax, with some embedded HTML.

"marquee"

The labels are encoded as character strings using markdown syntax, with 'marquee' supported spans.

"text"

The labels are plain ASCII character strings.

"numeric"

No labels are generated. This value is accepted by the statistics, but not by the label formatting functions.

NULL

The value used depends on the argument passed to geom.

If geom = "latex" (package 'xdvir') the output type used is "latex.eqn". If geom = "richtext" (package 'ggtext') or geom = "textbox" (package 'ggtext') the output type used is "markdown". If geom = "marquee" (package 'marquee') the output type used is "marquee". For all other values of geom the default is "expression". Invalid values as argument trigger an error.

Position of labels

When data are grouped by mapping a factor to an aesthetic, e.g., colour, shape and/or linetype the model is fitted separately to each group, and for each group a whole set of labels is generated. If the argument passed to label.y is a vector of length 1, this value determines the position of the equation and/or other labels for the first group, and the positions of the labels for the remaining groups are generated by adding vspace based on the group number. If the argument passed to label.y is a vector of length > 1, it is used unchanged, possibly extended by recycling, ignoring vstep.

If the labels are rotated by 90 degrees then the automatic stepping is best based on hstep with vstep = 0. Similarly as described above, if label.x is a vector of length > 1, it is used unchanged, possibly extended by recycling, ignoring hstep.

When using facets and with a grouping that does not repeat in each panel, the automatic positioning in most cases will not be the desired one. Manual positioning using a vector of length > 1 for label.x and/or label.y is the currently available workaround.

Which variables are available for mapping?

Computed variables and their names can vary depending on the method used to fit a model or the output.type in use. They can also depend for a given method on other arguments passed when fitting a model or extracting estimates and other computed values. In many cases, when values are not available, the variables are filled with NA values.

In the statistics returning formatted strings for use as annotations, a message is issued by default in interactive R sessions, listing the short names for available formatted labels as recognized by functions use_label() and f_use_label(), except when output.type = "numeric" is passed, in which case the names of all variables accessible by after_stat() within a call to aes() are listed. This default ("nicknames") can be changed by setting R option "ggpmisc.stat.vars.message" to one of "names", "colnames" or "none".

In the statistics that plot a prediction or more generally mainly return numeric variables, a message is issued by default in interactive R sessions, listing the names of all variables accessible by after_stat() within a call to aes() with at least some non-missing values. This default ("colnames") can be changed by setting R option "ggpmisc.stat.vars.message" to "none".

To explore the whole returned data frame for a given input we suggest the use of geom_debug().

See also

For the underlying computations see for mixes of two or more Normal distributions normalmixEM() and for a single Normal distribution fitdistr().

statistics from 'ggpmisc' for model fit annotations: stat_poly_eq(), stat_quant_eq(), stat_ma_eq() and stat_distrmix_eq(), and for model fit predictions: stat_poly_line(), stat_quant_line(), stat_quant_band(), stat_ma_line() and stat_distrmix_line().

Aesthetics

stat_distrmix_eq() understands the following aesthetics. Required aesthetics are displayed in bold and defaults are displayed for optional aesthetics:

x or y
groupafter_stat(component)
hjust"inward"
labelafter_stat(eq.label)
npcxafter_stat(npcx)
npcyafter_stat(npcy)
vjust"inward"

stat_distrmix_line() understands the following aesthetics. Required aesthetics are displayed in bold and defaults are displayed for optional aesthetics:

x or y
groupafter_stat(component)
weightNULL

Learn more about setting these aesthetics in vignette("ggplot2-specs").

Examples

ggplot(faithful, aes(x = waiting)) +
  stat_distrmix_line() +
  stat_distrmix_eq()


ggplot(faithful, aes(x = waiting)) +
  stat_distrmix_area() +
  stat_distrmix_eq()


ggplot(faithful, aes(x = waiting)) +
  stat_distrmix_area(aes(fill = after_stat(quant.splits != 2)),
                     quantiles = c(0.025, 0.975),
                     show.legend = FALSE)


ggplot(faithful, aes(x = waiting)) +
  stat_distrmix_line(components = "sum") +
  stat_distrmix_eq(use_label("eq", "n", "method"))


ggplot(faithful, aes(x = waiting)) +
  stat_distrmix_line(components = "sum") +
  stat_distrmix_eq(geom = "label_npc")


ggplot(faithful, aes(x = waiting)) +
  stat_distrmix_line(components = "sum") +
  stat_distrmix_eq(geom = "text", label.x = "center", label.y = "bottom")


ggplot(faithful, aes(x = waiting)) +
  stat_distrmix_line(components = "sum") +
  stat_distrmix_eq(geom = "text", hjust = "inward")


ggplot(faithful, aes(x = waiting)) +
  stat_distrmix_line(components = "members") +
  stat_distrmix_eq(components = "members")


ggplot(faithful, aes(x = waiting)) +
  stat_distrmix_line(components = "members") +
  stat_distrmix_eq(components = "members", se = TRUE)


ggplot(faithful, aes(y = waiting)) +
  stat_distrmix_line(components = "sum") +
  stat_distrmix_eq(label.x = "right")


ggplot(faithful, aes(x = waiting)) +
  geom_histogram(aes(y = after_stat(density)), bins = 20) +
  stat_distrmix_line(aes(colour = after_stat(component),
                         fill = after_stat(component)),
                     geom = "area", linewidth = 1, alpha = 0.25) +
  stat_distrmix_eq(aes(colour = after_stat(component)))


ggplot(faithful, aes(x = waiting)) +
  stat_distrmix_line(aes(colour = after_stat(component),
                         fill = after_stat(component)),
                     geom = "area", linewidth = 1, alpha = 0.25,
                     components = "members") +
 stat_distrmix_eq(aes(colour = after_stat(component)),
                     components = "members")


ggplot(faithful, aes(x = waiting)) +
  stat_distrmix_line(geom = "area", linewidth = 1, alpha = 0.25,
                     colour = "black", outline.type = "upper",
                     components = "sum", se = FALSE) +
 stat_distrmix_eq(components = "sum")


# special case of no mixture
ggplot(subset(faithful, waiting > 66), aes(x = waiting)) +
  stat_distrmix_line(k = 1) +
  stat_distrmix_eq(k = 1)
#> With k = 1 one Normal distribution is fitted. Irrelevant parameters ignored!
#> With k = 1 one Normal distribution is fitted. Irrelevant parameters ignored!


ggplot(subset(faithful, waiting > 66), aes(x = waiting)) +
  stat_distrmix_line(k = 1) +
  stat_distrmix_eq(k = 1, se = TRUE)
#> With k = 1 one Normal distribution is fitted. Irrelevant parameters ignored!
#> With k = 1 one Normal distribution is fitted. Irrelevant parameters ignored!