前書き
- 本ドキュメントでは、Rのコーディング規約について明記しております。
- 基本的には、Hadley WickhamによるStyle guideとgoogle社で提案された規約をベースにします。
表記について
- パッケージ名は{}で表記
- 関数名は’’で表記
参照サイト
Advanced R, Google’s R Style Guide, R packages
Lint_tool_with_R
Rでコーディングスタイルを適用させる方法
gepuro流☆変数名の付け方
ご意見やご指摘など
- よりよいものにしてきたいと思います。こうした方がいいのではというご指摘があれば、コメントをよろしくお願いいたします。
- 下記のいずれかでご連絡・ご報告いただけますと励みになります。
Twitter, GitHub
Rのスクリプトファイルは、「-」(ハイフン)を区切り文字にした「.R」で終わる
実行順番がある場合は、先頭に付ける
ex) “00-read-date-file.R”, “01-date-preprocess.R”
Rのオブジェクトファイル(’save’で作成して、’load’で読み込み)は、「.RData」で終わる
ex) “forecast-result-201501.RData”
下記に基づき、語彙を選択(『リーダブルコード』を要参照)
- 機能を推測できる命名
- 知らせなければならない情報を追加
- 曖昧性が少ない
- 汎用的/抽象的な名前を避ける
- 予約語は避ける
変数名:
- 英語名詞
- snake_case(小文字単語を「_」で区切り)
- ハンガリアン記法は不要(命名に拘束力がないので、名前と実体が異なる混乱を招くため)
- ({ff}, {ffbase}によるオブジェクトは「ff」とすること(知らずに展開するとメモリを使い切る場合があるため))
- 名前属性もsnake_case
ex) names(input_vec) <- c(“var_1”, “var_2”)
- 定数は「大文字」単語を「」で区切る(名前属性も「大文字」単語のルール)
ex) “SET_FEATURE_NUM”
- ひとつの変数はひとつの目的だけで使う(目的が変わった場合は変数を流用しない)
関数名:
- 英語動詞
- lower Camel Case(小文字で書き始め、単語の先頭が大文字)
ex) selectValueName
- ブール値を返す関数は頭にis, has, can, should があるとわかりやすい
ex) isConvergence
- 名前から期待される通りの結果を出力、動作や合わせた明確な名前選びを
– getやsetを安易に使わない(適した語彙を選択すること)
属性値の取得・設定をイメージするので(属性値の取得・設定する場合はよい)
クラス名:
- Camel Case(単語の先頭が大文字)
ex) SelectValueName
パッケージ名:
- 「r」 + Camel Case(単語の先頭が大文字)
ex) rSelectValueName
Rのスクリプトファイルは下記の構造で管理(後述のShinyアプリ化する際、/script/以下の処理を読み込むことで対応できるように)
- 「/script/」以下は各言語による分析用スクリプトを配置
- 「/script/R/class/」以下はRのクラスファイルを置く
- 「/script/R/fun/」以下はRの関数ファイルを置く
- 「/doc/」以下はドキュメント(非自動生成)ファイルを置く
- 「/data/」以下はデータセット(「.RData」など)を配置
(例)「Analytics-Name」が分析名(命名規則は考え中)
/Analytics-Name/script/R/
/Analytics-Name/script/R/class/
/Analytics-Name/script/R/fun/
/Analytics-Name/script/SQL/
/Analytics-Name/doc/
/Analytics-Name/data/
Shinyアプリを作る場合
Shiny, The Shiny Cheat sheet
- 「app.R」でまとめず「ui.R」と「server.R」に分ける
- 共通処理は「global.R」で定義
- ただし、定数の設定は「global.R」と別ファイル(「def-constant.R」)で記述
- 「ReadMe.Rmd」でReadMeを記述(shinyによるアプリTOPで表示)
(例)「App-Name」がアプリ名(命名規則は考え中)
/App-Name/ui.R
/App-Name/server.R
/App-Name/global.R
/App-Name/def-constant.R
/App-Name/ReadMe.Rmd
/App-Name/script/R/
/App-Name/script/SQL/
/App-Name/data/
パッケージ化する場合
規定の構造に準ずる
- (例)
/PACKAGE_NAME/DESCRIPTION
/PACKAGE_NAME/NAMESPACE
/PACKAGE_NAME/NEWS
/PACKAGE_NAME/R/
/PACKAGE_NAME/man/
/PACKAGE_NAME/src/
/PACKAGE_NAME/tests/
/PACKAGE_NAME/data/
/PACKAGE_NAME/inst/
- パッケージ作成時に参照しておいた方がいい資料
R packages
東京R非公式おじさんが教える本当に気持ちいいパッケージ作成法
コピーライトは「DESCRIPTION」に書き、TODOやBUGFIXなどは「NEWS」に書く
分析スクリプト(関数定義ファイルは除く)は上から順に読み込んで、正しく実行されることが必須
(RStudio上で「Ctrl + Alt + R」のショートカットにて、コード全体を実行)
ひとつの関数にひとつの機能
ネストが深くなる場合(3を目安)は別の関数に分ける
– ただし、機能的に分けるべきでない場合はひとつのままにする(コメントを残しておくこと)
– 「,」の「後」(行列や配列の添字操作時の「,」も)
ex) lm(y ~ x1 + x2, data = data.frame(y = rnorm(100), x1 = rnorm(100), x2 = rnorm(100)))
ex) iris[1:10, ]
exampleFunction <- function (
iteration_num = 10,
...
){
# 演算子(「=, +, -, <-」など)の「前後」はスペース
res_pow <- numeric(iteration_num)
# 「for文の繰り返し指定は改行しない」
for (iter in seq(from = 1, to = iteration_num)) {
if (iter > 10) {
return(-1)
} else {
res_pow[iter] <- iter * iter
}
}
plot(res_pow) # 「関数呼び出しの前後は半角スペースをあけない」
return(1)
}
’dplyr’のパイプ処理ではチェイン演算子(%>%)ごとに改行
’if’文や’for’文、’apply’ファミリーはたとえ短くても一行でまとめてかかない(ワンライナー禁止)
res_sum <- 0
for (sum_iter in sample(x = seq(from = 1, to = 10), size = 1000, replace = TRUE)) {
res_sum <- res_sum + sum_iter
}
library(rugarch)
# 2週間分のARMAを考慮するため、「armaOrder = c(14, 14)」に設定
# キャンペーンや広告予算により、日毎に分散が変動すると考えられるため、分散不均一性を説明できるモデルを導入(「variance.model」を定義)
# 分布は「綺麗な正規分布」にはならないと想定できるため、「distribution.model = "snorm"」に設定
model_spec <- rugarch::ugarchspec(
mean.model = list(
armaOrder = c(14, 14), include.mean = TRUE,
arfima = TRUE,
external.regressors = NULL
),
variance.model = list(
model = "fGARCH", garchOrder = c(1, 1), submodel = "GARCH",
external.regressors = NULL
),
distribution.model = "snorm"
)
コメントの記述
– 「#」の後は半角スペースをひとつ空ける
– コードの後ろには書かない
– 関数内ではなるべく書かない
ブロック分け
– 「# Load data process —————————」のように、コードの塊を分ける
(RStudio上で「Ctrl + Shift + R」で挿入可能)
関数定義の際は{roxygen2}のフォーマットで必ずコメントを記述
– 「title, description, details」の各フィールドと「@param, @return, @examples」は必須項とする
「@param」は引数の説明(引数毎に記述)
「@return」は戻り値の説明
「@examples」は実行例
http://r-pkgs.had.co.nz/man.html
http://stackoverflow.com/tags/roxygen2/info
#' Sum of vector elements. (titleフィールド)
#'
#' \code{sum} returns the sum of all the values present in its arguments. (descriptionフィールド)
#'
#' This is a generic function: ... (detailsフィールド)
#'
#' @param ... Numeric, complex, or logical vectors.
#' @param na.rm A logical scalar. Should missing values (including NaN) be removed?
#' @return If all inputs are integer and logical, then the output
#' will be an integer. If integer overflow ...
#' @examples
#' sum(1:10)
#' sum(1:5, 6:10)
sum <- function(..., na.rm = TRUE) {}
上記の記述(一部省略している)によって生成される’sum’のドキュメントが下記のリンク
http://stat.ethz.ch/R-manual/R-devel/library/base/html/sum.html
コメントは日本語でもよいが、パッケージ化する際は英語にすること
パッケージ化する場合は{roxygen2}のフォーマットに準じたコメントを適宜行う
Introduction to roxygen2
オプションの引数名を指定する場合は必須
ex) read.table(“”, header = TRUE)
ex) read.table(file = “”, header = TRUE)
ただし、Rの基本パッケージ({stats}, {graphics}, {grDevices}, {utils}, {datasets}, {methods}, {base})内の関数を呼び出す際には、パッケージ名は省略(アドオンパッケージは省略せず)
【メモ】R のデフォルトパッケージを調べる #rstatsj
# {devtools}の関数呼び出し
devtools::session_info()
Session info --------------------------------------------------------------
setting value
version R version 3.2.0 (2015-04-16)
system x86_64, darwin13.4.0
ui X11
language (EN)
collate ja_JP.UTF-8
tz Asia/Tokyo
Packages ------------------------------------------------------------------
package * version date
devtools * 1.7.0 2015-01-17
digest * 0.6.8 2014-12-31
DistributionUtils * 0.5-1 2015-01-05
evaluate * 0.7 2015-04-21
expm * 0.99-1.1 2014-02-12
formatR * 1.2 2015-04-21
GeneralizedHyperbolic * 0.8-1 2015-01-05
htmltools * 0.2.6 2014-09-08
KernSmooth * 2.23-14 2015-02-11
knitr 1.10 2015-04-23
ks * 1.9.4 2015-03-07
lattice * 0.20-31 2015-03-30
magrittr * 1.5 2014-11-22
Matrix * 1.2-0 2015-04-04
misc3d * 0.8-4 2013-01-25
mvtnorm * 1.0-2 2014-12-18
nloptr * 1.0.4 2014-08-04
numDeriv * 2012.9-1 2012-10-14
Rcpp * 0.11.6 2015-05-01
rgl * 0.95.1201 2014-12-21
rmarkdown * 0.6.2.4 2015-06-07
Rsolnp * 1.15 2014-11-24
rstudioapi * 0.3.1 2015-04-07
rugarch 1.3-4 2014-11-09
SkewHyperbolic * 0.3-2 2015-01-05
spd * 2.0-0 2014-07-02
stringi * 0.4-1 2014-12-14
stringr * 1.0.0 2015-04-30
truncnorm * 1.0-7 2014-01-21
xts * 0.9-7 2014-01-02
yaml * 2.1.13 2014-06-12
zoo * 1.7-12 2015-03-16
source
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
Github (rstudio/rmarkdown@8c9e25b)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
CRAN (R 3.2.0)
# 基本パッケージ
sessionInfo()
R version 3.2.0 (2015-04-16)
Platform: x86_64-apple-darwin13.4.0 (64-bit)
Running under: OS X 10.10.2 (Yosemite)
locale:
[1] ja_JP.UTF-8/ja_JP.UTF-8/ja_JP.UTF-8/C/ja_JP.UTF-8/ja_JP.UTF-8
attached base packages:
[1] parallel stats graphics grDevices utils datasets methods
[8] base
other attached packages:
[1] rugarch_1.3-4 knitr_1.10
loaded via a namespace (and not attached):
[1] Rcpp_0.11.6 rstudioapi_0.3.1
[3] magrittr_1.5 devtools_1.7.0
[5] misc3d_0.8-4 lattice_0.20-31
[7] Rsolnp_1.15 stringr_1.0.0
[9] tools_3.2.0 xts_0.9-7
[11] SkewHyperbolic_0.3-2 GeneralizedHyperbolic_0.8-1
[13] spd_2.0-0 grid_3.2.0
[15] KernSmooth_2.23-14 htmltools_0.2.6
[17] yaml_2.1.13 digest_0.6.8
[19] rgl_0.95.1201 numDeriv_2012.9-1
[21] Matrix_1.2-0 nloptr_1.0.4
[23] formatR_1.2 DistributionUtils_0.5-1
[25] ks_1.9.4 evaluate_0.7
[27] rmarkdown_0.6.2.4 stringi_0.4-1
[29] expm_0.99-1.1 truncnorm_1.0-7
[31] mvtnorm_1.0-2 zoo_1.7-12
RStudioで「パッケージ名」+「::」で(+「キーボードのTabキー」)パッケージリストがサジェスト可
下記リンクにある’lsp’を定義して使うと、一覧リストがRコンソール上で取得できる
Show names of everything in a package
lsp <- function(package, what = 'all') {
ns <- asNamespace(package)
# base package does not have NAMESPACE
if (isBaseNamespace(ns))
return(ls(.BaseNamespaceEnv, all.names = TRUE))
else {
# for non base packages
if (exists('.__NAMESPACE__.', envir = ns, inherits = FALSE)) {
wh <- get('.__NAMESPACE__.', envir = asNamespace(package, base.OK = FALSE),
inherits = FALSE)
if ('?' %in% what)
return(ls(wh))
if (!is.null(what) && !any(what %in% c('all', ls(wh))))
stop('what is invalid; see ?rawr::lsp \'details\'')
tmp <- sapply(ls(wh), function(x) getNamespaceInfo(ns, x))
tmp <- rapply(tmp, ls, classes = 'environment',
how = 'replace', all.names = TRUE)
if (is.null(what))
return(tmp)
if (what %in% 'all')
return(ls(getNamespace(package), all.names = TRUE))
if (any(what %in% ls(wh)))
return(tmp[what])
} else
stop(sprintf('no NAMESPACE file found for package %s', package))
}
}
lsp("devtools", "exports")$exports
[1] "add_path" "add_rstudio_project"
[3] "add_test_infrastructure" "add_travis"
[5] "as.package" "bash"
[7] "build" "build_github_devtools"
[9] "build_vignettes" "build_win"
[11] "check" "check_cran"
[13] "check_doc" "clean_dll"
[15] "clean_source" "clean_vignettes"
[17] "compile_dll" "compiler_flags"
[19] "cran_env_vars" "create"
[21] "create_description" "dev_example"
[23] "dev_help" "dev_meta"
[25] "dev_mode" "dev_packages"
[27] "devtest" "document"
[29] "eval_clean" "evalq_clean"
[31] "find_rtools" "find_topic"
[33] "get_path" "github_pat"
[35] "github_pull" "github_release"
[37] "has_devel" "has_tests"
[39] "imports_env" "in_dir"
[41] "inst" "install"
[43] "install_bitbucket" "install_deps"
[45] "install_git" "install_github"
[47] "install_gitorious" "install_local"
[49] "install_svn" "install_url"
[51] "install_version" "is.package"
[53] "lint" "load_all"
[55] "load_code" "load_data"
[57] "load_dll" "loaded_packages"
[59] "missing_s3" "ns_env"
[61] "on_path" "parse_deps"
[63] "parse_ns_file" "pkg_env"
[65] "r_env_vars" "release"
[67] "release_checks" "reload"
[69] "revdep" "revdep_check"
[71] "revdep_check_save_logs" "revdep_check_save_summary"
[73] "revdep_check_summary" "revdep_maintainers"
[75] "run_examples" "session_info"
[77] "set_path" "setup"
[79] "show_news" "source_gist"
[81] "source_url" "submit_cran"
[83] "test" "unload"
[85] "use_appveyor" "use_build_ignore"
[87] "use_cran_comments" "use_data"
[89] "use_data_raw" "use_git_hook"
[91] "use_package" "use_package_doc"
[93] "use_rcpp" "use_readme_rmd"
[95] "use_revdep" "use_rstudio"
[97] "use_testthat" "use_travis"
[99] "use_vignette" "wd"
[101] "with_collate" "with_debug"
[103] "with_env" "with_envvar"
[105] "with_lib" "with_libpaths"
[107] "with_locale" "with_options"
[109] "with_par" "with_path"
用途がない限り、引数に「…」(dotsMethods)を与えない(引数名を間違えた際にも実行できてしまうケースを避ける)
パラメータに関しては、用途ごとにlistにまとめた引数を用いる
明示的に’return’を書く
クラスを使用しなくてもよいが、定義する場合は’R6’クラスを用いること
’R6’クラスについて
wch/R6, Introduction to R6 classes, Understanding R6: new OO system in R
R6パッケージの紹介―機能と実装, R6 class さっそく調査 #rstatsj
条件式の引数の並びは文法と同じに
if (age >= 20) 「もし年齢が20才以上ならば」(よい)
if (20 <= age) 「もし20才が年齢以下ならば」(だめ)
(左辺が「調査対象」(変化する)、右辺が「比較対象」(あまり変化しない))
可読性を考慮して、’ifelse’はネストさせてはいけない
極力ループは避け、ベクトル・行列演算または’apply’ファミリーで処理する
理由がない限り、’while’は使わない(無限ループを起こしやすい)
i, j, k などのfor文の添字は事前に必ず初期化する
ループ回数が多くなる場合は{iterators}と{foreach}を利用する
library(iterators)
library(foreach)
SET_ITER_NUM <- 5000
i <- res_sum <- 0
foreach_res <- foreach::foreach (i = iterators::icount(SET_ITER_NUM), .combine = c) %do% {
res_sum <- res_sum + i
i / res_sum
}
# library(devtools)
# devtools::install_github("hoxo-m/pforeach")
library(pforeach)
library(randomForest)
library(kernlab)
SET_PARALLE <- list(
IS_PARALLEL = TRUE,
CORE = 3
)
data(spam)
fit_rf <- pforeach::pforeach(
ntree = rep(250, SET_PARALLE$CORE), .c = c,
.export = "spam", .packages = "randomForest",
.parallel = SET_PARALLE$IS_PARALLEL, .cores = SET_PARALLE$CORE
)({
randomForest(type ~ ., data = spam, ntree = ntree)
})
添字操作よりも{dplyr}や{tidyr}を積極的に使ってパイプ処理で書く(ただし、{magrittr}と{pipeR}は可読性の観点で、なるべく避ける)
dplyr, tidyr, magrittr, pipeR
Data Wrangling with dplyr and tidyr Cheat Sheet
文字列処理は、基本的には{stringr}で、困ったときは{stringi}を利用する
hadley/stringr
stringr 1.0.0を使ってみる
stringiで輝く☆テキストショリスト
データベース接続には{RODBC}や{RJDBC}ではなく、{RMySQL}, {RPostgreSQL}, {ROracle}などを使う(または、{dplyr}の’src_*’を用いる)
欠損値について
– 欠損値を含んだレコードを除去する(リストワイズ法)か、「完全情報最尤推定による推定値」または「多重代入法」で補完する
(行った処理と理由をコメントとして残しておく)
kaggleで予測モデルを構築してみた (5) - Rで行うMultipleImputation
kaggleで予測モデルを構築してみた (6) - FIMLの仕組みとRのimputationパッケージ
外れ値と異常値について(外れ値検出(知識))
– 外れ値は他の値から大きく外れた値
– 異常値は外れ値のうち、原因(測定ミス、記録ミスなど)がわかっているもの
– 除去する場合は、どういう条件の値を除去するかコメントとして残しておく
統計モデリングには{RStan}を使う({JAGS}, {OpenBUGS}, {WinBUGS}は使わない)
RStan
RStanで『予測にいかす統計モデリングの基本』の売上データの分析をトレースしてみた
実践 統計モデリング入門
{LaplacesDemon}と{Zelig}に関しては検討中
LaplacesDemon, Zelig
機械学習アルゴリズムを実装する際は{caret}で実行できないか確認してから行う
train Model List
グラフのy軸は0から始める(それ以外の場合は凡例で明示する)
{ggplot2}を積極的に使う(派生も可)
ggplot2, Getting started with ggplot2
ggファミリーを可視化
ブラウザ表示やインタラクティブ処理がありえる場合は{ggvis}や{htmlwidgets}を使う
ggvis, htmlwidgets
{rCharts}や{plotly}と{googlevis}はなるべく使わない
rCharts, plotly, googleVis
Shinyアプリ化した際とパッケージ化する際は極力Gitでバージョン管理する
複数人が分析結果を使う場合や、前処理やDB接続や定型操作などはパッケージ化する
Rによる分析の際はR Markdownで分析結果をドキュメント化する(R以外の部分は今後の課題)
R Markdown, R Markdown Cheat Sheet
R Markdownで楽々レポートづくり, Rとウェブの融合(4)ーrmarkdownー
R Markdownでドキュメント化した際は実行時の環境を結果の後に出力する
({devtools}の’session_info’か{base}の’sessionInfo’を利用する)
Shinyで分析結果の利活用度を上げておく
[1] "2015-06-11 23:13:22 JST"