查找多边形区域:垂直和水平几何约束下的积分

问题描述 投票:2回答:1

我正在尝试计算零度以下和曲线下的区域。我的曲线有离散的xy值,看起来像下面的例子。

y <- c(-1, 5, 2, 3.5, 1, 5.5, -2, 3, -1)
plot(1:length(y), y, type = "l")
abline(h = 0)

我正在尝试计算受垂直和水平几何约束的区域:

  • (垂直)曲线下但高于零;
  • (水平)两个相邻的局部最小值之间。

也就是说,我需要下图中多边形A,B,C和D的区域。

trying to calculate areas of A, B, C and D each

我现在正在努力解决两件事:

  1. 我使用:which(diff(sign(diff(y))) == 2) + 1确定了局部最小值的位置指数,但是这并没有给出C的上x值或者D的x值。如何获得曲线与零相交的点?
  2. 我想如果我能得到1)正确的零点以上的局部最小值列表,2)那些交叉点为零,3)正确的局部最大值列表高于零,我知道A,B,C和D的所有边界点,所以计算他们的区域是可能的。但这在R中编码似乎并不简单。这是解决我的问题的最简单方法,还是有更好的方法?
r polygon area integral numerical-integration
1个回答
2
投票
## (x, y) data
y <- c(-1, 5, 2, 3.5, 1, 5.5, -2, 3, -1)
x <- 1:length(y)

Analytical method

您可以通过两个步骤完成所需的计算:

  1. 每个线性段上的分段积分。如果你只想整合零以上的比例就没有困难(详见下文);
  2. 对于A,B,C和D区域适当地分段聚合结果(详见下文)。

步骤1:零度积分,零度以上

如果有n(x,y)数据,则会有(n - 1)段。将(xl, yl)表示为该段的左侧点,并将(xr, yr)表示为正确的点。

  1. 如果(yl < 0) && (yr < 0),整个部分低于零;
  2. 如果(yl > 0) && (yr > 0),整个部分高于零;
  3. 如果(yl < 0) && (yr > 0),该部分正在增加,越过零;
  4. 如果(yl > 0) && (yr < 0),该段正在减少,过零。

在案例3和4中,将(xm, 0)表示为交叉点。 xm很容易确定。线段的等式是

y = yl + (yr - yl) * (x - xl) / (xr - xl)

通过将y设置为0,你得到了

xm = xl - yl * (xr - xl) / (yr - yl)

由于您希望整合每个细分的零以上比例,因此我们针对每种情况:

  1. 积分为0;
  2. 该区域是梯形,积分是(yl + yr) * (xr - xl) / 2;
  3. 该地区是(xm, 0)(xr, yr)之间的三角形;积分是yr * (xr - xm) / 2;
  4. 该地区是(xl, yl)(xm, 0)之间的三角形;积分是yl * (xm - xl) / 2

由于您最终希望将计算应用于长向量,因此我将在Rcpp函数中呈现计算。

library(Rcpp)

cppFunction('NumericVector foo_cpp (NumericVector x, NumericVector y) {
  int n_segments = x.size() - 1;
  NumericVector integral(n_segments);
  double xl, xr, yl, yr, xm; int i;
  for (i = 0; i < n_segments; i++) {
    xl = x[i]; xr = x[i + 1];
    yl = y[i]; yr = y[i + 1];
    if (yl < 0 && yr < 0) integral[i] = 0.0;
    if (yl > 0 && yr > 0) integral[i] = 0.5 * (yl + yr) * (xr - xl);
    if (yl < 0 && yr > 0) {
      xm = xl - yl * (xr - xl) / (yr - yl);
      integral[i] = 0.5 * yr * (xr - xm);
      }
    if (yl > 0 && yr < 0) {
      xm = xl - yl * (xr - xl) / (yr - yl);
      integral[i] = 0.5 * yl * (xm - xl);
      }
    }
  return integral;
  }')

z <- foo_cpp(x, y)
#[1] 2.083333 3.500000 2.750000 2.250000 3.250000 2.016667 0.900000 1.125000

我懒得做进一步的代码优化。它的速度足以满足您的实际应用。

第2步:聚合

实际上,您通过局部最小值将段切割成块,并且旨在计算每个块上的积分。

当地最小值的位置指数(正如您在问题中得出的那样):

which(diff(sign(diff(y))) == 2) + 1
#[1] 3 5 7

这意味着段应按断点分割:

b <- which(diff(sign(diff(y))) == 2)
#[1] 2 4 6

那是,

## number of segments per chunk
n_chunks <- length(x) - 1
n_segments_per_chunk <- diff(c(0, b, n_chunks))
#[1] 2 2 2 2

## grouping index for each chunk
grp <- rep.int(seq_along(n_segments_per_chunk), n_segments_per_chunk)
#[1] 1 1 2 2 3 3 4 4

所以A,B,C和D的区域是:

sapply(split(z, grp), sum)
#       1        2        3        4 
#5.583333 5.000000 5.266667 2.025000

Numerical method

## original linear interpolation function
f <- approxfun(x, y)

## a function zeroing out below-zero part of `f`
g <- function (x) {
  fx <- f(x)
  ifelse(fx > 0, fx, 0)
  }

## local minima
x_minima <- x[which(diff(sign(diff(y))) == 2) + 1]

## break points for numerical integration
xx <- c(x[1], x_minima, x[length(x)])

## integration will happen on:
# cbind(xx[-length(xx)], xx[-1])
#     [,1] [,2]
#[1,]    1    3  ## A
#[2,]    3    5  ## B
#[3,]    5    7  ## C
#[4,]    7    9  ## D

## use `mapply`
mapply(function (lwr, upr) integrate(g, lower = lwr, upper = upr)$value,
       xx[-length(xx)], xx[-1])
#[1] 5.583333 5.000000 5.266679 2.025000
© www.soinside.com 2019 - 2024. All rights reserved.