-
-
Save jonocarroll/2f9490f1f5e7c82ef8b791a4b91fc9ca to your computer and use it in GitHub Desktop.
library(ggplot2) ## devtools::install_github("hadley/ggplot2) | |
library(grid) ## rasterGrob | |
library(EBImage) ## readImage (alternatively: magick::image_read) | |
library(ggthemes) ## theme_minimal | |
## ########## | |
## INDEPENDENT CODE TO BE SOURCED: | |
## ########## | |
# user-level interface to the element grob | |
my_axis = function(img, angle = 90) { | |
structure( | |
list(img=img, angle=angle), | |
class = c("element_custom", "element_blank", "element_text", "element") # inheritance test workaround | |
) | |
} | |
# returns a gTree with two children: the text label, and a rasterGrob below | |
element_grob.element_custom <- function(element, x, ...) { | |
stopifnot(length(x) == length(element$img)) | |
tag <- names(element$img) | |
# add vertical padding to leave space | |
g1 <- textGrob(paste0(tag, "\n\n\n\n\n"), x=x, rot = element$angle, vjust=0.6) | |
g2 <- mapply(rasterGrob, x=x, image=element$img[tag], | |
MoreArgs=list(vjust=0.7, interpolate=FALSE, | |
height=unit(3,"lines")), | |
SIMPLIFY=FALSE) | |
gTree(children=do.call(gList, c(g2, list(g1))), cl="custom_axis") | |
} | |
# gTrees don't know their size and ggplot would squash it, so give it room | |
grobHeight.custom_axis = heightDetails.custom_axis = function(x, ...) | |
unit(6, "lines") | |
## ########## | |
## END | |
## ########## | |
## ########## | |
## OBTAIN FLAGS: | |
## ########## | |
library(rvest) | |
## GDP per capita, top 10 countries | |
url <- "https://en.wikipedia.org/wiki/List_of_countries_by_GDP_(nominal)_per_capita" | |
html <- read_html(url) | |
gdppc <- html_table(html_nodes(html, "table")[3])[[1]][1:10,] | |
## clean up; remove non-ASCII and perform type conversions | |
gdppc$Country <- gsub("Â ", "", gdppc$Country) | |
gdppc$Rank <- iconv(gdppc$Rank, "latin1", "ASCII", sub="") | |
gdppc$Country <- iconv(gdppc$Country, "latin1", "ASCII", sub="") | |
gdppc$`US$` <- as.integer(sub(",", "", gdppc$`US$`)) | |
## flag images (yes, this processing could be done neater, I'm sure) | |
## get the 200px versions | |
flags_img <- html_nodes(html_nodes(html, "table")[3][[1]], "img")[1:10] | |
flags_url <- paste0('http://', sub('[0-9]*px', '200px', sub('\\".*$', '', sub('^.*src=\\"//', '', flags_img)))) | |
flags_name <- sub('.*(Flag_of)', '\\1', flags_url) | |
if(!dir.exists("flags")) dir.create("flags") | |
for(flag in seq_along(flags_url)) { | |
switch(Sys.info()[['sysname']], | |
Windows= {download.file(flags_url[flag], destfile=file.path("flags", paste0(flag,"_", flags_name[flag])), method="auto", mode="wb")}, | |
Linux = {download.file(flags_url[flag], destfile=file.path("flags", paste0(flag,"_", flags_name[flag])))}, | |
Darwin = {print("Not tested on Mac. Use one of the above and find out?")}) | |
} | |
## ########## | |
## END | |
## ########## | |
## load the images from filenames | |
pics <- vector(mode="list", length=npoints) | |
image.file <- dir("flags", full.names=TRUE) | |
image.file <- image.file[order(as.integer(sub("_.*","",sub("flags/","",image.file))))] | |
for(i in 1:npoints) { | |
pics[[i]] <- magick::image_read(image.file[i]) | |
# pics[[i]] <- EBImage::readImage(image.file[i]) | |
} | |
names(pics) <- sub(".svg.png","",sub(".*Flag_of_","",image.file)) | |
## create a dummy dataset | |
npoints <- length(flags_name) | |
y <- gdppc$`US$` | |
x <- names(pics) | |
dat <- data.frame(x=factor(x, levels=names(pics)), y=y) | |
## create the graph, as per normal now with @baptiste's adapted grob processing | |
## NB: #85bb65 is the color of money in the USA apparently. | |
gg <- ggplot(dat, aes(x=x, y=y/1e3L, group=1)) | |
gg <- gg + geom_bar(col="black", fill="#85bb65", stat="identity") | |
gg <- gg + scale_x_discrete() | |
gg <- gg + theme_minimal() | |
gg <- gg + scale_fill_discrete(guide=FALSE) | |
gg <- gg + theme(plot.background = element_rect(fill="grey90")) | |
gg <- gg + labs(title="GDP per capita", | |
subtitle="Top 10 countries", | |
x="", y="$US/1000", | |
caption=paste0("Source: ",url)) | |
gg <- gg + theme(axis.text.x = my_axis(pics, angle = 0), ## that's much better | |
axis.text.y = element_text(size=14), | |
axis.title.x = element_blank()) | |
gg | |
Hi Jon,
I was wondering if you have a solution to resize the images. I tried with magick::image_scale and image_resize but they did not work.
I'm not sure about the magick
issues you're both having but I have an updated method for achieving this here: https://jcarroll.com.au/2019/08/13/ggtext-for-images-as-x-axis-labels/
Thank you very much for your quick reply.
However the new method does not work for me.
I tried to run your script but when i run the line
gdppc$Country <- gsub("Â ", "", gdppc$Country)
the following error appears
Error: Assigned data gsub("Â ", "", gdppc$Country)
must be compatible with existing data.
x Existing data has 11 rows.
x Assigned data has 0 rows.
i Only vectors of size 1 are recycled.
Run rlang::last_error()
to see where the error occurred.
In addition: Warning message:
Unknown or uninitialised column: Country
.
In my case I have named list of 4 images and when i run
scale_x_discrete(name = NULL, labels = image_list_cor) I get the following erro
Error: gridtext has encountered a tag that isn't supported yet: pointer:
Only a very limited number of tags are currently supported.
Here is my code
Load images
`vp_cor <- image_read("C:/Users/jc448892/Desktop/Images/vp_cor.png")
p_cor <- image_read("C:/Users/jc448892/Desktop/Images/p_cor.png")
g_cor <- image_read("C:/Users/jc448892/Desktop/Images/g_cor.png")
vg_cor <- image_read("C:/Users/jc448892/Desktop/Images/vg_cor.png")
image_list_cor <- list(vg_cor, g_cor, p_cor,vp_cor)
names(image_list_cor) <- c("Very good", "Good", "Poor", "Very poor")
create a dummy dataset
status_cor <- pp_df %>% group_by(coral_image) %>% count
sum(status_cor$n)
status_cor <- status_cor %>% mutate(prop = round(n/73, 2))
y_cor <- status_cor$prop
x_cor <- names(image_list_cor)
dat_cor <- data.frame(x = factor(x_cor, levels=names(image_list_cor)), y=y_cor)
Make the plot
ggplot(dat_cor, aes(x= x, y=y)) +
geom_bar(col="black", fill="coral1", stat="identity") +
scale_x_discrete(name = NULL, labels = image_cor_list) +
theme(plot.background = element_rect(fill="grey90")) +
labs(title="What is the image the best represents the corals you see in the GBR?",
x="", y="Proportion") +
theme(axis.text.x = element_markdown(color = 'black, size = 7),
axis.text.y = element_text(size=14),
axis.title.x = element_blank())
`
I can't debug files from your desktop, but the Wikipedia table has changed so that code no longer applies. You'll need this change to make that part work
url <- "https://en.wikipedia.org/wiki/List_of_countries_by_GDP_(nominal)_per_capita"
html <- xml2::read_html(url)
gdppc <- html_table(html_nodes(html, "table")[2])[[1]][2:12,]
## clean up; remove non-ASCII and perform type conversions
gdppc$Country <- sub(" (more)", "", gdppc$`Country/Territory`, fixed = TRUE)
gdppc$Country <- gsub("Â ", "", gdppc$Country)
gdppc$Country <- iconv(gdppc$Country, "latin1", "ASCII", sub="")
gdppc$Country[10] <- "United States of America"
gdppc$Country[2] <- "Liechtenshein"
gdppc$`US$` <- as.integer(sub(",", "", gdppc$`World Bank[5]`))
but even then, there's something wrong with the flag images and readPNG
is complaining.
The idea of using element_markdown()
should be solid, though.
Thank you for that
I m just not sure how to store my images that are not online but on a normal directory
Should I just put the file paths as values of the character vector labels ?
for example
labels <- setNames(c("C:/Users/jc448892/Desktop/Images/very_poor.png", "C:/Users/jc448892/Desktop/Images/poor.png", "C:/Users/jc448892/Desktop/Images/good.png", "C:/Users/jc448892/Desktop/Images/very_good.png"), c("Very good", "Good", "Poor", "Very poor"
Sorry I am pretty new to R
All good I managed to do it!
Thank you very much for your patience
How do you handle spacing between images and image size? Some of the flags on my laptop look jumbled up on top of each other (horizontally). I tried resizing with magick::image_resize, but if I keep the crop factor, when plotting the image the flags just enlarge again and makes the same thing - only more pixelated.