How to Show Only Part of the Plot Area of Polar Ggplot with Facet

How to show only part of the plot area of polar ggplot with facet?

This is an inelegant hack, but you can use grid functions to cover up the area you don't want. For example:

library(ggplot2)
library(reshape2)
library(grid)

data <- melt(matrix(rnorm(1000), nrow = 20))
data$type <- 1:2
data$Var1 <- data$Var1*6 - 60

p1 = ggplot(data, aes(Var1, Var2)) +
geom_tile(aes(fill = value)) +
coord_polar(theta = "x", start = pi) +
scale_x_continuous(limits = c(-180, 180)) +
facet_wrap(~type)
g1 = ggplotGrob(p1)

grid.newpage()
pushViewport(viewport(height=1, width=1, clip="on"))
grid.draw(g1)
grid.rect(x=0,y=0,height=1, width=2, gp=gpar(col="white"))

This cuts off the bottom half of the graph (see below). It would be nice to find a more elegant approach, but failing that, maybe you can play around with viewport placement and drawing functions (not to mention changing the location of the axis labels and legend) to get something close to what you want.
Sample Image

Polar coordinate / circular layout for the whole facet_grid

I don't think you can arrange facets in a circle, but you can cheat by changing your x and y variables and then using coord_polar().

library(ggplot2)
library(dplyr)
d <- rbind(
mutate(randomData, col = "dark"),
mutate(randomData, col = "blank", z = 1 - z)
) %>% arrange(d, index2, index1, col)

ggplot(d, aes(x = factor(index2), y = z)) +
geom_bar(aes(fill = col), stat = "identity", position = "stack") +
scale_fill_manual(values = c(blank = "white", dark = "black")) +
coord_polar() +
theme_minimal() +
guides(fill = FALSE)

Sample Image

You would have to adapt this quite a bit to fit your original data set but you get the idea - add rows for the values (1 - z) and use them to stack the bars

ggplot2 outside panel border when using facet

Two options for consideration, both making use of a secondary axis to simulate the panel border on the right side. Use option 2 if you want to do away with the facet box outlines on top as well.

Option 1:

ggplot(df,
aes(x = Month, y = Abundance, fill = Type)) +
geom_col(position = "dodge", colour = "black") +
scale_y_continuous(labels = function(x){paste(x, "-")}, # simulate tick marks for left axis
sec.axis = dup_axis(breaks = 0)) + # add right axis
scale_fill_grey() +
facet_grid(~Season, scales = "free_x", space = "free_x") +
theme_classic() +
theme(axis.title.y.right = element_blank(), # hide right axis title
axis.text.y.right = element_blank(), # hide right axis labels
axis.ticks.y = element_blank(), # hide left/right axis ticks
axis.text.y = element_text(margin = margin(r = 0)), # move left axis labels closer to axis
panel.spacing = unit(0, "mm"), # remove spacing between facets
strip.background = element_rect(size = 0.5)) # match default line size of theme_classic

option 1

(I'm leaving the legend in the default position as it's not critical here.)

Option 2 is a modification of option 1, with facet outline removed & a horizontal line added to simulate the top border. Y-axis limits are set explicitly to match the height of this border:

y.upper.limit <- diff(range(df$Abundance)) * 0.05 + max(df$Abundance)
y.lower.limit <- 0 - diff(range(df$Abundance)) * 0.05

ggplot(df,
aes(x = Month, y = Abundance, fill = Type)) +
geom_col(position = "dodge", colour = "black") +
geom_hline(yintercept = y.upper.limit) +
scale_y_continuous(labels = function(x){paste(x, "-")}, #
sec.axis = dup_axis(breaks = 0), #
expand = c(0, 0)) + # no expansion from explicitly set range
scale_fill_grey() +
facet_grid(~Season, scales = "free_x", space = "free_x") +
coord_cartesian(ylim = c(y.lower.limit, y.upper.limit)) + # set explicit range
theme_classic() +
theme(axis.title.y.right = element_blank(), #
axis.text.y.right = element_blank(), #
axis.ticks.y = element_blank(), #
axis.text.y = element_text(margin = margin(r = 0)), #
panel.spacing = unit(0, "mm"), #
strip.background = element_blank()) # hide facet outline

option 2

Sample data used:

set.seed(10)
df <- data.frame(
Month = rep(c("Jun 14", "Aug 14", "Oct 14", "Dec 14", "Apr 15", "Jun 15"),
each = 3),
Type = rep(c("Mangrove", "Mudflat", "Fringe"), 6),
Season = rep(c("Dry1", rep("Wet1", 3), rep("Dry2", 2)), each = 3),
Abundance = sample(50:600, 18)
)

df <- df %>%
mutate(Month = factor(Month, levels = c("Jun 14", "Aug 14", "Oct 14",
"Dec 14", "Apr 15", "Jun 15")),
Season = factor(Season, levels = c("Dry1", "Wet1", "Dry2")))

(For the record, I don't think facet_grid / facet_wrap were intended for such use cases...)

How to wrap around the polar coordinates in ggplot2 with geom_rect?

One way is to calculate the wrap around amount yourself & define separate rectangles. For example:

test2 <- test %>%
mutate(xmin = mang - sd,
xmax = mang + sd) %>%
mutate(xmin1 = pmax(xmin, -180),
xmax1 = pmin(xmax, 180),
xmin2 = ifelse(xmin < -180, 2 * 180 + xmin, -180),
xmax2 = ifelse(xmax > 180, 2 * -180 + xmax, 180))

> test2
# A tibble: 4 x 10
group mang mdisp sd xmin xmax xmin1 xmax1 xmin2 xmax2
<fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 100. 22.2 88.8 11.6 189. 11.6 180 -180 -171.
2 2 61.6 16.2 115. -53.7 177. -53.7 177. -180 180
3 3 -93.4 13.7 89.1 -183. -4.31 -180 -4.31 177. 180
4 4 -150. 16.3 75.4 -226. -74.9 -180 -74.9 134. 180

Plot:

ggplot(test2) +
geom_rect(aes(xmin = xmin1, xmax = xmax1, ymin = 0, ymax = mdisp, fill = group)) +
geom_rect(aes(xmin = xmin2, xmax = xmax2, ymin = 0, ymax = mdisp, fill = group)) +
geom_segment(aes(x = mang, y = 0, xend = mang, yend = mdisp)) +
scale_x_continuous(breaks = seq(-90, 180, 90), limits = c(-180, 180)) +
coord_polar(start = 2 * pi, direction = -1) +
facet_grid(~ group)

plot

ggplot coord_polar() plotting between pi/2 and -pi/2 at top and bottom

You need scale_x_continuous(expand = c(0,0)). ggplot automatically pads a bit on each end of all the scales so there's a nice border around a plot, but in polar coordinates, you usually want it to wrap with no gap.


Updated with additional steps to neaten the appearance:

  ggplot(ellipse_df, aes(theta/(pi))) + 
geom_histogram(binwidth = .05, colour = "white", boundary = 0) +
scale_x_continuous(expand = c(0,0), breaks = -2:2/4,
labels = c(expression(frac(-pi, 2)),
expression(frac(-pi, 4)),
"0",
expression(frac(+pi, 4)),
expression(frac(+pi, 2)))) +
coord_polar(start = pi/2, direction = -1)

If you run this without the arguments to geom_histogram and without coord_polar, you can diagnose what's going on:

Sample Image

The default 30 bins leaves one at the upper edge of the data with very few observations. By forcing the bins to have a certain width and choosing whether 0 is the center or edge, you can force the bins to line up neatly with the range of your data.

Sample Image

Then when you convert it to polar coordinates, it looks how you want, I assume:

Sample Image


Updated to change scale to equal radians

To get the -pi/2 to pi/2 to be the right half of the circle, you need to expand the x limits quite a bit:

  ... +
scale_x_continuous(expand = c(0,0),
breaks = c(-4, -2:2, 4)/4,
limits = c(-1, 1), # this is the important change
labels = c(expression(-pi),
expression(frac(-pi, 2)),
expression(frac(-pi, 4)), "0",
expression(frac(+pi, 4)),
expression(frac(+pi, 2)),
expression(+pi))) + ...

Since (after scaling by pi), your data go from -1/2 to 1/2, but you want the figure to display -1 to 1, you have to tell it to display all that non-data space.

Sample Image

Sample Image

If your next question is: how can I show it just as a semicircle, without the wasted space on the left? I'll preemptively answer that it's more challenging and will involve precalculating your histogram values and converting the corners of each bar to polar coordinates "by hand".



Related Topics



Leave a reply



Submit