Faisons d'abord une analyse.
Supposons que dans le polygone sa densité de probabilité soit la fonction proportionnelle Alors la constante de proportionnalité est l'inverse de l'intégrale de sur le polygone,Pp(x,y).p
μ0,0(P)=∬Pp(x,y)dxdy.
Le barycentre du polygone est le point des coordonnées moyennes, calculées comme leurs premiers moments. Le premier est
μ1,0(P)=1μ0,0(P)∬Pxp(x,y)dxdy.
Le tenseur inertiel peut être représenté comme le réseau symétrique de seconds moments calculé après la translation du polygone pour mettre son barycentre à l'origine: c'est-à-dire la matrice des seconds moments centraux
μ′k,l(P)=1μ0,0(P)∬P(x−μ1,0(P))k(y−μ0,1(P))lp(x,y)dxdy
où varie de à à Le tenseur lui-même - alias matrice de covariance - est(k,l)(2,0)(1,1)(0,2).
I(P)=(μ′2,0(P)μ′1,1(P)μ′1,1(P)μ′0,2(P)).
Une PCA de donne les axes principaux de ce sont les vecteurs propres unitaires mis à l'échelle par leurs valeurs propres.I(P)P:
Ensuite, découvrons comment faire les calculs. Parce que le polygone est présenté comme une séquence de sommets décrivant sa frontière orientée il est naturel d'invoquer∂P,
Théorème de Green: où est une forme unique définie dans un voisinage de et∬Pdω=∮∂Pω
ω=M(x,y)dx+N(x,y)dyPdω=(∂∂xN(x,y)−∂∂yM(x,y))dxdy.
Par exemple, avec et une densité constante ( c'est -à- dire uniforme) nous pouvons (par inspection) sélectionner l'un des nombreux telles quedω=xkyldxdyp,ω(x,y)=−1l+1xkyl+1dx.
Le fait est que l'intégrale de contour suit les segments de ligne déterminés par la séquence de sommets. Tout segment de ligne allant du sommet au sommet peut être paramétré par une variable réelle sous la formeuvt
t→u+tw
où est la direction normale d'unité de àLes valeurs de vont donc de à Sous ce paramétrage, et sont des fonctions linéaires de et et sont des fonctions linéaires de Ainsi l'intégrande de l'intégrale de contour sur chaque bord devient une fonction polynomiale de qui est facilement évaluée pour les petits etw∝v−uuv.t0|v−u|.xytdxdydt.t,kl.
La mise en œuvre de cette analyse est aussi simple que le codage de ses composants. Au niveau le plus bas, nous aurons besoin d'une fonction pour intégrer une forme polynomiale sur un segment de ligne. Les fonctions de niveau supérieur les agrégeront pour calculer les moments bruts et centraux pour obtenir le barycentre et le tenseur inertiel, et enfin nous pouvons opérer sur ce tenseur pour trouver les axes principaux (qui sont ses vecteurs propres à l'échelle). Le R
code ci-dessous effectue ce travail. Il ne fait aucune prétention d'efficacité: il ne vise qu'à illustrer l'application pratique de l'analyse qui précède. Chaque fonction est simple et les conventions de dénomination sont parallèles à celles de l'analyse.
Le code comprend une procédure pour générer des polygones fermés valides, simplement connectés et non auto-entrecroisés (en déformant aléatoirement des points le long d'un cercle et en incluant le sommet de départ comme point final afin de créer une boucle fermée). Voici quelques instructions pour tracer le polygone, afficher ses sommets, jouxter le barycentre et tracer les axes principaux en rouge (le plus grand) et bleu (le plus petit), créant un système de coordonnées orienté positivement centré sur le polygone.
#
# 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")