How to apply two modifications on ggplot's colour scale bar - r

I would like to display a scale_color_gradient scale bar that (i) only has a set number of decimal points and (ii) also always displays "0" not "0.00". What is the best way to do this?
library(ggplot2)
dat <- data.frame(x = rnorm(10, 30, .2), y = runif(10, 3, 5),z = rnorm(10, 30, .2))
scaled.dat <- data.frame(scale(dat))
ggplot(scaled.dat, aes(x, y, colour = z)) + geom_point()+
# Modify the number of decimal points
scale_color_gradient(labels = function(x) sprintf("%.5f", x))
# Make zero value display "0" only
#scale_color_gradient(labels = ~sub("0.0", "0", sprintf("%.1f", .x)))

Using an ifelse you could do:
library(ggplot2)
set.seed(123)
dat <- data.frame(x = rnorm(10, 30, .2), y = runif(10, 3, 5), z = rnorm(10, 30, .2))
scaled.dat <- data.frame(scale(dat))
ggplot(scaled.dat, aes(x, y, colour = z)) +
geom_point() +
scale_color_gradient(
labels = ~ ifelse(.x != 0, sprintf("%.5f", .x), sprintf("%.0f", .x))
)

Related

Incorrect points plotting on ggplot subplots

I would like the following code to show a dot at x = 10, 20 and 30 on subplots 1, 2 and 3 respectively (actually it's y = 10, 20 and 30 but the axes/coordinates are flipped). Instead it is plotting the dot at x = 30 for each one.
pcrtle <- c(10, 20, 30)
df <- data.frame(quartile = c("Q1", "Q2", "Q3", "Q4"), x = c(
1, 1, 1,
1
), y = c(25, 25, 25, 25))
plt1 <- c()
for (ii in 1:length(pcrtle)) {
plt1[[ii]] <- ggplot() +
geom_bar(aes(x = x, y = y, fill = quartile), data = df, stat = "identity") +
coord_flip() +
ylab("") +
geom_point(aes(x = 1, y = pcrtle[ii]), size = 5, shape = 21, fill = "#3d3d29")
}
plt2 <- do.call("grid.arrange", c(plt1, ncol = 1))
plt2
The code above produces the following plot
I use the do.call for grid.arrange as the length of Percentile will be variable.
This seems to come about because of non-standard evaluation in ggplot, you can fix it by moving the point's y data outside the aes because it's not being evaluated in the data dataframe, to give:
geom_point(aes(x = 1), y = pcrtle[ii], size = 5, shape = 21, fill = "#3d3d29")

predefine bins in geom_histogram

How do I pre-define bins for the histogram? For e.g.
predefine_bin_edges <- seq(0, 10, 1)
Can someone please use this predefine bin edges to update the example.
Edited later -
Also, Is there a way to include the extreme points, that are outside the binrange, in the corner bins for a complete picture of data distribution.
library(tidyverse)
# data
x <- rnorm(n = 1000, mean = 5, sd = 3)
tbl <- tibble(x)
# geom_histogram()
ggplot(data = tbl,
aes(x = x)) +
geom_histogram()
Do you mean like this?
library(tidyverse)
# data
x <- rnorm(n = 1000, mean = 5, sd = 3)
tbl <- tibble(x)
# geom_histogram()
ggplot(data = tbl,
aes(x = x)) +
geom_histogram(breaks = seq(0, 10, 1))
I don't think there is an argument to geom histogram that can include the values outside the range in the corner bins. However, you can squish the data at the aes() level.
ggplot(data = tbl,
aes(x = pmax(pmin(x, 10), 0))) +
geom_histogram(breaks = seq(0, 10, 1))
Or if you're uncomfortable with that, you can set the limits + oob arguments in the scale.
ggplot(data = tbl,
aes(x = x)) +
geom_histogram(breaks = seq(0, 10, 1)) +
scale_x_continuous(limits = c(0, 10),
oob = scales::oob_squish)

ggplot2 plot an angle between two lines

I would like to plot an angle between two lines using ggplot2, meaning something similar to the bold red line in the plot below. Is there an easy solution to this?
Data and code to make the plot without the red line:
library(tidyverse)
df <- tibble(
line = c("A", "A", "B", "B"),
x = c(1, 5, 1, 3),
y = c(1, 3, 1, 5))
ggplot(
df, aes(x, y, group = line))+
geom_path()
have a look at geom_curve, e.g. :
ggplot( df, aes(x, y, group = line))+
geom_path() +
geom_curve(aes(x = 1.5, y = 2, xend = 2, yend = 1.5), curvature = -0.5, color = "red", size = 3)
You will have to tweak it a bit to use it in a more robust, automatic way, for example:
red_curve <- df %>%
group_by(line) %>%
summarise( avg_x = mean(x),
avg_y = mean(y))
ggplot( df, aes(x, y, group = line))+
geom_path() +
geom_curve( data = red_curve, aes(x = avg_x[1], y = avg_y[1], xend = avg_x[2], yend = avg_y[2]), curvature = 0.5, color = "red", size = 3)
Here is a solution with geom_arc of the ggforce package.
library(ggplot2)
library(ggforce)
angle <- function(p, c){
M <- p - c
Arg(complex(real = M[1], imaginary = M[2]))
}
O <- c(1,1)
P1 <- c(5,3)
P2 <- c(3,5)
a1 <- angle(P1, O)
a2 <- angle(P2, O)
df <- data.frame(
line = c("A", "A", "B", "B"),
x = c(1, 5, 1, 3),
y = c(1, 3, 1, 5)
)
ggplot(df, aes(x, y, group = line)) +
geom_path() +
geom_arc(aes(x0 = 1, y0 = 1, r = 1, start = a1, end = a2),
color="red", size = 2, inherit.aes = FALSE)
The arc does not look like a true arc circle. That's because the aspect ratio is not set to 1. To set the aspect ratio to 1:
ggplot(df, aes(x, y, group = line)) +
geom_path() +
geom_arc(aes(x0 = 1, y0 = 1, r = 1, start = a1, end = a2),
color="red", size = 2, inherit.aes = FALSE) +
coord_fixed()

Lines on top and bottom of ggplot plot area

I want to add a line on the top and bottom of my plots (bottom line below the x label and axis) created using ggplot2. So far I have added a rectangle around the plot, but I do not want the lines on the sides.
x <- 1:10
y <- rnorm(10,mean = x)
df <- data.frame(x,y)
library(ggplot2)
ggplot(data = df, mapping = aes(x,y)) + geom_point() +
theme(plot.background = element_rect(size = 1, color = 'blue'))
I hope you guys have a solution.
Will something similar to this work?
x <- 1:10
y <- rnorm(10,mean = x)
df <- data.frame(x,y)
ggplot(data = df, mapping = aes(x,y)) + geom_point() +
annotate(geom = 'segment',
y = Inf,
yend = Inf,
x = -Inf,
xend = Inf,
size = 2) +
theme(axis.line.x = element_line(size = 1))
Not a perfect, but working solution. You have to plot huge "-" (size = 1000) outside plot area. This solution is not perfect as you have to manually adjust position of "-" on the y-axis.
df <- data.frame(x = 1:10, y = 1:10)
library(ggplot2)
ggplot(df, aes(x, y)) +
geom_point() +
# Y position adjusted manually
geom_text(aes(5, 2.9, label = "-"), color = "blue", size = 1000) +
# Y position adjusted manually
geom_text(aes(5, 21.2, label = "-"), color = "blue", size = 1000) +
# Plot outside plot area
coord_cartesian(ylim = c(0, 10), clip = "off")
I am not completely happy with the solution as I don't fully grasp
how to change the size of the lines
why they are not perfectly aligned with top and bottom when using patchwork::wrap_plots()
why it does not show the top line using ggpubr::ggarrange() or cowplot::plot_grid()
but based on this code, I suggest the following solution:
library(ggplot2)
df <- data.frame(x = 1:5, y = 1:5)
p <- ggplot(data = df) + aes(x, y) + geom_point()
top_line <- grid::grobTree(grid::linesGrob(x = grid::unit(c(0, 1), "npc"), y = grid::unit(1, "npc")))
bot_line <- grid::grobTree(grid::linesGrob(x = grid::unit(c(0, 1), "npc"), y = grid::unit(0, "npc")))
patchwork::wrap_plots(top_line, p, bot_line,
ncol = 1, nrow = 3,
heights = c(0, 1, 0))
ggpubr::ggarrange(top_line, p, bot_line,
ncol = 1, nrow = 3,
heights = c(0, 1, 0))
cowplot::plot_grid(top_line, p, bot_line,
ncol = 1, nrow = 3,
rel_heights = c(0, 1, 0))
Created on 2022-08-25 with reprex v2.0.2

How to draw boxes around "groups" in a heatmap?

I made a graph like this:
library(reshape2)
library(ggplot2)
m <- matrix(1:64 - 32, 8)
rownames(m) <- colnames(m) <-
c(paste0("a", 1:3), paste0("b", 1:2), paste0("c", 1:3))
d <- melt(m)
gg <- ggplot(d) +
geom_tile(aes(x = Var1, y = Var2, fill = value)) +
scale_fill_gradient2()
How can I programmatically draw boxes around the "a", "b", and "c" groups?
The matrix m will always be square. colnames(m) and rownames(m) will always be the same. Therefore the boxes will be cover the entire grid and will never overlap. The group sizes will vary, in general.
I'm also not married to ggplot2. I'm open to a solution in base graphics with image if it's not fussier than the ggplot2/grid version.
I got as far as
d$group <- substr(d$Var1, 1, 1)
before I realized I had no clue how to proceed.
What I have:
What I want:
Or geom_segment
library('reshape2')
library('ggplot2')
m <- matrix(1:64 - 32, 8)
rownames(m) <- colnames(m) <-
c(paste0("a", 1:3), paste0("b", 1:2), paste0("c", 1:3))
gg <- ggplot(melt(m)) +
geom_tile(aes(x = Var1, y = Var2, fill = value)) +
scale_fill_gradient2()
tt <- table(gsub('\\d+', '', colnames(m)))
ll <- unname(c(0, cumsum(tt)) + .5)
gg + geom_segment(aes(x = ll, xend = ll, y = head(ll, 1), yend = tail(ll, 1))) +
geom_segment(aes(y = ll, yend = ll, x = head(ll, 1), xend = tail(ll, 1)))
This may be considered cheating, but very easy:
# Depending on your data you may be able to generate `d2` directly
# here we need to re-order a bit
d2 <- transform(
d, V1 = substr(Var1, 1, 1),
V2=factor(substr(Var2, 1, 1), levels=letters[3:1])
)
ggplot(d2) +
geom_tile(aes(x = Var1, y = Var2, fill = value)) +
scale_fill_gradient2() +
facet_grid(V2 ~ V1, scales="free", space="free")
i.e. something like this:
xmin <- c(0.5,3.5,5.5)
xmax <- c(3.5,5.5,8.5)
ymin <- c(0.5,3.5,5.5)
ymax <- c(3.5,5.5,8.5)
box1 <- data.frame(xmin = rep(xmin,times = 3),
xmax = rep(xmax,each = 3),
ymin = rep(ymin,times = 3),
ymax = rep(ymax,each = 3))
ggplot(melt(m)) +
geom_tile(aes(x = Var1, y = Var2, fill = value)) +
geom_rect(data = box1,aes(xmin = xmin,xmax = xmax,ymin = ymin,ymax= ymax),
fill = NA,color = "black") +
scale_fill_gradient2()
Another option would be to simply use geom_hline and geom_vline, although you might have a hard time removing the little bits that extend past the edges of the colored rectangles.

Resources