я использовал решение Розен Matev часто, и был разочарован, когда он порвал с ggplot версии 2,0. Я предлагаю решение, хотя и не столь изящное, как Rosen's, но будет работать на участках без фасетов, facet_wrap
и facet_grid
, а также в одностороннем и двухстороннем порядке facet_grid
. Однако он не будет работать с более сложными сетками фасетов и не будет работать с coord_flip
. Существуют две функции: одна для асимметричного расширения вдоль оси y и одна для разложения вдоль оси x. Функции выполняют мультипликативные и аддитивные разложения.
Функции собирают информацию из графика, вычисляют новые пределы для оси y (или x), затем используют geom_blank
для построения новых графиков с требуемыми коэффициентами расширения.
Во-первых, функция для выполнения асимметричного расширения вдоль оси y.
# Function takes two parameters
# 'p' is the plot
# 'expand' is a list of two vectors:
# First vector contains the multiplicative factors;
# Second vector contains the additive parts.
# First element in each vector refers to the lower boundary;
# Second element refers to the upper boundary.
asymmY = function(p, expand = list(mult = c(0, .2), add = c(0, 0))) {
np = p + coord_cartesian(expand = FALSE) # No expand
gb <- ggplot_build(np)
limits <- sapply(gb$panel$ranges, "[[", "y.range")
range = apply(limits, 2, function(x) max(x) - min(x))
rangeU = range*expand[[1]][2]
rangeL = range*expand[[1]][1]
limits <- limits + rbind(-rangeL, rangeU) # Multiplicative expand
limits[1,] = limits[1,] - expand[[2]][1] # Additive expand
limits[2,] = limits[2,] + expand[[2]][2]
limits = as.vector(limits)
df = facet_type(np, gb, "y", limits) # df with new limits - depends on facet type
np = np + geom_blank(data = df, inherit.aes = FALSE, aes(x = Inf, y = y)) # new plot
# But the x axis expansions were set to false. Put back the default expand
gb <- ggplot_build(np)
if(any(grepl("Discrete", class(gb$panel$x_scale[[1]])))) {
limits <- sapply(gb$panel$ranges, "[[", "x.range")
limits[1,] = ceiling(limits[1,]) - .6
limits[2,] = trunc(limits[2,]) + .6
limits = as.vector(limits)
} else {
limits <- sapply(gb$panel$ranges, "[[", "x.range")
range = apply(limits, 2, function(x) max(x) - min(x))
rangeU = range*.05
rangeL = range*.05
limits <- limits + rbind(-rangeL, rangeU)
limits = as.vector(limits)
}
df = facet_type(np, gb, "x", limits)
np + geom_blank(data = df, inherit.aes = FALSE, aes(x = x, y = Inf))
}
# Function to determine type of facetting
# and to get data frame of new limits.
facet_type = function(np, gb, axis, limits) {
if(class(np$facet)[1] == "null") {
setNames(data.frame(y = limits), axis)
} else
if(class(np$facet)[1] == "wrap") {
facetvar <- as.character(np$facet$facets)
facetlev <- gb$panel$layout[[facetvar]]
setNames(data.frame(rep(facetlev, each = 2), limits), c(facetvar, axis))
} else {
facetvar <- as.character(np$facet$cols)
if(length(facetvar) == 0) facetvar <- as.character(np$facet$rows)
facetlev <- gb$panel$layout[[facetvar]]
setNames(data.frame(rep(facetlev, each = 2), limits), c(facetvar, axis))
}
}
Попробуйте использовать некоторые граничные сетки и фасетные сетки.
# Try asymmetric expand along y-axis
library(ggplot2)
p1 <- ggplot(mtcars) +
geom_bar(aes(x = factor(cyl))) +
facet_grid(am ~ vs , scales = "free_y")
p2 <- ggplot(mtcars) +
geom_bar(aes(x = factor(cyl), fill = factor(vs)), width = .5) +
facet_grid(vs ~ ., scales = "free_y")
p3 <- ggplot(mtcars) +
geom_bar(aes(x = factor(cyl), fill = factor(vs)), width = .5) +
facet_grid(. ~ vs)
p4 <- ggplot(mtcars) +
geom_bar(aes(x = factor(cyl), fill = factor(vs)), width = .5) +
facet_wrap(~vs, scales = "free_y")
asymmY(p1, list(c(0, 0.1), c(0, 0)))
asymmY(p2, list(c(0, 0.1), c(0, 0)))
asymmY(p3, list(c(0, 0.1), c(0, 0)))
asymmY(p4, list(c(0, 0.1), c(0, 0)))
Во-вторых, функция для выполнения асимметричного расширения вдоль оси х.
asymmX = function(p, expand = list(mult = c(0, .2), add = c(0, 0))) {
np = p + coord_cartesian(expand = FALSE) # No expand
gb <- ggplot_build(np)
limits <- sapply(gb$panel$ranges, "[[", "x.range")
range = apply(limits, 2, function(x) max(x) - min(x))
rangeU = range*expand[[1]][2]
rangeL = range*expand[[1]][1]
limits <- limits + rbind(-rangeL, rangeU) # Mult expand
limits[1,] = limits[1,] - expand[[2]][1]
limits[2,] = limits[2,] + expand[[2]][2] # Add expand
limits = as.vector(limits)
df = facet_type(np, gb, "x", limits) # df with new limits - depends on facet type
np = np + geom_blank(data = df, inherit.aes = FALSE, aes(x = x, y = Inf)) # new plot
# But the y axis expansions were set to false. Put back the default expand
gb <- ggplot_build(np)
if(any(grepl("Discrete", class(gb$panel$y_scale[[1]])))) {
limits <- sapply(gb$panel$ranges, "[[", "y.range")
limits[1,] = ceiling(limits[1,]) - .6
limits[2,] = trunc(limits[2,]) + .6
limits = as.vector(limits)
} else {
limits <- sapply(gb$panel$ranges, "[[", "y.range")
range = apply(limits, 2, function(x) max(x) - min(x))
rangeU = range*.05
rangeL = range*.05
limits <- limits + rbind(-rangeL, rangeU)
limits = as.vector(limits)
}
df = facet_type(np, gb, "y", limits)
np + geom_blank(data = df, inherit.aes = FALSE, aes(x = Inf, y = y))
}
Попробуйте.
# Try asymmetric expand along x-axis
df = data.frame(x = c(20, 15, 25, 23, 12, 14),
y = rep(c("a", "b", "c"), 2),
z = rep(c("aaa", "bbb"), each = 3),
w = rep(c("ccc", "ddd", "eee"), each = 2))
p1 = ggplot(df[,-4]) + geom_point(aes(x, y)) +
geom_segment(aes(x = 0, xend = x, y = y, yend = y)) +
geom_text(aes(x = x, y = y, label = x), hjust = -1) +
facet_grid(. ~ z, scales = "free_x")
p2 = ggplot(df[, -4]) + geom_point(aes(x, y)) +
geom_segment(aes(x = 0, xend = x, y = y, yend = y)) +
geom_text(aes(x = x, y = y, label = x), hjust = -1) +
facet_grid(z ~ .)
p3 = ggplot(df) + geom_point(aes(x, y)) +
geom_segment(aes(x = 0, xend = x, y = y, yend = y)) +
geom_text(aes(x = x, y = y, label = x), hjust = -1) +
facet_grid(w ~ z)
p4 = ggplot(df[,-4]) + geom_point(aes(x, y)) +
geom_segment(aes(x = 0, xend = x, y = y, yend = y)) +
geom_text(aes(x = x, y = y, label = x), hjust = -1) +
facet_wrap(~ z)
asymmX(p1, list(c(0, .15), c(0, 0)))
asymmX(p2, list(c(0, 0), c(0, 5)))
asymmX(p3, list(c(0, .2), c(0, 0)))
asymmX(p4, list(c(0, 0), c(9, 5)))
Для обеспечения максимальной гибкости, я бы предложил сделать пример в ваших аспектах использования вопрос. Для незапланированного графика можно агрегировать свои данные и использовать 'ylim = c (0, 1.1 * max (aggregated_data $ y_variable))' - хотя и не так хорошо, как модифицированное решение расширения. С граненой графикой решение 'ylim' не будет работать хорошо, поэтому решение' expand' * необходимо *. – Gregor
В незавершенном случае вы не могли использовать 'coord_cartesian', чтобы получить нужный эффект? Например: 'coord_cartesian (ylim = c (0, max (таблица (mtcars $ cyl)) + 2), xlim = c (min (mtcars $ cyl) -0.75, max (mtcars $ cyl) +0.75), expand = FALSE) '. До версии 2.0 нет необходимости устанавливать 'xlim', но (насколько я могу сказать) теперь, когда по умолчанию используется' expand = TRUE', вам нужно явно установить оба ограничения, если вы хотите сохранить заполнение на ось х. – eipi10
@ eipi10 делает мой пункт. В безграничном случае вы можете найти обходные пути довольно легко. Ответ * Меня интересует (и я думаю, что Хью тоже) - это тот, который работает как с фасетками, так и без них. – Gregor