提取 list-in-a-list-in-a-list 以在 R 中构建数据框
extracting list-in-a-list-in-a-list to build dataframe in R
我正在尝试使用我的个人数据从 LibraryThing api 构建一个包含书籍 ID、标题、作者、评分 collection、开始和结束日期的数据框。我能够相当轻松地获得嵌套列表,并且我已经想出如何使用除日期之外的所有内容构建数据框(也许不是最好的方法,但它有效)。我的问题是日期。
我正在使用的列表通常有 20 个元素,但只有当我在我的帐户中向书中添加日期时,它才会添加 startfinishdates 元素。这导致了两个问题:
- 如果它一直存在,我可以像提取其他所有内容一样提取它,而且大多数时候它都会有 NA,而且我可以使用 cbind 使其与其他信息正确对齐
- 当我使用名称提取它并获得元素较少的 object 时,我没有办法将它加入其他所有内容(它没有书 ID)
最终,我想构建这个数据框,一个告诉我如何提取图书 ID 并将其与每个 startfinishdate 关联以便我可以加入图书 ID 的答案是可以接受的。我会把它添加到我的代码中。
我也乐于从跳跃和 re-designing 中学习更好的方法,因为我在 R 中使用列表的次数不多,而且我在多次试验和错误后组合在一起。不过,我确实想使用 R,因为最终我将使用它为我的网站创建一个 R Markdown 页面(例如,一个显示书籍完成日期的图表)。
您可以运行下面的代码并获取数据(不需要api密钥)。
library(jsonlite)
library(tidyverse)
library(assertr)
data<-fromJSON("http://www.librarything.com/api_getdata.php?userid=cau83&key=392812157&max=450&showCollections=1&responseType=json&showDates=1")
books.lst<-data$books
#create df from json
create.df<-function(item){
df<-map_df(.x=books.lst,~.x[[item]])
df2 <- t(df)
return(df2)
}
ids<-create.df(1)
titles<-create.df(2)
ratings<-create.df(12)
authors<-create.df(4)
#need to get the book id when i build the date df's
startdates.df<-map_df(.x=books.lst,~.x$startfinishdates) %>% select(started_stamp,started_date)
finishdates.df<-map_df(.x=books.lst,~.x$startfinishdates) %>% select(finished_stamp,finished_date)
collections.df<-map_df(.x=books.lst,~.x$collections)
#from assertr: will create a vector of same length as df with all values concatenated
collections.v<-col_concat(collections.df, sep = ", ")
#assemble df
books.df<-as.data.frame(cbind(ids,titles,authors,ratings,collections.v))
names(books.df)<-c("ID","Title","Author","Rating","Collections")
books.df<-books.df %>% mutate(ID=as.character(ID),Title=as.character(Title),Author=as.character(Author),
Rating=as.character(Rating),Collections=as.character(Collections))
这种方法在 tidyverse
元包之外。使用 base-R,您可以使用以下代码使其工作。
Map
会将用户定义的函数应用于参数中提供的 data$books
的每个元素,并为您的 data.frame
提取所需的字段。 Reduce
将获取所有单独的数据帧并将它们合并(或减少)为单个 data.frame booksdf
.
library(jsonlite)
data<-fromJSON("http://www.librarything.com/api_getdata.php?userid=cau83&key=392812157&max=450&showCollections=1&responseType=json&showDates=1")
booksdf=Reduce(function(x,y){rbind(x,y)},
Map(function(x){
lenofelements = length(x)
if(lenofelements>20){
if(!is.null(x$startfinishdates$started_date)){
started_date = x$startfinishdates$started_date
}else{
started_date=NA
}
if(!is.null(x$startfinishdates$started_stamp)){
started_stamp = x$startfinishdates$started_date
}else{
started_stamp=NA
}
if(!is.null(x$startfinishdates$finished_date)){
finished_date = x$startfinishdates$finished_date
}else{
finished_date=NA
}
if(!is.null(x$startfinishdates$finished_stamp)){
finished_stamp = x$startfinishdates$finished_stamp
}else{
finished_stamp=NA
}
}else{
started_stamp = NA
started_date = NA
finished_stamp = NA
finished_date = NA
}
book_id = x$book_id
title = x$title
author = x$author_fl
rating = x$rating
collections = paste(unlist(x$collections),collapse = ",")
return(data.frame(ID=book_id,Title=title,Author=author,Rating=rating,
Collections=collections,Started_date=started_date,Started_stamp=started_stamp,
Finished_date=finished_date,Finished_stamp=finished_stamp))
},data$books))
我正在尝试使用我的个人数据从 LibraryThing api 构建一个包含书籍 ID、标题、作者、评分 collection、开始和结束日期的数据框。我能够相当轻松地获得嵌套列表,并且我已经想出如何使用除日期之外的所有内容构建数据框(也许不是最好的方法,但它有效)。我的问题是日期。 我正在使用的列表通常有 20 个元素,但只有当我在我的帐户中向书中添加日期时,它才会添加 startfinishdates 元素。这导致了两个问题:
- 如果它一直存在,我可以像提取其他所有内容一样提取它,而且大多数时候它都会有 NA,而且我可以使用 cbind 使其与其他信息正确对齐
- 当我使用名称提取它并获得元素较少的 object 时,我没有办法将它加入其他所有内容(它没有书 ID)
最终,我想构建这个数据框,一个告诉我如何提取图书 ID 并将其与每个 startfinishdate 关联以便我可以加入图书 ID 的答案是可以接受的。我会把它添加到我的代码中。
我也乐于从跳跃和 re-designing 中学习更好的方法,因为我在 R 中使用列表的次数不多,而且我在多次试验和错误后组合在一起。不过,我确实想使用 R,因为最终我将使用它为我的网站创建一个 R Markdown 页面(例如,一个显示书籍完成日期的图表)。
您可以运行下面的代码并获取数据(不需要api密钥)。
library(jsonlite)
library(tidyverse)
library(assertr)
data<-fromJSON("http://www.librarything.com/api_getdata.php?userid=cau83&key=392812157&max=450&showCollections=1&responseType=json&showDates=1")
books.lst<-data$books
#create df from json
create.df<-function(item){
df<-map_df(.x=books.lst,~.x[[item]])
df2 <- t(df)
return(df2)
}
ids<-create.df(1)
titles<-create.df(2)
ratings<-create.df(12)
authors<-create.df(4)
#need to get the book id when i build the date df's
startdates.df<-map_df(.x=books.lst,~.x$startfinishdates) %>% select(started_stamp,started_date)
finishdates.df<-map_df(.x=books.lst,~.x$startfinishdates) %>% select(finished_stamp,finished_date)
collections.df<-map_df(.x=books.lst,~.x$collections)
#from assertr: will create a vector of same length as df with all values concatenated
collections.v<-col_concat(collections.df, sep = ", ")
#assemble df
books.df<-as.data.frame(cbind(ids,titles,authors,ratings,collections.v))
names(books.df)<-c("ID","Title","Author","Rating","Collections")
books.df<-books.df %>% mutate(ID=as.character(ID),Title=as.character(Title),Author=as.character(Author),
Rating=as.character(Rating),Collections=as.character(Collections))
这种方法在 tidyverse
元包之外。使用 base-R,您可以使用以下代码使其工作。
Map
会将用户定义的函数应用于参数中提供的 data$books
的每个元素,并为您的 data.frame
提取所需的字段。 Reduce
将获取所有单独的数据帧并将它们合并(或减少)为单个 data.frame booksdf
.
library(jsonlite)
data<-fromJSON("http://www.librarything.com/api_getdata.php?userid=cau83&key=392812157&max=450&showCollections=1&responseType=json&showDates=1")
booksdf=Reduce(function(x,y){rbind(x,y)},
Map(function(x){
lenofelements = length(x)
if(lenofelements>20){
if(!is.null(x$startfinishdates$started_date)){
started_date = x$startfinishdates$started_date
}else{
started_date=NA
}
if(!is.null(x$startfinishdates$started_stamp)){
started_stamp = x$startfinishdates$started_date
}else{
started_stamp=NA
}
if(!is.null(x$startfinishdates$finished_date)){
finished_date = x$startfinishdates$finished_date
}else{
finished_date=NA
}
if(!is.null(x$startfinishdates$finished_stamp)){
finished_stamp = x$startfinishdates$finished_stamp
}else{
finished_stamp=NA
}
}else{
started_stamp = NA
started_date = NA
finished_stamp = NA
finished_date = NA
}
book_id = x$book_id
title = x$title
author = x$author_fl
rating = x$rating
collections = paste(unlist(x$collections),collapse = ",")
return(data.frame(ID=book_id,Title=title,Author=author,Rating=rating,
Collections=collections,Started_date=started_date,Started_stamp=started_stamp,
Finished_date=finished_date,Finished_stamp=finished_stamp))
},data$books))