I have a shapefile with 6 polygons, each one with a specific id.

I want to create random points within these polygons, but with different sample size (each id has a specific value).

The file with the polygons has the following structure:

> HR95_all
Object of class "SpatialPolygonsDataFrame" (package sp):

Number of SpatialPolygons:  6

Variables measured:
   id      area
0  40 257879080
01 41  53136845
02 42  64198336
03 43 186475935
04 44  27405855
05 45  53485801

And the data.frame with the number of points per id is a simple data.frame:

> AV_locs
   id  num
40 40 1010
41 41 1222
42 42  493
43 43 1111
44 44  369
45 45  598

I would like to create a loop that could do this automatically, and I've tried this:

for (i in 1:length(unique(HR95_all$id))) {
             id <- unique(HR95_all$id)[i]
             Rnd <- spsample(HR95_all[[id]], AV_locs[which(AV_locs$id==id), "num"] , "random")
             writeOGR(Rnd,dsn=getwd(), layer=paste0("Rnd_", id), driver = "ESRI Shapefile",overwrite_layer=TRUE)

But I obtain the following error:

Error in (function (classes, fdef, mtable)  : 
  unable to find an inherited method for function ‘spsample’ for signature ‘"factor"’

I've tried other options, like including n = AV_locs[which(AV_locs$id==id), "num"] (following the help page), but the error persists.

Any idea on how to overcome this?

----- UPDATE -----

I've tried a different loop, by subsetting the HR95_all before applying the spsample():

for (i in (unique(HR95_all$id))) {
        HR <- HR95_all[HR95_all$id==i,]
        Rnd_locs <- spsample(HR, n = AV_locs[which(AV_locs$id==i), "num"], "random")
        writeOGR(Rnd_locs,dsn=getwd(), layer=paste0("Random_", i), driver = "ESRI Shapefile",overwrite_layer=TRUE)

The previous error is gone but a new one showed up:

Error in writeOGR(Rnd_locs, dsn = getwd(), layer = paste0("Random_", i),  : 
  obj must be a SpatialPointsDataFrame, SpatialLinesDataFrame or

I've tried to add a as(Rnd_locs, "SpatialPointsDataFrame") before, but it still doesn't work; I recieve this error:

Error in writeOGR(Rnd_locs, dsn = getwd(), layer = paste0("Random_", i),  : 
  NA NULL unknown data type

Any thoughts?


2 Answers 2


You can easily do this with a for loop.

Lets first create some data.


sr=SpatialPolygons(list(Polygons(list(Polygon(cbind(c(180114, 180553, 181127, 181477, 181294, 
  181007, 180409, 180162, 180114), c(332349, 332057, 332342, 333250, 333558, 
  333676, 332618, 332413, 332349)))),'1'),
  Polygons(list(Polygon(cbind(c(180042, 180545, 180553, 180314, 179955, 
  179142, 179437, 179524, 179979, 180042), c(332373, 332026, 331426, 330889, 
  330683, 331133, 331623, 332152, 332357, 332373)))),'2')))
x=SpatialPolygonsDataFrame(sr, data.frame(row.names=c('1','2'), PIDS=1:2))

Here we create a column "size" that contains the per-polygon sample sizes

x$size <- c(10,20)

To create the random samples for each polygon we first create a list to store the results. Then we subset each polygon, create the sample, put it into the list and then, when the loop is finished, rbind the results together. Within the loop, for each set of random sample points, I am assigning an attribute "PIDS" contained in the polygons attributes.

samples <- list()
  for (i in 1:nrow(x)) {
    psub <- x[i,]
      ns <- x[i,]$size
    psamp <- try( sp::spsample(psub, n = ns, type = "random", 
                               iter = 10) )
    samples[[i]] <- sp::SpatialPointsDataFrame(psamp, 
      data.frame(PIDS = rep(psub$PIDS,length(psamp))))
samples <- do.call("rbind", samples)
  proj4string(samples) <- proj4string(x)

Here we can plot the results and check our sample sizes.

  points(samples, pch=20) 

dim(samples[samples$PIDS == "1",])
dim(samples[samples$PIDS == "2",])
  • This does work, but how exactly do I apply it in my situation? I've tried to use your approach but I still get an error similar to the last one I have in my question... My data is organized in different way then the one you showed; I've tried to include the sample size in the polygons (using inner_join()) but it did not work...
    – mto23
    May 4, 2020 at 17:16
  • Your data is not organized differently, you just have an attribute in a separate data.frame. Just use merge ie., HR95_all <- merge(HR95_all, AV_locs, by="id") It also looks like your data is ordered so, you can just pull the corresponding sample size from the other data.frame using the "i" index in the for loop. It is important that you really try to understand code structures with R and not look for "exact" solutions. Much in the way of recommendations that you may receive on how to do certain things in R will likely be approximate and you need to adapt them to your own analysis. May 4, 2020 at 17:43
  • Thanks for the info! I've tried with your code but it still doesn't work with my example, I've got this error "Error in samples[[i]] <- sp::SpatialPointsDataFrame(psamp, data.frame(PIDS = rep(psub$id, : invalid type/length (S4/0) in vector allocation"; I think that my code should work as well, it does not seem that different from what you've suggested, but I have that last issue when creating the shapefiles, which would be good to have, separately for each id...
    – mto23
    May 4, 2020 at 18:50

The issue was solved by simply converting the object obtained from spsample() into a SpatialPointsDataFrame (following Convert SpatialPoints to SpatialPointsDataFrame); spsample() returned an object from a SpatialPoint class, which is not supported by writeORG().

My final loop was:

for (i in (unique(HR95_all$id))) {
        HR <- HR95_all[HR95_all$id==i,]
        Rnd_locs <- spsample(HR, n = AV_locs[which(AV_locs$id==i), "num"] , "random")
        Rnd_locs_SP <- SpatialPointsDataFrame(Rnd_locs, data.frame(ID=1:length(Rnd_locs)))
        writeOGR(Rnd_locs_SP,dsn=getwd(), layer=paste0("Random_", i), driver = "ESRI Shapefile",overwrite_layer=TRUE)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.