How to create an adjacency matrix from a distance matrix in R? - r

I have been trying to figure out how to create an empty adjacency matrix form the given function:
AdjDist <- function(distMatrix, dist){}
Everything I have tried does not work. Is there anyone who can help with this? (the distance matrix is 5x5 if that helps.)

It is not at all clear as to what you are after and please do follow the advice on how to ask a complete, reproducible question. An "empty adjacency matrix" is a bit of a non sequitur and does hint at a novice understanding of R.
You can easily perform a adjacency analysis using spdep. Hopefully this is close to what you are after.
First, load libraries and example data (meuse from sp library)
library(sp)
library(spdep)
data(meuse)
coordinates(meuse) <- ~x+y
Now we create a neighbor object and look at the first six observations of the neighbor matrix with the associated four neighbors. The row number corresponds to the row number of meuse and each column is the row index of the nearest neighbor.
meuse.knn <- knearneigh(coordinates(meuse), k=4)
head(meuse.knn$nn)
We can plot the linkages of k=4 using a graph structure
plot(meuse, pch=19)
plot(knn2nb(meuse.knn), coordinates(meuse), add=TRUE)
title(main="K nearest neighbours, k=4")
Now, for illustration purposes, we can subset the fifth observation in meuse and it's associated (k=4) nearest observations.
nn1.ids <- as.vector(meuse.knn$nn[5,])
nn1 <- meuse[nn1.ids,]
And then plot the fifth observation in meuse with its 4 nearest neighbors.
plot(nn1, pch=19, col="red")
plot(meuse[5,], pch=19, col="black", add=TRUE)
The actual adjacency matrix is contained in the knearneigh object (x$nn).

Related

How to construct a spatial adjacency matrix in R

I am new to using shapefiles in R and I was wondering if you can help me get a better understanding.
I need to create a spatial adjacency matrix W so that I can build a spatial model. W is an n x n matrix where n is the number of area polygons. The diagonal entries are wii = 0 and the off-diagonal entries wij = 1 if areas i and j share a common boundary and wij = 0 otherwise.
I know I would probably need to construct a contiguity matrix (I chose to use a queen neighborhood). But I am not sure how to further derive my spatial adjacency matrix from this.
#load relevant packages
library(sf)
library(tmap)
library(tmaptools)
library(dplyr)
#import data
mydata <- read.csv("tobago_communities.csv")
#import shapefile
mymap <-st_read("C:/Users/ndook/OneDrive/Desktop/Tobago/2011_parish_data.shp", stringsAsFactors = FALSE)
#join data and shapefile into one dataframe
map_and_data <- inner_join(mymap, mydata, by = "TGOLOC_ID")
#generate map
tm_shape(map_and_data) + tm_polygons("Unemployment")
#specify queen neighborhood
queen_tobago.nb <- poly2nb(mymap)
So I'm assuming the queen neighborhood would somehow be relevant to getting the spatial adjacency matrix but I am stuck at this point. Any further suggestions would be greatly appreciated.
The poly2nb function does generate a neighborhood list. Note that you need to call the option queen=T if you want queen neighborhood.
Some R packages expect a list representation of the spatial matrix, others might want a matrix form. The nb2listw function turns the neighborhood list into a list of spatial weights.
With the nb2mat function, you get a matrix representation that you are probably looking for (https://rdrr.io/rforge/spdep/man/nb2mat.html).

Assigning covariate associated to spatial points to a bigger set of spatial points in R?

I have two data sets with spatial points (in .csv format): data1 with 220 spatial points with latitude and longitude and data2 with 80 spatial points with latitude and longitude. For data2 I have one covariate indicated the genetic origin of each points. Spatial points in both datasets are not exactly the same.
I would like to assign the genetic origin for spatial points in data1. It seems that I need to define around each point in data2 a square (or other) to be able to associate a genetic origin at each points in data1.
I am using R and I think packages as raster or sp may be useful.
Thanks for your help.
Best,
Marie.
You need to make your mind up about how you want to assign "genetic origin". One approach that seem to be hinting at is assigning it to its nearest neighbor.
When asking a question you should always include some example data.
library(raster)
d1 <- data.frame(lon=c(1,5,55,31), lat=c(3,7,20,22))
d2 <- data.frame(lon=c(4,2,8,65,5,4), lat=c(50,-90,20,32,10,10), origin=LETTERS[1:6], stringsAsFactors=FALSE)
Here is how you can assign origin based on the nearest known origin
# make sure your data are (x,y) or (longitude,latitude), not the reverse
pd <- pointDistance(d1, d2[,1:2], lonlat=TRUE)
nd <- apply(pd, 1, which.min)
d1$origin <- d2$origin[nd]

Create neighborhood list of large dataset / fasten up

I want to create a weight matrix based on distance. My code for the moment looks as follows and functions for a smaller sample of the data. However, with the large dataset (569424 individuals in 24077 locations) it doesn't go through. The problem arise at the nb2blocknb fuction. So my question would be: How can I optimize my code for large datasets?
# load all survey data
DHS <- read.csv("Daten/final.csv")
attach(DHS)
# define coordinates matrix
coormat <- cbind(DHS$location, DHS$lon_s, DHS$lat_s)
coorm <- cbind(DHS$lon_s, DHS$lat_s)
colnames(coormat) <- c("location", "lon_s", "lat_s")
coo <- cbind(unique(coormat))
c <- as.data.frame(coo)
coor <- cbind(c$lon_s, c$lat_s)
# get a list with beneighbored locations thath are inbetween 50 km distance
neighbor <- dnearneigh(coor, d1 = 0, d2 = 50, row.names=c$location, longlat=TRUE, bound=c("GE", "LE"))
# get neighborhood list on individual level
nb <- nb2blocknb(neighbor, as.character(DHS$location)))
# weight matrix in list format
nbweights.lw <- nb2listw(nb, style="B", zero.policy=TRUE)
Thanks a lot for your help!
you're trying to make 1.3 e10 distance calculations. The results would be in the GB.
I think you'd want to limit either the maximum distance or the number of nearest neighbors you're looking for. Try nn2 from the RANN package:
library('RANN')
nearest_neighbours_w_distance<-nn2(coordinatesA, coordinatesB,10)
note that this operation is not symmetric (Switching coordinatesA and coordinatesB gives different results).
Also you would first have to convert your gps coordinates to a coordinate reference system in which you can calculate euclidean distances, for example UTM (code not tested):
library("sp")
gps2utm<-function(gps_coordinates_matrix,utmzone){
spdf<-SpatialPointsDataFrame(gps_coordinates_matrix[,1],gps_coordinates_matrix[,2])
proj4string(spdf) <- CRS("+proj=longlat +datum=WGS84")
return(spTransform(spdf, CRS(paste0("+proj=utm +zone=",utmzone," ellps=WGS84"))))
}

In R, how to average spatial points data over spatial grid squares

Managed to solve problem now
I have a set of around 50 thousand points that have coordinates and one value associated with them. I would like to be able to place points into a grid averaging the associated value of all points that fall into a grid square. So I want to end up with an object that identifies each grid square and gives the average inside the grid square.
I have the data in a spatial points data frame and a spatial grid object if that helps.
Improving answer: I have definitely done some searching, sorry about the initial state of the question I had only managed to frame the question inside my own head; hadn't had to communicate it to anyone else before...
Here is example data that hopefully illustrates the problem more clearly
##make some data
longi <- runif(100,0,10)
lati <- runif(100,0,10)
value <- runif(500,20,30)
##put in data frame then change to spatial data frame
df <- data.frame("lon"=longi,"lat"=lati,"val"=value)
coordinates(df) <- c("lon","lat")
proj4string(df) <- CRS("+proj=longlat")
##create a grid that bounds the data
grd <- GridTopology(cellcentre.offset=bbox(df)[,1],
cellsize=c(1,1),cells.dim=c(11,11))
sg <- SpatialGrid(grd)
Then I hope to get an object albeit a vector/data frame/list that gives me the average of value in each grid cell/square and some way of identifying which cell it is.
Solution
##convert the grid into a polygon##
polys <- as.SpatialPolygons.GridTopology(grd)
proj4string(polys) <- CRS("+proj=longlat")
##can now use the function over to select the correct points and average them
results <- rep(0, length(polys))
for(i in 1:length(polys)) {
results[i] = mean(df$val[which(!is.na(over(x=df,y=polys[i])))])
}
My question now is if this is the best way to do it or is there a more efficient way?
Your description is vague at best. Please try to ask more specific answers preferably, with code illustrating what you have already tried. Averaging a single value in your point data or a single raster cell makes absolutely no sense.
The best guess at an answer I can provide is to use raster extract() to assign the raster values to a sp point object and then use tapply() to aggregate the values to your grouping values in the points. You can use the coordinates of the points to identify cell location or alternately, the cellnumbers returned from extract (per below example).
require(raster)
require(sp)
# Create example data
r <- raster(ncol=500, nrow=500)
r[] <- runif(ncell(r))
pts <- sampleRandom(r, 100, sp=TRUE)
# Add a grouping value to points
pts#data <- data.frame(ID=rownames(pts#data), group=c( rep(1,25),rep(2,25),
rep(3,25),rep(4,25)) )
# Extract raster values and add to #data slot dataframe. Note, the "cells"
# attribute indicates the cell index in the raster.
pts#data <- data.frame(pts#data, extract(r, pts, cellnumbers=TRUE))
head(pts#data)
# Use tapply to cal group means
tapply(pts#data$layer, pts#data$group, FUN=mean)

Export R plot to shapefile

I am fairly new to R, but not to ArcView. I am plotting some two-mode data, and want to convert the plot to a shapefile. Specifically, I would like to convert the vertices and the edges, if possible, so that I can get the same plot to display in ArcView, along with the attributes.
I've installed the package "shapefiles", and I see the convert.to.shapefile command, but the help doesn't talk about how to assign XY coords to the vertices.
Thank you,
Tim
Ok, I'm making a couple of assumptions here, but I read the question as you're looking to assign spatial coordinates to a bipartite graph and export both the vertices and edges as point shapefiles and polylines for use in ArcGIS.
This solution is a little kludgey, but will make shapefiles with coordinate limits xmin, ymin and xmax, ymax of -0.5 and +0.5. It will be up to you to decide on the graph layout algorithm (e.g. Kamada-Kawai), and project the shapefiles in the desired coordinate system once the shapefiles are in ArcGIS as per #gsk3's suggestion. Additional attributes for the vertices and edges can be added where the points.data and edge.data data frames are created.
library(igraph)
library(shapefiles)
# Create dummy incidence matrix
inc <- matrix(sample(0:1, 15, repl=TRUE), 3, 5)
colnames(inc) <- c(1:5) # Person ID
rownames(inc) <- letters[1:3] # Event
# Create bipartite graph
g.bipartite <- graph.incidence(inc, mode="in", add.names=TRUE)
# Plot figure to get xy coordinates for vertices
tk <- tkplot(g.bipartite, canvas.width=500, canvas.height=500)
tkcoords <- tkplot.getcoords(1, norm=TRUE) # Get coordinates of nodes centered on 0 with +/-0.5 for max and min values
# Create point shapefile for nodes
n.points <- nrow(tkcoords)
points.attr <- data.frame(Id=1:n.points, X=tkcoords[,1], Y=tkcoords[,2])
points.data <- data.frame(Id=points.attr$Id, Name=paste("Vertex", 1:n.points, sep=""))
points.shp <- convert.to.shapefile(points.attr, points.data, "Id", 1)
write.shapefile(points.shp, "~/Desktop/points", arcgis=TRUE)
# Create polylines for edges in this example from incidence matrix
n.edges <- sum(inc) # number of edges based on incidence matrix
Id <- rep(1:n.edges,each=2) # Generate Id number for edges.
From.nodes <- g.bipartite[[4]]+1 # Get position of "From" vertices in incidence matrix
To.nodes <- g.bipartite[[3]]-max(From.nodes)+1 # Get position of "To" vertices in incidence matrix
# Generate index where position alternates between "From.node" to "To.node"
node.index <- matrix(t(matrix(c(From.nodes, To.nodes), ncol=2)))
edge.attr <- data.frame(Id, X=tkcoords[node.index, 1], Y=tkcoords[node.index, 2])
edge.data <- data.frame(Id=1:n.edges, Name=paste("Edge", 1:n.edges, sep=""))
edge.shp <- convert.to.shapefile(edge.attr, edge.data, "Id", 3)
write.shapefile(edge.shp, "~/Desktop/edges", arcgis=TRUE)
Hope this helps.
I'm going to take a stab at this based on a wild guess as to what your data looks like.
Basically you'll want to coerce the data into a data.frame with two columns containing the x and y coordinates (or lat/long, or whatever).
library(sp)
data(meuse.grid)
class(meuse.grid)
coordinates(meuse.grid) <- ~x+y
class(meuse.grid)
Once you have it as a SpatialPointsDataFrame, sp provides some decent functionality, including exporting shapefiles:
writePointsShape(meuse.grid,"/home/myfiles/wherever/myshape.shp")
Relevant help files examples are drawn from:
coordinates
SpatialPointsDataFrame
readShapePoints
At least a few years ago when I last used sp, it was great about projection and very bad about writing projection information to the shapefile. So it's best to leave the coordinates untransformed and manually tell Arc what projection it is. Or use writeOGR rather than writePointsShape.

Resources