I am having trouble printing a plot from ggplot to plotly, and maintaining a good text position.
Data example:
library(ggplot2)
library(plotly)
library(dplyr)
library(reshape2)
#mock data
df1 <- data.frame(
Gruppering2 = factor(c("Erhverv Erhverv Salg","Erhverv Erhverv Salg","Erhverv Erhverv Salg")),
periode = factor(c("Denne maaned","Denne uge", "I gaard")),
Answer_rate = c(0.01,0.4,0.7),
SVL = c(0.40,0.43,0.67),
over_180 = c(0.5,0.7,0.3)
)
#color
plotCol <- c( rgb(44,121,91, maxColorValue = 255), rgb(139,0,0, maxColorValue = 255),rgb(0,0,139, maxColorValue = 255))
#plot code
dfpct <- melt(df1[,c(2,3,4,5)], id.vars = "periode",
measure.vars = c( "Answer_rate","SVL", "over_180"),
variable.name = "P", value.name = "value")
dfpct <- na.omit(dfpct)
pct <- ggplot(dfpct, aes(x = periode, y = value, fill = P, group = P, width = 0.6)) +
geom_bar(stat = "identity", position="dodge", colour = "black", width = 0.7, show.legend = FALSE) +
labs(x = NULL, y = "Calls") +
#ggtitle("Forecast Error") +
theme_bw() +
theme(panel.grid.major = element_blank(),
plot.title = element_text(size = rel(1.2), face = "bold", vjust = 1.5),
axis.title = element_text(face = "bold"),
axis.text = element_text(),
legend.position = "bottom",
legend.direction = "vertical",
legend.key.width = unit(2, "lines"),
legend.key.height = unit(0.5, "lines"),
legend.title = element_blank()) +
geom_text(aes(label=paste(value*100,"%",sep="")), position = position_dodge(width=0.6), vjust = -0.5 ) +
scale_fill_manual(values = plotCol)
pct # the is perfectly located above
ggplotly(pct, textposition = 'top center') # text crosses over the bars
As you can see - the ggplot works excellent - however when I convert to plotly, the text is moved. I've tried playing around with various settings in both ggplot and plotly, but no luck yet.
Looks like vjust is not recognized but maybe on the roadmap. From GitHub:
# convert ggplot2::element_text() to plotly annotation
make_label <- function(txt = "", x, y, el = ggplot2::element_text(), ...) {
if (is_blank(el) || is.null(txt) || nchar(txt) == 0 || length(txt) == 0) {
return(NULL)
}
angle <- el$angle %||% 0
list(list(
text = txt,
x = x,
y = y,
showarrow = FALSE,
# TODO: hjust/vjust?
ax = 0,
ay = 0,
font = text2font(el),
xref = "paper",
yref = "paper",
textangle = -angle,
...
))
}
Easiest approach might be to assign the y value in geom_text, but you'll lose some scaling in the height.
pct <- ggplot(dfpct, aes(x = periode, y = value, fill = P, group = P, width = 0.6)) +
geom_bar(stat = "identity", position="dodge", colour = "black", width = 0.7, show.legend = FALSE) +
labs(x = NULL, y = "Calls") +
theme_bw() +
theme(panel.grid.major = element_blank(),
plot.title = element_text(size = rel(1.2), face = "bold", vjust = 1.5),
axis.title = element_text(face = "bold"),
axis.text = element_text(),
legend.position = "bottom",
legend.direction = "vertical",
legend.key.width = unit(2, "lines"),
legend.key.height = unit(0.5, "lines"),
legend.title = element_blank()) +
geom_text(aes(label=paste(value*100,"%",sep=""), y = value+0.01), position = position_dodge(width = 0.6)) +
scale_fill_manual(values = plotCol)
ggplotly(pct)
Alternatively, if you know the dimensions of the final output, you could edit the components of a plotly_build object:
gg <- plotly_build(pct)
gg$data[[4]]$y <- gg$data[[4]]$y+0.006
gg$data[[5]]$y <- gg$data[[5]]$y+0.006
gg$data[[6]]$y <- gg$data[[6]]$y+0.006
I had the same issue, I solved it by adding a value to the y axis, but in order to avoid the scaling issue, I added a percentage of the minimum. You can adjust it depending upon your data itself. I hope it helps:
geom_text(aes(label=paste(value*100,"%",sep=""), y = value+0.1*min(value), position = position_dodge(width = 0.6))
Related
I have a data with over 700 observations but below is a sample. Using geom_curve I want to make a plot where the line size(total_trips) corresponds to a color say 3 different colors. For instance between 0-100 (total_trips) can have a color of red
df <- data.frame(
origin_x = c(659627.8,642136.2,648774.7,659627.8,659627.8,658455.7,659627.8,659620.6,661641.8,656246.4),
origin_y = c(6473200,6473200,6462166,6473200,6473200,6467413,6473200,6467163,6479577,6487039),
dest_x = c(642136.2,659627.8,659627.8,648774.7,659620.6,659627.8,658455.7,659627.8,659627.8,659627.8),
dest_y = c(6456563,6473200,6473200,6462166,6467163,6473200,6467413,6473200,6473200,6473200
),
total_trips = c(4002,49878,2011,500,100,3000,2500,654,900,600))
I tried
ggplot() + geom_sf(data=shapefile, colour='grey', fill='grey93', size = 0.25) +
geom_curve(
data = df),
aes(
x = origin_x,
xend = dest_x,
y = origin_y,
yend = dest_y,
size = n,
colour= as.factor(c('red','blue'))),
curvature = 0.3
) + scale_alpha_continuous(range = c(0.09,1)) +
theme(
axis.title = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
plot.title = element_text(hjust = 0.5, size = 6),
plot.caption = element_text(hjust = 1),
plot.caption.position = 'plot',
axis.ticks = element_blank(),
panel.background = element_rect(fill = 'white'),
panel.grid = element_blank(),
plot.background = element_rect(color = NA, size = 0.5, fill=NA),
panel.border = element_rect(color = 'black', fill = NA, size=0.2) ,
legend.position = c(0.89,0.15),
legend.key.size = unit(0.4, 'cm'),
legend.text = element_text(size=7)
) +
annotation_scale(location = 'br', style = 'ticks') + coord_sf(crs=3301) +
annotation_north_arrow(location = 'tr', width = unit(0.20, 'cm'),height = unit(0.5,'cm'))
If I understand correctly - you want to change the colour of the line according to a categorised continuous variable (total_trips), we can do this:
Use cut to categorise the variable and give labels to the groups
Add this new variable to the aes(colour =.
library(dplyr)
library(ggplot2)
df <- df |> mutate(trips = cut(total_trips, c(0, 2000, 5000, 50000),
labels = c("0-2k", "2k-5k", "5k-50k")))
ggplot() +
geom_curve(data = df, aes(x = origin_x,
xend = dest_x,
y = origin_y,
yend = dest_y,
size = total_trips,
colour = trips
))
Output:
Not sure if this is what you want, though – your sample dataset doesn't contain the variable n that you mention in size = n, and you haven't provided us with shapefile.
I use the code below to plot a multiple lines chart and having hover informations with ggplotly :
data <- data %>%
mutate(text = paste("Epoch : ", epoch, "\nTrain Loss : ", loss, "\nTest Loss : ", test_loss))
g <- ggplot(
data,
aes(x = epoch + 1, text = text, group = 1)
) +
geom_line(
aes(y = loss, color = 'train set loss'),
size = 0.8
) +
geom_line(
aes(y = test_loss, color = 'test set loss'),
size = 0.8
) +
labs(x = 'epoch', y = 'loss') +
scale_color_manual(values = c('train set loss' = 'blue', 'test set loss' = 'red')) +
theme_bw() +
theme(
legend.title = element_blank(),
legend.text = element_text(size = 15),
axis.text.x = element_text(face = "bold", size = 12),
axis.text.y = element_text(face = "bold", size = 12),
axis.title.x = element_text(face = "bold", size = 15),
axis.title.y = element_text(face = "bold", size = 15)
)
g <- ggplotly(g, tooltip = 'text') %>%
config(displayModeBar = F)
g <- layout(g, legend = list(x = 0.75, y = 0.99), hovermode = 'x unified')
g
The corresponding result is the image below :
The problem is that I woul like to have only one hover text at the top or the bottom and not two (because as you can see, they are the same right now). I thought that with hovermode = 'x unified' would do the job, but it seems that it is not.
Thanks in advance guys.
Edit
The code below, with adding 2 columns text1 and text2 instead of just text and adding the text parameter into the aesthtitics of the two geom_line works and does the image below :
data <- data %>%
mutate(text1 = paste("Epoch : ", epoch + 1, "\nLoss : ", loss)) %>%
mutate(text2 = paste("Epoch : ", epoch + 1, "\nLoss : ", test_loss))
g <- ggplot(
data,
aes(x = epoch + 1)
) +
geom_line(
aes(y = loss, color = 'train set loss', text = text1, group = 1),
size = 0.8
) +
geom_line(
aes(y = test_loss, color = 'test set loss', text = text2, group = 1),
size = 0.8
) +
labs(x = 'epoch', y = 'loss') +
scale_color_manual(values = c('train set loss' = 'blue', 'test set loss' = 'red')) +
theme_bw() +
theme(
legend.title = element_blank(),
legend.text = element_text(size = 15),
axis.text.x = element_text(face = "bold", size = 12),
axis.text.y = element_text(face = "bold", size = 12),
axis.title.x = element_text(face = "bold", size = 15),
axis.title.y = element_text(face = "bold", size = 15)
)
g <- ggplotly(g, tooltip = 'text') %>%
config(displayModeBar = F)
g <- layout(g, legend = list(x = 0.75, y = 0.99), hovermode = 'x unified')
g
However, this solution brings the following message : Warning: Ignoring unknown aesthetics: text
Perhaps you should try tooltip = c("x","y"). See example below
# create some data
x <- c(1:10); y <- x*x ; yy <- x+x
df1 <- data.frame(x,y,yy)
p1 <- ggplot(df1,aes(x=x)) +
geom_line(aes(y=y, color = 'train set loss')) +
geom_line(aes(y=yy, color = 'train set loss2'))
p2 <- ggplotly(p1, tooltip = c("x","y")) %>%
config(displayModeBar = F)
p2 <- layout(p2, legend = list(x = 0.75, y = 0.99), hovermode = 'x unified')
p2
I'm making a heatmap in R using ggplot2 and I want to change the size of my tiles because the text in my heatmap doesn't fit in the cells. Below is my data and attempted plots.
library("ggplot2")
s = sprintf("dataset number %s", 1:9)
vars = sprintf("many many words here, here and here %s", 1:6)
data = data.frame(s = rep(s, 6), stringsAsFactors = FALSE)
data$variable = rep(vars, rep.int(9, 6))
data$variable = as.factor(data$variable)
data$value = rep.int(-1000, 54)
data$font = "plain"
for(v in (unique(data$s))) {
ids = which(data$s %in% "dataset number 1")
vals = data[ids, ]
row = rownames(vals[vals$value %in% max(vals$value, na.rm = TRUE), ])
data[row, ]$font = "bold"
}
I'm making a heatmap like this
title = "Heatmap"
cbbPalette = list(grey = "#999999", black = "#000000", orange = "#E69F00", sky = "#56B4E9", green = "#009E73", yellow = "#F0E442", blue = "#0072B2", darko = "#D55E00",
pink = "#CC79A7")
pdf(sprintf("./heatmap.pdf"))
heatmap = ggplot(data = data, aes(x = variable, y = s, fill = value)) +
geom_tile(color = "black") +
scale_fill_gradient2(low = cbbPalette$pink, high = cbbPalette$green, mid = cbbPalette$grey,
midpoint = 0, limit = c(-100,100), space = "Lab",
name=title) +
scale_x_discrete(limits = levels(data$variable)) +
geom_vline(xintercept = 6 - 0.5, color = "white", size = 1) +
geom_vline(xintercept = 2 + 0.5, color = "white", size = 1) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, vjust = 1,
size = 16, hjust = 1), legend.title = element_text(size = 18),
axis.text.y = element_text(size = 16)) +
coord_fixed()
#add numbers to cells
heatmap = heatmap + geom_text(aes(x = variable, y = s, label = value, fontface = font), color = cbbPalette$black, size = 3) +
theme(
axis.title.x = element_blank(),
axis.title.y = element_blank(),
panel.grid.major = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
axis.ticks = element_blank(),
legend.justification = c(0.5, 0),
legend.direction = "horizontal",
legend.position = "top") +
guides(fill = guide_colorbar(barwidth = 7, barheight = 1,
title.position = "top", title.hjust = 0.5))
# Print the heatmap
print(heatmap)
dev.off()
And I get the following result -- heatmap
Other SO posts suggested to change width and height within geom_tile, but I get the following result:
heatmap = ggplot(data = data, aes(x = variable, y = s, fill = value)) +
geom_tile(color = "black", aes(width = 2L)) + ...
wrong_heatmap
I also tried changing height in geom_tile as well as coord_fixed(ratio = 1L) but without any success.
Also, I know that I can simply change the size of the text and numbers will fit, but I need to keep the font size the same. I can also shorten the labels but assume this is not an option.
I would appreciate any help with this.
I want to give each facet an alpha code, from A to H since there are eight facets, and draw each code on the top-left of each facet:
ggthemr('dust', layout = 'scientific',
spacing = 1, type = 'inner', line_weight = 0.6,
)
ptitles <- c('A' = "Total mass (g)", 'B' = "Root mass (g)", 'C' = "Stem mass (g)",
'D' = "Leaf mass (g)", 'E' = "Number of nodes",
'F' = "Number of leaves", 'G' = "Total stem length (cm)", 'H' = "RDI")
ggplot(gtr, aes(sediment, value)) +
geom_boxplot(aes(fill = nitrogen)) +
geom_text(aes(label = trait, group = trait)) +
facet_wrap(~trait, scales = "free_y", ncol = 2,
labeller = as_labeller(ptitles),
strip.position = "left"
) +
theme(legend.position = "bottom",
legend.title = element_text(size = 12),
legend.key.size = unit(2, "lines"),
legend.text = element_text(size = 12),
strip.text.x = element_text(size = 12, margin = margin(0, 0, 0, 10)),
strip.text.y = element_text(size = 14),
strip.placement = "outside",
axis.title.y = element_text(size = 14),
axis.title.x = element_text(size = 14),
axis.text.x = element_text(size = 14),
panel.spacing.x = unit(0.5, "lines"),
panel.spacing.y = unit(0.3, "lines"),
aspect.ratio = 2 / 3
) +
xlab("Effects of sediment type and nitrogen deposition") +
ylab(NULL)
I tried to use geom_text():
geom_text(aes(label = trait, group = trait))
(Here the variable trait stores factors from A to H to distinguish each facet)
But it did not work like what I expected:
Is there a simple way to such a thing?
UPDATE:
According to baptiste's answer, I changed my geom_text() code above to below:
geom_text(aes(x = -Inf, y = Inf, label = trait, group = trait),
size = 5,
hjust = -0.5,
vjust = 1.4,
inherit.aes = FALSE)
inherit.aes = FALSE here seems to do nothing, how does this parameter work?.
Now my plot looks good:
library(ggplot2)
d <- data.frame(x=rep(1:3, 4), f=rep(letters[1:4], each=3))
labels <- data.frame(f=letters[1:4], label=LETTERS[1:4])
ggplot(d, aes(x,x)) +
facet_wrap(~f) +
geom_point() +
geom_label(data = labels, aes(label=label),
x = Inf, y = -Inf, hjust=1, vjust=0,
inherit.aes = FALSE)
For a final article submission I have been asked to update my figures so that they meet the following specifications:
axis lines are 0.25 mm
axis lines all around with ticks facing in
data lines are 0.5 mm
font is 10pt
figures should be 80 or 169 mm wide
must be 300 dpi
What I've tried:
library(ggplot2)
library(cowplot)
theme_set(theme_bw())
x <- rnorm(100)
mydata <- data.frame(x = x,
y = x^2 + runif(100),
z = rep(letters[1:4], 25))
p <- ggplot(data = mydata, aes(x, y)) +
geom_point(aes(color = z)) +
geom_smooth(color = 'black', se = FALSE, size = 0.5) +
theme(text = element_text(family = 'Times', size = 10, color = 'black'),
axis.ticks.length = unit(-0.1, 'cm'),
axis.text.x = element_text(margin = margin(t = 4, unit = 'mm')),
axis.text.y = element_text(margin = margin(r = 4, unit = 'mm')),
panel.grid = element_blank(),
axis.line = element_line(size = 0.25),
legend.position = c(0.5, 0.75))
p
ggsave(plot = p,
filename = 'myplot.png',
width = 80, height = 50, dpi = 300, units = 'mm')
p2 <- cowplot::plot_grid(plotlist = list(p, p, p, p), nrow = 1)
ggsave(plot = p2,
filename = 'mymultipleplot.png',
width = 169, height = 50, dpi = 300, units = 'mm')
Which returns the following two plots:
I can figure out how to handle some of the issues here (e.g. legend positions), but am having difficulty with the following:
How can I get ticks around top and right axes?
How can I get the sizes correct ...
These look much bigger than 10 pt. (download them or open in new window to see unscaled version)
The sizes are not maintained in the two figures despite being specified in the theme (font, line).
I don't know how to confirm that the lines are the correct size (in points or mm)... does ggsave do its own scaling?
update For my present task I exported as svg files and edited them in Inkscape. It took a few hours but was easier than getting ggplot to contort to the specifications.
But, it would be helpful to know for the future how to do this programmatically within ggplot2.
Answer to question:
1) as Henrik told in comments:
For question 1 (How can I get ticks around top and right axes?), see the new sec.axis argument in scale_ in ggplot 2.2.0. Try e.g. ggplot(mpg, aes(displ, hwy)) + geom_point() + scale_x_continuous(sec.axis = dup_axis()) + scale_y_continuous(sec.axis = dup_axis())
2) the problem here is that you have the same resolution with different sizes. Since the height of the two figures is the same, you can fix this problem scaling the font size by hand multiplying the font-size with the ratio of the width: e.g.
theme(text = element_text(family = 'Times', size = 10*(80/169), color = 'black')
The whole code should look like this:
library(ggplot2)
library(cowplot)
theme_set(theme_bw())
x <- rnorm(100)
mydata <- data.frame(x = x,
y = x^2 + runif(100),
z = rep(letters[1:4], 25))
p1 <- ggplot(data = mydata, aes(x, y)) +
geom_point(aes(color = z)) + scale_x_continuous(sec.axis = dup_axis()) +
scale_y_continuous(sec.axis = dup_axis()) +
geom_smooth(color = 'black', se = FALSE, size = 0.5) +
theme(text = element_text(family = 'Times', size = 10*(80/169), color = 'black'),
axis.ticks.length = unit(-0.1, 'cm'),
axis.text.x = element_text(margin = margin(t = 4, unit = 'mm')),
axis.text.y = element_text(margin = margin(r = 4, unit = 'mm')),
panel.grid = element_blank(),
axis.line = element_line(size = 0.25),
legend.position = c(0.5, 0.75))
p2 <- ggplot(data = mydata, aes(x, y)) +
geom_point(aes(color = z)) + scale_x_continuous(sec.axis = dup_axis()) +
scale_y_continuous(sec.axis = dup_axis()) +
geom_smooth(color = 'black', se = FALSE, size = 0.5) +
theme(text = element_text(family = 'Times', size = 10, color = 'black'),
axis.ticks.length = unit(-0.1, 'cm'),
axis.text.x = element_text(margin = margin(t = 4, unit = 'mm')),
axis.text.y = element_text(margin = margin(r = 4, unit = 'mm')),
panel.grid = element_blank(),
axis.line = element_line(size = 0.25),
legend.position = c(0.5, 0.75))
p1
ggsave(plot = p1,
filename = 'myplot.png',
width = 80, height = 50, dpi = 300, units = 'mm')
p2multi <- cowplot::plot_grid(plotlist = list(p2, p2, p2, p2), nrow = 1)
ggsave(plot = p2multi ,
filename = 'mymultipleplot.png',
width = 169, height = 50, dpi = 300, units = 'mm')