Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Rのmutate()で、case_when中にfor文をいれたい

Q&A

Closed

解決したいこと

Rのdplyr::mutate()で、case_whenにより条件分けしている中にfor文を入れることは可能でしょうか。

状況

Rでデータ編集を行なっています。
以下のコードで結果を得ることはできているのですが、for文を使って簡略化することは可能でしょうか。
STARTとENDは範囲を示す数値であり、
「1のSTARTからENDの範囲内であれば、1のANSWERを出力、以下2以降を繰り返し」というような条件付けです。
(以下のコードでは条件4つにしていますが、実際には条件50以上あり、なんとか簡略化したいです、、)

結果は出るが、煩雑なコード

df %>%
  mutate(newcol= case_when(
        POS >= data_base[[1, "START"]] &  
          POS <= data_base[[1, "END"]] ~ data_base[[1, "ANSWER"]],
        POS >= data_base[[2, "START"]] &  
          POS <= data_base[[2, "END"]] ~ data_base[[2, "ANSWER"]], 
        POS >= data_base[[3, "START"]] &  
          POS <= data_base[[3, "END"]] ~ data_base[[3, "ANSWER"]], 
        POS >= data_base[[4, "START"]] &  
          POS <= data_base[[4, "END"]] ~ data_base[[4, "ANSWER"]],
                TRUE ~ NA
       )) -> df_v2

以下のようにfor文を試してみましたが、これでは出力された結果が最後の4つ目の条件付けのもののみとなってしまいました。
for文を入れる位置がポイントのように思うのですが、他の場所ではエラーが出てしまっています。

for文を試すが、思うような結果が得られないコード

for (i in 1:4){df %>% mutate(
      newcol= case_when( 
        POS >= data_base[[i, "START"]] &  
          POS <= data_base[[i, "END"]] ~ data_base[[i, "ANSWER"]],
                TRUE ~ NA
     ))} -> df_v2

適切なコード等ありましたら、教えて頂けますと幸いです。よろしくお願いいたします。

0

3Answer

範囲が互いに排反であるのであれば,cutを使って前処理をするのが簡単なように思います.

set.seed(0)
df <- tibble(POS = runif(10, 0, 1) * 10)
df <- df %>% 
  mutate(newcol = cut(POS,
                      breaks = c(0, 1, 2, 3, 4, 5, 6, 7, 10),
                      labels = c("A", NA, "B", NA, "C", NA, "D", NA),
                      include.lowest = TRUE))
df
# A tibble: 10 × 2
     POS newcol
   <dbl> <fct> 
 1  8.97 NA    
 2  2.66 B     
 3  3.72 NA    
 4  5.73 NA    
 5  9.08 NA    
 6  2.02 B     
 7  8.98 NA    
 8  9.45 NA    
 9  6.61 D     
10  6.29 D   
1Like

Comments

  1. @ktakahashi2727

    Questioner

    コメント頂き誠にありがとうございます。

    範囲は排反しますが、桁数が膨大であるため(範囲を示す表を次のコメントに添付させて頂きます)、記載していたコードのように表の要素を指定する形(table[[1, "START"]]等)が間違いが少ないと考えていました。

    妙案ありますでしょうか。

そうすると範囲を表す表からbreakslabelsを先に作ってあげる方がループを使うことを考えるより楽だと思います.

set.seed(0)
df <- tibble(POS = runif(10, 0, 20))
tbl_breaks <- tibble(
  START = c(2, 6, 10, 16),
  END = c(5, 9, 13, 19),
  ANSWER = c(1, 2, NA, 1)
)

breaks <- tbl_breaks %>%
  select(START, END) %>%
  t() %>%
  as.vector()
breaks <- c(0, breaks, 20)
breaks
labels <- tbl_breaks %>% 
  select(ANSWER) %>%
  mutate(DUMMY = NA) %>%
  t() %>%
  as.vector()
labels <- c(NA, labels)
labels

df <- df %>% 
  mutate(newcol = cut(POS,
                      breaks = breaks,
                      labels = labels,
                      include.lowest = TRUE))

df
# A tibble: 10 × 2
     POS newcol
   <dbl> <fct> 
 1 17.9  1     
 2  5.31 NA    
 3  7.44 2     
 4 11.5  NA    
 5 18.2  1     
 6  4.03 1     
 7 18.0  1     
 8 18.9  1     
 9 13.2  NA    
10 12.6  NA   

注意点としては,cutだと半開区間しか扱えないので,POSの値が整数値で両側の境界にドンピシャで乗ってしまう可能性がある場合には,ENDに0.1を足しておくとか工夫が必要なところでしょうか.

1Like

Comments

  1. @ktakahashi2727

    Questioner

    丁寧にご回答頂きありがとうございます。
    無事、解決できました。
    前処理をしてcutを使用するという方法、今後も有効利用させて頂きます。

Your answer might help someone💌