Re-labeling one axis on a dual-axis chart - r

I have a chart which shows lap position progression over a 2 lap race from starting position to end position.
I would like to amend the chart to have the drivers name on the left y-axis, which aligns with their starting position (i.e from top to bottom, the names would read bill, who started first, maria, who started second, and claudio, who started third) and their end position number would be on the right side (from top to bottom, first to third)
I have been working on this for a while but can't figure it out. Also, as shown, I'd like the chart to go from 1 at the top to 3 at the bottom, but I can't get rid of the negative for some reason. Many thanks for anybody that can help:
## reprex
library(ggplot2)
library(scales)
dat <- data.frame(name = c(rep("bill", 3), rep("maria", 3), rep("claudio", 3)),
lap = rep(0:2, 3),
pos = c(1, 1, 2,
2, 3, 1,
3, 2, 3))
dat %>%
ggplot(aes(x = lap, y = -pos, color = name)) +
geom_line() +
scale_y_continuous(breaks = pretty_breaks(3),
sec.axis = dup_axis()) +
scale_x_continuous(breaks = pretty_breaks(3))
Desired end result:

You can label the primary axis using a labeller function that finds which driver was at each position on lap 0, and add a sec_axis instead of a dup_axis.
You can also use scale_y_reverse rather than using -pos to order the axis correctly:
dat %>%
ggplot(aes(x = lap, y = pos, color = name)) +
geom_line() +
scale_y_reverse(breaks = 1:3,
labels = function(x) {
dat$name[dat$lap == 0][order(dat$pos[dat$lap == 0])][x]
},
sec.axis = sec_axis(function(x) x,
breaks = 1:3, labels = label_ordinal())) +
scale_x_continuous(breaks = pretty_breaks(3))

You could also stick with your -pos to reorder your y-axis and then simply reorder the breaks and labels the same way. Less elegant than Allan Cameron solution, though.
Cheers
ggplot(dat, aes(x = lap, y = -as.numeric(pos), color = Driver, group = Driver)) +
geom_line(show.legend = F) +
scale_y_continuous(name = "Driver",
breaks = -(1:nlevels(dat$Driver)),
labels = unique(dat$Driver),
sec.axis = sec_axis(~.,
name = "End Position",
breaks = -(1:length(levels(as.factor(dat$pos)))),
labels = levels(as.factor(dat$pos))))+
scale_x_continuous(breaks = pretty_breaks(3))

Related

How to make a functioning pie chart, R

I am trying to make a pie chart in R that displays the percent of each section as a label. I was following this tutorial, (https://www.geeksforgeeks.org/r-pie-charts/). I modified it a little bit to make it similar to the data frame I am actually working with. When I try and run my code I get the error, "Error in edges * dx[i] : non-numeric argument to binary operator". It seems the error is coming from legend(), when I comment it out I get no error. Where am I going wrong?
Thanks in advance for any help.
df <- data.frame( geeks = c(23, 56, 20, 63),
labels = c("Mumbai", "Pune", "Chennai", "Bangalore"))
df <- mutate(df, percent = round(df$geeks/sum(df$geeks)*100, 1))
df_pie <- pie(df$geeks,
round(df$percent,1),
main = "City pie chart",
col = rainbow(length(df$geeks)),
legend("topright", c(df$labels),
cex = 0.5, fill = rainbow(length(df$geeks))))
legend is a stand-alone function, not an argument to pie
pie(df$geeks,
round(df$percent,1),
main = "City pie chart",
col = rainbow(length(df$geeks)))
legend("topright", c(df$labels), fill = rainbow(length(df$geeks)))
You might get a nicer look with ggplot though:
library(ggplot)
ggplot(df, aes(x = 1, y = percent, fill = labels)) +
geom_col() +
coord_polar(theta = "y") +
geom_text(aes(label = paste(percent, "%")),
position = position_stack(vjust = 0.5),
size = 8) +
theme_void(base_size = 20) +
scale_fill_brewer(name = NULL, palette = "Pastel2")

Ggplot - always place 'Total' bar as the farthest right bar using geom_col

I am creating a chart that looks like the below. Problem is that I'd like the grey 'total' bar to always be on the far right hand side.
Current code is below, can anyone please amend/provide any additional code to create this effect?
#plot with reorder
PrevalencePlot <- ggplot(ICSTable4, aes(x = reorder(value, Area), y = value, fill = Statistical_Significance)) +
geom_col() +
scale_fill_manual(values = colours)+
geom_errorbar(aes(ymin=errorbarlowerplot, ymax=errorbarhigherplot),
width=.2, # Width of the error bars
position=position_dodge(.9)) +
theme_bw() +
geom_text(aes(label = valuelabel), vjust = 2.5, colour = "black")+
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust=1))
If anyone is able to help then the below data frame could be used to generate the principle I think? Thank you!
df <- data.frame(Area = c("Area1", "Area2", "Area3", "Area4", "Total"),
Value = c(1, 3, 7, 5, 4)
)
Building on the minimal example data, we can make a spartanic version of the plot that addresses the question of ordering the values, and placing a selected column at the end.
df <- data.frame(Area = c("Area1", "Area2", "Area3", "Area4", "Total"),
value = c(1, 3, 7, 5, 4),
Statistical_Significance = c("higher", "lower", "lower", "higher", NA))
It's easier to create the order of the columns before plotting, as we need to create the factors based on the order of value and then reposition the target column ("Total").
df <- df %>%
dplyr::arrange(desc(value)) %>% #arrange by value
dplyr::mutate(Area = forcats::as_factor(Area)) %>% # factor that defines order on x-axis
dplyr::mutate(Area = forcats::fct_relevel(Area, "Total", after = Inf)) # reposition "Total" column
ggplot(df, aes(x = Area, y = value, fill = Statistical_Significance)) +
geom_col() +
theme_bw()

R, ggplot2 - In the legend, how do I hide unused colors from one geom while showing them in others?

I'm making a plot that has color defined for a geom_point(), and everything looks good.
points_a <- data.frame(x = sample(1:10, 4), y = sample(50:60, 4), id = "a")
points_b <- data.frame(x = sample(1:100, 4), y = sample(1:100, 4), id = "b")
points_c <- data.frame(x = sample(1:100, 4), y = sample(1:100, 4), id = "c")
points_all <- rbind(points_a, points_b, points_c)
ggplot(points_all) + geom_point(aes(x, y, color=id))
I'd like to highlight a group of points by drawing a rectangle around them using geom_rect(), picking up the same color already used in the chart. As desired, the legend adds a border around the item in the key; unfortunately, it also adds a border to every other item in the legend, as shown below:
my_box <- data.frame(left = 1, right = 10, bottom = 50, top = 60, id = "a")
ggplot(points_all) +
geom_point(aes(x, y, color=id)) +
geom_rect(data = my_box,
aes(xmin=left, xmax=right, ymin=bottom, ymax=top, color = id),
fill = NA, alpha = 1)
I want to get rid of the outlines around items "b" and "c" in the legend, since they're not plotted. I don't know how to do that, since they're defined by the same color aesthetic as the points. Ideally these unused factors should have been dropped from the legend for the outline color, as they rightfully are when showing only one geom_, but that doesn't seem to be how it works. (And defining the color manually outside the aes() call means it wouldn't get shown on the legend for id="a".)
Lots of searching hasn't yet yielded an answer, though I may have overlooked something. What's the best way to hide from the legend unused colors for one geom_ while keeping them for others? (Alternatively: Should I split these into two legends, and how?)
I usually tackle this sort of thing with override.aes in guide_legend(). In your case you can set the line type for the last two legend items to be 0 (no line). The first legend item should have linetype 1.
ggplot(points_all) +
geom_point(aes(x, y, color=id)) +
geom_rect(data = my_box,
aes(xmin=left, xmax=right, ymin=bottom, ymax=top,
color = id),
fill = NA, alpha = 1) +
guides(color = guide_legend(override.aes = list(linetype = c(1, 0, 0) ) ) )
aosmith has a great answer above. Alternatively, you could split the legends by changing the aesthetic in geom_point to fill in a hollow point:
points_all %>%
ggplot() +
geom_point(aes(x = x, y = y, fill = id), shape = 21, stroke = 0, size = 2.5) +
geom_rect(
data = my_box,
aes(xmin = left, xmax = right, ymin = bottom, ymax = top, color = id),
fill = NA, alpha = 1
)

R: ggplot2 let the characters of geom_text exactly cover one X unit

I want to highlight text based on the position in a string, for example if we have this text:
this is a really nice informative piece of text
Then I want to say let's draw a rectangle around positions 2 till 4:
t[his] is a really nice informative piece of text
I tried to do so in ggplot2 using the following code:
library(ggplot2)
library(dplyr)
box.data <- data.frame(
start = c(4,6,5,7,10,7),
type = c('BOX1.start', 'BOX1.start', 'BOX1.start','BOX1.end', 'BOX1.end', 'BOX1.end'),
text.id = c(1,2,3,1,2,3)
)
text.data <- data.frame(
x = rep(1,3),
text.id = c(1,2,3),
text = c('Thisissomerandomrandomrandomrandomtext1',
'Thisissomerandomrandomrandomrandomtext2',
'Thisissomerandomrandomrandomrandomtext3')
)
ggplot(data = text.data, aes(x = x, y = text.id)) +
scale_x_continuous(limits = c(1, nchar(as.character(text.data$text[1])))) +
geom_text(label = text.data$text, hjust = 0, size = 3) +
geom_line(data = box.data, aes(x = start, y = text.id, group = text.id, size = 3, alpha = 0.5, colour = 'red'))
This produces the following graph:
My method fails as a letter does not cover exactly one unit of the x-axis, is there any way to achieve this?
I just figured out that I can split the string in characters and plot these, perhaps it is useful for someone else.
library(ggplot2)
library(dplyr)
library(splitstackshape)
# First remember the plotting window, which equals the text length
text.size = nchar(as.character(text.data$text[1]))
# Split the string into single characters, and adjust the X-position to the string position
text.data <- cSplit(text.data, 'text', sep = '', direction = 'long', stripWhite = FALSE) %>%
group_by(text.id) %>%
mutate(x1 = seq(1,n()))
# Plot each character and add highlights
ggplot(data = text.data, aes(x = x1, y = text.id)) +
scale_x_continuous(limits = c(1, text.size)) +
geom_text(aes(x = text.data$x1, y = text.data$text.id, group = text.id, label = text)) +
geom_line(data = box.data, aes(x = start, y = text.id, group = text.id, size = 3, alpha = 0.5, colour = 'red'))
Which produces this plot:
Perhaps the marking should extend a little but upwards and downwards, but that's an easy fix.

Using geom_segment to create a timeline visualization

I am trying to create a chart like this one produced in the NYTimes using ggplot:
I think I'm getting close, but I'm not quite sure how to separate out some of my data so I get the right view. My data is political office holders that appear something like this:
name,year_elected,year_left,years_in_office,type,party
Person 1,1969,1969,1,Candidate,Unknown
Person 2,1969,1971,2,Candidate,Unknown
Person 3,1969,1973,4,Candidate,Unknown
Person 4,1969,1973,4,Candidate,Unknown
Person 5,1971,1974,3,Candidate,Unknown
Person 1,1971,1976,5,Candidate,Unknown
Person 2,1971,1980,9,Candidate,Unknown
Person 6,1973,1978,5,Candidate,Unknown
Person 7,1973,1980,7,Candidate,Unknown
Person 8,1975,1980,5,Candidate,Unknown
Person 9,1977,1978,1,Candidate,Unknown
And I've used the below code to get very close to this view, but I think an issue I'm running into is either drawing segments incorrectly (e.g., I don't seem to have a single segment for each candidate), or segments are overlapping/stacking. The key issue I'm running into is my list of office holders is around 60, but my chart is only drawing around 28 lines.
library(googlesheets)
library(tidyverse)
# I'm reading from a Google Spreadsheet
data <- gs_title("Council Members")
data_sj <- gs_read(ss = data, ws = "Sheet1")
ggplot(data, aes(year_elected, years_in_office)) +
geom_segment(aes(x = year_elected, y = 0,
xend = year_left, yend = years_in_office)) +
theme_minimal()
The above code gives me:
Thanks ahead of time for any pointers!
If your data frame is called d, then:
Transform it to data.table
Add jitter to year_electer
Add equivalent jitter to year_left
Add group (as an example) to color your samples
Use ggrepel to add text if there are many points.
Code:
library(data.table)
library(ggplot2)
library(ggrepel)
d[, year_elected2 := jitter(year_elected)]
d[, year_left2 := year_left + year_elected2 - year_elected + 0.01]
d[, group := TRUE]
d[factor(years_in_office %/% 9) == 1, group := FALSE]
ggplot(d, aes(year_elected2, years_in_office)) +
geom_segment(aes(x = year_elected2, xend = year_left2,
y = 0, yend = years_in_office, linetype = group),
alpha = 0.8, size = 1, color = "grey") +
geom_point(aes(year_left2), color = "black", size = 3.3) +
geom_point(aes(year_left2, color = group), size = 2.3) +
geom_text_repel(aes(year_left2, label = name), ) +
scale_colour_brewer(guide = FALSE, palette = "Dark2") +
scale_linetype_manual(guide = FALSE, values = c(2, 1)) +
labs(x = "Year elected",
y = "Years on office") +
theme_minimal(base_size = 10)
Result:
For the record and to address my comment on #PoGibas answer above, here's my tidyverse version:
data_transform <- data_sj %>%
mutate(year_elected_jitter = jitter(year_elected)) %>%
mutate(year_left_jitter = year_left + year_elected_jitter - year_elected + 0.01)
ggplot(data_transform, aes(year_elected, years_in_office, label = name)) +
geom_segment(aes(x = year_elected_jitter, y = 0, xend = year_left_jitter, yend = years_in_office, color = gender), size = 0.3) +
geom_text_repel(aes(year_left_jitter, label = name)) +
theme_minimal()

Resources