Date: 2024-08-25
R
version: 3.5.0
*Corresponding author: matthew.malishev [at] gmail.com
This document can be found at https://github.com/darwinanddavis/UsefulCode
Same deal as Useful Code, but the second instalment because the first one has too much stuff in it and now runs slow.
Typing text animation based on typed.js
::install_github("JohnCoene/typed")
remotesrequire(typed)
typed("Hello")
typed("Emphasis word <span style ='color: red;'>with html</span>.", contentType = "html")
typed(list(shiny::h3("First sentence."), shiny::h4("Second sentence")), typeSpeed = 2)
Colorspace
require(colorspace)
hcl_palettes(plot = TRUE) # show all palettes
# https://cran.r-project.org/web/packages/colorspace/vignettes/colorspace.html
require(colorspace)
<- qualitative_hcl(4, palette = "Dark 3") # discrete
q4 <- sequential_hcl(9, "Purples 3") # continuous
s9 # for ggplot
scale_color_discrete_sequential(palette = "Purples 3", nmax = 6, order = 2:6)
# for colospace functions: hcl_palettes() %>% str hcl_palettes()['type']
Neon colour palettes
# https://www.shutterstock.com/blog/neon-color-palettes
<- c("#3B27BA", "#FF61BE", "#13CA91", "#FF9472")
neon1 <- c("#FFDEF3", "#FF61BE", "#3B55CE", "#35212A")
neon2 <- c("#FEA0FE", "#F85125", "#02B8A2", "#535EEB")
neon3 <- c("#535EEB", "#001437", "#C6BDEA", "#FFAA01")
neon4 ::show_col(c(neon1, neon2, neon3, neon4)) scales
Hexadecimal color code for transparency
See https://gist.github.com/lopspower/03fb1cc0ac9f32ef38f4.
require(colorspace)
require(stringr)
<- c("#004616", sequential_hcl(5, "Lajolla"))
colv str_sub(colv, 0, 1) <- "#66" # add alpha opac to col vector
Lighten/darken colours
require(colorspace)
"#EFEFEF" %>% lighten(0.2)
"#EFEFEF" %>% darken(0.2)
Colour gradient palettes for multi coloured lines/paths/routes
require(colorspace)
require(ggplot2)
require(dplyr)
# data
<- 100
nn <- data.frame(x = 1:nn, var1 = sample(200, nn, replace = T))
df
# option 1
<- "#f4d29f"
colp <- colorRampPalette(colors = c(colp %>% darken(0.2), colp, colp %>% lighten(0.2)))
colv <- colv(df$var1 %>% unique %>% length)
colpal
# option 2
<- sequential_hcl(df$var1 %>% unique %>% length, "Purple-Blue", power = 0, l = 50)
colpal
# plot
ggplot() + geom_line(data = df, aes(1:nrow(df), var1, color = var1), size = 1, show.legend = F, lineend = "round",
linejoin = "round") + scale_color_gradientn(colours = colpal, aesthetics = "col")
Alphanumeric hexcodes with opacity
In HTML/CSS
(browser code), the format is #RRGGBBAA with
the alpha channel as last two hexadecimal digits eg. Mapbox fill colour
for both manual colour and df variable. Otherwise, the alpha channel is
the last two digits.
Example: For 85% white, you would use #D9FFFFFF. Here 85% = “D9”
& White = “FFFFFF”
100% — FF 95% — F2 90% — E6 85% — D9 80% — CC 75% — BF 70% — B3 65% — A6
60% — 99 55% — 8C 50% — 80 45% — 73 40% — 66 35% — 59 30% — 4D 25% — 40
20% — 33 15% — 26 10% — 1A 5% — 0D 0% — 00
Change colour palette
require(circlize,reshape2,tidyr)
# use either melted df or table
<- df %>% # opt1
dtab dcast(var1~var2, fill = 0) %>%
melt() %>%
uncount(value)
<- df %>% table() # opt2
dtab
# plot pars
par(mar = rep(2, 4),mfrow = c(1, 1))
circos.clear()
circos.par(start.degree = -180, # chord setup
gap.degree = 2, track.margin = c(-0.1, 0.1),
points.overflow.warning = F,
track.height = 0.2)
circos.par(cell.padding =c(0.02, 0, 0.02, 0))
# plot
chordDiagram(dtab,
grid.col = colpal,
transparency = 0.3,
directional = 1, # 1 = link origin is from sectors
diffHeight = -0.05,
# link.border = "#FFFFFF",
annotationTrack = c("grid"
# ,"name" # to check name placment
),annotationTrackHeight = c(0.05, 0.1),
big.gap = 5, small.gap = 2, # gaps between sectors
link.sort = F, # order links are drawn
link.decreasing = T, # define link overlap
link.largest.ontop = T,
preAllocateTracks = list(track.height = 0.1)
# symmetric = F
# scale = F # weight links equally
)
Add custom labels
# after initiating plot (see above)
<- 0.85
ylim <- 0.6
im circos.track(track.index = 1,
panel.fun = function(x,y){ # add text labels
= get.cell.meta.data("sector.numeric.index")
sector_index circos.text(x = CELL_META$xcenter,
y = ylim,
# remove medal labels
labels = CELL_META$sector.index,
facing = "clockwise", niceFacing = T, cex = im, col = col_lab
)}bg.border = NA) # set bg to NA ,
Use custom raster images as sector labels
require(magick)
# create img labels
<- "img.png"
img <- function(img){
img_convert <- img %>% magick::image_read() %>% as.raster() # convert img to raster layer
imgr == "#002163ff"] <- col_lab # change main img color
imgr[imgr %>% return()
imgr
}
<- c(as.list(rep(img,dtab$var1 %>% unique %>% length))) # match imgs to no of chord sectors
imgl <- lapply(imgl,img_convert) # apply convert to raster func
imglist <- c(as.list(rep(NA,n)),imglist) # optional: rm first n images from sectors
imgtab names(imgtab) <- get.all.sector.index() # get names from plot sector indices
<- 3
ylim <- "7mm"
im circos.track(track.index = 1,
for(si in seq_along(get.all.sector.index())){ # apply event img to each sector index
circos.raster(x = CELL_META$xcenter, #0.5,
y = ylim,
sector.index = get.all.sector.index()[si],
image = imgtab[[si]], # add image
width = im, height = im,
facing = "downward")
},panel.fun = function(x,y){ # add text labels
circos.text(x = CELL_META$xcenter, # center label
y = ylim/3,
labels = CELL_META$sector.index,
facing = "clockwise", niceFacing = T,
cex = 0.5, col = col_lab,
adj = c(0, 0.5) # nudge xy position
)}bg.border = NA) # set bg to NA ,
D3 and leaflet
# devtools::install_github('jcheng5/d3scatter')
require(pacman)
p_load(d3scatter, crosstalk, leaflet, tibble, httpuv)
# converting df to crosstalk df
<- SharedData$new(df)
sd $data()[, "var1"] # access data.frame
sd
# load data
<- SharedData$new(quakes[sample(nrow(quakes), 100), ])
sd # sd$data() %>% head
bscols(widths = c(12, 6, 6), filter_slider("stations", "Stations", sd, ~stations), leaflet(sd, width = "100%",
height = 400) %>% addTiles() %>% addCircleMarkers(lng = sd$data()[, "long"], lat = sd$data()[, "lat"],
stroke = F, fill = T, color = "red", fillOpacity = 0.5, radius = ~mag + 2, label = ~paste0("Depth: ",
as.character(depth))), d3scatter(sd, width = "100%", height = 400, ~mag, ~depth, color = ~stations))
Add dropdown menu to crosstalk
bscols(widths = c(12, 6, 6), filter_select(id = "stations", label = "Stations", sharedData = sd, group = ~stations))
Convert R code to D3 https://rstudio.github.io/r2d3/articles/visualization_options.html
Create calendar plot
# https://rstudio.github.io/r2d3/articles/gallery/calendar/ install.packages('r2d3')
require(r2d3)
require(readr)
require(dplyr)
require(colorspace)
require(scales)
require(stringr)
# col pal
<- "PuBuGn" # seq
col <- "Tropic" # diverge
col2
# seq
<- sequential_hcl(12, col)
pal # pal %>% show_col(borders = NA,labels=F)
paste0("\"", pal, "\"") %>% cat(sep = ",")
"#004533","#005C4E","#00726F","#008795","#0095B5","#56A0C8","#8FACD6","#B6BAE0","#D2CAE7","#E6DAEE","#F5EAF5","#FFF7FD"
# diverge
<- diverge_hcl(12, col2)
pal # pal %>% show_col(borders = NA,labels=F) paste0(''',pal,''') %>% cat(sep=',')
<- read_csv("https://raw.githubusercontent.com/rstudio/r2d3/master/vignettes/gallery/calendar/dji-latest.csv")
cal
r2d3(data = cal, d3_version = 4, container = "div", options = list(start = 2006, end = 2011), script = "calendar.js")
Raindrop D3 animate chart
# library(d3rain)
%>% d3rain(var_category, var_numeric, toolTip = var_colour) %>% drip_settings(dripSequence = "iterate",
df ease = "bounce", jitterWidth = 20, dripSpeed = 1000, dripFill = colpal) %>% chart_settings(fontFamily = font,
yAxisTickLocation = "left")
rCharts
* Bubble
* Scatter
* + more
::p_load(rCharts)
pacman= hPlot(Pulse ~ Height, data = MASS::survey, type = "bubble", group = "Sex", size = "Age", radius = 6,
h4 group.na = "Not Available")
$chart(zoomType = "xy")
h4$exporting(enabled = F)
h4# h4$print(include_assets=T) # print d3js output
h4
Reversing order of rows in dataframe/entire df
# df = data.frame
require(tidyverse)
%>% map_df(rev) df
Visualise data structure as tree
# explore package
require(DataExplorer)
require(palmerpenguins)
<- penguins
p plot_str(p)
Convert df row values to columns and lengthen df (ideal for tables/matrix inputs)
# convert each distinct value in var2 into new column while maintaining var1
require(reshape2)
require(tidyr)
%>% dcast(var1 ~ var2, fill = 0) # fill NAs with 0
df %>% dcast(. ~ var1, fill = NA, drop = T) # convert rows to cols for one variable
df
%>% dcast(. ~ factor(var1, levels = unique(var1)), fill = NA, drop = T) # retain row order when converting to cols
df
%>% melt() %>% tidyr::uncount(value) # lengthen df by each row value
df
# transpose
%>% t %>% data.frame df
Create custom df from existing data
# no need to rename
<- with(world.cities, data.frame( # //maps
latlon_data "city" = name,"country" = country.etc,"lat" = lat,"lon" = long,"population" = pop)
)
Convert df rows to cols and rename
%>% as_tibble(rownames = "row_names") %>% # convert cols to rows
df rename("year" = 1,"percent" = 2) %>% na.omit()
Select df cols based on string
%>% select(contains("Lat"))
df %>% select(starts_with("Lat")) df
Rename df with pipe
%>% # df of two cols
df `colnames<-`(c("A","B"))
# rename directly with 'select'
%>%
df select(Lat, Lon, "Site" = `Site name`)
Split df into multiple dfs based on row value
# create new col with rownames to split by (can also below use for splitting by single row)
<- c(rep("A", 7), rep("B", 6), rep("C", 5))
ss
<- df %>% mutate(Split = ss)
df1
%>% split(df$Split) # split by each unique element in df$Split df1
dplyr
basicsrequire(dplyr,gapminder)
::p_load(gapminder)
pacman
# mutate
<- mutate(gapminder,
africa_ranked "African" = continent == "Africa",
"RankPop" = rank(desc(pop))
)
# summarise data into one line
%>%
gapminder summarise("MinYear" = min(year,na.rm = T),
"MaxYear" = max(year),
"CountryCount" = n_distinct(country),
"Counts" = n()
)
%>%
gapminder summarise(median(lifeExp))
# group by
# vari = response var
# var1-var3 = conditional vars that will be considered in grouping
%>%
df group_by(var1,var2,var3) %>%
summarise("total" = vari %>% sum)
# plot will be var1 (x) vari (y), fill/group (var2), facet (var3)
%>%
gapminder group_by(continent) %>%
summarise(median(lifeExp))
# group by continent and filter by year
%>%
gapminder group_by("Continent" = continent) %>%
filter(year == 1992) %>%
summarise(LifeExpect = median(lifeExp)) -> life_cont_1992
# rename specific cols
%>%
df rename(.cols = 2:5, # only rename these cols
"Country" = 2,
"Gold" = 3,
"Silver" = 4,
"Bronze" = 5)
# replace case when values based on numeric range
%>%
df mutate(var1 = case_when(
between(var2, 1, 5) ~ "A",
between(var2, 6, 10) ~ "B",
~ var1)
T
)# classic case when
%>%
df mutate_at("var1",funs(
case_when(var1 == 1 ~ "alt1",
== 2 ~ "alt2",
var1 ~ "alt3")))
T
# get distinct count per grouped var
%>% group_by(var1) %>%
df summarise(n = n_distinct(var2))
# count instances
%>% group_by(var1) %>% count(var2) df
Execute unfriendly pipe functions inline in pipes
require(palmerpenguins)
require(dplyr)
<- penguins
p
# %T>%
%T>% glimpse %>% select(island)
p
# with()
%>% with(lm(body_mass_g ~ flipper_length_mm)) %>% summary()
p
# %$% when var on lhs is undefined
require(magrittr)
data.frame(z = rnorm(100)) %$% ts.plot(z)
Apply function easily using mutate_at
# eg 1
%>% mutate_at("var1", ~str_replace_all(., " ", "<br>"))
df
# eg 2
%>% mutate_at("layer", ~replace(., is.nan(.), 0)) df
Expand/fill df by number of repeat instances
require(tidyr)
%>% select(v1,v2,v3) %>%
df melt() %>%
::uncount(value) %>% # expand df by no. of instances (value from melted table)
tidyrselect(variable,v1) # reorder for colpal
Arrange df col by custom order
<- c("A","C","B") # custom order
var_levels
# opt 1
%>%
df filter(var1 %in% var_levels) %>%
arrange(var1 = factor(var1,levels = var_levels))
# with mutate
%>%
df mutate("var1" = factor(var1, levels = var_levels)) %>%
arrange(var1)
# get top n value based on wt variable
# also works for sfc
%>%
df group_by(country) %>% # optional
top_n(n = 5, wt = area) # get top 5 geometries bassed on area
Remove unwanted df row using string arg
%>% slice(-str_which(var1, "Unwanted point")) df
Separate char values within df row into separate columns
%>%
df ::separate(col = "ColumnA",
tidyrinto = c("lon", "lat", "elev"), sep = " ", remove = T) %>% # separate char into individual columns
mutate_all(as.numeric)
Get distinct values across multiple columns
# distinct
%>% distinct(v1, v2, v3, .keep_all = T)
df
# non-distinct only
%>% group_by(v1, v2, v3) %>% filter(n() > 1)
df
# exclude any non-distinct
%>% group_by(v1, v2, v3) %>% filter(n() == 1)
df
# base method
!duplicated(df[1:3]), ]
df[!duplicated(df[c("var1", "var2"), ]), ] df[
Split one col into two separate cols and mutate (mutate and mutate_at in one line)
%>% mutate(Year = var1 %>% str_split_fixed("-", Inf) %>% .[, 1], Month = var1 %>% str_split_fixed("-",
df Inf) %>% .[, 2]) %>% mutate_at(c("Year", "Month"), as.numeric)
Apply multiple mutate_at
functions
# use list for multiple functions of either one or multiple cols
%>% mutate_at(5:7, list(
df ~str_remove_all(., "string1|string2|"), # func1
~str_split_fixed(., "/", n = 2) # func2
))
# Apply multiple `mutate` functions using select or contains
%>% select(contains("string1")) %>% # select var
df mutate_all(~str_remove_all(., "string1|string2|") %>% as.numeric) # apply funcs
Mutate multiple vars
require(lubridate)
%>%
df mutate(AM = timevar %>% am,
Hour = timevar %>% hour,
Day = timevar %>% day,
Month = timevar %>% month,
Meridian = timevar %>%
round_date(unit = "hour") %>% # round off hour
format("%I") %>% as.numeric) # get 12 hour time
Seperate/detect df into categories based on row name
%>% filter(Restaurants %>% str_detect("Market|Water")) df
Using ifelse
with mutate
%>% mutate_at("var1", ~ifelse(. > 0.5, "this", "that")) df
Gather/melt data into groups/stacks
%>% gather(key = "Country", value = "value")
df
# reorder factors to df
Count instances of grouped data
%>% group_by(date) %>% tally(var1 * var2, name = "new_var1") df
Lengthen df by column variables
(see https://haswal.github.io/pivot/ for good visual
example)
# select vars you want and lengthen by 'var_n' e.g. var_n = year
%>% tidyr::pivot_longer(cols = c(var1, var2, var3)) %>% select(var_n, name, value)
df
# year name value <chr> <chr> <int> A var1 7 A var2 4 A var3 8 B var1 3 B var2 10 B var3 2 C var1 6 C
# var2 1 C var3 9
Separate/split df into individual dfs based on repeating value in a column
# Name Description IDS geometry
# <fct> <fct> <fct> <GEOMETRY [°]>
# 1 2023 "" 0 LINESTRING Z (12.4822 41.89…
# 2 Rome "" 1 POINT Z (12.4822 41.8967 0)
# 3 Grosse… "" 2 POINT Z (11.11239 42.76355 …
# 4 San Gi… "" 3 POINT Z (11.04341 43.4672 0)
# 5 Floren… "" 0 POINT Z (11.25767 43.76997 …
# 6 Interl… "" 1 POINT Z (11.25767 43.76997 …
%>%
df mutate(trips = cumsum(IDS == 0)) %>% # create new helper col between every instance of 0 in IDS
group_by(trips) %>% # group by this helper col
group_split() %>% # spit into individual dfs
set_names(LETTERS[1:length(.)]) %>% # create new names for each df
imap(~write_csv(.x, paste0(.y, ".csv"))) # save each object '.x' as '.y' to dir as csv based on their list names
Convert character class to numeric (ideal when creating colour palettes to turn string cols in df to numeric)
require(dplyr)
set.seed(12)
<- data.frame(X = LETTERS[sample(20)])
df <- df$X %>% unlist %>% as.factor %>% as.integer # converts to numbers
int_vec
int_vec$I <- int_vec
df df
Pipe vector to multiple arguments
require(dplyr)
# as list
Sys.time() %>% list(format(., c("%y-%m", "%Y-%m", "%Y-%m")))
# use curly braces to keep original class
Sys.time() %>% {
format(., c("%y-%m", "%Y-%m", "%Y-%m"))
}
Merge/combine/match/fill rows of two data frames based on value and retain original number of rows
merge(a, b, by = "ID", sort = F)
Access vars in df/tibble that failed to load eg. time series that return NA
# as tibble
%>% attr("problems") df
Search available methods for package
showMethods("coerce", classes = "sf")
methods(st_as_sf)
`?`(methods)
Get object size
%>% object.size() df
Assign multiple values to multiple LHS objects
require(zeallot)
<- c(1, 2, 3, 4)
values c(a, b) %<-% values[c(2, 4)] # assign `a` and `b`
c(a, b) %<-% c(1, "A") # returns both as char
Repeat vector n times (each)
rep(c("A", "B", "C"), 10)
rep(c("A", "B", "C"), each = 10) # repeat each instance
Split vector into equal chunks
<- 1:100
ids <- 20
nn split(ids, ceiling(seq_along(ids)/nn))
Find difference between vectors
1:10] %>% setdiff(LETTERS[1:5]) LETTERS[
Get order of vector
$id %>% order df
Access files on Google Drive
Common commands: find, ls, mv, cp, mkdir, rm
http://googledrive.tidyverse.org/
require(googledrive)
drive_find(n_max = 10) # set output limits
drive_find(type = "folder")
drive_get("~/Data/eli/feb.csv")
Write html code to dir
<- "<!DOCTYPE html>
code <html>
<body>
<h1>My First Heading</h1>
<p>My first paragraph.</p>
</body>
</html>"
<- paste(as.character(code), collapse = "\n")
code
wte.table(code, file = "/Users/code.html", quote = FALSE, col.names = FALSE, row.names = FALSE)
Extracting multiple nodes/range of nodes at once
# require(dplyr,rvest,xml2,readr,magrittr)
<- "https://www.postholer.com/databook/Appalachian-Trail/3"
url %>% read_html() %>% html_nodes("table") %>% .[1:3] # get range (node)
url %>% read_html() %>% html_nodes("table") %>% .[[1]] # get individual (nodeset) url
Extract values within nested nodes
require(purrr)
require(dplyr)
require(XML)
<- doc # gpx, xml, or XMLInternalDocument class
doc getNodeSet(doc, path = "//parentnode") %>% purrr::map(xpathSApply, path = "child1/child2/child3", xmlValue) # extract values within child 3 node (three nodes deep) and separate into individual lists
# option 2
getNodeSet(gpx2, path = "//folder") %>% lapply(function(x) {
list(NODE1 = x %>% xpathSApply(path = "placemark/track/coord", xmlValue))
%>% bind_rows() })
Extract css class/id using xpath
require(rvest)
<- "\"class_large\"" # use css class (note quote escape)
nid
%>% read_html %>% html_nodes(xpath = paste0("//*[@id=", nid, "]")) url
Recolor png/svg
require(magick)
%>% image_colorize(color = "#EFEFEF", opacity = 70) png
Read text from image
require(magick)
require(tesseract)
"test.png" %>% image_read() %>% image_ocr()
# return df with new word per col and associated bbox
"test.png" %>% image_read() %>% image_ocr_data()
Clickme, NVD3, Polychart, rCharts, Rickshaw, and xCharts in
R
.
Link to
collated Github page. ### Javascript in R
Crosstalk, plotly, setting up widgets, customing JS in R, mapping
with JS, d3, etc
https://book.javascript-for-r.com/widgets-intro-intro.html#widgets-intro-crosstalk
Interactive label options and custom tiles
require(leaflet)
require(dplyr)
require(geosphere)
require(htmltools)
<- c(7.369722, 12.354722)
setview <- data.frame(name = c("Melbourne", "Atlanta"), lat = c(-37.813629, 33.748997), lon = c(144.963058,
mp -84.387985))
<- matrix(c(mp[, "lon"], mp[, "lat"]), ncol = 2)
latlon_matrix <- "http://a.sm.mapstack.stamen.com/(positron,(mapbox-water,$776699[hsl-color]),(buildings,$002bff[hsl-color]),(parks,$6abb9d[hsl-color]))/{z}/{x}/{y}.png"
custom_tile <- "#4C3661"
colv <- 0.5
opac <- mp$name
site_names <- "Debunking Flat Earth theory 101"
ttl <- "https://github.com/darwinanddavis" # weblink
weblink <- "My github"
webname <- paste0("<b><a href=", weblink, ">", webname, "</a></b>")
href <- paste(sep = "<br/>", href, "606 5th Ave. S", "Seattle, WA 98138")
text_label # label options
<- labelOptions(textsize = "20px", opacity = 0.5, offset = c(0, 0))
marker_label_opt <- labelOptions(noHide = T, direction = "top", textOnly = T, opacity = 1, offset = c(0,
text_label_opt 0))
# title
<- tags$style(HTML(".leaflet-control.map-title {
tag.map.title transform: translate(-50%,20%);
position: fixed !important;
left: 50%;
text-align: center;
padding-left: 10px;
padding-right: 10px;
background: white; opacity: 0.7;
font-weight: bold;
font-size: 25px;
}"))
<- tags$div(tag.map.title, HTML(ttl))
title
# map
<- gcIntermediate(latlon_matrix[1, ], latlon_matrix[2, ], n = 100, addStartEnd = T, sp = T) %>% leaflet() %>%
map setView(setview[2], setview[1], zoom = 3) %>% addTiles(custom_tile) %>% addCircleMarkers(mp[, "lon"],
"lat"], radius = 10, stroke = TRUE, weight = 3, opacity = opac, color = colv, fillColor = colv,
mp[, label = paste(site_names), labelOptions = marker_label_opt) %>% addPolylines(color = colv, opacity = opac) %>%
addPopups(-122.327298, 47.597131, text_label, options = popupOptions(closeButton = FALSE, textOnly = T)) %>%
addLabelOnlyMarkers(setview[2], setview[1], label = text_label, labelOptions = text_label_opt) %>%
addControl("@darwinanddavis", position = "topright") %>% addControl(title, position = "topleft",
className = "map-title")
map
Add multiple layers at once (uses loop)
# https://stackoverflow.com/questions/38701359/grouped-layer-control-in-leaflet-r
# function to plot a map with layer selection
<- function() {
map_layers
# number of groups
<- n_distinct(quakes$groups)
k
# base map
<- leaflet() %>% addProviderTiles(providers$CartoDB.Positron)
map
# loop through all groups and add a layer one at a time
for (i in 1:k) {
<- map %>% addCircleMarkers(data = quakes %>% filter(groups == i), group = as.character(i),
map lng = ~long, lat = ~lat, radius = 1)
}
# create layer control
%>% addLayersControl(overlayGroups = c(1:k), options = layersControlOptions(collapsed = FALSE)) %>%
map hideGroup(as.character(c(2:k))) #hide all groups except the 1st one
}
# plot the map
map_layers()
Continuous colour legend
# set colpal
<- sequential_hcl(6, "ag_GrnYl")
colpal <- colpal[1]
colv ::show_col(colpal)
scales
# match data to colpal
<- colorNumeric(palette = colpal, domain = df$var1)
pal
<- leaflet() %>% setView(setview[1], setview[2], zoom = 2) %>% addTiles(custom_tile) %>% addPolygons() %>%
map addLegend(pal = pal, values = df$var1, position = "bottomright", title = "Legend title", opacity = opac)
Avoiding wrap around paths/lines in leaflet maps
<- c(df[, "lon"], df[, "lat"]) %>% matrix(ncol = 2) # convert df to matrix
latlon_matrix <- gcIntermediate(latlon_matrix[1, ], latlon_matrix, n = 100, breakAtDateLine = T, addStartEnd = T,
inter1 sp = T) %>% st_as_sf() # converge all paths to first latlon postion
<- sites_flight %>% st_as_sf(coords = c("lon", "lat"), crs = 4326)
sites_flight
ggplot() + geom_sf(data = inter1)
Transpose list (flip list elements)
<- list(1:2, 3:4, 5:7, 8:10)
l
l<- data.table::transpose(l)
b b
lengths
for getting length of list indices
require(dplyr)
= list(rep(list(sample(50, replace = T)), 5))
ls %>% length
ls %>% lengths
ls lapply(ls, lengths)
Split list into smaller sublists
= rep(list(1:5), 6)
la names(la) = rep(LETTERS[1:3], 2)
<- length(unique(names(la)))
u <- length(la)/u
n split(la, rep(1:n, each = u))
# for when list has two elements in the name that change create a list of 10 letters with 5 lists in
# each
<- rep(list(1:10), 5) %>% pmap(list)
big_list names(big_list) <- LETTERS[1:10]
# to index the upper list
"B"] # 1
big_list[pluck(big_list, "B") # 2
# to index the sublists
map(big_list["B"], 3) # 1
<- list() # 2
bb_final for (i in 1:10) {
<- big_list["B"]
bb <- c(bb_final, bb)
bb_final
} bb_final
Fill list elements with NAs to match length of longest element
# https://stackoverflow.com/questions/34570860/add-nas-to-make-all-list-elements-equal-length
# for single index list
set.seed(1)
= replicate(5, sample(1:100, 10), simplify = FALSE)
ls names(ls) = LETTERS[1:length(ls)]
lapply(ls, `length<-`, max(lengths(ls)))
# for sublists
= list(replicate(5, sample(1:100, 10), simplify = FALSE))
ls = 20
n.ticks = function(x) {
fillvec = lapply(x, `length<-`, n.ticks) # fill remaining vec with NAs to match total length
nv rapply(nv, f = function(x) ifelse(is.na(x), 0, x), how = "replace") # replace NAs with 0s
}lapply(ls, fillvec) # apply fillvec to list
Access list elements in loop by name/string
set.seed(12)
# inputs
<- 5
time <- 1:10
time_vec <- runif(10)
a_vec <- 1:10
beta1_vec <- 11:20
beta2_vec <- list(a_vec,beta1_vec,beta2_vec)
param_vec names(param_vec) <-c("alpha","beta1","beta2")
<- sapply(rep(NA,length(param_vec)),list) # create empty final params vector
params names(params) <- names(param_vec)
# select parameter to test
<- "alpha" #beta1 #beta2
param_input
# run from here -----------------------------------------------------------
for(time in time_vec) {
= param_vec[`param_input`][[1]][time] # get parameter value by name
p_in # create new list of with updated param_input value
<- c(param_vec[-which(names(param_vec)==param_input)], # everything but param_input
params param_input = p_in # param_input
)# get just the latest value
# remove this if you want all list elements
<- sapply(params,function(x) x[1]) %>% as.numeric
params # rename this new list
names(params) <- c(names(param_vec)[-which(names(param_vec)==param_input)], # everything but param_input
param_input
)print(params)
# end loop
} # each list element changes depending on user input params
Apply function to nested lists
= list(replicate(5, sample(1:100, 10), simplify = FALSE))
ls %>% glimpse
ls lapply(ls, lapply, mean)
lapply(ls, sapply, mean) # return as one list
rapply(ls, mean, how = "unlist") # unlist, replace, or list
Apply function to list (without lapply)
require(lubridate)
<- c(now(), now() %>% rollback(), now() %>% rollback(roll_to_first = T))
ft <- ft %>% list(isoyear(.), epiyear(.), wday(.), wday(., label = T), qday(.), week(.), semester(.),
ftl am(.), pm(.))
names(ftl) <- c("data", "international standard date-time code (ISO 8601)", "epidemiological year", "weekday",
"weekday as label", "day into yearly quarter", "week of year", "semester", "AM?", "PM?")
Unlist and bind list elements together
%>% # list of sf objects
ll ::map(st_collection_extract,"LINESTRING") %>% # get just linestring
purrr::list.rbind() # unlist these and bind together rlist
Convert list to df
require(plyr)
%>% plyr::ldply(tibble) # convert list to df ls
Conditional rules for lists
require(purrr)
<- ls %>% map(class) == "list" # check if elements are class list
cond %>% unique()
cond %>% keep(cond) # retain just these entries
ls %>% vec_depth # check how far list indexes ls
Rename list/matrices with pipe/dplyr
# without pipe
names(ls) <- LETTERS[1:4]
require(purrr)
require(magrittr)
# option 1
%>% map(matrix, ncol=2) %>% # turn elements into matrices
ls map(`colnames <-`, c("X","Y"))
# option 2
%>% map(matrix, ncol=2) %>% # turn elements into
ls map(magrittr::set_colnames, c("X","Y"))
Filter lists by values using pipe/map
require(purrr)
require(magrittr)
<- ls %>% purrr::map(matrix, ncol = 2) %>% purrr::map(magrittr::set_colnames, c("X", "Y")) # convert list to matrix
df
%>% purrr::map(~.[, "Y"] > 150) # return logical numeric
df
%>% purrr::map(~. > 150) # return logical matrix
df
%>% purrr::map(~.[.[, "X"] < 30], drop = T) # return numeric df
Find/detect list elements
%>% detect(is.matrix)
ls %>% every(is.matrix) ls
Find common terms among vectors/lists
intersect(intersect(l1$var1, l2$var1), l3$var1) %>% unique
Reduce(intersect, list(l1$var1, l2$var1, l3$var1)) %>% unique
Step through list indices/depths using map(c())
require(purrr)
%>%
ls ::map("results") %>% # access data index
purrr::flatten() %>% # remove inner list if necessary
purrr::map(c("geometry","location","lat")) %>% # use c() to step through each index consecutively
purrr unlist
Reorder list
# use other vector/df to index list
$id]
ls[df
# alphabetise names if not already
order(names(ls))] ls[
Convert list to df
<- list(A = list(sample(10), list(sample(20))), B = list(sample(10)), C = list(sample(10)))
ls
# convert list w mutiple indices to wide df
%>% purrr::map(unlist) %>% rlist::list.rbind() %>% tibble %>% map_df(data.frame)
ls
# convert list w mutiple indices to long df
%>% purrr::map(unlist) %>% tibble %>% map_df(data.frame) ls
Split list by nth element
# option 1
<- list("splithere", "TEXT 1 TEXT 1", "TEXT 1", "splithere", "TEXT 2 TEXT 2", "splithere",
main_list "TEXT 3")
# Define the separate vector of defined integers called 'years_vector'
<- c(2010, 2011, 2012, 2013)
years_vector <- "splithere"
split_by
# Function to split the list based on the key string phrase every nth element
<- function(main_list, key_phrase, years_vector) {
split_list_by_key_phrase # Identify the indices of the key_phrase in the main_list
<- which(main_list == key_phrase)
key_indices
# Initialize an empty list to store the separate smaller lists
<- list()
result_lists
# Split the main_list based on the key_phrase and nth element defined by years_vector
for (i in seq_along(key_indices)) {
<- key_indices[i]
start_idx <- ifelse(i < length(key_indices), key_indices[i + 1] - 1, length(main_list))
end_idx
# Determine the corresponding year from years_vector
<- years_vector[i]
year
# Extract the sublist and remove the 'splithere' string
<- main_list[start_idx:end_idx]
sublist <- sublist[sublist != key_phrase]
sublist
# Name the sublist with the corresponding year
names(sublist) <- year
length(result_lists) + 1]] <- sublist
result_lists[[
}
return(result_lists)
}
# Call the function with the main list, key phrase 'splithere', and the separate vector of defined
# integers (years_vector)
<- split_list_by_key_phrase(main_list, split_by, years_vector)
result_lists
# Print the separate smaller lists with named elements and 'splithere' string removed
print(result_lists)
# option 2 (basic)
require(stringr)
%>% strsplit(split_by) main_list
# benchmark
# devtools::install_github('olafmersmann/microbenchmarkCore')
# devtools::install_github('olafmersmann/microbenchmark')
require(microbenchmark)
<- microbenchmark(list = list(b1 = {
mbm # first operation
b2 = {
}, # second operation
}))# print results
mbm autoplot(mbm) # plot results
Hand drawn plotting using roughViz.js
. Link to package
page.
# install.packages('remotes') remotes::install_github('XiangyunHuang/roughviz')
require(roughviz)
Read in csv data sources directly from web
# link to raw csv link on e.g. github
require(readr)
<- "https://raw.githubusercontent.com/plotly/datasets/master/2011_february_aa_flight_paths.csv"
url <- read_csv(url) flights
Read in tab-delim .txt files
"data.txt" %>% read_delim(delim = "\t")
Rename files in dir
# useful for removing string sections or modifying file extension
file.rename(from = here::here() %>% list.files(full.names = T), to = paste0(here::here(), "img/", new_names))
Read multiple dir files at once and combine
%>% list.files(full.names = T) %>%
dir lapply(st_read) %>% # read in as list
::list.rbind() # rbind lists into df rlist
<- "algae"
resource_type # this regex expression
list.files(pattern = paste0("^", resource_type, "_[0,5]{1}_[0-9]{1,2}_hostpop50_predpop", "[0-9]{1,3}_rep[1-5]{1}\\.R$"))
# returns this begins with resource_type, either 0 or 5 as one integer, 0 to 9 as either one or two
# integers, 0 to 9 as one to three integers, and 1 to 5 as one integer
"algae_0_5_hostpop50_predpop5_rep1.R"
"algae_5_20_hostpop50_predpop30_rep2.R"
"algae_0_15_hostpop50_predpop150_rep5.R"
GUI for regex (2021)
RegExplain
https://www.garrickadenbuie.com/project/regexplain/
::install_github("gadenbuie/regexplain")
devtoolssource("https://install-github.me/gadenbuie/regexplain")
plotly
HTML widget with plotly and crosstalk
require(pacman)
p_load(plotly,tidyr,crosstalk)
<- gather(mpg, variable, value, -c(year,cyl)) # data source
m <- highlight_key(m, ~variable) # var to highlight
msd <- ggplot(m, aes(factor(year), value)) + # ggplot obj
gg geom_jitter(alpha = 0.3) +
labs(x = "Year") +
theme_classic()
# create layout
bscols(
widths = c(11, rep(5,2)), # max = 12
filter_select("id", "Select a variable", msd, ~variable, multiple = F), # dropdown menu
ggplotly(gg, dynamicTicks = "y") %>%
layout(margin = list(l = 30)),
plot_ly(msd, x = ~jitter(cyl), y = ~value, alpha = ~cyl, linetype = NULL,
mode = "markers",
hoverinfo = "text",
text = ~paste0("Cyl: ", round(cyl),
"\n",variable,": ", value,
"\nYear: ", year)
%>% # interactive vars
) add_markers(alpha = 0.3) %>%
layout(xaxis = list(showgrid = F, # general plot params
title = "Cylinder"),
yaxis = list(showgrid = F)
)
)
# example 2 with changing output margins to fill browser
require(htmltools)
# title
<- tags$html(
plotbg HTML("<body style=\"background-color: black;\"></body>"))
<- "detritus"
resource_type <- readr::read_csv("https://raw.githubusercontent.com/darwinanddavis/mybio/master/data/memi_df.csv")
memi_df <- data.frame(memi_df)
memi_df %>% str
memi_df # heatmap -----------------------------------------------------------------
require(viridis)
require(ggthemes)
require(plotly)
<- c("skip30","skip60","skip90","skip120")
me_day_vec <- as.factor(c("Skip~30", "Skip~60", "Skip~90", "Skip120")) # char vec for labelling facets
me_day_names <- ""
ttl = ""
subttl <- "Time (days)"
xlab <- "Control intensity"
ylab # turn names into function for labeller for facets
<- c("No control","50%", "75%", "90%", "95%", "99%")
me_im_names <- memi_df[,"Cercs"]
dens <- memi_df[,"ControlImpact"]
yy <- memi_df[,"Time"]
xx <- memi_df[,"ControlDay"]
facet1 <- ggplot(memi_df,aes(x=xx,y=yy,fill=dens)) +
p geom_tile(colour= "gray",size=0.01, width=2, linetype = 0) +
scale_fill_viridis(name="Density",option ="magma")
<- p + facet_wrap(~ ControlDay_names,nrow=length(me_day_names), ncol=1, drop= F, labeller=label_parsed) # use for adding facet labels
p # p <-p + facet_wrap(facet1, nrow=3, ncol=1, drop= F)
<- p + scale_y_continuous(breaks = unique(yy), labels = me_im_names, trans = "reverse")
p <- p + scale_x_continuous(breaks = seq(0,max(xx),30), expand =c(0,2))
p <- p + geom_segment(aes(x = 152, xend = 152, y= 4, yend= 4),
p arrow=arrow(length=unit(0.2,"cm")))
<- p + theme_calc() +
p theme(text = element_text(size=18)) +
# labs(title= paste0("Density of ",ttl, " by ",subttl), y=ylab, x=xlab) +
labs(title = paste0("\n","\n",ttl), y=ylab, x=xlab,size=3) +
theme(plot.title = element_text(vjust=-7)) +
theme(legend.position = "bottom",legend.direction = "horizontal") +
theme(legend.text = element_text(size=12)) +
theme(plot.background = element_rect(fill = "black")) +
::theme_transparent()
ggpubr# plot_it_gg("black","white")
<- list(
m t = 100,
r = 1,
b = 1,
l = 1,
padding = 4
)
<- ggplotly(p)
p
require(htmlwidgets)
<- p %>%
h layout(
plot_bgcolor = 'black',
paper_bgcolor = 'black',
font = list(color = 'black'),
autosize = T, margin=m) %>%
sizingPolicy(padding = 0, browser.fill = TRUE,plotbg)
Crosstalk example 2
# time series plotly
::p_load(dplyr, lubridate, ggplot2, plotly, gridExtra, plyr, ggthemes)
pacman# install.packages('crosstalk')
library(crosstalk)
# load mock data
<- readr::read_csv("/Users/malishev/Documents/Data/time_series/call_activity/call_activity.csv")
df <- seq(min(df$Date), max(df$Date), length.out = length(df$Date))
xinter
# plot data
<- ggplot() + geom_vline(mapping = NULL, xintercept = xinter, colour = "grey80", size = 0.03) + geom_point(data = df,
p aes(Date, Hour, color = Person, size = Calls)) + scale_y_continuous(limits = c(1, 23)) + scale_x_datetime(date_breaks = "1 week",
date_minor_breaks = "1 day", date_labels = "%D") + theme(axis.text.x = element_text(angle = 45)) +
labs(title = "Calls per hour of day", x = "Date (M/D/Y)", y = "Hour of day") + theme(panel.border = element_blank(),
panel.grid.major = element_line(color = "gray"), panel.grid.minor = element_line(color = "light gray"),
axis.line = element_line(color = "gray"))
<- p + theme_hc()
p ggplotly(p)
# plotly crosstalk
<- highlight_key(df, ~Hour)
calls_person <- highlight_key(df)
person_person
<- bscols(widths = 12, p1 <- plot_ly(df, x = ~Date, y = ~Hour, color = ~Person, size = ~Calls, type = "scatter",
pp hoverinfo = "text", text = ~paste0("Date: ", Date, "\nName: ", Person, "\nCalls: ", Calls)) %>% layout(title = "Calls per hour of day",
xaxis = list(tickangle = 45, showgrid = T), yaxis = list(range = c(0, 23), showgrid = T), margin = list(l = 0.5)),
filter_select("id", "Select hour of day", calls_person, ~Hour, multiple = F), p2 <- plot_ly(calls_person,
x = ~Person, color = ~Person, type = "histogram") %>% layout(title = "Calls per person", yaxis = list(showgrid = F)))
<- htmltools::tagList(list(p1, p2))
pp
# register plotly user Sys.setenv('plotly_username'='malishev') Sys.setenv('plotly_api_key'='apikey')
# ff <- plotly::api_create(p1,username='malishev')
subplot(p1, p2, nrows = 2)
::knit_print.shiny.tag.list(pp) htmltools
Gather/melt dfs to make dfs plotly friendly
require(tidyr)
require(plotly)
<- as.data.frame(EuStockMarkets) %>% gather(index, price) %>% mutate(time = rep(EuStockMarkets %>%
sm time(), 4))
%>% plot_ly(x = sm$time, y = sm$price, color = sm$index) sm
Detect strings in data frame or vector based on partial pattern. Useful when you don’t know the complete name of data frame col.
<- df %>% pull(var1) %>% unique
df_names %>% filter(var1 == df_names[str_detect(df_names, "va")]) # use partial string to pull df col
df
# dplyr version
%>% select(starts_with("Sepal"), contain("Sepal"), matches("Sepal")) iris
Convert multiple strings per vectors into separate numeric vectors or df cols
<- c("-123.233786 49.553438 41.41", "-123.233715 49.553847 42.5", "-123.233645 49.55426 43.8")
str1
%>%
str1 as.data.frame() %>%
::separate(col = ".",into = c("lon", "lat", "elev"), sep = " ", remove = T) %>% # separate char into individual values
tidyrmutate_all(as.numeric)
lon lat elev
1 -123.2338 49.55344 41.41
2 -123.2337 49.55385 42.50
3 -123.2336 49.55426 43.80
Replace all spaces with breaks (ideal for adding ggrichtext labels)
%>% mutate_at("city", ~str_replace_all(., " ", "<br>")) df
Insert characters/string to larger string
require(stringi)
"In the beginning" %>% stri_sub_replace(1, 2, value = "TEXT") # set first and second char order to replace
"In the beginning" %>% stri_sub_replace(2, 1, value = "TEXT") # set second and first char order to insert
Extract text from between characters, esp special characters
require(qdapRegex)
# works for multiple instances
<- "${ extract only this text } and also ${ get this text }"
txt ::ex_between(txt, "${", "}")
qdapRegex
# define left and right boundaries to extract multiple instances
<- "${ extract only this text } and also $[ get this text ]"
txt ::ex_between(txt, c("${", "$["), c("}", "]")) qdapRegex
Check for numeric elements in a string
::p_load(varhandle)
pacman$Value %>% check.numeric() df
Extract integers from alphanumeric string
require(stringr)
<- "3,160.72 A$"
string1
# extracting numbers
%>% str_split_fixed(".*?[0-9]", 2)
string1 %>% str_split_fixed("[[:digit:]]+", 2)
string1 %>% str_split_fixed("", 3)
string1 gsub("[0-9]+", "", string1)
%>% str_extract_all("[[:digit:]]+", 3)
string1 %>% str_replace_all("[:digit:]", " ")
string1
gsub("[a-z]", "", string1)
gsub(".*?([0-9]+).*", "\\1", string1)
%>% str_remove_all("[[:alpha:]]") # rm all letters
string1
# split by ',' and convert to numeric
%>% str_split_fixed("\\p{WHITE_SPACE}", Inf) %>% as.vector() %>% str_replace_all(",", "") %>%
string1 as.numeric()
# rm letters and special chars
%>% str_remove_all("[a-zA-Z[^[:alnum:]]]+[$]") %>% str_replace_all(",", "") %>% as.numeric() string1
Easily parse numbers from character to numeric
c("$32,930", "$32,990", "$33,749", "$33,965") %>% readr::parse_number()
Remove empty string entries
require(stringi)
c(LETTERS[1:3], rep("", 4)) %>% stri_remove_empty(na_empty = F)
Remove brackets, braces, and angle tags (HTML div tags)
require(qdap)
<- paste0("Keep this text [remove this text] and", "<p> remove angle tags </p>")
txt
%>% bracketX(bracket = c("square", "angle")) txt
Get exact string/word
require(stringr)
require(dplyr)
<- "Get only this word not these words"
txt
# get only 'word' and not 'words'
%>% str_extract_all("\\bword\\b" %>% regex(ignore.case = T)) txt
Check spelling in string and suggest options
require(qdap)
"text1" %>% qdap::check_spelling()
Replace multiple strings with single or multiple strings
require(stringr)
require(purrr)
<- "text1 text2 text3 text4" # string to read
ln <- c("text2", "text3") # string to modify
wds ::reduce2(wds, paste0("<em>", wds, "</em>"), .init = ln, str_replace # func to apply
purrr )
Extract latlons as string from between special chars and convert into sf
<- "Corte (42.3052,9.1520)
city_df Bocca del Oro (41.54754,9.28543)
Bastia (42.6992,9.4515)" %>%
::ex_between("(", ")",trim = T, fixed = T,clean = T,extract = T) %>%
qdapRegex%>%
unlist as.data.frame() %>%
::separate(col = ".",into = c("lat", "lon"), sep = ",", remove = T) %>% # separate char into individual values
tidyrmutate_all(as.numeric) %>%
st_as_sf(coords = c("lon","lat"), crs = 4326)
Read in svg
require(XML)
<- "img1.svg" %>% xmlParse() imgr
Get just HMS portion of POSIX class
require(dplyr)
Sys.time() %>% format(format = "%H:%M:%S")
Convert character to hms format, esp for erraneous timedate data
::p_load(lubridate, hms)
pacman
%>% pull(var1) %>% as.factor %>% lubridate::hms() %>% period_to_seconds() %>% hms::as_hms() df
Timezone converter
# get local tz
require(dplyr)
require(lubridate)
require(stringr)
<- "2021-02-01T02:22:59.000Z"
itz ::ymd_hms(itz) %>% with_tz(OlsonNames()[OlsonNames() %>% str_which("Melb")]) lubridate
Set time to 12 hour with AM/PM
require(lubridate)
$time %>% as_datetime() %>% format("%d/%m %I:%M %p") # set time to 12 hour with AM/PM df
Convert character string to just year/month
# adds ambiguous day to vector, which can be ignored or removed
require(anytime)
require(lubridate)
# '1982-04' '1982-05' '1982-06' '1982-07'
$Time %>% anytime::anydate() # opt 1
df$Time %>% lubridate::parse_date_time("ym") # opt 2
dfas.POSIXct(df$Time, format = "%Y-%M") # opt 3
Round off datetime
require(lubridate)
%>% mutate(Meridian = timevar %>%
df round_date(unit = "hour") %>% # round off hour
format("%I") %>% as.numeric) # get 12 hour time
Summary tables
::install_github("ddsjoberg/gtsummary")
remotestbl_summary(
trial2,by = trt, # split table by group
missing = "no" # don't list missing data separately
%>%
) add_n() %>% # add column with total number of non-missing observations
add_p() %>% # test for a difference between groups
modify_header(label = "**Variable**") %>% # update the column header
bold_labels()
Download data directly from web
<- "https://www.data.com/test.xlsx"
url
::GET(url, httr::write_disk("test.xlsx", overwrite = T)) httr
Get HTML elements by class
require(rvest)
<- "https://au.movember.com/report-cards/index/report_category/testicular-cancer"
url
# get element for ' class='class1' '
%>% read_html() %>% html_nodes(".class1") # opt 1
url %>% read_html() %>% html_nodes("#class1") # opt 2
url
%>% read_html() %>% xml2::xml_find_all("//a") # opt 3 url
Get text associated with external url link
%>% read_html() %>% html_nodes("a") %>% html_text() %>% str_trim(side = "both") %>% str_split_fixed("\n",
url Inf) %>% data.frame
Check structure and layout of HTML page
%>% read_html() %>% html_nodes(".class1") %>% html_stucture() # show html structure
url html_attrs() # get available nodes
html_text() # or get all text
Search for string contained in html
%>% read_html %>% read_html() %>% html_nodes(":contains('string1')") url
Get child divs
%>% read_html %>% read_html() %>% html_nodes("a") %>% html_children() url
Using CCS selector tool - Open CCS selector tool
- Click on element
- Use class selector that appears in box e.g. ‘YYrds’ OR
- Click on ‘xpath’ and use xpath string with
xml_final_all()
# class selector
%>% read_html() %>% html_nodes(".YYrds")
url
# xpath option using copied string
%>% read_html() %>% xml2::xml_find_all("//*[contains(concat( ' ', @class, ' ' ), concat( ' ', 'YYrds', ' ' ))]") url