1. ホーム
  2. r

[解決済み] Rの再現性のある優れた例題の作り方

2022-03-16 09:17:19

質問

同僚とパフォーマンスについて議論するとき、教えるとき、バグレポートを送るとき、メーリングリストやここStack Overflowでガイダンスを探すときに 再現可能な例 という質問がよくあり、いつも助かっています。

優れたサンプルを作るためのコツは何ですか?のデータ構造をどのように貼り付けるのですか? r をテキスト形式で表示できますか?また、他にどのような情報を含めるべきでしょうか?

を使う以外に何かコツがあるのでしょうか? dput() , dump() または structure() ? どのような場合に library() または require() の文は? のほかに、どの予約語を避けるべきでしょうか? c , df , data などでしょうか?

どうすれば素晴らしい r 再現性のある例?

解決方法は?

基本的には 再現可能な最小限の例(MRE) は、他の人が まさに あなたのマシンであなたの問題を再現してください。

MREは以下の項目で構成されています。

  • a 最小限のデータセット 問題を実証するために必要な
  • その 最小限の 実行可能 コード エラーを再現するために必要で、与えられたデータセットで実行できるもの。
  • すべて 必要な情報 使用するパッケージ、Rのバージョン、実行するOSについて。
  • ランダムプロセスの場合 (によって設定される)。 set.seed() ) で、再現性を確保します。

良いMREの例については、使用している関数のヘルプファイルの一番下にある「"例"」のセクションを参照してください。例えば、以下のように入力します。 help(mean) または、短い ?mean をRコンソールに入力してください。

最小限のデータセットを提供する

通常、巨大なデータセットを共有する必要はありませんし、むしろ他の人があなたの質問を読む気をなくすかもしれません。したがって、組み込みのデータセットを使用するか、あなたの元のデータに似た小さな"toy"の例を作成する方がよいでしょう。 ミニマル . 何らかの理由でどうしても元のデータを共有する必要がある場合は、次のような方法を使用する必要があります。 dput() 他の人があなたのデータの正確なコピーを取得できるようにするためです。

組み込みのデータセット

ビルトインデータセットのいずれかを使用することができます。ビルトインデータセットの一覧は data() . 各データセットには短い説明があり、より詳細な情報は例えば ?iris インストールされたパッケージには、さらに別のデータセットが含まれているかもしれません。

サンプルデータセットの作成

予備知識 時には、要因、日付、時系列などの特別なフォーマット(つまりクラス)が必要な場合があります。これらについては、次のような関数を利用する。 as.factor , as.Date , as.xts , ...

d <- as.Date("2020-12-30")

ここで

class(d)
# [1] "Date"

ベクター

x <- rnorm(10)  ## random vector normal distributed
x <- runif(10)  ## random vector uniformly distributed    
x <- sample(1:100, 10)  ## 10 random draws out of 1, 2, ..., 100    
x <- sample(LETTERS, 10)  ## 10 random draws out of built-in latin alphabet

マトリックス

m <- matrix(1:12, 3, 4, dimnames=list(LETTERS[1:3], LETTERS[1:4]))
m
#   A B C  D
# A 1 4 7 10
# B 2 5 8 11
# C 3 6 9 12

データフレーム

set.seed(42)  ## for sake of reproducibility
n <- 6
dat <- data.frame(id=1:n, 
                  date=seq.Date(as.Date("2020-12-26"), as.Date("2020-12-31"), "day"),
                  group=rep(LETTERS[1:2], n/2),
                  age=sample(18:30, n, replace=TRUE),
                  type=factor(paste("type", 1:n)),
                  x=rnorm(n))
dat
#   id       date group age   type         x
# 1  1 2020-12-26     A  27 type 1 0.0356312
# 2  2 2020-12-27     B  19 type 2 1.3149588
# 3  3 2020-12-28     A  20 type 3 0.9781675
# 4  4 2020-12-29     B  26 type 4 0.8817912
# 5  5 2020-12-30     A  26 type 5 0.4822047
# 6  6 2020-12-31     B  28 type 6 0.9657529

これは広く使われていますが、データフレームに名前をつけないほうがよいでしょう。 df なぜなら df() は,密度(点 x )のF分布と衝突する可能性があります。

元データのコピー

特別な理由がある場合、または例題を作成するのが難しいデータの場合、元データの小さなサブセットを提供することができます。 dput .

を使用する理由 dput() ?

dput は、あなたのコンソールでデータを正確に再現するために必要なすべての情報を投げます。その出力をコピーして、あなたの質問に貼り付けるだけでよいのです。

呼び出し dat (上記より) 質問で共有した場合、変数クラスなどの情報がまだ不足している出力が出ます。さらに、スペースが type の列は、何かをするのが難しいのです。せっかくデータを使おうと思っても、データの重要な特徴を正しく把握することができないのです。

  id       date group age   type         x
1  1 2020-12-26     A  27 type 1 0.0356312
2  2 2020-12-27     B  19 type 2 1.3149588
3  3 2020-12-28     A  20 type 3 0.9781675

データのサブセット

サブセットを共有するには head() , subset() またはインデックス iris[1:4, ] . そして、それを dput() を使えば、すぐにRに入れられるものを他の人に与えることができます。

dput(iris[1:4, ]) # first four rows of the iris data set

質問で共有するコンソール出力。

structure(list(Sepal.Length = c(5.1, 4.9, 4.7, 4.6), Sepal.Width = c(3.5, 
3, 3.2, 3.1), Petal.Length = c(1.4, 1.4, 1.3, 1.5), Petal.Width = c(0.2, 
0.2, 0.2, 0.2), Species = structure(c(1L, 1L, 1L, 1L), .Label = c("setosa", 
"versicolor", "virginica"), class = "factor")), row.names = c(NA, 
4L), class = "data.frame")

を使用する場合 dput のように、関連する列のみを含めることもできます(例: dput(mtcars[1:3, c(2, 5, 6)])。

<サブ データフレームに多くのレベルを持つ因子がある場合は dput 出力は、たとえそれがデータのサブセットに存在しなくても、可能性のあるすべての因子レベルをリストするので、扱いにくいことがあります。この問題を解決するには droplevels() という関数があります。以下のように、speciesが1つのレベルしかない因子であることに注目してください。 dput(droplevels(iris[1:4, ])) . もう一つの注意点は dput は、キー付きの data.table オブジェクトや、グループ化された tbl_df (クラス grouped_df ) から tidyverse . このような場合は、共有する前に通常のデータフレームに変換して戻すことができます。 dput(as.data.frame(my_data)) .

最小限のコードを生成する

最小限のデータ(上記参照)を組み合わせれば、あなたのコードをコピー&ペーストするだけで、他のマシンでも問題を正確に再現できるはずです。

これは簡単なことのはずですが、そうでないこともよくあります。やってはいけないこと

  • 提供されたデータがすでに正しい形式であることを確認する(もちろん、それが問題である場合を除く)。
  • どこかでエラーになるスクリプトを丸ごとコピーペーストしてください。どの行がエラーになるのか、具体的に探してみてください。多くの場合、何が問題なのか自分で見つけることができるはずです。

やるべきこと

  • を使用している場合は、使用するパッケージを追加します。 library() )
  • 新しいRセッションでコードをテスト実行し、コードが実行可能であることを確認します。コンソールにあなたのデータとコードをコピーペーストして、あなたと同じものを得ることができるはずです。
  • 接続を開いたりファイルを作成したりした場合は、それらを閉じたりファイルを削除するコードを追加してください ( unlink() )
  • オプションを変更した場合は、元のオプションに戻す記述がコードに含まれていることを確認すること。(例 op <- par(mfrow=c(1,2)) ...some code... par(op) )

必要な情報を提供する

ほとんどの場合、Rのバージョンとオペレーティング・システムだけで十分である。パッケージとのコンフリクトが発生した場合は sessionInfo() は本当に助かります。他のアプリケーションとの接続について話すとき(ODBCであれ何であれ)、それらのバージョン番号と、可能であればセットアップに関する必要な情報も提供する必要があります。

でRを実行している場合 Rスタジオ を使用しています。 rstudioapi::versionInfo() は、RStudioのバージョンを報告するのに役立ちます。

特定のパッケージで問題が発生した場合、そのパッケージのバージョンを packageVersion("name of the package") .

シード

使用方法 set.seed() は、シードを指定することができます。 1 つまり、Rの乱数発生器が固定されている状態である。これにより、以下のようなランダム関数が可能になります。 sample() , rnorm() , runif() などがあり、常に同じ結果を返します。

set.seed(42)
rnorm(3)
# [1]  1.3709584 -0.5646982  0.3631284

set.seed(42)
rnorm(3)
# [1]  1.3709584 -0.5646982  0.3631284

1 <サブ の出力は set.seed() は、R >3.6.0とそれ以前のバージョンとでは異なります。ランダム処理に使用したRのバージョンを指定し、古い問題を追ったときに若干異なる結果が出ても驚かないようにしましょう。そのような場合に同じ結果を得るためには RNGversion() -関数 before set.seed() (例: RNGversion("3.5.2") ).