niszetの日記

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

(R) 懲りずにラズパイにR入れていたんですけどね… (メモ)

罠があった…

Rを入れるまでは多分大丈夫なのですが、RStudioを入れるのに失敗してしましました。多分OS入れ直し。

OSのバージョンは上がっているんですけど、32bitなままなんですよねぇ…。 www.raspberrypi.org

Rの導入の基本的な手順はこちらの通りなのですが、 www.karada-good.net

jdk7が入らずjdk8にしたり、pandocが2.0になっているのでうまく入っていないようだったり。

Rについてはapt-get系は下記で必要十分だと思います。

sudo apt-get update
sudo apt-get install libcurl3-dev libcurl3 libcpre3-dev liblzma-dev libbz2-dev libxt-dev libreadline6-dev gfortran libcurl4-openssl-dev
./configure
sudo make
sudo make install

RStudioですが、commonは良いとして、次のステップでamazon AWS S3 にログイン出来ないよーってERRORを出した後にラズパイ自体がおかしくなってしまって以降うまく起動できていません。ご注意。

ラズパイに何か入れるの面倒くさいのですよねぇ…仮想環境…

ちなみにjdk8まわりはここら辺に情報がありました。一応この対応をしたので大丈夫だったと思うのだけど… missing jvm server - Raspberry Pi Forums

まぁ別にラズパイにR入れなくてもいいじゃないって気がしているので、手が空いたらまたやってみます。

(R) 社内勉強会でggplotのオブジェクトを見ながら説明した時の話

あるいはViewでデータ構造を視る話。応用編。

最近、社内でRを布教しています。

Rの強みは色々あると思いますが、個人的には探索的な使い方1に向いていてさらにそれが再現可能である(コードに落とせる)点が大きいかなと思っています。まさに環境ですね2。 特に可視化に関連する部分はRStudio IDEが充実していることもあって、興味を持ってもらいやすい特長の1つだと思います(まぁ、今更言うことでもないですけどね…)

ふんわりした理解で使っている人にはぜひ読んでほしい

さて、そんなわけで社内勉強会でRを布教して、基本的な文法からggplot2パッケージの使い方などを色々やってきたのですが、「geomってのがよくわからない」「+で足していくのか良く分からない」「レイヤー?レイヤーって何?」みたいな意見をいただいたので、kazutanさんの下記の資料、

ggplotのオブジェクトから眺めてみる

を使って説明をしました。このとき、RStudio IDE上でView()を使って作成されたggオブジェクトの構造を見せることで理解が深まったのではないかなと思いますので、ざっくりとご紹介します。

なお、基本的な説明の流れは🐘さんの資料の通りですので、View()で表示するとどうなるのか?を中心に書いていきます。

見ていく。

さて、順に見ていくです。

library(ggplot2)
g_null <- ggplot()
View(g_null)

f:id:niszet:20180301225644p:plain

こんな感じ。元記事にもあるように、何も設定していないすっからかんの状態では、data, layers, mapping, などなど中身がすっからかんなことが Type列から読み取れますね。

続いで、dataにみんな大好きirisデータを入れてみる例。

g_def_data <- ggplot(data = iris)
View(g_def_data)

f:id:niszet:20180301230940p:plain

こんな感じ。dataのところ、data.frameとしてirisのデータがまるまる格納されている事がよみとれますね。また、📜のアイコンをクリックすればこのdata.frameを取り出して(つまりirisデータを)Viewで視ることが出来ます。コンソール上には

View(g_def_data[["data"]])

と表示されます(そのうえで実行される=表示される)

次。mappingも指定した例

g_def <- ggplot(iris, aes(Sepal.Length, Sepal.Width))
View(g_def)

はこのように、xとyに指定した列名が(symbolとして)入っていますね。

f:id:niszet:20180301230917p:plain

レイヤー

レイヤーも当然?View()で視れます。

lay_iris_point <- layer(data = iris, mapping = aes(x = Sepal.Length, y = Sepal.Width),
                      geom = "point", stat = "identity", position = "identity")
View(lay_iris_point)

f:id:niszet:20180301231550p:plain

これはggplot()から返ってくるオブジェクトではなくlayerであることが1行目のTypeからわかりますね。そして、geomやstat、positionがどうやら設定した項目に関係するS3クラスが入っていることが確認できます。

どんどんいきましょうか。

g_null_lay_iris_point <- g_null + lay_iris_point
View(g_null_lay_iris_point)

f:id:niszet:20180301231933p:plain

空のggplot()に先に定義したlayerを+で足すと、layersの下にlayerの情報が全て格納されることが見て取れます。top階層には入らないんですね~。あくまでも「layerとして定義したものは必要な情報を完備しているのでそれ自身で完結し、layersの中収まる」ということなんですねぇ

さて、あと2つ。よく見るgeomで指定するタイプですね。 2つの違いがわかると腹にストンと落ちる感じがします。

geom_point_iris <- geom_point(data = iris, mapping = aes(x = Sepal.Length, y = Sepal.Width),
                              stat = "identity", position = "identity")
g_gpoint_iris <- g_null + geom_point_iris
View(g_gpoint_iris)

f:id:niszet:20180301232908p:plain

こちらはgeomの方に全部指定、g_nullggplot()なので一番上の階層はデフォルト(dataなどは空の状態)のまま、geomで指定したものが全てlayersの中に入っていることがわかりますね。

geom_point_null <- geom_point()
g_def_gpoint_null <- g_def + geom_point_null
View(g_def_gpoint_null)

f:id:niszet:20180301232933p:plain

で、こちらはg_defなのでggplot()にdataとmappingを指定しています。実際にその通りになっていますね。つまり、それぞれの関数に指定した引数はそれぞれの階層に収まるということなわけですね。

この場合、layers1の中にはdataが入っていないので、上の階層にあるdataを参照する、これがkazutanさんの記事にある「継承」の意味ということです。
layersにあるように、これにgeom_line()などを追加していくとここが1, 2, ...と増えていきます。それぞれどうなっていくのかを見ていくのも良いと思います。それぞれに引数を指定したら?しなかったら?で見ていくと面白いかもしれませんね。

あと、geomほげほげはlayerを返します。実際に中を見てみると

geom_point
#>function (mapping = NULL, data = NULL, stat = "identity", position = #>"identity", 
#>    ..., na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) 
#>{
#>    layer(data = data, mapping = mapping, stat = stat, geom = GeomPoint, 
#>        position = position, show.legend = show.legend, inherit.aes = #>inherit.aes, 
#>        params = list(na.rm = na.rm, ...))
#>}
#><environment: namespace:ggplot2>

なので、layersの中に納まるんですね。なぜgeomを使うかと言えば、上のコードを見るとわかるようにそれぞれのgeomにはよく使う設定(statなら"identity")がデフォルト引数として指定されているので、記述を省略することが出来るのですね(layerは5要素が全部そろっていないといけないというルールはgeomではデフォルト引数を与えることで満たしている、ということ)

半年前の自分へ。

そんなわけで、ただView()で視るだけだし、元ネタkazutanさんの記事が良すぎて乗っかっただけなのですが、半年前の自分にはそれでもちょっと敷居が高かった気がする。

その理由の一つがstrで、コンソールにぶわっと階層構造が表示されるのでどれが重要でどれが重要じゃないかがちょっとわからなかったのですよね…。まぁ理解してなくても動くし…まだ早いんだよ…くらいに思っていましたが、わからなかったら聞いたほうが良いぞ!半年前の自分!

さて、しかしView()を使うと階層構造が折りたたんだ状態で表示できるのでかなり見やすくなると思います。TypeやValueに必要な情報が入っているので、構造と何が収まっているかの確認がとてもしやすい。と、思います。

元の記事が出た後にRStudio v1.1、そしてこのViewの機能が出たので、今ならこれを使って必要な情報と差分を見比べていく方法で初心者に見せていくのが良いのかなって思っています(実際にやったんだけど)

この、ggplotのオブジェクトの構造の理解は手を動かして見てみるのが一番です。まだふんわり理解しているなぁ…という方、是非元のkazutanさんの記事にあるコード、上から全部実行して、VIewで確認しながら進めて言ってほしいです。

そんな感じ。

最後ただのポエムですなぁ…。まぁいいか…

Enjoy!!


  1. インタラクティブに、と言っても良いかもしれないですね。バッチでやるためにRで書く必要はないですが、結果としてそういうコードに落としていくのはアリですよね

  2. 先日のこちらのツイート見て改めて思いましたです。

(Julia) Julia使ってみた記念。

そろそろ触ろうと思って3か月。ようやく。

信号処理系、Rでやっていくのが辛い、MATLABはライセンスの問題、Scilabとかはちょっと…と思っていて、Juliaを始めることにしました。

とりあえずこちらを。 github.com

マニュアルもある。 juliacontrol.github.io

こちらの記事によると、もうひとつ?パッケージ?があるみたいなのだけど、乱立するのあまりうれしくないよねーみたいなことが書かれているっぽい。 discourse.julialang.org

とりあえず色々使ってみて考えよう。

IDE

どんなものがあるかなーって思って調べて、とりあえずJuliaProを導入してみた(登録した)。 Atomの環境もかかなり使いやすかったのでそちらでも良いかなと思う。これももうちょっと調べつつ。まぁまずは使ってみるのが先。

はじめるには

Juliaのことをとりあえず書けるレベルにするにはこちらの記事を読んで実行してくのが良さそうですね。

github.com

使ってみて

ちょっとライブラリの導入に時間がかかる印象?コンパイルしているんだっけ。
使い勝手はかなり良い気がしていて、v1.0になるまでに下地を作っておこうと思います。

というわけで、Julia開始記念日でした(今日Hello worldでした)

(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!!

FlashAir Developers Summit 2018に行ってきました。

午前中からの参加でした。

MFT2017にてその存在を知ったFlashAirですが、どこから手をつければ良いのかイマイチわからん…1
…ということで、ずっと触るのを保留していたのですが、FlashAirのイベントが今年2018年もあるのをTwitterで見つけて午前中のハンズオンから参加してきました!

ちなみにMFT2017(イベント全体)のページはこちらですね。2018の開催が待ち遠しいですな…。 makezine.jp

ここで私はFlashAirの存在を知ったのですよね。同人誌もこの時いただいた。

さて、FlashAir Developers Summit 2018のページはconpassとFlashAir™ Developersにそれぞれあります。

conpassの方には、「今後小規模のハンズオンを複数回開催する予定です。」とあるので、また機会を見つけて参加したいですね~

イベントの感想とか

午前の部

ハンズオンでFlashAirを実際に使って動かしてみるというものでした。私はFlashAirからインターネット接続するところで躓いていましたが、そのあとのセンサーとの接続はうまくいきました。
翌日に試したところインターネット同時接続が出来たので、これで遠隔でデータを得ることが出来るようになったのです。いずれ簡単にまとめますね。

なお、自宅環境だとDHCPにしたらうまくいきました。多分ルータのせい。
当日も電波の混雑と、このIPアドレス周りで問題があったのかな

午後

午後は18時頃まで発表とLTで密度の濃い時間でしたね。色々なお話が聞けて楽しかったです。
次の機会にはLT出来るように頑張ろう💪

以下、テンションの上がったniszetさんのツイートです。

何気にファインプレーだったこの写真。

展示とか

中には当日朝に作りました!という展示もありましたね。凄い…。
動くものが展示されているとやっぱり面白いですね~。FlashAirが無線で受け取ってGPIOで制御するものが多いかな。ロガーとしての使い方も良いけど、FlashAirから何かを制御する形の方が展示的には良さそうですね。

じゃんけん大会・景品抽選会

景品、かなり豪華でした。内容はconpassのページを見ていただければ。ちなみに私はじゃんけん弱いので全然ダメでしたぁ…残念。

懇親会

美味しい料理とお酒が振る舞われ、技術者の方や参加者の方とお話出来た良い時間でした。すべての方とはお話をすることはできなかった2ですが…

レベル感

年齢層が比較的高めのイベントかなと思います。70歳の方もいらっしゃいましたね。IoTに年齢関係なし!ってことですね
ハンズオン、もちろん初心者歓迎のイベントなのですが、例えばラズパイちょっと触ったことがあるとかレベルで良いのでちょっと経験はあった方がよいかもしれません。でも雰囲気しかわかってない私でも大丈夫なので大丈夫でしょう。 周りの人に聞きながら試行錯誤するのも楽しいですよね。私は色々聞きながらやってました

雑感

まだ私がちゃんと扱えるところまでレベルが上がっていませんが、もし仮にFlashAirにとっつき悪さを感じる点があるとすれば、GPIOを制御するような低レイヤーからHTTPなどの高レイヤーについての知識と技術を必要とする3ところでしょうか。ハードの仕様を理解するところがまた少しハードル高いかも。SDカードの仕様は下記で見られます。英語です。長いです。頑張りましょう。

Simplified Specifications - SD Association

SDスロット付きの機械からFlashAirを介してデータを送ることは一般的に行われているので、その点はユーザ的には問題ないので、その逆、FlashAirのGPIOを制御するあたりが試行錯誤が必要という感じです4

関連するページ

FlashAirIoTHub

iot-hub.flashair-developers.com

FlashAir™ Developers

flashair-developers.com

ということで、忘れないうちに感想を書きました。

とりあえず直近でやってみたいことがあるのでそれを目指して色々と準備するです。


  1. 単純に僕が始める踏ん切りがつかなかっただけですけど…。

  2. ご縁があればまた会えるし、ネット上でもコミュニケーションできますしね。

  3. それぞれに対する要求はそれほど高くないのでは?と思うものの、それらの技術領域を網羅するという意味ではそれなりに人を選ぶのかもしれないな、という感想です。この感想は使ってる間に変わっていくのかなと思っています。

  4. FlashAir DevelopersやIoTHubなどの環境が整ってきているので、情報収集と発信をしていきたい。

(R) skimrの表示をカスタマイズしたい 3回目(追記修正)

まさかの3回目。

これらの記事の続きです。

niszet.hatenablog.com

niszet.hatenablog.com

先日の2回目の記事によって、無事にhistの表示のさせ方を変えることが出来たのですが、ここでteramonagiさんから

と追加の情報をいただきました。それぞれのページ、

blog.recyclebin.jp

qiita.com

を見て、ちょっとやってみた感じの内容です。

なお、この方法で関数の挙動を変えるのは上級者向けという気がします。ちゃんと内部の挙動を把握しているので後からでも分かるよ!という場合以外は1おススメしないですが、こういうことも出来るという紹介の記事ということで。

やってみる。

先の記事2つのうち、kos59125さんのブログの方で書かれている方でやってみます。

まず、inline_histの中身を確認します。

skimr::inline_hist
#> function (x) 
#> {
#>     if (length(x) < 1 || all(is.na(x))) 
#>         return(structure(" ", class = c("spark", "character")))
#>     if (all(x == 0)) 
#>         x <- x + 1
#>     hist_dt <- table(cut(x, options$formats$character$width))
#>     hist_dt <- hist_dt/max(hist_dt)
#>     structure(spark_bar(hist_dt), class = c("spark", "character"))
#> }
#> <environment: namespace:skimr>

問題となっているのはoptions$formats$character$widthが外部から変更できない(skimr:::optionsを直接編集することが出来ない)ことでした。2回目の記事では関数を変更する方法で対応していました(ただし関数はグローバル環境に置かれている)が、今回はこの環境オブジェクトskimr:::optionsをコピーした上で修正し、再びskimr名前空間に入れるという方法です(記事にあったやり方の通りです)

さて、やってみます。

# 一旦適当な変数に代入
rr<-skimr:::options

# 変更がわかりやすいように、思い切って幅を20にしてみます。グローバル環境にある自分で作った変数なのでアクセスし放題ですね。
rr$formats$character$width <- 20

# そして、skimrの名前空間に変数rrをoptionsという名前でassignする
assignInNamespace("options", rr, getNamespace("skimr"))

なお、私の今の環境ではreprexで表示させると文字化けするので以下はコンソールコピペです…2。 デフォルトの動作だと、

> skimr::inline_hist(iris$Petal.Length)
▇▁▁▂▅▅▃▁

ですが、上記の操作をした後は

> skimr::inline_hist(iris$Petal.Length)
▁▇▂▁▁▁▁▁▁▁▃▃▃▅▂▃▂▁▁▁

となります。元の関数のまま、見事に挙動が変わりましたね…。

もちろん、skimr::skim()でも同じようになります。

> skimr::skim(iris)
Skim summary statistics
 n obs: 150 
 n variables: 5 

Variable type: factor 
 variable missing complete   n n_unique                       top_counts ordered
  Species       0      150 150        3 set: 50, ver: 50, vir: 50, NA: 0   FALSE

Variable type: numeric 
     variable missing complete   n mean   sd  p0 p25 median p75 p100                 hist
 Petal.Length       0      150 150 3.76 1.77 1   1.6   4.35 5.1  6.9 ▁▇▂▁▁▁▁▁▁▁▃▃▃▅▂▃▂▁▁▁
  Petal.Width       0      150 150 1.2  0.76 0.1 0.3   1.3  1.8  2.5 ▇▂▂▁▁▁▁▂▁▅▂▃▁▁▃▂▂▁▂▂
 Sepal.Length       0      150 150 5.84 0.83 4.3 5.1   5.8  6.4  7.9 ▂▂▃▇▆▁▆▇▅▆▂▇▃▆▂▁▂▁▂▁
  Sepal.Width       0      150 150 3.06 0.44 2   2.8   3    3.3  4.4 ▁▁▁▁▃▃▅▃▇▇▂▃▂▁▃▁▁▁▁▁

ということで、skim()の方でもhistの表示幅が変わっていることが確認できましたね。

パラメータを変えるのは良くないかも。関数を定義しなおして置きなおすべきかも。

ここまで書いておいてなんですが…

今回は関数を置き換える形ではなく、パラメータを管理しているskimr:::optionsという環境オブジェクトの値を更新しました。
しかし注意すべきなのは、options$formats$character$widthはその構造と名前から察するに(本来?)文字列の表示する幅を制御する用途と考えられます。そのため、このようなパッケージ内部の変数を変更するのは副作用が考えられるので、パッケージ全体を確認する必要があるのかなと思います3。 なので、ちゃんと(?)関数を定義して、固有のパラメータを与えてあげる方が良いでしょう。この記事は長くなってしまったのでもう一回やるかな…4

元に戻すには…?

ちなみにこの方法で挙動を変えた場合、RStudioを終了して再度起動したら元に戻っていました。起動ごとにlibrary()でパッケージをロードしないといけないのと似ていますね。
パッケージ内の深いところにある関数のバグの暫定対応としてこの方法で挙動を変えた場合は、逆に起動のたびに忘れずに実行しないといけませんね。

補足

ちょっと文章的によろしくないところを全体的に直しました(2/26 1830ごろ)

最近名前空間とかスコープに関係する内容が多くなってきた気がしますね。自分なりにまとめる時期なのかもしれません。

Enjoy!!


  1. つまりniszetさんにはまだ早い技術。

  2. 一貫性がなくてすみません。これについてはちょっと調べないと理解しきれていないので…

  3. が、iris$Species <- as.character(iris$Species)して[^2]表示させてもfactorとcharacterで表示が変わらないような…?なので今回は副作用はないかもしれないです。しかしコード全てを見たわけではないので確かなことは言えないです…。あと、こうやってirisを直接編集しちゃダメですよ…。data(iris)しましょうね…。ここでは簡単のためにね…。

  4. どうかな…。inline_hist()skim()それぞれの関係とかちょっと調べることがあるので時間かかりそうです。

(R) R-wakalangで質問してみた。あるいはパッケージ内の関数の一覧を得る方法について

初の投稿...ども...

来週のTokyoRにてyutannihilationさんがwakalangについて発表すると聞き、そういえば僕はちゃんとここで質問したことがなかったなぁ…と思って、気になっていたことを質問してみました1

様子

こんな感じです。

f:id:niszet:20180223215752p:plain

めちゃくちゃリプライが早い…!(わずか3分…。)

国内のトップRユーザの方々からこんなに簡単にコメントいただけるなんて…すごい場所ですね…。

ワタシも結構しり込みしていたのでわかるのですが、習うより慣れよですね。とはいえ、どういう感じに質問すればよいの…?みたいなものはありますよね…

わからないことを呟いてみよう。

そんなわけで、yutannihilationさんがRワカラングのわからないことを募集?中です。ask.fm(下記の記事をさかのぼるとリンクがあるはず)や、タグをつけて呟いてみましょう!

私は、

  • 寿司をつける方法がわからなかった
  • コードを添付するには??
  • 文中のコードハイライトは…?

みたいなことが良く分かりませんでしたね…。それぞれ、なんかslackポチポチ触ってたらそれらしきものが見つかったりしたので少しだけわかりましたが…。

質問の答えについて

hoxo_mさんに教えていただいたのはこのやり方。

ls(envir = asNamespace("package_name"))

この場合はそのパッケージ内に含まれる、エクスポートされていない関数も含めて一覧が得られます。なお、パッケージ名を囲う""は必須です(文字列で渡す)。

# 実行例
> head(ls(envir = asNamespace("fs")))
[1] "!.fs_perms" "%||%"       "&.fs_perms" "[.fs_bytes" "[.fs_path" 
[6] "[.fs_perms"

それとは別経路で(というかtwitterで)kazutanさんに教えていただいたのはこのやり方。

library(fs)
ls("package:fs")
# 以下は実行結果
 [1] "as_fs_bytes"      "as_fs_path"       "as_fs_perms"      "dir_copy"        
 [5] "dir_create"       "dir_delete"       "dir_exists"       "dir_info"        
 [9] "dir_ls"           "dir_map"          "dir_walk"         "file_access"     
[13] "file_chmod"       "file_chown"       "file_copy"        "file_create"     
[17] "file_delete"      "file_exists"      "file_info"        "file_move"       
[21] "file_show"        "file_temp"        "file_temp_pop"    "file_temp_push"  
[25] "fs_bytes"         "fs_path"          "fs_perms"         "group_ids"       
[29] "is_absolute_path" "is_dir"           "is_file"          "is_link"         
[33] "link_copy"        "link_create"      "link_delete"      "link_exists"     
[37] "link_path"        "path"             "path_abs"         "path_common"     
[41] "path_dir"         "path_expand"      "path_ext"         "path_ext_remove" 
[45] "path_ext_set"     "path_ext<-"       "path_file"        "path_filter"     
[49] "path_home"        "path_join"        "path_norm"        "path_real"       
[53] "path_rel"         "path_sanitize"    "path_split"       "path_temp"       
[57] "path_tidy"        "user_ids"        

この場合はエクスポートされている関数のみが得られます。用途に応じて使い分けましょう。どちらも文字列のベクトルとして得ることが出来ます。

conflictsはコード追っかけ中…。これも何かわかったらまとめておこう… github.com

ということで、わからないことはRワカラングで質問してみようというお話でした!

追記

この記事について、技術的な面でもう少し掘り下げた記事をid:yutannihilationさんに書いていただけたので、ls()の挙動についてもう一歩踏み込んだ理解をしたい方はこちらの記事も是非!

notchained.hatenablog.com

Enjoy!!


  1. あらためて見直してみると私の考えていた方法の回りくどさよ…。ls()はbaseの関数なので、これも若者(じゃなくておじさんだけど)のbase離れってやつなのか…。