The function circleProgressiveLayout
arranges a set of
circles deterministically. The first two circles are placed to the left
and right of the origin respectively. Subsequent circles are placed so
that each:
The algorithm was described in the paper: Visualization of large hierarchical data by circle packing by Weixin Wang et al. (2006). The implementation in this package is based on a version written in C by Peter Menzel.
The algorithm is very efficient and this, combined with the implementation in Rcpp, means arrangements for large numbers of circles can be found quickly.
We begin by arranging 10 circles of various sizes. First we pass a
vector of circle areas to the circleProgressiveLayout
function. It returns a data frame of centre coordinates and radii.
areas <- c(20, 10, 40, rep(5, 7))
# Generate the layout
packing <- circleProgressiveLayout(areas)
head( round(packing, 2) )
## x y radius
## 1 -2.52 0.00 2.52
## 2 1.78 0.00 1.78
## 3 0.61 -5.22 3.57
## 4 0.22 2.61 1.26
## 5 2.72 2.90 1.26
## 6 4.59 1.19 1.26
Next we derive a data frame of circle vertices for plotting using the
circleLayoutVertices
function, and then use ggplot to
display the layout, labelling the circles to show their order of
placement:
library(ggplot2)
t <- theme_bw() +
theme(panel.grid = element_blank(),
axis.text=element_blank(),
axis.ticks=element_blank(),
axis.title=element_blank())
theme_set(t)
dat.gg <- circleLayoutVertices(packing, npoints=50)
ggplot(data = dat.gg) +
geom_polygon(aes(x, y, group = id), colour = "black",
fill = "grey90", alpha = 0.7, show.legend = FALSE) +
geom_text(data = packing, aes(x, y), label = 1:nrow(packing)) +
coord_equal()
By default, circleProgressiveLayout
takes the input
sizes to be circle areas. If instead you have input radii, add
sizetype = "radius"
to the arguments when calling the
function.
Re-ordering the input sizes will generally produce a different layout unless the sizes are uniform. Here we repeatedly shuffle the area values used above and generate a new layout each time.
ncircles <- length(areas)
nreps <- 6
packings <- lapply(
1:nreps,
function(i) {
x <- sample(areas, ncircles)
circleProgressiveLayout(x)
})
packings <- do.call(rbind, packings)
npts <- 50
dat.gg <- circleLayoutVertices(packings, npoints = npts)
dat.gg$rep <- rep(1:nreps, each = ncircles * (npts+1))
ggplot(data = dat.gg, aes(x, y)) +
geom_polygon(aes(group = id),
colour = "black", fill = "grey90") +
coord_equal() +
facet_wrap(~ rep, nrow = 2)
We can use this ordering effect to create some circle art…
areas <- 1:1000
# area: small to big
packing1 <- circleProgressiveLayout(areas)
dat1 <- circleLayoutVertices(packing1)
# area: big to small
packing2 <- circleProgressiveLayout( rev(areas) )
dat2 <- circleLayoutVertices(packing2)
dat <- rbind(
cbind(dat1, set = 1),
cbind(dat2, set = 2) )
ggplot(data = dat, aes(x, y)) +
geom_polygon(aes(group = id, fill = -id),
colour = "black", show.legend = FALSE) +
scale_fill_distiller(palette = "RdGy") +
coord_equal() +
facet_wrap(~set,
labeller = as_labeller(
c('1' = "small circles first",
'2' = "big circles first"))
)
The package includes an example data set of the abundance of different types of bacteria measured in a study of biofilms. Columns are value (abundance), display colour and label (bacterial taxon).
## value colour label
## 1 4232 #DDF379 Dehalogenimonas sp. WBC-2
## 2 5097 #F3C679 Desulfatibacillum alkenivorans AK-01
## 3 2825 #79F398 Opitutus terrae PB90-1
## 4 3471 #F3C679 Geobacter
## 5 7515 #79F3EA Methanosaeta harundinacea 6Ac
## 6 2476 #C4F379 Rubinisphaera brasiliensis DSM 5305
The following example shows how to display the abundance values as
circles filled with the specified colours. It relies on the fact that
the id
column in the output of
circleLayoutVertices
maps to the row number of the input
data.
Note: the y-axis is reversed so that the layour is rendered similarly to the example here.
packing <- circleProgressiveLayout(bacteria)
dat.gg <- circleLayoutVertices(packing)
ggplot(data = dat.gg) +
geom_polygon(aes(x, y, group = id, fill = factor(id)),
colour = "black",
show.legend = FALSE) +
scale_fill_manual(values = bacteria$colour) +
scale_y_reverse() +
coord_equal()
As a further flourish, we can make the plot interactive so that the name of the bacterial taxon is displayed when the mouse cursor hovers a circle.
Note: the ggiraph
package is required for this.
if (requireNamespace("ggiraph")) {
gg <- ggplot(data = dat.gg) +
ggiraph::geom_polygon_interactive(
aes(x, y, group = id, fill = factor(id),
tooltip = bacteria$label[id], data_id = id),
colour = "black",
show.legend = FALSE) +
scale_fill_manual(values = bacteria$colour) +
scale_y_reverse() +
labs(title = "Hover over circle to display taxon name") +
coord_equal()
ggiraph::ggiraph(ggobj = gg, width_svg = 5, height_svg = 5)
}
## Loading required namespace: ggiraph
## Function `ggiraph()` is replaced by `girafe()` and will be removed soon.