有没有一种方法可以有效地从JSON列提取多个属性?

时间:2019-12-02 05:20:10

标签: r tidyverse mutate jsonlite

我有一个数据框,其中有一列包含json数据。我想从此json数据中提取一些属性到数据框的命名列中。

样本数据

json_col = c('{"name":"john"}','{"name":"doe","points": 10}', '{"name":"jane", "points": 20}')
id = c(1,2,3)
df <- data.frame(id, json_col)

我能够使用

实现这一目标
library(tidyverse)
library(jsonlite)

extract_json_attr <- function(from, attr, default=NA) {
  value <- from %>% 
             as.character() %>% 
             jsonlite::fromJSON(txt = .) %>%
             .[attr]

  return(ifelse(is.null(value[[1]]), default, value[[1]]))
}

df <- df %>% 
        rowwise() %>%
        mutate(name = extract_json_attr(json_col, "name"),
               points = extract_json_attr(json_col, "points", 0))

在这种情况下,extract_json_attr需要多次解析json列才能提取每个属性。

有一种更好的方法来一次提取所有属性吗?

我尝试过此函数以列表形式返回多个值,但是我无法将其与mutate一起使用来设置多个列。

extract_multiple <- function(from, attributes){
  values <- from %>% 
             as.character() %>% 
             jsonlite::fromJSON(txt = .) %>%
             .[attributes]
  return (values)
} 

我可以使用此功能提取所需的值

extract_multiple(df$json_col[1],c('name','points'))
extract_multiple(df$json_col[2],c('name','points')) 

但是不能将其应用于一次设置多个列。有没有更好的方法可以有效地做到这一点?

2 个答案:

答案 0 :(得分:2)

这是使用bind_rows中的dplyr的一种方式

dplyr::bind_rows(lapply(as.character(df$json_col), jsonlite::fromJSON))

# A tibble: 3 x 2
#  name  points
#  <chr>  <int>
#1 john      NA
#2 doe       10
#3 jane      20

要从功能中获取特定子集的属性,我们可以

bind_rows(lapply(as.character(df$json_col), function(x) 
          jsonlite::fromJSON(x)[c('name', 'points')]))

答案 1 :(得分:0)

在R4DS松弛通道上,我收到了将JSON数组作为列处理的另一种方法。使用该方法,我发现了另一种似乎在较大的数据集上更有效的方法。

library(tidyverse)
library(jsonlite)

extract <- function(input, fields){
    json_df <- fromJSON(txt=input)
    missing <- setdiff(fields, names(json_df))
    json_df[missing] <- NA

    return (json_df %>% select(fields))
}


df <- data.frame(id=c(1,2,3),
                 json_col=c('{"name":"john"}','{"name":"doe","points": 10}', '{"name":"jane", "points": 20}'),
                 stringsAsFactors=FALSE)
df %>%
  mutate(json_col = paste0('[',json_col,']'),
         json_col = map(json_col, function(x) extract(input=x, fields=c('name', 'points')))) %>%
  unnest(cols=c(json_col))