12

I have created an object of type SpatialPointsDataFrame using the sp package in R. However, I am confused about the @, $, . and [] operators and when to use them to access the different properties of my object. Here is my sample code:

library(sp)
library(rgdal)

#creating a SpatialPointsDataFrame with sample points in UTM
x <- c(15.2, 15.3, 15.4, 15.5, 15.7)
y <- c(50.4, 50.2, 50.3, 50.1, 50.4)
v1 <- c(1.0, 2.0, 3.0, 4.0, 5.0)
v2 <- c("a","b","b","c","a")
attributes <- as.data.frame(cbind(v1,v2))
xy <- cbind(x,y)
locationsDD <- SpatialPointsDataFrame(xy, attributes)
proj4string(locationsDD) <- CRS("+proj=longlat")
locations <- spTransform(locationsDD, CRS("+proj=utm +zone=33"))
plot(locations)

#using the different operators: WHEN TO USE @, $ or [] ?

#all these work!
property1 <- locations$v1
property2 <- locations@data$v1
property3 <- locations@data[,"v1"]
property4 <- locations@data["v1"]

#these also work
property5 <- locations@coords
property6 <- locations@bbox
property7 <- locations@coords[,2]

#these three work only in my special case
property8 <- locations@coords[,"y"]
property9 <- locations$x
property10 <- locations$y

#these don't work: $ operator is invalid for atomic vectors
property11 <- locations@coords$x
property12 <- locations@coords$y

Could anybody help me, when to use the @, $, [] operators? When I try to read the documentation ?SpatialPointsDataFrame I can see the different properties such as coords or bbox but I'm confused which operator @, $, [] to use to access them or modify them.

  • 1
    Because this is really a question about R syntax, it is not particular to the sp package or its objects. R is installed with a tutorial: begin there in your research. The Web and the print media offer a wealth of additional resources for learning R. – whuber Feb 26 '14 at 14:36
20

Spatial sp data are S4 class objects and are made up of slots (called using @) that contain components of the spatial feature class being represented (e.g., @data contains attributes, @coords contain coordinate pairs, etc...). You can return the top level slot names using slotNames() but it is not recursive and will not return nested slot names for polygon class objects. Each slot can contain a different object class and, before operating on it, should be checked using str() or class(). The @data slot is always a data.frame object and @coords is a matrix whereas @polygons is a list object with additional slots (labpt, area, hole, ringDir and coords).

The available slots and organization of them is dependent on what type of feature class is being represented. SpatialPointsDataFrame objects are the most basic, whereas SpatialPolygonsDataFrame objects have nesting (as seen above). This nested structure, representing each polygon, has to be accounted for using something like sapply to operate on each list object (polygon).

Here is an example that uses sapply to return the area for each polygon by iterating through the "polygons" then, the nested, "area" slot(s).

sapply(slot(sdat, 'polygons'), function(i) slot(i, 'area')) 

In the case of polygon objects, since they are stored as a list for each polygon, you can alternatively use list indexing. Here is an example to return the first polygon (resulting in a "Polygon" class object and not SpatialPolygonsDataFrame):

sdat@polygons[[1]]

In more recent versions of sp the developers have started, in some instances, removing the necessity of calling the @data slot directly.

For instance, to index @data you previously:

sdat@data[sdat@data$att >= 0.5 ,]  

and now:

sdat[sdat$att >= 0.5 ,]

However, as previously indicated, this is not the case for the other slots (e.g., coordinates, polygons, etc...). As far as when to use [] or $ this is still dependent on the type of operation. Brackets "[]" can be used to call a name in a dataframe but are primarily used for indexing whereas $ is specifically used to call a column in a dataframe. The reason that an "indirect" call to a column name works that the developers have added functionality to allow for a recursive search through the sp object. However, to avoid name conflicts (as in your example; having x,y columns in your dataframe would conflict with the x,y names in the @coord matrix names) there is some internal consistency checking that accounts for why this only works in some instances.

One convenient characteristic is that you can subset a spatial object through a row index. Here I am subsetting the first 10 objects.

sub.sdat <- sdat[1:10,] 

Or, alternatively, a random sample (n=10) using a row index vector.

rs.sdat <- sdat[sample(1:nrow(sdat), 10),]

Understanding indexing and how to use brackets is a very important thing in writing R code.

Edit (03/24/2017): Please note that the simple feature (sf) class, following the GeoJSON standard, will likely become the new standard for spatial objects in R. You can read a detailed description of this class at the CRAN sf website Simple Features for R.

  • Thanks for a detailed explanation of what is happening behind the scenes. It appears that for SpatialPointsDataFrame not only the @data columns, but also the @coords columns can be retrieved with the $ operator without the necessity of calling the @coords slot. So sdat@coords$easting gives the same result as sdat$easting. – jirikadlec2 Feb 26 '14 at 16:07
  • It looks like you are calling a column in <at>data. This is not the same as the <at>coords slot. You will notice that if you call colnames(sdat<at>coords) you will return the matrix column names: "coords.x1", "coords.x2". It is not necessary to hold coordinates in the dataframe and, since it is duplicated, a waist of memory. – Jeffrey Evans Feb 26 '14 at 16:12
  • No. I'm not calling the column in <at> data. Using the SpatialPointsDataFrame from my sample script, colnames(locations@coords) returns [1] "x" "y" but colnames(locations@data) returns [1] "v1" "v2". Maybe the behaviour is dependent on what function was used to create the SpatialPointsDataFrame? – jirikadlec2 Feb 26 '14 at 16:23
  • Actually I have a mistake in my first comment. sdat@coords$easting doesn't work because sdat@coords is a matrix. But sdat@coords[,"easting"] is equivalent to sdat@coords[,1] and to sdat$easting. – jirikadlec2 Feb 26 '14 at 16:51
  • One caveat, colnames() is used to return column names in a matrix whereas, names() will return NULL. Although, both names() and colnames() will work on a dataframe object such as <at>data. The best way to retrieve data from the <at>coord matrix is to index it: sdat<at>coords[,1] or by column name sdat<at>coords[,"coords.x1"] but as you noted $ does not work because it is a matrix object. – Jeffrey Evans Feb 26 '14 at 17:51
4

You should try str(locations) to clarify this.

for example, these ones are correct:

property2 <- locations@data$v1
property5 <- locations@coords
property6 <- locations@bbox
property7 <- locations@coords[,"x"]
property8 <- locations@coords[,2]

And this one property1 <- locations$v1 works, because it is referencing the data.frame inside location, @data

  • str(locations) gave me some good hints. Now I understand that @ is used for "slot of a class". But I still don't understand why property9 <- locations$x works when names(locations) doesn't contain any column named x – jirikadlec2 Feb 26 '14 at 14:57
  • 1
    When you create the SpatialPointDataFrame, you assign x and y as coordinates names. If you look at locations@coords you can see the matrix with the coordinates. Also, if you try to create a new column in @data with the name "x", you can't, because its already use as coordinate name. – Guillermo Olmedo Feb 26 '14 at 15:11
  • I still don't quite get what kind of 'magic' the SpatialPointsDataFrame object uses for accessing the coordinates with the $ operator. But at least I'm more comfortable with using it now. I ran the following code: colnames(locations@coords) <- c("easting","northing") After I run it, locations$easting gives me the x-coordinate vector and locations$northing gives me the y-coordinate vector. – jirikadlec2 Feb 26 '14 at 15:45
  • I think in some way R considers the two columns for the coordinates as two more columns of the dataframe part of the SpatialPointsDataFrame. Thats why you can have a column with the same name inside the @data slot – Guillermo Olmedo Feb 26 '14 at 18:51
  • 1
    It seems that the naming of the columns in the @coords matrix of the SpatialPointsDataFrame is dependent on how the SpatialPointsDataFrame object was created. Method one: coordinates(sdat) <- x ~ y will re-name the columns to "coords.x1", "coords.x2". Method two: sdat <- SpatialPointsDataFrame(xy, attributes) will preserve the original column names from the xy matrix. – jirikadlec2 Feb 28 '14 at 8:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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