niszetの日記

細かい情報を載せていくブログ

(R) 複雑なデータ構造を取り扱う練習にrepurrrsiveパッケージを。あるいはxmlやjsonとViewの話。

そういえばこのパッケージの説明がまだなかったので。

以前、この記事

niszet.hatenablog.com

でさらっと触れているのですが、repurrrsiveパッケージってご存知でしょうか?

CRAN版: CRAN - Package repurrrsive

以下引用

Recursive lists in the form of R objects, 'JSON', and 'XML', for use in teaching and examples. Examples include color palettes, Game of Thrones characters, 'GitHub' users and repositories, and entities from the Star Wars universe. Data from the 'gapminder' package is also included, as a simple data frame and in nested and split forms.

とあるように、いろいろなデータが含まれています。

こちらに書かれてる方法で関数一覧を作って見てみると、 niszet.hatenablog.com

こんなかんじ

library(repurrrsive)
ls("package:repurrrsive")
#> [1] "gap_nested"       "gap_simple"       "gap_split"        "gh_repos"        
#> [5] "gh_repos_json"    "gh_repos_xml"     "gh_users"         "gh_users_json"   
#> [9] "gh_users_xml"     "got_chars"        "got_chars_json"   "got_chars_xml"   
#>[13] "sw_films"         "sw_people"        "sw_planets"       "sw_species"      
#>[17] "sw_starships"     "sw_vehicles"      "wesanderson"      "wesanderson_json"
#>[21] "wesanderson_xml" 

上にあげたうち、末尾にjsonxmlがついたものは実際は関数で、呼び出すと対応するjsonxmlのファイルのありか(パス)を返してきます。あとはRのオブジェクト。

私の環境ではたとえばこんな感じです。

repurrrsive::gh_repos_json()
#>[1] "C:/Users/niszet/Documents/R/win-library/3.4/repurrrsive/extdata/gh_repos.json"
repurrrsive::gh_repos_xml()
#>[1] "C:/Users/niszet/Documents/R/win-library/3.4/repurrrsive/extdata/gh_repos.xml"

jsonjsonlite::read_json()で取り扱えますね。例えばこんな感じ。

# 返却値はlistなのでこのまま実行するとコンソールが文字まみれに…
jsonlite::read_json(repurrrsive::got_chars_json())

Rはjsonの取り扱いも簡単に出来て良いですね。

しかし、この方法で得たRオブジェクト(リスト)と先のデータ(_jsonがついていないオブジェクト)は厳密には一致しておらず、下記のコードはFALSEを返します。

identical(jsonlite::read_json(repurrrsive::got_chars_json()), repurrrsive::gh_repos)
#> FALSE

これはViewで見てみると、[[1]][["titles"]][[1]][["aliases"]] が異なります。

jsonの方は

f:id:niszet:20180227080534p:plain

無印は

f:id:niszet:20180227080550p:plain

という感じで、jsonの場合はここがリストになっているのですね。

xmlの場合はxml2::read_xml()ですね。これを実際にやってみると、

xml2::read_xml(repurrrsive::gh_repos_xml())
#> {xml_document}
#> <root>
#> [1] <elem>\n  <elem>\n    <id>61160198</id>\n    <name>after</name>\n    <full_name>gab ...
#> [2] <elem>\n  <elem>\n    <id>14756210</id>\n    <name>2013-11_sfu</name>\n    <full_na ...
#> [3] <elem>\n  <elem>\n    <id>41645119</id>\n    <name>advdatasci</name>\n    <full_nam ...
#> [4] <elem>\n  <elem>\n    <id>56019902</id>\n    <name>2016-14</name>\n    <full_name>j ...
#> [5] <elem>\n  <elem>\n    <id>17120350</id>\n    <name>ampolcourse</name>\n    <full_na ...
#> [6] <elem>\n  <elem>\n    <id>57878579</id>\n    <name>aqi_pdf</name>\n    <full_name>m ...

と、ちょっと趣が違いますね。とりあえずView()で見てみると f:id:niszet:20180227080851p:plain

となります。ふむ。ここで(▷)のアイコンを押しても展開されません。が、適当な要素にマウスカーソルをあてると[ ⇐]のアイコンが出ますね。1行目を押してみると

xml_child(xml2::read_xml(repurrrsive::gh_repos_xml()), 1)

という形で、なんと子要素を適切な形で抜き取れるようにRStudio IDEの方でよしなにしてくれました。ここでxml_child()'はxml2パッケージの関数なので、実行する際はxml2::をつけるかlibrary(xml2)`を事前に実行しておきます。

どんな感じかなー。

library(xml2)
xml_child(xml2::read_xml(repurrrsive::gh_repos_xml()), 1)
#> {xml_node}
#> <elem>
#>  [1] <elem>\n  <id>61160198</id>\n  <name>after</name>\n  <full_name>gaborcsardi/after< ...
#>  [2] <elem>\n  <id>40500181</id>\n  <name>argufy</name>\n  <full_name>gaborcsardi/arguf ...
#>  [3] <elem>\n  <id>36442442</id>\n  <name>ask</name>\n  <full_name>gaborcsardi/ask</ful ...
#>  [4] <elem>\n  <id>34924886</id>\n  <name>baseimports</name>\n  <full_name>gaborcsardi/ ...
#>  [5] <elem>\n  <id>61620661</id>\n  <name>citest</name>\n  <full_name>gaborcsardi/cites ...
#>  [6] <elem>\n  <id>33907457</id>\n  <name>clisymbols</name>\n  <full_name>gaborcsardi/c ...
#>  [7] <elem>\n  <id>37236467</id>\n  <name>cmaker</name>\n  <full_name>gaborcsardi/cmake ...
#>  [8] <elem>\n  <id>67959624</id>\n  <name>cmark</name>\n  <full_name>gaborcsardi/cmark< ...
#>  [9] <elem>\n  <id>63152619</id>\n  <name>conditions</name>\n  <full_name>gaborcsardi/c ...
#> [10] <elem>\n  <id>24343686</id>\n  <name>crayon</name>\n  <full_name>gaborcsardi/crayo ...
#> [11] <elem>\n  <id>69169722</id>\n  <name>debugme</name>\n  <full_name>gaborcsardi/debu ...
#> [12] <elem>\n  <id>64197030</id>\n  <name>diffobj</name>\n  <full_name>gaborcsardi/diff ...
#> [13] <elem>\n  <id>25061555</id>\n  <name>disposables</name>\n  <full_name>gaborcsardi/ ...
#> [14] <elem>\n  <id>23227981</id>\n  <name>dotenv</name>\n  <full_name>gaborcsardi/doten ...
#> [15] <elem>\n  <id>23483249</id>\n  <name>elasticsearch-jetty</name>\n  <full_name>gabo ...
#> [16] <elem>\n  <id>22438731</id>\n  <name>falsy</name>\n  <full_name>gaborcsardi/falsy< ...
#> [17] <elem>\n  <id>61160771</id>\n  <name>fswatch</name>\n  <full_name>gaborcsardi/fswa ...
#> [18] <elem>\n  <id>37042992</id>\n  <name>gh</name>\n  <full_name>gaborcsardi/gh</full_ ...
#> [19] <elem>\n  <id>68708330</id>\n  <name>gitty</name>\n  <full_name>gaborcsardi/gitty< ...
#> [20] <elem>\n  <id>10396379</id>\n  <name>ISA</name>\n  <full_name>gaborcsardi/ISA</ful ...
#> ...

と、このような形で得られました。これをまた'View()`で囲ってあげれば表示されるので、一応繰り返していけば目的の場所にたどり着けるのでは?と思います(ちょっと手間ですね。下の階層まで一度に表示されると良いのですが、多分listとかでないと展開できないだろうな…)

さて、これがどのclass(データ構造)ならRStudio IDEView()対応しているのか?などの情報はまだ押さえていませんが、とりあえずView()して行を選択してみる。コンソール上に出てきたコマンドを実行してみて値が得られたらラッキー…!という使い方でも良いのではないかなと思います。これならRに不慣れな人でも、手間はかかるけど1つ1つ目で見て確認しながら作業が出来るのでは?と思います。

S4, R5, RCなどでどのような挙動になるのか確認したかったのですが、未調査です…。これもいずれ…。

ただし、対象となるデータの一番上の階層がdata.frameの場合はdata.frameとして扱われてしまうので要注意です。repurrrsiveにもあります。たとえばこのデータ。

repurrrsive::gap_nested
#> # A tibble: 142 x 3
#>    country     continent data             
#>    <fct>       <fct>     <list>           
#>  1 Afghanistan Asia      <tibble [12 x 4]>
#>  2 Albania     Europe    <tibble [12 x 4]>
#>  3 Algeria     Africa    <tibble [12 x 4]>
#>  4 Angola      Africa    <tibble [12 x 4]>
#>  5 Argentina   Americas  <tibble [12 x 4]>
#>  6 Australia   Oceania   <tibble [12 x 4]>
#>  7 Austria     Europe    <tibble [12 x 4]>
#>  8 Bahrain     Asia      <tibble [12 x 4]>
#>  9 Bangladesh  Asia      <tibble [12 x 4]>
#> 10 Belgium     Europe    <tibble [12 x 4]>
#> # ... with 132 more rows

この場合は、

View(repurrrsive::gap_nested)

で表示すると f:id:niszet:20180227081952p:plain

となります。

View(as.list(repurrrsive::gap_nested))

のように、as.list()で囲えば

f:id:niszet:20180227082150p:plain

と出来ますので、データフレーム内にlistを格納しているような構成で、とりあえずViewで中身を覗きたいというときにはこの方法もありかなと思います。

Enjoy!!