Search code examples
rif-statementdplyrrowlag

Is there a way to do a conditional and multiple row by row operation along a sorted and grouped tibble?


I have a grouped tibble where several parameters have to be calculated from others assuming a function that gets its values from a previous row. I have tried to find answers that involve lag, mutate, case_when, and aggregate but had no luck implementing these in the following toy dataset:

library(tidyverse)

set.seed(42)

df <- tibble(
  gr = c(1,1,1,2,2,2),
  t = rep((seq(1:3)),2),
  v1 = c(1,NA,NA,1.6,NA,NA),
  v2 = rnorm(6),
  v3 = c(-0.2,0.3,-0.6,-0.2,1,0.2)
  ) 

# These operations 
(df <- df %>% group_by(gr) %>%  arrange(t, .by_group = TRUE) %>% 
  mutate(R1=abs(v1-5*v2)) %>% 
  mutate(R2=abs(R1*v2)^(1/2)) %>% mutate(RI3=R1/R2)) 

# A tibble: 6 x 8
# Groups:   gr [2]
     gr     t    v1     v2    v3    R1    R2   RI3
  <dbl> <int> <dbl>  <dbl> <dbl> <dbl> <dbl> <dbl>
1     1     1   1   -1.39   -0.2  7.94  3.32  2.39
2     1     2  NA   -0.279   0.3 NA    NA    NA   
3     1     3  NA   -0.133  -0.6 NA    NA    NA   
4     2     1   1.6  0.636  -0.2  1.58  1.00  1.58
5     2     2  NA   -0.284   1   NA    NA    NA   
6     2     3  NA   -2.66    0.2 NA    NA    NA   

Now, what I would need to do is to use df$RI3[i-1] as input for df$v1[i]

if ia.na(df$v1[i]) is TRUE and subsequently calculate:

mutate(R1=abs(v1-5*v2))  %>%  mutate(R2=(R1^(1/2))) %>% mutate(RI3=R1/R2)  

row-by-row in order to fill the gaps within the sorted and grouped dataset;

doing this one by one would look like this:

Rdf <-   df
Rdf$v1[2] <- df$RI3[1]
Rdf$v1[5] <- df$RI3[4]
Rdf <- Rdf %>%  mutate(R1=abs(v1-5*v2)) %>% 
  mutate(R2=abs(R1*v2)^(1/2)) %>% mutate(RI3=R1/R2) 
Rdf
Rdf$v1[3] <- Rdf$RI3[2]
Rdf$v1[6] <- Rdf$RI3[5]
Rdf <- Rdf %>%  mutate(R1=abs(v1-5*v2)) %>% 
  mutate(R2=abs(R1*v2)^(1/2)) %>% mutate(RI3=R1/R2) 
Rdf

and would result in:

# A tibble: 6 x 8
# Groups:   gr [2]
     gr     t    v1     v2    v3    R1    R2   RI3
  <dbl> <int> <dbl>  <dbl> <dbl> <dbl> <dbl> <dbl>
1     1     1  1    -1.39   -0.2  7.94 3.32   2.39
2     1     2  2.39 -0.279   0.3  3.79 1.03   3.68
3     1     3  3.68 -0.133  -0.6  4.35 0.762  5.71
4     2     1  1.6   0.636  -0.2  1.58 1.00   1.58
5     2     2  1.58 -0.284   1    3.00 0.923  3.25
6     2     3  3.25 -2.66    0.2 16.5  6.63   2.49

I guess a for-loop within an if-condition applied to a nested df would work.

Any advise implementing this would be great!


Solution

  • I implemented a for loop. But I am not sure I start off with the same df given the seed. Hope it does what you need.

    When I need to write for-loops that seem complicated, I use browser() to build it.

    library(tibble)
    library(dplyr)
    
    set.seed(42)
    df <- tibble(
      gr = c(1,1,1,2,2,2),
      t = rep((seq(1:3)),2),
      v1 = c(1,NA,NA,1.6,NA,NA),
      v2 = rnorm(6),
      v3 = c(-0.2,0.3,-0.6,-0.2,1,0.2)
    ) 
    
    # Data prep
    df <- df %>%
      group_by(gr) %>%  
      arrange(t, .by_group = TRUE) %>% 
      mutate(R1=abs(v1-5*v2)) %>% 
      mutate(R2=abs(R1*v2)^(1/2)) %>% #
      mutate(RI3=R1/R2) %>%
      ungroup()
    
    #going through df row by row
    for (i in 1:nrow(df)) {
        #browser()
        # run into problems with i == 1 for the lagged operation, hence made two cases
        if (i == 1) {
          df$v1[i] <- if_else(is.na(df$v1[i]), df$RI3[i], df$v1[i])
        } else {
          df$v1[i] <- if_else(is.na(df$v1[i]), df$RI3[i-1], df$v1[i])
        }
      
      # rowwise calculation
      df$R1[i] <- abs(df$v1[i]-5*df$v2[i])
      df$R2[i] <- abs(df$R1[i]*df$v2[i])^(1/2)
      df$RI3[i]=df$R1[i]/df$R2[i]
      
    }