0

我正在映射json级别(x)的一系列条目。对于每个级别 x,都有嵌套级别 (x+1),其中包含一些我想要组合到数据框中的信息以及来自 x 的一些信息。

这是我用来在 R中学习purrr和处理的玩具示例。json

例如

(entry) <- level x 
   (year: 2016)         <- want this 
   (category: "physics") <- want this
       (winners)  
            (1) <- level x+1
               (name: "bob" ) <- want this 
               (id: ) <- want this 
            (2..n) <- level x+1 
               (name: "steve" ) <- want this 
               (id: ) <- want this 

制作数据框:

 name id year category 
 bob   1  2016 physics 
 steve 2  2016 physics
 mel   3  2016 chemistry .. etc

我已经解决了这个问题,但它map在 x 的每一层都使用了嵌套,并且非常脆弱

 library(purr)
 library(tidyverse)
 library(stringr)
 library(jsonlite)
 # get example data 
 winners <- fromJSON("http://api.nobelprize.org/v1/prize.json", simplifyDataFrame=FALSE) 


 x <- winners$prizes %>%
          map_df(function(prize) {
              map_df(prize$laureates, function(person) {
                   tibble(id = person$id, firstname = person$firstname, 
                         surname=ifelse(!is.null(person$surname),
                              person$surname, NA),
          category=prize$category, year=prize$year)
  })
}) 

有没有更好的方法来做到这一点?上述代码的担忧:

  • 有没有不必嵌套 map 调用的替代方法?是否有一些purrr我不知道我可以使用的功能?
  • 处理丢失键的更好方法?在上面,测试数据的元素 22 没有姓氏键。如果不将 surfname 包装在 an 中ifelse,它将失败。从技术上讲,我应该将所有内容都包含在tibble调用中,ifelse但是它变得非常冗长并且感觉不是正确的解决方案。
4

1 回答 1

1

你所做的是——正如他们在新英格兰所说的——非常好,尤其是因为它产生了一个其他人可以阅读的工作解决方案(即两个最重要的事情)。

这是我要采取的方法(只是略有不同):

winners <- fromJSON("http://api.nobelprize.org/v1/prize.json", simplifyDataFrame=FALSE) 

extract_laureates <- function(x) {

  surname <- NULL

  map_df(x$laureates, flatten_df) %>% 
    mutate(name=paste(firstname, surname, sep=" "),
           year=x$year, 
           category=x$category) %>% 
    select(name, id, year, category)

}

map_df(winners$prizes, extract_laureates)
## # A tibble: 911 × 4
##                      name    id  year   category
##                     <chr> <chr> <chr>      <chr>
## 1       David J. Thouless   928  2016    physics
## 2    F. Duncan M. Haldane   929  2016    physics
## 3   J. Michael Kosterlitz   930  2016    physics
## 4     Jean-Pierre Sauvage   931  2016  chemistry
## 5  Sir J. Fraser Stoddart   932  2016  chemistry
## 6      Bernard L. Feringa   933  2016  chemistry
## 7        Yoshinori Ohsumi   927  2016   medicine
## 8               Bob Dylan   937  2016 literature
## 9      Juan Manuel Santos   934  2016      peace
## 10            Oliver Hart   935  2016  economics
## # ... with 901 more rows

除非我正在编写一个我很确定我永远不会再使用的快速 hack,否则我喜欢制作非匿名函数,因为它有助于分解逻辑/步骤。

您可以使用 R 的范围规则ifelse()通过声明与列同名的变量来简化。如果dplyr找到具有该名称的列,它将使用它。如果不是,R 将使用局部变量。

然后,我们将年份和类别添加到您想要的新data_frame内容select()中。

要解决您的具体问题:

  1. 我不确定您所说的“易碎”是什么意思。您处理边缘情况,大多数 JSON 需要专门的方法来正确提取。
  2. 我看不出没有两个map…()电话可以做到这一点。即使我能想出一个,它也可能看起来像一个丑陋的、难以理解的黑客(记住,你正在为人类编写代码)。
  3. 上面的说明中涵盖了缺少的键 q。

另一种选择是等到“填充” data.frame 构建然后进行name处理:

extract_laureates <- function(x) {
  map_df(x$laureates, flatten_df) %>% 
    mutate(year=x$year, category=x$category)
}

map_df(winners$prizes, extract_laureates) %>% 
  mutate(surname=ifelse(is.na(surname), I(NULL), surname),
         name=paste(firstname, surname, sep=" ")) %>% 
  select(name, id, year, category) %>% View()
于 2016-10-23T12:12:47.440 回答