I want to plot a diagram with a log scales y axis:
data <- data.frame(x = seq_len(5), y = c(2,1,100,500,30))
ggplot(data) + aes(x,y) + geom_col() + scale_y_log10()
But because the horizontal axis always crosses the vertical axis at y = 1, the bar for y = 1 is never drawn.
I know that log(1) = 0, but can I set the plot base to 0.1 to let the bar start below 1?
Can't you just multiply the values of y by 10 but divide the labels by the same amount?
ggplot(data) + aes(x, y * 10) + geom_col() +
scale_y_log10(labels = function(x) x/10, name = "y")
You could do the same with a histogram:
set.seed(2)
ggplot(data.frame(x = rnorm(1000)), aes(x)) +
geom_histogram(aes(y = ..count.. * 10), color = "black", fill = "gold") +
scale_y_log10(labels = function(x) x/10, name = "count")
An alternative solution is to place the bottom of the bars at -Inf, so that the log10(1) still shows up. This is a bit tricky, because you have to reparameterise the bars as rectangles and get the geom to understand that the -Inf is after scale transformation.
library(ggplot2)
data <- data.frame(x = seq_len(5), y = c(2,1,100,500,30))
ggplot(data) +
geom_rect(
aes(xmin = x - 0.45, xmax = x + 0.45,
ymin = stage(1, after_scale = -Inf), ymax = y)
) +
scale_y_log10()
Created on 2020-11-03 by the reprex package (v0.3.0)
Related
I am trying to use ggplot to shade the region between the vertical line and the diagonal line as shown below. Is there a way to do this?
df <- data.frame(x=c(1,-1), y=c(1,-1))
ggplot(df, aes(x, y)) +
geom_point() +
geom_abline(slope=-1) +
geom_vline(xintercept = 0)
It is a bit tricky, but if you know the coordinates of the area (Inf), you can use them to fill the shades using geom_polygon like this:
library(ggplot2)
df <- data.frame(x=c(1,-1), y=c(1,-1))
upper_area <- data.frame(x=c(-Inf,0,0),y=c(Inf,Inf,0))
down_area <- data.frame(x=c(0,0,Inf),y=c(0,-Inf,-Inf))
ggplot(df, aes(x, y)) +
geom_point() +
geom_abline(slope=-1) +
geom_vline(xintercept = 0) +
geom_polygon(aes(x=x, y=y), data=upper_area, fill="red") +
geom_polygon(aes(x=x, y=y), data=down_area, fill="blue")
Created on 2022-07-15 by the reprex package (v2.0.1)
old answer
It is a bit tricky, but if you know the coordinates of the area, you can use them to fill the shades using geom_polygon like this:
library(ggplot2)
df <- data.frame(x=c(1,-1), y=c(1,-1))
upper_area <- data.frame(x=c(-1,0,0),y=c(1,1,0))
down_area <- data.frame(x=c(0,0,1),y=c(0,-1,-1))
ggplot(df, aes(x, y)) +
geom_point() +
geom_abline(slope=-1) +
geom_vline(xintercept = 0) +
geom_polygon(aes(x=x, y=y), data=upper_area, fill="red") +
geom_polygon(aes(x=x, y=y), data=down_area, fill="blue")
Created on 2022-07-15 by the reprex package (v2.0.1)
One option would be to use annotate with geom="ribbon":
df <- data.frame(x=c(1,-1), y=c(1,-1))
library(ggplot2)
ggplot(df, aes(x, y)) +
geom_point() +
# 1.1 = 1 + default expansion of 5 % of the data range, i.e. .05 * 2 (= 1 - (-1))
annotate(geom="ribbon", x = c(-Inf, 0), ymin = c(1.1, 0), ymax = Inf, fill = "grey45") +
annotate(geom="ribbon", x = c(0, Inf), ymin = -Inf, ymax = c(0, -1.1), fill = "grey45") +
geom_vline(xintercept = 0) +
geom_abline(slope=-1) +
coord_cartesian(xlim = c(-1, 1), ylim = c(-1, 1))
For a more robust example that works with any flexible slope, intercept and axis limits.
library(ggplot2)
df <- data.frame(x = c(1,-1), y = c(1,-1))
slope <- -0.5
intercept <- 0.5
limit_x <- 4
limit_y <- 4
ggplot(df, aes(x, y)) +
annotate(geom = "ribbon", x = c(-Inf, intercept), ymin = c(-limit_x * slope, slope * intercept), ymax = Inf, fill = "red") +
annotate(geom = "ribbon", x = c(intercept, Inf), ymin = -Inf, ymax = c(slope * intercept, limit_x * slope), fill = "blue") +
scale_x_continuous(limits = c(-limit_x, limit_x), expand = c(0, 0)) +
scale_y_continuous(limits = c(-limit_y, limit_y), expand = c(0, 0)) +
geom_point() +
geom_abline(slope = slope) +
geom_vline(xintercept = intercept)
I have the following graph and code:
Graph
ggplot(long2, aes(x = DATA, y = value, fill = variable)) + geom_area(position="fill", alpha=0.75) +
scale_y_continuous(labels = scales::comma,n.breaks = 5,breaks = waiver()) +
scale_fill_viridis_d() +
scale_x_date(date_labels = "%b/%Y",date_breaks = "6 months") +
ggtitle("Proporcions de les visites, només 9T i 9C") +
xlab("Data") + ylab("% visites") +
theme_minimal() + theme(legend.position="bottom") + guides(fill=guide_legend(title=NULL)) +
annotate("rect", fill = "white", alpha = 0.3,
xmin = as.Date.character("2020-03-16"), xmax = as.Date.character("2020-06-22"),
ymin = 0, ymax = 1)
But it has some sawtooth, how am I supposed to smooth it out?
I believe your situation is roughly analogous to the following, wherein we have missing x-positions for one group, but not the other at the same position. This causes spikes if you set position = "fill".
library(ggplot2)
x <- seq_len(100)
df <- data.frame(
x = c(x[-c(25, 75)], x[-50]),
y = c(cos(x[-c(25, 75)]), sin(x[-50])) + 5,
group = rep(c("A", "B"), c(98, 99))
)
ggplot(df, aes(x, y, fill = group)) +
geom_area(position = "fill")
To smooth out these spikes, it has been suggested to linearly interpolate the data at the missing positions.
# Find all used x-positions
ux <- unique(df$x)
# Split data by group, interpolate data groupwise
df <- lapply(split(df, df$group), function(xy) {
approxed <- approx(xy$x, xy$y, xout = ux)
data.frame(x = ux, y = approxed$y, group = xy$group[1])
})
# Recombine data
df <- do.call(rbind, df)
# Now without spikes :)
ggplot(df, aes(x, y, fill = group)) +
geom_area(position = "fill")
Created on 2022-06-17 by the reprex package (v2.0.1)
P.S. I would also have expected a red spike at x=50, but for some reason this didn't happen.
I want to shade part of the background in each facet of a simple plot. If I omit faceting and run geom_rect + geom_point, the expected results appear as shown in the MRE below. If I omit the rectangle and run geom_point + facet_grid, the expected 4 panels have each point in the correct facet. But when I combine geom_rect + geom_point + and facet_grid, the points in the first category and only those get plotted in every facet. What is going on please???
library(ggplot2)
set.seed(42)
syn.dat <- data.frame(
category.1 = as.factor(rep(c("1A", "1B"), each = 8)),
category.2 = as.factor(rep(rep(c("2A", "2B"), times = 2), each = 4)),
x = rep(-1:2, each = 4) + runif(8, max = .4),
y = rep(-1:2, each = 4) + runif(8, max = .4))
ggplot() +
geom_rect(aes(xmin = -Inf, xmax = Inf, ymin = .5,
ymax = Inf), fill = "lightyellow") +
geom_point(data = syn.dat, aes(x = x, y = y)) +
facet_grid(cols = vars(category.1),
rows = vars(category.2))
I'm not totally sure about this, but it may be that you need to explicitly provide the data argument to ggplot itself, in order for facet_grid to correctly pick up all the values?
ggplot(syn.dat) +
geom_rect(aes(xmin = -Inf, xmax = Inf, ymin = 0.5, ymax = Inf), fill = "lightyellow") +
geom_point(aes(x = x, y = y)) +
facet_grid(rows = vars(category.2), vars(cols = category.1))
I usually use infinite values in the position aesthetics of ggplot text objects to make labels appear in the corner of the plot regardless of the data scale. I mainly use this when making multi-panel figures which should have letters in each panel to identify each panel in the figure legend. However, this does not seem to work with log scales if I want the label to appear on the left or bottom, since obviously transforming log(-Inf) returns NaN. Is there an easy fix for this? I could do a long workaround but I was hoping there is something easier. Example is below:
notlogdata <- data.frame(x = 1:3,y = 1:3)
ggplot(notlogdata, aes(x = x,y = y)) +
geom_point() +
geom_text(data = data.frame(x = -Inf, y = Inf, l = 'a'), aes(label = l), hjust = -0.5, vjust = 1)
logdata <- data.frame(x = 10^(1:3), y = 10^(1:3))
ggplot(logdata, aes(x = x,y = y)) +
geom_point() +
geom_text(data = data.frame(x = -Inf, y = Inf, l = 'a'), aes(label = l), hjust = -0.5, vjust = 1) +
scale_x_log10() +
scale_y_log10()
First plot with untransformed axes appears fine:
The second plot does not have a label and returns the warning:
Warning messages:
1: In self$trans$transform(x) : NaNs produced
2: Removed 1 rows containing missing values (geom_text).
annotation_custom(gTree(children=gList(textGrob("a", hjust=0,x=0,vjust=1,y=1))))
You can take a log of Inf --- log(Inf) is Inf. The issue is you can't take a log of a negative like -Inf. But log(0) is -Inf, so if you set x = 0 it will work as expected.
ggplot(logdata, aes(x = x,y = y)) +
geom_point() +
geom_text(
data = data.frame(x = 0, y = Inf, l = 'a'),
aes(label = l), hjust = -0.5, vjust = 1
) +
scale_x_log10() +
scale_y_log10()
It does not appear to be possible to use negative positioning aesthetics, including -Inf, on a log-transformed axis. Here is a solution using cowplot as suggested by zx8754
library(cowplot)
notlogdata <- data.frame(x = 1:3,y = 1:3)
notlogplot <- ggplot(notlogdata, aes(x = x,y = y)) +
geom_point()
logdata <- data.frame(x = 10^(1:3), y = 10^(1:3))
logplot <- ggplot(logdata, aes(x = x,y = y)) +
geom_point() +
scale_x_log10() +
scale_y_log10()
plot_grid(notlogplot, logplot, labels=c('a','b'), hjust=-8)
This outputs the plot below:
I want to have an error bar for my geom_hline and thought that a geom_ribbon with opacity would look best. but i cant figure out how to make it reach the ends of the plot. I want the geom_ribbon to touch the sides of the plot as the geom_hline does. Here is the example code:
library('ggplot2')
x <- c(1,2,3,4,5,6,7,8,9,10)
y <- c(1,2,3,4,5,6,7,8,9,10)
data <- data.frame(x,y)
p1 <- ggplot(data,aes(x = x, y = y)) + geom_line() + geom_hline(yintercept=5)
p1 + geom_ribbon(aes(y = y[5],ymin = y[5]-0.5, ymax = y[5]+0.5, fill = 'red'), alpha = 0.4)
Use annotate with infinite x-values:
ggplot(data, aes(x, y)) +
geom_line() +
geom_hline(yintercept = 5) +
annotate('ribbon', x = c(-Inf, Inf), ymin = 5 - 0.5, ymax = 5 + 0.5,
alpha = 0.4, fill = 'red')
If you need a legend, use geom_ribbon directly, like so:
ggplot(data, aes(x, y)) +
geom_line() +
geom_hline(yintercept = 5) +
geom_ribbon(
aes(x, y = NULL, ymin = ymin, ymax = ymax, fill = 'my_label'),
data.frame(x = c(-Inf, Inf), ymin = 5 - 0.5, ymax = 5 + 0.5),
alpha = 0.4
)
There is no way in geom_hline() to set xlim, instead the horizontal line goes from end of the plot to the other. You can use geom_segment instead, which allows you to control the x-range and y-range of the line by specifying segment's start coordinate (x, y) and end coordinate (xend, yend)
This works:
library('ggplot2')
x <- c(1,2,3,4,5,6,7,8,9,10)
y <- c(1,2,3,4,5,6,7,8,9,10)
data <- data.frame(x,y)
p1 <- ggplot(data, aes(x = x, y = y)) + geom_line() + geom_segment(aes(x = x[1], xend = x[10], y = y[5], yend = y[5]))
p1 + geom_ribbon(aes(y = y[5],ymin = y[5]-0.5, ymax = y[5]+0.5, fill = 'red'), alpha = 0.4)
Couple options:
1) Use geom_hline instead of geom_ribbon like so (probably best option):
p1 + geom_hline(yintercept = y[5], color = 'red', size = 8, alpha = 0.4)
2) Remove area between plot area and axis by adding scale_x_continuous(expand=c(0,0)) like so (credit to https://stackoverflow.com/a/22945857/5727278):
p1 + geom_ribbon(aes(y = y[5],ymin = y[5]-0.5, ymax = y[5]+0.5, fill = 'red'), alpha = 0.4) +
scale_x_continuous(expand=c(0,0))