Hariharan S
Hariharan S

Reputation: 165

The apply function returns "Error: $ operator is invalid for atomic vectors"

I have a data frame called 'bank' like below, it has around 40K rows

  age  job      salary marital education targeted default balance housing loan contact day month
  58 management 100000 married  tertiary      yes      no    2143     yes   no unknown   5   may

  duration campaign pdays previous poutcome response
       261        1    -1        0  unknown       no

I created the following function that checks some conditions and then returns a value that gets added as a new value under a new column called 'new'.

decide <- function(x){
  if(x$marital=='married' & x$salary > 60000 & x$housing=='yes'){
    return('yes') 
  }else if(x$marital=='single' & x$salary > 40000 & x$education=='tertiary'){
    return('yes') 
  }else{
    return('no') 
  }
}

Then I would use the below loop to run over all the rows and derive the new value

for(i in 1:nrow(bank)){
  person<-bank[i,]
  bank[i,'new']<-decide(person)
}

This satisfies my requirement.

However when I try to use an apply function instead of a for loop like below,

bank$new1<-sapply(bank,decide)

it returns the below error

Error: $ operator is invalid for atomic vectors

What is the issue with the apply function I wrote, can I ask you to write the correct apply function that works like the for loop ?

Upvotes: 2

Views: 3278

Answers (2)

MKR
MKR

Reputation: 20095

I think, you can use dplyr::case_when in such situations where there are multiple conditions will decide value of new column. The logic will look simpler and neat as:

library(dplyr)

bank %>% mutate(new = case_when(
    marital=='married' & salary > 60000 & housing=='yes' ~ 'yes', 
    marital=='single' & salary > 40000 & education=='tertiary' ~ 'yes',
    TRUE                      ~ 'no'
       ))

Upvotes: 2

coffeinjunky
coffeinjunky

Reputation: 11514

Try something like this:

decide <- function(x){
  if(x["cyl"]==6 & x["disp"] > 150){
    return('yes') 
  }else if(x["cyl"] == 8 & x["disp"] > 200){
    return('yes') 
  }else{
    return('no') 
  }
}

apply(mtcars, 1, decide)

Slightly more elegant and efficient:

with(mtcars, ifelse(cyl == 6 & disp > 150, "yes", ifelse(cyl==8 & disp > 200, "no", "no")))

Here, ifelse works on vectors, which means you do not have to loop through the entire dataframe.

To understand your error, note that the apply-function would pass a vector to the function, and vectors are not accessed with $. See:

vec <- c("a"=1, "b"=2)
vec
# a b 
# 1 2 
vec$a
# Error in vec$a : $ operator is invalid for atomic vectors
vec["a"]
# a 
# 1

Upvotes: 2

Related Questions