La mia motivazione per la sfida di questo mese è stata quella di farti pensare oltre la tradizionale definizione di valori anomali come solo osservazioni che cadono lontano dal centro dei dati (rispetto alla quantità di variabilità dei dati). In generale, possiamo considerare un outlier come un punto dati non rappresentativo, qualcosa che non appartiene al resto dei dati. Qui sta la vera sfida: come definiamo rappresentativo? L'approccio che fornisco di seguito è la mia interpretazione del problema.
L'algoritmo seguente raggruppa le osservazioni in insiemi simili e dissimili in base alle distanze tra i punti. A differenza dell'approccio agglomerativo standard al clustering, iniziare con un gruppo. Definire un punto di taglio come il valore a metà strada tra le osservazioni più distanti, a condizione che la loro distanza superi il triplo della differenza tra i 75 ns quantile e mediana di tutte le distanze diverse da zero (differenziale della distanza). Se non ci sono punti di taglio, fermati. In caso contrario, sostituire il gruppo esistente con i due nuovi gruppi. Ripetere la procedura di cui sopra con ogni nuovo gruppo, utilizzando la distanza distribuita dal nuovo gruppo o dall'intero set di dati, a seconda di quale sia maggiore. Continua finché nessun gruppo contiene un punto di taglio.
findOutliers = Function({vals},{Default Local},
q1 = 3; //Spread Multiplier
q2 = 0.75; //Quantile to define upper bound of spread
distSpread = 0;
nMissing = 0;
tmpTbl = New Table("tmp",New Column("C1",Values(vals)),Private);
sumTbl = tmpTbl << Summary(Group(:C1),Private);
vals = Column(sumTbl,1) << Get Values;
freqs = Column(sumTbl,2) << Get Values;
If(N Row(Loc(vals)) == 1, Return([]));
Close(tmpTbl,No Save);
If(Is Missing(vals[1]),
nMissing = freqs[1];
freqs = freqs[2::N Row(vals)];
vals = vals[2::N Row(vals)];
);
vals = Sort Ascending(vals[Loc Nonmissing(vals)]);
groups = Eval List({vals});
cutPoints = [];
While(N Items(groups) > 0,
newGroups = {};
For Each({nextVals},groups,
distances = nextVals[2::N Row(nextVals)] - nextVals[1::(N Row(nextVals)-1)];
distSpread = Max(distSpread,Median(distances[Loc(distances>0)]) + q1*(Quantile(q2,distances[Loc(distances>0)]) - Median(distances[Loc(distances>0)])));
maxDist = Maximum(distances);
If(maxDist>distSpread,
cutID = Loc(distances==maxDist)[1];
cutPoints |/= (nextVals[cutID+1]+nextVals[cutID])/2;
If(cutID == 1,
Insert Into(newGroups,nextVals[2::N Row(nextVals)]),//ELSE
If(cutID == N Row(distances),
Insert Into(newGroups,nextVals[1::cutID]),
//ELSE
Insert Into(newGroups,nextVals[1::cutID]);
Insert Into(newGroups,nextVals[(cutID+1)::N Row(nextVals)]);
)
)
);
);
groups = newGroups;
);
cutPoints = Sort Ascending(cutPoints);
cutPoints;
);
Per iniziare, è necessario un po' di pulizia per bypassare i dati con un singolo valore. Sulla base dei risultati di Sfida 9 , sto usando Riepilogo per trovare contemporaneamente i valori univoci nei dati e per ordinarli. Le distanze si trovano spostando il vettore di uno e prendendo la loro differenza. L'algoritmo procede raccogliendo i punti di taglio ed espellendo i gruppi senza punti di taglio. Questo continua fino a quando non rimangono più gruppi.
L'algoritmo è abbastanza veloce, richiede circa 1,6 secondi per elaborare tutte le colonne di dati nel Sonda dati di esempio Tabella dati sulla mia macchina. Nella parte superiore dell'algoritmo, ho aggiunto due variabili per consentirti di modificare il moltiplicatore 3x e il 75 ns quantile. Nello script allegato di seguito, Sfida 11 Screening Outlier , ho incluso alcune finestre aggiuntive per fornire un po' di interfaccia, consentendo la selezione delle colonne e la generazione di una finestra di report contenente informazioni di riepilogo e istogrammi con i punti di taglio disegnati come linee di riferimento.
Prenderò un po' di pausa dal JMP Challenge per lavorare su un altro progetto di scripting. Metterò insieme istruzioni scritte e video per piccoli progetti per aiutare gli utenti nuovi e intermedi a migliorare le proprie competenze JSL. Mi concentrerò su operazioni comuni come mettere insieme tabelle, accedere a dati da più fonti, creare report personalizzati e così via. Cercherò anche di lavorare su cose più avanzate, come la gestione delle espressioni e la corrispondenza dei modelli. Se c'è qualcosa che ritieni possa valere la pena, faccelo sapere pubblicandolo qui o inviandomi un messaggio o un'e-mail della Community ( don.mccormack@jmp.com ).