Bastien
Bastien

Reputation: 3176

dplyr mutate not applying to individual element of field

I'm trying to do a calculation using an home made function inside a mutate call and it's not doing what I'm expecting it to do.

Here is a reproducible example:

library(dplyr)
#the data
dd <- structure(list(fire_zone = c("ET", "EJB", "WJB"), base_med = c(1, 1, 2)), class = "data.frame", row.names = c(NA, -3L))

# my home made function
med2lambda <- function(med) polyroot(c(-0.02, 1/3 - med, 1)) %>% {suppressWarnings(as.numeric(.))} %>% max

So, what my function does is to estimate the lambda associated to the median from a Poisson distribution by calculating the root of a quadratic function. Despite the long explanation, it's actually pretty basic:

med2lambda(1)
[1] 0.695426
med2lambda(2)
[1] 1.678581

Now, I want to use it in a mutate call to add a field giving the lambda associated to each median in the table:

dd %>% mutate(lambda = med2lambda(base_med), log = log(base_med))
fire_zone base_med   lambda       log
1        ET        1 2.128966 0.0000000
2       EJB        1 2.128966 0.0000000
3       WJB        2 2.128966 0.6931472

The result is wrong, mutate actually gives me the results of:

med2lambda(dd$base_med)
[1] 2.128966

I've added the log call in the mutate to give an idea of what it should do. log works great in the mutate as it is called element by element.

Any insight about this behavior would be appreciated.

Upvotes: 0

Views: 85

Answers (2)

Maurits Evers
Maurits Evers

Reputation: 50738

You need to allow med2lambda to take vectors instead of scalars.

One possibility is to use Vectorize:

med2lambda.vectorised <- Vectorize(med2lambda)
dd %>% mutate(lambda = med2lambda.vectorised(base_med), log = log(base_med))
#  fire_zone base_med   lambda       log
#1        ET        1 0.695426 0.0000000
#2       EJB        1 0.695426 0.0000000
#3       WJB        2 1.678581 0.6931472

Alternatively, you could rewrite med2lambda to take a vector as argument:

med2lambda2 <- function(med) sapply(med, function(x)
    polyroot(c(-0.02, 1/3 - x, 1)) %>% {suppressWarnings(as.numeric(.))} %>% max)
dd %>% mutate(lambda = med2lambda2(base_med), log = log(base_med))
#  fire_zone base_med   lambda       log
#1        ET        1 0.695426 0.0000000
#2       EJB        1 0.695426 0.0000000
#3       WJB        2 1.678581 0.6931472

Upvotes: 2

akrun
akrun

Reputation: 887901

We can use map

library(tidyverse)
dd %>% 
    mutate(lambda = map_dbl(base_med, med2lambda), log = log(base_med))

Upvotes: 0

Related Questions