Is there a way to bring the labels forward with respect to plot panel in ggplot?
Actually I was trying to answer my question here. I have not got any satisfactory response to that one although I thought it would be possible in ggplot. This is an attempt to get a solution although a hacky one. But the labels are rendered below the plot panel here.
Following are my (example) data, attempted solution and the resulting plot.
library(ggplot2)
library(magrittr)
mydata = data.frame(expand.grid(Tag = c('A','B','C'),Year = 2010:2011,PNo = paste0("X-",1:4)),Value = round(runif(24,1,20)))
mydata$dist = ifelse(mydata$Tag == 'A',0,ifelse(mydata$Tag=='B',2,7))
mydata %>% ggplot(aes(x = dist,y = Value,fill = factor(Year))) +geom_bar(stat='summary',position = 'dodge',fun.y='mean',width = 1) +
facet_wrap(~PNo,ncol=2) +
theme(axis.text.x = element_blank(),axis.ticks.x = element_blank()) +
geom_label(data = mydata %>% filter(PNo %in% c('X-3','X-4')),aes(x = dist,y=0,label = Tag),size=6,inherit.aes=F,color = 'red')
You have to turn off clipping of the bottom panel elements:
p <- mydata %>% ggplot(aes(x = dist,y = Value,fill = factor(Year))) +geom_bar(stat='summary',position = 'dodge',fun.y='mean',width = 1) +
facet_wrap(~PNo,ncol=2) +
theme(axis.text.x = element_blank(),axis.ticks.x = element_blank()) +
geom_label(data = mydata %>% dplyr::filter(PNo %in% c('X-3','X-4')),aes(x = dist,y=0,label = Tag),size=6,inherit.aes=F,color = 'red')
library(grid)
gt <- ggplot_gtable(ggplot_build(p))
gt$layout$clip[grep("panel-2-\\d+", gt$layout$name)] <- "off"
grid.draw(gt)
See Point clipped on x-axis in ggplot
Related
Here I have interactive barplot given by ggplotly. The only issue is that when I move mouse around bars, in the "model" category there is strange number instead of A or B (see the picture). Is it possible to customize plotly popup windows?
df <- data.frame (model = c("A", "A","B","B"),
year = c("2022","2021","2022","2021"),
sale = c(350,170,300,150),
change = c(180,NA,150,NA),
percent = c(105.8,NA,100,NA),
info = c("180, 105.8%",NA,"300,100%",NA)
)
#ggplot
plot <- ggplot(df, aes(fill=year, y=model, x=sale)) +
geom_bar(position="dodge", stat="identity") + geom_text(aes(label=info, x=1.11*max(sale),), fontface='bold')+ xlim(0, 1.2*max(df$sale)) +
theme(legend.position="bottom")+labs(fill = " ")+
scale_fill_brewer(palette = "Paired")
ggplotly(plot)
Personally, i avoid using ggplotly() as it more often than not formats the visuals in a way that i do not want.
A full plotly approach could look like this:
plot_ly(
data = df,
x = ~sale,
y = ~model,
color = ~year,
text = ~year,
type = "bar") %>%
add_trace(
x = ~max(df$sale) * 1.1,
y = ~model,
type = 'scatter',
mode = 'text',
text = ~info,
showlegend = FALSE
) %>%
style(hovertemplate = paste("Sale: %{x}",
"Model: %{y}",
"Year: %{text}",
sep = "<br>"))
You could also try to append the style() object to your ggplotly() object. I am not sure if this will work however.
For some reason, it works better if you use x=model and flip the axes:
plot <- ggplot(df, aes(fill=year, x=model, y=sale)) +
geom_bar(position="dodge", stat="identity") + geom_text(aes(label=info,y=1.11*max(sale),), fontface='bold')+
ylim(0, 1.2*max(df$sale)) +
theme(legend.position="bottom")+labs(fill = " ")+
scale_fill_brewer(palette = "Paired")+
coord_flip()
ggplotly(plot)
I am trying to add a number label on each cell of a heatmap. Because it also needs marginal barcharts I have tried two packages. iheatmapr and ComplexHeatmap.
(1st try) iheatmapr makes it easy to add to add bars as below, but I couldnt see how to add labels inside the heatmap on individual cells.
library(tidyverse)
library(iheatmapr)
library(RColorBrewer)
in_out <- data.frame(
'Economic' = c(2,1,1,3,4),
'Education' = c(0,3,0,1,1),
'Health' = c(1,0,1,2,0),
'Social' = c(2,5,0,3,1) )
rownames(in_out) <- c('Habitat', 'Resource', 'Combined', 'Protected', 'Livelihood')
GreenLong <- colorRampPalette(brewer.pal(9, 'Greens'))(12)
lowGreens <- GreenLong[0:5]
in_out_matrix <- as.matrix(in_out)
main_heatmap(in_out_matrix, colors = lowGreens)
in_out_plot <- iheatmap(in_out_matrix,
colors=lowGreens) %>%
add_col_labels() %>%
add_row_labels() %>%
add_col_barplot(y = colSums(bcio)/total) %>%
add_row_barplot(x = rowSums(bcio)/total)
in_out_plot
Then used: save_iheatmap(in_out_plot, "iheatmapr_test.png")
Because I couldnt use ggsave(device = ragg::agg_png etc) with iheatmapr object.
Also, the iheatmapr object's apparent incompatibility (maybe I am wrong) with ggsave() is a problem for me because I normally use ragg package to export image AGG to preserve font sizes. I am suspecting some other heatmap packages make custom objects that maybe incompatible with patchwork and ggsave.
ggsave("png/iheatmapr_test.png", plot = in_out_plot,
device = ragg::agg_png, dpi = 72,
units="in", width=3.453, height=2.5,
scaling = 0.45)
(2nd try) ComplexHeatmap makes it easy to label individual number "cells" inside a heatmap, and also offers marginal bars among its "Annotations", and I have tried it, but its colour palette system (which uses integers to refer to a set of colours) doesnt suit my RGB vector colour gradient, and overall it is a sophisticated package clearly designed to make graphics more advanced than what I am doing.
I am aiming for style as shown in screenshot example below, which was made in Excel.
Please can anyone suggest a more suitable R package for a simple heatmap like this with marginal bars, and number labels inside?
Instead of relying on packages which offer out-of-the-box solutions one option to achieve your desired result would be to create your plot from scratch using ggplot2 and patchwork which gives you much more control to style your plot, to add labels and so on.
Note: The issue with iheatmapr is that it returns a plotly object, not a ggplot. That's why you can't use ggsave.
library(tidyverse)
library(patchwork)
in_out <- data.frame(
'Economic' = c(1,1,1,5,4),
'Education' = c(0,0,0,1,1),
'Health' = c(1,0,1,0,0),
'Social' = c(1,1,0,3,1) )
rownames(in_out) <- c('Habitat', 'Resource', 'Combined', 'Protected', 'Livelihood')
in_out_long <- in_out %>%
mutate(y = rownames(.)) %>%
pivot_longer(-y, names_to = "x")
# Summarise data for marginal plots
yin <- in_out_long %>%
group_by(y) %>%
summarise(value = sum(value)) %>%
mutate(value = value / sum(value))
xin <- in_out_long %>%
group_by(x) %>%
summarise(value = sum(value)) %>%
mutate(value = value / sum(value))
# Heatmap
ph <- ggplot(in_out_long, aes(x, y, fill = value)) +
geom_tile() +
geom_text(aes(label = value), size = 8 / .pt) +
scale_fill_gradient(low = "#F7FCF5", high = "#00441B") +
theme(legend.position = "bottom") +
labs(x = NULL, y = NULL, fill = NULL)
# Marginal plots
py <- ggplot(yin, aes(value, y)) +
geom_col(width = .75) +
geom_text(aes(label = scales::percent(value)), hjust = -.1, size = 8 / .pt) +
scale_x_continuous(expand = expansion(mult = c(.0, .25))) +
theme_void()
px <- ggplot(xin, aes(x, value)) +
geom_col(width = .75) +
geom_text(aes(label = scales::percent(value)), vjust = -.5, size = 8 / .pt) +
scale_y_continuous(expand = expansion(mult = c(.0, .25))) +
theme_void()
# Glue plots together
px + plot_spacer() + ph + py + plot_layout(ncol = 2, widths = c(2, 1), heights = c(1, 2))
I have the following dataset:
HIU,0.0833333333,0,0.35,0.0208333333,0.40625,0,0.21875,0.125,0.078125,0.0104166667,1,0.53125,0.4375
TTHY,0,0,0.8,0,0.5,0,0.7083333333,0.2708333333,0,0.6597222222,0,0.1435185185,0
Full,0.0554986339,0.1034836066,0.4620901639,0.0683060109,0.4961577869,0.0696721311,0.222079918,0.1465163934,0.2085040984,0.0476007514,0.893613388,0.396943306,0.4223872951
I made a grouped bar plot according to the rows of HIU and TTHY (figure 1). But I want to add a line according to the "Full" row, such as the second image.
Figure 1:
Figure 2:
How can I do it with R? This is my current code:
df = read.csv('TTR-HIU/resultados.csv',header=FALSE,colClasses=c("NULL",NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA))
df.bar <- barplot(as.matrix(df[-nrow(df),]),beside=TRUE,col=c("darkblue","red"))
Using ggplot2, you could try something like this:
# put data in data frame:
df <- data.frame(HIU = c(0.0833333333,0,0.35,0.0208333333,0.40625,0,0.21875,0.125,0.078125,0.0104166667,1,0.53125,0.4375),
TTHY = c(0,0,0.8,0,0.5,0,0.7083333333,0.2708333333,0,0.6597222222,0,0.1435185185,0),
Full= c(0.0554986339,0.1034836066,0.4620901639,0.0683060109,0.4961577869,0.0696721311,0.222079918,0.1465163934,0.2085040984,0.0476007514,0.893613388,0.396943306,0.4223872951))
library(ggplot2)
library(tidyr) # to make data long (gather)
# create x-values:
df$x <- as.factor(seq_len(nrow(df)))
# make data long for ggplot2:
df_long <- df %>% gather(key, value, -x)
ggplot() +
# plot bars:
geom_col(data = subset(df_long, key %in% c("HIU", "TTHY")),
mapping = aes(x = x, y = value, fill = key),
position = position_dodge()) +
# plot lines:
geom_line(data = subset(df_long, key == "Full"),
mapping = aes(x = x, y = value, group = key, color = key),
size = 2) +
# make plot look a little like your desired output:
scale_color_manual(values = c("Full" = "yellow")) +
scale_fill_manual(values = c("HIU" = "blue", "TTHY" = "red")) +
theme_minimal() +
theme(axis.title = element_blank(),
legend.title = element_blank(),
panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank())
However, you might have to put your data in data-frame-shape as in this example. Use dput to show how your data exactly looks like, if you need further help...
Lets draw a bar chart with ggplot2 from the following data (already in a long format). The values of the variable are then placed in the middle of the bars via geom_text() directive.
stuff.dat<-read.csv(text="continent,stuff,num
America,apples,13
America,bananas,13
Europe,apples,30
Europe,bananas,21
total,apples,43
total,bananas,34")
library(ggplot2)
ggplot(stuff.dat, aes(x=continent, y=num,fill=stuff))+geom_col() +
geom_text(position = position_stack(vjust=0.5),
aes(label=num))
Now it is necessary to add on top of the bars the "Apple-Bananas Index", which is defined as f=apples/bananas - just as manually added in the figure. How to program this in ggplot? How it would be possible to add it to the legend as a separate entry?
I think that the easiest way to achieve this is to prepare the data before you create the plot. I define a function abi() that computes the apple-banana-index from stuff.dat given a continent:
abi <- function(cont) {
with(stuff.dat,
num[continent == cont & stuff == "apples"] / num[continent == cont & stuff == "bananas"]
)
}
And then I create a data frame with all the necessary data:
conts <- levels(stuff.dat$continent)
abi_df <- data.frame(continent = conts,
yf = aggregate(num ~ continent, sum, data = stuff.dat)$num + 5,
abi = round(sapply(conts, abi), 1))
Now, I can add that information to the plot:
library(ggplot2)
ggplot(stuff.dat, aes(x = continent, y = num, fill = stuff)) +
geom_col() +
geom_text(position = position_stack(vjust = 0.5), aes(label = num)) +
geom_text(data = abi_df, aes(y = yf, label = paste0("f = ", abi), fill = NA))
Adding fill = NA to the geom_text() is a bit of a hack and leads to a warning. But if fill is not set, plotting will fail with a message that stuff was not found. I also tried to move fill = stuff from ggplot() to geom_col() but this breaks the y⁻coordinate of the text labels inside the bars. There might be a cleaner solution to this, but I haven't found it yet.
Adding the additional legend is, unfortunately, not trivial, because one cannot easily add text outside the plot area. This actually needs two steps: first one adds text using annotation_custom(). Then, you need to turn clipping off to make the text visible (see, e.g., here). This is a possible solution:
p <- ggplot(stuff.dat, aes(x = continent, y = num, fill = stuff)) +
geom_col() +
geom_text(position = position_stack(vjust = 0.5), aes(label = num)) +
geom_text(data = abi_df, aes(y = yf, label = paste0("f = ", abi), fill = NA)) +
guides(size = guide_legend(title = "f: ABI", override.aes = list(fill = 1))) +
annotation_custom(grob = textGrob("f: ABI\n(Apple-\nBanana-\nIndex",
gp = gpar(cex = .8), just = "left"),
xmin = 3.8, xmax = 3.8, ymin = 17, ymax = 17)
# turn off clipping
library(grid)
gt <- ggplot_gtable(ggplot_build(p))
gt$layout$clip[gt$layout$name == "panel"] <- "off"
grid.draw(gt)
How do I stop the Y-axis changing during an animation?
The graph I made is at http://i.imgur.com/EKx6Tw8.gif
The idea is to make an animated heatmap of population and income each year. The problem is the y axis jumps to include 0 or not include the highest value sometime. How do you solidly set the axis values? I know this must be a common issue but I can't find the answer
The code to recreate it is
library(gapminder)
library(ggplot2)
library(devtools)
install_github("dgrtwo/gganimate")
library(gganimate)
library(dplyr)
mydata <- dplyr::select(gapminder, country,continent,year,lifeExp,pop,gdpPercap)
#bin years into 5 year bins
mydata$lifeExp2 <- as.integer(round((mydata$lifeExp-2)/5)*5)
mydata$income <- cut(mydata$gdpPercap, breaks=c(0,250,500,750,1000,1500,2000,2500,3000,3500,4500,5500,6500,7500,9000,11000,21000,31000,41000, 191000),
labels=c(0,250,500,750,1000,1500,2000,2500,3000,3500,4500,5500,6500,7500,9000,11000,21000,31000,41000))
sizePer <- mydata%>%
group_by(lifeExp2, income, year)%>%
mutate(popLikeThis = sum(pop))%>%
group_by(year)%>%
mutate(totalPop = sum(as.numeric(pop)))%>%
mutate(per = (popLikeThis/totalPop)*100)
sizePer$percent <- cut(sizePer$per, breaks=c(0,.1,.3,1,2,3,5,10,20,Inf),
labels=c(0,.1,.3,1,2.0,3,5,10,20))
saveGIF({
for(i in c(1997,2002,2007)){
print(ggplot(sizePer %>% filter(year == i),
aes(x = lifeExp2, y = income)) +
geom_tile(aes(fill = percent)) +
theme_bw()+
theme(legend.position="top", plot.title = element_text(size=30, face="bold",hjust = 0.5))+
coord_cartesian(xlim = c(20,85), ylim = c(0,21)) +
scale_fill_manual("%",values = c("#ffffcc","#ffeda0","#fed976","#feb24c","#fd8d3c","#fc4e2a","#e31a1c","#bd0026","#800026"),drop=FALSE)+
annotate(x=80, y=3, geom="text", label=i, size = 6) +
annotate(x=80, y=1, geom="text", label="#iamreddave", size = 5) +
ylab("Income") + # Remove x-axis label
xlab("Life Expenctancy")+
ggtitle("Worldwide Life Expectancy and Income")
)}
}, interval=0.7,ani.width = 900, ani.height = 600)
Solution:
Adding scale_y_discrete(drop = F) to the ggplot call. Answered by #bdemarest in comments.