R中的递归JSON树索引提取
Recursive JSON tree index extraction in R
我在提取 R 中 JSON 树的结构时遇到困难。
考虑以下场景(从 Reddit.com 中提取数据):
library(RJSON)
URL = "http://www.reddit.com/r/newzealand/comments/3p25qy/where_can_i_get_a_chromecast_2/"
X = paste0(gsub("\?ref=search_posts$","",URL),".json?limit=500")
raw_data = fromJSON(readLines(X, warn = FALSE))
main.node = raw_data[[2]]$data$children
replies = main.node[[2]]$data$replies
node = replies$data$children
现在 main.node[[1]]
包含与 URL 上的第一条评论相对应的属性,而 replies
包含有关对第二条评论的回复的信息。我们可以通过查看 replies$data$children
找到这些回复。但是一个回复可以嵌套在另一个回复中,这就是为什么要得到它们,我们需要递归地解析树。
下面的table表示评论的结构以及我试图获得的输出:
row comment | reply_to_comment | reply_to_reply | desired_output
1) * | | | 1
2) | * | | 1.1
3) | * | | 1.2
4) | | * | 1.2.1
5) | | * | 1.2.2
6) | * | | 1.3
7) * | | | 2
8) | * | | 2.1
9) | | * | 2.1.1
到目前为止我能得到的最接近的代码由以下代码表示:
node = main.node
reply_function = function(node){
struct = seq_along(node)
replies = node$data$replies
rep.node = if (is.list(replies)) replies$data$children else NULL
return(list(struct,lapply(rep.node,function(x) reply_function(x))))
}
[1] 1 2 1 2 1 2 1 2 1 2 1 2 1 2
请注意,如果您重新运行,数字可能会发生变化 -- 此数据是动态的。
然而,这种方法不包含整个线程的历史,它只告诉我们某个节点可能有多少回复,而不管它是原始评论还是对回复的回复。
如果有人对此有任何建议,请告诉我,我很乐意听取您的意见。
非常感谢!
我会避开递归,只使用 unlist
,例如:
library(jsonlite)
URL = "http://www.reddit.com/r/newzealand/comments/3p25qy/where_can_i_get_a_chromecast_2/"
X = paste0(gsub("\?ref=search_posts$","",URL),".json?limit=500")
raw_data = fromJSON(readLines(X, warn = FALSE))
data = unlist(raw_data)
comments = names(data)[grepl('\.body$', names(data))]
comments = data[comments]
names(comments) <- NULL
comments
这是一个使用 previously answered rjson reader.
修改版本的方法
首先,我们可以修改之前的递归reader来记录它在哪一层:
get.comments <- function(node, depth=0) {
if(is.null(node)) {return(list())}
comment <- node$data$body
replies <- node$data$replies
reply.nodes <- if (is.list(replies)) replies$data$children else NULL
return(list(paste0(comment, " ", depth), lapply(1:length(reply.nodes), function(x) get.comments(reply.nodes[[x]], paste0(depth, ".", x)))))
}
现在读取您的数据:
library(rjson)
URL = "http://www.reddit.com/r/newzealand/comments/3p25qy/where_can_i_get_a_chromecast_2/"
X = paste0(gsub("\?ref=search_posts$","",URL),".json?limit=500")
rawdat <- fromJSON(readLines(X, warn = FALSE))
main.node <- rawdat[[2]]$data$children
然后递归应用函数并取消列出:
txt <- unlist(lapply(1:length(main.node), function(x) get.comments(main.node[[x]], x)))
现在txt是评论的一个向量,级别在最后。例如
"Holy fuck, thank you! Didn't realise this was actually a thing.\n\nfreeeedom 1.1"
我们可以通过终端space拆分,得到data.frame:
z<-as.data.frame(do.call(rbind, strsplit(txt, ' (?=[^ ]+$)', perl = TRUE)))
V2
1 1
2 1.1
3 1.1.1
4 1.1.1.1
5 2
6 3
7 4
8 4.1
9 4.2
我在提取 R 中 JSON 树的结构时遇到困难。
考虑以下场景(从 Reddit.com 中提取数据):
library(RJSON)
URL = "http://www.reddit.com/r/newzealand/comments/3p25qy/where_can_i_get_a_chromecast_2/"
X = paste0(gsub("\?ref=search_posts$","",URL),".json?limit=500")
raw_data = fromJSON(readLines(X, warn = FALSE))
main.node = raw_data[[2]]$data$children
replies = main.node[[2]]$data$replies
node = replies$data$children
现在 main.node[[1]]
包含与 URL 上的第一条评论相对应的属性,而 replies
包含有关对第二条评论的回复的信息。我们可以通过查看 replies$data$children
找到这些回复。但是一个回复可以嵌套在另一个回复中,这就是为什么要得到它们,我们需要递归地解析树。
下面的table表示评论的结构以及我试图获得的输出:
row comment | reply_to_comment | reply_to_reply | desired_output
1) * | | | 1
2) | * | | 1.1
3) | * | | 1.2
4) | | * | 1.2.1
5) | | * | 1.2.2
6) | * | | 1.3
7) * | | | 2
8) | * | | 2.1
9) | | * | 2.1.1
到目前为止我能得到的最接近的代码由以下代码表示:
node = main.node
reply_function = function(node){
struct = seq_along(node)
replies = node$data$replies
rep.node = if (is.list(replies)) replies$data$children else NULL
return(list(struct,lapply(rep.node,function(x) reply_function(x))))
}
[1] 1 2 1 2 1 2 1 2 1 2 1 2 1 2
请注意,如果您重新运行,数字可能会发生变化 -- 此数据是动态的。
然而,这种方法不包含整个线程的历史,它只告诉我们某个节点可能有多少回复,而不管它是原始评论还是对回复的回复。
如果有人对此有任何建议,请告诉我,我很乐意听取您的意见。
非常感谢!
我会避开递归,只使用 unlist
,例如:
library(jsonlite)
URL = "http://www.reddit.com/r/newzealand/comments/3p25qy/where_can_i_get_a_chromecast_2/"
X = paste0(gsub("\?ref=search_posts$","",URL),".json?limit=500")
raw_data = fromJSON(readLines(X, warn = FALSE))
data = unlist(raw_data)
comments = names(data)[grepl('\.body$', names(data))]
comments = data[comments]
names(comments) <- NULL
comments
这是一个使用 previously answered rjson reader.
修改版本的方法首先,我们可以修改之前的递归reader来记录它在哪一层:
get.comments <- function(node, depth=0) {
if(is.null(node)) {return(list())}
comment <- node$data$body
replies <- node$data$replies
reply.nodes <- if (is.list(replies)) replies$data$children else NULL
return(list(paste0(comment, " ", depth), lapply(1:length(reply.nodes), function(x) get.comments(reply.nodes[[x]], paste0(depth, ".", x)))))
}
现在读取您的数据:
library(rjson)
URL = "http://www.reddit.com/r/newzealand/comments/3p25qy/where_can_i_get_a_chromecast_2/"
X = paste0(gsub("\?ref=search_posts$","",URL),".json?limit=500")
rawdat <- fromJSON(readLines(X, warn = FALSE))
main.node <- rawdat[[2]]$data$children
然后递归应用函数并取消列出:
txt <- unlist(lapply(1:length(main.node), function(x) get.comments(main.node[[x]], x)))
现在txt是评论的一个向量,级别在最后。例如
"Holy fuck, thank you! Didn't realise this was actually a thing.\n\nfreeeedom 1.1"
我们可以通过终端space拆分,得到data.frame:
z<-as.data.frame(do.call(rbind, strsplit(txt, ' (?=[^ ]+$)', perl = TRUE)))
V2
1 1
2 1.1
3 1.1.1
4 1.1.1.1
5 2
6 3
7 4
8 4.1
9 4.2