Reputation: 35
I know there are many questions on ifelse statements on the forum already, but I cannot seem to find an answer to my particular query.
I would like to use ifelse to generate a new column in a dataframe based on either of two conditions. Basically I want hypertension to say 1 if "hypertension" is in the heart conditions column, OR, if bp medication=1. As you can see below the hypertension column is marked as 1 for all rows currently. Is there an issue with using the %in% command in an ifelse statement, or have I gone wrong somewhere else along the way?
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)
Upvotes: 1
Views: 308
Reputation: 10152
You have it the wrong way around. What you want is heart_conditions %in% "hypertension"
(or heart_conditions == "hyptertension"
)!
Or the full answer:
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%
checks if the left-hand-side is present in the right-hand-side and returns an object of the length of the left-hand-side.
names <- c("Alice", "Bob", "Charlie")
names %in% c("Alice", "Charlie")
#> [1] TRUE FALSE TRUE
"Alice" %in% names
#> [1] TRUE
Created on 2020-08-06 by the reprex package (v0.3.0)
As mentioned in the comments: %in%
fully compares the elements. To check if a string is inside another string, we can do the following:
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>
Created on 2020-08-06 by the reprex package (v0.3.0)
A slightly slower solution, which does not rely on string comparison, is to split the conditions by space and check if any are hypertension, you can do it like so:
# 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>
Created on 2020-08-06 by the reprex package (v0.3.0)
Intrigued by my earlier comments, I ran a quick benchmark comparing the different solutions, also adding a solution using 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 × …
Which clearly shows that stringi::stri_detect(txt, fixed = "hypertension")
is by far the fastest!
Upvotes: 4