R::Ggplot2::Geom_Points: How to Swap Points with Pie Charts

R::ggplot2::geom_points: how to swap points with pie charts?

Update:

If you want pie charts in particular, you're better off with package scatterpie, https://cran.r-project.org/web/packages/scatterpie/vignettes/scatterpie.html. My method below works with non-pie charts as well though.


I was curious to see if it could be done, and I'm not sure how flexible this solution is, but here's what I came up with. It's worth stepping through this code chunk line by line, stopping before each %>% pipe to see what it's generating as it goes along. This first chunk generates some data: 5 random X and Y values. Then component labels and their values are generated and bound to the Xs and Ys. Then for proof-of-concept I created an additional column that shows the sum of the components for each X-Y pair.

require(dplyr)
require(ggplot2)

df <- data_frame(x1 = rnorm(5), y1 = rnorm(5)) %>%
group_by(x1, y1) %>%
do(data_frame(component = LETTERS[1:3], value = runif(3))) %>%
mutate(total = sum(value)) %>%
group_by(x1, y1, total)

df

 Source: local data frame [15 x 5] Groups: x1, y1, total [5]

x1 y1 component value total
<dbl> <dbl> <chr> <dbl> <dbl>

1 -1.0933810 0.4162150 A 0.920992065 2.1406433
2 -1.0933810 0.4162150 B 0.914163390 2.1406433
3 -1.0933810 0.4162150 C 0.305487891 2.1406433
4 -0.9579912 1.4080922 A 0.006967777 0.3149009
5 -0.9579912 1.4080922 B 0.128341286 0.3149009
6 -0.9579912 1.4080922 C 0.179591852 0.3149009
7 0.5617438 -0.8770998 A 0.233895761 1.2324975
8 0.5617438 -0.8770998 B 0.942843309 1.2324975
9 0.5617438 -0.8770998 C 0.055758395 1.2324975
10 0.9970852 -0.4142704 A 0.035965092 1.4261429
11 0.9970852 -0.4142704 B 0.454193773 1.4261429
12 0.9970852 -0.4142704 C 0.935984062 1.4261429
13 1.2253968 0.3557304 A 0.692594728 2.1289173
14 1.2253968 0.3557304 B 0.972569822 2.1289173
15 1.2253968 0.3557304 C 0.463752786 2.1289173

This chunk takes the first dataframe and for each unique x1-y1-total combination, generates a plain pie chart in a list-column called subplots. Each value in that column is a list of ggplot elements to generate a figure. Then it constructs another column that turns each of those subplots into a custom annotation in a column called subgrobs. These annotations are what can be dropped into a larger figure.

df.grobs <- df %>% 
do(subplots = ggplot(., aes(1, value, fill = component)) +
geom_col(position = "fill", alpha = 0.75, colour = "white") +
coord_polar(theta = "y") +
theme_void()+ guides(fill = F)) %>%
mutate(subgrobs = list(annotation_custom(ggplotGrob(subplots),
x = x1-total/4, y = y1-total/4,
xmax = x1+total/4, ymax = y1+total/4)))

df.grobs

Source: local data frame [5 x 5]
Groups: <by row>

# A tibble: 5 × 5
x1 y1 total subplots subgrobs
<dbl> <dbl> <dbl> <list> <list>
1 -1.0933810 0.4162150 2.1406433 <S3: gg> <S3: LayerInstance>
2 -0.9579912 1.4080922 0.3149009 <S3: gg> <S3: LayerInstance>
3 0.5617438 -0.8770998 1.2324975 <S3: gg> <S3: LayerInstance>
4 0.9970852 -0.4142704 1.4261429 <S3: gg> <S3: LayerInstance>
5 1.2253968 0.3557304 2.1289173 <S3: gg> <S3: LayerInstance>

Here it just takes the 5 unique x1-y1-total combinations and plots them as a regular ggplot, but then also adds in the custom annotations we made, which are sized proportional to total. Then a fake legend is constructed from the original dataframe df using blank geom_cols.

df.grobs %>%
{ggplot(data = ., aes(x1, y1)) +
scale_x_continuous(expand = c(0.25, 0)) +
scale_y_continuous(expand = c(0.25, 0)) +
.$subgrobs +
geom_text(aes(label = round(total, 2))) +
geom_col(data = df,
aes(0,0, fill = component),
colour = "white")}

Sample Image

A lot of the numeric constants for adjusting the sizes and x,y scales would need to be changed by eye to fit your dataset.

ggplot2: piecharts instead of points in an xy scatterplot

This does the trick:

ggplot(aes(x=x,y=y)) + geom_point(aes(size=z)) + stat_spoke(aes(angle=r*2*pi, radius=3*z))

ggplot piecharts on a ggmap: labels destroy the small plots

Short answer:

I think the problem is actually in your earlier line:

geom_bar(position = "fill", alpha = 0.5, colour = "white", stat="identity") +

If you change the position from fill to stack (i.e. the default), it should work properly (at least it did on mine).

Long(-winded) explanation:

Let's use a summarised version of the mtcars dataset to reproduce the problem:

dfm <- mtcars %>% group_by(cyl) %>% summarise(disp = mean(disp)) %>% ungroup()

# correct pie chart
ggplot(dfm, aes(x = 1, y = disp, label = factor(cyl), fill = factor(cyl))) +
geom_bar(stat = "identity", position = "stack") +
geom_text(position = position_stack(vjust = 0.5)) +
coord_polar(theta = "y") + theme_void()

# "empty" pie chart
ggplot(dfm, aes(x = 1, y = disp, label = factor(cyl), fill = factor(cyl))) +
geom_bar(stat = "identity", position = "fill") +
geom_text(position = position_stack(vjust = 0.5)) +
coord_polar(theta = "y") + theme_void()

Correct version

Problematic version

Why does changing geom_bar's position affect this? If we look at the plot before the coord_polar step, things may become clearer:

ggplot(dfm, aes(x = 1, y = disp, label = factor(cyl), fill = factor(cyl))) + 
geom_bar(stat = "identity", position = "stack") +
geom_text(position = position_stack(vjust = 0.5))

Correct bar chart

Check the bar chart's y-axis. The bars & the labels are correctly positioned.

Now the version with position = "fill":

ggplot(dfm, aes(x = 1, y = disp, label = factor(cyl), fill = factor(cyl))) + 
geom_bar(stat = "identity", position = "fill") +
geom_text(position = position_stack(vjust = 0.5))

Problematic bar chart

Your bar chart now occupies the range 0-1 on the y-axis, while your labels continue to occupy the original full range, which is much larger. Thus when you convert the chart to polar coordinates, the bar chart is squeezed to a tiny slice that becomes practically invisible.

centering the label segment starting points for labelled pie charts

You can calculate the labels' stacked positions manually, instead of relying on ggplot. See if the following works for you:

foo <- function(df, x) {
df$y.label <- 0.5 * df$counts + rev(cumsum(dplyr::lag(rev(df$counts), default = 0)))
ggplot2::ggplot(
data = df,
mapping = ggplot2::aes(x = "", y = counts, fill = forcats::fct_inorder({{ x }}))
) +
ggplot2::geom_col(
color = "black",
width = 1,
na.rm = TRUE
) +
coord_polar("y", start = 0) +
ggrepel::geom_label_repel(
aes(label = label, y = y.label),
show.legend = FALSE,
nudge_x = 1
) +
guides(fill = guide_legend(title = rlang::as_name(rlang::ensym(x))))
}

foo(df, cyl)

plot

ggplot2 using facet_grid to generate Pie charts for each row

First, pie charts is not the best way to show your data!

But anyway here is one solution. Most important part here is scale_y_continuous() to set limits from 0 to 1 and then convert them to percents using percent_format() from library scales.

library(ggplot2)
library(scales)
ggplot(df,aes(x=1,y=Errors/Fields))+geom_bar(stat="identity")+
facet_grid(~Hospital)+
coord_polar(theta="y")+
scale_y_continuous(labels = percent_format(),limits=c(0,1),
breaks=c(0.25,0.5,0.75,1))+
theme(axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank())


Related Topics



Leave a reply



Submit