Hagamos un análisis primero.
Suponga que dentro del polígono su densidad de probabilidad es la función proporcional Entonces la constante de proporcionalidad es la inversa de la integral de sobre el polígono,Pp(x,y).p
μ0,0(P)=∬Pp(x,y)dxdy.
El baricentro del polígono es el punto de coordenadas promedio, calculado como sus primeros momentos. El primero es
μ1,0(P)=1μ0,0(P)∬Pxp(x,y)dxdy.
El tensor inercial se puede representar como la matriz simétrica de segundos momentos calculados después de traducir el polígono para colocar su baricentro en el origen: es decir, la matriz de segundos momentos centrales
μ′k,l(P)=1μ0,0(P)∬P(x−μ1,0(P))k(y−μ0,1(P))lp(x,y)dxdy
donde van de a a El tensor en sí, también conocido como matriz de covarianza, es(k,l)(2,0)(1,1)(0,2).
I(P)=(μ′2,0(P)μ′1,1(P)μ′1,1(P)μ′0,2(P)).
Un PCA de produce los ejes principales de estos son los vectores propios unitarios escalados por sus valores propios.I(P)P:
A continuación, veamos cómo hacer los cálculos. Debido a que el polígono se presenta como una secuencia de vértices que describe su límite orientado es natural invocar∂P,
Teorema de Green: where es una forma definida en una vecindad de y∬Pdω=∮∂Pω
ω=M(x,y)dx+N(x,y)dyPdω=(∂∂xN(x,y)−∂∂yM(x,y))dxdy.
Por ejemplo, con y densidad constante ( es decir , uniforme) podemos (por inspección) seleccionar uno de los muchos soluciones, comodω=xkyldxdyp,ω(x,y)=−1l+1xkyl+1dx.
El punto de esto es que la integral de contorno sigue los segmentos de línea determinados por la secuencia de vértices. Cualquier segmento de línea desde el vértice al vértice puede parametrizarse mediante una variable real en la formauvt
t→u+tw
donde es la dirección normal de la unidad de aPor lo tanto, los valores de varían de a Bajo esta parametrización, e son funciones lineales de y y son funciones lineales de Por lo tanto, el integrando de la integral de contorno sobre cada borde se convierte en una función polinómica de que se evalúa fácilmente para los pequeños yw∝v−uuv.t0|v−u|.xytdxdydt.t,kl.
Implementar este análisis es tan sencillo como codificar sus componentes. En el nivel más bajo necesitaremos una función para integrar un polinomio de una forma sobre un segmento de línea. Las funciones de nivel superior los agregarán para calcular los momentos sin procesar y centrales para obtener el baricentro y el tensor inercial, y finalmente podemos operar en ese tensor para encontrar los ejes principales (que son sus vectores propios escalados). El R
siguiente código realiza este trabajo. No tiene pretensiones de eficiencia: solo pretende ilustrar la aplicación práctica del análisis anterior. Cada función es sencilla y las convenciones de denominación son paralelas a las del análisis.
Se incluye en el código un procedimiento para generar polígonos cerrados, simplemente conectados, no auto-intersectantes (deformando al azar los puntos a lo largo de un círculo e incluyendo el vértice inicial como su punto final para crear un bucle cerrado). A continuación se presentan algunas declaraciones para trazar el polígono, mostrar sus vértices, unir el baricentro y trazar los ejes principales en rojo (el más grande) y azul (el más pequeño), creando un sistema de coordenadas centrado positivamente centrado en el polígono.
#
# Integrate a monomial one-form x^k*y^l*dx along the line segment given as an
# origin, unit direction vector, and distance.
#
lintegrate <- function(k, l, origin, normal, distance) {
# Binomial theorem expansion of (u + tw)^k
expand <- function(k, u, w) {
i <- seq_len(k+1)-1
u^i * w^rev(i) * choose(k,i)
}
# Construction of the product of two polynomials times a constant.
omega <- normal[1] * convolve(rev(expand(k, origin[1], normal[1])),
expand(l, origin[2], normal[2]),
type="open")
# Integrate the resulting polynomial from 0 to `distance`.
sum(omega * distance^seq_along(omega) / seq_along(omega))
}
#
# Integrate monomials along a piecewise linear path given as a sequence of
# (x,y) vertices.
#
cintegrate <- function(xy, k, l) {
n <- dim(xy)[1]-1 # Number of edges
sum(sapply(1:n, function(i) {
dv <- xy[i+1,] - xy[i,] # The direction vector
lambda <- sum(dv * dv)
if (isTRUE(all.equal(lambda, 0.0))) {
0.0
} else {
lambda <- sqrt(lambda) # Length of the direction vector
-lintegrate(k, l+1, xy[i,], dv/lambda, lambda) / (l+1)
}
}))
}
#
# Compute moments of inertia.
#
inertia <- function(xy) {
mass <- cintegrate(xy, 0, 0)
barycenter = c(cintegrate(xy, 1, 0), cintegrate(xy, 0, 1)) / mass
uv <- t(t(xy) - barycenter) # Recenter the polygon to obtain central moments
i <- matrix(0.0, 2, 2)
i[1,1] <- cintegrate(uv, 2, 0)
i[1,2] <- i[2,1] <- cintegrate(uv, 1, 1)
i[2,2] <- cintegrate(uv, 0, 2)
list(Mass=mass,
Barycenter=barycenter,
Inertia=i / mass)
}
#
# Find principal axes of an inertial tensor.
#
principal.axes <- function(i.xy) {
obj <- eigen(i.xy)
t(t(obj$vectors) * obj$values)
}
#
# Construct a polygon.
#
circle <- t(sapply(seq(0, 2*pi, length.out=11), function(a) c(cos(a), sin(a))))
set.seed(17)
radii <- (1 + rgamma(dim(circle)[1]-1, 3, 3))
radii <- c(radii, radii[1]) # Closes the loop
xy <- circle * radii
#
# Compute principal axes.
#
i.xy <- inertia(xy)
axes <- principal.axes(i.xy$Inertia)
sign <- sign(det(axes))
#
# Plot barycenter and principal axes.
#
plot(xy, bty="n", xaxt="n", yaxt="n", asp=1, xlab="x", ylab="y",
main="A random polygon\nand its principal axes", cex.main=0.75)
polygon(xy, col="#e0e0e080")
arrows(rep(i.xy$Barycenter[1], 2),
rep(i.xy$Barycenter[2], 2),
-axes[1,] + i.xy$Barycenter[1], # The -signs make the first axis ..
-axes[2,]*sign + i.xy$Barycenter[2],# .. point to the right or down.
length=0.1, angle=15, col=c("#e02020", "#4040c0"), lwd=2)
points(matrix(i.xy$Barycenter, 1, 2), pch=21, bg="#404040")