Jak uzyskać region elipsy z dwuwymiarowych normalnych danych rozproszonych?

13

Mam dane, które wyglądają następująco:

Postać

Próbowałem zastosować rozkład normalny (szacowanie gęstości jądra działa lepiej, ale nie potrzebuję tak dużej precyzji) i działa całkiem dobrze. Wykres gęstości tworzy elipsę.

Potrzebuję uzyskać tę funkcję elipsy, aby zdecydować, czy punkt leży w regionie elipsy, czy nie. Jak to zrobić?

Kod R lub Mathematica są mile widziane.

matejuh
źródło

Odpowiedzi:

18

Corsario zapewnia dobre rozwiązanie w komentarzu: użyj funkcji gęstości jądra, aby sprawdzić, czy ma zostać włączony do zestawu poziomów.

Inna interpretacja tego pytania polega na tym, że wymaga procedury testowania włączenia w elipsy utworzone przez dwuwymiarowe normalne przybliżenie danych. Na początek wygenerujmy dane, które wyglądają jak na ilustracji w pytaniu:

library(mvtnorm) # References rmvnorm()
set.seed(17)
p <- rmvnorm(1000, c(250000, 20000), matrix(c(100000^2, 22000^2, 22000^2, 6000^2),2,2))

Elipsy są określane przez pierwszy i drugi moment danych:

center <- apply(p, 2, mean)
sigma <- cov(p)

Formuła wymaga odwrócenia macierzy wariancji-kowariancji:

sigma.inv = solve(sigma, matrix(c(1,0,0,1),2,2))

Funkcja „wysokości” elipsy jest ujemna logarytmu dwuwymiarowej normalnej gęstości :

ellipse <- function(s,t) {u<-c(s,t)-center; u %*% sigma.inv %*% u / 2}

(Zignorowałem stałą addytywną równą log(2)πdet(Σ)) .)

Aby to przetestować , narysujmy niektóre jego kontury. Wymaga to wygenerowania siatki punktów w kierunkach xiy:

n <- 50
x <- (0:(n-1)) * (500000/(n-1))
y <- (0:(n-1)) * (50000/(n-1))

Oblicz funkcję wysokości na tej siatce i wykreśl ją:

z <- mapply(ellipse, as.vector(rep(x,n)), as.vector(outer(rep(0,n), y, `+`)))
plot(p, pch=20, xlim=c(0,500000), ylim=c(0,50000), xlab="Packets", ylab="Flows")
contour(x,y,matrix(z,n,n), levels=(0:10), col = terrain.colors(11), add=TRUE)

Działka konturowa

Najwyraźniej to działa. Dlatego test mający na celu ustalenie, czy punkt leży wewnątrz konturu eliptycznego na poziomie jest(s,t)do

ellipse(s,t) <= c

Mathematica wykonuje zadanie w ten sam sposób: oblicz macierz wariancji-kowariancji danych, odwróć to, skonstruuj ellipsefunkcję i gotowe.

Whuber
źródło
Dziękuję wszystkim, szczególnie @whuber. Właśnie tego potrzebuję.
matejuh
Btw. czy jest jakieś proste rozwiązanie dla konturów szacowania gęstości jądra? Ponieważ jeśli chcę być bardziej rygorystyczny, moje dane wyglądają następująco: github.com/matejuh/doschecker_wiki_images/raw/master/… odpowiednio. github.com/matejuh/doschecker_wiki_images/raw/master/…
matejuh
Nie mogę znaleźć prostego rozwiązania w R. Zastanów się nad użyciem funkcji „SmoothKernelDistribution” Mathematica 8.
whuber
2
Czy poziomy odpowiadają poziomowi ufności? Nie sądzę. Jak mogę to zrobić, proszę?
matejuh
To wymaga nowego pytania, ponieważ musisz sprecyzować, czego szukasz zaufania, i - sądząc z twoich wykresów - istnieją obawy, czy takie elipsy to przede wszystkim odpowiednie opisy danych.
whuber
10

Fabuła jest prosta z ellipse()funkcją mixtoolspakietu dla R:

library(mixtools)
library(mvtnorm) 
set.seed(17)
p <- rmvnorm(1000, c(250000, 20000), matrix(c(100000^2, 22000^2, 22000^2, 6000^2),2,2))
plot(p, pch=20, xlim=c(0,500000), ylim=c(0,50000), xlab="Packets", ylab="Flows")
ellipse(mu=colMeans(p), sigma=cov(p), alpha = .05, npoints = 250, col="red") 

wprowadź opis zdjęcia tutaj

Stéphane Laurent
źródło
5

Pierwsze podejście

Możesz spróbować tego podejścia w Mathematica.

Wygenerujmy dane dwuwymiarowe:

data = Table[RandomVariate[BinormalDistribution[{50, 50}, {5, 10}, .8]], {1000}];

Następnie musimy załadować ten pakiet:

Needs["MultivariateStatistics`"]

I teraz:

ellPar=EllipsoidQuantile[data, {0.9}]

daje wynik, który definiuje 90% elipsę ufności. Wartości otrzymane z tego wyniku są w następującym formacie:

{Ellipsoid[{x1, x2}, {r1, r2}, {{d1, d2}, {d3, d4}}]}

x1 i x2 określają punkt, w którym elipsa w środku, r1 i r2 określają promienie półosi, a d1, d2, d3 i d4 określają kierunek wyrównania.

Możesz również wykreślić to:

Show[{ListPlot[data, PlotRange -> {{0, 100}, {0, 100}}, AspectRatio -> 1],  Graphics[EllipsoidQuantile[data, 0.9]]}]

Ogólna parametryczna forma elipsy to:

ell[t_, xc_, yc_, a_, b_, angle_] := {xc + a Cos[t] Cos[angle] - b Sin[t] Sin[angle],
    yc + a Cos[t] Sin[angle] + b Sin[t] Cos[angle]}

Możesz wykreślić to w ten sposób:

ParametricPlot[
    ell[t, ellPar[[1, 1, 1]], ellPar[[1, 1, 2]], ellPar[[1, 2, 1]], ellPar[[1, 2, 2]],
    ArcTan[ellPar[[1, 3, 1, 2]]/ellPar[[1, 3, 1, 1]]]], {t, 0, 2 \[Pi]},
    PlotRange -> {{0, 100}, {0, 100}}]

Możesz wykonać sprawdzenie w oparciu o informacje czysto geometryczne: jeśli odległość euklidesowa między środkiem elipsy (ellPar [[1,1]]) a punktem danych jest większa niż odległość między środkiem elipsy a granicą elipsa (oczywiście w tym samym kierunku, w którym znajduje się twój punkt), wtedy ten punkt danych znajduje się poza elipsą.

Drugie podejście

To podejście opiera się na płynnej dystrybucji jądra.

Oto niektóre dane dystrybuowane w podobny sposób jak Twoje dane:

data1 = RandomVariate[BinormalDistribution[{.3, .7}, {.2, .3}, .8], 500];
data2 = RandomVariate[BinormalDistribution[{.6, .3}, {.4, .15}, .8], 500];
data = Partition[Flatten[Join[{data1, data2}]], 2];

Uzyskujemy płynny rozkład jądra na tych wartościach danych:

skd = SmoothKernelDistribution[data];

Otrzymujemy wynik liczbowy dla każdego punktu danych:

eval = Table[{data[[i]], PDF[skd, data[[i]]]}, {i, Length[data]}];

Naprawiamy próg i wybieramy wszystkie dane, które są wyższe niż ten próg:

threshold = 1.2;
dataIn = Select[eval, #1[[2]] > threshold &][[All, 1]];

Tutaj otrzymujemy dane spoza regionu:

dataOut = Complement[data, dataIn];

A teraz możemy wykreślić wszystkie dane:

Show[ContourPlot[Evaluate@PDF[skd, {x, y}], {x, 0, 1}, {y, 0, 1}, PlotRange -> {{0, 1}, {0, 1}}, PlotPoints -> 50],
ListPlot[dataIn, PlotStyle -> Darker[Green]],
ListPlot[dataOut, PlotStyle -> Red]]

Punkty w kolorze zielonym są powyżej progu, a punkty w kolorze czerwonym są poniżej progu.

wprowadź opis zdjęcia tutaj

VLC
źródło
Dzięki, twoje drugie podejście bardzo mi pomaga z dystrybucją jądra. Jestem programistą, nie statystyki i jestem nowicjuszem w Mathmatica i R, więc bardzo doceniam twoją pomoc. W drugim podejściu jasne jest dla mnie, jak przetestować jeden punkt, w którym leży. Ale jak to zrobić w pierwszym podejściu? Przypuszczam, że muszę porównać mój punkt widzenia z definicją elipsoidy. Czy możesz podać, w jaki sposób? Teraz mam nadzieję, że w R są takie same definicje, ponieważ muszę go używać w RinRuby ...
matejuh
@ matejuh Właśnie dodałem kilka kolejnych wierszy o pierwszym podejściu, które może skierować cię do rozwiązania.
VLC
2

ellipseFunkcji w ellipsepakiecie dla R generuje te elipsy (a właściwie wielobok zbliżony do elipsy). Możesz użyć tej elipsy.

ellipseχ2)

Greg Snow
źródło
1

Znalazłem odpowiedź na: /programming/2397097/how-can-a-data-ellipse-be-superimposed-on-a-ggplot2-scatterplot

#bootstrap
set.seed(101)
n <- 1000
x <- rnorm(n, mean=2)
y <- 1.5 + 0.4*x + rnorm(n)
df <- data.frame(x=x, y=y, group="A")
x <- rnorm(n, mean=2)
y <- 1.5*x + 0.4 + rnorm(n)
df <- rbind(df, data.frame(x=x, y=y, group="B"))

#calculating ellipses
library(ellipse)
df_ell <- data.frame()
for(g in levels(df$group)){
df_ell <- rbind(df_ell, cbind(as.data.frame(with(df[df$group==g,], ellipse(cor(x, y), 
                                         scale=c(sd(x),sd(y)), 
                                         centre=c(mean(x),mean(y))))),group=g))
}
#drawing
library(ggplot2)
p <- ggplot(data=df, aes(x=x, y=y,colour=group)) + geom_point(size=1.5, alpha=.6) +
  geom_path(data=df_ell, aes(x=x, y=y,colour=group), size=1, linetype=2)

wprowadź opis zdjęcia tutaj

Guy L.
źródło