How Achieve Identical Facet Sizes and Scales in Several Multi-Facet Ggplot2 Graphics

How achieve identical facet sizes and scales in several multi-facet ggplot2 graphics?

I use this code to set panel sizes to absolute values, maybe it helps here

set_panel_size <- function(p=NULL, g=ggplotGrob(p), file=NULL, 
margin = unit(1,"mm"),
width=unit(4, "cm"),
height=unit(4, "cm")){

panels <- grep("panel", g$layout$name)
panel_index_w<- unique(g$layout$l[panels])
panel_index_h<- unique(g$layout$t[panels])
nw <- length(panel_index_w)
nh <- length(panel_index_h)

if(getRversion() < "3.3.0"){

# the following conversion is necessary
# because there is no `[<-`.unit method
# so promoting to unit.list allows standard list indexing
g$widths <- grid:::unit.list(g$widths)
g$heights <- grid:::unit.list(g$heights)

g$widths[panel_index_w] <- rep(list(width), nw)
g$heights[panel_index_h] <- rep(list(height), nh)

} else {

g$widths[panel_index_w] <- rep(width, nw)
g$heights[panel_index_h] <- rep(height, nh)

}

if(!is.null(file))
ggsave(file, g,
width = convertWidth(sum(g$widths) + margin,
unitTo = "in", valueOnly = TRUE),
height = convertHeight(sum(g$heights) + margin,
unitTo = "in", valueOnly = TRUE))

invisible(g)
}

print.fixed <- function(x) grid.draw(x)

How to get identical panel width with different y-axis text lengths

You could produce a dummy plot, 'p', with the full dataset, convert it to a gtable with 'g=ggplotGrob(p)', store its widths 'w=g$widths', and for each plot in the loop replace the gtable's widths by 'w'.

p <- ggplot(df) +
geom_point(aes(x = x, y = y))
g <- ggplotGrob(p)
w <- g$widths

pdf("test.pdf")
for(i in 1:2){

p <- ggplot(df[df$name == i,]) +
geom_point(aes(x = x, y = y))
g <- ggplotGrob(p)
g$widths <- w
grid::grid.newpage()
grid::grid.draw(g)
}
dev.off()

Sample Image

Obtaining Percent Scales Reflective of Individual Facets with ggplot2

Using the ..density.. stat rather than ..count.. seems to work for me:

ggplot(dat, aes(x=factor(ANGLE))) +
geom_bar(aes(y = ..density..,group = SHOW,fill = NETWORK)) +
facet_wrap(~SHOW) +
opts(legend.position = "top") +
scale_y_continuous(labels = percent_format())

At least, this produces a different result, I can't say for sure it reflects what you want. Additionally, I'm not sure why the ..count.. stat was behaving that way.

Sample Image

Using coord_flip() with facet_wrap(scales = free_y ) in ggplot2 seems to give unexpected facet axis tick marks and tick labels

This is the second or third time I have run into this problem myself. I have found that I can hack my own solution by defining a custom geom.

geom_bar_horz <- function (mapping = NULL, data = NULL, stat = "bin", position = "stack", ...) {
GeomBar_horz$new(mapping = mapping, data = data, stat = stat, position = position, ...)
}

GeomBar_horz <- proto(ggplot2:::Geom, {
objname <- "bar_horz"

default_stat <- function(.) StatBin
default_pos <- function(.) PositionStack
default_aes <- function(.) aes(colour=NA, fill="grey20", size=0.5, linetype=1, weight = 1, alpha = NA)

required_aes <- c("y")

reparameterise <- function(., df, params) {
df$width <- df$width %||%
params$width %||% (resolution(df$x, FALSE) * 0.9)
OUT <- transform(df,
xmin = pmin(x, 0), xmax = pmax(x, 0),
ymin = y - .45, ymax = y + .45, width = NULL
)
return(OUT)
}

draw_groups <- function(., data, scales, coordinates, ...) {
GeomRect$draw_groups(data, scales, coordinates, ...)
}
guide_geom <- function(.) "polygon"
})

This is just copying the geom_bar code from the ggplot2 github and then switching the x and y references to make a horizontal barplot in the standard Cartesian coordinators.

Note that you must use position='identity' and possibly also stat='identity' for this to work. If you need to use a position other than identity then you will have to eddit the collide function for it to work properly.

Question on drawing two scales dummy facet plot in R

Change

ggplot(data = dtF, mapping = aes(x = location, y = num,fill=group))

in

ggplot(data = dtF, mapping = aes(x = location, y = num, fill=group, color=group))

Play graphic of ggplot2 using multiple facets

I got it using grid.arrange:

library(ggplot2)
library(grid)
library(utils)
grid.newpage()

dt1 <- read.table("https://cdn.rawgit.com/fsbmat/StackOverflow/master/sim50.txt", header = TRUE)
attach(dt1)
#head(dt1)
dt2 <- read.table("https://cdn.rawgit.com/fsbmat/StackOverflow/master/sim200.txt", header = TRUE)
attach(dt2)
#head(dt2)
dt3 <- read.table("https://cdn.rawgit.com/fsbmat/StackOverflow/master/sim1000.txt", header = TRUE)
attach(dt3)
#head(dt3)
g1 <- ggplot(dt1, aes(x=dt1$gamma0))+coord_cartesian(xlim=c(-1.3,3.3),ylim = c(0,0.63)) +
theme(plot.title = element_text(margin = margin(b = 2),size = 6,hjust = 0))
g1 <- g1+geom_histogram(aes(y=..density..), # Histogram with density instead of count on y-axis
binwidth=.5,
colour="black", fill="white",breaks=seq(-3, 3, by = 0.1))
g1 <- g1 + stat_function(fun=dnorm,
color="black",geom="area", fill="gray", alpha=0.1,
args=list(mean=mean(dt1$gamma0),
sd=sd(dt1$gamma0)))
g1 <- g1+ geom_vline(aes(xintercept=1.23, linetype="Valor Verdadeiro"),show.legend =TRUE)
g1 <- g1+ geom_vline(aes(xintercept=mean(dt1$gamma0, na.rm=T), linetype="Valor Estimado"),show.legend =TRUE)
g1 <- g1+ scale_linetype_manual(values=c("dotdash","solid")) # Overlay with transparent density plot
g1 <- g1+ xlab(expression(paste(gamma[0])))+ylab("densidade")
g1 <- g1+ annotate("rect", xmin = -2, xmax = 3.52, ymin = 0.635, ymax = 0.7, fill="gray")
g1 <- g1+ annotate("text", x = 1.23, y = 0.65, label = "n=50", size=4)
g1 <- g1+ theme(plot.margin=unit(c(0.1, 0.1, 0.1, 0.1), units="line"),
legend.text=element_text(size=5),
legend.position = c(0, 0.97),
legend.justification = c("left", "top"),
legend.box.just = "left",
legend.margin = margin(0,0,0,0),
legend.title=element_blank(),
legend.direction = "vertical",
legend.background = element_rect(colour = NA,fill="transparent", size=.5, linetype="dotted"),
legend.key = element_rect(colour = "transparent", fill = NA),
axis.text.y=element_blank(),
axis.ticks.y=element_blank())
g1 <- g1+ guides(linetype = guide_legend(override.aes = list(size = 0.5)))

# Adjust key height and width
g1 = g1 + theme(
legend.key.height = unit(0.3, "cm"),
legend.key.width = unit(0.4, "cm"))

# Get the ggplot Grob
gt1 = ggplotGrob(g1)

# Edit the relevant keys

gt1 <- editGrob(grid.force(gt1), gPath("key-[3,4]-1-[1,2]"),
grep = TRUE, global = TRUE,
x0 = unit(0, "npc"), y0 = unit(0.5, "npc"),
x1 = unit(1, "npc"), y1 = unit(0.5, "npc"))

###################################################

g2 <- ggplot(dt2, aes(x=dt2$gamma0))+coord_cartesian(xlim=c(0,3),ylim = c(0,1.26)) +
theme(plot.title = element_text(margin = margin(b = 2),size = 6,hjust = 0))
g2 <- g2+geom_histogram(aes(y=..density..), # Histogram with density instead of count on y-axis
binwidth=.5,
colour="black", fill="white",breaks=seq(-3, 3, by = 0.1))
g2 <- g2 + stat_function(fun=dnorm,
color="black",geom="area", fill="gray", alpha=0.1,
args=list(mean=mean(dt2$gamma0),
sd=sd(dt2$gamma0)))
g2 <- g2+ geom_vline(aes(xintercept=1.23, linetype="Valor Verdadeiro"),show.legend =TRUE)
g2 <- g2+ geom_vline(aes(xintercept=mean(dt2$gamma0, na.rm=T), linetype="Valor Estimado"),show.legend =TRUE)
g2 <- g2+ scale_linetype_manual(values=c("dotdash","solid")) # Overlay with transparent density plot
g2 <- g2+ xlab(expression(paste(gamma[0])))+ylab("")
g2 <- g2+ annotate("rect", xmin = -2, xmax = 3.5, ymin = 1.27, ymax = 1.8, fill="gray")
g2 <- g2+ annotate("text", x = 1.23, y = 1.3, label = "n=200", size=4)

g2 <- g2+ theme(plot.margin=unit(c(0.1, 0.1, 0.1, 0.1), units="line"),
legend.text=element_text(size=5),
legend.position = c(0, 0.97),
legend.justification = c("left", "top"),
legend.box.just = "left",
legend.margin = margin(0,0,0,0),
legend.title=element_blank(),
legend.direction = "vertical",
legend.background = element_rect(colour = NA,fill="transparent", size=.5, linetype="dotted"),
legend.key = element_rect(colour = "transparent", fill = NA),
axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank())
g2 <- g2+ guides(linetype = guide_legend(override.aes = list(size = 0.5)))

# Adjust key height and width
g2 = g2 + theme(
legend.key.height = unit(0.3, "cm"),
legend.key.width = unit(0.4, "cm"))

# Get the ggplot Grob
gt2 = ggplotGrob(g2)

# Edit the relevant keys

gt2 <- editGrob(grid.force(gt2), gPath("key-[3,4]-1-[1,2]"),
grep = TRUE, global = TRUE,
x0 = unit(0, "npc"), y0 = unit(0.5, "npc"),
x1 = unit(1, "npc"), y1 = unit(0.5, "npc"))

#######################################################

g3 <- ggplot(dt3, aes(x=gamma0))+coord_cartesian(xlim=c(0.6,1.8),ylim = c(0,2.6)) +
theme(plot.title = element_text(margin = margin(b = 2),size = 6,hjust = 0))
g3 <- g3+geom_histogram(aes(y=..density..), # Histogram with density instead of count on y-axis
binwidth=.5,
colour="black", fill="white",breaks=seq(-3, 3, by = 0.1))
g3 <- g3 + stat_function(fun=dnorm,
color="black",geom="area", fill="gray", alpha=0.1,
args=list(mean=mean(dt3$gamma0),
sd=sd(dt3$gamma0)))
g3 <- g3+ geom_vline(aes(xintercept=1.23, linetype="Valor Verdadeiro"),show.legend =TRUE)
g3 <- g3+ geom_vline(aes(xintercept=mean(dt3$gamma0, na.rm=T), linetype="Valor Estimado"),show.legend =TRUE)
g3 <- g3+ scale_linetype_manual(values=c("dotdash","solid")) # Overlay with transparent density plot
g3 <- g3+ labs(x=expression(paste(gamma[0])), y="")
g3 <- g3+ annotate("rect", xmin = -2, xmax = 1.875, ymin = 2.62, ymax = 3, fill="gray")
g3 <- g3+ annotate("text", x = 1.23, y = 2.68, label = "n=1000", size=4)
g3 <- g3+ theme(plot.margin=unit(c(0.1, 0.1, 0.1, 0.1), units="line"),
legend.text=element_text(size=5),
legend.position = c(0, 0.97),
legend.justification = c("left", "top"),
legend.box.just = "left",
legend.margin = margin(0,0,0,0),
legend.title=element_blank(),
legend.direction = "vertical",
legend.background = element_rect(colour = NA,fill="transparent", size=.5, linetype="dotted"),
legend.key = element_rect(colour = "transparent", fill = NA),
axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank())
g3 <- g3+ guides(linetype = guide_legend(override.aes = list(size = 0.5)))

# Adjust key height and width
g3 = g3 + theme(
legend.key.height = unit(.3, "cm"),
legend.key.width = unit(.4, "cm"))

# Get the ggplot Grob
gt3 = ggplotGrob(g3)

# Edit the relevant keys

gt3 <- editGrob(grid.force(gt3), gPath("key-[3,4]-1-[1,2]"),
grep = TRUE, global = TRUE,
x0 = unit(0, "npc"), y0 = unit(0.5, "npc"),
x1 = unit(1, "npc"), y1 = unit(0.5, "npc"))

####################################################

library(gridExtra)
grid.arrange(gt1, gt2, gt3, widths=c(0.1,0.1,0.1), ncol=3)

Sample Image

ggplot - facet wrap - adjusting scale to show apparent differences between values

Edit

(I wasn't planning to return to this, since I think all the answers are really on the same side. But it kept popping up on my feed, until I finally thought of an angle that hasn't been mentioned yet.)

The only way I can think of that somewhat justifies using bars in this context is to make it very clear that the bar heights represent rank, not value:

df2 <- melt(df, id = "brand") %>%
group_by(variable) %>%
mutate(rank = rank(value))

> df2
# A tibble: 12 x 4
# Groups: variable [6]
brand variable value rank
<fctr> <fctr> <dbl> <dbl>
1 nissan a 99.2 2.00
2 toyota a 99.0 1.00
3 nissan b 99.8 1.50
4 toyota b 99.8 1.50
5 nissan c 6496 1.00
6 toyota c 7652 2.00
7 nissan d 1.28 1.00
8 toyota d 1.39 2.00
9 nissan e 216 2.00
10 toyota e 205 1.00
11 nissan f 0.630 2.00
12 toyota f 0.600 1.00

Use rank for the y-values, and everything else can flow naturally without bringing in geom_rect() & so on:

ggplot(df2,
aes(x = brand, y = rank, fill = brand,
label = value)) +
geom_col() +
geom_text(vjust = 0) +
facet_wrap(~variable) +
scale_fill_brewer(palette = "Paired") +
theme(
legend.position="top",
strip.text.y = element_text(angle = 0),
axis.text=element_blank(),
axis.ticks = element_blank()
)

(Plot image not included. It's essentially the same chart as below, except for the y-axis title being "rank" now.)

Is the result still chart junk? I'd say yes, but if you absolutely have to show arbitrary bars to others, tell them the height is based on rank, and it should make a bit more sense.

Original answer below

If you really need to use a bar plot, you can use geom_rect & manually define a different ymin for each facet.

Modify dataframe:

library(dplyr)

df2 <- melt(df, id = "brand") %>%
group_by(variable) %>%
mutate(ymax = value,
ymin = ifelse(diff(value) == 0, 0,
min(value) - (max(value) - min(value)) / 2),
yblank = ifelse(diff(value) == 0, value * 2,
max(value) + (max(value) - min(value)) / 2),
x = as.integer(brand),
xmin = x - 0.4,
xmax = x + 0.4,
label = ifelse(value > 100, round(value, 0), value)) %>%
ungroup()

> df2
# A tibble: 12 x 10
brand variable value ymax ymin yblank x xmin xmax label
<fctr> <fctr> <dbl> <dbl> <dbl> <dbl> <int> <dbl> <dbl> <dbl>
1 nissan a 99.2 99.2 98.9 99.3 1 0.600 1.40 99.2
2 toyota a 99.0 99.0 98.9 99.3 2 1.60 2.40 99.0
3 nissan b 99.8 99.8 0 200 1 0.600 1.40 99.8
4 toyota b 99.8 99.8 0 200 2 1.60 2.40 99.8
5 nissan c 6496 6496 5918 8230 1 0.600 1.40 6496
6 toyota c 7652 7652 5918 8230 2 1.60 2.40 7652
7 nissan d 1.28 1.28 1.23 1.44 1 0.600 1.40 1.28
8 toyota d 1.39 1.39 1.23 1.44 2 1.60 2.40 1.39
9 nissan e 216 216 200 222 1 0.600 1.40 216
10 toyota e 205 205 200 222 2 1.60 2.40 205
11 nissan f 0.630 0.630 0.585 0.645 1 0.600 1.40 0.630
12 toyota f 0.600 0.600 0.585 0.645 2 1.60 2.40 0.600

This creates bars such that the shorter bar in each facet occupies one quarter of the facet's height, while the taller bar occupies three quarters. If the two bars are exactly the same height, they both occupy half the facet's height. If you want to tweak the appearance, just change ymin / yblank.

Plot:

ggplot(df2,
aes(x = x, y = ymax, fill = brand)) +
geom_rect(aes(xmin = xmin, xmax = xmax,
ymin = ymin, ymax = ymax)) +
geom_text(aes(label = label),
vjust = -1) + # position labels slightly above top of each bar
geom_blank(aes(y = yblank)) +
facet_wrap(~ variable, scales = "free_y") +
scale_fill_brewer(palette = "Paired") +
theme(
legend.position="top",
strip.text.y = element_text(angle = 0),
axis.text=element_blank(),
axis.ticks = element_blank()
)

plot

Free scale, but same per-panel range in x/y

As the latest version of ggplot2 uses gtable internally, you can do this kind of task quite easily:

d = data.frame(x=rnorm(100),group=c("A","B"))
d$y = d$x+rnorm(100,0,0.5)
d[d$group=="B","x"]=d[d$group=="B","x"]/100
d[d$group=="B","y"]=d[d$group=="B","y"]/60

# create plots for each level of group
p <- lapply(levels(d$group),
function(i) {
dat <- subset(d, group == i)
lim <- range(c(dat$x, dat$y))
ggplot_gtable(ggplot_build(qplot(x,y,data=dat,asp=1) +
facet_wrap(~group,scale="free") +
coord_equal() +
xlim(lim) + ylim(lim)))
})

# tweaking margins
p[[1]] <- p[[1]][, -6]
p[[2]] <- p[[2]][, -(1:2)]

# draw it
grid.newpage()
grid.draw(cbind(p[[1]], p[[2]], size = "first"))

Sample Image



Related Topics



Leave a reply



Submit