SNAGeek

Sociologically Technological, and Technologically Sociological

Rでtibble(data.frame)からネストされたjsonを出力する

tibble to nested json などで検索するといろいろな方法出てきますが、一番スマートなのは tidyr::nest() でネストしたtibbleを jsonlite::toJSON()jsonに変換するやり方かと思います。

セッション情報

諸事情あってRStudioDesktopで動かしています。

> sessionInfo()
R version 4.0.2 (2020-06-22)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)

Matrix products: default

locale:
[1] LC_COLLATE=Japanese_Japan.932  LC_CTYPE=Japanese_Japan.932    LC_MONETARY=Japanese_Japan.932 LC_NUMERIC=C                  
[5] LC_TIME=Japanese_Japan.932    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
[1] compiler_4.0.2 tools_4.0.2    yaml_2.2.1    
> 

データ読み込み

データは MASS パッケージに同梱されている Cars93 データの一部を用います。

> library(tidyverse)
> library(jsonlite)

 次のパッケージを付け加えます: ‘jsonlite’ 

 以下のオブジェクトは ‘package:purrr’ からマスクされています: 

     flatten 

> 
> car_data <- MASS::Cars93 %>%
+   as_tibble %>%
+   select(1:5)
> car_data
# A tibble: 93 x 5
   Manufacturer Model      Type    Min.Price Price
   <fct>        <fct>      <fct>       <dbl> <dbl>
 1 Acura        Integra    Small        12.9  15.9
 2 Acura        Legend     Midsize      29.2  33.9
 3 Audi         90         Compact      25.9  29.1
 4 Audi         100        Midsize      30.8  37.7
 5 BMW          535i       Midsize      23.7  30  
 6 Buick        Century    Midsize      14.2  15.7
 7 Buick        LeSabre    Large        19.9  20.8
 8 Buick        Roadmaster Large        22.6  23.7
 9 Buick        Riviera    Midsize      26.3  26.3
10 Cadillac     DeVille    Large        33    34.7
# ... with 83 more rows

tibbleそのまま出力

素直に toJSON() すると、一つの行の各要素が同じ階層内に格納された形で出力されます。

> car_data %>%
+   toJSON(pretty = TRUE)
[
  {
    "Manufacturer": "Acura",
    "Model": "Integra",
    "Type": "Small",
    "Min.Price": 12.9,
    "Price": 15.9
  },
  {
    "Manufacturer": "Acura",
    "Model": "Legend",
    "Type": "Midsize",
    "Min.Price": 29.2,
    "Price": 33.9
  },
...

1段ネストされたjsonを出力

ネストされたjsonを出力するためには、一度何らかの変数のセットで他の変数をネストしてから toJSON() します。 例えば、 Manufacturer で他の変数をネストしてみます。

> car_data %>%
+   group_by(Manufacturer) %>%
+   nest() %>%
+   toJSON(pretty = TRUE)
[
  {
    "Manufacturer": "Acura",
    "data": [
      {
        "Model": "Integra",
        "Type": "Small",
        "Min.Price": 12.9,
        "Price": 15.9
      },
      {
        "Model": "Legend",
        "Type": "Midsize",
        "Min.Price": 29.2,
        "Price": 33.9
      }
    ]
  },
  {
    "Manufacturer": "Audi",
    "data": [
      {
        "Model": "90",
        "Type": "Compact",
        "Min.Price": 25.9,
        "Price": 29.1
      },
      {
        "Model": "100",
        "Type": "Midsize",
        "Min.Price": 30.8,
        "Price": 37.7
      }
    ]
  },
...

ManufacturerType の2変数でネストすると、

> car_data %>%
+   group_by(Manufacturer,Type) %>%
+   nest() %>%
+   toJSON(pretty = TRUE)
[
  {
    "Manufacturer": "Acura",
    "Type": "Small",
    "data": [
      {
        "Model": "Integra",
        "Min.Price": 12.9,
        "Price": 15.9
      }
    ]
  },
  {
    "Manufacturer": "Acura",
    "Type": "Midsize",
    "data": [
      {
        "Model": "Legend",
        "Min.Price": 29.2,
        "Price": 33.9
      }
    ]
  },

2段ネストされたjsonを出力

次は、ネストされたjsonの中で更にネストされたjsonを出力します。 この場合は, purrr:map() を使うのが便利です。 例えば、Manufacturer の内部で Type によって更にネストしてみます。

> car_data %>%
+   group_by(Manufacturer) %>%
+   nest() %>%
+   mutate(data = map(data,group_by,Type) %>%
+            map(nest)) %>%
+   toJSON(pretty = TRUE)
[
  {
    "Manufacturer": "Acura",
    "data": [
      {
        "Type": "Small",
        "data": [
          {
            "Model": "Integra",
            "Min.Price": 12.9,
            "Price": 15.9
          }
        ]
      },
      {
        "Type": "Midsize",
        "data": [
          {
            "Model": "Legend",
            "Min.Price": 29.2,
            "Price": 33.9
          }
        ]
      }
    ]
  },
  {
    "Manufacturer": "Audi",
    "data": [
      {
        "Type": "Compact",
        "data": [
          {
            "Model": "90",
            "Min.Price": 25.9,
            "Price": 29.1
          }
        ]
      },
      {
        "Type": "Midsize",
        "data": [
          {
            "Model": "100",
            "Min.Price": 30.8,
            "Price": 37.7
          }
        ]
      }
    ]
  },
...

Enjoy!