How to position lines at the edges of stacked bar charts - r

Is it possible to change the position of lines, such that they start and end at the edges of stacked bar charts instead of in the center?
R code:
library(ggplot2)
plot11 = ggplot(CombinedThickness2[CombinedThickness2$DepSequence == "Original",], aes(x = Well, y = Thickness, fill = Sequence, alpha = Visible, width = 0.3)) +
geom_bar(stat = "identity") +
scale_y_reverse()
plot11 = plot11 + geom_line(aes(group = Sequence, y = Depth, color = Sequence))
plot11
Current image:
Data:
http://pastebin.com/D7uSKBmA

It seems that what is required is segments rather than lines; that is, use geom_segment() in place of geom_line(). geom_segment requires x and y coordinates for the start and end points of the segments. Getting the end y value is a bit unwieldy. But it works with your data frame assuming that there are 30 observations for each "Well", and that the order for "Sequence" is the same for each "Well".
library(ggplot2)
df = CombinedThickness2[CombinedThickness2$DepSequence == "Original",]
# Get the y end values
index = 1:dim(df)[1]
NWell = length(unique(df$Well))
df$DepthEnd[index] = df$Depth[index + dim(df)[1]/NWell]
BarWidth = 0.3
plot11 = ggplot(df,
aes(x = Well, y = Thickness, fill = Sequence, alpha = Visible)) +
geom_bar(stat = "identity", width = BarWidth) +
scale_y_reverse() + scale_alpha(guide = "none")
plot11 = plot11 +
geom_segment(aes(x = as.numeric(Well) + 0.5*BarWidth, xend = as.numeric(Well) + (1-0.5*BarWidth),
y = Depth, yend = DepthEnd, color = Sequence))
plot11

Related

how to set dual Y axis in geom_bar plot in ggplot2?

I'd like to draw bar plot like this but in dual Y axis
(https://i.stack.imgur.com/ldMx0.jpg)
the first three indexs range from 0 to 1,
so I want the left y-axis (corresponding to NSE, KGE, VE) to range from 0 to 1,
and the right y-axis (corresponding to PBIAS) to range from -15 to 5.
the following is my data and code:
library("ggplot2")
## data
data <- data.frame(
value=c(0.82,0.87,0.65,-3.39,0.75,0.82,0.63,1.14,0.85,0.87,0.67,-7.03),
sd=c(0.003,0.047,0.006,4.8,0.003,0.028,0.006,4.77,0.004,0.057,0.014,4.85),
index=c("NSE","KGE","VE","PBIAS","NSE","KGE","VE","PBIAS","NSE","KGE","VE","PBIAS"),
period=c("all","all","all","all","calibration","calibration","calibration","calibration","validation","validation","validation","validation")
)
## fix index sequence
data$index <- factor(data$index, levels = c('NSE','KGE','VE',"PBIAS"))
data$period <- factor(data$period, levels = c('all','calibration', 'validation'))
## bar plot
ggplot(data, aes(x=index, y=value, fill=period))+
geom_bar(position="dodge", stat="identity")+
geom_errorbar(aes(ymin=value-sd, ymax=value+sd),
position = position_dodge(0.9), width=0.2 ,alpha=0.5, size=1)+
theme_bw()
I try to scale and shift the second y-axis,
but PBIAS bar plot was removed because of out of scale limit as follow:
(https://i.stack.imgur.com/n6Jfm.jpg)
the following is my code with dual y axis:
## bar plot (scale and shift the second y-axis with slope/intercept in 20/-15)
ggplot(data, aes(x=index, y=value, fill=period))+
geom_bar(position="dodge", stat="identity")+
geom_errorbar(aes(ymin=value-sd, ymax=value+sd),
position = position_dodge(0.9), width=0.2 ,alpha=0.5, size=1)+
theme_bw()+
scale_y_continuous(limits = c(0,1), name = "value", sec.axis = sec_axis(~ 20*.- 15, name="value"))
Any advice for move bar_plot or other solution?
Taking a different approach, instead of using a dual axis one option would be to make two separate plots and glue them together using patchwork. IMHO that is much easier than fiddling around with the rescaling the data (that's the step you missed, i.e. if you want to have a secondary axis you also have to rescale the data) and makes it clearer that the indices are measured on a different scale:
library(ggplot2)
library(patchwork)
data$facet <- data$index %in% "PBIAS"
plot_fun <- function(.data) {
ggplot(.data, aes(x = index, y = value, fill = period)) +
geom_bar(position = "dodge", stat = "identity") +
geom_errorbar(aes(ymin = value - sd, ymax = value + sd),
position = position_dodge(0.9), width = 0.2, alpha = 0.5, size = 1
) +
theme_bw()
}
p1 <- subset(data, !facet) |> plot_fun() + scale_y_continuous(limits = c(0, 1))
p2 <- subset(data, facet) |> plot_fun() + scale_y_continuous(limits = c(-15, 15), position = "right")
p1 + p2 +
plot_layout(guides = "collect", width = c(3, 1))
A second but similar option would be to use ggh4x which via ggh4x::facetted_pos_scales allows to set the limits for facet panels individually. One drawback, the panels have the same width. (I failed in making this approach work with facet_grid and space="free")
library(ggplot2)
library(ggh4x)
data$facet <- data$index %in% "PBIAS"
ggplot(data, aes(x = index, y = value, fill = period)) +
geom_bar(position = "dodge", stat = "identity") +
geom_errorbar(aes(ymin = value - sd, ymax = value + sd),
position = position_dodge(0.9), width = 0.2, alpha = 0.5, size = 1
) +
facet_wrap(~facet, scales = "free") +
facetted_pos_scales(
y = list(
facet ~ scale_y_continuous(limits = c(-15, 15), position = "right"),
!facet ~ scale_y_continuous(limits = c(0, 1), position = "left")
)
) +
theme_bw() +
theme(strip.text.x = element_blank())

How can I show proportional (0-1) bar charts alongside line data with very different axes?

I have three lots of data I want to plot together: two geom_line() over the top of one stacked geom_bar(). All of that is over a time series, with a bar and two line values for each year.
The data looks something like this:
df <- data.frame(year = rep(1:5, each = 3),
cat = c("small", "med", "large"),
count = rep(sample(1:10, 5)),
line1 = rep(sample(30000:40000, 5), each = 3),
line2 = rep(sample(200:300, 5), each = 3))
It's easy enough to plot all three together, but I don't want to show the y-axis label for the bars. Instead, I want the left axis to show one line and the right to show the other. I want the plot to look something like this:
but to have the left axis show the line1 value (i.e. the 30000:40000 value). How would I go about including the two line axes, but still showing the bars across the whole height of the plot?
library(ggplot2)
ggplot(data = df, aes(x=year)) +
geom_bar(aes(y = count, x = year, fill = cat), position = "fill", stat="identity") +
geom_line(aes(y = line1/max(line1))) +
geom_line(aes(y = line2/max(line2)), color = "red") +
scale_y_continuous(sec.axis = sec_axis(~.*max(df$line2), name = "line2 (red)"))
Simply adding the geom_bar() after a two-axis line plot results in the bars not showing, because the scale of the lines data is far beyond that of the proportional (0-1) bar data:
ggplot(data = df, aes(x=year)) +
geom_line(aes(y = line1)) +
geom_line(aes(y = line2*100), color = "red") +
scale_y_continuous(sec.axis = sec_axis(~./100, name = "line2 (red)")) +
geom_bar(aes(y = count, x = year, fill = cat), position = "fill", stat="identity")
I'd like these axes, but still show the bars:
Could do it with dplyr calculations in line:
library(dplyr); library(ggplot2)
ggplot(data = df, aes(x=year)) +
geom_col(data = df %>% group_by(year) %>%
mutate(share = count / sum(count) * max(df$line1)),
aes(y = share, x = year, fill = cat)) +
geom_line(aes(y = line1)) +
geom_line(aes(y = line2*100), color = "red") +
scale_y_continuous(sec.axis = sec_axis(~./100, name = "line2 (red)"))

ground geom_text to x axis (e.g. y =0)

I have a graph made in ggplot that looks like this:
I wish to have the numeric labels at each of the bars to be grounded/glued to the x axis where y <= 0.
This is the code to generate the graph as such:
ggplot(data=df) +
geom_bar(aes(x=row, y=numofpics, fill = crop, group = 1), stat='identity') +
geom_point(data=df, aes(x = df$row, y=df$numofparcels*50, group = 2), alpha = 0.25) +
geom_line(data=df, aes(x = df$row, y=df$numofparcels*50, group = 2), alpha = 0.25) +
geom_text(aes(x=row, y=numofpics, label=bbch)) +
geom_hline(yintercept=300, linetype="dashed", color = "red", size=1) +
scale_y_continuous(sec.axis= sec_axis(~./50, name="Number of Parcels")) +
scale_x_discrete(name = c(),breaks = unique(df$crop), labels = as.character(unique(df$crop)))+
labs(x=c(), y="Number of Pictures")
I've tried vjust and experimenting with position_nudge for the geom_text element, but every solution I can find changes the position of each element of the geom_text respective to its current position. As such everything I try results in situation like this one:
How can I make ggplot ground the text to the bottom of the x axis where y <= 0, possibly with the possibility to also introduce a angle = 45?
Link to dataframe = https://drive.google.com/file/d/1b-5AfBECap3TZjlpLhl1m3v74Lept2em/view?usp=sharing
As I said in the comments, just set the y-coordinate of the text to 0 or below, and specify the angle : geom_text(aes(x=row, y=-100, label=bbch), angle=45)
I'm behind a proxy server that blocks connections to google drive so I can't access your data. I'm not able to test this, but I would introduce a new label field in my dataset that sets y to be 0 if y<0:
df <- df %>%
mutate(labelField = if_else(numofpics<0, 0, numofpics)
I would then use this label field in my geom_text call:
geom_text(aes(x=row, y=labelField, label=bbch), angle = 45)
Hope that helps.
You can simply define the y-value in geom_text (e.g. -50)
ggplot(data=df) +
geom_bar(aes(x=row, y=numofpics, fill = crop, group = 1), stat='identity') +
geom_point(data=df, aes(x = df$row, y=df$numofparcels*50, group = 2), alpha = 0.25) +
geom_line(data=df, aes(x = df$row, y=df$numofparcels*50, group = 2), alpha = 0.25) +
geom_text(aes(x=row, y=-50, label=bbch)) +
geom_hline(yintercept=300, linetype="dashed", color = "red", size=1) +
scale_y_continuous(sec.axis= sec_axis(~./50, name="Number of Parcels")) +
scale_x_discrete(name = c(),breaks = unique(df$crop), labels =
as.character(unique(df$crop)))+
labs(x=c(), y="Number of Pictures")

ggplot2 putting data labels (geom_text) in wrong order

I'm having an issue with data labels incorrectly ordering using ggplot2.
Unfortunately, other SE Q&As on this topic are not very insightful(example), so I'm having to reach out with a reprex. I have the following data:
df = as.data.frame(structure(list(geotype = c('urban','urban','urban','urban','suburban','suburban','suburban','suburban'),
limitations = c('all','some','all','some','all','some','all','some'),
metric = c('lte','lte','5g','5g','lte','lte','5g','5g'),
capacity=c(12,11,5,4,14,10,5,3))))
If I then try to plot this data using this code:
ggplot(df, aes(x = geotype, y = capacity, fill=metric)) + geom_bar(stat="identity") +
facet_grid(~limitations) +
geom_text(data = df, aes(geotype, capacity + 2, label=capacity), size = 3)
I get this incorrect labelling order:
I've played with the ordering of the variables for ages (e.g. rev(capacity)) but I can't fix the issue. Can anyone provide a more comprehensive answer for the whole SE community as to how to deal with label ordering?
You need to call the position argument in geom_text to match the filled aesthetics data with geom_bar and to let the function know the data is stacked.
ggplot(df, aes(x = geotype, y = capacity, fill=metric)) +
geom_bar(stat="identity") +
geom_text(data = df, aes(geotype, capacity, label=capacity), size = 3, vjust = 2,
position = position_stack()) +
facet_grid(~limitations)
You can set the labels in the mail ggplot aes
ggplot(df, aes(x = geotype, y = capacity, fill = metric, label = capacity ) ) +
geom_col() +
geom_text( size = 3, position = position_stack( vjust = 0.5 ) ) +
facet_grid( ~limitations )
Something like this? The position stack.
g <- ggplot(df, aes(x = geotype, y = capacity, fill = metric, label =
capacity))
g + geom_col() + facet_grid(~limitations) +
geom_text(size = 3, vjust =3, position = position_stack())

geom_bar ggplot2 subtract two Values

g = ggplot(Values, aes(x = X, y = Y, fill=factor(Z))) +
geom_bar(width=0.8, stat = "identity", position="dodge") +
facet_grid(Z ~ ., scale = "free_y") +
labs(x="Anno", y = "Riserva Rivalutata") +
theme(legend.position ="none") +
scale_x_continuous(breaks = seq(2000, 2070, by = 5))
d = ggplot(Values, aes(x = X, y = Y, fill=factor(Z))) +
geom_bar(width=0.8, stat = "identity", position="dodge")
p = subplot(g,d, nrows=2, shareX= T,which_layout = 1)
Hello,
I'm creating an object that when I click 2 objects Z, shows me above 2 distinct graphs, while below I would like to see the subtraction of the two.
Right now I can only see them distinct.
Can you help me to make a single chart but with only one bar given by the difference between the two data?
Thank you

Resources