Использование% in% в операторах ifelse? - PullRequest
1 голос
/ 06 августа 2020

Я знаю, что на форуме уже есть много вопросов по операторам ifelse, но я не могу найти ответа на свой конкретный запрос.

Я хотел бы использовать ifelse для создания нового столбца в фрейме данных на основе любого из двух условий. Обычно я хочу, чтобы гипертония имела значение 1, если «гипертония» находится в столбце сердечных заболеваний, ИЛИ, если bp medication = 1. Как вы можете видеть ниже, столбец гипертонии отмечен как 1 для всех строк в настоящее время. Есть ли проблема с использованием команды% in% в операторе ifelse, или я ошибся где-то еще по пути?

heart_conditions                  high_chol_tabs            bp_med         hypertension
                                            
 1 hypertension high_cholesterol 2 [no]              2 [no]                        1
 2 none                          4 [not applicable]  4 [not applicable]            1
 3 hypertension high_cholesterol 1 [yes]             1 [yes]                       1
 4 heart_attack angina           4 [not applicable]  4 [not applicable]            1
 5 high_cholesterol              2 [no]              4 [not applicable]            1
 6 hypertension high_cholesterol 1 [yes]             1 [yes]                       1
 7 none                          4 [not applicable]  4 [not applicable]            1
 8 none                          4 [not applicable]  4 [not applicable]            1
 9 high_cholesterol              2 [no]              4 [not applicable]            1
10 hypertension high_cholesterol 1 [yes]             1 [yes]                       1
hypertension.df$hypertension <- ifelse(("hypertension" %in% heart_conditions)|(bp_med == 1), 1, 2)

1 Ответ

4 голосов
/ 06 августа 2020

У вас все не так. Вам нужно heart_conditions %in% "hypertension" (или heart_conditions == "hyptertension")!

Или полный ответ:

hypertension.df$hypertension <- ifelse(heart_conditions == "hypertension" | bp_med == 1, 1, 2)

# or using %in%
selection <- "hypertension"
hypertension.df$hypertension <- ifelse(heart_conditions %in% selection | bp_med == 1, 1, 2)

Более длинное объяснение

%in% проверяет, если левый- hand-side присутствует в правой части и возвращает объект длины левой стороны.

names <- c("Alice", "Bob", "Charlie")

names %in% c("Alice", "Charlie")
#> [1]  TRUE FALSE  TRUE
"Alice" %in% names
#> [1] TRUE

Создано 06.08.2020 пакет репекс (v0.3.0)

Частичное совпадение

Как упоминалось в комментариях: %in% полностью сравнивает элементы. Чтобы проверить, находится ли строка внутри другой строки, мы можем сделать следующее:

Сравнение строк


library(tibble) # data.frames

df <- tribble(
  ~heart_conditions, ~high_chol_tabs, ~bp_med, ~hypertension,
  "hypertension high_cholesterol", 2, 2, 1,
  "none", 4, 4, 1,
  "hypertension high_cholesterol", 1, 1, 1,
  "heart_attack angina", 4, 4, 1,
  "high_cholesterol", 2, 4, 1,
  "hypertension high_cholesterol", 1, 1, 1,
  "none", 4, 4, 1,
  "none", 4, 4, 1,
  "high_cholesterol", 2, 4, 1,
  "hypertension high_cholesterol", 1, 1, 1
)



df$hypertension1 <- ifelse(grepl("hypertension", df$heart_conditions) | df$bp_med == 1, 1, 2)

library(stringr)

# imho more user friendly than grepl, but slightly slower
df$hypertension2 <- ifelse(str_detect(df$heart_conditions, "hypertension") | df$bp_med == 1, 1, 2)

df
#> # A tibble: 10 x 6
#>    heart_conditions high_chol_tabs bp_med hypertension hypertension1
#>    <chr>                     <dbl>  <dbl>        <dbl>         <dbl>
#>  1 hypertension hi…              2      2            1             1
#>  2 none                          4      4            1             2
#>  3 hypertension hi…              1      1            1             1
#>  4 heart_attack an…              4      4            1             2
#>  5 high_cholesterol              2      4            1             2
#>  6 hypertension hi…              1      1            1             1
#>  7 none                          4      4            1             2
#>  8 none                          4      4            1             2
#>  9 high_cholesterol              2      4            1             2
#> 10 hypertension hi…              1      1            1             1
#> # … with 1 more variable: hypertension2 <dbl>

Создано 06.08.2020 пакетом REPEX (v0.3.0)

Разделить и сравнить

Немного более медленное решение, которое не полагается на сравнение строк, состоит в том, чтобы разделить условия по пробелу и проверить, есть ли они являются гипертонией, вы можете сделать это так:

# split the heart-conditions
conds <- strsplit(df$heart_conditions, " ")
conds
#> [[1]]
#> [1] "hypertension"     "high_cholesterol"
#> 
#> [[2]]
#> [1] "none"
#> 
#> [[3]]
#> [1] "hypertension"     "high_cholesterol"
#> 
#> [[4]]
#> [1] "heart_attack" "angina"      
#> 
#> [[5]]
#> [1] "high_cholesterol"
#> 
#> [[6]]
#> [1] "hypertension"     "high_cholesterol"
#> 
#> [[7]]
#> [1] "none"
#> 
#> [[8]]
#> [1] "none"
#> 
#> [[9]]
#> [1] "high_cholesterol"
#> 
#> [[10]]
#> [1] "hypertension"     "high_cholesterol"
# for each row of the data, check if any value is hypertension
has_hypertension <- sapply(conds, function(cc) any(cc == "hypertension"))
has_hypertension
#>  [1]  TRUE FALSE  TRUE FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE

df$hypertension3 <- ifelse(has_hypertension | df$bp_med == 1, 1, 2)
df
#> # A tibble: 10 x 7
#>    heart_conditions high_chol_tabs bp_med hypertension hypertension1
#>    <chr>                     <dbl>  <dbl>        <dbl>         <dbl>
#>  1 hypertension hi…              2      2            1             1
#>  2 none                          4      4            1             2
#>  3 hypertension hi…              1      1            1             1
#>  4 heart_attack an…              4      4            1             2
#>  5 high_cholesterol              2      4            1             2
#>  6 hypertension hi…              1      1            1             1
#>  7 none                          4      4            1             2
#>  8 none                          4      4            1             2
#>  9 high_cholesterol              2      4            1             2
#> 10 hypertension hi…              1      1            1             1
#> # … with 2 more variables: hypertension2 <dbl>, hypertension3 <dbl>

Создано 06.08.2020 с помощью пакета реплекс (v0.3.0)

Бенчмарк

Заинтригованный моими предыдущими комментариями, я провел быстрый тест, сравнивая различные решения, а также добавил решение, используя stringi:

# splitter function
has_hypertension <- function(x) sapply(strsplit(x, " "), function(cc) any(cc == "hypertension"))

# create a larger dataset
df_large <- df %>% slice(rep(1:n(), 10000))

# benchmark the code:
bench::mark(
  grepl = grepl("hypertension", df_large$heart_conditions),
  stringi = stringi::stri_detect(df_large$heart_conditions, fixed = "hypertension"),
  stringr = str_detect(df_large$heart_conditions, "hypertension"),
  splitter = has_hypertension(df_large$heart_conditions)
)
#> # A tibble: 4 x 13
#>   expression      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result        memory               time        gc            
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   #> <bch:tm> <list>        <list>               <list>      <list>        
#> 1 grepl       16.67ms  16.91ms     59.0   390.67KB     2.11    28     1      474ms <lgl [100,00… <Rprofmem[,3] [1 × … <bch:tm [2… <tibble [29 ×…
#> 2 stringi      2.68ms   2.93ms    344.    390.67KB     6.22   166     3      482ms <lgl [100,00… <Rprofmem[,3] [1 × … <bch:tm [1… <tibble [169 …
#> 3 stringr     17.74ms  17.96ms     55.1   390.67KB     0       28     0      508ms <lgl [100,00… <Rprofmem[,3] [1 × … <bch:tm [2… <tibble [28 ×…
#> 4 splitter   153.39ms 153.39ms      6.52    3.67MB    19.6      1     3      153ms <lgl [100,00… <Rprofmem[,3] [551 … <bch:tm [4… <tibble [4 × …

Что ясно показывает, что stringi::stri_detect(txt, fixed = "hypertension") безусловно, самый быстрый!

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