jsm2025
R
packageNote: This package was generated using the litr
R
package, which lets you define full R packages in a single R Markdown
file. For more on litr
, see here.
We start by specifying the information needed in the DESCRIPTION file of the R package.
usethis::create_package(
path = ".",
fields = list(
Package = params$package_name,
Version = "0.0.1",
Title = "Deciding on Your Schedule for JSM",
Description = "Navigate the JSM schedule from the comfort of an R console, get personalized recommendations for talks, and export your schedule as an ical.",
`Authors@R` = c(
person(
given = "Jacob",
family = "Bien",
email = "jbien@usc.edu",
role = c("aut", "cre")
),
person(
given = "Yibin",
family = "Xiong",
role = "ctb")
)
)
)
usethis::use_mit_license(copyright_holder = "J. Bien")
#' A package to help you decide what talks to attend at JSM
#'
#' We wrangled the JSM program and decades of citation and coauthorship
#' data from Semantic Scholar and Arxiv. We have also included functionality
#' for exporting your schedule as an ical that can be loaded into Google
#' Calendar or similar. The result is a package that streamlines the
#' process of finding talks that you may want to attend.
#'
#' @docType package
#' @seealso [`get_talks`], [`get_coauthors`], [`get_out_citations`], [`get_in_citations`], [`export_calendar_to_ics`], [`export_calendar_to_csv`]
Some packages we’ll be using:
library(dplyr)
library(stringr)
library(purrr)
library(lubridate)
We have wrangled the JSM program into a data frame, which we load here from RDS file.
load(file.path("..", "source-files", "jsm-program.Rdata"), envir = jsm <- new.env())
talks <- jsm$talks %>%
transmute(session_number = session_id,
session_title = session_title,
session_type = session_type,
day = lubridate::date(start_time),
start_time = start_time,
end_time = end_time,
author = Author %>% map_chr(~ paste(.x, collapse = ", ")),
speaker = Presenter %>% map_chr(~ paste(.x, collapse = ", ")),
title = talk_title,
room = room,
chair = Chair %>% map_chr(~ paste(.x, collapse = ", ")))
There are 3000 talks. For each talk, we provide the following information:
names(talks)
## [1] "session_number" "session_title" "session_type" "day"
## [5] "start_time" "end_time" "author" "speaker"
## [9] "title" "room" "chair"
Let’s send this off to the package:
usethis::use_data(talks)
## ✔ Adding R to 'Depends' field in DESCRIPTION.
## ✔ Creating 'data/'.
## ✔ Setting 'LazyData' to "true" in 'DESCRIPTION'.
## ✔ Saving "talks" to "data/talks.rda".
## ☐ Document your data (see <https://r-pkgs.org/data.html>).
And we’ll need to document the data set as well:
#' The JSM program as a data frame
#'
#' @format A data frame where each row is a talk and with the following variables:
#' \describe{
#' \item{session_number}{}
#' \item{session_title}{}
#' \item{session_type}{}
#' \item{day}{The day}{}
#' \item{start_time}{}
#' \item{end_time}{}
#' \item{author}{The name(s) of the author(s). Names separated by commas.}
#' \item{speaker}{The name of the speaker. In rare cases where there are multiple speakers, their names are separated by commas.}
#' \item{title}{The talk title}
#' \item{room}{}
#' \item{chair}{}
#' }
#' @source This data was provided by the ASA (thank you Donna LaLonde).
"talks"
## [1] "talks"
Since this is a tibble, we’ll need to make sure our package knows how to handle tibbles:
usethis::use_tibble()
## ✔ Adding tibble to 'Imports' field in DESCRIPTION.
## ✔ Adding "@importFrom tibble tibble" to 'R/jsm2025-package.R'.
## ✔ Writing 'NAMESPACE'.
## ☐ Document a returned tibble like so:
## #' @return a [tibble][tibble::tibble-package]
Let’s also add a character vector of words appearing in the titles:
words <- jsm$words
usethis::use_data(words)
## ✔ Saving "words" to "data/words.rda".
## ☐ Document your data (see <https://r-pkgs.org/data.html>).
#' JSM words used in titles
#' @format A character vector of 4627 JSM words from titles
#' @source From the JSM 2025 program.
"words"
## [1] "words"
While some users might just be happy with talks
itself,
we can offer some helpful functions.
#' Get talks based on some criteria
#'
#' @param speakers character vector of speaker names. Names start with given (first) name, following the format of JSM program.
#' @param authors character vector of author names. Same as above.
#' @param people character vector of people names. This looks for the person as a speaker, author, and chair.
#' @param keywords character vector of words to look for in talk title
#' @param days vector of Dates (e.g. "2025-08-04")
#' @param session_types character vector, e.g. "Invited Papers"
#' @export
get_talks <- function(speakers, authors, people, keywords, days, session_types) {
talks <- jsm2025::talks
if (!missing(speakers)) {
pattern <- paste(speakers, collapse = "|") %>%
stringr::str_replace("\\(", "\\\\(") %>% # escape parentheses in names
stringr::str_replace("\\)", "\\\\)")
if (!(pattern %in% c("", "NA")))
talks <- talks %>% dplyr::filter(stringr::str_detect(.data$speaker, pattern))
}
if (!missing(authors)) {
pattern <- paste(authors, collapse = "|") %>%
stringr::str_replace("\\(", "\\\\(") %>% # escape parentheses in names
stringr::str_replace("\\)", "\\\\)")
if (!(pattern %in% c("", "NA")))
talks <- talks %>% dplyr::filter(stringr::str_detect(.data$author, pattern))
}
if (!missing(people)) {
pattern <- paste(people, collapse = "|") %>%
stringr::str_replace("\\(", "\\\\(") %>% # escape parentheses in names
stringr::str_replace("\\)", "\\\\)")
if (!(pattern %in% c("", "NA")))
talks <- talks %>%
dplyr::filter(
stringr::str_detect(.data$author, pattern) |
stringr::str_detect(.data$speaker, pattern) |
stringr::str_detect(.data$chair, pattern)
)
}
if (!missing(keywords)) {
pattern <- paste(tolower(keywords), collapse = "|")
if (!(pattern %in% c("", "NA")))
talks <- talks %>%
dplyr::filter(stringr::str_detect(tolower(.data$title), pattern))
}
if (!missing(days)) {
talks <- talks %>% dplyr::filter(.data$day %in% days)
}
if (!missing(session_types)) {
pattern <- paste(tolower(session_types), collapse = "|")
if (!(pattern %in% c("", "NA")))
talks <- talks %>%
dplyr::filter(stringr::str_detect(tolower(.data$session_type), pattern))
}
talks %>% dplyr::arrange(.data$start_time)
}
In the above we used dplyr
, stringr
, the
magrittr
pipe, and .data
so let’s make sure
these are properly included in the package.
usethis::use_pipe()
## ✔ Adding magrittr to 'Imports' field in DESCRIPTION.
## ✔ Writing 'R/utils-pipe.R'.
## ☐ Run `devtools::document()` to update 'NAMESPACE'.
usethis::use_package("dplyr")
## ✔ Adding dplyr to 'Imports' field in DESCRIPTION.
## ☐ Refer to functions with `dplyr::fun()`.
usethis::use_package("stringr")
## ✔ Adding stringr to 'Imports' field in DESCRIPTION.
## ☐ Refer to functions with `stringr::fun()`.
usethis::use_import_from("rlang", ".data")
## ✔ Adding rlang to 'Imports' field in DESCRIPTION.
## ✔ Adding "@importFrom rlang .data" to 'R/jsm2025-package.R'.
## ✔ Writing 'NAMESPACE'.
testthat::test_that("get_talks() works", {
talk_rt <- get_talks(speakers = "Ryan Tibshirani")
testthat::expect_equal(
talk_rt$title[[1]],
"Gradient Equilibrium in Online Learning"
)
sb_rt <- c("Sumanta Basu", "Ryan Tibshirani")
testthat::expect_equal(nrow(get_talks(speakers = sb_rt)), 2)
testthat::expect_true(
all(
get_talks(speakers = sb_rt)$title %in% get_talks(authors = sb_rt)$title
))
testthat::expect_equal(
nrow(get_talks(authors = "Sumanta Basu")),
8
)
testthat::expect_equal(
get_talks(speakers = "Sumanta Basu", authors = sb_rt),
get_talks(speakers = "Sumanta Basu")
)
testthat::expect_equal(
nrow(get_talks(people = "Lucas Janson")),
7 # speaker, author, and chair
)
})
#' Export events to a .csv format that can be imported to Google Calendar
#'
#' This will create a .csv file that can then be imported into Google Calendar.
#' To do so, go to https://calendar.google.com/calendar/u/0/r/settings/export
#' and then click "Select file from your computer", and find the file created by
#' this function. Then choose which calendar you want these events added to and
#' click "Import." The disadvantage of this approach is that the times (which are
#' ET) will be entered in the local time of your calendar. So if your calendar is
#' in a different time zone when you enter these, the events will be at the wrong
#' time. Using `export_calendar_to_ics()` does not have this problem so is the
#' recommended function to use rather than this one.
#'
#' @param schedule output of a call to `get_talks()`
#' @param file filename of .csv that will be created
#'
#' @seealso [`export_calendar_to_ics`]
#' @export
export_calendar_to_csv <- function(schedule, file = NULL) {
schedule %>%
dplyr::transmute(
Subject = paste0(.data$speaker, ": ", .data$title),
`Start Date` = .data$day,
`All Day Event` = FALSE,
`Start Time` = .data$start_time,
`End Time` = .data$end_time,
Location = .data$room,
Description = stringr::str_glue("Authors: {.data$author}\nSession: {.data$session_title} ({.data$session_type})\nChair: {.data$chair}; ww3.aievolution.com/JSMAnnual2025/Events/viewEv?ev={.data$session_number}")
) %>%
readr::write_csv(file)
}
usethis::use_package("readr")
## ✔ Adding readr to 'Imports' field in DESCRIPTION.
## ☐ Refer to functions with `readr::fun()`.
#' Export events to a .ics file that can be imported to Google Calendar, etc.
#'
#' This will represent the events in the standardized iCalendar format and export
#' as a .ics file that can then be imported into most calendar apps (including
#' Google Calendar). To do so, go to
#' https://calendar.google.com/calendar/u/0/r/settings/export
#' and then click "Select file from your computer", and find the file created by
#' this function. Then choose which calendar you want these events added to and
#' click "Import."
#'
#' @param schedule output of a call to `get_talks()`
#' @param file filename (should end with .ics) that will be created
#'
#' @export
export_calendar_to_ics <- function(schedule, file = NULL) {
if (is.null(file))
file <- paste0("jsm2025-cal-",
stringr::str_replace_all(Sys.time(), " ", "-"),
".ics")
cal <- schedule %>%
dplyr::rowwise() %>%
dplyr::mutate(
event = calendar::ic_event(
start_time = .data$start_time,
end_time = as.numeric(difftime(time1 = .data$end_time,
time2 = .data$start_time,
units = "hours")),
summary = paste0(.data$speaker, ": ", .data$title)
)
)
cal$event$LOCATION <- schedule$room
cal$event$DESCRIPTION <- stringr::str_glue_data(
schedule,
"Authors: {author}\\nSession: <a href='ww3.aievolution.com/JSMAnnual2025/Events/viewEv?ev={session_number}' target='_blank'>{session_title}</a> ({session_type})\\nChair: {chair}")
cal$event %>%
calendar::ic_character() %>%
stringr::str_replace_all("^DTSTART", "DTSTART;TZID=America/Los_Angeles") %>%
stringr::str_replace_all("^DTEND", "DTEND;TZID=America/Los_Angeles") %>%
writeLines(file)
}
usethis::use_package("calendar")
## ✔ Adding calendar to 'Imports' field in DESCRIPTION.
## ☐ Refer to functions with `calendar::fun()`.
We finish by running commands that will document the package.
litr::document() # <-- use instead of devtools::document()
## ℹ Updating jsm2025 documentation
## ℹ Loading jsm2025
## Writing 'NAMESPACE'
## Writing 'authors.Rd'
## Writing 'authors2025.Rd'
## Writing 'cites.Rd'
## Writing 'coauthor.Rd'
## Writing 'export_calendar_to_csv.Rd'
## Writing 'export_calendar_to_ics.Rd'
## Writing 'get_coauthors.Rd'
## Writing 'get_in_citations.Rd'
## Writing 'get_out_citations.Rd'
## Writing 'get_talks.Rd'
## Writing 'jsm2025-package.Rd'
## Writing 'ordered_nz.Rd'
## Writing 'talks.Rd'
## Writing 'pipe.Rd'
## Writing 'words.Rd'
litr::add_readme("../source-files/README.Rmd")
## ✔ Writing 'README.Rmd'.
## ✔ Adding "^README\\.Rmd$" to '.Rbuildignore'.
## ☐ Update 'README.Rmd' to include installation instructions.
## ✔ Creating '.git/hooks/'.
## ✔ Writing '.git/hooks/pre-commit'.
The README has an image, which we’ll put in the package:
if (!fs::dir_exists("man/figures")) fs::dir_create("man/figures")
fs::file_copy("../source-files/ics-imported.png", "man/figures/ics-imported.png")
First, let’s include the github link of our package as the URL so that we can have a link to it on our pkgdown site.
desc::desc_set(
"URL",
"https://github.com/jacobbien/jsm2025-project/tree/main/jsm2025"
)
## Package: jsm2025
## Title: Deciding on Your Schedule for JSM
## Version: 0.0.1
## Authors@R (parsed):
## * Jacob Bien <jbien@usc.edu> [aut, cre]
## * Yibin Xiong [ctb]
## Description: Navigate the JSM schedule from the comfort of an R console,
## get personalized recommendations for talks, and export your schedule
## as an ical.
## License: MIT + file LICENSE
## URL: https://github.com/jacobbien/jsm2025-project/tree/main/jsm2025
## Depends:
## R (>= 3.5)
## Imports:
## calendar,
## dplyr,
## magrittr,
## Matrix,
## readr,
## rlang,
## stringr,
## tibble
## Suggests:
## testthat (>= 3.0.0)
## Config/testthat/edition: 3
## Encoding: UTF-8
## LazyData: true
## Roxygen: list(markdown = TRUE)
## RoxygenNote: 7.3.2
Also, let’s add a hex sticker:
add_hex_sticker("../source-files/jsm2025-hex.png")
Be sure that this next command appears after
litr::document()
has been called in this file.
litr::add_pkgdown("../source-files/_pkgdown.yml")
## ✔ Adding "^_pkgdown\\.yml$", "^docs$", and "^pkgdown$" to '.Rbuildignore'.
## Warning in readLines(con, warn = readLines.warn): incomplete final line found
## on './_pkgdown.yml'
## ── Installing package jsm2025 into temporary library ───────────────────────────
## ── Building pkgdown site for package jsm2025 ───────────────────────────────────
## Reading from: /Users/jacobbien/Documents/GitHub/jsm2025-project/jsm2025
## Writing to: /Users/jacobbien/Documents/GitHub/jsm2025-project/docs
## ── Sitrep ──────────────────────────────────────────────────────────────────────
## ✖ URLs not ok.
## In _pkgdown.yml, url is missing.
## See details in `vignette(pkgdown::metadata)`.
## ✖ Favicons not ok.
## Found package logo but not favicons.
## Do you need to run `build_favicons()`?
## ✔ Open graph metadata ok.
## ✔ Articles metadata ok.
## ✔ Reference metadata ok.
## ── Initialising site ───────────────────────────────────────────────────────────
## ── Building favicons ───────────────────────────────────────────────────────────
## ℹ Building favicons with <https://realfavicongenerator.net>...
## ✔ Added 'apple-touch-icon.png', 'favicon-96x96.png', 'favicon.ico',
## 'favicon.svg', 'site.webmanifest', 'web-app-manifest-192x192.png', and
## 'web-app-manifest-512x512.png'.
## ── Building home ───────────────────────────────────────────────────────────────
## Reading LICENSE.md
## Reading README.md
## Writing `404.html`
## ── Building function reference ─────────────────────────────────────────────────
## Copying man/figures/ics-imported.png to reference/figures/ics-imported.png
## Reading man/authors.Rd
## Reading man/authors2025.Rd
## Reading man/cites.Rd
## Reading man/coauthor.Rd
## Reading man/export_calendar_to_csv.Rd
## Reading man/export_calendar_to_ics.Rd
## Reading man/get_coauthors.Rd
## Reading man/get_in_citations.Rd
## Reading man/get_out_citations.Rd
## Reading man/get_talks.Rd
## Reading man/jsm2025-package.Rd
## Reading man/ordered_nz.Rd
## Reading man/pipe.Rd
## Reading man/talks.Rd
## Reading man/words.Rd
## ── Building sitemap ────────────────────────────────────────────────────────────
## ── Building search index ───────────────────────────────────────────────────────
## ── Checking for problems ───────────────────────────────────────────────────────
## ✖ Missing alt-text in 'README.md'
## • reference/figures/ics-imported.png
## ℹ Learn more in `vignette(pkgdown::accessibility)`.
## ── Finished building pkgdown site for package jsm2025 ──────────────────────────
## ── Finished building pkgdown site for package jsm2025 ──────────────────────────