vector filling true or false based on the given conditions in R without cycle

Viewed 604

I have sample vector and some values:

q = c(0.00000000, -0.70218526, -0.60635393,  0.32325554,  -0.45921704,  -0.57336113,  -0.77683717,
  -1.76347868,  -1.90884891,  -0.86157465,  -0.72896622,  -0.86831735,  -0.79357262,  -0.65279976,
  0.39921356,  0.78018094,  0.75703279,  0.70898895,  1.10155383,  0.88428135,  0.81338108,
  0.65611568,  0.89776945, 0.65447442,  0.16289673,  0.19464041,  0.01762445, -0.57663945,
  -1.01231868, -0.81204022, -0.99165533, -0.62666993, -1.05661282, -0.78221866, -0.03129549,  1.04051915)

s = -1.59688
i = -0.6373684
z = 0

I need to create a new vector in which boolean values will be filled according to the following conditions:

  • if q is less than i we fill TRUE until
    • q becomes more than 0 (that is, z)
    • or until q becomes less than s.
  • If Filling has stopped due to the condition of the s value, then you need to wait until the q becomes greater than 0 (that is, z) and
  • only after that you can start filling TRUE again, otherwise fill in FALSE

As a result, for this sample data, you should get the following result (I filled it in manually):

out <- c(FALSE,  TRUE,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE, FALSE, FALSE,
                 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
                 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,  TRUE,  TRUE,  TRUE,  TRUE,  TRUE,  TRUE,  TRUE, FALSE)

out

[1] FALSE  TRUE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE

I would like to do it without loops, since they are too slow in R

1 Answers

** FURTHER EDIT in view of OP's request to state logic/strategy**

Actually, your conditions are combination of three conditions. If we create four zones, as I created in the plot above and name the zones as 1 to 4 with

  • 1 where q values are >= z
  • 2 where q values are >= i
  • 3 where q values are >= s
  • 4 where q values are < s

Now, the conditions can be translated as

  • TRUE when in zone 2 and 3
  • But if exited once from these TRUE zones, it will become TRUE only if it arrives in zone 3
  • Moreover, if it has hit zone 4, it can become TRUE only if it arrives/hits zone 1, at least once.

Strategy

  • To integrate these all, I used tidyverse piped syntax
    1. First divide all values in respective zones (say q1)
    1. As a first condition divide zones 2 & 3 in TRUE and others in `FALSE
    1. As second condition, say c2 , i.e. whether exited from zone 4 and has hit zone 1 or not, mark zone 4 as F and zone 1 as T rest all as NAs.
    • First value can be NA so replace first value, if NA, with c1
    1. As last condition say c3 i.e. TRUE when arrive in zone 3, mark 3 as TRUE 1 and 4 as FALSE and leave zone 2 as NA to later-on check whether it arrived here from which zone.
    • First value can be NA so replace first value, if NA, with FALSE
    • Now only job remains to fill NAs in c2 and c3. Use zoo::na.locf or tidyr::fill which fills all NAs will last available value.
    • Your final desired result is combination of all conditions so c1 & c2 & c3
q = c(0.00000000, -0.70218526, -0.60635393,  0.32325554,  -0.45921704,  -0.57336113,  -0.77683717,
      -1.76347868,  -1.90884891,  -0.86157465,  -0.72896622,  -0.86831735,  -0.79357262,  -0.65279976,
      0.39921356,  0.78018094,  0.75703279,  0.70898895,  1.10155383,  0.88428135,  0.81338108,
      0.65611568,  0.89776945, 0.65447442,  0.16289673,  0.19464041,  0.01762445, -0.57663945,
      -1.01231868, -0.81204022, -0.99165533, -0.62666993, -1.05661282, -0.78221866, -0.03129549,  1.04051915)

s = -1.59688
i = -0.6373684
z = 0
library(tidyverse)
q %>% as.data.frame() %>% setNames('q') %>%
  mutate(q1 = case_when(q >= z ~ 1,
                        q >= i ~ 2,
                        q >= s ~ 3,
                        TRUE ~ 4),
         c1 = q1 %in% c(2,3),
         c2 = case_when(q1 == 4 ~ F,
                        q1 == 1 ~ T,
                        TRUE ~ NA),
         c2 = ifelse(row_number() == 1 & is.na(c2), c1, c2),
         c3 = case_when(q1 %in% c(1,4) ~ F,
                        q1 == 3 ~ T,
                        TRUE ~ NA),
         c3 = ifelse(row_number() ==1 & is.na(c3), F, c3)) %>%
  fill(c2, c3) %>%
  transmute(output = c1 & c2 & c3) %>% pull(output)
#>  [1] FALSE  TRUE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
#> [13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [25] FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE

Created on 2021-06-02 by the reprex package (v2.0.0)


OLD ANSWER

#data given
q = c(0.00000000, -0.70218526, -0.60635393,  0.32325554,  -0.45921704,  -0.57336113,  -0.77683717,
      -1.76347868,  -1.90884891,  -0.86157465,  -0.72896622,  -0.86831735,  -0.79357262,  -0.65279976,
      0.39921356,  0.78018094,  0.75703279,  0.70898895,  1.10155383,  0.88428135,  0.81338108,
      0.65611568,  0.89776945, 0.65447442,  0.16289673,  0.19464041,  0.01762445, -0.57663945,
      -1.01231868, -0.81204022, -0.99165533, -0.62666993, -1.05661282, -0.78221866, -0.03129549,  1.04051915)

s = -1.59688
i = -0.6373684
z = 0

#loading libraries
library(dplyr)
library(tidyr)

#creating zones
q1 <- dplyr::case_when(q >= z ~ 1,
                q >= i ~ 2,
                q >= s ~ 3,
                TRUE ~ 4)

#first condition
c1 <- dplyr::case_when(q1 %in% c(2,3) ~ T,
                TRUE ~ F)

#second condition (third in above statements)
c2 <- dplyr::case_when(q1 == 4 ~ F,
                q1 == 1 ~ T,
                TRUE ~ NA)

c2[1] <- ifelse(is.na(c2[1]), c1[1], c2[1])

c2 <- tidyr::fill(data.frame(id = 1:length(q), c2 = c2), c2)$c2

#third condition
c3 <- dplyr::case_when(q1 == 3 ~ T,
          q1 %in% c(1,4) ~ F,
          TRUE ~ NA)

c3[1] <- ifelse(is.na(c3[1]), F, c3[1])

c3 <- tidyr::fill(data.frame(id = 1:length(q), c3 = c3), c3)$c3

#creating output

output <- (c1 & c2 & c3)
> output
 [1] FALSE  TRUE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[21] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE

#check it with your given `out`

> which((c1 & c2 & c3) == out)
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

#OR

> which((c1 & c2 & c3) != out)
integer(0)

UPDATE If you want to use baseR only, use these expressions/codes for c2 and c3


#second condition
c2 <- case_when(q1 == 4 ~ F,
                q1 == 1 ~ T,
                TRUE ~ NA)

c2 <- c2[!cumsum(!is.na(c2)) | !is.na(c2)][cumsum(!cumsum(!is.na(c2)) | !is.na(c2))]

#third condition
c3 <- case_when(q1 == 3 ~ T,
          q1 %in% c(1,4) ~ F,
          TRUE ~ NA)

c3 <- c3[!cumsum(!is.na(c3)) | !is.na(c3)][cumsum(!cumsum(!is.na(c3)) | !is.na(c3))]

enter image description here

for new data

q <- c(-0.01563733, -0.05829460, -0.05884189, -0.08954093, -0.13268677, -0.31748724, -0.40060792, -0.08515156, -0.14303489, -0.24525535, -0.93842637, -0.77738228, -1.29502715, -0.89000932, -1.49038656, -1.64953167, -1.67114179, -1.47482366, -0.85874778, -1.01021450, -0.90078260, -1.24313333, -0.99053914, -1.11684140, -1.34073045, -1.36406163, -1.25163185, -1.42429376, -1.48127185, -1.79040671, -2.26811789, -1.82124304, -1.85208201, -1.76394637, -1.63173292) 
i = -0.489
s = -1.032
z = 0

#after running the above code
> output
 [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE
[17] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[33] FALSE FALSE FALSE

and chart

enter image description here

for a new vector with random values

set.seed(202)
q <- runif(35, -2, 2)

enter image description here

Related