r - How to calculate the volume under a surface defined by discrete data? -
i need determine volume beneath series of surfaces represented discrete data points. in data, each sample stored separate data frame within list of data frames. here (small) example data:
df1 <- data.frame(x=c(2,2,2,3,3,3,4,4,4,5,5,5,6,6,6), y=c(1,2,3,1,2,3,1,2,3,1,2,3,1,2,3), z=c(0,2,0,4,6,7,3,2,1,2,7,8,9,4,2)) df2 <- data.frame(x=c(2,2,2,3,3,3,4,4,4,5,5,5,6,6,6), y=c(1,2,3,1,2,3,1,2,3,1,2,3,1,2,3), z=c(1,1,2,3,5,6,2,1,3,3,8,9,8,3,1)) df <- list(df1,df2)
answers similar questions either in other languages (matlab, python), or answers not contain useable script address problem (as here). can think of 2 acceptable ways estimate volume beneath each surface: 1) write out discretized version of simpson's rule function in r applied across list of data frames (df); 2) calculate arbitrary relationship between x, y, , z , use multivariate numerical integration find volume under surface (with functions simpson2d / quad2d in package pracma or adaptintegrate in cubature).
regarding first approach, formula composite simpson's rule (that use) here, due complexity, have been unsuccessful in writing working double summation function. in expression, i(lambda(em) lambda(ex)) equal z in above datasets @ each x,y grid point, , delta(em) , delta(ex) represent interval between x , y points.
the second approach extend approach found here multivariate spline fits , pass predicted z values function integration. here's have tried far approach:
require(pracma) df1.loess <- loess(z ~ x + y, data=df[[1]]) mod.fun <- function(x,y) predict(df1.loess, newdata=x,y) simpson2d(mod.fun, x=c(2,6), y=c(1,3))
but not yield useful results.
in reality, have list of 100 data frames individual samples, need able express solution series of lapply functions automate these calculations across data frames in list. example looks this:
require(akima) df.splines <- lapply(df, function(x,y,z) interp(x = "x", y = "y", z = "z", linear=f, nx=4, ny=2))
unfortunately, produces exception missing values , infs. i'm extremely open suggestions how implement 1 of these strategies, or utilize different (simpler?) approach. kriging function (like km in dicekriging package) produce better fit passed on numerical integration?
i assuming volume surface mesh defined connecting points via straight lines. can find volume beneath surface via
- triangular tessellation of
(x,y)
grid trianglest_i
areaa_i
- finding corresponding
z
valuesz_i
each of trianglest_i
- calculating volume
v_i
of truncated prisms (definedt_i
,z_i
) viav_i=a_i*sum(z_i)/3
(see https://en.wikipedia.org/wiki/prism_(geometry) , https://math.stackexchange.com/questions/2371139/volume-of-truncated-prism) - summing truncated prism volumes
v_i
keep in mind, however, volume depend on tessellation , tessellation not unique. problem not defined in sense not describe how 1 should interpolate between points. approach calculate volume have make additional assumptions.
going solution approach, points 1 , 2 can achieved via geometry
package. here code
library(geometry) getvolume=function(df) { #find triangular tesselation of (x,y) grid res=delaunayn(as.matrix(df[,-3]),full=true,options="qz") #calulates sum of truncated prism volumes sum(mapply(function(tripoints,a) a/3*sum(df[tripoints,"z"]), split.data.frame(res$tri,seq_along(res$areas)), res$areas)) } sapply(df,getvolume) #[1] 32.50000 30.33333
since it's hard check whether results consistent, here simple example know right answer. it's cube side length 2 have cut out wedge along x axis. cut-out region 1/4 of total volume.
cutoutcube=expand.grid(c(0,1,2),c(0,1,2)) colnames(cutoutcube)=c("x","y") cutoutcube$z=ifelse(cutoutcube$x==1,1,2) sapply(list(cutoutcube),getvolume) #[1] 6
that's correct since 2^3*(1-1/4)=6
.
another sanity check can performed calculating "complement" of volume w.r.t. simple cuboid z
values set max z
value (in case max(z)=9
in both cases). simple cuboid volumes 72 both of cases. not let's define complement surfaces , sum volume , complement volume
df1c=df1 df1c$z=max(df1c$z)-df1c$z df2c=df2 df2c$z=max(df2c$z)-df2c$z dfc=list(df1c,df2c) sapply(dfc,getvolume)+sapply(df,getvolume) #[1] 72 72
so volume , complement volume give right simple cuboid volume in both cases.
Comments
Post a Comment