12

以下のような一連の数字を持つデータフレームがあります。

data <- c(1,1,1,0,0,1,1,2,2,2,0,0,0,2,1,1,0,1,0,2)

私が必要としているのは、前後の数字が同一である 0 の 1、2、または 3 回の繰り返しのすべてのインスタンスを見つけることです。つまり、両方とも 1 または両方とも 2 (たとえば、1,0,1 または 2,0,0,2 2,0,1 ではありません)。

次に、周囲の値だけでゼロを埋める必要があります。

連続するゼロを見つけて数えることができました

consec <- (!data) * unlist(lapply(rle(data)$lengths, seq_len))

次に、これらの連続するゼロが始まる行を見つけました。

consec <- as.matrix(consec)
first_na <- which(consec==1,arr.ind=TRUE)

しかし、私は交換プロセスに困惑しています

これについてあなたの助けに本当に感謝します!

カール

4

4 に答える 4

14

rle()と を使用したループのないソリューションを次に示しますinverse.rle()

data <- c(1,1,1,0,0,1,1,2,2,2,0,0,0,2,1,1,0,1,0,2)

local({
  r <- rle(data)
  x <- r$values
  x0 <- which(x==0) # index positions of zeroes
  xt <- x[x0-1]==x[x0+1] # zeroes surrounded by same value
  r$values[x0[xt]] <- x[x0[xt]-1] # substitute with surrounding value
  inverse.rle(r)
})

[1] 1 1 1 1 1 1 1 2 2 2 2 2 2 2 1 1 1 1 0 2

PS。local()新しい一時オブジェクトの負荷でワークスペースを壊さないようにするための単純なメカニズムとして使用します。functionを使用する代わりに を作成することもできます。最近では、このタイプのタスクに多くlocal使用していることがわかります。local


PPS。このコードを変更して、元のデータの先頭または末尾のゼロを除外する必要があります。

于 2013-02-25T13:29:58.400 に答える
2

この質問への回答に多くの関心が寄せられているように思われるので、後世のために別の正規表現方法を書き留めておこうと思いました。

'gregexpr' 関数を使用すると、パターンを検索し、結果の位置の一致と一致の長さを使用して、元のベクトルで変更する値を呼び出すことができます。正規表現を使用する利点は、一致させたいパターンを正確に明示できることです。その結果、心配する除外ケースがなくなります。

注: 次の例は、1 桁の値を想定しているため、記述どおりに機能します。他のパターンにも簡単に適応させることができますが、単一の文字を使用した小さなショートカットを使用できます。可能性のある複数桁の値でこれを行いたい場合は、最初の連結 (「貼り付け」) 関数の一部として区切り文字を追加する必要があります。


コード

str.values <- paste(data, collapse="") # String representation of vector
str.matches <- gregexpr("1[0]{1,3}1", str.values) # Pattern 101/1001/10001
data[eval(parse(text=paste("c(",paste(str.matches[[1]] + 1, str.matches[[1]] - 2 + attr(str.matches[[1]], "match.length"), sep=":", collapse=","), ")")))] <- 1 # Replace zeros with ones
str.matches <- gregexpr("2[0]{1,3}2", str.values) # Pattern 202/2002/20002
data[eval(parse(text=paste("c(",paste(str.matches[[1]] + 1, str.matches[[1]] - 2 + attr(str.matches[[1]], "match.length"), sep=":", collapse=","), ")")))] <- 2 # Replace zeros with twos

ステップ 1 : すべてのデータ値の 1 つの文字列を作成します。

str.values <- paste(data, collapse="")
# "11100112220002110102"

これにより、データが 1 つの長い文字列に折りたたまれるため、正規表現を使用できます。

ステップ 2 : 正規表現を適用して、文字列内の一致の場所と長さを見つけます。

str.matches <- gregexpr("1[0]{1,3}1", str.values)
# [[1]]
# [1]  3 16
# attr(,"match.length")
# [1] 4 3
# attr(,"useBytes")
# [1] TRUE

この場合、正規表現を使用して最初のパターンを探します。1 つから 3 つのゼロ ( [0]{2,}) と両側に 1 がある ( 1[0]{1,3}1)。両端で一致する 1 つまたは 2 つをチェックする必要がないように、パターン全体を一致させる必要があります。次のステップで、これらの両端を差し引きます。

ステップ 3 : 元のベクトル内のすべての一致する位置に 1 を書き込みます。

data[eval(parse(text=paste("c(",paste(str.matches[[1]] + 1, str.matches[[1]] - 2 + attr(str.matches[[1]], "match.length"), sep=":", collapse=","), ")")))] <- 1
# 1 1 1 1 1 1 1 2 2 2 0 0 0 2 1 1 1 1 0 2

ここでは、いくつかの手順を一度に実行しています。まず、正規表現で一致した数字から数列のリストを作成しています。この場合、インデックス 3 と 16 で始まり、それぞれ 4 と 3 アイテムの長さの 2 つの一致があります。これは、ゼロがインデックス (3+1):(3-2+4) または 4:5 および (16+1):(16-2+3) または 17:17 にあることを意味します。複数の一致がある場合は、「collapse」オプションを使用してこれらのシーケンスを連結 (「貼り付け」) します。次に、2 番目の連結を使用して、シーケンスを結合 ( c()) 関数内に配置します。「eval」および「parse」関数を使用して、このテキストをコードに変換し、インデックス値として [data] 配列に渡します。これらの場所にすべて 1 を書き込みます。

ステップ x : パターンごとに繰り返します。この場合、2 回目の検索を実行して、両側に 2 を持つ 1 ~ 3 個のゼロを見つけてから、ステップ 3 と同じステートメントを実行する必要がありますが、1 ではなく 2 を割り当てます。

str.matches <- gregexpr("2[0]{1,3}2", str.values)
# [[1]]
# [1] 10
# attr(,"match.length")
# [1] 5
# attr(,"useBytes")
# [1] TRUE

data[eval(parse(text=paste("c(",paste(str.matches[[1]] + 1, str.matches[[1]] - 2 + attr(str.matches[[1]], "match.length"), sep=":", collapse=","), ")")))] <- 2
# 1 1 1 1 1 1 1 2 2 2 2 2 2 2 1 1 1 1 0 2

更新:元のコードに書いた「2つ以上」ではなく、1つから3つのゼロが連続して一致すると言われている元の問題に気付きました。コードは同じままですが、正規表現と説明を更新しました。

于 2013-02-25T17:22:20.920 に答える
1

ループのない解決策があるかもしれませんが、forこれを試すことができます:

tmp <- rle(data)
val <- tmp$values
for (i in 2:(length(val)-1)) {
  if (val[i]==0 & val[i-1]==val[i+1]) val[i] <- val[i-1]
}
tmp$values <- val
inverse.rle(tmp)  

与える:

[1] 1 1 1 1 1 1 1 2 2 2 2 2 2 2 1 1 1 1 0 2
于 2013-02-25T12:44:39.857 に答える