It's amazing what you can find on the internet these days (gosh I hate GH)
Now, get some "live" data:
httr::GET(
url = "http://api.cbssports.com/fantasy/league/rosters",
query = list(
version = "3.0",
team_id = "all",
response_format = "JSON",
access_token = "U2FsdGVkX1_UOTKnzQ2JGvAyTUPuawlVQE9PoIjeo0aDg2RSJO3VH-J9zNT4uLFubhcDdHD8XXjPy-M04ZKlZLacVlVg7E-B05OCGJ0RgS9sg6WdnK_Me6_KZdaWia6pq_QO3wPCTq6dPRKvv2_AHw"
)
) -> res
httr::warn_for_status(res) # some of the leaked access tokens no longer work
out <- httr::content(res, as = "text", encoding = "UTF-8") # what a terrible HTTP server/endpoint
out <- jsonlite::fromJSON(out) # JSON that was not meant for data science
At this point we have a similar data structure as your example at the top. HOWEVER you should place an emphasis on the word 'similar'. This result set is missing that "salary" and "contract" data you have. However, it's ugly enough that you can likely extrapolate.
We're doing this to reduce confusing $ use below.
teams <- out$body$rosters$teams
players <- teams$players
And, everybody needs some base R practice every now and again:
do.call(
rbind.data.frame,
mapply(function(team_df, players_df) {
players_df[,c("firstname", "on_waivers", "photo", "eligible_for_offense_and_defense",
"position", "update_type", "roster_pos", "lastname", "eligible", "age",
"is_locked", "elias_id", "ytd_points", "owned_by_team_id", "bats",
"roster_status", "percentstarted", "profile_link", "id", "is_keeper",
"pro_status", "on_waivers_until", "profile_url", "jersey", "fullname",
"percentowned", "headline", "pro_team", "throws", "starting-pitcher-today",
"injury", "return")] -> pdf
pdf[["projected_points"]] <- team_df[["projected_points"]]
pdf[["long_abbr"]] <- team_df[["long_abbr"]]
pdf[["lineup_status"]] <- team_df[["lineup_status"]]
pdf[["short_name"]] <- team_df[["short_name"]]
pdf[["division"]] <- team_df[["division"]]
pdf[["name"]] <- team_df[["name"]]
pdf[["logo"]] <- team_df[["logo"]]
pdf[["abbr"]] <- team_df[["abbr"]]
pdf[["point"]] <- team_df[["point"]]
pdf[["id"]] <- team_df[["id"]]
pdf
}, split(teams, 1:nrow(teams)), players, SIMPLIFY = FALSE, USE.NAMES = FALSE)
) %>%
dplyr::glimpse()
Which looks like:
## Observations: 592
## Variables: 41
## $ firstname <chr> "Wilson", "Mike", "Matt", "Jonathan", "Matt"…
## $ on_waivers <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ photo <chr> "http://sports.cbsimg.net/images/baseball/ml…
## $ eligible_for_offense_and_defense <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ position <chr> "C", "C", "1B", "2B", "1B", "SS", "SS", "2B"…
## $ update_type <chr> "normal", "normal", "normal", "normal", "nor…
## $ roster_pos <chr> "C", "C", "1B", "2B", "3B", "SS", "MI", "MI"…
## $ lastname <chr> "Ramos", "Zunino", "Olson", "Villar", "Carpe…
## $ eligible <chr> "C,U", "C,U", "1B,CI,U", "2B,MI,U", "1B,3B,C…
## $ age <int> 31, 27, 24, 27, 33, 25, 24, 30, 30, 24, 20, …
## $ is_locked <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ elias_id <chr> "RAM571096", "ZUN334886", "OLS682188", "VIL4…
## $ ytd_points <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ owned_by_team_id <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
## $ bats <chr> "R", "R", "L", "S", "L", "R", "L", "R", "L",…
## $ roster_status <chr> "A", "A", "A", "A", "A", "A", "A", "A", "A",…
## $ percentstarted <chr> "79%", "37%", "71%", "46%", "92%", "52%", "3…
## $ profile_link <chr> "<a class='playerLink' aria-label=' Wilson R…
## $ id <chr> "1", "1", "1", "1", "1", "1", "1", "1", "1",…
## $ is_keeper <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ pro_status <chr> "A", "A", "A", "A", "A", "A", "A", "A", "A",…
## $ on_waivers_until <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
## $ profile_url <chr> "http://rfbl2006.baseball.cbssports.com/play…
## $ jersey <chr> "40", "10", "28", "2", "13", "7", "5", "15",…
## $ fullname <chr> "Wilson Ramos", "Mike Zunino", "Matt Olson",…
## $ percentowned <chr> "89%", "52%", "87%", "59%", "95%", "69%", "5…
## $ headline <chr> "Mets' Wilson Ramos: Flashing power early", …
## $ pro_team <chr> "NYM", "TB", "OAK", "BAL", "STL", "CHW", "LA…
## $ throws <chr> "R", "R", "R", "R", "R", "R", "R", "R", "R",…
## $ `starting-pitcher-today` <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
## $ injury <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
## $ return <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
## $ projected_points <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
## $ long_abbr <chr> "Coco", "Coco", "Coco", "Coco", "Coco", "Coc…
## $ lineup_status <chr> "ok", "ok", "ok", "ok", "ok", "ok", "ok", "o…
## $ short_name <chr> "#IKnow", "#IKnow", "#IKnow", "#IKnow", "#IK…
## $ division <chr> "", "", "", "", "", "", "", "", "", "", "", …
## $ name <chr> "#IKnowI'mBetterThanThis", "#IKnowI'mBetterT…
## $ logo <chr> "http://rfbl2006.baseball.cbssports.com/imag…
## $ abbr <chr> "CFY", "CFY", "CFY", "CFY", "CFY", "CFY", "C…
## $ point <chr> "20190320", "20190320", "20190320", "2019032…
So, the general idea is to separate the ugly fields from the non-ugly ones and bind them all together separately.
You can change this up to make it a bit more dynamic by getting the gnarlier names at each distinct step programmatically (e.g. sapply(teams, is.atomic)) , using base R set functions to partition them out and then have the rbinding be a bit more dynamic.