Vamos fazer algumas análises primeiro.
Suponha que dentro do polígono sua densidade de probabilidade seja a função proporcional Então a constante de proporcionalidade é o inverso da integral de sobre o polígono,Pp(x,y).p
μ0,0(P)=∬Pp(x,y)dxdy.
O baricentro do polígono é o ponto das coordenadas médias, computadas como seus primeiros momentos. O primeiro é
μ1,0(P)=1μ0,0(P)∬Pxp(x,y)dxdy.
O tensor inercial pode ser representado como a matriz simétrica de segundos momentos calculada após a tradução do polígono para colocar seu baricentro na origem: ou seja, a matriz dos segundos momentos centrais
μ′k,l(P)=1μ0,0(P)∬P(x−μ1,0(P))k(y−μ0,1(P))lp(x,y)dxdy
onde varia de a a O próprio tensor - também conhecido como matriz de covariância - é(k,l)(2,0)(1,1)(0,2).
I(P)=(μ′2,0(P)μ′1,1(P)μ′1,1(P)μ′0,2(P)).
Um PCA de produz os eixos principais de esses são os autovetores unitários dimensionados por seus autovalores.I(P)P :P:
Em seguida, vamos descobrir como fazer os cálculos. Como o polígono é apresentado como uma sequência de vértices que descreve seu limite orientado é natural invocar∂P,
Teorema de Green: onde é uma forma única definida em uma vizinhança de e∬Pdω=∮∂Pω
ω=M(x,y)dx+N(x,y)dyPdω=(∂∂xN(x,y)−∂∂yM(x,y))dxdy.
Por exemplo, com densidade constante ( ou seja , uniforme) podemos (por inspeção) selecionar um dos muitos soluções, comodω=xkyldxdyp,ω(x,y)=−1l+1xkyl+1dx.
O ponto disso é que a integral do contorno segue os segmentos de linha determinados pela sequência de vértices. Qualquer segmento de linha do vértice ao vértice pode ser parametrizado por uma variável real no formatouvt
t→u+tw
onde é a direção normal da unidade de paraOs valores de portanto, variam de a Sob este parametrização e são funções lineares de e e são funções lineares do Assim, o integrando da integral de contorno sobre cada aresta se torna uma função polinomial de que é facilmente avaliada para pequeno ew∝v−uuv.t0|v−u|.xytdxdydt.t,kl.
A implementação dessa análise é tão direta quanto a codificação de seus componentes. No nível mais baixo, precisaremos de uma função para integrar uma forma polinomial sobre um segmento de linha. Funções de nível superior as agregam para calcular os momentos brutos e centrais para obter o baricentro e o tensor inercial, e finalmente podemos operar nesse tensor para encontrar os eixos principais (que são seus autovetores em escala). O R
código abaixo executa este trabalho. Não faz pretensões de eficiência: destina-se apenas a ilustrar a aplicação prática da análise anterior. Cada função é direta e as convenções de nomenclatura são paralelas às da análise.
Incluído no código está um procedimento para gerar polígonos válidos, fechados, simplesmente interceptados e sem auto-interseção (deformando pontos aleatoriamente ao longo de um círculo e incluindo o vértice inicial como seu ponto final para criar um loop fechado). A seguir, algumas instruções para plotar o polígono, exibir seus vértices, unir o baricentro e plotar os eixos principais em vermelho (maior) e azul (menor), criando um sistema de coordenadas orientadas positivamente para o 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")