\name{bixplot}
\alias{bixplot}

\title{
Boxplot version suited for bimodal and multimodal data, combining density, box,
and rug elements with automatic cluster detection
}
\description{
Draws a bixplot for one or more numeric variables. A bixplot extends
the violin plot and boxplot by automatically testing each variable for
unimodality (via Hartigan's dip test) and, when multimodality is
detected, fitting a constrained k-medoids clustering to identify and
separately display the modes. Each variable is rendered as a filled
density body, a box-and-whisker summary, and a rug of individual
data values. The rug can optionally be colored by an external numeric
or factor variable. Input can be provided as vectors, a data frame, a
matrix, a list of vectors, or a formula.
}
\usage{
bixplot(...,
        names = NA,
        add = FALSE,
        at = NULL,
        horizontal = FALSE,
        col = "gray60", bodyCol = NULL,
        bodyOpaque = 0.5,
        bodyW = 0.80,
        bodysize = "area_from_count",
        modeCol = c("cadetblue3", "hotpink2",
                    "lawngreen", "orange", "cyan3",
                    "coral2", "gray60", "darkgoldenrod2",
                    "slateblue2", "brown2", "khaki3"),
        curveCol = "black",
        curveLwd = 1,
        border = "black", boxCol = NULL,
        boxOpaque = 0.5,
        boxW = 0.32,
        boxLwd = 2,
        makewhiskers = TRUE,
        innerwhiskers = TRUE,
        rugCol = "black",
        rugoutCol = par("fg"),
        rugNumeric = NULL,
        rugNumericColors = c("blue", "green"),
        colorbarW = 0.12,
        rugFactor = NULL,
        rugFactorColors = c("red", "blue", "forestgreen"),
        rugLwd = par("lwd"),
        rugoutLwd = 2 * rugLwd,
        rugOpaque = 0.4,
        jittering = TRUE,
        jitteramount = NULL,
        rugW = 0.12,
        stickCol = "black",
        stickLwd = 2,
        boxwex = 1,
        width = NULL,
        side = "no",
        tick = TRUE,
        diplevel = 0.01,
        minN = 15,
        kmax = NULL,
        bigN = 500,
        clusMinN = 3,
        bw = "SJ-dpi",
        kernel = "gaussian",
        cut = 3,
        cutmin = -Inf,
        cutmax = Inf,
        xlim = NULL,
        ylim = NULL,
        cex.axis = 1,
        main = "bixplot",
        cex.main = 1, line.main = 1,
        cex.colorbar = 1,
        xlab = "",
        ylab = "",
        cex.lab = 1, line.lab = 2.2,
        xaxs = "r", yaxs = "r", las = 1,
        ann = !add,
        plot = TRUE,
        log = NULL)
}

\arguments{
\item{...}{one or more numeric vectors to plot, or a single data
frame or matrix whose columns are plotted, or a single list of
numeric vectors (which may have different lengths), or a formula
such as \code{y ~ grp} where \code{y} is a numeric vector to be
split into groups by the grouping variable \code{grp} (usually a
factor). Note that \code{~ g1 + g2} is equivalent to
\code{~ g1:g2}. When a formula is used it may be necessary to also
supply a \code{data =} argument. Missing values are silently
removed.}
\item{names}{character vector of group labels printed next to each
variable. If \code{NA} (the default), labels are extracted from
the input data; if the data carry no names the integers
\code{1, 2, 3, \ldots} are used. [Argument from
\code{\link[graphics]{boxplot}}.]}
\item{add}{logical. If \code{TRUE}, the bixplot is added to the
current plot without drawing new axes. Defaults to \code{FALSE}.
[Argument from \code{boxplot}.]}
\item{at}{numeric vector specifying the axis positions at which the
bixplots are drawn, particularly useful when \code{add = TRUE}.
Defaults to \code{1:p} where \code{p} is the number of variables.
[Argument from \code{boxplot}.]}
\item{horizontal}{logical. If \code{TRUE}, bixplots are drawn
horizontally. Defaults to \code{FALSE} (vertical). [Argument from
\code{boxplot}.]}
\item{col}{color(s) for the density body of variables deemed
unimodal. Can be a single color or a vector recycled to length
\code{p}. When \code{NA}, the body is not filled. [Argument from
\code{boxplot}.]}
\item{bodyCol}{if not \code{NULL}, overrides \code{col} for the
body fill color.}
\item{bodyOpaque}{numeric between 0 (transparent) and 1 (opaque)
controlling the opacity of the filled density body. Defaults to
\code{0.5}.}
\item{bodyW}{numeric vector (recycled to length \code{p}) giving the
width of the widest density body for each variable. When two or
more modes are shown for a variable, this is the width of the
widest mode body. Set to zero to suppress the body entirely.
Defaults to \code{0.80}.}
\item{bodysize}{character string determining how the density bodies
of individual modes within the same variable are sized relative to
each other. One of \code{"area_from_count"} (the default; body
area proportional to cluster membership count),
\code{"area_is_constant"} (all modes have equal area), or
\code{"width_is_constant"} (all modes have equal width). In all
cases the widest body has width \code{bodyW[j]}, possibly scaled
by \code{width}.}
\item{modeCol}{color(s) of the density bodies for variables with
more than one detected mode (cluster). Cycled across all modes of
all multimodal variables. If \code{NULL}, each mode inherits the
\code{col} color of its variable.}
\item{curveCol}{color(s) of the density curve boundary drawn around
each body. Can be a single color or a vector recycled to length
\code{p}. Set to \code{NA} to suppress the curve. Defaults to
\code{"black"}.}
\item{curveLwd}{single number giving the line width of the density
curve.}
\item{border}{color(s) of the box and whiskers. Can be a single
color or a vector recycled to length \code{p}. Set to \code{NA}
to suppress the box. [Argument from \code{boxplot}.]}
\item{boxCol}{if not \code{NULL}, overrides \code{border} for the
box color.}
\item{boxOpaque}{numeric between 0 and 1 controlling the opacity of
the lines making up the boxplot. Defaults to \code{0.5}.}
\item{boxW}{numeric vector (recycled to length \code{p}) giving the
width of the interquartile box for each variable. Set to zero to
suppress the box. Defaults to \code{0.32}.}
\item{boxLwd}{single number giving the line width of the box and
whiskers.}
\item{makewhiskers}{logical. If \code{TRUE} (the default), whiskers
are drawn extending from the box to the most extreme non-outlying
values.}
\item{innerwhiskers}{logical. Relevant only when a variable has more
than one mode. If \code{TRUE}, whiskers are also drawn between
adjacent modes. If \code{FALSE}, inner whiskers are
omitted because their interpretation is ambiguous when mode
densities overlap.}
\item{rugCol}{color(s) of the rug tick marks for each variable.
Can be a single color or a vector recycled to length \code{p}.
Set to \code{NA} to suppress the rug. Defaults to
\code{"black"}.}
\item{rugoutCol}{color(s) of the portion of rug lines that extends
outside the density body. If \code{NULL} or \code{NA}, the same
color as \code{rugCol} is used throughout. Defaults to
\code{par("fg")}.}
\item{rugNumeric}{optional numeric vector of the same length as each
\code{y} variable (requires all variables to have the same length)
used to color the rug lines via a continuous color palette. Cannot
be combined with \code{rugFactor}.}
\item{rugNumericColors}{vector of two or three colors passed to
\code{\link[grDevices]{colorRampPalette}} to construct the palette
for \code{rugNumeric}. Defaults to \code{c("blue", "green")}.}
\item{colorbarW}{width of the color bar legend for \code{rugNumeric}
relative to the main plot width. Only has an effect when
\code{rugNumeric} is specified. Defaults to \code{0.12}.}
\item{rugFactor}{optional factor variable of the same length as each
\code{y} variable used to color the rug lines by factor level.
Cannot be combined with \code{rugNumeric}.}
\item{rugFactorColors}{character vector of colors for the levels of
\code{rugFactor}, recycled as needed. Defaults to
\code{c("red", "blue", "forestgreen")}.}
\item{rugLwd}{single number giving the line width of the rug marks.
Defaults to \code{par("lwd")}.}
\item{rugoutLwd}{single number giving the line width of the part of
rug lines outside the density body. Defaults to
\code{2 * rugLwd} to make isolated points visually prominent.}
\item{rugOpaque}{numeric between 0 and 1 controlling the opacity of
the rug marks. Defaults to \code{0.4}.}
\item{jittering}{logical. If \code{TRUE} (the default), rug tick
positions are jittered via \code{\link[base]{jitter}} to make tied
values distinguishable.}
\item{jitteramount}{amount of jittering passed to
\code{\link[base]{jitter}}. If \code{NULL}, the default amount is
used.}
\item{rugW}{numeric vector (recycled to length \code{p}) giving the
width of the rug (i.e. the length of each tick mark). Set to zero
to suppress the rug. Defaults to \code{0.12}.}
\item{stickCol}{color(s) of the vertical or horizontal "stick" drawn
when \code{side = "both"}, which separates the two half-bixplots
sharing an axis. Set to \code{NA} to suppress the stick. Can be a
single color or a vector recycled to length \code{p}. Defaults to
\code{"black"}.}
\item{stickLwd}{single number giving the line width of the stick.
Set to zero to suppress the stick. Defaults to \code{2}.}
\item{boxwex}{scale factor applied uniformly to the widths of the
body, box and rug across all bixplots. [Argument from
\code{boxplot}.] Defaults to \code{1}.}
\item{width}{optional numeric vector of length \code{p} giving the
relative widths of the bixplots. Entries must be strictly
positive; they are divided by their maximum so that the widest
bixplot has relative width 1. The resulting ratios multiply the
body, box and rug widths. If \code{NULL} (the default), all
bixplots have equal width. [Argument from \code{boxplot}.]}
\item{side}{character string specifying which side of the variable
axis the body, box and rug are drawn on. One of \code{"no"} (the
default; each bixplot is symmetric about its axis), \code{"first"}
or \code{"second"} (all half-bixplots on that side), or
\code{"both"} (adjacent variables are plotted on alternate sides
of shared axes, halving the number of axes needed). [Argument from
\code{\link[beanplot]{beanplot}}.]}
\item{tick}{logical indicating whether tick marks are drawn on the
group-label axis. Only has an effect when \code{add = FALSE}.
Defaults to \code{TRUE}.}
\item{diplevel}{significance level for Hartigan's dip test for
unimodality. A cluster search is only performed for variables
whose dip test p-value is at most \code{diplevel}. Defaults to
\code{0.01}. Increasing this value may yield more clusters;
decreasing it fewer.}
\item{minN}{minimum number of observations required per potential
cluster. Defaults to \code{15}. The maximum number of clusters
searched is bounded by \code{floor(n / minN)}. If
\code{n < 2 * minN}, clustering is not attempted.}
\item{kmax}{maximum number of clusters to consider. Internally
capped at 5. If \code{NULL} (the default), it is set to
\code{min(floor(n / minN), 5)}. Setting \code{kmax = 1} treats
all variables as unimodal and the display resembles a violin
plot.}
\item{bigN}{when a variable has more than \code{bigN} non-missing
values, a sample of \code{bigN} observations (always including
the minimum and maximum) is drawn without replacement before
computing densities and clusters, to reduce computation time.
Defaults to \code{500}; values below 300 are silently raised to
300.}
\item{clusMinN}{minimum number of unique values that each cluster
must contain. The constrained clustering enforces this bound.
Defaults to \code{3}.}
\item{bw}{bandwidth for \code{\link[stats]{density}}, used to
construct the density body. Can be a numeric value or a character
string naming a bandwidth selector (see \code{?density}).
Defaults to \code{"SJ-dpi"}.}
\item{kernel}{kernel for \code{\link[stats]{density}}. Defaults to
\code{"gaussian"}.}
\item{cut}{the density is computed from
\code{min(y) - cut * bw} to \code{max(y) + cut * bw}
where \code{bw} is the numeric bandwidth. Defaults to \code{3}.
[Argument from \code{beanplot}.]}
\item{cutmin}{if finite, the density of every variable and mode is
truncated to begin no lower than \code{cutmin}. Defaults to
\code{-Inf} (no effect). [Argument from \code{beanplot}.]}
\item{cutmax}{if finite, the density of every variable and mode is
truncated to end no higher than \code{cutmax}. Defaults to
\code{Inf} (no effect). [Argument from \code{beanplot}.]}
\item{xlim}{numeric vector of length 2 giving the limits of the
group axis (whether \code{horizontal} is \code{TRUE} or
\code{FALSE}). If \code{NULL}, limits are set automatically.
[Convention from \code{boxplot}.]}
\item{ylim}{numeric vector of length 2 giving the limits of the
numeric value axis (whether \code{horizontal} is \code{TRUE} or
\code{FALSE}). If \code{NULL}, limits are set automatically.
[Convention from \code{boxplot}.]}
\item{cex.axis}{character expansion factor for axis tick labels.}
\item{main}{title of the plot. Defaults to \code{"bixplot"}.}
\item{cex.main}{character expansion factor for the title.}
\item{line.main}{margin line for the title (passed to
\code{\link[graphics]{title}}).}
\item{cex.colorbar}{character expansion factor for the color bar
axis labels when \code{rugNumeric} is used.}
\item{xlab}{label for the horizontal axis. Defaults to \code{""}.}
\item{ylab}{label for the vertical axis. Defaults to \code{""}.}
\item{cex.lab}{character expansion factor for axis labels.}
\item{line.lab}{margin line for axis labels. Defaults to
\code{2.2}.}
\item{xaxs}{axis interval calculation style for the x-axis
(see \code{\link[graphics]{par}}). Defaults to \code{"r"}.}
\item{yaxs}{axis interval calculation style for the y-axis.
Defaults to \code{"r"}.}
\item{las}{orientation of axis tick labels
(see \code{\link[graphics]{par}}). Defaults to \code{1}
(always horizontal).}
\item{ann}{logical indicating whether the plot should be annotated
with \code{xlab}, \code{ylab} and \code{main}. Defaults to
\code{!add}. [Argument from \code{boxplot}.]}
\item{plot}{if \code{TRUE} (the default), the summary list is
returned invisibly. If \code{FALSE}, it is also printed to the
console. [Argument from \code{boxplot}.]}
\item{log}{this argument from \code{boxplot} must be \code{NULL}.
If a non-\code{NULL} value is supplied, a warning is issued and
the argument is ignored, because a log transformation can change
the number and position of modes. Apply the log transform
explicitly to the data before calling \code{bixplot} if
desired.}
}

\details{
For each variable, \code{bixplot} proceeds as follows. Hartigan's
dip test is applied to test for unimodality; if the p-value exceeds
\code{diplevel}, or if the sample is too small (\code{n < 2 * minN}),
the variable is treated as unimodal (\code{k = 1}). Otherwise,
constrained k-medoids clustering (via \code{\link{pamc1d}}) is fitted
for \code{k = 2, \ldots, kmax} clusters, and the best \code{k} is
selected by the highest mean silhouette width (computed via
\code{\link[cluster]{silhouette}}). If no \code{k > 1} yields a
positive mean silhouette width, the variable is treated as unimodal.

A single global bandwidth is computed once per variable and reused
for the density of every mode, ensuring that density bodies are
comparable across modes. Mode sizes are scaled according to
\code{bodysize}.

When \code{side = "both"}, adjacent variables are paired and plotted
as half-bixplots on opposite sides of shared axes, and axis labels
are combined automatically from the names of each pair.

The arguments \code{rugNumeric} and \code{rugFactor} require all
variables to have the same number of observations. They cannot be
specified simultaneously.
}

\value{
A list returned invisibly when \code{plot = TRUE} (or visibly when
\code{plot = FALSE}), containing:

\item{call}{the matched call.}
\item{p}{the number of variables plotted.}
\item{<name_1>, <name_2>, \ldots}{one list entry per variable,
named after the variable. For a unimodal variable the list
contains \code{values} (the sorted non-missing observations) and
\code{fivenumbersummary} (the five-number summary from
\code{\link[stats]{fivenum}} used to draw the box). For a
multimodal variable the list additionally contains
\code{clustering} (an integer vector of cluster assignments) and
one sub-list per cluster named \code{cluster_1},
\code{cluster_2}, \ldots, each containing \code{members} and
\code{fivenumbersummary} for that cluster.}
}


\references{
Montalcini, C., Rousseeuw, P.J. (2025). The bixplot: A variation on the boxplot suited for bimodal data, \doi{10.48550/arXiv.2510.09276} (open access).
}

\author{
 P.J. Rousseeuw
}

\seealso{
\code{\link{pamc1d}},
\code{\link[cluster]{pam}},
\code{\link[cluster]{silhouette}},
\code{\link[diptest]{dip.test}},
\code{\link[stats]{density}},
\code{\link[graphics]{boxplot}}
}

\examples{
set.seed(1)
# A unimodal and a clearly bimodal variable
x1 <- rnorm(100)
x2 <- c(rnorm(60, mean = -3), rnorm(60, mean = 3))
bixplot(x1, x2, names = c("unimodal", "bimodal"),
        main = "Basic bixplot example")

# Formula interface, coloring rug by a factor
n <- 150
grp <- factor(rep(c("A", "B", "C"), each = 50))
y   <- c(rnorm(50, 0), rnorm(50, 4), rnorm(50, 8))
bixplot(y ~ grp, main = "Formula interface")

# Horizontal layout with an external numeric rug variable
set.seed(42)
vals <- c(rnorm(80, mean = -2), rnorm(80, mean = 2))
covariate <- runif(160)
bixplot(vals, horizontal = TRUE,
        rugNumeric = covariate,
        rugNumericColors = c("purple", "yellow"),
        main = "Horizontal bixplot with numeric rug")

# Side-by-side ("both") half-bixplots
bixplot(x1, x2, side = "both",
        names = c("unimodal vs bimodal"),
        main = "side = both")

# For more examples, we refer to the vignette:
\dontrun{
vignette("bixplot_examples")
}
}
