non-numeric argument to binary operator with ifelse and apply

Viewed 200

I hope nobody is getting (too) tired of me. I have given a completely fictional example of the problem below. The error is only given after including a mixture of numeric and string column in a data frame, even if the numeric columns are excluded from the function (not shown below). The function looks in column c for contents of d and vice-versa using grepl and then the should make a new column of the 'yes' and 'no' statements from ifelse. I need to ifelse to return the 'yes' and 'no' after the test but this is the part that is giving the error.

  a <- c(5:10)
  b <- c(105:110)
  c <- c("a","b","c","d","e","f")
  d <- c("aa","bc","cd","ff","ee", "gf")
  df <- data.frame(a,b,c,d)

  newfunction <- function(x, col1, col2, col3, col4){ifelse(((sapply(lapply(x[[col4]], grepl, 
  x[[col3]]),any)) | (sapply(lapply(x[[col3]], grepl, x[[col4]]),any))), (11 - x[[col1]]), (1 - 
  x[[col2]]))}
  df$new <- apply(df, 1, newfunction, "a", "b", "c", "d")
  
  Error in 11 - x[[col1]] : non-numeric argument to binary operator
2 Answers

Into the question. The best way to approach the question is using the function case_when from the package dplyr. I used the function str_detect from stringr.

> library(tidyverse)
> library(stringr)
> 
> a  <- c(5:10)
> b  <- c(105:110)
> cc <- c("a","b","c","d","e","f")
> d  <- c("aa","bc","cd","ff","ee", "gf")
> df <- data.frame(a, b, cc, d)
> 
> 
> 
> df %>% mutate(case_when((str_detect(cc, d) | str_detect(d, cc)) ~ 'Yes', 
+                         TRUE ~ 'No'))
   a   b cc  d case_when(...)
1  5 105  a aa            Yes
2  6 106  b bc            Yes
3  7 107  c cd            Yes
4  8 108  d ff             No
5  9 109  e ee            Yes
6 10 110  f gf            Yes

The error is caused since you use apply which forces the matrix to a character matrix (transforming the numeric values to character). Example,

> apply(df, 2, function(x) x)
     a    b     cc  d   
[1,] " 5" "105" "a" "aa"
[2,] " 6" "106" "b" "bc"
[3,] " 7" "107" "c" "cd"
[4,] " 8" "108" "d" "ff"
[5,] " 9" "109" "e" "ee"
[6,] "10" "110" "f" "gf"

Some pointers as it seems that you are new to R. First of all, do not use c as a name it is very commonly used function to combine elements to a vector. Second, the way the function is written is very hard to read. You should break it up into multiple step to make it easier.

You can also vectorize grepl to simplify your code:

Vgrepl <- Vectorize(grepl)
TF <- Vgrepl(pattern=df$c, x=df$d) | Vgrepl(pattern=df$d, x=df$c)
df$comp <- ifelse(TF, "Yes", "No")
df
#    a   b c  d comp
# 1  5 105 a aa  Yes
# 2  6 106 b bc  Yes
# 3  7 107 c cd  Yes
# 4  8 108 d ff   No
# 5  9 109 e ee  Yes
# 6 10 110 f gf  Yes
Related