How to create a raster from a data frame in r? - r

I have a data frame in which values (l) are specified for Cartesian coordinates (x, y) as in the following minimal working example.
set.seed(2013)
df <- data.frame( x = rep( 0:1, each=2 ),
y = rep( 0:1, 2),
l = rnorm( 4 ))
df
# x y l
# 1 0 0 -0.09202453
# 2 0 1 0.78901912
# 3 1 0 -0.66744232
# 4 1 1 1.36061149
I want to create a raster using the raster package, but my reading of the documentation has not revealed a simple method for loading data in the form that I have it into the raster cells. I've come up with a couple ways to do it using for loops, but I suspect that there's a much more direct approach that I'm missing.

An easier solution exists as
library(raster)
dfr <- rasterFromXYZ(df) #Convert first two columns as lon-lat and third as value
plot(dfr)
dfr
class : RasterLayer
dimensions : 2, 2, 4 (nrow, ncol, ncell)
resolution : 1, 1 (x, y)
extent : -0.5, 1.5, -0.5, 1.5 (xmin, xmax, ymin, ymax)
coord. ref. : NA
data source : in memory
names : l
values : -2.311813, 0.921186 (min, max)
Further, you may specify the CRS string. Detailed discussion is available here.

Here is one approach, via SpatialPixelsDataFrame
library(raster)
# create spatial points data frame
spg <- df
coordinates(spg) <- ~ x + y
# coerce to SpatialPixelsDataFrame
gridded(spg) <- TRUE
# coerce to raster
rasterDF <- raster(spg)
rasterDF
# class : RasterLayer
# dimensions : 2, 2, 4 (nrow, ncol, ncell)
# resolution : 1, 1 (x, y)
# extent : -0.5, 1.5, -0.5, 1.5 (xmin, xmax, ymin, ymax)
# coord. ref. : NA
# data source : in memory
# names : l
# values : -0.6674423, 1.360611 (min, max)
help('raster') describes a number of methods to create a raster from objects of different classes.

Updating this corresponding to #zubergu about Rasterizing irregular data.
So the answer that I have adapted from the link below and possibly makes it even more simpler to understand is:
library(raster)
library(rasterize)
# Suppose you have a dataframe like this
lon <- runif(20, -180, 180)
lat <- runif(20, -90, 90)
vals <- rnorm(20)
df <- data.frame(lon, lat, vals)
# will need to rename colnames for raster
colnames(df) <- c('x', 'y', 'vals')
# create a raster object
r_obj <- raster(xmn=-180, xmx=180, ymn=-90, ymx=90, resolution=c(5,5))
# use rasterize to create desired raster
r_data <- rasterize(x=df[, 1:2], # lon-lat data
y=r_obj, # raster object
field=df[, 3], # vals to fill raster with
fun=mean) # aggregate function
plot(r_data)
Original response:
for those of you like #yuliaUU looking to convert irregular data to a raster, please see #RobertH's answer here.

Related

How to select top 30% of cells in a raster (by cell value) in R

I have a raster in R, I need to select the highest cell values up until 30% of the raster area is selected.
The way that I've tried to accomplish this is by calculating the average cell area, and then calculating how many cells I need to meet this 30% target (I know this is not entirely accurate). Then I sort the raster values, descending. Here is where I'm stuck. Of these sorted values, I need to set all cells beyond #12,678 to NA. I can't figure out how to set values to NA based on their place in an order. Does anyone know how to do this? Or have a better idea for the entire process?
you can use tidyterra for this.
Once that you have identified your thresold (12,678) use tidyterra::filter() on your raster.
I don't know if you are using raster or terra, I use here terra and I show you how to convert back to Raster* object.
On this example I present a full workflow with an example raster file, just adapt it to your data.
library(terra)
#> terra 1.5.21
library(tidyterra)
#> -- Attaching packages --------------------------------------- tidyterra 0.1.0 --
#>
#> Suppress this startup message by setting Sys.setenv(tidyterra.quiet = TRUE)
#> v tibble 3.1.7 v dplyr 1.0.9
#> v tidyr 1.2.0
# Create a SpatRaster from a file
f <- system.file("ex/elev.tif", package = "terra")
r <- rast(f)
# If you are using raster package use this line for switch it to terra:
# r <- rast(your_data)
r
#> class : SpatRaster
#> dimensions : 90, 95, 1 (nrow, ncol, nlyr)
#> resolution : 0.008333333, 0.008333333 (x, y)
#> extent : 5.741667, 6.533333, 49.44167, 50.19167 (xmin, xmax, ymin, ymax)
#> coord. ref. : lon/lat WGS 84 (EPSG:4326)
#> source : elev.tif
#> name : elevation
#> min value : 141
#> max value : 547
totarea_km <- expanse(r, unit = "km")
prettyNum(totarea_km, big.mark = ",")
#> [1] "2,563.61"
plot(r)
# There is a bug on slice_max, use filter
# Get thresold for filtering by working on the values of the raster
# as it is a data frame
min_value <- as_tibble(r, na.rm = TRUE) %>%
slice_max(order_by = elevation, prop = .3) %>%
min()
# Here it goes!!
top30perc <- r %>% filter(elevation > min_value)
# Check
area_30perc <- expanse(top30perc, unit = "km")
prettyNum(area_30perc, big.mark = ",")
#> [1] "761.0991"
# Check
area_30perc / totarea_km
#> [1] 0.2968857
# Seems ok
plot(top30perc)
# If you need to convert to Raster* object
raster::stack(top30perc)
#> class : RasterStack
#> dimensions : 90, 95, 8550, 1 (nrow, ncol, ncell, nlayers)
#> resolution : 0.008333333, 0.008333333 (x, y)
#> extent : 5.741667, 6.533333, 49.44167, 50.19167 (xmin, xmax, ymin, ymax)
#> crs : +proj=longlat +datum=WGS84 +no_defs
#> names : elevation
#> min values : 387
#> max values : 547
Created on 2022-05-31 by the reprex package (v2.0.1)
If you assume a constant cell size, then you might as well ignore it and you can do something like this (find the quantile of interest and use that as a threshold):
library(terra)
r <- rast(system.file("ex/elev.tif", package = "terra"))
q <- global(r, \(i) quantile(i, 0.7, na.rm=T))
x <- ifel(r <= q[[1]], NA, r)
plot(x)
Check
g <- global(c(x,r), "notNA")
g[1,1]/g[2,1]
# 0.2979601
If variation in cell size is important, you could do something like the below (sort the values, and find the cells below the cumulative cell size threshold), but note that this approach is not memory-safe (would fail with very large rasters)
s <- c(r, cellSize(r, unit="km"))
d <- as.data.frame(s, cell=T, na.rm=T)
d <- d[order(d$elevation, decreasing=TRUE), ]
d$sumarea <- cumsum(d$area) / sum(d$area)
cells <- d[d$sumarea <= 0.3, "cell"]
msk <- rast(r)
msk[cells] <- 1
x <- mask(r, msk)
plot(x)

How do you change coordinates of a raster? [duplicate]

I have a raster with XY pixel coordinates which I want to convert to lat and long.
class : RasterLayer
dimensions : 1617, 1596, 2580732 (nrow, ncol, ncell)
resolution : 1, 1 (x, y)
extent : 0, 1596, 0, 1617 (xmin, xmax, ymin, ymax)
coord. ref. : NA
data source : C:\janW1.png
names : janW1
values : 0, 255 (min, max)
I have calculated the lat/long coords using the formula specified here.
This has resulted in the following dataframe
heads(cords)
lat lon x y janW1
1 46.99401 -14.99122 0.5 1616.5 0
2 46.99401 -14.97367 1.5 1616.5 0
3 46.99401 -14.95611 2.5 1616.5 0
4 46.99401 -14.93856 3.5 1616.5 0
5 46.99401 -14.92100 4.5 1616.5 0
6 46.99401 -14.90345 5.5 1616.5 0
How can I over-write or create a duplicate raster with the spatial extent in lat/long instead of image coordinates (XY pixels)? Or is there an easier way to convert the pixels to lat/Lon?
Code
library(raster)
test <- raster('janW1.png')
data_matrix <- rasterToPoints(test)
# Calculate longitude.
lonfract = data_matrix[,"x"] / (1596 - 1)
lon = -15 + (lonfract * (13 - -15))
# Calculate latitude.
latfract = 1.0 - (data_matrix[,"y"] / (1617 - 1))
Ymin = log(tan ((pi/180.0) * (45.0 + (47 / 2.0))))
Ymax = log(tan ((pi/180.0) * (45.0 + (62.999108 / 2.0))))
Yint = Ymin + (latfract * (Ymax - Ymin))
lat = 2.0 * ((180.0/pi) * (atan (exp (Yint))) - 45.0)
# Make single dataframe with XY pixels and latlon coords.
latlon <- data.frame(lat,lon)
tmp <- data.frame(data_matrix)
cords <- cbind(latlon, tmp)
janW1.png
Changing the projection of raster data is not as simple as for points (and lines, polygons). This is because if you compute the new coordinates from the current cells, they won't be in a regular raster.
You can use function projectRaster (raster package) to deal with this.
library(raster)
test <- raster('janW1.png')
# In this case, you need to provide the correct crs to your data
# I am guessing. (this would be necessary for spatial data sets)
crs(test) <- '+proj=merc +datum=WGS84'
# you may also need to set the extent to actual coordinate values
# extent(test) <- c( , , ,)
x <- projectRaster(test, crs='+proj=longlat +datum=WGS84')
Alternatively, you can interpolate values you computed to a new raster. See ?raster::interpolate for examples.
Could you create a raster from scratch with the resolution and spatial extent that you want and then import your values into it. To create a raster you could use something like:
# Create a matrix of coordinates that define the limits of your raster
ex <- matrix(c(-20, -9.5, 20.5, 31.5), nrow = 2, ncol = 2, byrow = T)
# Turn those coordinates into an extent
ex <- extent(ex)
# Create a raster with the same dimensions as your original one
r <- raster(nrows = 1617, ncols = 1596)
# Set the extent of your raster
r <- setExtent(r, ex, keepres=F)
To get the values from your previous raster into the raster you've just created you can use:
test <- raster('janW1.png')
# Create vector of values from test
n <- values(test)
# Give values from test to r
values(r) <- n
I think I got the correct resolution from your code but you will need to put the four coordinates for your extent in yourself. The blank raster will have to have exactly the same resolution as your original raster or it won't work. The blank raster you create is automatically in WGS84 so you might want to reproject it once you've got your data in.

Convert three vectors (lat, lon, value) to raster object in R [duplicate]

I have a data frame in which values (l) are specified for Cartesian coordinates (x, y) as in the following minimal working example.
set.seed(2013)
df <- data.frame( x = rep( 0:1, each=2 ),
y = rep( 0:1, 2),
l = rnorm( 4 ))
df
# x y l
# 1 0 0 -0.09202453
# 2 0 1 0.78901912
# 3 1 0 -0.66744232
# 4 1 1 1.36061149
I want to create a raster using the raster package, but my reading of the documentation has not revealed a simple method for loading data in the form that I have it into the raster cells. I've come up with a couple ways to do it using for loops, but I suspect that there's a much more direct approach that I'm missing.
An easier solution exists as
library(raster)
dfr <- rasterFromXYZ(df) #Convert first two columns as lon-lat and third as value
plot(dfr)
dfr
class : RasterLayer
dimensions : 2, 2, 4 (nrow, ncol, ncell)
resolution : 1, 1 (x, y)
extent : -0.5, 1.5, -0.5, 1.5 (xmin, xmax, ymin, ymax)
coord. ref. : NA
data source : in memory
names : l
values : -2.311813, 0.921186 (min, max)
Further, you may specify the CRS string. Detailed discussion is available here.
Here is one approach, via SpatialPixelsDataFrame
library(raster)
# create spatial points data frame
spg <- df
coordinates(spg) <- ~ x + y
# coerce to SpatialPixelsDataFrame
gridded(spg) <- TRUE
# coerce to raster
rasterDF <- raster(spg)
rasterDF
# class : RasterLayer
# dimensions : 2, 2, 4 (nrow, ncol, ncell)
# resolution : 1, 1 (x, y)
# extent : -0.5, 1.5, -0.5, 1.5 (xmin, xmax, ymin, ymax)
# coord. ref. : NA
# data source : in memory
# names : l
# values : -0.6674423, 1.360611 (min, max)
help('raster') describes a number of methods to create a raster from objects of different classes.
Updating this corresponding to #zubergu about Rasterizing irregular data.
So the answer that I have adapted from the link below and possibly makes it even more simpler to understand is:
library(raster)
library(rasterize)
# Suppose you have a dataframe like this
lon <- runif(20, -180, 180)
lat <- runif(20, -90, 90)
vals <- rnorm(20)
df <- data.frame(lon, lat, vals)
# will need to rename colnames for raster
colnames(df) <- c('x', 'y', 'vals')
# create a raster object
r_obj <- raster(xmn=-180, xmx=180, ymn=-90, ymx=90, resolution=c(5,5))
# use rasterize to create desired raster
r_data <- rasterize(x=df[, 1:2], # lon-lat data
y=r_obj, # raster object
field=df[, 3], # vals to fill raster with
fun=mean) # aggregate function
plot(r_data)
Original response:
for those of you like #yuliaUU looking to convert irregular data to a raster, please see #RobertH's answer here.

R - create boxplot with rasters

this might be rather simple, but I am a novice in R. I have tried for awhile now to plot two rasters against each other using boxplot from the package raster.
I have a DEM raster and a categorical raster that contains 4 cluster groups, which I would like to use as 'zones' as described in the manual:
boxplot(x, y=NULL, maxpixels=100000, ...)
x Raster* object
y If x is a RasterLayer object, y can be an additional RasterLayer to group the
values of x by ’zone’
> DEM
class : RasterLayer
dimensions : 12381, 61922, 766656282 (nrow, ncol, ncell)
resolution : 0.1, 0.1 (x, y)
extent : 478307.4, 484499.6, 6131862, 6133100 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=utm +zone=32 +ellps=GRS80 +units=m +no_defs
data source : /Users/Yvonne/Desktop/Boxplot/Ribe_DEM_0.1m.tif
names : Ribe_DEM_0.1m
values : -7.523334, -0.36 (min, max)
> Cluster
class : RasterLayer
dimensions : 12381, 61922, 766656282 (nrow, ncol, ncell)
resolution : 0.1, 0.1 (x, y)
extent : 478307.4, 484499.6, 6131862, 6133100 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=utm +zone=32 +ellps=GRS80 +units=m +no_defs
data source : /Users/Yvonne/Desktop/Boxplot/final_cluster.tif
names : final_cluster
values : 1, 4 (min, max)
attributes :
ID Rowid COUNT
1 0 463524
2 1 4118997
3 2 3390160
4 3 3218998
> boxplot(DEM, Cluster, xlab="Cluster", ylab="Elevation")
Error in parse(text = x, keep.source = FALSE) :
<text>:2:0: unexpected end of input
1: ~
^
In addition: Warning message:
In .local(x, ...) : taking a sample of 1e+05 cells
Update:
I just found a working example, which does exactly what I want. However if I run it with my own data I always get above error. Maybe someone could explain the error message. Would be really appreciated.
r1 <- r2 <- r3 <- raster(ncol=10, nrow=10)
r1[] <- rnorm(ncell(r1), 100, 40)
r2[] <- rnorm(ncell(r1), 80, 10)
r3[] <- rnorm(ncell(r1), 120, 30)
s <- stack(r1, r2, r3)
names(s) <- c('A', 'B', 'C')
rc <- round(r1[[1]]/100)
hist(rc)
summary(rc)
boxplot(s[[1]],rc)
Okey I found an answer, I don't know exactly why but it works for me:
I had to create a brick and then I could use the boxplot as mentioned above.
s <- stack(DEM, Cluster)
sbrick <- brick(s)
boxplot(sbrick[[1]], sbrick[[2]], xlab="Cluster", ylab="Elevation")
Resulting in this plot boxplot DEM against cluster groups
Thanks everyone for their help!
You can use bwplot function in rasterVis library.
Here is an example from rasterVis:
library(raster)
library(rasterVis)
r <- raster(system.file("external/test.grd", package="raster"))
s <- stack(r, r*2)
bwplot(s,violin=FALSE,strip=strip.custom(strip.levels=TRUE))
It is not clear to me why you get that error. Perhaps you can run the code below and see for yourself:
x <- stack(DEM, Cluster)
s <- sampleRegular(s, 100000, useGDAL=TRUE)
cn <- colnames(s)
f <- as.formula(paste(cn[1], '~', cn[2]))
boxplot(f, data=s)
Perhaps you should only provide your raster values as a vector and let the boxplot() function do the rest by:
boxplot(values(DEM) ~ values(Cluster), xlab="Cluster", ylab="Elevation")
Note that this will only work if both DEM and Cluster are exactly the same extent and resolution.

R: over-write xy coordinates of raster layer

I have a raster with XY pixel coordinates which I want to convert to lat and long.
class : RasterLayer
dimensions : 1617, 1596, 2580732 (nrow, ncol, ncell)
resolution : 1, 1 (x, y)
extent : 0, 1596, 0, 1617 (xmin, xmax, ymin, ymax)
coord. ref. : NA
data source : C:\janW1.png
names : janW1
values : 0, 255 (min, max)
I have calculated the lat/long coords using the formula specified here.
This has resulted in the following dataframe
heads(cords)
lat lon x y janW1
1 46.99401 -14.99122 0.5 1616.5 0
2 46.99401 -14.97367 1.5 1616.5 0
3 46.99401 -14.95611 2.5 1616.5 0
4 46.99401 -14.93856 3.5 1616.5 0
5 46.99401 -14.92100 4.5 1616.5 0
6 46.99401 -14.90345 5.5 1616.5 0
How can I over-write or create a duplicate raster with the spatial extent in lat/long instead of image coordinates (XY pixels)? Or is there an easier way to convert the pixels to lat/Lon?
Code
library(raster)
test <- raster('janW1.png')
data_matrix <- rasterToPoints(test)
# Calculate longitude.
lonfract = data_matrix[,"x"] / (1596 - 1)
lon = -15 + (lonfract * (13 - -15))
# Calculate latitude.
latfract = 1.0 - (data_matrix[,"y"] / (1617 - 1))
Ymin = log(tan ((pi/180.0) * (45.0 + (47 / 2.0))))
Ymax = log(tan ((pi/180.0) * (45.0 + (62.999108 / 2.0))))
Yint = Ymin + (latfract * (Ymax - Ymin))
lat = 2.0 * ((180.0/pi) * (atan (exp (Yint))) - 45.0)
# Make single dataframe with XY pixels and latlon coords.
latlon <- data.frame(lat,lon)
tmp <- data.frame(data_matrix)
cords <- cbind(latlon, tmp)
janW1.png
Changing the projection of raster data is not as simple as for points (and lines, polygons). This is because if you compute the new coordinates from the current cells, they won't be in a regular raster.
You can use function projectRaster (raster package) to deal with this.
library(raster)
test <- raster('janW1.png')
# In this case, you need to provide the correct crs to your data
# I am guessing. (this would be necessary for spatial data sets)
crs(test) <- '+proj=merc +datum=WGS84'
# you may also need to set the extent to actual coordinate values
# extent(test) <- c( , , ,)
x <- projectRaster(test, crs='+proj=longlat +datum=WGS84')
Alternatively, you can interpolate values you computed to a new raster. See ?raster::interpolate for examples.
Could you create a raster from scratch with the resolution and spatial extent that you want and then import your values into it. To create a raster you could use something like:
# Create a matrix of coordinates that define the limits of your raster
ex <- matrix(c(-20, -9.5, 20.5, 31.5), nrow = 2, ncol = 2, byrow = T)
# Turn those coordinates into an extent
ex <- extent(ex)
# Create a raster with the same dimensions as your original one
r <- raster(nrows = 1617, ncols = 1596)
# Set the extent of your raster
r <- setExtent(r, ex, keepres=F)
To get the values from your previous raster into the raster you've just created you can use:
test <- raster('janW1.png')
# Create vector of values from test
n <- values(test)
# Give values from test to r
values(r) <- n
I think I got the correct resolution from your code but you will need to put the four coordinates for your extent in yourself. The blank raster will have to have exactly the same resolution as your original raster or it won't work. The blank raster you create is automatically in WGS84 so you might want to reproject it once you've got your data in.

Resources