SkyWalker
SkyWalker

Reputation: 14317

How to flatten results of function call as part of dplyr::mutate?

I have the following simple use-case where I define two samples containing name and location:

if(!require(tidyverse)) install.packages("tidyverse", repos = "http://cran.us.r-project.org")
if(!require(ggmap)) devtools::install_github("dkahle/ggmap")

# you may also need to 
#register_google(key="<register your own key>")

x <- tibble(name=c('a', 'b'), loc=c('china', 'switzerland'))
x
# A tibble: 2 x 2
   name  loc        
   <chr> <chr>      
1 a     china      
2 b     switzerland

Now I'd like to enrich my tibble with longitude and latitude information. I do that by running:

x %>% 
  mutate(lon=geocode(loc)$lon, lat=geocode(loc)$lat)

but this is expensive because I'd need to call the geocode function two times per sample and that [s] ain't free. Is there a way to flatten the return of the geocode function into the tibble? This is a failed attempt and a demonstration of what I'm trying to achieve:

x %>% 
  mutate(xx=geocode(loc), lon=xx$lon, lat=xx$lat)
>Error: Column `xx` is of unsupported class data.frame

Upvotes: 1

Views: 214

Answers (2)

camille
camille

Reputation: 16862

For the specific case of adding geocoded coordinates, ggmap actually has a function mutate_geocode that does exactly this:

library(dplyr)
library(ggmap)

mutate_geocode(x, location = loc)
#> # A tibble: 2 x 4
#>   name  loc            lon   lat
#>   <chr> <chr>        <dbl> <dbl>
#> 1 a     china       104.    35.9
#> 2 b     switzerland   8.23  46.8

For more general uses, purrr::map_* functions work well. You can map over location names, apply geocode, and unnest that list:

mutate(x, coords = purrr::map(loc, geocode)) %>%
  tidyr::unnest(coords)
# same output as above

You could also extract each column you need with purrr::map_dbl. This might be helpful if you got a data frame back with more than just lon and lat columns, such as if you'd set a different value of output in geocode:

mutate(x, coords = purrr::map(loc, geocode),
       lon = purrr::map_dbl(coords, "lon"),
       lat = purrr::map_dbl(coords, "lat"))
# same output as above

Or by column position:

mutate(x, coords = purrr::map(loc, geocode),
       lon = purrr::map_dbl(coords, 1),
       lat = purrr::map_dbl(coords, 2))
# same output

Upvotes: 1

akrun
akrun

Reputation: 887711

The output of geocode can be placed in a list, and then extract the components

library(dplyr)
library(purrr)
library(ggmap)
library(tidyr)
x %>%
    mutate(xx = map(loc, geocode), 
           out = map(xx, ~ tibble(lon = .x$lon, lat = .x$lat))) %>%
    unnest_wider(c(out))

Upvotes: 1

Related Questions