How do I create new variables based on a comparison across two character variables without using a nested for loop function?

Viewed 33

Avoiding nested for loops in R:How do I create new variables based on a comparison across two character variables without using a nested for loop function?

For example, I have two character variables A and B:

A=c("yellow","orange","dog","dog","green","heaven","tree") #searchspace
B=c("dog","house","tree","yellow","dog","road","hell","rain","dog","tree") # target labels

For each element in the searchspace (A) I want to:

  1. count how many times the element is present in list (B) and,
  2. if present, at which position it was first found in list B and,
  3. if present in multiple positions in B, which position was it last found in list B.
  4. save the output (1),(2) and (3) in a data.table including list A.

The output would look like something like this:

       A totcount firstindex lastindex
1: yellow        1          4         0
2: orange        0          0         0
3:    dog        3          1         9
4:    dog        3          1         9
5:  green        0          0         0
6: heaven        0          0         0
7:   tree        2          3        10

I have written a nested for loop function to run this, but this is really slow with list A and B having over 16k and 2K elements each, respectively. I have been struggling to use apply functions to solve this.

Any suggestions would be much appreciated. Thank you

2 Answers

You can just put A into a data frame and use dplyr to calculate without any use of apply.

library(dplyr)

df <- as.data.frame(A)

df %>%
  group_by(A) %>%
  mutate(totcount = sum(B %in% A),
         firstindex = ifelse(totcount > 0, min(which(B %in% A)), 0),
         lastindex = ifelse(totcount > 1, max(which(B %in% A)), 0))

#> # A tibble: 7 x 4
#> # Groups:   A [6]
#>   A      totcount firstindex lastindex
#>   <fct>     <int>      <dbl>     <dbl>
#> 1 yellow        1          4         0
#> 2 orange        0          0         0
#> 3 dog           3          1         9
#> 4 dog           3          1         9
#> 5 green         0          0         0
#> 6 heaven        0          0         0
#> 7 tree          2          3        10

Base R solution:

A <- c("yellow", "orange", "dog", "dog", "green", "heaven", "tree")
B <- c("dog", "house", "tree", "yellow", "dog", "road", "hell", "rain",
       "dog", "tree")

X <- sapply(A, function(a) {
    totcount <- sum(B %in% a)
    firstindex <- ifelse(totcount > 0, min(which(B %in% a)), 0)
    lastindex <- ifelse(totcount > 1, max(which(B %in% a)), 0)
    c(totcount=totcount, firstindex=firstindex, lastindex=lastindex)
})

> t(X)
       totcount firstindex lastindex
yellow        1          4         0
orange        0          0         0
dog           3          1         9
dog           3          1         9
green         0          0         0
heaven        0          0         0
tree          2          3        10
Related