beeswarm/0000755000176200001440000000000014055524462012066 5ustar liggesusersbeeswarm/NAMESPACE0000644000176200001440000000073214045166730013306 0ustar liggesusersexport(beeswarm, bxplot, swarmx, swarmy) S3method(beeswarm, default) S3method(beeswarm, formula) S3method(bxplot, default) S3method(bxplot, formula) importFrom("grDevices", "extendrange", "xy.coords") importFrom("graphics", "axis", "box", "boxplot", "par", "plot", "points", "segments", "xinch", "yinch") importFrom("stats", "approx", "density", "na.omit", "quantile", "runif") importFrom("utils", "head", "tail") useDynLib(beeswarm, .registration = TRUE, .fixes = "C_") beeswarm/README.md0000644000176200001440000000232214020625103013326 0ustar liggesusersbeeswarm ======== [![CRAN_Status_Badge](http://www.r-pkg.org/badges/version/beeswarm)](https://cran.r-project.org/package=beeswarm) [![CRAN_Downloads_Badge](http://cranlogs.r-pkg.org/badges/beeswarm)](https://cran.r-project.org/package=beeswarm) An R package implementing bee swarm plots You can see some examples here: http://www.cbs.dtu.dk/~eklund/beeswarm/ Installation ------------ You can install the latest release on CRAN like this: install.packages("beeswarm") Or you can install the latest development version from GitHub like this: ## install.packages("devtools") devtools::install_github("aroneklund/beeswarm") Related works ------------- * [**ggbeeswarm**](https://cran.r-project.org/package=ggbeeswarm) bee swarm and related plots in the ggplot2 framework * [**pybeeswarm**](https://github.com/mgymrek/pybeeswarm) bee swarm plots in Python It's also known as a dot plot ----------------------------- Leland Wilkinson (1999). Dot plots. The American Statistician. 53(3):276-281. [PDF](https://www.cs.uic.edu/~wilkinson/Publications/dotplots.pdf) It seems to be the same basic point layout algorithm, except that Wilkinson suggests an additional smoothing step that the beeswarm package does not do. beeswarm/data/0000755000176200001440000000000014020625103012761 5ustar liggesusersbeeswarm/data/breast.RData0000644000176200001440000001411314020625103015156 0ustar liggesusersZ \LiO*M)%IM9gLjRn%&ʥtQDE-!ʥĆBn%벡tAZ*W3kۜy~˙cD[MAAAQAQbOt@ ̈􋜯S)w 1B=@cg^=߲[~+w?ɶ6'<_:sw{>_ͷ['RS-DWQs#ѕCN8exX$k\ȏ[uL?a AS^jτ7edhZ".|4۳Vl֘'ATs+^ts@-ꌻ+x*)@! 9c cAe.}NΆf㇟AY&< ]8 Ú^! ,e#?Jj'2c0Yв':Z.U[bGO rP (8;J>e@lcc=pL:3*@da^5 } )2FD9 zq@VzGY;m_>iM8IpyN`Ʒe@m|wnn.]2Sל^D^cTРcZAxmd?E'xpw=Tw1 Ƚ?v(>A؋nBOϐBo:2S5=DǍt yj'ViY2ʘ!״uJ}f[,jܐ"#.S9 K1FOU,dLj8?<$]g붬E``gxˠwJSYYs:&-7ŷ¸ Lt)g@-~S&J&A/R@U~6 =ao@o3Wp8[9-oJtQyY rkzk?qnpI@5ȟ'+wo#]?ƍ}>[;iN{ ds): L5DIˁj5Q Դ#'[@G} t:ܠb7No~-\9q7o-Uӝâ@?Dg7(i*7@q# 7H1״?rV|wdEĮQ0g =F榀 "#rmFkZdGA/b4PKWa<5RV3(rʵځ_W;a: Q4-s]wx ]Fi:`Β@8 @NwioU dUEy@v]4B#g5 m @y/\%P繍's)~==y0DϗȮ A?Oil}X# (jUhdH)As Aq*.qU J E [5Ձ]5*0ԛ^:Ju0lWf-CI7߶=-\RaIm`Ns npnqDdV JN2\qP=Z<@d>Nڵej PR|ޓ  "rUC{o?۫u^w1"wAdbq5[lz<>]N>sij=Tk@>\ xW.ď"h%r2п`+ur5~-^_kn<`zc .vN&8(sU{\ēg/ײ :Zzr끠y#ꮳ`2[R`_*MA( 5浿,k~Ð ;e)+@<`d>e(簊[@s?q;7*5p=Q-׬ܷ"2ĝ7 l* d:N7 D!r (ӵ UZ< 8i_-OBcԯrڴ @^J|}gEUh 乘ȟ?9VRZ:n+ӣ~{-@r =Yt=]xfUd\_jj{׀^k b j}\ Tu͋(n<7[L.Qہ.@ۙ -aե q| <sݪ~qE-k\uq^gEPΙU𥷤Zܺ=# !״=`E௞}5pw婠k 7vY7*v+?TܴJ@tP rC CG>>&'G_ޮg7)`f..`#E`0!ӋK/<#)_C1*tGD3zyk2aY2<8 ݈YӗQ~#Pc<& Aϗ Q }$c!4c_cnό3}r?c|; a B*[[1zAc$_;7sE~xF36Zas/Gn}c_F-u B/iWfl(Lm ׏Y̻X8)#e|ys16븈<02JCAHDe6eda|as/Ǡ#'2`:F6?3?ә}?111ya|,c";y,c؅## Nl*/a}D(#*+KANήf/`["(2BcS8VV%VY#(VVV%N`'K0XczYc2[c'[c'['c boedl&Kl$XHJ,^`A*TRL 8,p$XH&`zcQ" cE57,n$XH`q#aG ;(nx`l1^ m1m1|bܥw)G#bH1kHy#0e2L@& GKslJc+cEch,;vv,UYvfq%lņM`vRqصbvqaW ;8ذN-:;8Xjbe-~.e4UvH!zԒRllbcIb#RH+:+VaXbmMR|HIJk>RH#Śk>R,+*)BKiXb.)L^vaXkTه`\52 eX8ʰpa(Â2l@a% 2lԐaeQɇ_V.jlc&Ú KD/2˰.G&Ŭ7֑e쎌(1%4(11E>.úƘcza`hD4Øc'o%h쉂{ȥBnAcOh[X'IQƒhL`XR삍(X{Xf"Y[4V.h\XϣB;`J 54 @cEhXaj4V@hvఫ4V.h8v졖-v6Ja֗Ƥc0La;[!qulW4beeswarm/man/0000755000176200001440000000000014045166730012640 5ustar liggesusersbeeswarm/man/swarmx.Rd0000644000176200001440000000762514045166730014462 0ustar liggesusers\name{swarmx} \alias{swarmx} \alias{swarmy} \title{ Adjust 1-d data to separate coincident points } \description{Take a series of points lying in a horizontal or vertical line, and jitter them in the other dimension such that no points are overlapping. } \usage{ swarmx(x, y, xsize = xinch(0.08, warn.log = FALSE), ysize = yinch(0.08, warn.log = FALSE), log = NULL, cex = par("cex"), side = 0L, priority = c("ascending", "descending", "density", "random", "none"), fast = TRUE, compact = FALSE) swarmy(x, y, xsize = xinch(0.08, warn.log = FALSE), ysize = yinch(0.08, warn.log = FALSE), log = NULL, cex = par("cex"), side = 0L, priority = c("ascending", "descending", "density", "random", "none"), fast = TRUE, compact = FALSE) } \arguments{ \item{x, y}{ Coordinate vectors in any format supported by \code{\link{xy.coords}}. } \item{xsize, ysize}{ Width and height of the plotting character in user coordinates. } \item{log}{ Character string indicating which axes are logarithmic, as in \code{\link{plot.default}}, or \code{NULL} to figure it out automatically.} \item{cex}{ Relative plotting character size.} \item{side}{ Direction to perform jittering: 0: both directions; 1: to the right or upwards; -1: to the left or downwards.} \item{priority}{ Method used to perform point layout (see below).} \item{fast}{ Use compiled version of algorithm? This option is ignored for all methods except \code{"swarm"} and \code{"compactswarm"}.} \item{compact}{ Use compact layout? (see below)} } \details{ For \code{swarmx}, the input coordinates must lie in a vertical line. For \code{swarmy}, the input coordinates must lie in a horizontal line. \code{swarmx} adjusts coordinates to the left or right; \code{swarmy} adjusts coordinates up or down. \code{priority} controls the order in which the points are placed; this has generally has a noticeable effect on the resulting appearance. \code{"ascending"} gives the "traditional" beeswarm plot in which the points are placed in an ascending order. \code{"descending"} is the opposite. \code{"density"} prioritizes points with higher local density. \code{"random"} places points in a random order. \code{"none"} places points in the order provided. When \code{compact} is FALSE, points are placed in a predetermined order. When \code{compact} is TRUE, a greedy strategy is used to determine which point will be placed next. This often leads to a more tightly-packed layout. The strategy is very simple: on each iteration, a point that can be placed as close as possible to the non-data axis is chosen and placed. If there are two or more equally good points, \code{priority} is used to break ties. Usually it makes sense to call this function after a plotting device has already been set up (e.g. when adding points to an existing plot), so that the default values for \code{xsize}, \code{ysize}, and \code{log} will be appropriate. } \value{ A data frame with columns \code{x} and \code{y} with the new coordinates. } \seealso{ \code{\link{beeswarm}}, \code{\link{jitter}} } \examples{ ## Plot points in one dimension index <- rep(0, 100) values <- rnorm(100) plot(index, values, xlim = c(-0.5, 2.5)) points(swarmx(index + 1, values), col = 2) points(swarmx(index + 2, values, cex = 1.5), col = 3, cex = 1.5) ## Try the horizontal direction, with a log scale plot(values, index, log = "x", ylim = c(-1, 2)) points(swarmy(values, index + 1), col = 2) ## Newer examples using "side", "priority", and "compact" plot(c(-0.5, 3.5), range(values), type = 'n') points(swarmx(index + 0, values), col = 1) points(swarmx(index + 0.9, values, side = -1), col = 2) points(swarmx(index + 1.1, values, side = 1, priority = "descending"), col = 3) points(swarmx(index + 2 , values, priority = 'density'), col = 4) points(swarmx(index + 3 , values, priority = 'random'), col = 5) points(swarmx(index + 3 , values, priority = 'random', compact = TRUE), col = 5) } \keyword{ dplot } beeswarm/man/breast.Rd0000644000176200001440000000242414020625103014374 0ustar liggesusers\name{breast} \alias{breast} \docType{data} \title{ Lymph-node-negative primary breast tumors } \description{ Tumor molecular measurements and outcome from breast cancer patients. } \usage{data(breast)} \format{ A data frame with 286 observations on the following 5 variables. \describe{ \item{\code{ER}}{Estrogen receptor status (factor with levels \code{neg}, \code{pos})} \item{\code{ESR1}}{Expression of the ESR1 gene (numeric)} \item{\code{ERBB2}}{Expression of the ERBB2 gene (numeric)} \item{\code{time_survival}}{Time in months (numeric)} \item{\code{event_survival}}{Coded event: 0 = censored, 1 = metastasis (numeric)} } } \details{ ER, ESR1, and ERBB2 were measured on a tumor specimen taken at surgery (time = 0). ESR1 and ERBB2 expression values were determined by microarray probe sets 205225_at and 216836_s_at using RMA-normalized data. } \source{ Wang Y, Klijn JG, Zhang Y, Sieuwerts AM, Look MP, Yang F, Talantov D, Timmermans M, Meijer-van Gelder ME, Yu J, Jatkoe T, Berns EM, Atkins D, Foekens JA. Gene-expression profiles to predict distant metastasis of lymph-node-negative primary breast cancer. Lancet. 2005 Feb 19-25;365(9460):671-9. } \examples{ data(breast) with(breast, plot(ESR1, ERBB2, col = as.numeric(ER)) ) } \keyword{datasets} beeswarm/man/bxplot.Rd0000644000176200001440000000626314020625103014431 0ustar liggesusers\name{bxplot} \alias{bxplot} \alias{bxplot.default} \alias{bxplot.formula} \title{Plot quantile lines} \description{ Plot lines indicating the specified quantiles for each group. This function is intended as a simplified interpretation of \code{\link{boxplot}}, which can be combined with a \code{\link{beeswarm}} (or \code{\link{stripchart}}) plot. } \usage{ bxplot(x, \dots) \method{bxplot}{formula}(formula, data = NULL, \dots, subset, na.action = NULL) \method{bxplot}{default}(x, probs = c(0.25, 0.5, 0.75), vertical = TRUE, horizontal = !vertical, add = FALSE, col = par("col"), lty = par("lty"), lwd = NULL, at = NULL, width = 0.75, \dots) } \arguments{ \item{formula}{A formula, such as \code{y ~ grp}, where \code{y} is a numeric vector of data values to be split into groups according to the grouping variable \code{grp} (usually a factor).} \item{data}{A data.frame (or list) from which the variables in \code{formula} should be taken.} \item{subset}{An optional vector specifying a subset of observations to be used.} \item{na.action}{A function which indicates what should happen when the data contain \code{NA}s. The default is to quietly ignore missing values in either the response or the group.} \item{x}{A numeric vector, or a data frame or list of numeric vectors, each of which is considered as a group.} \item{probs}{A numeric vector of probabilities with values in [0,1]} \item{vertical, horizontal}{ Orientation of the plot. \code{horizontal} takes precedence if both are specified. } \item{add}{Add to an existing plot?} \item{col, lty}{Color and line type for each probability.} \item{lwd}{Line width for each probability (see below).} \item{at}{Numeric vector giving the locations where the swarms should be drawn; defaults to \code{1:n} where \var{n} is the number of groups.} \item{width}{Width of the lines.} \item{\dots}{Further arguments passed to \code{\link{boxplot}}.} } \details{ This function is intended as a minimalistic interpration of \code{\link{boxplot}}; however, the quantiles plotted by \code{bxplot} are not necessarily the same as the hinges plotted by a \code{boxplot}. Notice that specifying a vector of graphical parameters such as \code{lwd} or \code{col} will refer to each of \code{probs}, \emph{not} to each group in the data (as one might expect by analogy with \code{boxplot}). If \code{lwd} is \code{NULL}, and if the \code{probs} includes 0.5, \code{lwd} will be set to 3 times \code{par("lwd")} for probs=0.5, and \code{par("lwd")} for the others. (Thus something resembling the median line and hinges of a boxplot is produced by default.) } \value{ None.} \examples{ ## bxplot on bottom beeswarm(len ~ dose, data = ToothGrowth) bxplot(len ~ dose, data = ToothGrowth, add = TRUE) ## bxplot on top bxplot(decrease ~ treatment, data = OrchardSprays, probs = 0.5, col = 2) beeswarm(decrease ~ treatment, data = OrchardSprays, add = TRUE) ## Show deciles data(breast) bxplot(time_survival ~ event_survival, data = breast, probs = seq(0, 1, by = 0.1), col = rainbow(10)) beeswarm(time_survival ~ event_survival, data = breast, pch = 21, bg = "gray75", add = TRUE) } \keyword{ hplot } beeswarm/man/beeswarm.Rd0000644000176200001440000003006414045166730014737 0ustar liggesusers\name{beeswarm} \alias{beeswarm} \alias{beeswarm.default} \alias{beeswarm.formula} \title{Bee swarm plot} \description{ Create a bee swarm plot. A bee swarm plot is a one-dimensional scatter plot similar to \code{\link{stripchart}}, but with various methods to separate coincident points such that each point is visible. Also, \code{beeswarm} introduces additional features unavailable in \code{stripchart}, such as the ability to control the color and plotting character of each point. } \usage{ beeswarm(x, \dots) \method{beeswarm}{formula}(formula, data = NULL, subset, na.action = NULL, pwpch = NULL, pwcol = NULL, pwbg = NULL, pwcex = NULL, dlab, glab, \dots) \method{beeswarm}{default}(x, method = c("swarm", "compactswarm", "center", "hex", "square"), vertical = TRUE, horizontal = !vertical, cex = 1, spacing = 1, breaks = NULL, labels, at = NULL, corral = c("none", "gutter", "wrap", "random", "omit"), corralWidth, side = 0L, priority = c("ascending", "descending", "density", "random", "none"), fast = TRUE, pch = par("pch"), col = par("col"), bg = NA, pwpch = NULL, pwcol = NULL, pwbg = NULL, pwcex = NULL, do.plot = TRUE, add = FALSE, axes = TRUE, log = FALSE, xlim = NULL, ylim = NULL, dlim = NULL, glim = NULL, xlab = NULL, ylab = NULL, dlab = "", glab = "", \dots) } \arguments{ \item{formula}{A formula, such as \code{y ~ grp}, where \code{y} is a numeric vector of data values to be split into groups according to the grouping variable \code{grp} (usually a factor).} \item{data}{A data.frame (or list) from which the variables in \code{formula} should be taken.} \item{subset}{An optional vector specifying a subset of observations to be used.} \item{na.action}{A function which indicates what should happen when the data contain \code{NA}s. The default is to quietly ignore missing values in either the response or the group.} \item{x}{ A numeric vector, or a data frame or list of numeric vectors, each of which is plotted as an individual swarm.} \item{method}{ Method for arranging points (see Details). } \item{vertical, horizontal}{ Orientation of the plot. \code{horizontal} takes precedence if both are specified. } \item{cex}{ Size of points relative to the default given by \code{par("cex")}. Unlike other plotting functions, this must be a single value. (But see also the \code{pwcex} argument)} \item{spacing}{ Relative spacing between points.} \item{breaks}{ Breakpoints (optional). If \code{NULL}, breakpoints are chosen automatically. If \code{NA}, bins are not used (similar to \code{stripchart} with \code{method = "stack"}).} \item{labels}{ Labels for each group. Recycled if necessary. By default, these are inferred from the data. } \item{at}{ Numeric vector giving the locations where the swarms should be drawn; defaults to \code{1:n} where \var{n} is the number of groups. } \item{corral}{ Method to adjust points that would be placed outside their own group region (see Details). } \item{corralWidth}{ Width of the "corral" in user coordinates. If missing, a sensible value will be chosen. } \item{side}{ Direction to perform jittering: 0: both directions; 1: to the right or upwards; -1: to the left or downwards.} \item{priority}{ Order used to perform point layout when method is \code{"swarm"} or \code{"compactswarm"}; ignored otherwise (see Details).} \item{fast}{ Use compiled version of algorithm? This option is ignored for all methods except \code{"swarm"} and \code{"compactswarm"}.} \item{pch, col, bg}{ Plotting characters and colors, specified by group. Recycled if necessary (see Details). } \item{pwpch, pwcol, pwbg, pwcex}{ \dQuote{Point-wise} plotting characteristics, specified for each data point (see Details). } \item{do.plot}{ Draw a plot? } \item{add}{ Add to an existing plot? } \item{axes}{ Draw axes and box? } \item{log}{ Use a logarithmic scale on the data axis? } \item{xlim, ylim}{ Limits of the plot. } \item{dlim, glim}{ An alternative way to specify limits (see Details). } \item{xlab, ylab}{ Axis labels. } \item{dlab, glab}{ An alternative way to specify axis labels (see Details). } \item{\dots}{ Further arguments passed to \code{\link{plot}}. } } \details{ Several methods for placing the points are available; each method uses a different algorithm to avoid overlapping points. The default method, \code{swarm}, places points in increasing order. If a point would overlap an existing point, it is shifted sideways (along the group axis) by a minimal amount sufficient to avoid overlap. \code{breaks} is ignored. The other three methods first discretize the values along the data axis, in order to create more efficient packing: \code{square} places the points on a square grid, whereas \code{hex} uses a hexagonal grid. \code{center} uses a square grid to produce a symmetric swarm. By default, the number of breakpoints for discretization is determined by a combination of the available plotting area and the plotting character size. The discretization of the data can be explicitly controlled using \code{breaks}. If \code{breaks} is set to \code{NA}, the data will not be grouped into intervals; this may be a sensible option if the data is already discrete. In contrast to most other plotting functions, changing the size of the graphics device will often change the position of the points. The plotting characters and colors can be controlled in two ways. First, the arguments \code{pch}, \code{col} and \code{bg} can specify plotting characters and colors in the same way as \code{\link{stripchart}} and \code{\link{boxplot}}: in short, the arguments apply to each group as a whole (and are recycled if necessary). Alternatively, the \dQuote{point-wise} characteristics of each individual data point can be controlled using \code{pwpch}, \code{pwcol}, and \code{pwbg}, which override \code{pch}, \code{col} and \code{bg} if these are also specified. Likewise, \code{pwcex} controls the size of each point relative to the default (which may be adjusted by \code{cex}). Notably, the point layout algorithm is applied without considering the point-wise arguments; thus setting \code{pwcex} larger than 1 will usually result in partially overlapping points. These arguments can be specified as a list or vector. If supplied using the formula method, the arguments can be specified as part of the formula interface; i.e. they are affected by \code{data} and \code{subset}. The \code{dlab} and \code{glab} labels may be used instead of \code{xlab} and \code{ylab} if those are not specified. \code{dlab} applies to the continuous data axis (the Y axis unless \code{horizontal} is \code{TRUE}); \code{glab} to the group axis. Likewise, \code{dlim} and \code{glim} can be used to specify limits of the axes instead of \code{xlim} or \code{ylim}. This function is intended to be mostly compatible with calls to \code{\link{stripchart}} or \code{\link{boxplot}}. Thus, code that works with these functions should work with \code{beeswarm} with minimal modification. By default, swarms from different groups are not prevented from overlapping. Thus, large data sets, or data sets with uneven distributions, may produce somewhat unpleasing beeswarms. If this is a problem, consider reducing \code{cex}. Another approach is to control runaway points (those that would be plotted outside a region allotted to each group) with the \code{corral} argument: The default, \code{"none"}, does not control runaway points. \code{"gutter"} collects runaway points along the boundary between groups. \code{"wrap"} implements periodic boundaries. \code{"random"} places runaway points randomly in the region. \code{"omit"} omits runaway points. See Examples below. When using the \code{"swarm"} method, \code{priority} controls the order in which the points are placed; this generally has a noticeable effect on the resulting appearance. \code{"ascending"} gives the "traditional" beeswarm plot in which the points are placed in an ascending order. \code{"descending"} is the opposite. \code{"density"} prioritizes points with higher local density. \code{"random"} places points in a random order. \code{"none"} places points in the order provided. Whereas the \code{"swarm"} method places points in a predetermined order, the \code{"compactswarm"} method uses a greedy strategy to determine which point will be placed next. This often leads to a more tightly-packed layout. The strategy is very simple: on each iteration, a point that can be placed as close as possible to the non-data axis is chosen and placed. If there are two or more equally good points, \code{priority} is used to break ties. } \value{ A data frame with plotting information, invisibly. } \seealso{ \code{\link{stripchart}}, \code{\link{boxplot}} } \examples{ ## One of the examples from 'stripchart' beeswarm(decrease ~ treatment, data = OrchardSprays, log = TRUE, pch = 16, col = rainbow(8)) ## One of the examples from 'boxplot', with a beeswarm overlay boxplot(len ~ dose, data = ToothGrowth, main = "Guinea Pigs' Tooth Growth", xlab = "Vitamin C dose mg", ylab = "Tooth length") beeswarm(len ~ dose, data = ToothGrowth, col = 2, add = TRUE) ## Compare the 5 methods op <- par(mfrow = c(2,3)) for (m in c("swarm", "compactswarm", "center", "hex", "square")) { beeswarm(len ~ dose, data = ToothGrowth, method = m, main = m) } par(op) ## Demonstrate the use of 'pwcol' data(breast) beeswarm(time_survival ~ ER, data = breast, pch = 16, pwcol = 1 + as.numeric(event_survival), xlab = "", ylab = "Follow-up time (months)", labels = c("ER neg", "ER pos")) legend("topright", legend = c("Yes", "No"), title = "Censored", pch = 16, col = 1:2) ## The list interface distributions <- list(runif = runif(200, min = -3, max = 3), rnorm = rnorm(200), rlnorm = rlnorm(200, sdlog = 0.5)) beeswarm(distributions, col = 2:4) ## Demonstrate 'pwcol' with the list interface myCol <- lapply(distributions, function(x) cut(x, breaks = quantile(x), labels = FALSE)) beeswarm(distributions, pch = 16, pwcol = myCol) legend("bottomright", legend = 1:4, pch = 16, col = 1:4, title = "Quartile") ## Demonstrate the 'corral' methods par(mfrow = c(2,3)) beeswarm(distributions, col = 2:4, main = 'corral = "none" (default)') beeswarm(distributions, col = 2:4, corral = "gutter", main = 'corral = "gutter"') beeswarm(distributions, col = 2:4, corral = "wrap", main = 'corral = "wrap"') beeswarm(distributions, col = 2:4, corral = "random", main = 'corral = "random"') beeswarm(distributions, col = 2:4, corral = "omit", main = 'corral = "omit"') ## Demonstrate 'side' and 'priority' par(mfrow = c(2,3)) beeswarm(distributions, col = 2:4, main = 'Default') beeswarm(distributions, col = 2:4, side = -1, main = 'side = -1') beeswarm(distributions, col = 2:4, side = 1, main = 'side = 1') beeswarm(distributions, col = 2:4, priority = "descending", main = 'priority = "descending"') beeswarm(distributions, col = 2:4, priority = "random", main = 'priority = "random"') beeswarm(distributions, col = 2:4, priority = "density", main = 'priority = "density"') ## Demonstrate 'side' and 'priority' for compact method par(mfrow = c(2,3)) beeswarm(distributions, col = 2:4, method = "compactswarm", main = 'Default') beeswarm(distributions, col = 2:4, method = "compactswarm", side = -1, main = 'side = -1') beeswarm(distributions, col = 2:4, method = "compactswarm", side = 1, main = 'side = 1') beeswarm(distributions, col = 2:4, method = "compactswarm", priority = "descending", main = 'priority = "descending"') beeswarm(distributions, col = 2:4, method = "compactswarm", priority = "random", main = 'priority = "random"') beeswarm(distributions, col = 2:4, method = "compactswarm", priority = "density", main = 'priority = "density"') ## Demonstrate pwcol, pwpch, pwbg, and pwcex beeswarm(mpg ~ cyl, data = mtcars, cex = 3, pwcol = gear, pwbg = am + 1, pwpch = gear + 18, pwcex = hp / 335) } \keyword{ hplot } beeswarm/DESCRIPTION0000644000176200001440000000171514055524462013600 0ustar liggesusersPackage: beeswarm Title: The Bee Swarm Plot, an Alternative to Stripchart Description: The bee swarm plot is a one-dimensional scatter plot like "stripchart", but with closely-packed, non-overlapping points. Version: 0.4.0 Date: 2021-05-07 Authors@R: c( person("Aron", "Eklund", , "aroneklund@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0003-0861-1001")), person("James", "Trimble", role = "aut", comment = c(ORCID = "0000-0001-7282-8745")) ) Imports: stats, graphics, grDevices, utils NeedsCompilation: yes License: Artistic-2.0 URL: https://github.com/aroneklund/beeswarm BugReports: https://github.com/aroneklund/beeswarm/issues Packaged: 2021-05-07 08:52:45 UTC; aron Author: Aron Eklund [aut, cre] (), James Trimble [aut] () Maintainer: Aron Eklund Repository: CRAN Date/Publication: 2021-06-01 21:40:02 UTC beeswarm/tests/0000755000176200001440000000000014045166730013227 5ustar liggesusersbeeswarm/tests/beeswarm-test.R0000644000176200001440000000240014045166730016130 0ustar liggesuserslibrary(beeswarm) test_swarms <- function(x) { for (na_positions in list(integer(0), c(2L,4L,6L,8L))) { x[na_positions] <- NA for (compact in c(FALSE, TRUE)) { for (priority in c("ascending", "descending", "density", "random", "none")) { for (side in -1:1) { print(na_positions) print(compact) print(priority) print(side) # compare R and C versions of swarmy and swarmx set.seed(1) y1 <- swarmy(x, numeric(length(x)), compact=compact, side=side, priority=priority, fast=TRUE)$y set.seed(1) y2 <- swarmy(x, numeric(length(x)), compact=compact, side=side, priority=priority, fast=FALSE)$y stopifnot(all.equal(y1, y2)) stopifnot(identical(which(is.na(y1)), na_positions)) set.seed(1) x1 <- swarmx(numeric(length(x)), x, compact=compact, side=side, priority=priority, fast=TRUE)$x set.seed(1) x2 <- swarmx(numeric(length(x)), x, compact=compact, side=side, priority=priority, fast=FALSE)$x stopifnot(all.equal(x1, x2)) stopifnot(identical(which(is.na(x1)), na_positions)) } } } } } set.seed(1) test_swarms(rnorm(250) / 100) test_swarms(numeric(10)) test_swarms(1:10) beeswarm/src/0000755000176200001440000000000014045166730012654 5ustar liggesusersbeeswarm/src/init.c0000644000176200001440000000103514045166730013762 0ustar liggesusers#include // for NULL #include #include #include "beeswarm.h" static R_NativePrimitiveArgType calculateSwarm_types[] = { REALSXP, INTSXP, LGLSXP, INTSXP, INTSXP, REALSXP, REALSXP}; static const R_CMethodDef CEntries[] = { {"calculateSwarm", (DL_FUNC) &calculateSwarm, 7, calculateSwarm_types}, {NULL, NULL, 0, NULL} }; void R_init_beeswarm(DllInfo *dll) { R_registerRoutines(dll, CEntries, NULL, NULL, NULL); R_useDynamicSymbols(dll, FALSE); R_forceSymbols(dll, TRUE); } beeswarm/src/beeswarm-win.def0000644000176200001440000000005614045166730015735 0ustar liggesusersLIBRARY beeswarm.dll EXPORTS R_init_beeswarm beeswarm/src/beeswarm.c0000644000176200001440000001035314045166730014627 0ustar liggesusers#include "beeswarm.h" #include #include #include #include #include static bool is_y_feasible(double y, double *pre_dx_sq, double *pre_y, int pre_xy_len) { for (int i=0; i= 1) continue; double x_diff = x[i] - x[j]; nearby_dx_sq[nearby_xy_len] = x_diff * x_diff; nearby_y[nearby_xy_len] = y[j]; nearby_xy_len++; double poty_off = sqrt(1 - x_diff * x_diff); poty[poty_len++] = y[j] + poty_off; poty_low[poty_low_len++] = y[j] - poty_off; } if (side == -1) poty_len = 1; // remove poty values > 0 if (side != 1) for (int j=0; j= 1) continue; double y_offset = sqrt(1 - xdiff * xdiff); double y_hi = fmax(y_high[j], yi + y_offset); y_high[j] = y_hi; if (side == 0) { double y_lo = fmin(y_low[j], yi - y_offset); y_low[j] = y_lo; y_best[j] = -y_lo < y_hi ? y_lo : y_hi; } else { y_best[j] = y_hi; } } } if (side == -1) for (int i=0; i 1) { stop('the parameter "cex" must have length 1') } stopifnot(side %in% -1:1) if(is.numeric(x)) { x <- list(x) } n.groups <- length(x) #### Resolve group labels if(missing(labels) || is.null(labels)) { if(is.null(names(x))) { if(n.groups == 1) { labels <- NA } else { labels <- 1:n.groups } } else { labels <- names(x) } } else { labels <- rep(labels, length.out = n.groups) } if (is.null(at)) at <- 1:n.groups else if (length(at) != n.groups) stop(gettextf("'at' must have length equal to %d, the number of groups", n.groups), domain = NA) if (is.null(dlab)) dlab <- deparse(substitute(x)) ## this function returns a "group" vector, to complement "unlist" unlistGroup <- function(x, nms = names(x)) rep(nms, sapply(x, length)) x.val <- unlist(x) x.gp <- unlistGroup(x, nms = labels) if((range(x.val, finite = TRUE)[1] <= 0) && log) warning('values <= 0 omitted from logarithmic plot') n.obs <- length(x.val) n.obs.per.group <- lengths(x) #### Resolve xlim, ylim, dlim, xlab, ylab if(is.null(dlim)) { if(log) { dlim <- 10 ^ (extendrange(log10(x.val[x.val > 0]))) } else { dlim <- extendrange(x.val, f = 0.01) } } else if (length(dlim) != 2) { stop ("'dlim' must have length 2") } if(is.null(glim)) { glim <- c(min(at) - 0.5, max(at) + 0.5) } else if (length(glim) != 2) { stop ("'glim' must have length 2") } if(horizontal) { ## plot is horizontal if(is.null(ylim)) ylim <- glim if(is.null(xlim)) { xlim <- dlim } else { dlim <- xlim } if (is.null(xlab)) xlab <- dlab if (is.null(ylab)) ylab <- glab } else { ## plot is vertical if(is.null(xlim)) xlim <- glim if(is.null(ylim)) { ylim <- dlim } else { dlim <- ylim } if (is.null(ylab)) ylab <- dlab if (is.null(xlab)) xlab <- glab } if(length(xlim) != 2) stop ("'xlim' must have length 2") if(length(ylim) != 2) stop ("'ylim' must have length 2") #### Resolve plotting characters and colors if(is.null(pwpch)) { pch.out <- unlistGroup(x, nms = rep(pch, length.out = n.groups)) } else { if(is.list(pwpch)) { names(pwpch) <- names(x) stopifnot(all(sapply(pwpch, length) == n.obs.per.group)) pch.out <- unlist(pwpch) } else { pch.out <- pwpch } } stopifnot(length(pch.out) == n.obs) if(is.null(pwcol)) { col.out <- unlistGroup(x, nms = rep(col, length.out = n.groups)) } else { if(is.list(pwcol)) { names(pwcol) <- names(x) stopifnot(all(sapply(pwcol, length) == n.obs.per.group)) col.out <- unlist(pwcol) } else { col.out <- pwcol } } stopifnot(length(col.out) == n.obs) if(is.null(pwbg)) { bg.out <- unlistGroup(x, nms = rep(bg, length.out = n.groups)) } else { if(is.list(pwbg)) { names(pwbg) <- names(x) stopifnot(all(sapply(pwbg, length) == n.obs.per.group)) bg.out <- unlist(pwbg) } else { bg.out <- pwbg } } stopifnot(length(bg.out) == n.obs) if(is.null(pwcex)) { cex.out <- unlistGroup(x, nms = rep(1, length.out = n.groups)) } else { if(is.list(pwcex)) { names(pwcex) <- names(x) stopifnot(all(sapply(pwcex, length) == n.obs.per.group)) cex.out <- unlist(pwcex) } else { cex.out <- pwcex } } stopifnot(length(cex.out) == n.obs) #### Set up the plot if(do.plot & !add) { plot(xlim, ylim, type = 'n', axes = FALSE, log = ifelse(log, ifelse(horizontal, 'x', 'y'), ''), xlab = xlab, ylab = ylab, ...) } #### Calculate the size of a plotting character along group- or data-axis sizeMultiplier <- par('cex') * cex * spacing if(horizontal) { size.g <- yinch(0.08, warn.log = FALSE) * sizeMultiplier size.d <- xinch(0.08, warn.log = FALSE) * sizeMultiplier } else { # vertical size.g <- xinch(0.08, warn.log = FALSE) * sizeMultiplier size.d <- yinch(0.08, warn.log = FALSE) * sizeMultiplier } ##### Calculate point positions g.pos, d.pos if(method %in% c('swarm', 'compactswarm')) { compact <- method == 'compactswarm' if(horizontal) { g.offset <- lapply(x, function(a) swarmy(x = a, y = rep(0, length(a)), cex = sizeMultiplier, side = side, priority = priority, fast = fast, compact = compact)$y) } else { g.offset <- lapply(x, function(a) swarmx(x = rep(0, length(a)), y = a, cex = sizeMultiplier, side = side, priority = priority, fast = fast, compact = compact)$x) } d.pos <- x } else { #### non-swarm methods ##### first determine positions along the data axis if(method == 'hex') size.d <- size.d * sqrt(3) / 2 if(log) { ## if data axis IS on a log scale if(is.null(breaks)) breaks <- 10 ^ seq(log10(dlim[1]), log10(dlim[2]) + size.d, by = size.d) if(length(breaks) == 1 && is.na(breaks[1])) { d.index <- x d.pos <- x } else { mids <- 10 ^ ((log10(head(breaks, -1)) + log10(tail(breaks, -1))) / 2) d.index <- lapply(x, cut, breaks = breaks, labels = FALSE, include.lowest = TRUE) d.pos <- lapply(d.index, function(a) mids[a]) } } else { ## if data axis is NOT on a log scale if(is.null(breaks)) breaks <- seq(dlim[1], dlim[2] + size.d, by = size.d) if(length(breaks) == 1 && is.na(breaks[1])) { d.index <- x d.pos <- x } else { mids <- (head(breaks, -1) + tail(breaks, -1)) / 2 d.index <- lapply(x, cut, breaks = breaks, labels = FALSE, include.lowest = TRUE) d.pos <- lapply(d.index, function(a) mids[a]) } } ##### now determine positions along the group axis x.index <- lapply(d.index, function(v) { if(length(na.omit(v)) == 0) return(v) v.s <- lapply(split(v, v), seq_along) if(method %in% c('center', 'square') && side == -1) v.s <- lapply(v.s, function(a) a - max(a)) else if(method %in% c('center', 'square') && side == 1) v.s <- lapply(v.s, function(a) a - 1) else if(method == 'center') v.s <- lapply(v.s, function(a) a - mean(a)) else if(method == 'square') v.s <- lapply(v.s, function(a) a - floor(mean(a))) else if(method == 'hex') { odd.row <- (as.numeric(names(v.s)) %% 2) == 1 if(side == 0) { v.s[ odd.row] <- lapply(v.s[ odd.row], function(a) a - floor(mean(a)) - 0.25) v.s[!odd.row] <- lapply(v.s[!odd.row], function(a) a - ceiling(mean(a)) + 0.25) } else if(side == -1) { v.s[ odd.row] <- lapply(v.s[ odd.row], function(a) a - max(a)) v.s[!odd.row] <- lapply(v.s[!odd.row], function(a) a - max(a) - 0.5) } else if(side == 1) { v.s[ odd.row] <- lapply(v.s[ odd.row], function(a) a - 1) v.s[!odd.row] <- lapply(v.s[!odd.row], function(a) a - 0.5) } } unsplit(v.s, v) }) g.offset <- lapply(1:n.groups, function(i) x.index[[i]] * size.g) } ###### end of non-swarm methods ##### now check for runaway points (if "corral" has been set) if(corral != 'none') { if(missing(corralWidth)) { if(n.groups > 1) { corralWidth <- min(at[-1] - at[-n.groups]) - (2 * size.g) } else { corralWidth <- 2 * (min(diff(c(par('usr')[1], at, par('usr')[2]))) - size.g) } } else { stopifnot(length(corralWidth) == 1) stopifnot(corralWidth > 0) } corralLo <- (side - 1) * corralWidth / 2 corralHi <- (side + 1) * corralWidth / 2 if(corral == 'gutter') { g.offset <- lapply(g.offset, function(zz) pmin(corralHi, pmax(corralLo, zz))) } if(corral == 'wrap') { if(side == -1) { ## special case with side=-1: reverse the corral to avoid artifacts at zero g.offset <- lapply(g.offset, function(zz) corralHi - ((corralHi - zz) %% corralWidth)) } else { g.offset <- lapply(g.offset, function(zz) ((zz - corralLo) %% corralWidth) + corralLo) } } if(corral == 'random') { g.offset <- lapply(g.offset, function(zz) ifelse(zz > corralHi | zz < corralLo, yes = runif(length(zz), corralLo, corralHi), no = zz)) } if(corral == 'omit') { g.offset <- lapply(g.offset, function(zz) ifelse(zz > corralHi | zz < corralLo, yes = NA, no = zz)) } } g.pos <- lapply(1:n.groups, function(i) at[i] + g.offset[[i]]) out <- data.frame(x = unlist(g.pos), y = unlist(d.pos), pch = pch.out, col = col.out, bg = bg.out, cex = cex * cex.out, x.orig = x.gp, y.orig = x.val, stringsAsFactors = FALSE) if(do.plot) { if(horizontal) { ## plot is horizontal points(out$y, out$x, pch = out$pch, col = out$col, bg = out$bg, cex = out$cex) if(axes & !add) { axis(1, ...) axis(2, at = at, labels = labels, tick = FALSE, ...) box(...) } } else { ## plot is vertical points(out$x, out$y, pch = out$pch, col = out$col, bg = out$bg, cex = out$cex) if(axes & !add) { axis(2, ...) axis(1, at = at, labels = labels, tick = FALSE, ...) box(...) } } } invisible(out) } beeswarm.formula <- function (formula, data = NULL, subset, na.action = NULL, pwpch = NULL, pwcol = NULL, pwbg = NULL, pwcex = NULL, dlab, glab, ...) { if (missing(formula) || (length(formula) != 3)) stop("'formula' missing or incorrect") m <- match.call(expand.dots = FALSE) if (is.matrix(eval(m$data, parent.frame()))) m$data <- as.data.frame(data) m$... <- NULL m$dlab <- NULL m$glab <- NULL m$na.action <- na.action m[[1]] <- as.name("model.frame") mf <- eval(m, parent.frame()) response <- attr(attr(mf, "terms"), "response") if (missing(dlab)) dlab <- names(mf)[response] if (missing(glab)) glab <- as.character(formula)[3] f <- mf[-response] f <- f[names(f) %in% attr(attr(mf, "terms"), "term.labels")] if(!is.null(mf$'(pwpch)')) pwpch <- split(mf$'(pwpch)', f) if(!is.null(mf$'(pwcol)')) pwcol <- split(mf$'(pwcol)', f) if(!is.null(mf$'(pwbg)')) pwbg <- split(mf$'(pwbg)',f) if(!is.null(mf$'(pwcex)')) pwcex <- split(mf$'(pwcex)',f) beeswarm(split(mf[[response]], f), pwpch = pwpch, pwcol = pwcol, pwbg = pwbg, pwcex = pwcex, dlab = dlab, glab = glab, ...) } #### hidden function to do swarm layout .calculateSwarm <- function(x, dsize, gsize, side = 0L, priority = "ascending") { if(length(x) == 0) return(numeric(0)) stopifnot(side %in% -1:1) out <- data.frame(x = x / dsize, y = 0, index = seq_along(x)) #### Determine the order in which points will be placed if( priority == "ascending" ) { out <- out[order( out$x), ] } ## default "smile" else if(priority == "descending") { out <- out[order(-out$x), ] } ## frown else if(priority == "none") { } ## do not reorder else if(priority == "density") { dens.x <- density(out$x, na.rm = TRUE) ## compute kernel density estimate dens.interp <- approx(dens.x$x, dens.x$y, xout = out$x, rule = 2) ## interpolated density out <- out[order(-dens.interp$y), ] ## arrange outward from densest areas } else if(priority == "random") { out <- out[sample(nrow(out)), ] } #### place the points if(nrow(out) > 1) { for (ii in 2:nrow(out)) { ## we will place one point at a time xi <- out$x[ii] ## identify previously-placed points with potential to overlap the current point isPotOverlap <- (abs(xi - out$x) < 1) & (1:nrow(out) < ii) isPotOverlap[is.na(isPotOverlap)] <- FALSE if(any(isPotOverlap)) { pre.x <- out[isPotOverlap, 'x'] pre.y <- out[isPotOverlap, 'y'] poty.off <- sqrt(1 - ((xi - pre.x) ^ 2)) ## potential y offsets poty <- switch(side + 2, c(0, pre.y - poty.off), c(0, pre.y + poty.off, pre.y - poty.off), c(0, pre.y + poty.off) ) poty.bad <- sapply(poty, function(y) { ## check for overlaps any(((xi - pre.x) ^ 2 + (y - pre.y) ^ 2) < 0.999) }) poty[poty.bad] <- Inf out$y[ii] <- poty[which.min(abs(poty))] } else { out$y[ii] <- 0 } } } out[is.na(out$x), 'y'] <- NA ## missing x values should have missing y values out$y[order(out$index)] * gsize } .calculateCompactSwarm <- function(x, dsize, gsize, side = 0L, priority = "ascending") { if(length(x) == 0) return(numeric(0)) stopifnot(side %in% -1:1) ## out$y.low: best permitted position <= 0 for each point ## out$y.high: best permitted position >= 0 for each point ## out$y.best: best permitted position for each point (or Inf for placed points) ## out$y.placed: which points have been placed out <- data.frame(x = x / dsize, y = 0, index = seq_along(x), placed = FALSE, y.low = 0, y.high = 0, y.best = 0) #### Determine the order in which points will be placed if( priority == "ascending" ) { out <- out[order( out$x), ] } ## default "smile" else if(priority == "descending") { out <- out[order(-out$x), ] } ## frown else if(priority == "none") { } ## do not reorder else if(priority == "density") { dens.x <- density(out$x, na.rm = TRUE) ## compute kernel density estimate dens.interp <- approx(dens.x$x, dens.x$y, xout = out$x, rule = 2) ## interpolated density out <- out[order(-dens.interp$y), ] ## arrange outward from densest areas } else if(priority == "random") { out <- out[sample(nrow(out)), ] } #### place the points if(nrow(out) > 1) { for (iter in 1:nrow(out)) { ## we will place one point at a time i <- which.min(abs(out$y.best)) ## Choose a point that can be placed ## close to non-data axis xi <- out$x[i] yi <- out$y[i] <- out$y.best[i] out$placed[i] <- TRUE out$y.best[i] <- Inf ## Ensure it won't be chosen again xdiff = abs(xi - out$x) if(side == 0) { for (j in which(!out$placed & xdiff < 1)) { y.offset <- sqrt(1 - (xdiff[j] ^ 2)) y.high <- out$y.high[j] <- max(out$y.high[j], yi + y.offset) y.low <- out$y.low[j] <- min(out$y.low[j], yi - y.offset) out$y.best[j] <- ifelse(-y.low < y.high, y.low, y.high) } } else if(side == 1) { for (j in which(!out$placed & xdiff < 1)) { y.offset <- sqrt(1 - (xdiff[j] ^ 2)) out$y.best[j] <- out$y.high[j] <- max(out$y.high[j], yi + y.offset) } } else { for (j in which(!out$placed & xdiff < 1)) { y.offset <- sqrt(1 - (xdiff[j] ^ 2)) out$y.best[j] <- out$y.low[j] <- min(out$y.low[j], yi - y.offset) } } } } out[is.na(out$x), 'y'] <- NA ## missing x values should have missing y values out$y[order(out$index)] * gsize } .calculateSwarmUsingC <- function(x, dsize, gsize, side = 0L, priority = "ascending", compact = FALSE) { if(length(x) == 0) return(numeric(0)) stopifnot(side %in% -1:1) out <- data.frame(x = x / dsize, index = seq_along(x)) #### Determine the order in which points will be placed if( priority == "ascending" ) { out <- out[order( out$x), ] } ## default "smile" else if(priority == "descending") { out <- out[order(-out$x), ] } ## frown else if(priority == "none") { } ## do not reorder else if(priority == "density") { dens.x <- density(out$x, na.rm = TRUE) ## compute kernel density estimate dens.interp <- approx(dens.x$x, dens.x$y, xout = out$x, rule = 2) ## interpolated density out <- out[order(-dens.interp$y), ] ## arrange outward from densest areas } else if(priority == "random") { out <- out[sample(nrow(out)), ] } out <- out[!is.na(out$x), ] n <- nrow(out) #### place the points result <- .C(C_calculateSwarm, x = as.double(out$x), n = n, compact = as.logical(compact), side = as.integer(side), placed = integer(n), # used internally by C implementations workspace = numeric(n * 4), # used internally by C implementations y = numeric(n)) y <- rep(NA, length(x)) y[out$index] <- result[[7]] * gsize y } ### jitter points horizontally swarmx <- function(x, y, xsize = xinch(0.08, warn.log = FALSE), ysize = yinch(0.08, warn.log = FALSE), log = NULL, cex = par("cex"), side = 0L, priority = c("ascending", "descending", "density", "random", "none"), fast = TRUE, compact = FALSE) { priority <- match.arg(priority) if(is.null(log)) log <- paste(ifelse(par('xlog'), 'x', ''), ifelse(par('ylog'), 'y', ''), sep = '') xlog <- 'x' %in% strsplit(log, NULL)[[1L]] ylog <- 'y' %in% strsplit(log, NULL)[[1L]] xy <- xy.coords(x = x, y = y, recycle = TRUE, log = log) stopifnot((length(unique(xy$x)) <= 1)) if(xlog) xy$x <- log10(xy$x) if(ylog) xy$y <- log10(xy$y) if (fast) { x.new <- xy$x + .calculateSwarmUsingC(xy$y, dsize = ysize * cex, gsize = xsize * cex, side = side, priority = priority, compact = compact) } else { swarmFn <- ifelse(compact, .calculateCompactSwarm, .calculateSwarm) x.new <- xy$x + swarmFn(xy$y, dsize = ysize * cex, gsize = xsize * cex, side = side, priority = priority) } out <- data.frame(x = x.new, y = y) if(xlog) out$x <- 10 ^ out$x out } ### jitter points vertically swarmy <- function(x, y, xsize = xinch(0.08, warn.log = FALSE), ysize = yinch(0.08, warn.log = FALSE), log = NULL, cex = par("cex"), side = 0L, priority = c("ascending", "descending", "density", "random", "none"), fast = TRUE, compact = FALSE) { priority <- match.arg(priority) if(is.null(log)) log <- paste(ifelse(par('xlog'), 'x', ''), ifelse(par('ylog'), 'y', ''), sep = '') xlog <- 'x' %in% strsplit(log, NULL)[[1L]] ylog <- 'y' %in% strsplit(log, NULL)[[1L]] xy <- xy.coords(x = x, y = y, recycle = TRUE, log = log) stopifnot((length(unique(xy$y)) <= 1)) if(xlog) xy$x <- log10(xy$x) if(ylog) xy$y <- log10(xy$y) if (fast) { y.new <- xy$y + .calculateSwarmUsingC(xy$x, dsize = xsize * cex, gsize = ysize * cex, side = side, priority = priority, compact = compact) } else { swarmFn <- ifelse(compact, .calculateCompactSwarm, .calculateSwarm) y.new <- xy$y + swarmFn(xy$x, dsize = xsize * cex, gsize = ysize * cex, side = side, priority = priority) } out <- data.frame(x = x, y = y.new) if(ylog) out$y <- 10 ^ out$y out } beeswarm/MD50000644000176200001440000000144714055524462012404 0ustar liggesusers7da025470682c95ef0d9a32294318deb *DESCRIPTION 4af45e98fd5f84546fc33a4bfc5eea7d *NAMESPACE 45edfeb132eda40b3d3207928bb2c0e4 *NEWS 9fa79873496adc544225d42673290ce8 *R/beeswarm.R 99b47bff686cbb9afaf34e18a74ce49d *R/bxplot.R 16cc12d2742742718c88949f18d5ac5d *R/zzz.R a0fec315966b30ffc2f4a837346c2673 *README.md bcf102fab14afb8d779c8c4148685174 *data/breast.RData 41a6bd60e88704ecb17875c4e5db918b *man/beeswarm.Rd 1ee38a2972fa0874a81c807e36dd22a6 *man/breast.Rd d7097bb52220995656e837936b892d12 *man/bxplot.Rd 27310d0b708cbf70b32848a6aab8abc9 *man/swarmx.Rd 9a4f3351ddb36f5a3aed5cb869a4ac2c *src/beeswarm-win.def 9f8918c236945341ea99925b9356d929 *src/beeswarm.c 7d4b1b93c9e0e8484c5bc77a9e363241 *src/beeswarm.h c15e8d8f1b692e5f3fdb747ddc60b727 *src/init.c 47b347c2c88a22802635a2ab082406e5 *tests/beeswarm-test.R