Assign and use data values of Spatial*DataFrame easily

2.7k Views Asked by At

Is it possible to somehow easily work with data in Spatial*DataFrame (* = Points, Lines, Polygons, Pixels, Grid,...)? In particular I have difficulties with assigning values to it and to operate with them:

require(gstat)
data(meuse)
coordinates(meuse) = ~x+y
data(meuse.grid)
gridded(meuse.grid) = ~x+y

######## 1) assigning value

meuse[1,'zinc'] <- NA
# Error in meuse[1, "zinc"] <- NA : object of type 'S4' is not subsettable
as.data.frame(meuse)[1,'zinc'] <- NA
# Error in as.data.frame(meuse)[1, "zinc"] <- NA : 
#   could not find function "as.data.frame<-"

######## 2) operating with values

meuse[, 'zinc'] + 2
# Error in meuse[, "zinc"] + 2 : non-numeric argument to binary operator

I have found pretty ugly workarounds for both cases:

# ad 1)
meuse2 <- as.data.frame(meuse)
meuse2[1, 'zinc'] <- NA
meuse2 <- SpatialPointsDataFrame(SpatialPoints(meuse), meuse2)

# ad 2)
as.data.frame(meuse)[, 'zinc'] + 2

but these are just beginners' attempts, way too ugly and complicated... Must be much easier in R!

2

There are 2 best solutions below

0
On

For Spatial*DataFrame objects, you can access the data.frame slot with '@data', and the usual data.frame operations should work. Using your example,

 meuse@data[1, 'zinc'] <- NA

gives

 str(meuse@data)
'data.frame':   155 obs. of  12 variables:
 $ cadmium: num  11.7 8.6 6.5 2.6 2.8 3 3.2 2.8 2.4 1.6 ...
 $ copper : num  85 81 68 81 48 61 31 29 37 24 ...
 $ lead   : num  299 277 199 116 117 137 132 150 133 80 ...
 $ zinc   : num  NA 1141 640 257 269 ...
 ...
3
On

You can do this like this:

meuse$zinc[1] <- NA 

or, better since you can use a variable name rather than literal:

aname <- 'zinc'
meuse[[aname]][1] <- NA

It seems that other pathways to this are not provided as methods, but they could be added. I'm not sure why this is the case, but originally the *DataFrame part was built as an AttributeList in sp since R's actual data.frame was inefficient with rownames at the time, but that is ancient history and all fixed now.

You could expect that these would work, like a normal data.frame but they do not:

meuse[1,'zinc'] <- NA
meuse[1,][['zinc']] <- NA
meuse[1,]$zinc <- NA 

It's probably best to avoid the @ operator if you can, because it is for developers to hide inside the code they provide to users, i.e. it's subverting their API if you don't use the methods provided. There's no massive danger in it, except that the developers might change the API and your code won't work, and you might corrupt the object by modifying one part but not another in the way the object was designed, so you shouldn't use it except privately or in code you maintain actively (I think it's safe in this instance but avoid it generally if you can). See ?"@" and ?"slot" for the basics.