library(tidyverse)
library(jsonlite)
library(httr2)
Before we begin, let’s load a few R packages
Motivation
Today, we are going to talk about getting data from APIs and examples of common data formats.
First, let’s have a bit of a philosophical discussion about data.
“Raw” vs “Clean” data
As data analysts, this is what we wished data looked like whenever we start a project
However, the reality, is data is rarely in that form in comes in all types of “raw” formats that need to be transformed into a “clean” format.
For example, in field of genomics, raw data looks like something like this:
Or if you are interested in analyzing data from Twitter:
Or data from Electronic Healthcare Records (EHRs):
We all have our scary spreadsheet tales. Here is Jenny Bryan from Posit and UBC actually asking for some of those spreadsheet tales on twitter.
For example, this is an actual spreadsheet from Enron in 2001:
What do we mean by “raw” data?
From https://simplystatistics.org/posts/2016-07-20-relativity-raw-data/ raw data is defined as data…
…if you have done no processing, manipulation, coding, or analysis of the data. In other words, the file you received from the person before you is untouched. But it may not be the rawest version of the data. The person who gave you the raw data may have done some computations. They have a different “raw data set”.
Where do data live?
Data lives anywhere and everywhere. Data might be stored simply in a .csv
or .txt
file. Data might be stored in an Excel or Google Spreadsheet. Data might be stored in large databases that require users to write special functions to interact with to extract the data they are interested in.
For example, you may have heard of the terms mySQL
or MongoDB
.
From Wikipedia, MySQL is defined as an open-source relational database management system (RDBMS). Its name is a combination of “My”, the name of co-founder Michael Widenius’s daughter,[7] and “SQL”, the abbreviation for Structured Query Language.
From Wikipeda, MongoDB is defined as “a free and open-source cross-platform document-oriented database program. Classified as a NoSQL database program, MongoDB uses JSON-like documents with schemata.”
So after reading that, we get the sense that there are multiple ways large databases can be structured, data can be formatted and interacted with. In addition, we see that database programs (e.g. MySQL and MongoDB) can also interact with each other.
We will learn more about JSON
today and learn about SQL
in a later lecture more formally.
Best practices on sharing data
A great article in PeerJ was written titled How to share data for collaboration, in which the authors describe a set of guidelines for sharing data:
We highlight the need to provide raw data to the statistician, the importance of consistent formatting, and the necessity of including all essential experimental information and pre-processing steps carried out to the statistician. With these guidelines we hope to avoid errors and delays in data analysis. the importance of consistent formatting, and the necessity of including all essential experimental information and pre-processing steps carried out to the statistician.
It’s a great paper that describes the information you should pass to a statistician to facilitate the most efficient and timely analysis.
Specifically:
- The raw data (or the rawest form of the data to which you have access)
- Should not have modified, removed or summarized any data; Ran no software on data
- e.g. strange binary file your measurement machine spits out
- e.g. complicated JSON file you scrapped from Twitter Application Programming Interfaces (API)
- e.g. hand-entered numbers you collected looking through a microscope
- A clean data set
- This may or may not be transforming data into a
tidy
dataset, but possibly yes
- This may or may not be transforming data into a
- A code book describing each variable and its values in the clean or tidy data set.
- More detailed information about the measurements in the data set (e.g. units, experimental design, summary choices made)
- Doesn’t quite fit into the column names in the spreadsheet
- Often reported in a
.md
,.txt
or Word file.
- An explicit and exact recipe you used to go from 1 -> 2,3
Getting data
JSON files
JSON (or JavaScript Object Notation) is a file format that stores information in human-readable, organized, logical, easy-to-access manner.
For example, here is what a JSON file looks like:
var stephanie = {
"job-title" : "Associate Professor",
"hometown" : "Baltimore, MD",
"pronouns": "she/her",
"states-lived" : {
"state1" : "Louisiana",
"state2" : "Texas",
"state3" : "Massachusetts",
"state4" : "Maryland"
} }
Some features about JSON
objects:
- JSON objects are surrounded by curly braces
{}
- JSON objects are written in key/value pairs
- Keys must be strings, and values must be a valid JSON data type (string, number, object, array, boolean)
- Keys and values are separated by a colon
- Each key/value pair is separated by a comma
Overview of APIs
From AWS, API stands for Application Programming Interface.
- “Application” = any software with a distinct function
- “Interface” = a contract of service between two applications. This contract defines how the two communicate with each other using requests and responses.
The API documentation contains information on how developers are to structure those requests and responses.
The purpose of APIs is enable two software components to communicate with each other using a set of definitions and protocols.
For example, the weather bureau’s software system contains daily weather data. The weather app on your phone “talks” to this system via APIs and shows you daily weather updates on your phone.
How do APIs work?
To understand how APIs work, two terms that are important are
- client. This is the application sending the request.
- server. This is the application sending the response.
So in the weather example, the bureau’s weather database is the server, and the mobile app is the client.
Four types of API architectures
There are four different ways that APIs can work depending on when and why they were created.
SOAP APIs. These APIs use Simple Object Access Protocol. Client and server exchange messages using XML. This is a less flexible API that was more popular in the past.
RPC APIs. These APIs are called Remote Procedure Calls. The client completes a function (or procedure) on the server, and the server sends the output back to the client.
Websocket APIs. Websocket API is another modern web API development that uses JSON objects to pass data. A WebSocket API supports two-way communication between client apps and the server. The server can send callback messages to connected clients, making it more efficient than REST API.
REST APIs. REST stands for Representational State Transfer (and are the most popular and flexible APIs). The client sends requests to the server as data. The server uses this client input to start internal functions and returns output data back to the client. REST defines a set of functions like GET, PUT, DELETE, etc. that clients can use to access server data. Clients and servers exchange data using HTTP.
The main feature of REST API is statelessness (i.e. servers do not save client data between requests). Client requests to the server are similar to URLs you type in your browser to visit a website. The response from the server is plain data, without the typical graphical rendering of a web page.
How to use an API?
The basic steps to using an API are:
- Obtaining an API key. This is done by creating a verified account with the API provider.
- Set up an HTTP API client. This tool allows you to structure API requests easily using the API keys received. Here, we will use functions from the
httr2
package (which is the next generation of thehttr
package). - If you don’t have an API client, you can try to structure the request yourself in your browser by referring to the API documentation.
- Once you are comfortable with the new API syntax, you can start using it in your code.
Where can I find new APIs?
New web APIs can be found on API marketplaces and API directories, such as:
- Rapid API – One of the largest global API markets (10k+ public APIs). Users to test APIs directly on the platform before committing to purchase.
- Public REST APIs – Groups REST APIs into categories, making it easier to browse and find the right one to meet your needs.
- APIForThat and APIList – Both these websites have lists of 500+ web APIs, along with in-depth information on how to use them.
GitHub API
The GitHub REST API may be of interest when studying online communities, working methods, organizational structures, communication and discussions, etc. with a focus on (open-source) software development.
Many projects that are hosted on GitHub are open-source projects with a transparent development process and communications. For private projects, which can also be hosted on GitHub, there’s understandably only a few aggregate data available.
Let’s say we want to use the GitHub REST API to find out how many of my GitHub repositories have open issues?
The API can be used for free and you can send up to 60 requests per hour if you are not authenticated (i.e. if you don’t provide an API key).
For serious data collection, this is not much, so it is recommended to sign up on GitHub and generate a personal access token that acts as API key.
This token can then be used to authenticate your API requests. Your quota is then 5000 requests per hour.
Access the API from R
There are packages for many programming languages that provide convenient access for communicating with the GitHub API, but there are no such packages (that I’m aware of) for accessing the API from R.
This means we can only access the API directly, e.g. by using the jsonlite
package to fetch the data and convert it to an R list
or data.frame
.
Specifically, we will use the jsonlite::read_json()
function to read a JSON file into a data frame.
The JSON file is located at https://api.github.com/users/stephaniehicks/repos.
<- "https://api.github.com/users/stephaniehicks/repos"
github_url
library(jsonlite)
library(tidyverse)
<- read_json(github_url, simplifyVector = TRUE)
jsonData glimpse(jsonData)
Rows: 30
Columns: 79
$ id <int> 160194123, 132884754, 647539937, 225501707…
$ node_id <chr> "MDEwOlJlcG9zaXRvcnkxNjAxOTQxMjM=", "MDEwO…
$ name <chr> "2018-bioinfosummer-scrnaseq", "advdatasci…
$ full_name <chr> "stephaniehicks/2018-bioinfosummer-scrnase…
$ private <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ owner <df[,19]> <data.frame[26 x 19]>
$ html_url <chr> "https://github.com/stephaniehicks/201…
$ description <chr> NA, NA, "Repo to share code for the atlas-…
$ fork <lgl> FALSE, TRUE, FALSE, TRUE, TRUE, FALSE, FAL…
$ url <chr> "https://api.github.com/repos/stephaniehic…
$ forks_url <chr> "https://api.github.com/repos/stephaniehic…
$ keys_url <chr> "https://api.github.com/repos/stephaniehic…
$ collaborators_url <chr> "https://api.github.com/repos/stephaniehic…
$ teams_url <chr> "https://api.github.com/repos/stephaniehic…
$ hooks_url <chr> "https://api.github.com/repos/stephaniehic…
$ issue_events_url <chr> "https://api.github.com/repos/stephaniehic…
$ events_url <chr> "https://api.github.com/repos/stephaniehic…
$ assignees_url <chr> "https://api.github.com/repos/stephaniehic…
$ branches_url <chr> "https://api.github.com/repos/stephaniehic…
$ tags_url <chr> "https://api.github.com/repos/stephaniehic…
$ blobs_url <chr> "https://api.github.com/repos/stephaniehic…
$ git_tags_url <chr> "https://api.github.com/repos/stephaniehic…
$ git_refs_url <chr> "https://api.github.com/repos/stephaniehic…
$ trees_url <chr> "https://api.github.com/repos/stephaniehic…
$ statuses_url <chr> "https://api.github.com/repos/stephaniehic…
$ languages_url <chr> "https://api.github.com/repos/stephaniehic…
$ stargazers_url <chr> "https://api.github.com/repos/stephaniehic…
$ contributors_url <chr> "https://api.github.com/repos/stephaniehic…
$ subscribers_url <chr> "https://api.github.com/repos/stephaniehic…
$ subscription_url <chr> "https://api.github.com/repos/stephaniehic…
$ commits_url <chr> "https://api.github.com/repos/stephaniehic…
$ git_commits_url <chr> "https://api.github.com/repos/stephaniehic…
$ comments_url <chr> "https://api.github.com/repos/stephaniehic…
$ issue_comment_url <chr> "https://api.github.com/repos/stephaniehic…
$ contents_url <chr> "https://api.github.com/repos/stephaniehic…
$ compare_url <chr> "https://api.github.com/repos/stephaniehic…
$ merges_url <chr> "https://api.github.com/repos/stephaniehic…
$ archive_url <chr> "https://api.github.com/repos/stephaniehic…
$ downloads_url <chr> "https://api.github.com/repos/stephaniehic…
$ issues_url <chr> "https://api.github.com/repos/stephaniehic…
$ pulls_url <chr> "https://api.github.com/repos/stephaniehic…
$ milestones_url <chr> "https://api.github.com/repos/stephaniehic…
$ notifications_url <chr> "https://api.github.com/repos/stephaniehic…
$ labels_url <chr> "https://api.github.com/repos/stephaniehic…
$ releases_url <chr> "https://api.github.com/repos/stephaniehic…
$ deployments_url <chr> "https://api.github.com/repos/stephaniehic…
$ created_at <chr> "2018-12-03T13:20:45Z", "2018-05-10T10:22:…
$ updated_at <chr> "2019-08-08T02:18:17Z", "2018-05-10T10:22:…
$ pushed_at <chr> "2018-12-05T17:07:09Z", "2017-12-18T17:18:…
$ git_url <chr> "git://github.com/stephaniehicks/2018-bioi…
$ ssh_url <chr> "git@github.com:stephaniehicks/2018-bioinf…
$ clone_url <chr> "https://github.com/stephaniehicks/2018-bi…
$ svn_url <chr> "https://github.com/stephaniehicks/2018-bi…
$ homepage <chr> NA, NA, NA, NA, NA, "", NA, NA, NA, NA, NA…
$ size <int> 60296, 172353, 8866, 121, 675, 26688, 20, …
$ stargazers_count <int> 4, 0, 2, 1, 0, 0, 1, 8, 0, 1, 0, 15, 3, 0,…
$ watchers_count <int> 4, 0, 2, 1, 0, 0, 1, 8, 0, 1, 0, 15, 3, 0,…
$ language <chr> "TeX", "HTML", "R", NA, NA, "R", "R", "Jup…
$ has_issues <lgl> TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, TRU…
$ has_projects <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, …
$ has_downloads <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, …
$ has_wiki <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, …
$ has_pages <lgl> TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, F…
$ has_discussions <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ forks_count <int> 4, 0, 1, 0, 1, 0, 0, 2, 0, 0, 0, 4, 1, 1, …
$ mirror_url <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
$ archived <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ disabled <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ open_issues_count <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ license <df[,5]> <data.frame[26 x 5]>
$ allow_forking <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, …
$ is_template <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALS…
$ web_commit_signoff_required <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ topics <list> <>, <>, <>, <>, <>, <>, <>, <>, <>, <>, <>…
$ visibility <chr> "public", "public", "public", "public", "p…
$ forks <int> 4, 0, 1, 0, 1, 0, 0, 2, 0, 0, 0, 4, 1, 1,…
$ open_issues <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ watchers <int> 4, 0, 2, 1, 0, 0, 1, 8, 0, 1, 0, 15, 3, 0,…
$ default_branch <chr> "master", "master", "main", "master", "mas…
We see this has returned a data frame with the argument simplifyVector
which converts the output from a list to a dataframe.
However, from here, we see that there are only 30 rows (or 30 repositories). If you look on my github page, you can see there are more than 30 repositories.
What’s happening is called pagination.
At a high-level, the API is limiting the amount of items a user gets and splitting it into pages.
Formally, pagination is the process of splitting the contents or a section of a website into discrete pages. Users tend to get lost when there’s bunch of data and with pagination splitting they can concentrate on a particular amount of content. Hierarchy and paginated structure improve the readability score of the content.
In this use case Github api splits the result into 30 items per resonse, depends on the request
Solution: You should explicitly specify in your request how many items you would like to receive from server pagination engine, using formula for Github pagination api:
?page=1&per_page=<numberOfItemsYouSpecify>"
You can read more about pagination here:
Here we can visit this website:
And see there are more than 30 repos. Let’s read it into R.
= "https://api.github.com/users/stephaniehicks/repos?page=1&per_page=1000"
github_url
<- read_json(github_url, simplifyVector = TRUE)
jsonDataAll dim(jsonDataAll)
[1] 92 79
We now get all the public repositories! yay!
Access APIs with httr2
There are a set of basic HTTP verbs that allow you access a set of endpoints.
The basic request patterns are:
- Retrieve a single item (GET)
- Retrieve a list of items (GET)
- Create an item (POST)
- Update an item (PUT)
- Delete an item (DELETE)
Example: GitHub commits
Let’s say you want to retrieve information about the latest commits from a GitHub repository. We will use httr2
to make a request to the GitHub API for a repository of your choice. Later on we will make this an authenticated HTTP response to the GitHub API.
First, we make sure we have the httr2
package installed and loaded. We’ll also need jsonlite
package for handling JSON files and dplyr
for data wrangling.
library(httr2)
library(jsonlite)
library(dplyr)
Now we will set up our request to the GitHub API. The GitHub API endpoint for getting the latest commits in a repository is at https://api.github.com/repos/{owner}/{repo}/commits
.
For this example, we’ll look at the latest commits for the tidyverse/dplyr repository. We will use httr2::request()
function to set up the request, (and later on we will add authentication – optional, but recommended for higher rate limits), and parse the response.
<- "tidyverse"
owner <- "dplyr"
repo <- paste0("https://api.github.com/repos/", owner, "/", repo, "/commits")
url
<- request(url) %>%
response req_perform()
response
<httr2_response>
GET https://api.github.com/repos/tidyverse/dplyr/commits
Status: 200 OK
Content-Type: application/json
Body: In memory (146056 bytes)
Next, we can see if the response was successful.
# Check if the response was successful
if (resp_status(response) == 200) {
# Parse JSON response into an R list
<- resp_body_json(response, simplifyVector = TRUE)
commits
# View the first few rows of the commits data
head(commits)
else {
} message("Failed to retrieve data. Status code: ", resp_status(response))
}
sha
1 fb25640fa1eb74746a7a74a06090045106e5d20f
2 e4e9a295a373b85e02ae084a23f12e9212a72b98
3 1d17672a54305170dc75c251f8ae69a85c0bea37
4 cfb25a030f9f7c39f77fed2c97f3fa7b15a55e84
5 85e94fcde02ad49c77510991078899278489fd8f
6 e0555277878ed174d40bda86690674ecdc27db55
node_id
1 C_kwDOAGIUpdoAKGZiMjU2NDBmYTFlYjc0NzQ2YTdhNzRhMDYwOTAwNDUxMDZlNWQyMGY
2 C_kwDOAGIUpdoAKGU0ZTlhMjk1YTM3M2I4NWUwMmFlMDg0YTIzZjEyZTkyMTJhNzJiOTg
3 C_kwDOAGIUpdoAKDFkMTc2NzJhNTQzMDUxNzBkYzc1YzI1MWY4YWU2OWE4NWMwYmVhMzc
4 C_kwDOAGIUpdoAKGNmYjI1YTAzMGY5ZjdjMzlmNzdmZWQyYzk3ZjNmYTdiMTVhNTVlODQ
5 C_kwDOAGIUpdoAKDg1ZTk0ZmNkZTAyYWQ0OWM3NzUxMDk5MTA3ODg5OTI3ODQ4OWZkOGY
6 C_kwDOAGIUpdoAKGUwNTU1Mjc3ODc4ZWQxNzRkNDBiZGE4NjY5MDY3NGVjZGMyN2RiNTU
commit.author.name commit.author.email
1 Kirill Müller kirill@cynkra.com
2 Davis Vaughan davis@rstudio.com
3 Mike Du 58779940+ilovemane@users.noreply.github.com
4 Adam adampeterso@gmail.com
5 Núria Mercadé-Besora 61558739+nmercadeb@users.noreply.github.com
6 James Wade github@jameshwade.com
commit.author.date commit.committer.name commit.committer.email
1 2024-11-02T17:43:50Z Kirill Müller kirill@cynkra.com
2 2024-10-01T15:53:13Z GitHub noreply@github.com
3 2024-08-27T16:31:39Z GitHub noreply@github.com
4 2024-08-27T16:30:57Z GitHub noreply@github.com
5 2024-08-27T15:51:07Z GitHub noreply@github.com
6 2024-08-27T15:40:46Z GitHub noreply@github.com
commit.committer.date
1 2024-11-02T17:44:39Z
2 2024-10-01T15:53:13Z
3 2024-08-27T16:31:39Z
4 2024-08-27T16:30:57Z
5 2024-08-27T15:51:07Z
6 2024-08-27T15:40:46Z
commit.message
1 Move to tidyverse, already applied manually to gh-pages
2 Remove not needed `new_expanded_quosures()` (#7090)
3 Add error message when passing an array in `conditions` (#7069)\n\n* add error message when passing a matrix\r\n\r\ntidyverse day\r\n\r\n* Update test-vec-case-when.R\r\n\r\n* fixes\r\n\r\n* Tweaks\r\n\r\n* NEWS bullet\r\n\r\n* Update snap\r\n\r\n---------\r\n\r\nCo-authored-by: Davis Vaughan <davis@posit.co>
4 Add documentation clarifying appropriate use of weights in `slice_sample()` (#7052)\n\n* Add documentation clarifying appropriate use of weights in dplyr's `slice_sample()`.\r\n\r\n* Add documentation to relevant .Rd file.\r\n\r\n* Tweak documentation placement a bit\r\n\r\n---------\r\n\r\nCo-authored-by: Davis Vaughan <davis@posit.co>
5 Document that `group_by()` works with data-masking (#7071)
6 Add a `ptype` argument to `between()` (#7073)\n\n* add ptype argument to between Fixes #6906\r\n\r\n* update NEWS with ptype argument\r\n\r\n* document() and add tests\r\n\r\n* add back see also\r\n\r\n* add back see also\r\n\r\n* Redocument\r\n\r\n* Trim trailing whitespace\r\n\r\n* Remove extra line\r\n\r\n* Minor docs tweaks\r\n\r\n* require names ptype, update tests, update function documentation\r\n\r\n* remove unnecessary if else blocks\r\n\r\n* Tweak NEWS bullet\r\n\r\n* A few more tweaks\r\n\r\n* Few more tweaks\r\n\r\n---------\r\n\r\nCo-authored-by: Davis Vaughan <davis@posit.co>
commit.tree.sha
1 5aaa2c4dcdabd6c8303b660f15c963740193eb17
2 8299635297b6b5e61c9522b525a5414993405e08
3 9144e07c3b09886b79b64a76904501f565f1b6c8
4 3d6a95112c5bc59e6a60d245704984e722aeacad
5 852e02c951972c5a4e9a831c68e2425085e7fdd5
6 18d4e6763ceb64f3967f9addb712ad0878798b38
commit.tree.url
1 https://api.github.com/repos/tidyverse/dplyr/git/trees/5aaa2c4dcdabd6c8303b660f15c963740193eb17
2 https://api.github.com/repos/tidyverse/dplyr/git/trees/8299635297b6b5e61c9522b525a5414993405e08
3 https://api.github.com/repos/tidyverse/dplyr/git/trees/9144e07c3b09886b79b64a76904501f565f1b6c8
4 https://api.github.com/repos/tidyverse/dplyr/git/trees/3d6a95112c5bc59e6a60d245704984e722aeacad
5 https://api.github.com/repos/tidyverse/dplyr/git/trees/852e02c951972c5a4e9a831c68e2425085e7fdd5
6 https://api.github.com/repos/tidyverse/dplyr/git/trees/18d4e6763ceb64f3967f9addb712ad0878798b38
commit.url
1 https://api.github.com/repos/tidyverse/dplyr/git/commits/fb25640fa1eb74746a7a74a06090045106e5d20f
2 https://api.github.com/repos/tidyverse/dplyr/git/commits/e4e9a295a373b85e02ae084a23f12e9212a72b98
3 https://api.github.com/repos/tidyverse/dplyr/git/commits/1d17672a54305170dc75c251f8ae69a85c0bea37
4 https://api.github.com/repos/tidyverse/dplyr/git/commits/cfb25a030f9f7c39f77fed2c97f3fa7b15a55e84
5 https://api.github.com/repos/tidyverse/dplyr/git/commits/85e94fcde02ad49c77510991078899278489fd8f
6 https://api.github.com/repos/tidyverse/dplyr/git/commits/e0555277878ed174d40bda86690674ecdc27db55
commit.comment_count commit.verification.verified commit.verification.reason
1 0 FALSE unsigned
2 0 TRUE valid
3 0 TRUE valid
4 0 TRUE valid
5 0 TRUE valid
6 0 TRUE valid
commit.verification.signature
1 <NA>
2 -----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJm/BrpCRC1aQ7uu5UhlAAApk0QABIAjfuu47L1gaXmicgRMYpN\nTs1JfHO7zGxafb5QYqgk80yH/w+XsqHt+Q2Xd3JLvIYSWBJ4IAuKU+R4vBOF6yhW\nDprWxU3K0bLeOc9ycmH21WUPSBNjw/dhyFkMULBa6ZbZhsfPddb8OhPSw02ze/Rw\n9oQSVIWC5ZGIHwBp4/dvrdmQ7ztwmFnTp8uegpIdLdfpqCereJdV74VYx7TrGYB4\n/+48s84H2X2p/P3h15riv5t+545uhrMZMP9RspV7etRju4mloG5EP2zpLPgxrCIi\nLhmpZdjKgrOc0AlocOWyjcGzRLSgExYV0bTeJnTpxqZRsZzkM5JMEGEnPCBP5OD3\nd+PGv+l7WeOioB8+r5Sz+asoWqEfv2rfrjibqZpT4BSwV+R4+4p0NFtzOZpBzrCz\nd+7mu0PWyLP4H7RQ0vWNZTMP3YYpQoEL5FX+X9qKKuFnob8n6W4Fje/B1ru4cYip\nvI7sX43WLQ9X32x7FleFHsFAZDKSg8x/QXlJ2T1/VZSv0ge4M/wg5YmzhPi17xSl\n7JcizRSKzqZDtDiIZQD2FjnVMNdL1LsjbnbWJGf02KejxFkgsYa2oEos2ekouilL\n+xswdRrF8Fuh8KNCMc3MGO3nQovjMGgMoaFaYetXhYOTUSGAfQJBOOpNrH0vzf6g\nOSuaLjJrCYFv0IIxdtrA\n=kx9G\n-----END PGP SIGNATURE-----\n
3 -----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJmzf9rCRC1aQ7uu5UhlAAA+k4QADkSbzuLgeAvxtczSazlRvuW\nAYPlvaK02ndKbIY8Ucwp/wyTGgorMH2kC3g3r0cy1bwVHvsNp0gt1S3tbxdvXXEA\n8R0+pHSxgDvzCIkZdE5WJpN1VjQVYAmqRzeVt9ieifuBGVWapIn26fDPndXswPWq\nQWDKh6tZn5wsfWiclR4TSKIcqOrva7AzegB0iyF/8kKer7jOuG/R5OQoTGJoXwlR\nzGdI+JxKBnY9Q24SBo5xruKq3LKQIWUiICm48aFQ35JbsT83v+RuiAfMkrn1C0RM\nQdN0820iz5/oShO5iIXvcIBZSZhSjQ/EQkv394YS0N4gElGwEbXQIOon5nZIzK3d\n7jtXerms96sjUCOXyb0fB6YnbNEA3/Tiq7x61Fy6uUSrblewhd5AXNZu9/aJwvYv\nOj1BoMdr/HSIUnHl11+90Y0RIA66WsHNCZLKm3L2DAlYczGmL/zcFNivH/3epmlB\nMirSkqfuK1AYzaH5z8Fpu9UOkEVw18L+gFwn2fTye3rxzoy6Lr8R9eT/VAUtZCQV\ng3LurvIWjHe1NNVxeQ702E1ACBXcUhD4Bfqij1QZeEFP7bsIo+ggcyyEIn56ayEO\nSmGMb76kcBciL44oU8fAuzWK8nVVA54iM4ecJltYhp1o3tOFpJ+fQA4n5hxFYgc4\nU+1DPZvop1yluVlV1vVV\n=AwJu\n-----END PGP SIGNATURE-----\n
4 -----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJmzf9BCRC1aQ7uu5UhlAAA68AQAEtPIx3MrOwvPhjYi4mbBvZJ\n/EeNZqaiHoanezr8L+AR0Fym/Asnf5h35+t4PDYU/JqGdND/es0LB6GxH3YMNUsK\n6PrGbv4CMs3Ary5si5c7ynAlASCnbcMrvbIUM0UWzWd5eV+am0irDFmZc20g5Wt/\ngZPYGNKK0D3RWM2KP5O6a67zm9ipuqNbAkGRat9qehxvTEblnB/v8jHZGy5WpTjw\nZDxrdgdCHWCHM48viFsJlbEm8V17tFJ/ipy6JthZy7Ei1JyWpmuc28y3VqrofIDF\n58OdA6RA7JDvfhWPFKq9RKjgfSXp5hgz+5edSwc9fTS77KL9MDq8ZK+Mhm12F3x+\nzAb8fdajL4YwTjquwUfKJWYEgrc39vmFo/G84+VtYuRRvi2T8BFlXqTsEw3z109Y\nfFzem/3R0PFn/+M68dzh931G52JEkhlcowR/Dy+cy1bgnWlEDOHZQQUtFGsuRJ/T\nxOByNQ3GIiqxR+40VREeByXuCdLCOrD0KiggwNBIbUb9AU4fwvS3LNxDZLMB8gzJ\n+DRTH/uMwwlySvf/gn5NvlasXVnLSTuMCi/DBEUMFpOkfeUG63stOuiiqn0p9ll8\nHuAsLVT72uZOYOMiW+MsskehXZKKnOUlzv4JfoakFIEFMwBSDAdfsq9hWYtdZ9so\ndNwPfMuZLou4JZ4kNJoP\n=2gy1\n-----END PGP SIGNATURE-----\n
5 -----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJmzfXrCRC1aQ7uu5UhlAAAGIkQAAWFfWEtO+rNEL4oWHAm0Fdx\ncKsyfH/j+eIZDIwJ7KF9hcd1VLeVO96lxCtAqW2/x7lYReM/NyfBtz0nQuwaVM01\nstesFeyJZDclN2LgHWPwuq9QM3Gt2M1hrmguDvb6oM5rvT2W0XXKNS5TlPKx3TeU\nZT1RIFfndFuvifQlZjmYMaDI9kLQS8kQtQCTjuv3Ei57uWEAbkj5r2w6SA8HA8Ti\nQxCOWzQ8FTPqyvb3iev3xLmJvXXNr+AJXbJRkIZA3Pwu8CCOmpmf6H6HeBQqMzaG\nkjqX5uqzmyAyf38z7Y+yALQkPySQbNp/de2jea1hrq7v2W78cR4+gIzti09Y1Q9R\nhi5PIXwsykb4XKdZXl3kQfEBxBh7iIbBOglLPHMDRVolQMmj6S9ezjnn0FkpXdKE\nnCPGPI/+6MzTeSPrinfO7wUpXNQocVwBEUV8Qvv8Rc/uHbKw6FLPofZepJ+Z2UmA\npyEvwcADNGXhJM8Ef9O3tdDFhdV5bSORW9bhQh97l9yPneB5+Rui1ftiqKjqR0pC\n9uPNrMoexCc1O/uEv5CotGjViOJrdzbqJxBWO9BD8i8LKNI/Fiuu4OHMQsXwxHye\nqSPrOx4zrAxrg/qn9hEPQFjj+sHwjUV3dHgpSCSqzmLZzPYrAKQS7BMERogzgNke\nT9u5cp6sbK4eI414nNvP\n=4+mC\n-----END PGP SIGNATURE-----\n
6 -----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJmzfN+CRC1aQ7uu5UhlAAAL3cQABd/cNjfNWYYv/iRI12OoHJF\n8Sqp/50IfyuyMgEJT4wGstAUi/Z632TGTQEsEBuvVbrsmlRH6myYEXIUWGEv2RM2\nD4bgykku0rZCeIJFyMjCrnMK1NYOrp3RoqD7HlWFzvpW5ZzNyXZPwafeYvsakh5K\nPt1V4WjYzEioPWTJIgAKEUr8Pr3zePCO0zY7NXAhsgobaxUK0PcDoTt0WeHkxhBL\nwt2f+FylBKSbh3Fw6zv2xINGxEQWwSWa3EZTvsu5s3aTl0OOtz/1vLQ+gCYqhU9Z\n4VHdgj0j2+AbETBPiou8E/szSuuffRTwgSkobbAkgOF6uyFg3gxRM7zjXsDiN4o6\nwxNx72y1/tDanfUCRX/e3zcUh8gqtB4RggHoKtdun7UnAck+kgCeMH+ru5eNINxH\n9K/1xL70FoasI0Kd9k4L0Q2YsoXOhgHVoQFBKC0AgiIWaJrjpx/C+MeMeXj/XgNm\nQfybWEiXHL9S4jq0Wy/3he0BvwwiHcy682/FyeVx0fxUEHw4DzL0LkcBctm+yUKS\n3MFADYcgTiVlafXFdej9mtiElSGINNh/C83Vj+LQdG6axzHeiWX6mGiRCgfEZhpT\n+XhMnpI7XyQoeSyREnuVUx+E2PWsqG0gFocq0KV3UDnc5ZGE3JtH2L9w6CKlW7YA\n8RlhDNCiHgAk2O7S2iE/\n=Z8qV\n-----END PGP SIGNATURE-----\n
commit.verification.payload
1 <NA>
2 tree 8299635297b6b5e61c9522b525a5414993405e08\nparent 1d17672a54305170dc75c251f8ae69a85c0bea37\nauthor Davis Vaughan <davis@rstudio.com> 1727797993 -0400\ncommitter GitHub <noreply@github.com> 1727797993 -0400\n\nRemove not needed `new_expanded_quosures()` (#7090)\n\n
3 tree 9144e07c3b09886b79b64a76904501f565f1b6c8\nparent cfb25a030f9f7c39f77fed2c97f3fa7b15a55e84\nauthor Mike Du <58779940+ilovemane@users.noreply.github.com> 1724776299 +0100\ncommitter GitHub <noreply@github.com> 1724776299 -0400\n\nAdd error message when passing an array in `conditions` (#7069)\n\n* add error message when passing a matrix\r\n\r\ntidyverse day\r\n\r\n* Update test-vec-case-when.R\r\n\r\n* fixes\r\n\r\n* Tweaks\r\n\r\n* NEWS bullet\r\n\r\n* Update snap\r\n\r\n---------\r\n\r\nCo-authored-by: Davis Vaughan <davis@posit.co>
4 tree 3d6a95112c5bc59e6a60d245704984e722aeacad\nparent 85e94fcde02ad49c77510991078899278489fd8f\nauthor Adam <adampeterso@gmail.com> 1724776257 -0400\ncommitter GitHub <noreply@github.com> 1724776257 -0400\n\nAdd documentation clarifying appropriate use of weights in `slice_sample()` (#7052)\n\n* Add documentation clarifying appropriate use of weights in dplyr's `slice_sample()`.\r\n\r\n* Add documentation to relevant .Rd file.\r\n\r\n* Tweak documentation placement a bit\r\n\r\n---------\r\n\r\nCo-authored-by: Davis Vaughan <davis@posit.co>
5 tree 852e02c951972c5a4e9a831c68e2425085e7fdd5\nparent e0555277878ed174d40bda86690674ecdc27db55\nauthor Núria Mercadé-Besora <61558739+nmercadeb@users.noreply.github.com> 1724773867 +0100\ncommitter GitHub <noreply@github.com> 1724773867 -0400\n\nDocument that `group_by()` works with data-masking (#7071)\n\n
6 tree 18d4e6763ceb64f3967f9addb712ad0878798b38\nparent 173b4232bff810f563e1739211ea7545ed0651e6\nauthor James Wade <github@jameshwade.com> 1724773246 -0400\ncommitter GitHub <noreply@github.com> 1724773246 -0400\n\nAdd a `ptype` argument to `between()` (#7073)\n\n* add ptype argument to between Fixes #6906\r\n\r\n* update NEWS with ptype argument\r\n\r\n* document() and add tests\r\n\r\n* add back see also\r\n\r\n* add back see also\r\n\r\n* Redocument\r\n\r\n* Trim trailing whitespace\r\n\r\n* Remove extra line\r\n\r\n* Minor docs tweaks\r\n\r\n* require names ptype, update tests, update function documentation\r\n\r\n* remove unnecessary if else blocks\r\n\r\n* Tweak NEWS bullet\r\n\r\n* A few more tweaks\r\n\r\n* Few more tweaks\r\n\r\n---------\r\n\r\nCo-authored-by: Davis Vaughan <davis@posit.co>
url
1 https://api.github.com/repos/tidyverse/dplyr/commits/fb25640fa1eb74746a7a74a06090045106e5d20f
2 https://api.github.com/repos/tidyverse/dplyr/commits/e4e9a295a373b85e02ae084a23f12e9212a72b98
3 https://api.github.com/repos/tidyverse/dplyr/commits/1d17672a54305170dc75c251f8ae69a85c0bea37
4 https://api.github.com/repos/tidyverse/dplyr/commits/cfb25a030f9f7c39f77fed2c97f3fa7b15a55e84
5 https://api.github.com/repos/tidyverse/dplyr/commits/85e94fcde02ad49c77510991078899278489fd8f
6 https://api.github.com/repos/tidyverse/dplyr/commits/e0555277878ed174d40bda86690674ecdc27db55
html_url
1 https://github.com/tidyverse/dplyr/commit/fb25640fa1eb74746a7a74a06090045106e5d20f
2 https://github.com/tidyverse/dplyr/commit/e4e9a295a373b85e02ae084a23f12e9212a72b98
3 https://github.com/tidyverse/dplyr/commit/1d17672a54305170dc75c251f8ae69a85c0bea37
4 https://github.com/tidyverse/dplyr/commit/cfb25a030f9f7c39f77fed2c97f3fa7b15a55e84
5 https://github.com/tidyverse/dplyr/commit/85e94fcde02ad49c77510991078899278489fd8f
6 https://github.com/tidyverse/dplyr/commit/e0555277878ed174d40bda86690674ecdc27db55
comments_url
1 https://api.github.com/repos/tidyverse/dplyr/commits/fb25640fa1eb74746a7a74a06090045106e5d20f/comments
2 https://api.github.com/repos/tidyverse/dplyr/commits/e4e9a295a373b85e02ae084a23f12e9212a72b98/comments
3 https://api.github.com/repos/tidyverse/dplyr/commits/1d17672a54305170dc75c251f8ae69a85c0bea37/comments
4 https://api.github.com/repos/tidyverse/dplyr/commits/cfb25a030f9f7c39f77fed2c97f3fa7b15a55e84/comments
5 https://api.github.com/repos/tidyverse/dplyr/commits/85e94fcde02ad49c77510991078899278489fd8f/comments
6 https://api.github.com/repos/tidyverse/dplyr/commits/e0555277878ed174d40bda86690674ecdc27db55/comments
author.login author.id author.node_id
1 krlmlr 1741643 MDQ6VXNlcjE3NDE2NDM=
2 DavisVaughan 19150088 MDQ6VXNlcjE5MTUwMDg4
3 ilovemane 58779940 MDQ6VXNlcjU4Nzc5OTQw
4 apeterson91 4251291 MDQ6VXNlcjQyNTEyOTE=
5 nmercadeb 61558739 MDQ6VXNlcjYxNTU4NzM5
6 JamesHWade 6314313 MDQ6VXNlcjYzMTQzMTM=
author.avatar_url author.gravatar_id
1 https://avatars.githubusercontent.com/u/1741643?v=4
2 https://avatars.githubusercontent.com/u/19150088?v=4
3 https://avatars.githubusercontent.com/u/58779940?v=4
4 https://avatars.githubusercontent.com/u/4251291?v=4
5 https://avatars.githubusercontent.com/u/61558739?v=4
6 https://avatars.githubusercontent.com/u/6314313?v=4
author.url author.html_url
1 https://api.github.com/users/krlmlr https://github.com/krlmlr
2 https://api.github.com/users/DavisVaughan https://github.com/DavisVaughan
3 https://api.github.com/users/ilovemane https://github.com/ilovemane
4 https://api.github.com/users/apeterson91 https://github.com/apeterson91
5 https://api.github.com/users/nmercadeb https://github.com/nmercadeb
6 https://api.github.com/users/JamesHWade https://github.com/JamesHWade
author.followers_url
1 https://api.github.com/users/krlmlr/followers
2 https://api.github.com/users/DavisVaughan/followers
3 https://api.github.com/users/ilovemane/followers
4 https://api.github.com/users/apeterson91/followers
5 https://api.github.com/users/nmercadeb/followers
6 https://api.github.com/users/JamesHWade/followers
author.following_url
1 https://api.github.com/users/krlmlr/following{/other_user}
2 https://api.github.com/users/DavisVaughan/following{/other_user}
3 https://api.github.com/users/ilovemane/following{/other_user}
4 https://api.github.com/users/apeterson91/following{/other_user}
5 https://api.github.com/users/nmercadeb/following{/other_user}
6 https://api.github.com/users/JamesHWade/following{/other_user}
author.gists_url
1 https://api.github.com/users/krlmlr/gists{/gist_id}
2 https://api.github.com/users/DavisVaughan/gists{/gist_id}
3 https://api.github.com/users/ilovemane/gists{/gist_id}
4 https://api.github.com/users/apeterson91/gists{/gist_id}
5 https://api.github.com/users/nmercadeb/gists{/gist_id}
6 https://api.github.com/users/JamesHWade/gists{/gist_id}
author.starred_url
1 https://api.github.com/users/krlmlr/starred{/owner}{/repo}
2 https://api.github.com/users/DavisVaughan/starred{/owner}{/repo}
3 https://api.github.com/users/ilovemane/starred{/owner}{/repo}
4 https://api.github.com/users/apeterson91/starred{/owner}{/repo}
5 https://api.github.com/users/nmercadeb/starred{/owner}{/repo}
6 https://api.github.com/users/JamesHWade/starred{/owner}{/repo}
author.subscriptions_url
1 https://api.github.com/users/krlmlr/subscriptions
2 https://api.github.com/users/DavisVaughan/subscriptions
3 https://api.github.com/users/ilovemane/subscriptions
4 https://api.github.com/users/apeterson91/subscriptions
5 https://api.github.com/users/nmercadeb/subscriptions
6 https://api.github.com/users/JamesHWade/subscriptions
author.organizations_url
1 https://api.github.com/users/krlmlr/orgs
2 https://api.github.com/users/DavisVaughan/orgs
3 https://api.github.com/users/ilovemane/orgs
4 https://api.github.com/users/apeterson91/orgs
5 https://api.github.com/users/nmercadeb/orgs
6 https://api.github.com/users/JamesHWade/orgs
author.repos_url
1 https://api.github.com/users/krlmlr/repos
2 https://api.github.com/users/DavisVaughan/repos
3 https://api.github.com/users/ilovemane/repos
4 https://api.github.com/users/apeterson91/repos
5 https://api.github.com/users/nmercadeb/repos
6 https://api.github.com/users/JamesHWade/repos
author.events_url
1 https://api.github.com/users/krlmlr/events{/privacy}
2 https://api.github.com/users/DavisVaughan/events{/privacy}
3 https://api.github.com/users/ilovemane/events{/privacy}
4 https://api.github.com/users/apeterson91/events{/privacy}
5 https://api.github.com/users/nmercadeb/events{/privacy}
6 https://api.github.com/users/JamesHWade/events{/privacy}
author.received_events_url author.type
1 https://api.github.com/users/krlmlr/received_events User
2 https://api.github.com/users/DavisVaughan/received_events User
3 https://api.github.com/users/ilovemane/received_events User
4 https://api.github.com/users/apeterson91/received_events User
5 https://api.github.com/users/nmercadeb/received_events User
6 https://api.github.com/users/JamesHWade/received_events User
author.user_view_type author.site_admin committer.login committer.id
1 public FALSE krlmlr 1741643
2 public FALSE web-flow 19864447
3 public FALSE web-flow 19864447
4 public FALSE web-flow 19864447
5 public FALSE web-flow 19864447
6 public FALSE web-flow 19864447
committer.node_id committer.avatar_url
1 MDQ6VXNlcjE3NDE2NDM= https://avatars.githubusercontent.com/u/1741643?v=4
2 MDQ6VXNlcjE5ODY0NDQ3 https://avatars.githubusercontent.com/u/19864447?v=4
3 MDQ6VXNlcjE5ODY0NDQ3 https://avatars.githubusercontent.com/u/19864447?v=4
4 MDQ6VXNlcjE5ODY0NDQ3 https://avatars.githubusercontent.com/u/19864447?v=4
5 MDQ6VXNlcjE5ODY0NDQ3 https://avatars.githubusercontent.com/u/19864447?v=4
6 MDQ6VXNlcjE5ODY0NDQ3 https://avatars.githubusercontent.com/u/19864447?v=4
committer.gravatar_id committer.url
1 https://api.github.com/users/krlmlr
2 https://api.github.com/users/web-flow
3 https://api.github.com/users/web-flow
4 https://api.github.com/users/web-flow
5 https://api.github.com/users/web-flow
6 https://api.github.com/users/web-flow
committer.html_url committer.followers_url
1 https://github.com/krlmlr https://api.github.com/users/krlmlr/followers
2 https://github.com/web-flow https://api.github.com/users/web-flow/followers
3 https://github.com/web-flow https://api.github.com/users/web-flow/followers
4 https://github.com/web-flow https://api.github.com/users/web-flow/followers
5 https://github.com/web-flow https://api.github.com/users/web-flow/followers
6 https://github.com/web-flow https://api.github.com/users/web-flow/followers
committer.following_url
1 https://api.github.com/users/krlmlr/following{/other_user}
2 https://api.github.com/users/web-flow/following{/other_user}
3 https://api.github.com/users/web-flow/following{/other_user}
4 https://api.github.com/users/web-flow/following{/other_user}
5 https://api.github.com/users/web-flow/following{/other_user}
6 https://api.github.com/users/web-flow/following{/other_user}
committer.gists_url
1 https://api.github.com/users/krlmlr/gists{/gist_id}
2 https://api.github.com/users/web-flow/gists{/gist_id}
3 https://api.github.com/users/web-flow/gists{/gist_id}
4 https://api.github.com/users/web-flow/gists{/gist_id}
5 https://api.github.com/users/web-flow/gists{/gist_id}
6 https://api.github.com/users/web-flow/gists{/gist_id}
committer.starred_url
1 https://api.github.com/users/krlmlr/starred{/owner}{/repo}
2 https://api.github.com/users/web-flow/starred{/owner}{/repo}
3 https://api.github.com/users/web-flow/starred{/owner}{/repo}
4 https://api.github.com/users/web-flow/starred{/owner}{/repo}
5 https://api.github.com/users/web-flow/starred{/owner}{/repo}
6 https://api.github.com/users/web-flow/starred{/owner}{/repo}
committer.subscriptions_url
1 https://api.github.com/users/krlmlr/subscriptions
2 https://api.github.com/users/web-flow/subscriptions
3 https://api.github.com/users/web-flow/subscriptions
4 https://api.github.com/users/web-flow/subscriptions
5 https://api.github.com/users/web-flow/subscriptions
6 https://api.github.com/users/web-flow/subscriptions
committer.organizations_url
1 https://api.github.com/users/krlmlr/orgs
2 https://api.github.com/users/web-flow/orgs
3 https://api.github.com/users/web-flow/orgs
4 https://api.github.com/users/web-flow/orgs
5 https://api.github.com/users/web-flow/orgs
6 https://api.github.com/users/web-flow/orgs
committer.repos_url
1 https://api.github.com/users/krlmlr/repos
2 https://api.github.com/users/web-flow/repos
3 https://api.github.com/users/web-flow/repos
4 https://api.github.com/users/web-flow/repos
5 https://api.github.com/users/web-flow/repos
6 https://api.github.com/users/web-flow/repos
committer.events_url
1 https://api.github.com/users/krlmlr/events{/privacy}
2 https://api.github.com/users/web-flow/events{/privacy}
3 https://api.github.com/users/web-flow/events{/privacy}
4 https://api.github.com/users/web-flow/events{/privacy}
5 https://api.github.com/users/web-flow/events{/privacy}
6 https://api.github.com/users/web-flow/events{/privacy}
committer.received_events_url committer.type
1 https://api.github.com/users/krlmlr/received_events User
2 https://api.github.com/users/web-flow/received_events User
3 https://api.github.com/users/web-flow/received_events User
4 https://api.github.com/users/web-flow/received_events User
5 https://api.github.com/users/web-flow/received_events User
6 https://api.github.com/users/web-flow/received_events User
committer.user_view_type committer.site_admin
1 public FALSE
2 public FALSE
3 public FALSE
4 public FALSE
5 public FALSE
6 public FALSE
parents
1 e4e9a295a373b85e02ae084a23f12e9212a72b98, https://api.github.com/repos/tidyverse/dplyr/commits/e4e9a295a373b85e02ae084a23f12e9212a72b98, https://github.com/tidyverse/dplyr/commit/e4e9a295a373b85e02ae084a23f12e9212a72b98
2 1d17672a54305170dc75c251f8ae69a85c0bea37, https://api.github.com/repos/tidyverse/dplyr/commits/1d17672a54305170dc75c251f8ae69a85c0bea37, https://github.com/tidyverse/dplyr/commit/1d17672a54305170dc75c251f8ae69a85c0bea37
3 cfb25a030f9f7c39f77fed2c97f3fa7b15a55e84, https://api.github.com/repos/tidyverse/dplyr/commits/cfb25a030f9f7c39f77fed2c97f3fa7b15a55e84, https://github.com/tidyverse/dplyr/commit/cfb25a030f9f7c39f77fed2c97f3fa7b15a55e84
4 85e94fcde02ad49c77510991078899278489fd8f, https://api.github.com/repos/tidyverse/dplyr/commits/85e94fcde02ad49c77510991078899278489fd8f, https://github.com/tidyverse/dplyr/commit/85e94fcde02ad49c77510991078899278489fd8f
5 e0555277878ed174d40bda86690674ecdc27db55, https://api.github.com/repos/tidyverse/dplyr/commits/e0555277878ed174d40bda86690674ecdc27db55, https://github.com/tidyverse/dplyr/commit/e0555277878ed174d40bda86690674ecdc27db55
6 173b4232bff810f563e1739211ea7545ed0651e6, https://api.github.com/repos/tidyverse/dplyr/commits/173b4232bff810f563e1739211ea7545ed0651e6, https://github.com/tidyverse/dplyr/commit/173b4232bff810f563e1739211ea7545ed0651e6
Then, we can extract specific data from the commits including details like the commit message, author, and date. We will create a simplified data frame with just these columns.
<- tibble(
commits_df sha = commits$sha,
author = commits$commit$author$name,
date = commits$commit$author$date,
message = commits$commit$message)
commits_df
# A tibble: 30 × 4
sha author date message
<chr> <chr> <chr> <chr>
1 fb25640fa1eb74746a7a74a06090045106e5d20f Kirill Müller 2024-1… "Move …
2 e4e9a295a373b85e02ae084a23f12e9212a72b98 Davis Vaughan 2024-1… "Remov…
3 1d17672a54305170dc75c251f8ae69a85c0bea37 Mike Du 2024-0… "Add e…
4 cfb25a030f9f7c39f77fed2c97f3fa7b15a55e84 Adam 2024-0… "Add d…
5 85e94fcde02ad49c77510991078899278489fd8f Núria Mercadé-Besora 2024-0… "Docum…
6 e0555277878ed174d40bda86690674ecdc27db55 James Wade 2024-0… "Add a…
7 173b4232bff810f563e1739211ea7545ed0651e6 Kirill Müller 2024-0… "Fix e…
8 b2c7e047081301afe2ca376c508491d77bea4bde Jeremy Winget 2024-0… "Fix `…
9 663d7f07140b45d5d3a256465728d9d5c35b9455 Rodrigo Dal Ben 2024-0… "Fix t…
10 cdc99196a2d80f14b48ceb5edad328ab34a5bf65 Davis Vaughan 2024-0… "Add `…
# ℹ 20 more rows
Authentication
Authenticating with the GitHub API via an API key allows you to send much more requests to the API.
API access keys for the GitHub API are called personal access tokens (PAT) and the documentation explains how to generate a PAT once you have logged into your GitHub account.
To create a PAT: You can create a PAT from your GitHub account (Settings > Developer settings > Personal access tokens). It’s a good idea to only grant “read” permissions.
First, please be careful with your PATs and never publish them.
It is suggested you keep them in your .Renviron
file which looks something like this on the inside:
GITHUB_PAT = <add my GitHub PAT here>
If you do not have an .Renviron
file in your home directory, you can make one:
cd ~
touch .Renviron
If you want additional guidance on where you should store them, I like this post:
Assuming you have created and stored an API key in the .Renviron
file in your home directory, you can fetch it with the Sys.getenv()
function and use the PAT in our httr2
request.
# Read the PAT from environment variables
<- Sys.getenv("GITHUB_PAT")
github_pat
# Make the GET request with PAT for authentication
<- request(url) %>%
response req_auth_bearer_token(github_pat) %>%
req_perform()
Now, we check the response as before
if (resp_status(response) == 200) {
<- resp_body_json(response, simplifyVector = TRUE)
commits <- tibble(
commits_df sha = commits$sha,
author = commits$commit$author$name,
date = commits$commit$author$date,
message = commits$commit$message)
commits_dfelse {
} message("Failed to retrieve data. Status code: ", resp_status(response))
}
# A tibble: 30 × 4
sha author date message
<chr> <chr> <chr> <chr>
1 fb25640fa1eb74746a7a74a06090045106e5d20f Kirill Müller 2024-1… "Move …
2 e4e9a295a373b85e02ae084a23f12e9212a72b98 Davis Vaughan 2024-1… "Remov…
3 1d17672a54305170dc75c251f8ae69a85c0bea37 Mike Du 2024-0… "Add e…
4 cfb25a030f9f7c39f77fed2c97f3fa7b15a55e84 Adam 2024-0… "Add d…
5 85e94fcde02ad49c77510991078899278489fd8f Núria Mercadé-Besora 2024-0… "Docum…
6 e0555277878ed174d40bda86690674ecdc27db55 James Wade 2024-0… "Add a…
7 173b4232bff810f563e1739211ea7545ed0651e6 Kirill Müller 2024-0… "Fix e…
8 b2c7e047081301afe2ca376c508491d77bea4bde Jeremy Winget 2024-0… "Fix `…
9 663d7f07140b45d5d3a256465728d9d5c35b9455 Rodrigo Dal Ben 2024-0… "Fix t…
10 cdc99196a2d80f14b48ceb5edad328ab34a5bf65 Davis Vaughan 2024-0… "Add `…
# ℹ 20 more rows
Example: Learn about Stephanie
Let’s start by using the GitHub API to learn information about myself (Stephanie Hicks). First, let’s check out https://api.github.com/users/stephaniehicks.
Now, we have decided to explore https://api.github.com/users/stephaniehicks/repos.
To use httr2
, start by creating a request:
<- "stephaniehicks"
owner <- paste0("https://api.github.com/users/", owner,"/repos")
url
# Make the GET request with PAT for authentication
<- request(url) %>%
response req_auth_bearer_token(github_pat) %>%
req_perform()
<- resp_body_json(response, simplifyVector = TRUE) stephanie
We convert the response to a parsed JSON file (or a list).
A bit of EDA fun
Let’s have a bit of fun and explore some questions:
- How many public repositories do I have?
$forks stephanie
[1] 4 0 1 0 1 0 0 2 0 0 0 4 1 1 0 1 11 0 0 3 0 3 0 1 1
[26] 0 0 1 0 0
What’s the most popular language?
table(stephanie$language)
HTML Jupyter Notebook R Ruby
7 1 12 2
TeX
1
To find out how many repos that I have with open issues, we can just create a table:
# how many repos have open issues?
table(stephanie$open_issues)
0 1
27 3
Whew! Not as many as I thought.
Finally, I will leave you with a few other examples of using GitHub API: