I want to produce chart like this:
I go with this code:
dat <- data.frame(
name1 = c("A", "B", "C", "D", "E"),
name2 = c("F", "G", "H", "I", "J"),
value = c(-12,10,5,-7,-2)
)
ggplot(dat,aes(x = name1,y = value)) +
geom_bar(stat = "identity",
fill = "#465978",
width = 0.3) +
ylim(-50,50) +
geom_hline(yintercept = 0,
color = "#9e9e9e",
alpha = 0.3,
linetype = "dotted") +
coord_flip() +
theme_bw() +
theme(axis.line = element_blank(),
axis.title = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_text(colour = "#737373"),
axis.ticks = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank())
and ended up like this: (note: there is a line in the middle, if you can't see, may be due to low resolution)
How to add dat$name2 in the right side (i.e. top x axis)?
If you don't mind, maybe you can help me produce more similar chart?
Offering an alternative to r2evans' solution, you can convert a discrete variable to a continuous one by doing match(x, unique(x)). If you then have continuous variables on both axes, it is easy to add a secondary axis.
Here is how you could do that (with some extra decorations based on your request to make the chart more similar).
library(ggplot2)
dat <- data.frame(
name1 = c("A", "B", "C", "D", "E"),
name2 = c("F", "G", "H", "I", "J"),
value = c(-12,10,5,-7,-2)
)
# Probably easiest to define `cont_name1 = match(name1, unique(name1))` in the data
# instead of having to declare it in the `aes()` every time.
ggplot(dat,aes(x = value,y = match(name1, unique(name1)))) +
geom_tile(width = Inf, height = 0.3, fill = "grey95") +
geom_bar(stat = "identity",
fill = "#465978",
width = 0.3, orientation = "y") +
geom_segment(aes(y = match(name1, unique(name1)) - 0.15,
yend = match(name1, unique(name1)) + 0.3,
xend = value)) +
geom_text(aes(x = ifelse(value < 0, value - 4, value + 4),
y = match(name1, unique(name1)) + 0.2,
label = scales::percent(value, scale = 1, accuracy = 1)),
vjust = 0) +
xlim(-50,50) +
scale_y_continuous(
breaks = match(dat$name1, unique(dat$name1)),
labels = dat$name1,
sec.axis = sec_axis(~ .x, labels = dat$name2, breaks = 1:5)
) +
geom_vline(xintercept = 0,
color = "#9e9e9e",
alpha = 0.3,
linetype = "dotted") +
ggtitle("Title here (0%)") +
theme_bw() +
theme(axis.line = element_blank(),
axis.title = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_text(colour = "#737373"),
axis.ticks = element_blank(),
plot.title = element_text(hjust = 0.5, colour = "grey60"),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank())
Created on 2021-01-17 by the reprex package (v0.3.0)
Normally (with a continuous scale) I'd recommend scale_y_continuous(sec.axis=...). Unfortunately, scale_discrete does not yet support it (see https://github.com/tidyverse/ggplot2/issues/3171). With that, the two ways to go include geom_text and annotations. I'll offer the first.
To make it consistent on both sides, we'll need to remove the axis labels from the left axis. (I find this annoying, but consistency between axes is important to me. If it is not as much to you, then you can reduce some of the changes.)
ggplot(dat,aes(x = name1,y = value)) +
geom_bar(stat = "identity",
fill = "#465978",
width = 0.3) +
ylim(-50,50) +
geom_hline(yintercept = 0,
color = "#9e9e9e",
alpha = 0.3,
linetype = "dotted") +
geom_text(aes(y = -Inf, label = name1), hjust = 0, colour = "#737373") + # new
geom_text(aes(y = Inf, label = name2), hjust = 1, colour = "#737373") + # new
coord_flip() +
theme_bw() +
theme(axis.line = element_blank(),
axis.title = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(), # change
axis.ticks = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank())
One problem you will run into with multi-line labels (as in your example) is that of alignment. ggplot2 has a confounded sense of hjust=, where it means both direction of the text box from the point and alignment of the text within the text box. So hjust=0 means "text goes to the right of the point, and the text is left-aligned". There does not seem to be an easy way to have the text box go to the right of the point (first geom_text, on the left-edge) yet have the text right-justified. (I'll be happy if somebody can show an easy way to work around this!)
The workarounds to force that left-edge right-align textbox require knowing a priori the dimensions of the plot so that you can hard-position the text box (not y=-Inf, the left-edge) within the plot boundary and hard-code the limits of the plot. (Know that when you form the plot, none of the functions know what the dimensions of the rendered plot will be, in user-points, centimeters, or similar.)
As an aside, we can add a geom_text and geom_tile for a couple more features.
ggplot(dat,aes(x = name1,y = value)) +
geom_tile(height = 90, width = 0.3, fill = "gray90") +
geom_bar(stat = "identity",
fill = "#465978",
width = 0.3) +
ylim(-50,50) +
geom_hline(yintercept = 0,
color = "#9e9e9e",
alpha = 0.3,
linetype = "dotted") +
geom_text(aes(y = -Inf, label = name1), hjust = 0, colour = "#737373") +
geom_text(aes(y = Inf, label = name2), hjust = 1, colour = "#737373") +
geom_text(aes(label = value), vjust = -1.1) +
coord_flip() +
theme_bw() +
theme(axis.line = element_blank(),
axis.title = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank())
. + > ggplot(dat,aes(x = name1,y = value)) +
geom_tile(aes(y = 0), height = 90, width = 0.3, fill = "gray90") +
geom_bar(stat = "identity",
fill = "#465978",
width = 0.3) +
ylim(-50,50) +
geom_hline(yintercept = 0,
color = "#9e9e9e",
alpha = 0.3,
linetype = "dotted") +
geom_text(aes(y = -Inf, label = name1), hjust = 0, colour = "#737373") +
geom_text(aes(y = Inf, label = name2), hjust = 1, colour = "#737373") +
geom_text(aes(label = value), vjust = -1.1) +
coord_flip() +
theme_bw() +
theme(axis.line = element_blank(),
axis.title = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank())
Related
How can I implement histogram with such complex x-axis?
First x-axis row is the week start, second - week end.
Data for tests in csv: https://gofile.io/d/FrhLZh.
What I managed to
hist_data %>%
ggplot(aes(x = week, y = count)) +
geom_col(fill = "#5B879E", width = 0.9, size = 0.7) +
labs(title = "", x = "", y = "") +
theme_bw() + theme_minimal() + theme(legend.position="none")+
theme(
panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank(),
panel.grid.major.y = element_blank(),
panel.grid.minor.y = element_blank(),
axis.text.y = element_blank(),
axis.text.x = element_text(vjust = 0.5, size = 8, family = "Inter", colour = "#ffffff"),
axis.line.x = element_blank(),
axis.title.x = element_blank(),
plot.background = element_rect(fill = "#3A464F"),
plot.margin=unit(c(0,0.25,0.5,0), "cm"))+
scale_x_discrete(expand=c(0,0), labels = format(as.Date(hist_data$week_start), "%d-%m"), position = "bottom") +
scale_y_continuous()
Thanks to teunbrand and his ggh4x package, solution:
hist_data %>%
ggplot(aes(x = week, y = count)) +
geom_col(fill = "#5B879E", width = 0.8, size = 0.7)+
labs(title = "", x = "", y = "") +
theme_bw() + theme_minimal() + theme(legend.position="none")+
theme(
panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank(),
panel.grid.major.y = element_blank(),
panel.grid.minor.y = element_blank(),
axis.text.y = element_blank(),
axis.text.x = element_text(vjust = 0.5, size = 8, lineheight = 0.8, family = "Inter", colour = "#ffffff"),
axis.line.x = element_blank(),
axis.title.x = element_blank(),
ggh4x.axis.nestline.x = element_line(size = 0.5, colour = "#5B879E", lineend = "square"),
plot.background = element_rect(fill = "#3A464F"),
plot.margin=unit(c(1,0.5,1,0.5), "cm"))+
scale_x_discrete(expand=c(0,0),
labels = paste0(format(as.Date(sort(hist_data$week_start)), "%d.%m"),
"\n", "nonsense", "\n",
format(as.Date(sort(hist_data$week_end)), "%d.%m")), position = "bottom") +
scale_y_continuous() +
guides(x = guide_axis_nested(delim = "nonsense"))
You can add multiple layers of geom_text and geom_segment. Adjust the relative y positions of these layers using a scaling factor.
plotscale <- max(hist_data$count)/50
library(ggplot2)
ggplot(data = hist_data,
aes(x = week_start + floor(week_end-week_start)/2, y = count)) +
geom_col(fill = "#5B879E", width = 4) +
geom_text(aes(y = -6 * plotscale ,
label = format(week_start, "%m-%d")),
color = "#ffffff")+
geom_segment(aes(x = week_start, xend = week_end,
y = -10 * plotscale, yend = -10 * plotscale),
color = "#5B879E", size = 1.5)+
geom_text(aes(y = -14 * plotscale,
label = format(week_end, "%m-%d")),
color = "#ffffff")+
theme_minimal() +
theme(
panel.grid = element_blank(),
axis.text = element_blank(),
axis.title = element_blank(),
plot.background = element_rect(fill = "#3A464F"))+
scale_x_date(expand=c(0,0), date_breaks = "1 week",
labels = NULL)
Consider using ggh4x package for more complex nested x axes.
Raw Data
hist_data <- read.table(text='"","week","count","week_start","week_end"
"1","1",21.5823972708382,2021-01-04,2021-01-10
"2","2",36.122556304552,2021-01-11,2021-01-17
"3","3",34.2809483156697,2021-01-18,2021-01-24
"4","4",25.8546925450454,2021-01-25,2021-01-31
"5","5",29.0309819292706,2021-02-01,2021-02-07
"6","6",33.1503608888827,2021-02-08,2021-02-14
"7","7",27.0490347440184,2021-02-15,2021-02-21
"8","8",30.3031289757874,2021-02-22,2021-02-28
"9","50",32.2876434072602,2020-12-07,2020-12-13
"10","51",33.1939593686481,2020-12-14,2020-12-20
"11","52",26.6853246329896,2020-12-21,2020-12-27
"12","53",23.0715199391151,2020-12-28,2021-01-03', header = TRUE, sep = ",")
hist_data$week_start <- as.Date(hist_data$week_start)
hist_data$week_end <- as.Date(hist_data$week_end)
I've come across several threads pointing out how to annotate bar charts, but I've tried a number of iterations of this code and can't seem to get the text left justified, starting at 0% on the x axis. I've tried to change hjust to "left", 0.95, and progressively larger numbers - none of them have the text tethered to the x origin.
dummy_data <- tibble(Proportion = c(0.87, 1),
`Person of Interest` = c("Person B", "Person A"))
dummy_data %>%
ggplot(aes(x = Proportion, y = `Person of Interest`,
fill = `Person of Interest`,
label = paste0(`Person of Interest`, "~", scales::percent(Proportion))))+
geom_col(width = 0.5) +
geom_text(position = position_dodge(width = .9), # move to center of bars
vjust = 0, # nudge above top of bar
hjust = "top",
size = 4.5,
colour = "white",
fontface = "bold") +
scale_x_continuous(labels = scales::percent,
limits = c(0, 1.01),
expand = c(0, 0)) +
ggthemes::theme_economist(horizontal = F) +
scale_fill_manual(values = alpha(c("black", "#002D62"), .5)) +
ggtitle("Lack of Skill") +
theme(title = element_text("Lack of Skill"),
plot.title = element_text(hjust = 0.5, face = "italic"),
axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.x = element_blank(),
axis.text.x = element_text(hjust = 0.25),
legend.position="none",
aspect.ratio = 1/3)
I've often found text data with ggplot maddening - a huge thanks to anyone willing to take a look.
Try this approach that is close to what you want. Your themes can be producing the issues with placing the labels:
#Code
dummy_data %>%
ggplot(aes(x=`Person of Interest`,
y=Proportion,
fill=`Person of Interest`,
label = paste0(`Person of Interest`, "~", scales::percent(Proportion))))+
geom_bar(stat = 'identity')+
geom_text(aes(y=0.13),
size = 4.5,
colour = "white",
fontface = "bold")+coord_flip()+
scale_y_continuous(labels = scales::percent,
limits = c(0, 1.01),
expand = c(0, 0)) +
ggthemes::theme_economist(horizontal = F) +
scale_fill_manual(values = alpha(c("black", "#002D62"), .5)) +
ggtitle("Lack of Skill") +
theme(title = element_text("Lack of Skill"),
plot.title = element_text(hjust = 0.5, face = "italic"),
axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.x = element_blank(),
axis.text.x = element_text(hjust = 0.25),
legend.position="none",
aspect.ratio = 1/3)
Output:
I have created a linear gauge in R to be displayed within PowerBI.
My only issue is that the width of the plot cannot be adjusted so I am getting the following:
(Plot is being rendered in PowerBI)
Whereas I would like to obtain the same graph but half the width.
I tried using width within geom_bar but it resizes the bar and the final output is the same.
Ideally, the bar would be half its current width (I am building this graph for a PowerBI report).
This is the code I used:
library(ggplot2)
scores = factor(c('Inadequate','Adequate','Fair','Good','Great','Excellent','Exceptional'),
levels = (c('Inadequate','Adequate','Fair','Good','Great','Excellent','Exceptional')),
ordered = TRUE)
x <- data.frame(points = rep(1,7), scores= scores)
x %>%
ggplot(aes(x=points, fill=scores)) +
geom_bar(position = "stack", show.legend = FALSE) +
geom_text(aes(label=scores, y = seq(from=0.5, to=6.5, by = 1)), label.size = 0.25)+
coord_flip() +
theme(panel.background = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank(),
axis.text = element_blank()) +
geom_point(aes(x= 1.45, y=5), shape = 25, size=10, colour = "black", fill = "black") +
geom_point(aes(x= 0.55, y=3), shape = 24, size=10, colour = "black", fill = "black") +
geom_point(aes(x= 0.55, y=6), shape = 24, size=10, colour = "black", fill = "black") +
scale_fill_brewer(palette = "RdYlGn", direction = -1)
If simply resizing the Power BI visual is no option, you can use theme(plot.margin = unit(c(0, 0.2, 0, 0.2), "npc")) for increasing margins that ggplot draws around plot. Full code:
library(tidyverse)
scores = factor(c('Inadequate','Adequate','Fair','Good','Great','Excellent','Exceptional'),
levels = (c('Inadequate','Adequate','Fair','Good','Great','Excellent','Exceptional')),
ordered = TRUE)
x <- data.frame(points = rep(1,7), scores= scores)
x %>%
ggplot(aes(x=points, fill=scores)) +
geom_bar(position = "stack", show.legend = FALSE) +
geom_text(aes(label=scores, y = seq(from=0.5, to=6.5, by = 1)), label.size = 0.25)+
coord_flip() +
theme(panel.background = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank(),
axis.text = element_blank()) +
geom_point(aes(x= 1.45, y=5), shape = 25, size=10, colour = "black", fill = "black") +
geom_point(aes(x= 0.55, y=3), shape = 24, size=10, colour = "black", fill = "black") +
geom_point(aes(x= 0.55, y=6), shape = 24, size=10, colour = "black", fill = "black") +
scale_fill_brewer(palette = "RdYlGn", direction = -1) +
theme(plot.margin = unit(c(0, 0.2, 0, 0.2), "npc"))
I have run the following ggplot function :
g <- ggplot(results_table, aes(x = "", y = Pct*100, fill = Criteria),width = 0.5) +
geom_bar(stat = "identity", color = Palcolor, fill = Palcolor) +
coord_polar(theta = "y", start = 0, direction = -1) +
theme_minimal() +
theme(legend.position = "none", axis.title.x = element_blank(), axis.title.y = element_blank(),
panel.border = element_blank(), panel.grid = element_blank(), axis.text = element_blank(),
axis.ticks = element_blank(), legend.title = element_blank(),
plot.title = element_text(size = 14, hjust = 0.5, vjust = 0)) +
geom_text(aes(label = paste0(Criteria,"; ",sprintf("%0.1f", round(Pct*100, digits = 1)),"%")),
position = position_stack (vjust = 0.5, reverse = TRUE)) +
labs(title = gTitle)
}
and the pie is the following :
but it is too large. I would like to have a diameter smaller than the length of the second part of the title. How can I proceed ?
I created a ggplot2 object:
a <- replicate(8,rnorm(100))
colnames(a) <- letters[1:8]
b < -melt(a,id.vars=1:1)
colnames(b) <- c("c","variable","value")
ggplot(b,aes(x = c,y = value, colour = variable, linetype = variable)) +
geom_line()+
geom_point(aes(shape = factor(variable)), size = 1.7) +
scale_x_continuous(limits = c(-1, 1),
breaks = seq(-1, 1, 0.1),
expand=c(0.01, 0.01)) +
scale_y_continuous(limits = c(-1, 1),
breaks = seq(-1, 1, 0.1),
expand = c(0.01, 0.01))+
theme_bw(base_size = 12, base_family = "Helvetica") +
theme(axis.text=element_text(size = 10),
axis.title=element_text(size = 10),
text = element_text(size = 10),
axis.line = element_line(size = 0.25),
axis.ticks=element_line(size = 0.25),
panel.grid.major = element_blank(),
#panel.grid.minor = element_blank(),
panel.border = element_rect(colour = "black", fill = NA, size = 0.5),
panel.background = element_blank(),
legend.position = "top" ,
legend.direction = "vertical",
legend.title = element_blank(),
legend.text = element_text(size = 13),
legend.background = element_blank(),
legend.key = element_blank()) +
labs(x = '', y = '', title = "") +
theme(plot.title = element_text(size=10)) +
theme(strip.text.x = element_text(size = 8,color="black"),
strip.background = element_blank()) +
theme(strip.text.x = element_text(size = 8, colour = "black"))
My problem is the following:
when I create the legend, there is a separate legend for the colors and a separate one for the points.
How can I create a single legend for each of the 8 variables?
Let me minimise your code and focus on the legend issue. This is what you have now.
ggplot(b,aes(x = c, y = value, colour = variable, linetype = variable)) +
geom_line() +
geom_point(aes(shape = factor(variable)),size=1.7)
Your data frame, b has variable as factor. You use this in two ways here; variable and factor(variable). You can simply use variable for shape in geom_point; make all variable identical.
ggplot(b,aes(x = c, y = value, colour = variable, linetype = variable)) +
geom_line()+
geom_point(aes(shape = variable),size = 1.7)
I saw some warning messages related to colours and other things. You may want to take care of them. But, for legend, this is one way to go.
Take from the ideas on this page: http://www.cookbook-r.com/Graphs/Legends_(ggplot2)/#modifying-the-text-of-legend-titles-and-labels
I edited your code to make the data visible (you had problems with your x-axis limits. Note the final three lines. These commands tell ggplot to create only one legend.
a<-replicate(6,rnorm(100))
colnames(a)<-letters[1:6]
b<-melt(a,id.vars=1:1)
colnames(b)<-c("c","variable","value")
ggplot(b,aes(x=c,y=value,colour=variable,linetype=variable)) +
geom_line() + geom_point(aes(shape=factor(variable)),size=1.7)+
scale_x_continuous(limits=c(0,100))+
scale_y_continuous(limits=c(-2,2),breaks=seq(-2,2,0.1),expand=c(0.01,0.01))+
theme_bw(base_size=12, base_family="Helvetica") +
theme(axis.text=element_text(size=10),
axis.title=element_text(size=10),
text = element_text(size=10),
axis.line = element_line(size=0.25),
axis.ticks=element_line(size=0.25),
panel.grid.major = element_blank(),
#panel.grid.minor = element_blank(),
panel.border = element_rect(colour="black",fill=NA,size=0.5),
panel.background = element_blank(),
legend.position="top" ,
legend.direction="vertical",
legend.title=element_blank(),
legend.text=element_text(size=13),
legend.background=element_blank(),
legend.key=element_blank())+
labs(x='', y='',title="")+
theme(plot.title=element_text(size=10))+
theme(strip.text.x = element_text(size = 8,color="black"),strip.background=element_blank())+
theme(strip.text.x = element_text(size = 8,color="black"))+
scale_colour_discrete(name ="Factor")+
scale_linetype_discrete(name ="Factor") +
scale_shape_discrete(name ="Factor")