I have made a ggplot line plot that uses two uses two sets of time series data and looks as it should as a static plot. Script and plot below:
p_curve <- ggplot(df, aes(x = Var1, y = Var2)) +
geom_path(size = 1, colour = "red") +
geom_path(x = Var3, y = Var4, size = 1, colour = "blue") +
geom_vline(xintercept = 0) +
geom_hline(yintercept = Var2[1]) +
xlim(c(min(df$Var1, df$Var3)), c(max(df$Var1, df$Var3))) +
ylim(c(min(df$Var2, df$Var4)), c(max(df$Var2, df$Var4))) +
theme_classic() +
labs(x = "Variable", y = "Other Variable", title = "Variable x Variable Curve") +
theme(plot.title = element_text(hjust = 0.5),
panel.border = element_rect(colour = "black", fill = NA, size = 0.5))
The plot looks exactly as it should. I would like to animate it so that it starts where the data starts (the middle of the curve, intersection of hline and vline) and then follows the time series. When I add transition_reveal, the plot animates from left to right in a wipe like fashion.
p_curve + transition_reveal(along = Var1)
Can anyone help with getting this to reveal along the data series, not the x-axis? Thanks in advance.
Related
This is a screenshot of part of my stacked bar-plot. I would like to have the mean values of each of the bars on top of each of the stacked bars
I have tried to include another df with my means through geom_text in the code.
However, since the dfs differ in size it did not work I think. I also tried to directly include the mean through calculating it with the geom_text function. What are your suggestions?
ggplot(Df_class, aes(x = series, y = Class, group = Month, color = Month + label = Class)) + geom_col(position = "stack",show.legend = T) +
labs(x = "BG ID", y = "Class Total",title ="Class Total") +
theme(axis.text = element_text(colour = "black", size = rel(0.45))) +
geom_text(aes(Df_class_mean$Mean_Class, size = 3, nudge_y = 0, vjust = 0.45))
I have an NMDS ordination that I've plotted using ggplot2. I've added environmental vectors on top (from the envfit() function in vegan) using geom_segment() and added corresponding labels to the same coordinates as the segments using geom_text() (code below):
ggplot() +
geom_point(data = nmds.sites.plot, aes(x = NMDS1, y = NMDS2, col = greening), size = 2) +
labs(title = "Study Area",
col = "Sites") +
geom_polygon(data = hull.data, aes(x = NMDS1, y = NMDS2, fill = grp, group = grp), alpha = 0.2) +
scale_fill_discrete(name = "Ellipses",
labels = c("High", "Moderate", "Control")) +
xlim(c(-1, 1)) +
guides(shape = guide_legend(order = 1),
colour = guide_legend(order = 2)) +
geom_segment(data = env.arrows,
aes(x = 0, xend = NMDS1, y = 0, yend = NMDS2),
arrow = arrow(length = unit(0.25, "cm")),
colour = "black", inherit.aes = FALSE) +
geom_text(data = env.arrows, aes(x = NMDS1, y = NMDS2, label = rownames(env.arrows))) +
coord_fixed() +
theme_bw() +
theme(text = element_text(size = 14))
However, since the labels are justified to centre, part of the label sometimes overlaps with the end of the arrow. I want to have the text START at the end of the arrow. In some other cases, if the arrow is pointing up, it pushes into the middle of the text. Essentially, I want to be able to see both the arrow head AND the text.
I have tried using geom_text_repel() from the ggrepel package but the placement seems random (and will also repel from other points or text in the plot (or just not do anything at all).
[EDIT]
Below are the coordinates of the NMDS vectors (this is the env.arrows object from the example code above):
NMDS1 NMDS2
Variable1 -0.46609087 0.27567532
Variable2 -0.21524887 -0.10128795
Variable3 0.59093184 0.03423775
Variable4 -0.00136418 0.46550043
Variable5 -0.30900813 -0.19659929
Variable6 0.53510347 -0.36387227
Variable7 0.66376246 -0.05220685
In the code below, we create a radial shift function to move the labels away from the arrows. The shift includes a constant amount plus an additional shift that varies with the absolute value of the cosine of the label's angle to the x-axis. This is because labels with theta near 0 or 180 degrees have a larger length of overlap with the arrows, and therefore need to be moved farther, than labels with theta near 90 or 270 degrees.
You may need to tweak the code a bit to get the labels exactly where you want them. Also, you'll likely need to add an additional adjustment if the variable names can have different widths.
One additional note: I've turned the variable names into a data column. You should do this with your data as well and then map that data column to the label argument of aes. Using rownames(env.arrows) for the labels reaches outside the ggplot function environment to the external data frame env.arrows and breaks the mapping to the data frame you've provided in the data argument to geom_text (although it likely won't cause a problem in this particular case).
library(tidyverse)
library(patchwork)
# data
env.arrows = read.table(text=" var NMDS1 NMDS2
Variable1 -0.46609087 0.27567532
Variable2 -0.21524887 -0.10128795
Variable3 0.59093184 0.03423775
Variable4 -0.00136418 0.46550043
Variable5 -0.30900813 -0.19659929
Variable6 0.53510347 -0.36387227
Variable7 0.66376246 -0.05220685", header=TRUE)
# Radial shift function
rshift = function(r, theta, a=0.03, b=0.07) {
r + a + b*abs(cos(theta))
}
# Calculate shift
env.arrows = env.arrows %>%
mutate(r = sqrt(NMDS1^2 + NMDS2^2),
theta = atan2(NMDS2,NMDS1),
rnew = rshift(r, theta),
xnew = rnew*cos(theta),
ynew = rnew*sin(theta))
p = ggplot() +
geom_segment(data = env.arrows,
aes(x = 0, xend = NMDS1, y = 0, yend = NMDS2),
arrow = arrow(length = unit(0.25, "cm")),
colour = "black", inherit.aes = FALSE) +
geom_text(data = env.arrows, aes(x = NMDS1, y = NMDS2, label = var)) +
coord_fixed() +
theme_bw() +
theme(text = element_text(size = 14))
pnew = ggplot() +
geom_segment(data = env.arrows,
aes(x = 0, xend = NMDS1, y = 0, yend = NMDS2),
arrow = arrow(length = unit(0.2, "cm")),
colour = "grey60", inherit.aes = FALSE) +
geom_text(data = env.arrows, aes(x = xnew, y = ynew, label = var), size=3.5) +
coord_fixed() +
theme_bw() +
theme(text = element_text(size = 14)) +
scale_x_continuous(expand=expansion(c(0.12,0.12))) +
scale_y_continuous(expand=expansion(c(0.07,0.07)))
p / pnew
One of the value in my dataset is zero, I think because of that I am not able to adjust labels correctly in my pie chart.
#Providing you all a sample dataset
Averages <- data.frame(Parameters = c("Cars","Motorbike","Bicycle","Airplane","Ships"), Values = c(15.00,2.81,50.84,51.86,0.00))
mycols <- c("#0073C2FF", "#EFC000FF", "#868686FF", "#CD534CFF","#FF9999")
duty_cycle_pie <- Averages %>% ggplot(aes(x = "", y = Values, fill = Parameters)) +
geom_bar(width = 1, stat = "identity", color = "white") +
coord_polar("y", start = 0)+
geom_text(aes(y = cumsum(Values) - 0.7*Values,label = round(Values*100/sum(Values),2)), color = "white")+
scale_fill_manual(values = mycols)
Labels are not placed in the correct way. Please tell me how can get 3D piechart.
Welcome to stackoverflow. I am happy to help, however, I must note that piecharts are highly debatable and 3D piecharts are considered bad practice.
https://www.darkhorseanalytics.com/blog/salvaging-the-pie
https://en.wikipedia.org/wiki/Misleading_graph#3D_Pie_chart_slice_perspective
Additionally, if the names of your variables reflect your actual dataset (Averages), a piechart would not be appropriate as the pieces do not seem to be describing parts of a whole. Ex: avg value of Bicycle is 50.84 and avg value of Airplane is 51.86. Having these result in 43% and 42% is confusing; a barchart would be easier to follow.
Nonetheless, the answer to your question about placement can be solved with position_stack().
library(tidyverse)
Averages <-
data.frame(
Parameters = c("Cars","Motorbike","Bicycle","Airplane","Ships"),
Values = c(15.00,2.81,50.84,51.86,0.00)
) %>%
mutate(
# this will ensure the slices go biggest to smallest (a best practice)
Parameters = fct_reorder(Parameters, Values),
label = round(Values/sum(Values) * 100, 2)
)
mycols <- c("#0073C2FF", "#EFC000FF", "#868686FF", "#CD534CFF","#FF9999")
Averages %>%
ggplot(aes(x = "", y = Values, fill = Parameters)) +
geom_bar(width = 1, stat = "identity", color = "white") +
coord_polar("y", start = 0) +
geom_text(
aes(y = Values, label = label),
color = "black",
position = position_stack(vjust = 0.5)
) +
scale_fill_manual(values = mycols)
To move the pieces towards the outside of the pie, you can look into ggrepel
https://stackoverflow.com/a/44438500/4650934
For my earlier point, I might try something like this instead of a piechart:
ggplot(Averages, aes(Parameters, Values)) +
geom_col(aes(y = 100), fill = "grey70") +
geom_col(fill = "navyblue") +
coord_flip()
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")
I am trying to plot two flows and one rainfall data in one graph. I have broke it up into top and bottom parts as shown in the following pic. Here I have two issues with this plots and spent ages but cannot solve it.
Why the observed flow always in black, even I have set it up as blue? Did I accidentally used some other arguments to overwrite it?
The most importantly is, how do I able to add a legend for the bottom plot? I tried many different codes but they don't seem to work for me.
x = data.frame(date = Date, rain = Obs_rain, obsflow = Obs_flow,simflow=Sim_flow)
g.top <- ggplot(x, aes(x = date, y = rain, ymin=0, ymax=rain)) +
geom_linerange() +
scale_y_continuous(trans = "reverse") +
theme_bw() +
theme(plot.margin = unit(c(1,5,-30,6),units="points"),
axis.title.y = element_text(vjust =0.3)) +
labs(x = "Date",y = "Rain(mm)")
g.bottom <- ggplot(x, aes(x = date, y = obsflow, ymin=0, ymax=obsflow), colour = "blue",size=0.5) +
geom_linerange() + #plot flow
geom_linerange(aes(y = simflow, ymin=0, ymax=simflow), colour = "red", size =0.5)+
labs(x = "Date", y = "River flow (ML/day)") +
theme_classic() +
theme(plot.background = element_rect(fill = "transparent"),
plot.margin = unit(c(2,0,1,1),units="lines"))
grid.arrange(g.top,g.bottom, heights = c(1/5, 4/5))
Update:
I have resolved the issue with blue line colour. I accidently put arguments in the wrong place. But I'm still struggling with the legend.
g.bottom <- ggplot(x, aes(x = date, y = obsflow, ymin=0, ymax=obsflow)) +
geom_linerange(colour = "blue",size=0.5) + #plot flow
As an explanation of what #pierre means... turn your data from "wide" to "long" format using reshape2::melt, so that the flow type for each date is in one column flow_type, and the value is another (flow_val). Then you specify flow_type as the grouping variable with which to assign colour:
require(reshape2)
x.melted <- melt(x, id.vars = c("date", "rain"), variable.name="flow_type",
value.name="flow_val")
g.bottom <- ggplot(x.melted, aes(x = date),size=0.5) +
geom_linerange(aes(ymin=0, ymax=flow_val, colour=flow_type)) + #plot flow
labs(x = "Date", y = "River flow (ML/day)") +
theme_classic() +
theme(plot.background = element_rect(fill = "transparent"),
plot.margin = unit(c(2,0,1,1),units="lines"),
legend.position="bottom") +
scale_colour_manual(guide = guide_legend(title = "Flow Type"),
values = c("obsflow"="blue", "simflow"="red"))