KDE adaptativo
O KDE adaptativo é uma melhoria de um KDE normal. Seleccionar uma janela que suavize todas as partes da curva de estimativas de um forma adequada é difícil quando as amostras contêm zonas com densidades muito diferentes. Frequentemente, é necessário um compromisso na suavidade da estimativa resultante em áreas de baixa densidade. O KDE Adaptativo tenta resolver o problema permitindo que a dimensão da janela varie em função da densidade da amostra. Em áreas de maior concentração de pontos a janela é menor, e vice-versa. Como a densidade é uma incógnita, o primeiro passo é realizar uma estimativa piloto usando uma função KDE normal. Em seguida calcula-se a média geométrica das estimativas, e com este valor calcula-se um factor de janela lambda para cada vértice que é de seguida usado para calcular estimativas ajustadas à densidade observada localmente. Estes passos podem ser sumarizados da seguinte forma (Silverman 1986):
- Encontrar uma estimativa piloto kde(x) que satisfaça kde(Xi)>0 para todos i;
- Calcular a média geométrica de todas as estimativas kde(Xi);
- Calcular o factor de janela local lambda para cada i;
- Calcular o KDE adaptativo
A definição do Grasshopper é a seguinte:
Passo 1 (Step 1): Para o primeiro passo pode ser usada qualquer função kde. No entanto, deve ser assegurado que todos os vértices da mesh têm estimativas com valores superiores a 0, ou seja, o domínio das estimativas de KDE não pode começar ou incluir 0 e tem que ser positivo.
Passo 2 (Step 2): O Grasshopper tem um componente para o cálculo da média geométrica . Contudo, não é adequado para números tão pequenos como os que são resultado da função kde. O código C# abaixo usa um método diferente de calcular a média geométrica que evita os problemas da multiplicação de números muito pequenos.
private void RunScript(List x, ref object A) { double tempv = 0.0; double a = 0.0; for (int i = 0; i < x.Count; i++) tempv += Math.Log(x[i]); a = tempv / x.Count; if(Double.IsNaN(a)) return; if(Double.IsInfinity(a)) return; A = Math.Pow(Math.E, a); }
Passo 3 (Step 3): O código abaixo permite calcular o valor de lambda. Necessita de um parâmetro a, um valor entre 0 e 1. Um valor adequado é 0,5. Se for usado 0, o efeito de lambda é anulado e a função passa a ser um KDE normal.
private void RunScript(List x, double g, double a, ref object A) { if (g <= 0) return; if (a < 0 || a > 1) return; if (x.Count == 0) return; List lambda = new List(); for(int i = 0; i < x.Count; i++) lambda.Add(Math.Pow((x[i] / g), -a)); A = lambda; }
Passo 4 (Step 4): O último passo implementa a função KDE adaptativo que calcula as estimativas ajustadas à densidade real.
private void RunScript(Mesh mesh, List pts, double window, List lambda, ref object A) { var rtnlist = new List(); for(int i = 0; i < mesh.Vertices.Count; i++) { double l = lambda[i]; double tempval = 0.0; double kde = 0.0; Point3d v = new Point3d(mesh.Vertices[i]); for(int j = pts.Count - 1; j > 0; j--) { var pt = pts[j]; Vector3d vt = new Vector3d(v - pt); tempval += kernel(vt / (window * l)) / ((window * window) * (l * l)); } kde = 1.0 / pts.Count * tempval; rtnlist.Add(kde); } A = rtnlist; } public double kernel(Vector3d input) { //Epanechnikov Kernel double k = Vector3d.Multiply(input, input); if ( k < 1) { return 3 * (1 / Math.PI) * (1 - k) * (1 - k); }else{ return 0; } }