<- dir("meetup-data", full.names = TRUE)
dir
<- function(pic) {
check_image <- jpeg::readJPEG(pic)
img if (any(dim(img) == 3)) {
if (length(unique(img[,1,1])) == 1) {
file.remove(pic)
}
}
}
::walk(dir, check_image) purrr
The making of “We R-Ladies”
Last March Maëlle wrote a blog post “Faces of #rstats Twitter”, a great tutorial on scraping twitter photos and compiling them in a collage. This inspired a similar adventure, but for #RLadies!
We brainstormed different ways we could make this collage unique, but ultimilately landed on creating a mosaic with the R-Ladies logo.
We first wanted to use Twitter profile pics but even after launching a small campaign we realized we’d identify too few R-Ladies to fill a collage. Therefore we mixed two information sources:
- For the collage itself we decided on using Meetup data, which was made even cooler by the fact that the
meetupr
package was developed by R-Ladies in the R-Ladies Github organization!
- For the frame of the collage we chose to use the R-Ladies identified via Twitter.
library("dplyr")
## devtools::install_github("rladies/meetupr")
library("meetupr")
library("magick")
library("rtweet")
Pull in Meetup data
To get all of our current Meetups, we scrape our R-Ladies GitHub data.
<- RCurl::getURL("https://raw.githubusercontent.com/rladies/starter-kit/master/Current-Chapters.md")
doc.raw <- stringr::str_match_all(doc.raw, "www.meetup.com/(.*?)/")[[1]][,2]
meetups <- unique(meetups) meetups
We use our meetupr
package to pull profile pictures of all of our members.
If you would like to follow along, you can grab a Meetup API Key.
<- "YOUR_MEETUP_API_KEY" api_key
Here are a few small functions to get and save the Meetup photos.
<- function(i,meetup){
get_photos <- try(get_members(meetup[i], api_key), silent = TRUE)
members if(class(members)[1] != "try-error"){
%>%
members ::map(.,"photo", .null = NA) %>%
purrr::map_chr(., "photo_link", .null = NA)
purrr
}
}
<- function(photo){
save_image <- try(image_read(photo), silent = TRUE)
image if(class(image)[1] != "try-error"){
<- image %>%
image image_scale("100x100!") %>%
image_write(
paste0(
"meetup-data/",
::str_match(photo[1], "member_(.*?)\\.")[, 2],
stringr".jpg"
),format = "jpeg"
)
}
}
Grab the photos for each Meetup group.
<- purrr::map(1:length(meetups),
pics
get_photos,meetup = meetups
%>%
) unlist()
Now walk it out 💃.
::walk(pics, save_image) purrr
Remove the default avatars
There are a few photos that are the default avatar. These have a grey color with a white center. To check if this is the case, test whether the first column of pixels is fully grey.
This removed ~200 photos.
Make into a mosaic!
We use RsimMosaic
, but had to tweak a few things (there were a few pictures causing errors making the whole thing break, so I added some error catching as well as the abilitity to specify the input tile size).
## devtools::install_github("LucyMcGowan/RsimMosaic")
library("RsimMosaicLDM")
set.seed(108)
composeMosaicFromImageRandomOptim(
originalImageFileName = "RLadies_tofill.jpeg",
outputImageFileName = "art-ladies.jpg",
inputTileSize = 100,
imagesToUseInMosaic = "meetup-data",
removeTiles = TRUE,
fracLibSizeThreshold = 0.01
)
We used 9106 unique tiles to create the image using K-nearest neighbors to select the optimal match.
Pull in the Twitter data
<- search_tweets(q = '#RLadies', n = 1000, include_rts = FALSE)
dat <- users_data(dat) %>%
tweets select(screen_name, profile_image_url)
<- search_users("#RLadies", n = 1000) %>%
users select(screen_name, profile_image_url)
<- unique(rbind(tweets, users)) prof
We removed a few accounts that we know are not actually (our version of) R-Ladies accounts.
<- prof[!(prof$screen_name %in%
prof c("Junior_RLadies",
"RLadies_LF",
"WomenRLadies",
"Rstn_RLadies13",
"RLadies",
"RnRladies")
), ]
Here is a function to save the images. We are using the twitter images as the frame, so they are larger than the Meetup images.
<- function(df){
save_image <- try(image_read(sub("_normal", "", df$profile_image_url)), silent = TRUE)
image if(class(image)[1] != "try-error"){
%>%
image image_scale("500x500") %>%
image_write(paste0("tweet-data/", df$screen_name, ".jpg"), format = "jpeg")
}
}
<- split(prof, 1:nrow(prof))
users ::walk(users, save_image) purrr
Now let’s randomly read them in!
set.seed(525)
<- dir("tweet-data", full.names = TRUE)
pics <- sample(pics, length(pics)) pics
We removed a few of the chapter accounts to ensure an even number.
## we need a multiple of 4
<- which(grepl("RLadies", pics))
rladies_chapters <- pics[-rladies_chapters[1:2]] pics
Create the frame
Create the top of the frame 🎉.
<- length(pics)/4
length_size 1:length_size] %>%
pics[image_read() %>%
image_append(stack = FALSE) %>%
image_write("frame/top.jpg")
Create the bottom of the frame 👇.
+ 1):(length_size * 2)] %>%
pics[(length_size image_read() %>%
image_append(stack = FALSE) %>%
image_write("frame/bottom.jpg")
Create the left side of the frame 👈.
* 2 + 1):(length_size * 3)] %>%
pics[(length_size image_read() %>%
image_append(stack = TRUE) %>%
image_write("frame/left.jpg")
Create the right side of the frame 👉.
* 3 + 1):length(pics)] %>%
pics[(length_size image_read() %>%
image_append(stack = TRUE) %>%
image_write("frame/right.jpg")
We add small R-Ladies logos to the sides of the frame!
<- "https://raw.githubusercontent.com/rladies/starter-kit/master/logo/R-LadiesGlobal_RBG_online_LogoOnly.png"
logo_url
image_read(logo_url) %>%
image_scale("500x500+0+0") %>%
image_write("frame/logo_small.jpg")
<- image_read("art-ladies.jpg") %>%
ht %>%
image_info select(height)
<- paste0(ht, "x", ht, "+0+0") dims
Put it all together! 🎨
c("frame/bottom.jpg") %>%
image_read() %>%
image_append(stack = FALSE) %>%
image_scale(geometry = dims) %>%
image_write("frame/good_bottom.jpg")
c("frame/top.jpg") %>%
image_read() %>%
image_append(stack = FALSE) %>%
image_scale(geometry = dims) %>%
image_write("frame/good_top.jpg")
c("frame/good_top.jpg", "art-ladies.jpg",
"frame/good_bottom.jpg") %>%
image_read() %>%
image_append(stack = TRUE) %>%
image_write("test_topbottom.jpg")
<- image_read("test_topbottom.jpg") %>%
ht %>%
image_info select(height)
<- paste0(ht, "x", ht, "+0+0")
dims
c("frame/logo_small.jpg",
"frame/left.jpg",
"frame/logo_small.jpg") %>%
image_read() %>%
image_append(stack = TRUE) %>%
image_scale(geometry = dims) %>%
image_write("frame/good_left.jpg")
c("frame/logo_small.jpg",
"frame/right.jpg",
"frame/logo_small.jpg") %>%
image_read() %>%
image_append(stack = TRUE) %>%
image_scale(geometry = dims) %>%
image_write("frame/good_right.jpg")
c("frame/good_left.jpg", "test_topbottom.jpg",
"frame/good_right.jpg") %>%
image_read() %>%
image_append(stack = FALSE) %>%
image_write("we-r-ladies.png")
Attending JSM? Come see our work on display at the Data Art Show! And since participating makes us real data artists now, find below the “artsy” description of our work!
R-Ladies is a global organization focused on improving gender diversity in the R community. R is an open-source statistics programming language. We used an R-Ladies developed package, meetupr to pull several thousand profile pictures from 33 R-Ladies meetups. We then used k-nearest neighbors to optimally match each image to a pixel of the R logo. The frame is comprised of Twitter profile pictures of individuals and local chapters who used the hashtag #RLadies in the days prior to our data collection. Pictures were assembled thanks to rOpenSci’s magick package. This work symbolizes how the global R-Ladies community is composed of diverse individuals attending events and communicating about the organization, whose strengths add up and create a strong grassroots organization. The whole creation process was carried out using R, showing the diversity of the language itself, and making the most of a tool, meetupr, created by members of the R-Ladies community, further underlining the synergy we want to convey. All in all, We R-Ladies is a tribute to diversity in the R community.