全ての引数の名前と値をlistとして取得したい
この前教えてもらったのから察するに、こんなんでいいっぽい。
三連ドット(...)的に全部の引数が取れればもっと楽そうだが・・・
> f <- function(x=NULL, y=1){ + purrr::map(rlang::set_names(c("x", "y")), ~ rlang::eval_tidy(rlang::sym(.x))) + } > f(y=10) $x NULL $y [1] 10
eval(parse(text=...))をモダンに書きたい
掲題の件、 eval(parse(text=...))をモダンな書き方にしたいと思って、r-wakalangで回答貰ったやつのメモ。
昔ながら(一部、purrr使ってるんでモダンにも見えるが・・・)の書き方だとこんなん
> hoge1 <- function(x){x+1} > hoge2 <- function(x){x+2} > > purrr::map(paste0("hoge", 1:2), ~ eval(parse(text=.x))(1)) [[1]] [1] 2 [[2]] [1] 3
これをモダンに書こうとすると、以下のようにするのが良いらしい
> purrr::map(paste0("hoge", 1:2), ~ rlang::eval_tidy(rlang::sym(.x))(1)) [[1]] [1] 2 [[2]] [1] 3 > purrr::invoke_map(rlang::set_names(c("hoge1", "hoge2")), 1) $hoge1 [1] 2 $hoge2 [1] 3
非常に勉強になった。
〜追記〜
後に教えてもらった以下が一番きれいかも。
> list(hoge1, hoge2) %>% + map(~ .(iris)) %>% + bind_rows() Sepal.Length Sepal.Width Petal.Length Petal.Width Species 1 5.1 3.5 1.4 0.2 setosa 2 5.9 3.0 5.1 1.8 virginica
Rのパッケージをtidyに開発する
この記事は tidyポエム Advent Calendar 2017 - Adventar の24日目の記事です。
はじめに
今年はRのパッケージをゴリっと開発することが多かったので、そこで培った2つのノウハウをここにメモっておきます。
tidyなデータ処理があるようにtidyなパッケージ開発(標準的なパッケージ開発?)もあっていいと思うのです。
tidyverseを使うな
かつてHadley verseと呼ばれていた Hadley Wicham作のパッケージ群を集めたパッケージであるtidyverse、最高ですよね?
これさえあればモダンな形でR言語のコードが書けますし、データ集計に必要な機能(前処理・可視化)はほぼカバーされています。
なので、データ分析の時にこのパッケージを使うのはとても良いのですが、パッケージ開発時においては、迂闊にコヤツを使うべきではありません。
tidyポエムなのにtidyverseを否定する、これ如何に?
なぜ使ってはいけないのかというと、開発しているパッケージ内でtidyverseに含まれるパッケージ(dplyrとかggplot2とか)を使おうとすると非常にややこしくなるからです。
例えば、以下のようにDESCRIPTIONにtidyverseへの依存関係を記載しておきます。こうすることで、貴方が開発しているパッケージをインストールする際にはtidyverse、およびそこに付随するパッケージもオートマ―チックにインストールされるので、一見すると楽ができるように思います。
...(省略)... Imports: tidyverse ...(省略)...
しかし、いざ以下のように適当なtidyvereのパッケージ(ここではggplot2)を使用したコード
#' @import tidyverse NULL #' @export hello <- function() { ggplot2::ggplot(ggplot2::mpg, ggplot2::aes(displ, hwy, colour = class)) + ggplot2::geom_point() }
をパッケージ内で書こうとすると、Packageのcheck時に以下のような警告が出てしまい、CRANにパッケージをUPすることができません。
checking dependencies in R code ... WARNING '::' or ':::' import not declared from: 'ggplot2'
パッケージ開発においては、億劫がらずに1つ1つの依存パッケージをきちんとDESCRIPTIONのImportsに指定していくのがよいでしょう。
再現用のパッケージはこちら
ドキュメンテーションもDRY(Don't Repeat Yourself)の原則に従え
パッケージ開発時のドキュメンテーションはほぼroxygen2パッケージ一択になるのですが、
その中でも、私が最近知って多用するようになった、@rdname, @describeInというものを紹介します。
日本語で言及している記事が Triadsou さんとHeavyWatal さんくらいしか見つからなかったので。
詳細はroxygen2のvignettesを読んでおけばOKです。
これを使うと複数の関数に共通の引数や説明を丸っと一か所に記述するだけでよくなるので、ドキュメンテーションが楽になる&見通しが良くなります。
例えばこんなかんじ。@rdnameと@describeInの違いは上のVignettesを読めばOK。
#' Hello() series #' #' There are lots of hello() s. #' #' @param x something argument #' @name hello NULL #' @rdname hello #' @export hello1 <- function(x) {print("Hello1")} #' @rdname hello #' @export hello2 <- function(x) {print("Hello2")} #' Hoge() series #' #' There are lots of hoge() s. #' #' @param x something argument hoge <- function(x){} #' @describeIn hoge I'm hoge1 hoge1 <- function(x){"hoge1"} #' @describeIn hoge I'm hoge2 hoge2 <- function(x){"hoge2"}
皆さんのRパッケージがよりtidyになりますように。
R Markdownの出力HTMLの幅を広げる
「R Markdownの出力HTMLの幅を広げたいときはどうするんだ」と思っていたが、こんな感じでCSS書けばいいだけだった。
--- title: "Untitled" author: "Nagi Teramo" output: html_document --- ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE) ``` <style type="text/css"> .main-container {max-width: 1500px;} </style> ## R Markdown This is an R Markdown document. ```{r cars} summary(cars) ```
rlang::eval_tidy()と!!は違う
老害度が増してきているので、dplyrパッケージなどの裏側で使われている新しい"Rの評価"を与えるrlangパッケージについて勉強してた。
rlang::eval_tidy()と!!は違うんだなってことである。
まずはそれらのパッケージを読みこむ。
library("rlang") library("dplyr")
そして、以下のような関数を考える。
f <- function(x) { a <- rlang::enquo(x) print(a) dplyr::select(head(iris, 3), !! a) }
この辺は日本語だと以下の2資料が詳しい。
この関数は標準評価(所謂Standard Evaluation, SE)でも非標準評価(所謂None Standard Evaluation, NSE)でも動作する*1。
> f(Sepal.Width) <quosure: global> ~Sepal.Width Sepal.Width 1 3.5 2 3.0 3 3.2 > f("Sepal.Width") <quosure: empty> ~"Sepal.Width" Sepal.Width 1 3.5 2 3.0 3 3.2
一方、f()中の!!の箇所をrlang::eval_tidy()に置き換えた
g <- function(x) { a <- rlang::enquo(x) print(a) dplyr::select(head(iris, 3), rlang::eval_tidy(a)) }
を定義して同様に動かしてみると、、
> g(Sepal.Width) <quosure: global> ~Sepal.Width Show Traceback Rerun with Debug Error in overscope_eval_next(overscope, expr) : object 'Sepal.Width' not found > g("Sepal.Width") <quosure: empty> ~"Sepal.Width" Sepal.Width 1 3.5 2 3.0 3 3.2
となってNSEのほうは動かない。
これは!!が一枚(という表現でいいのか?)だけquoteした内容を剥がす一方、rlang::eval_tidy()は最後まで全部その場で評価しちゃうからなんだろうな、って思った。
まだ勉強中なので自信はない。
*1:これはselect()の仕様のおかげ
適当なデータをsklearnでカーネル密度推定する
クロスバリデーション付きでどう書くのが楽かなと考えた。
私なりの答えはこんなん。ライブラリのimport設定は以下で使うものもまとめて書いちゃってる。
import numpy as np import matplotlib.pyplot as plt from scipy import integrate from sklearn.model_selection import GridSearchCV from sklearn.neighbors import KernelDensity def estimate_kde(x, bandwidth=np.linspace(0.4, 4.0, 50), cv=5): # cross-validation grid = GridSearchCV(KernelDensity(), {'bandwidth': bandwidth}, cv=cv) grid.fit(x.reshape(-1, 1)) return grid.best_estimator_
適当なサンプルデータ(ここでは混合正規分布)を作成してこれを元に実験する。
混合正規の作り方は適当なので、これよりいいやり方があったら是非知りたい。
size = 10**3 x1 = np.random.normal(0.0, 1.0, size) x2 = np.random.normal(3.0, 0.5, size) uniform = np.random.rand(size) x = np.array([(x1[i] if x > 0.3 else x2[i]) for i, x in np.ndenumerate(uniform)]) # 念のための可視化 plt.hist(x) plt.show()
カーネル密度推定を実行してみる&正しく確率密度関数になってるか、幅広い区間で積分して1になるかでチェック→良さげ。
score_sample()はlogとった値返してきやがるので、expとらないとダメな点に注意、くそ仕様め。
# カーネル密度推定の実行 kde = estimate_kde(x) # 積分してチャンと1になるか → なる integrate.quad(lambda x: np.exp(kde.score_samples(x)), -np.inf, np.inf)
元のデータと推定した確率密度関数を重ねてみる、ここもexpとらないとダメな点に注意だ…
結果は良さげ。
# 適当なX軸の範囲で描画 x_plot = np.linspace(-5, 5, 50)[:, np.newaxis] fig, ax = plt.subplots() ax.plot(x_plot, np.exp(kde.score_samples(x_plot)), linewidth=3, alpha=0.5) ax.hist(x, 30, fc='gray', histtype='stepfilled', alpha=0.3, normed=True) ax.legend(loc='upper left')
WTI原油価格連動型上場投信(1671)の粗い可視化
石油に強い興味があるので、WTI原油先物に連動するETFの価格データをサクッと取得&簡易表示。
まずデータの取得&可視化。変数名はxtsクラスのオブジェクトなのでdfは正しくないが、まぁいいや…
library("quantmod") #quantmodがYahoo Japanのサイトから抜いてきてくれる df <- getSymbols('1671', return.class="xts", src="yahooj", auto.assign=FALSE) plot(df["2016-01-01::"]$Adjusted)
投資収益率(日次リターン)系列の可視化(ヒストグラム)
ggplot2でサクッと可視化。リターン系列への変換はまぁこんな感じ。
library("ggplot2") plot_histgram <- function(x, bins=100){ colnames(x) <- "value" ggplot(x, aes(value)) + geom_histogram(bins=bins, fill="red") } plot_histgram((df$Close - df$Open)/df$Open) plot_histgram(diff(df$Adjusted)/lag(df$Adjusted)) plot_histgram(diff(df$Open)/lag(df$Open))
石油で儲けるという夢を追っていきたい。