I dati hanno due tendenze; come estrarre linee di tendenza indipendenti?


34

Ho una serie di dati che non sono ordinati in alcun modo particolare, ma quando tracciati hanno chiaramente due tendenze distinte. Una semplice regressione lineare non sarebbe davvero adeguata qui a causa della chiara distinzione tra le due serie. Esiste un modo semplice per ottenere le due linee di tendenza lineari indipendenti?

Per la cronaca sto usando Python e sono abbastanza a mio agio con la programmazione e l'analisi dei dati, incluso l'apprendimento automatico, ma sono disposto a passare a R se assolutamente necessario.

inserisci qui la descrizione dell'immagine


6
La migliore risposta che ho finora è di stamparlo su carta millimetrata e usare una matita, un righello e una calcolatrice ...
jbbiomed

Forse puoi calcolare le pendenze in coppia e raggrupparle in due "gruppi di pendenze". Tuttavia, ciò fallirà se si hanno due tendenze parallele.
Thomas Jungblut,

1
Non ho alcuna esperienza personale con esso, ma penso che vale la pena dare un'occhiata agli statsmodel . Statisticamente, una regressione lineare con un'interazione per gruppo sarebbe adeguata (a meno che tu non stia dicendo di avere dati non raggruppati, nel qual caso è un po 'più peloso ...)
Matt Parker,

1
Sfortunatamente non si tratta di dati di effetti ma di dati di utilizzo e chiaramente di utilizzo da due sistemi separati confusi nello stesso set di dati. Voglio essere in grado di descrivere i due modelli di utilizzo, ma non posso tornare indietro e ricordare i dati in quanto rappresentano circa 6 anni di informazioni raccolte da un cliente.
jbbiomed

2
Solo per essere sicuri: il tuo cliente non ha dati aggiuntivi che indichino quali misure provengono da quale popolazione? Questo è il 100% dei dati che tu o il tuo cliente avete o potete trovare. Inoltre, il 2012 sembra che la tua raccolta di dati sia andata in pezzi o che uno o entrambi i tuoi sistemi siano crollati. Mi chiedo se le linee di tendenza fino a quel momento contano molto.
Wayne,

Risposte:


30

Per risolvere il problema, un buon approccio consiste nel definire un modello probabilistico che corrisponda alle ipotesi sul set di dati. Nel tuo caso, probabilmente vuoi un mix di modelli di regressione lineare. È possibile creare un modello "miscela di regressori" simile a un modello di miscela gaussiana associando diversi punti dati a diversi componenti della miscela.

Ho incluso un codice per iniziare. Il codice implementa un algoritmo EM per una miscela di due regressori (dovrebbe essere relativamente facile estenderlo a miscele più grandi). Il codice sembra essere abbastanza robusto per set di dati casuali. Tuttavia, a differenza della regressione lineare, i modelli di miscela hanno obiettivi non convessi, quindi per un set di dati reale, potrebbe essere necessario eseguire alcune prove con diversi punti di partenza casuali.

import numpy as np
import matplotlib.pyplot as plt 
import scipy.linalg as lin

#generate some random data
N=100
x=np.random.rand(N,2)
x[:,1]=1

w=np.random.rand(2,2)
y=np.zeros(N)

n=int(np.random.rand()*N)
y[:n]=np.dot(x[:n,:],w[0,:])+np.random.normal(size=n)*.01
y[n:]=np.dot(x[n:,:],w[1,:])+np.random.normal(size=N-n)*.01


rx=np.ones( (100,2) )
r=np.arange(0,1,.01)
rx[:,0]=r

#plot the random dataset
plt.plot(x[:,0],y,'.b')
plt.plot(r,np.dot(rx,w[0,:]),':k',linewidth=2)
plt.plot(r,np.dot(rx,w[1,:]),':k',linewidth=2)

# regularization parameter for the regression weights
lam=.01

def em():
    # mixture weights
    rpi=np.zeros( (2) )+.5

    # expected mixture weights for each data point
    pi=np.zeros( (len(x),2) )+.5

    #the regression weights
    w1=np.random.rand(2)
    w2=np.random.rand(2)

    #precision term for the probability of the data under the regression function 
    eta=100

    for _ in xrange(100):
        if 0:
            plt.plot(r,np.dot(rx,w1),'-r',alpha=.5)
            plt.plot(r,np.dot(rx,w2),'-g',alpha=.5)

        #compute lhood for each data point
        err1=y-np.dot(x,w1)
        err2=y-np.dot(x,w2)
        prbs=np.zeros( (len(y),2) )
        prbs[:,0]=-.5*eta*err1**2
        prbs[:,1]=-.5*eta*err2**2

        #compute expected mixture weights
        pi=np.tile(rpi,(len(x),1))*np.exp(prbs)
        pi/=np.tile(np.sum(pi,1),(2,1)).T

        #max with respect to the mixture probabilities
        rpi=np.sum(pi,0)
        rpi/=np.sum(rpi)

        #max with respect to the regression weights
        pi1x=np.tile(pi[:,0],(2,1)).T*x
        xp1=np.dot(pi1x.T,x)+np.eye(2)*lam/eta
        yp1=np.dot(pi1x.T,y)
        w1=lin.solve(xp1,yp1)

        pi2x=np.tile(pi[:,1],(2,1)).T*x
        xp2=np.dot(pi2x.T,x)+np.eye(2)*lam/eta
        yp2=np.dot(pi[:,1]*y,x)
        w2=lin.solve(xp2,yp2)

        #max wrt the precision term
        eta=np.sum(pi)/np.sum(-prbs/eta*pi)

        #objective function - unstable as the pi's become concentrated on a single component
        obj=np.sum(prbs*pi)-np.sum(pi[pi>1e-50]*np.log(pi[pi>1e-50]))+np.sum(pi*np.log(np.tile(rpi,(len(x),1))))+np.log(eta)*np.sum(pi)
        print obj,eta,rpi,w1,w2

        try:
            if np.isnan(obj): break
            if np.abs(obj-oldobj)<1e-2: break
        except:
            pass

        oldobj=obj

    return w1,w2


#run the em algorithm and plot the solution
rw1,rw2=em()
plt.plot(r,np.dot(rx,rw1),'-r')
plt.plot(r,np.dot(rx,rw2),'-g')

plt.show()

25

Altrove in questo thread, user1149913 fornisce ottimi consigli (definire un modello probabilistico) e codice per un approccio potente (stima EM). Rimangono due questioni da affrontare:

  1. Come far fronte alle deviazioni dal modello probabilistico (che sono molto evidenti nei dati 2011-2012 e in qualche modo evidente nelle ondulazioni dei punti meno inclinati).

  2. Come identificare buoni valori iniziali per l'algoritmo EM (o qualsiasi altro algoritmo).

Per indirizzare il numero 2, considera l'utilizzo di una trasformazione di Hough . Questo è un algoritmo di rilevamento delle caratteristiche che, per trovare tratti lineari di caratteristiche, può essere calcolato in modo efficiente come trasformata di Radon .

xyx,ynella trasformazione di Hough. Quando le caratteristiche nella trama originale cadono lungo una linea comune, o abbastanza vicine a una, le raccolte di curve che producono nella trasformazione di Hough tendono ad avere un'intersezione comune corrispondente a quella linea comune. Trovando questi punti di massima intensità nella trasformazione di Hough, possiamo leggere buone soluzioni al problema originale.

Per iniziare con questi dati, ho prima ritagliato le cose ausiliarie (assi, segni di spunta ed etichette) e per buona misura ho ritagliato i punti ovviamente periferici in basso a destra e cosparso lungo l'asse inferiore. (Quando quella roba non viene ritagliata, la procedura funziona ancora bene, ma rileva anche gli assi, i frame, le sequenze lineari di tick, le sequenze lineari di etichette e persino i punti che si trovano sporadicamente sull'asse inferiore!)

img = Import["http://i.stack.imgur.com/SkEm3.png"]
i = ColorNegate[Binarize[img]]
crop2 = ImageCrop[ImageCrop[i, {694, 531}, {Left, Bottom}], {565, 467}, {Right, Top}]

(Questo e il resto del codice sono in Mathematica .)

Immagine ritagliata

Ad ogni punto in questa immagine corrisponde una ristretta gamma di curve nella trasformata di Hough, visibile qui. Sono onde sinusoidali:

hough2 = Radon[crop2, Method -> "Hough"]  // ImageAdjust

Trasformata

Ciò rende visivamente evidente il senso in cui la domanda è un problema di raggruppamento di linee : la trasformazione di Hough la riduce a un problema di raggruppamento puntuale , al quale possiamo applicare qualsiasi metodo di raggruppamento che ci piace.

In questo caso, il clustering è così chiaro che è sufficiente la semplice post-elaborazione della trasformazione di Hough. Per identificare le posizioni di maggiore intensità nella trasformazione, ho aumentato il contrasto e sfocato la trasformazione su un raggio di circa l'1%: è paragonabile ai diametri dei punti della trama nell'immagine originale.

blur = ImageAdjust[Blur[ImageAdjust[hough2, {1, 0}], 8]]

Trasformazione offuscata

Limitando il risultato lo si restringeva a due piccole chiazze i cui centroidi identificano ragionevolmente i punti di maggiore intensità: stimano le linee adattate.

comp = MorphologicalComponents[blur, 0.777]) // Colorize

0,777

Trasformazione binarizzata con soglia

Il lato sinistro dell'immagine corrisponde a una direzione di 0 gradi (orizzontale) e, osservando da sinistra a destra, tale angolo aumenta linearmente a 180 gradi. Interpolando, computo che i due blob sono centrati rispettivamente a 19 e 57,1 gradi. Possiamo anche leggere le intercettazioni dalle posizioni verticali delle chiazze. Questa informazione fornisce gli adattamenti iniziali:

width = ImageDimensions[blur][[1]];
slopes =  Module[{x, y, z}, ComponentMeasurements[comp, "Centroid"] /. 
          Rule[x_, {y_, z_}] :>  Round[((y - 1/2)/(width - 1))  180., 0.1]
  ]

{19., 57.1}

In modo simile si possono calcolare le intercettazioni corrispondenti a queste pendenze, dando questi accoppiamenti:

Linee aderenti

(La linea rossa corrisponde al puntino rosa nella figura precedente e la linea blu corrisponde alla goccia d'acqua più grande.)

In larga misura, questo approccio ha affrontato automaticamente il primo problema: le deviazioni dalla linearità macchiano i punti di maggiore intensità, ma in genere non li spostano molto. I punti francamente periferici contribuiranno al rumore di basso livello durante la trasformazione di Hough, che scomparirà durante le procedure di post-elaborazione.

A questo punto si possono fornire queste stime come valori iniziali per l'algoritmo EM o per un minimizzatore di probabilità (che, date buone stime, convergeranno rapidamente). Meglio, però, sarebbe usare un robusto stimatore della regressione come i minimi quadrati ripetutamente pesati . È in grado di fornire un peso di regressione in ogni punto. Pesi bassi indicano che un punto non "appartiene" a una linea. Sfruttare questi pesi, se lo si desidera, per assegnare ciascun punto alla propria linea. Quindi, dopo aver classificato i punti, è possibile utilizzare i minimi quadrati ordinari (o qualsiasi altra procedura di regressione) separatamente sui due gruppi di punti.


1
Le immagini dicono più di mille parole e tu ne hai 5. Questo è un lavoro incredibile da un rapido grafico che ho fatto solo per lo scopo di questa domanda! Complimenti!
jbbiomed

2
La trasformazione di Hough è ampiamente utilizzata nel campo di Computer Vision per identificare linee rette in un'immagine. Perché non dovrebbe essere usato anche nelle statistiche? ;)
Lucas Reis,

Questa è una domanda interessante, @Lucas. In molte applicazioni statistiche, tuttavia, esiste un'asimmetria tra ilX e yvariabili: una è vista come misurata con precisione e l'altra è vista come soggetta a variazioni casuali. Per tali applicazioni, i metodi di elaborazione delle immagini non daranno i risultati giusti (e in molte applicazioni non daranno risultati utili). Ma di tanto in tanto potrebbe esserci una proficua sovrapposizione tra i due campi: che credo sia il punto che intendevi fare.
whuber

Sì. Immagina, ad esempio, la quantità di valori anomali coinvolti nel confronto tra due immagini per rilevare se provengono dallo stesso soggetto. E, soprattutto, immagina di doverlo fare in tempo reale. La "velocità" è un fattore molto importante in Computer Vision e non così importante in Statistica.
Lucas Reis,

@RoyalTS Grazie per aver sottolineato la necessità di una correzione per uno degli snippet di codice. Quando ho scoperto che la tua modifica suggerita era stata respinta (correttamente, perché non era del tutto corretto, ma non importa: sono grato che tu abbia notato che c'era un errore). L'ho risolto rimuovendo il riferimento a rotation, che inizialmente era impostato su zero e quindi non faceva alcuna differenza.
whuber

15

Ho trovato questa domanda collegata ad un'altra domanda . In realtà ho fatto ricerche accademiche su questo tipo di problema. Per favore, controlla la mia risposta "Meno radice quadrata"? Un metodo di adattamento con minimi multipli per maggiori dettagli.

L'approccio basato sulla trasformazione di Hough di whuber è un'ottima soluzione per scenari semplici come quello che hai fornito. Ho lavorato su scenari con dati più complessi, come questo:

problema di associazione dei dati - set di dati candy

I miei coautori e io abbiamo indicato questo problema come "associazione di dati". Quando si tenta di risolverlo, il problema principale è in genere combinatorio a causa della quantità esponenziale di possibili combinazioni di dati.

Abbiamo una pubblicazione " Sovrapposizioni di miscele di processi gaussiani per il problema dell'associazione dei dati " in cui abbiamo affrontato il problema generale delle curve N con una tecnica iterativa, dando ottimi risultati. Puoi trovare il codice Matlab collegato nel documento.

[Aggiornamento] Un'implementazione Python della tecnica OMGP è disponibile nella libreria GPClust .

Ho un altro documento in cui abbiamo risolto il problema per ottenere un problema di ottimizzazione convessa, ma non è stato ancora accettato per la pubblicazione. È specifico per 2 curve, quindi funzionerebbe perfettamente sui tuoi dati. Fatemi sapere se siete interessati.


1
Sono triste nel vedere che in due anni nessun altro ha votato per questa risposta originale e preziosa. Nel frattempo, è stato accettato l'ultimo documento che hai citato?
whuber

1
Il documento è stato effettivamente accettato, solo pochi mesi fa. Puoi scaricarlo qui gtas.unican.es/pub/378 . Questo è in realtà un problema piuttosto raro (che potrebbe spiegare la sua mancanza di popolarità), ma siamo comunque riusciti a trovare alcune applicazioni interessanti. Se vuoi, dai un'occhiata agli esperimenti alla fine del documento.
Steven,

2

user1149913 ha una risposta eccellente (+1), ma mi sembra che la tua raccolta di dati sia andata in frantumi alla fine del 2011, quindi dovresti tagliare quella parte dei tuoi dati e quindi eseguire comunque alcune cose con diverse casuali coefficienti di partenza per vedere cosa ottieni.

Un modo semplice per fare le cose sarebbe quello di separare i tuoi dati in due insiemi a occhio, quindi utilizzare qualsiasi tecnica di modello lineare a cui sei abituato. In R, sarebbe la lmfunzione.

O si adatta a due linee a occhio. In R useresti ablineper fare questo.

I dati sono confusi, hanno valori anomali e alla fine cadono a pezzi, ma l'occhio ha due linee abbastanza ovvie, quindi non sono sicuro che ne valga la pena un metodo sofisticato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.