1つの解決策は、mice
パッケージ用に独自のカスタム代入関数を作成することです。パッケージはこのために準備されており、セットアップは驚くほど簡単です。
まず、提案されたとおりにデータをセットアップします。
dat=data.frame(x1=c(21, 50, 31, 15, 36, 82, 14, 14, 19, 18, 16, 36, 583, NA,NA,NA, 50, 52, 26, 24),
x2=c(0, NA, 18,0, 19, 0, NA, 0, 0, 0, 0, 0, 0,NA,NA, NA, 22, NA, 0, 0),
x3=c(0, 0, 0, 0, 0, 54, 0 ,0, 0, 0, 0, 0, 0, NA, NA, NA, NA, 0, 0, 0))
次に、mice
パッケージをロードし、デフォルトで選択するメソッドを確認します。
library(mice)
# Do a non-imputation
imp_base <- mice(dat, m=0, maxit = 0)
# Find the methods that mice chooses
imp_base$method
# Returns: "pmm" "pmm" "pmm"
# Look at the imputation matrix
imp_base$predictorMatrix
# Returns:
# x1 x2 x3
#x1 0 1 1
#x2 1 0 1
#x3 1 1 0
予測平均マッチングのpmm
略-おそらく連続変数を代入するための最も一般的な代入アルゴリズムです。回帰モデルを使用して予測値を計算し、予測値に最も近い5つの要素を選択します(ユークリッド距離による))を選択します。これらの選択された要素はドナープールと呼ばれ、最終値はこのドナープールからランダムに選択されます。
予測マトリックスから、メソッドは制限に関係する渡された変数を取得することがわかります。行がターゲット変数であり、列が予測変数であることに注意してください。x1のx3列に1がなかった場合、これをマトリックスに追加する必要があります。imp_base$predictorMatrix["x1","x3"] <- 1
さて、面白い方法として、代入メソッドを生成します。ここでは、基準を満たさない場合はすべての値を破棄する、かなり粗雑な方法を選択しました。これにより、ループ時間が長くなる可能性があり、有効な代入を保持し、残りの代入のみを再実行する方が効率的である可能性がありますが、少し調整が必要になります。
# Generate our custom methods
mice.impute.pmm_x1 <-
function (y, ry, x, donors = 5, type = 1, ridge = 1e-05, version = "",
...)
{
max_sum <- sum(max(x[,"x2"], na.rm=TRUE),
max(x[,"x3"], na.rm=TRUE))
repeat{
vals <- mice.impute.pmm(y, ry, x, donors = 5, type = 1, ridge = 1e-05,
version = "", ...)
if (all(vals < max_sum)){
break
}
}
return(vals)
}
mice.impute.pmm_x2 <-
function (y, ry, x, donors = 5, type = 1, ridge = 1e-05, version = "",
...)
{
repeat{
vals <- mice.impute.pmm(y, ry, x, donors = 5, type = 1, ridge = 1e-05,
version = "", ...)
if (all(vals == 0 | vals >= 14)){
break
}
}
return(vals)
}
mice.impute.pmm_x3 <-
function (y, ry, x, donors = 5, type = 1, ridge = 1e-05, version = "",
...)
{
repeat{
vals <- mice.impute.pmm(y, ry, x, donors = 5, type = 1, ridge = 1e-05,
version = "", ...)
if (all(vals == 0 | vals >= 16)){
break
}
}
return(vals)
}
メソッドの定義が完了したら、前のメソッドを単純に変更します。単一の変数のみを変更したい場合は、単に使用できますimp_base$method["x2"] <- "pmm_x2"
が、この例ではすべてを変更します(命名は不要です):
imp_base$method <- c(x1 = "pmm_x1", x2 = "pmm_x2", x3 = "pmm_x3")
# The predictor matrix is not really necessary for this example
# but I use it just to illustrate in case you would like to
# modify it
imp_ds <-
mice(dat,
method = imp_base$method,
predictorMatrix = imp_base$predictorMatrix)
次に、3番目の帰属データセットを見てみましょう。
> complete(imp_ds, action = 3)
x1 x2 x3
1 21 0 0
2 50 19 0
3 31 18 0
4 15 0 0
5 36 19 0
6 82 0 54
7 14 0 0
8 14 0 0
9 19 0 0
10 18 0 0
11 16 0 0
12 36 0 0
13 583 0 0
14 50 22 0
15 52 19 0
16 14 0 0
17 50 22 0
18 52 0 0
19 26 0 0
20 24 0 0
わかりました、それは仕事をします。主流の機能に便乗し、意味のある制限を追加するだけで、このソリューションが気に入っています。
更新
コメントに記載されている@ t0x1nの厳密な制限を実施するために、ラッパー関数に次の機能を追加することができます。
- 前の部分的に成功した実行からのデータが破棄されないように、ループ中に有効な値を保存します
- 無限ループを回避するためのエスケープメカニズム
- 適切な一致を見つけることなくx回試行した後、ドナープールを膨張させます(これは主にpmmに適用されます)
これにより、ラッパー関数が少し複雑になります。
mice.impute.pmm_x1_adv <- function (y, ry,
x, donors = 5,
type = 1, ridge = 1e-05,
version = "", ...) {
# The mice:::remove.lindep may remove the parts required for
# the test - in those cases we should escape the test
if (!all(c("x2", "x3") %in% colnames(x))){
warning("Could not enforce pmm_x1 due to missing column(s):",
c("x2", "x3")[!c("x2", "x3") %in% colnames(x)])
return(mice.impute.pmm(y, ry, x, donors = 5, type = 1, ridge = 1e-05,
version = "", ...))
}
# Select those missing
max_vals <- rowSums(x[!ry, c("x2", "x3")])
# We will keep saving the valid values in the valid_vals
valid_vals <- rep(NA, length.out = sum(!ry))
# We need a counter in order to avoid an eternal loop
# and for inflating the donor pool if no match is found
cntr <- 0
repeat{
# We should be prepared to increase the donor pool, otherwise
# the criteria may become imposs
donor_inflation <- floor(cntr/10)
vals <- mice.impute.pmm(y, ry, x,
donors = min(5 + donor_inflation, sum(ry)),
type = 1, ridge = 1e-05,
version = "", ...)
# Our criteria check
correct <- vals < max_vals
if (all(!is.na(valid_vals) |
correct)){
valid_vals[correct] <-
vals[correct]
break
}else if (any(is.na(valid_vals) &
correct)){
# Save the new valid values
valid_vals[correct] <-
vals[correct]
}
# An emergency exit to avoid endless loop
cntr <- cntr + 1
if (cntr > 200){
warning("Could not completely enforce constraints for ",
sum(is.na(valid_vals)),
" out of ",
length(valid_vals),
" missing elements")
if (all(is.na(valid_vals))){
valid_vals <- vals
}else{
valid_vals[is.na(valid_vals)] <-
vals[is.na(valid_vals)]
}
break
}
}
return(valid_vals)
}
これはそれほどうまく機能しないことに注意してください。これは、おそらく、提案されたデータセットがすべてのケースで制約を逃さずに失敗するためです。動作を開始する前に、ループの長さを400〜500に増やす必要があります。これは意図的なものではなく、あなたの帰属は実際のデータがどのように生成されるかを真似するべきだと思います。
最適化
引数ry
には非欠損値が含まれており、適格な補完を見つけた要素を削除することでループを高速化できますが、内部関数に慣れていないため、これを控えました。
フルフィルに時間がかかる強力な制約がある場合、最も重要なことは代入を並列化することだと思います(CrossValidatedに関する私の答えをご覧ください)。ほとんどのコンピューターには4〜8コアのコンピューターがあり、Rはデフォルトでそのうちの1つのみを使用します。コアの数を2倍にすることで、時間を(ほぼ)半分にスライスできます。
代入時に欠落しているパラメーター
問題についてx2
転嫁の時に行方不明であることを-マウスは、実際に欠損値を供給することはありませんx
- data.frame
。マウスの方法は、開始時にいくつかのランダムな値で充填含みます。代入のチェーン部分は、この初期値からの影響を制限します。mice
-function を見ると、代入呼び出しの前にこれを見つけることができます(mice:::sampler
-function):
...
if (method[j] != "") {
for (i in 1:m) {
if (nmis[j] < nrow(data)) {
if (is.null(data.init)) {
imp[[j]][, i] <- mice.impute.sample(y,
ry, ...)
}
else {
imp[[j]][, i] <- data.init[!ry, j]
}
}
else imp[[j]][, i] <- rnorm(nrow(data))
}
}
...
data.init
供給することができるmice
機能とmice.imput.sampleは基本サンプリング手順です。
訪問シーケンス
訪問順序が重要な場合、mice
-functionが代入を実行する順序を指定できます。デフォルトはfromです1:ncol(data)
が、を任意の値に設定できvisitSequence
ます。
0 or 16 or >= 16
しました。それがあなたの意味を台無しにしないことを願っています。同じ0 or >= 16
>=16
16
0 or 14 or >= 14