I'm trying to use gridExtra to combine two ggplot2 bar plots (with coordinates flipped so that the labels take up horizontal space). The problem is that some of the labels are short and some are long. I want the width of the left and right columns to be the same, irrespective of the width of the labels. Here's an example:
library(ggplot2)
library(gridExtra)
datashort <- data.frame(ecks = c("Short1", "Short2", "Short3"), why = c(30, 20, 40))
datalong <- data.frame(ecks = c(paste0("Really, Really, Really, Really Long", c(1:3))),
why = c(20, 30, 40))
plotshort <- ggplot(data = datashort, aes(x = ecks, y = why, width = .5)) +
geom_bar(stat = "identity") +
scale_y_continuous(breaks = c(10, 20, 30, 40, 50), limits = c(0, 50)) +
coord_flip()
plotlong <- ggplot(data = datalong, aes(x = ecks, y = why, width = .5)) +
geom_bar(stat = "identity") +
scale_y_continuous(breaks = c(10, 20, 30, 40, 50), limits = c(0, 50)) +
coord_flip()
grid.arrange(plotshort, plotlong, ncol = 2)
If you do that, you get a really wide left chart and a really squished right chart. I know that you can add widths to the grid arrange, but I'd like to know exactly what they should be. Here it looks like widths=c(.4, .6) works pretty well, but it's not exact. Also, you have to use trial and error to get there, which isn't ideal. Is there any way to figure out what the actual plot area is so that you can calculate the right widths?
One very simple solution would be to use cowplot:
cowplot::plot_grid(plotshort, plotlong, ncol = 2,align = "v")
The argument align allows you to set the plot areas equal.
Or another option would be to add some breaks to the titles:
library(stringr)
plotlong <- datalong %>%
mutate(ecks = str_replace_all(ecks, "[[:punct:]]\\s|\\s", "\\\n")) %>%
ggplot(aes(x = ecks, y = why, width = .5)) +
geom_bar(stat = "identity") +
scale_y_continuous(breaks = c(10, 20, 30, 40, 50), limits = c(0, 50)) +
coord_flip()
cowplot::plot_grid(plotshort, plotlong, ncol = 2,align = "v")
If you add the breaks, then you could use grid.arrange
grid.arrange(plotshort, plotlong, ncol = 2)
You can wrap the labels using stringr and modify your ggplot2 script to define your labels in st_wrap and plot as scale_x_discrete:
library(stringr)
plotshort <- ggplot(data = datashort, aes(x = ecks, y = why, width = .5)) +
geom_bar(stat = "identity") + coord_flip() +
scale_x_discrete(labels = str_wrap(c("Short1", "Short2", "Short3"), width = 10))
plotlong <- ggplot(data = datalong, aes(x = ecks, y = why, width = .5)) +
geom_bar(stat = "identity") + coord_flip() +
scale_x_discrete(labels = str_wrap(c("Really, Really, Really, Really Long1", "Really, Really, Really, Really Long2", "Really, Really, Really, Really Long3"), width = 10))
grid.arrange(plotshort, plotlong, ncol = 2)
Related
I want to show the x-axis labels and the form of the line clearly on this plot. It is a point plot with a lot of categories along the x-axis which makes the plot very wide and very hard to read the x-axis.
Would it be possible to fold the plot in half and display it on two panels, one above the other? How would I do that? I thought about hacking around with facet_wrap but this got ugly with the ordered points (as I wish to maintain the order of the x-axis based on the value).
Or are there better ways of showing this data? The position of the categories along the x-axis is of interest, as is the shape of the line formed by the points.
I generated the example plot using this code:
library(stringi)
example <- data.frame(
cat = do.call(paste0, Map(stri_rand_strings, n=150, length=c(25, 14, 13), pattern = c('[A-Z]', '[0-9]', '[A-Z]'))),
val = rnorm(150, mean = 20)
)
cat_ordered_by_val <- example$cat[order(example$val)]
example$cat = factor(example$cat, levels=cat_ordered_by_val)
ggplot(example, aes(y = val, x = cat)) +
geom_point() +
ylab("Value") + xlab("Category") +
theme_bw() +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1, size=5))
ggsave("~/Desktop/what_a_plot.jpg")
This puts points in one of the two facets in alternating ways. You can also do mutate(facet = row_number() < nrow(example) / 2) to put the first half of the points in one facet and the other half in the other facet:
library(tidyverse)
example <- data.frame(
cat = do.call(paste0, Map(stri_rand_strings, n = 150, length = c(25, 14, 13), pattern = c("[A-Z]", "[0-9]", "[A-Z]"))),
val = rnorm(150, mean = 20)
)
cat_ordered_by_val <- example$cat[order(example$val)]
example$cat <- factor(example$cat, levels = cat_ordered_by_val)
example %>%
arrange(cat) %>%
mutate(facet = row_number() %% 2) %>%
ggplot(aes(y = val, x = cat)) +
geom_point() +
ylab("Value") +
xlab("Category") +
theme_bw() +
facet_wrap(~facet, ncol = 1, scales = "free") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1, size = 5))
Hi I want to make this kind of graphs. I know it is bar charts plot. And can be used with bar_charts. But the only I can do is to make a simple bar charts with the maximum value above the bars. I suppose it is more complicated and I am not sure whether ggplot can make it. Any idea ?
[
tab1<read.csv("/Users/vladalexandru/Documente/vlad_R/R_markdown/Rmrkd/Tab/Tx_tn.csv")
tab1$dat <- as.Date(tab1$dat)
tab2 <- aggregate(tab1, by = list(format(tab1$dat, "%m")), FUN = "mean")
ggplot(data=tab2, aes(x=Group.1, y= tx)) +
geom_bar(stat="identity", fill="yellow")+
geom_text(aes(label=round(tx,0)), vjust=-0.3, size=3.5)+
labs(x = "month")+
scale_y_continuous(name = "°C")+
theme_minimal()
It is actually possible to make a gradient-filled boxplot like this in ggplot. It's a hassle though. You have to add a transparent bar at the bottom of a stacked bar chart, then construct the gradient by slicing the bar into thin pieces and coloring them:
set.seed(123)
a <- sample(10:20, 12, TRUE)
b <- sample(1:10, 12, TRUE)
data.frame(vals = c(sapply(1:12, function(i) c(rep(a[i]/39, 39), 20 - b[i]))),
month =factor( rep(month.abb, each = 40), levels = month.abb),
fills = rep(c(1:39, "top"), 12)) %>%
ggplot(aes(x = month, y = vals, fill = fills)) +
geom_col(fill = "gray95", aes(y = Inf), width = 0.7) +
geom_col(position = position_stack(), width = 0.5) +
scale_fill_manual(values = c("#00000000",
colorRampPalette(colors = c("forestgreen",
"gold", "orange"))(38),
"#00000000"),
guide = guide_none()) +
theme_classic()
I have this two data.frames
df1 <-
data.frame(unit = factor(1:20, levels = 20:1),
value = sample(1:10, 20, replace = T))
df2 <-
data.frame(unit =
factor(as.vector(sapply(1:20, FUN = function(x) rep(x, 10))).
levels = 1:20),
time = rep(1:10, 20),
value = sample(1:100, 10*20, replace = T))
Which I want to plot side by side like this:
library(ggplot2)
library(cowplot)
plot_grid(ggplot(df1, aes(x=value,y=unit)) +
geom_bar(stat = 'identity') +
scale_x_continuous(position = "top"),
ggplot(df2, aes(x=time,y=value)) +
geom_line() +
facet_grid(rows = vars(unit), scales = "free_y") +
scale_x_continuous(position = "top") +
theme(axis.text.y = element_text(size=6)),
ncol = 2)
which results in this output
Still, the rows from the two plots, mapping variables from the same unit are not perfectly aligned:
What's the easiest way to align them programmatically (so that it will also work with a different number of units)? The solution doesn't need to involve the cowplot package.
A simple solution to achieve this is by using facets for the bar plot, too. As long as the spacing between the panels is the same in both plots this should ensure that the bars and the line plots for each group are aligned. Try this:
df1 <-
data.frame(unit = factor(1:20, levels = 20:1),
value = sample(1:10, 20, replace = T))
df2 <-
data.frame(unit = factor(as.vector(sapply(1:20, FUN = function(x) rep(x, 10))), levels = 1:20),
time = rep(1:10, 20),
value = sample(1:100, 10*20, replace = T))
library(ggplot2)
library(cowplot)
plot_grid(ggplot(df1, aes(x=value,y=unit)) +
geom_bar(stat = 'identity') +
facet_grid(rows = vars(unit), scales = "free_y") +
scale_x_continuous(position = "top") +
theme(panel.spacing.y = unit(1, "pt"), strip.text = element_blank()),
ggplot(df2, aes(x=time,y=value)) +
geom_line() +
facet_grid(rows = vars(unit), scales = "free_y") +
scale_x_continuous(position = "top") +
theme(axis.text.y = element_text(size=6), panel.spacing.y = unit(1, "pt")),
ncol = 2)
If we look at the way the plots are aligned, it seems clear that to have the bars matching the corresponding facets, we have to get rid of the space at either end of the bars' y axis. We can do this with scale_y_discrete(expand = c(0, 0)). We can also scale the width of the bars so that it is equal to the proportion that each of the facet panels takes up in their allotted viewports. Unfortunately this is somewhat dependent on device dimensions. However, a width of 0.8 or 0.9 will get you pretty close.
plot_grid(ggplot(df1, aes(x=value,y=unit)) +
geom_bar(stat = 'identity', width = 0.8) +
scale_x_continuous(position = "top") +
scale_y_discrete(expand = c(0, 0)),
ggplot(df2, aes(x=time,y=value)) +
geom_line() +
facet_grid(rows = vars(unit), scales = "free_y") +
scale_x_continuous(position = "top") +
theme(axis.text.y = element_text(size=6)),
ncol = 2)
below a reproducible example of my grouped bar plot. I am struggling with the space / padding between the groups. I already found some question on stackoverflow like this and here.
I also played around with the geom_bar(position= .. argument to be either "dodge" or dodge_position(0.5) and also with the width of my bars. However I dont want to increase the width of my bars anyfurther. Is there any solution like changing the size of the discrete x axis to reduce the labeled spaces in figure 1? Update: Reduced the code to make it more minimal for the minimal reproducible example. The Problem remains the same
library(ggplot2)
library(ggthemes)
library(dplyr)
algorithm <- c(rep("0_DT",2),rep("1_RF",2),rep("2_MLP",2))
target <- rep(c("Some Data","Some Other Data"),3)
value <- runif(6,85,95) # Simulated Accuracies
CI_lower <- value - 5
CI_upper <- value + 5
data <- data.frame(target,algorithm,value,CI_lower,CI_upper)
ggplot(data, aes(fill=algorithm, y=value, x=target)) +
geom_bar(position=position_dodge(0.75), stat="identity", width = 0.65)+ theme_classic()+
scale_fill_manual("Algorithm",
values = alpha(c("0_DT" = "#20639B", "1_RF" = "#3CAEA3", "2_MLP" = "#F6D55C"),0.8),
labels=c("DT","RF","MLP"))+
scale_y_continuous("Accuracy in %",limits = c(0,100),oob = rescale_none,
# breaks= sort(c(seq(0,90,10),h)),
breaks= seq(0,100,10),
expand = c(0,0))
And here is my Barchart Figure 1
I may be misunderstanding you, but you should be able to get the result you want by tweaking the position argument:
ggplot(data, aes(fill = algorithm, y = value, x = target)) +
geom_bar(position = position_dodge(1), stat = "identity", width = 0.65) +
theme_classic() +
scale_fill_manual("Algorithm",
values = alpha(c("0_DT" = "#20639B",
"1_RF" = "#3CAEA3",
"2_MLP" = "#F6D55C"), 0.8),
labels = c("DT", "RF", "MLP")) +
scale_y_continuous("Accuracy in %",limits = c(0, 100), oob = rescale_none,
breaks= seq(0, 100, 10),
expand = c(0, 0))
If you want to keep the apparent width of the bars but also reduce the spaces between bars, then you will have to increase the width but change the shape of the over all plot by changing the window dimensions or the plot margins:
ggplot(data, aes(fill = algorithm, y = value, x = target)) +
geom_bar(position = position_dodge(1), stat = "identity", width = 0.8) +
theme_classic() +
scale_fill_manual("Algorithm",
values = alpha(c("0_DT" = "#20639B",
"1_RF" = "#3CAEA3",
"2_MLP" = "#F6D55C"), 0.8),
labels = c("DT", "RF", "MLP")) +
scale_y_continuous("Accuracy in %",limits = c(0, 100), oob = rescale_none,
breaks= seq(0, 100, 10),
expand = c(0, 0)) +
theme(plot.margin = unit(c(10, 50, 10, 50), "points"))
This may not be exactly what you want, but some combination of position_dodge, width and plot.margin will get you the spacing you are looking for.
EDIT
Following the OP's comments, it looks like the final solution should looks something like this, which is done with width = 0.9, position_dodge(0.9) and plot.margin = unit(c(10, 50, 10, 50), "points")):
Using R, I am trying to make a line graph which is revealed left to right based on x-axis using gganimate. I have managed to do this but what I also wanted to do was make it so that the scale_x_continuous(limits = c(i-5,i+5)), i.e. there is a window around the point that is being revealed and the window will move along while the next point is being revealed.
I have tried many ways to get this including implementing some sort of loop in scale_x_continuous with and without aes(). Nothing seems to work. I am quite new with ggplot2 and especially with gganimate but I couldn't find any help online. I have a feeling the answer is probably quite simple and I just missed it.
Sort of like this but with gganimate:
The following is some reproducible code to show you roughly what I've done so far.
library(ggplot2)
library(gganimate)
library(gifski)
library(png)
Step <- c(1:50,1:50)
Name <- c(rep("A",50), rep("B",50))
Value <- c(runif(50,0,10), runif(50,10,20))
Final <- data.frame(Step, Name, Value)
a <- ggplot(Final, aes(x = Step, y = Value, group = Name, color = factor(Name))) +
geom_line(size=1) +
geom_point(size = 2) +
transition_reveal(Step) +
coord_cartesian(clip = 'off') +
theme_minimal() +
theme(plot.margin = margin(5.5, 40, 5.5, 5.5)) +
theme(legend.position = "none")
options(gganimate.dev_args = list(width = 7, height = 6, units = 'in', res=100))
animate(a, nframes = 100)
Don't use a transition, use a view. E.g.:
ggplot(Final, aes(x = Step, y = Value, color = factor(Name))) +
geom_line(size = 1) +
geom_point() +
view_zoom_manual(
0, 1, pause_first = FALSE, ease = 'linear', wrap = FALSE,
xmin = 1:40, xmax = 11:50, ymin = min(Final$Value), ymax = max(Final$Value)
) +
scale_x_continuous(breaks = seq(0, 50, 2))