2

I'm having some trouble with rowwise operations on sf objects, where the geometry is polygon type.

sf functions inside by_row don't seem to work e.g. the following should create a list-column holding bounding box objects:

 purrr::by_row(sf_polygons_object, function(x) {
   list(st_bbox(x))
 }, .to = 'bb')

Error in UseMethod("st_bbox") : no applicable method for 'st_bbox' applied to an object of class "c('tbl_df', 'data.frame')"

(not the most useful example on its own, but demonstrates the problem).

I've tried a few alternative approaches e.g. rowwise() %>% mutate() , mutate( x = apply(., 1, function(x) ...)), and none work as they don't supply st_bbox() with the sf object it requires. Is this a bug, or am I approaching the problem poorly?

edit: reproducible example

library(sp)
library(rgdal)
library(rgeos)
library(sf)
library(tidyverse)
library(rnaturalearth)

nec <- st_as_sf(ne_countries()[1:5,]) %>%
  purrr::by_row(., function(x) st_bbox(x), .to = 'bb')
Pierre L
  • 28,203
  • 6
  • 47
  • 69
obrl_soil
  • 1,104
  • 12
  • 24
  • 1
    Please add a reproducible example. – Pierre L Apr 29 '17 at 10:01
  • My guess: `by_row` uses `as.data.frame` to convert your object to a dataframe. And there is no `st_bbox` method for dataframes. – Martin Schmelzer Apr 29 '17 at 10:08
  • It'd be nice to give people the warning that you are asking them to download very bloated packages to help. – Pierre L Apr 29 '17 at 10:22
  • I know you're frustrated with the problem but chill out. Your expression isn't working because `by_row` is changing the object when it breaks it apart by row. Use `split` and `map` to break the clean way. – Pierre L Apr 29 '17 at 11:19

3 Answers3

10

No hacks needed. Just split and map the function:

ne_countries(returnclass = "sf")[1:5,] %>% 
  mutate(bb = split(., 1:5) %>% purrr::map(st_bbox))
Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
Pierre L
  • 28,203
  • 6
  • 47
  • 69
1

I had to fiddle a bit. Do you have to work with an sf object? In that case, this would give you a list of the bounding boxes for each polygon:

library(sp)
library(rgdal)
library(sf)
library(tidyverse)
library(rnaturalearth)
library(rgeos)
library(purrr)

nec <- as.list(st_as_sf(ne_countries()[1:5,])$geometry) %>% lapply(., st_bbox)
nec

Resulting in:

[[1]]
    xmin     ymin     xmax     ymax 
60.52843 29.31857 75.15803 38.48628 

[[2]]
      xmin       ymin       xmax       ymax 
 11.640096 -17.930636  24.079905  -4.438023 

[[3]]
    xmin     ymin     xmax     ymax 
19.30449 39.62500 21.02004 42.68825 

[[4]]
    xmin     ymin     xmax     ymax 
51.57952 22.49695 56.39685 26.05546 

[[5]]
     xmin      ymin      xmax      ymax 
-73.41544 -55.25000 -53.62835 -21.83231 
Martin Schmelzer
  • 23,283
  • 6
  • 73
  • 98
1

I find it much easier not to use pipes. Here is Pierre's answer rewritten without pipes:

nec       <- st_as_sf(ne_countries()[1:5,])
nec_split <- split(nec, 1:5)
nec_map   <- map(nec_split, st_bbox)
nec       <- mutate(nec, bb = nec_map)
jsta
  • 3,216
  • 25
  • 35