jsm2023
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 scraped the JSM program and wrangled 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)
Quite a bit of coding went into scraping and wrangling the JSM program to get it into the form of a data frame. At some point we can add that code, but for now let’s just load it 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 = day,
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 = ", ")),
organizer = Organizer %>% map_chr(~ paste(.x, collapse = ", ")))
There are 3245 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" "organizer"
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}{}
#' \item{organizer}{}
#' }
#' @source This data was scraped from the JSM 2023 website.
"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/jsm2023-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 4853 JSM author names
#' @source This data was scraped from the JSM 2023 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, organizer, and chair.
#' @param keywords character vector of words to look for in talk title
#' @param days vector of Dates (e.g. "2023-08-07")
#' @param session_types character vector, e.g. "Invited Papers"
#' @export
get_talks <- function(speakers, authors, people, keywords, days, session_types) {
talks <- jsm2023::talks
if (!missing(speakers)) {
pattern <- paste(speakers, collapse = "|") %>%
stringr::str_replace("\\(", "\\\\(") %>% # escape parentheses in names
stringr::str_replace("\\)", "\\\\)")
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("\\)", "\\\\)")
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("\\)", "\\\\)")
talks <- talks %>%
dplyr::filter(
stringr::str_detect(.data$author, pattern) |
stringr::str_detect(.data$speaker, pattern) |
stringr::str_detect(.data$chair, pattern) |
stringr::str_detect(.data$organizer, pattern)
)
}
if (!missing(keywords)) {
pattern <- paste(tolower(keywords), collapse = "|")
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 = "|")
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()
usethis::use_package("dplyr")
usethis::use_package("stringr")
usethis::use_import_from("rlang", ".data")
## ✔ Adding 'magrittr' to Imports field in DESCRIPTION
## ✔ Writing 'R/utils-pipe.R'
## • Run `devtools::document()` to update 'NAMESPACE'
## ✔ Adding 'dplyr' to Imports field in DESCRIPTION
## • Refer to functions with `dplyr::fun()`
## ✔ Adding 'stringr' to Imports field in DESCRIPTION
## • Refer to functions with `stringr::fun()`
## ✔ Adding 'rlang' to Imports field in DESCRIPTION
## ✔ Adding '@importFrom rlang .data' to 'R/jsm2023-package.R'
## ✔ Writing 'NAMESPACE'
testthat::test_that("get_talks() works", {
talk_jb <- get_talks(speakers = "Jacob Bien")
testthat::expect_equal(
talk_jb$title[[1]],
"New and Evolving Roles of Business Statistics in the Big-data Era"
)
testthat::expect_equal(
nrow(get_talks(speakers = c("Jacob Bien", "Guo Yu"))),
1
)
jb_dw <- c("Jacob Bien", "Daniela Witten")
testthat::expect_equal(nrow(get_talks(speakers = jb_dw)), 2)
testthat::expect_true(
all(
get_talks(speakers = jb_dw)$title %in% get_talks(authors = jb_dw)$title
))
testthat::expect_equal(
nrow(get_talks(authors = "Sumanta Basu")),
2
)
testthat::expect_equal(
get_talks(speakers = "Jacob Bien", authors = c("Jacob Bien", "Guo Yu")),
talk_jb
)
testthat::expect_equal(
nrow(get_talks(people = "Jacob Bien")),
310 # poster chair!
)
testthat::expect_equal(
get_talks(keywords = "Business Statistics"),
talk_jb
)
testthat::expect_equal(
get_talks(keywords = "business statistics"),
talk_jb
)
testthat::expect_equal(
get_talks(people = "Jacob Bien",
session_types = "Invited Panel Session"),
talk_jb
)
testthat::expect_equal(
get_talks(people = "Jacob Bien",
session_types = c("Invited Paper Session",
"Contributed Posters",
"Invited Panel Session")),
get_talks(people = "Jacob Bien")
)
})
#' 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})\nOrganizer: {.data$organizer}; Chair: {.data$chair}; https://ww2.aievolution.com/JSMAnnual/index.cfm?do=ev.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("jsm2023-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='https://ww2.aievolution.com/JSMAnnual/index.cfm?do=ev.viewEv&ev={session_number}' target='_blank'>{session_title}</a> ({session_type})\\nOrganizer: {organizer}; Chair: {chair}")
cal$event %>%
calendar::ic_character() %>%
stringr::str_replace_all("^DTSTART", "DTSTART;TZID=America/New_York") %>%
stringr::str_replace_all("^DTEND", "DTEND;TZID=America/New_York") %>%
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 jsm2023 documentation
## ℹ Loading jsm2023
## Writing 'NAMESPACE'
## Writing 'authors.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 'jsm2023-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'
## ✔ 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/jsm2023-project/tree/main/jsm2023"
)
## Package: jsm2023
## 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/jsm2023-project/tree/main/jsm2023
## Depends:
## R (>= 2.10)
## 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.2.3
Also, let’s add a hex sticker:
add_hex_sticker("../source-files/jsm2023-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$', '^pkgdown$' to '.Rbuildignore'
## Warning in readLines(con, warn = readLines.warn): incomplete final line found
## on './_pkgdown.yml'
## -- Installing package into temporary library -----------------------------------
## == Building pkgdown site =======================================================
## Reading from: '/Users/jacobbien/Dropbox/jsmscheduler/2023/jsm2023-project/jsm2023'
## Writing to: '/Users/jacobbien/Dropbox/jsmscheduler/2023/jsm2023-project/docs'
## -- Initialising site -----------------------------------------------------------
## -- Building favicons -----------------------------------------------------------
## Building favicons with realfavicongenerator.net...
## Copying 'pkgdown/favicon/apple-touch-icon-120x120.png' to 'apple-touch-icon-120x120.png'
## Copying 'pkgdown/favicon/apple-touch-icon-152x152.png' to 'apple-touch-icon-152x152.png'
## Copying 'pkgdown/favicon/apple-touch-icon-180x180.png' to 'apple-touch-icon-180x180.png'
## Copying 'pkgdown/favicon/apple-touch-icon-60x60.png' to 'apple-touch-icon-60x60.png'
## Copying 'pkgdown/favicon/apple-touch-icon-76x76.png' to 'apple-touch-icon-76x76.png'
## Copying 'pkgdown/favicon/apple-touch-icon.png' to 'apple-touch-icon.png'
## Copying 'pkgdown/favicon/favicon-16x16.png' to 'favicon-16x16.png'
## Copying 'pkgdown/favicon/favicon-32x32.png' to 'favicon-32x32.png'
## -- Building home ---------------------------------------------------------------
## Reading 'LICENSE.md'
## Copying 'man/figures/ics-imported.png' to 'reference/figures/ics-imported.png'
## Writing '404.html'
## -- Building function reference -------------------------------------------------
## Reading 'man/authors.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/jsm2023-package.Rd'
## Reading 'man/ordered_nz.Rd'
## Reading 'man/pipe.Rd'
## Reading 'man/talks.Rd'
## Reading 'man/words.Rd'
## Writing 'sitemap.xml'
## -- Building search index -------------------------------------------------------
## == DONE ========================================================================