Условное обновление аналогично SQL - PullRequest
0 голосов
/ 07 октября 2019

У меня есть следующий фрейм данных

library(tidyverse)
x <- c(1,2,3,NA,NA,4,5)
y <- c(1,2,3,5,5,4,5)
z <- c(1,1,1,6,7,7,8)
df <- data.frame(x,y,z)

df
   x y z
1  1 1 1
2  2 2 1
3  3 3 1
4 NA 5 6
5 NA 5 7
6  4 4 7
7  5 5 8

Я бы хотел обновить фрейм данных в соответствии со следующими условиями

  • Если z == 1, обновить до x = 1,иначе оставьте текущее значение для x
  • Если z == 1, обновите до y = 2, иначе оставьте текущее значение для y

Следующий код отлично справляется с работой

df %>% mutate(x=if_else(z==1,1,x),y=if_else(z==1,2,y))
   x y z
1  1 2 1
2  1 2 1
3  1 2 1
4 NA 5 6
5 NA 5 7
6  4 4 7
7  5 5 8

Однако я должен добавить оператор if_else для функций мутации x и y. Это может сделать мой код сложным и трудным для чтения. Чтобы привести аналогию с SQL, рассмотрим следующий код

UPDATE df
SET x= 1, y= 2
WHERE z = 1;

Я бы хотел добиться следующего:

  • Укажите условие обновления заранее, поэтому я не будуЯ должен повторять это для каждой функции преобразования
  • Я бы хотел избежать использования data.table или base R. Я использую dplyr, поэтому я хотел бы придерживаться его для согласованности

Ответы [ 4 ]

0 голосов
/ 07 октября 2019

Используя mutate_cond, опубликованный в dplyr mutate / замену нескольких столбцов в подмножестве строк , мы можем сделать это:

df %>% mutate_cond(z == 1, x = 1, y = 2)

, давая:

   x y z
1  1 2 1
2  1 2 1
3  1 2 1
4 NA 5 6
5 NA 5 7
6  4 4 7
7  5 5 8

sqldf

Конечно, вы можете напрямую реализовать его в SQL с помощью sqldf - игнорируйте предупреждающее сообщение, которое выдает серверная часть RSQLite.

library(sqldf)
sqldf(c("update df set x = 1, y = 2 where z = 1", "select * from df"))

base R

Это прямовперед в базе R:

df[df$z == 1, c("x", "y")] <- list(1, 2)
0 голосов
/ 07 октября 2019
library(dplyr)
df %>%
    mutate(x = replace(x, z == 1, 1),
           y = replace(y, z == 1, 2))
#   x y z
#1  1 2 1
#2  1 2 1
#3  1 2 1
#4 NA 5 6
#5 NA 5 7
#6  4 4 7
#7  5 5 8

В базе R

transform(df,
          x = replace(x, z == 1, 1),
          y = replace(y, z == 1, 2))

Если вы храните условие в переменной, вам не нужно вводить его несколько раз

condn = (df$z == 1)
transform(df,
          x = replace(x, condn, 1),
          y = replace(y, condn, 2))
0 голосов
/ 07 октября 2019

Если у вас есть SQL-фон, вам действительно стоит проверить :

library(data.table)
dt <- as.data.table(df)

set(dt, which(z == 1), c('x', 'y'), list(1, 2))
dt

# or perhaps more classic syntax

dt <- as.data.table(df)
dt
#    x y z
#1:  1 1 1
#2:  2 2 1
#3:  3 3 1
#4: NA 5 6
#5: NA 5 7
#6:  4 4 7
#7:  5 5 8

dt[z == 1, `:=`(x = 1, y = 2)]
dt

#    x y z
#1:  1 2 1
#2:  1 2 1
#3:  1 2 1
#4: NA 5 6
#5: NA 5 7
#6:  4 4 7
#7:  5 5 8

Последний вариант - объединение обновлений. Это замечательно, если у вас уже есть предварительные данные поиска:

# update join:
dt <- as.data.table(df)
dt_lookup <- data.table(x = 1, y = 2, z = 1)

dt[dt_lookup, on = .(z), `:=`(x = i.x, y = i.y)]
dt
0 голосов
/ 07 октября 2019

Вот один вариант с map2. Прокрутите столбцы 'x', 'y' набора данных вместе со значениями, которые нужно изменить, примените case_when на основе значений 'z', если оно равно TRUE, затем верните новое значение или верните то же самоестолбец и bind столбцы с исходным набором данных

library(dplyr)
library(purrr)
map2_df(df %>%
          select(x, y), c(1, 2), ~ case_when(df$z == 1 ~ .y, TRUE ~ .x)) %>%
     bind_cols(df %>%
                 select(z), .) %>%
     select(names(df))

Или с помощью base R создайте логический вектор, используйте его для подстановки строк столбцов 'x', 'y' иобновить путем присвоения list значений

i1 <- df$z == 1
df[i1, c('x', 'y')] <- list(1, 2)
df
#   x y z
#1  1 2 1
#2  1 2 1
#3  1 2 1
#4 NA 5 6
#5 NA 5 7
#6  4 4 7
#7  5 5 8

Преимущество обоих решений состоит в том, что мы можем передать n количество столбцов с соответствующими значениями для передачи и не повторять код

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...