Citizen
Citizen

Reputation: 121

Loop within string in r to output expressions with vectorized values

Example data:

> DF
   A  B  C
1 11 22 88
2 11 22 47
3  2 30 21
4  3 30 21

> r
[1] "A==A[i] & B==B[i] "           "A==A[i] & C==C[i] "          
[3] "B==B[i] & C==C[i] "           "A==A[i] & B==B[i] & C==C[i] "

Executed code:

> output=list()
> for (j in r){
+   for (i in 1:nrow(DF)){
+    
+     output[[j]][i]=j  
+   }
+ }
> output
$`A==A[i] & B==B[i] `
[1] "A==A[i] & B==B[i] " "A==A[i] & B==B[i] " "A==A[i] & B==B[i] "
[4] "A==A[i] & B==B[i] "

$`A==A[i] & C==C[i] `
[1] "A==A[i] & C==C[i] " "A==A[i] & C==C[i] " "A==A[i] & C==C[i] "
[4] "A==A[i] & C==C[i] "

$`B==B[i] & C==C[i] `
[1] "B==B[i] & C==C[i] " "B==B[i] & C==C[i] " "B==B[i] & C==C[i] "
[4] "B==B[i] & C==C[i] "

$`A==A[i] & B==B[i] & C==C[i] `
[1] "A==A[i] & B==B[i] & C==C[i] " "A==A[i] & B==B[i] & C==C[i] "
[3] "A==A[i] & B==B[i] & C==C[i] " "A==A[i] & B==B[i] & C==C[i] "

> output=purrr::flatten_chr(output)
> output
 [1] "A==A[i] & B==B[i] "           "A==A[i] & B==B[i] "          
 [3] "A==A[i] & B==B[i] "           "A==A[i] & B==B[i] "          
 [5] "A==A[i] & C==C[i] "           "A==A[i] & C==C[i] "          
 [7] "A==A[i] & C==C[i] "           "A==A[i] & C==C[i] "          
 [9] "B==B[i] & C==C[i] "           "B==B[i] & C==C[i] "          
[11] "B==B[i] & C==C[i] "           "B==B[i] & C==C[i] "          
[13] "A==A[i] & B==B[i] & C==C[i] " "A==A[i] & B==B[i] & C==C[i] "
[15] "A==A[i] & B==B[i] & C==C[i] " "A==A[i] & B==B[i] & C==C[i] "

My target was to get the same output with the specific values A[i], B[i] and C[i] from DF, i.e., the final output would be the following:

> output
[1] "A==11 & B==22 "            "A==11 & B==22 "          
[3] "A==2 & B==30 "             "A==3 & B==30 "          
[5] "A==11 & C==88 "            "A==11 & C==47 "          
[7] "A==2 & C==21 "             "A==3 & C==21 "          
[9] "B==22 & C==88 "            "B==22 & C==47 "          
[11] "B==30 & C==21 "           "B==30 & C==21 "          
[13] "A==11 & B==22 & C==88 "   "A==11 & B==22 & C==47 "
[15] "A==2 & B==30 & C==21 "    "A==3 & B==30 & C==21 "

I would be grateful if someone could help me with this matter.

Upvotes: 2

Views: 101

Answers (2)

Romain Francois
Romain Francois

Reputation: 17642

The difficulty here comes from the way the problem is specified with the r vector, and it would be possible to modify Milan's answer to better match the expected result.

r <- c(
  "A==A[i] & B==B[i]", 
  "A==A[i] & C==C[i]",
  "B==B[i] & C==C[i]", 
  "A==A[i] & B==B[i] & C==C[i]"
)

Instead of that, I'll take another route, given that in each element of r, you only need to identify the names of the columns. For example, on the first case, you only need A and B.

library(purrr)
library(glue)
library(rlang)
names <- c("A", "B")
DF <- data.frame(
  A = c(11, 11, 2, 3),
  B = c(22, 22, 30, 30),
  C = c(88, 47, 21, 21)
)

map( names, ~{
  glue( "{name} == {values}", name = ., values = DF[[.]] )
})
#> [[1]]
#> A == 11
#> A == 11
#> A == 2
#> A == 3
#> 
#> [[2]]
#> B == 22
#> B == 22
#> B == 30
#> B == 30

you can then reduce it to only get one string per line of your data frame:

map( names, ~{
  glue( "{name} == {values}", name = ., values = DF[[.]] )
}) %>% 
  reduce( paste, sep = " & ")
#> [1] "A == 11 & B == 22" "A == 11 & B == 22" "A == 2 & B == 30" 
#> [4] "A == 3 & B == 30"

Wrapping it all up into a function that uses tidy eval to get the names to begin with:

tests <- function(data, ...){
  names <- map_chr(quos(...), f_name)

  map( names, ~{
    glue( "{name} == {values}", name = ., values = data[[.]] )
  }) %>% 
    reduce(paste, sep = " & " )

}

So that you can get the results by calling tests 4 times:

c(
  tests( DF, A, B), 
  tests( DF, A, C), 
  tests( DF, B, C), 
  tests( DF, A, B, C)
)
#>  [1] "A == 11 & B == 22"           "A == 11 & B == 22"          
#>  [3] "A == 2 & B == 30"            "A == 3 & B == 30"           
#>  [5] "A == 11 & C == 88"           "A == 11 & C == 47"          
#>  [7] "A == 2 & C == 21"            "A == 3 & C == 21"           
#>  [9] "B == 22 & C == 88"           "B == 22 & C == 47"          
#> [11] "B == 30 & C == 21"           "B == 30 & C == 21"          
#> [13] "A == 11 & B == 22 & C == 88" "A == 11 & B == 22 & C == 47"
#> [15] "A == 2 & B == 30 & C == 21"  "A == 3 & B == 30 & C == 21"

Upvotes: 2

Milan Val&#225;šek
Milan Val&#225;šek

Reputation: 611

Is this output what you are after?

DF <- data.frame(A = c(11, 11, 2, 3),
                 B = c(22, 22, 30, 30),
                 C = c(88, 47, 21, 21))

r <- c("A==A[i] & B==B[i]", "A==A[i] & C==C[i]",
       "B==B[i] & C==C[i]", "A==A[i] & B==B[i] & C==C[i]")

output=list()
for (j in r){
  for (i in 1:nrow(DF))
    output[[j]][[i]] <- DF[with(DF, eval(parse(text = j))), ]
}

output

$`A==A[i] & B==B[i]`
$`A==A[i] & B==B[i]`[[1]]
   A  B  C
1 11 22 88
2 11 22 47

$`A==A[i] & B==B[i]`[[2]]
   A  B  C
1 11 22 88
2 11 22 47

$`A==A[i] & B==B[i]`[[3]]
  A  B  C
3 2 30 21

$`A==A[i] & B==B[i]`[[4]]
  A  B  C
4 3 30 21


$`A==A[i] & C==C[i]`
$`A==A[i] & C==C[i]`[[1]]
   A  B  C
1 11 22 88
...

Upvotes: 0

Related Questions