Объедините логические векторы для создания нелогического вектора

У меня есть два логических вектора во фрейме данных:

df <- data.frame(log1 = c(FALSE, FALSE, TRUE, FALSE, TRUE), log2 = c(TRUE, FALSE, FALSE, FALSE, TRUE))

Я хочу создать третий столбец, объединив эти два. Но этот новый столбец не должен просто содержать логические значения. Вместо этого ему следует присвоить третьему столбцу одно из трех значений: «высокое», «выброс» или «нормальное». «Высокий» имеет приоритет, поэтому в третьем столбце для строки 5 должно быть указано «высокий», а не «выходящий».

Я думаю, это можно сделать, используя if и else, но мне не удалось заставить это работать, используя следующий код:

df$new <- NA
if (df$log1 == TRUE){
  df$new <-  "high"
  } else if (df$log2 == TRUE) {
    df$new  <-  "outlier"
    } else {
      df$new  <-  "normal"
      }

Кто-нибудь может помочь?

df$new <- ifelse(df$log1, "high", ifelse(df$log2, "outlier", "normal")) должно работать. Обратите внимание, что при работе с логикой вам не нужен x == TRUE — R неявно читает if (x){...} как «если x ИСТИНА, сделайте ...» и if (!x){...} как «если x ЛОЖЬ, сделайте ...».
jpsmith 28.09.2023 14:37
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
87
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Речь идет о ifelse и его производных.

база R

ifelse(df$log1, "high", ifelse(df$log2, "outlier", "normal"))
# [1] "outlier" "normal" "high"   "normal" "high"  

дплир

Мы можем вкладывать dplyr::if_else, но вложение обычно побуждает нас использовать case_when.

library(dplyr)
df %>%
  mutate(
    new1 = if_else(log1, "high", if_else(log2, "outlier", "normal")), 
    new2 = case_when(log1 ~ "high", log2 ~ "outlier", TRUE ~ "normal")
  )
#    log1  log2    new1    new2
# 1 FALSE  TRUE outlier outlier
# 2 FALSE FALSE  normal  normal
# 3  TRUE FALSE    high    high
# 4 FALSE FALSE  normal  normal
# 5  TRUE  TRUE    high    high

Таблица данных

Аналогично, fifelse и fcase:

library(data.table)
as.data.table(df)[, new1 := fifelse(log1, "high", fifelse(log2, "outlier", "normal"))
  ][, new2 := fcase(log1, "high", log2, "outlier", default = "normal")][]
#      log1   log2    new1    new2
#    <lgcl> <lgcl>  <char>  <char>
# 1:  FALSE   TRUE outlier outlier
# 2:  FALSE  FALSE  normal  normal
# 3:   TRUE  FALSE    high    high
# 4:  FALSE  FALSE  normal  normal
# 5:   TRUE   TRUE    high    high

Обратите внимание: хотя в dplyr::case_when выше используются формулы-тильды, как в cond1 ~ value1, cond2 ~ value2, в варианте fcase используются чередующиеся аргументы, cond1, value1, cond2, value2, ...).

Кроме того, аргумент default= работает, пока он является константой. Если требуется динамическое значение по умолчанию (т. е. на основе содержимого таблицы), то необходимо иметь полностью истинный вектор, как в fcase(..., rep(TRUE, .N), NEWVALUE).

Я ценю это... Обычно я тоже очень нерешителен, в прошлом я обнаружил некоторые изменения в своих ответах, добавленные выводы, которые я не говорил и не собирался делать, и это немного раздражало. Легко поправимо, конечно. Ваше редактирование было совершенно нормальным и поощрялось. Спасибо @jpsmith

r2evans 28.09.2023 14:48

Попробуйте это без if/else:

df$new <- "normal"
df[df$log1, ]$new <- "high"
df[!df$log1 & df$log2, ]$new <- "outlier"

   log1  log2     new
1 FALSE  TRUE outlier
2 FALSE FALSE  normal
3  TRUE FALSE    high
4 FALSE FALSE  normal
5  TRUE  TRUE    high

Решение dplyr: попробуй это:

library(dplyr)

df %>% 
  mutate(new = ifelse(log1,
                      "high",
                      ifelse(log2,
                             "outlier",
                             "normal")  
  )
  )

Это отличный альтернативный подход, и он отлично работает! Одно предложение: всякий раз, когда я использую этот подход в своем коде, я всегда ставлю переназначение с наивысшим приоритетом в последнюю очередь. Хотя логика здесь совершенно непересекающаяся (и, следовательно, никаких изменений в этих данных), в более обобщенном смысле, когда логика может быть не столь идеально дополняющей, то, что она последней, гарантирует, что она переопределяет любое другое значение.

r2evans 28.09.2023 14:50

Использование индексации:

df$new <- with(df, c("normal", "outlier", "high", "high")[2L*log1 + log2 + 1L])
df
#>    log1  log2     new
#> 1 FALSE  TRUE outlier
#> 2 FALSE FALSE  normal
#> 3  TRUE FALSE    high
#> 4 FALSE FALSE  normal
#> 5  TRUE  TRUE    high

Решения data.table, на которые указал @r2evans, являются самыми быстрыми и наиболее эффективными с точки зрения использования памяти. Индексирование выигрывает для базовых решений.

f1 <- function(df) {
  within(df, new <- ifelse(log1, "high", ifelse(log2, "outlier", "normal")))
}

f2 <- function(df) {
  df %>%
    mutate(
      new = if_else(log1, "high", if_else(log2, "outlier", "normal")), 
    )
}

f3 <- function(df) {
  df %>%
    mutate(
      new = case_when(log1 ~ "high", log2 ~ "outlier", TRUE ~ "normal")
    )
}

f4 <- function(df) {
  setDT(df)[, new := fifelse(log1, "high", fifelse(log2, "outlier", "normal"))]
}

f5 <- function(df) {
  setDT(df)[, new := fcase(log1, "high", log2, "outlier", default = "normal")]
}

f6 <- function(df) {
  df$new <- "normal"
  df[df$log1, ]$new <- "high"
  df[!df$log1 & df$log2, ]$new <- "outlier"
  df
}

f7 <- function(df) {
  within(df, new <- c("normal", "outlier", "high", "high")[2L*log1 + log2 + 1L])
}

Тест:

df <- data.frame(log1 = sample(!0:1, 1e5, 1), log2 = sample(!0:1, 1e5, 1))

bench::mark(
  ifelse = f1(df),
  if_else = f2(df),
  case_when = f3(df),
  fifelse = f4(df),
  fcase = f5(df),
  subsetting = f6(df),
  indexing = f7(df),
  check = FALSE # mix of data.table and data.frame
)
#> # A tibble: 7 × 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 ifelse      40.32ms   41.9ms      24.0   10.32MB     12.0
#> 2 if_else      8.13ms   8.92ms     111.     9.95MB     33.0
#> 3 case_when    6.58ms   7.09ms     137.     7.36MB     45.7
#> 4 fifelse      1.68ms   2.01ms     483.     3.42MB     18.0
#> 5 fcase        1.45ms   1.57ms     618.      1.2MB     22.6
#> 6 subsetting   6.99ms    8.2ms     120.    10.85MB     59.9
#> 7 indexing     1.57ms   2.54ms     389.     4.27MB     50.9

Проголосовал за. Я бы тоже предложил bitwShiftL(as.integer(log1), 1L) + as.integer(log2)

asd-tm 29.09.2023 08:49

Или даже bitwOr(bitwShiftL(as.integer(T), 1L), as.integer(T))

asd-tm 29.09.2023 08:53

Другие вопросы по теме