1. ホーム
  2. r

[解決済み] dplyr mutate/replace several columns on the subset of rows.

2023-02-01 05:40:37

質問

私はdplyrベースのワークフローを試している最中で(私が慣れているdata.tableをほとんど使用するのではなく)、同等のdplyrソリューションを見つけることができない問題に遭遇しています。私は一般的に、1つの条件に基づいて複数の列を条件付きで更新/置換する必要があるシナリオに遭遇します。以下は、私のdata.tableソリューションのいくつかのサンプルコードです。

library(data.table)

# Create some sample data
set.seed(1)
dt <- data.table(site = sample(1:6, 50, replace=T),
                 space = sample(1:4, 50, replace=T),
                 measure = sample(c('cfl', 'led', 'linear', 'exit'), 50, 
                               replace=T),
                 qty = round(runif(50) * 30),
                 qty.exit = 0,
                 delta.watts = sample(10.5:100.5, 50, replace=T),
                 cf = runif(50))

# Replace the values of several columns for rows where measure is "exit"
dt <- dt[measure == 'exit', 
         `:=`(qty.exit = qty,
              cf = 0,
              delta.watts = 13)]

この同じ問題に対する簡単なdplyrの解決策はありますか?私は条件を何度も入力したくないので、ifelseの使用を避けたいと思います。これは単純化された例ですが、1つの条件に基づいて多くの割り当てがあることがあります。

助けてくれてありがとうございます!

どのように解決するのですか?

これらの解決策は、(1) パイプラインを維持する、(2) ではなく を上書きせず、(3) 条件は一度だけ指定する必要があります。

1a) mutate_cond パイプラインに組み込むことができる、データフレームやデータテーブルのための簡単な関数を作成します。 この関数は以下のようなものです。 mutate のようなものですが、条件を満たす行に対してのみ作用します。

mutate_cond <- function(.data, condition, ..., envir = parent.frame()) {
  condition <- eval(substitute(condition), .data, envir)
  .data[condition, ] <- .data[condition, ] %>% mutate(...)
  .data
}

DF %>% mutate_cond(measure == 'exit', qty.exit = qty, cf = 0, delta.watts = 13)

1b) mutate_last これはデータフレームやデータテーブルのための代替関数で、これもまた以下のようなものです。 mutate の中で使われるだけです。 group_by (の中だけで使われ(下の例のように)、すべてのグループではなく、最後のグループに対してのみ動作します。 TRUE > FALSE に注意してください。 group_by が条件を指定した場合 mutate_last はその条件を満たす行に対してのみ動作します。

mutate_last <- function(.data, ...) {
  n <- n_groups(.data)
  indices <- attr(.data, "indices")[[n]] + 1
  .data[indices, ] <- .data[indices, ] %>% mutate(...)
  .data
}


DF %>% 
   group_by(is.exit = measure == 'exit') %>%
   mutate_last(qty.exit = qty, cf = 0, delta.watts = 13) %>%
   ungroup() %>%
   select(-is.exit)

2) 因子分解条件 条件を因数分解して、余分な列を作り、後で削除します。 次に ifelse , replace のように、論理を使った算術も可能です。 これはデータテーブルにも有効である。

library(dplyr)

DF %>% mutate(is.exit = measure == 'exit',
              qty.exit = ifelse(is.exit, qty, qty.exit),
              cf = (!is.exit) * cf,
              delta.watts = replace(delta.watts, is.exit, 13)) %>%
       select(-is.exit)

3) sqldf SQLを使うことができます update を使うことができます(ただし、変換しない限りデータテーブルは使えません。 参照 dplyr issue 1579 を参照してください。 ). の存在により、このコードでは入力を望ましくない形で変更しているように見えるかもしれません。 update が存在するために、このコードでは入力に望ましくない変更を加えているように見えるかもしれませんが、実際には update は実際の入力ではなく、一時的に生成されたデータベース内の入力のコピーに対して作用しています。

library(sqldf)

DF %>% 
   do(sqldf(c("update '.' 
                 set 'qty.exit' = qty, cf = 0, 'delta.watts' = 13 
                 where measure = 'exit'", 
              "select * from '.'")))

4) 行頭_ケース_時 また row_case_when で定義されている tibbleを返す: case_whenでどのようにベクトル化するか? . と似た構文で case_when に似た構文を使いますが、行に適用されます。

library(dplyr)

DF %>%
  row_case_when(
    measure == "exit" ~ data.frame(qty.exit = qty, cf = 0, delta.watts = 13),
    TRUE ~ data.frame(qty.exit, cf, delta.watts)
  )

注1: として使用しました。 DF

set.seed(1)
DF <- data.frame(site = sample(1:6, 50, replace=T),
                 space = sample(1:4, 50, replace=T),
                 measure = sample(c('cfl', 'led', 'linear', 'exit'), 50, 
                               replace=T),
                 qty = round(runif(50) * 30),
                 qty.exit = 0,
                 delta.watts = sample(10.5:100.5, 50, replace=T),
                 cf = runif(50))

注2: 行のサブセットを更新することを簡単に指定する方法の問題は、dplyrの問題でも取り上げられています 134 , 631 , 1518 および 1573 631 がメインスレッドで 1573 はここでの回答のレビューです。