Escludere un valore dell'array che si discosta troppo dalla media?

Salve a tutti.
Come da titolo volevo sapere se è possibile escludere un valore da un array che si discosti troppo dalle media.
Avendo questo codice:

int valori[5];
int somma;
float media;

void loop()
{

for (int i=0; i<5; i++)
{
  valori[i] =barometer.GetPressure();
  }
somma=0;

for (int i=0; i<5; i++)
{
 somma= somma+valori[i];
}
media= somma/5;

}

Adesso se ho una pressione sempre di 950 e poi mi fa 1200, la media viene 1000 e 1200 è il valore più lontano dalla media quindi lo escludo… l’unica problema che poi dovrei dividere nuovamente per 4 però… sapete dirmi qualcosa a riguardo?

Avere statistiche affidabili con 5 valori è già difficile, figuriamoci con 4.
Hai pochi valori, per questo motivo appena uno dei valori si discosta dalla media degli altri valori, influenza in modo pesante la media complessiva.
Se proprio vuoi usarne così pochi, prima di calcolare la media dovresti fare una verifica dei valori all’interno dell’array.
Per ogni valore dell’array, calcola la media degli ALTRI valori, escludendo quello che stai verificando.
Se si discosta troppo, lo elimini dalla matrice sostituendolo con uno zero.
Se ne trovi due da scartare, lascia perdere e ripeti tutto. Tre sarebbero pochi.
Se ne hai scartato uno, ovviamente poi dividi la somma per 4 invece che per 5.
Però quando diciamo “se si discosta troppo”, cosa intendiamo per “troppo”? Di quanto si può scostare?
In questo caso dovresti fissare una soglia tu. Come? In base a cosa? Si può anche fare, ma…

Se invece vuoi fare le cose per bene, allora acquisisci almeno 10 valori, sarebbe meglio 20 o anche 30.
A questo punto, l’influenza sulla media di un singolo valore sballato sarebbe meno rilevante.
Puoi anche calcolarti la deviazione standard che ti darebbe un numero valido per dire se una misura si discosta “troppo” dalla media e fare comunque la verifica sugli elementi dell’array (eliminando quelli che si discostano più di due volte la deviazione standard), a questo punto anche scartando tre o quattro valori, avresti una misura decente.

Diciamo che tutti i valori che sono oltre 10 o minori di 10 rispetto alla media li voglio scartare! Se acquisisco 30 valori come dici e voglio scartare di quei valori quelli che appunto superano quelle soglie come devo fare?

Vuoi quindi evitare l'uso della deviazione standard ed usare una tua soglia prefissata.

Metodo 1

Acquisisci i 30 valori e mettili nell'array.

Calcola la media.

Confronta ogni elemento dell'array con la media, se differisce più di 10, allora metti l'elemento a zero ed incrementa una variabile che ti conta i valori che hai scartato.

Alla fine fai la somma di tutti gli elementi e dividi per (30 - quanti ne hai scartati). Ovviamente se scartati = 0 la media è quella che avevi già calcolato prima. Usando 30 elementi, vedrai che l'influenza sulla media di un valore anomalo è molto limitata, a meno che l'anomalia sia eccessiva.

Metodo 2

Acquisisci i 30 valori e mettili nell'array. Somma tutti i valori, ottenendo somma1.

Per ogni elemento, calcola la media DEGLI ALTRI 29 elementi che è (somma1 - elemento) / 29 e fai il confronto, se la differenza è maggiore di 10, allora metti l'elemento a zero e incrementa la variabile che tiene il conto di quanti ne hai scartati.

Alla fine, fai la somma di tutti gli elementi della nuova matrice e dividila per (30 - quanti ne hai scartati)

paulus1969: Vuoi quindi evitare l'uso della deviazione standard ed usare una tua soglia prefissata.

Metodo 1

Acquisisci i 30 valori e mettili nell'array.

Calcola la media.

Confronta ogni elemento dell'array con la media, se differisce più di 10, allora metti l'elemento a zero ed incrementa una variabile che ti conta i valori che hai scartato.

Alla fine fai la somma di tutti gli elementi e dividi per (30 - quanti ne hai scartati). Ovviamente se scartati = 0 la media è quella che avevi già calcolato prima. Usando 30 elementi, vedrai che l'influenza sulla media di un valore anomalo è molto limitata, a meno che l'anomalia sia eccessiva.

Metodo 2

Acquisisci i 30 valori e mettili nell'array. Somma tutti i valori, ottenendo somma1.

Per ogni elemento, calcola la media DEGLI ALTRI 29 elementi che è (somma1 - elemento) / 29 e fai il confronto, se la differenza è maggiore di 10, allora metti l'elemento a zero e incrementa la variabile che tiene il conto di quanti ne hai scartati.

Alla fine, fai la somma di tutti gli elementi della nuova matrice e dividila per (30 - quanti ne hai scartati)

Grazie mille! Provo subito ;) Già comunque mettendo 30 valori già è molto difficile che mi segna qualche anomalia come dici. In più ho messo degli if che se mi segna un valore eccessivo, lo metto a un valore anomalo ma non eccessivo, così da influire ancora di meno sulla media dei 30 valori.

Senza usare 2000 mila if come faccio a confrontare in maniera semplice?

ciclo for....

Tipo? Ho scelto il metodo 1

tipo questo?

Quindi così va bene?

long currentPressureP;

for (int w=0; w<30; w++)
{
  currentPressureP = barometer.GetPressure();
   valori[w] =currentPressureP;
   if (valori[w]-media>10);
   {
    valori[w]=0;
    u++;
    }
    if (valori[w]-media<-10);
   {
    valori[w]=0;
    p++;
    }
  }
somma=0;

for (int w=0; w<30; w++)
{
 somma= somma+valori[w];
}
number=p+u;
media= somma/(30-number);

EDIT: Mi sa che ho fatto un errore grossolano… visto che valori[w] comprende tutti i valori…
ma come faccio allora nel ciclo for a confrontare ogni valore con la media?

tipo
for (valori[0]; valori[29])

poi però che metto dentro?

Mi sono inceppato su questa cosa, scusate se può sembrare banale!!

Prima acquisisci tutti i valori e li metti nell’array, con un primo ciclo for.
Altrimenti, senza prima avere tutti i valori, è inutile fare la media. Mente ci sei, fai la somma.

somma = 0;
for (int w=0; w<30; w++)
 {
  currentPressureP = barometer.GetPressure();
  valori[w] =currentPressureP;
  somma = somma + currentPressureP;
 }

Dopo calcoli la media = somma/30

Poi confronti i valori con la media, ad uno ad uno, tramite un for

scarto = 10;
scartati=0;
for (int w=0; w<30; w++)
 {
  if abs(valori[w]-media) > scarto
     {
      somma=somma-valori[w]; 
      valori[w]=0;
       scartati++;
     }
  
 }

//quindi calcoli la nuova media se ne hai scartati

if (scartati > 0)
      {
         media = somma / (30 - scartati)
      }

l’ho scritto di corsa
controllalo

Grazie mille :wink:

Adesso provo questo, ho sistemato qualcosa :wink:

long currentPressureP;
somma=0;
for (int w=0; w<30; w++)
{
  currentPressureP = barometer.GetPressure();
   valori[w] =currentPressureP;
    somma= somma+currentPressureP;
  }
media= somma/30;
scarto = 10;
scarto2 = -10;
scartati=0;
for (int w=0; w<30; w++)
{
if (abs(valori[w]-media)>scarto || abs(valori[w]-media)<scarto2)
{ 
     valori[w]=0;
     somma=somma-valori[w]; 
     scartati++;
       }
}

if (scartati > 0)
      {
         media = somma / (30 - scartati);
      }

Niente da fare mi segna valori sballati ancora di più, tipo una volta 1200, poi 1175, poi 1000 e così via... forse c'è qualche errore che non vedo

mostra come hai dichiarato il vettore "valori"

poi

 valori[w]=0;
 somma=somma-valori[w];

non ti sembra che quelle due linee di codice siano da invertire?

Così pare che faccia :wink:

float valori[30];
float somma;
float media;
int w;
int scarto;
int scarto2;
int scartati;

long currentPressureP;
somma=0;
for (int w=0; w<30; w++)
{
  currentPressureP = barometer.GetPressure();
   valori[w] =currentPressureP;
    somma= somma+currentPressureP;
  }
media= somma/30;
scarto = 300;
scarto2 = -300;
scartati=0;
for (int w=0; w<30; w++)
{
if (abs(valori[w]-media)>scarto || abs(valori[w]-media)<scarto2)
{ 
     somma=somma-valori[w]; 
     valori[w]=0;
     scartati++;
       }
}

if (scartati > 0)
      {
         media = somma / (30 - scartati);
      }

pressionelivellodelmare=media+5240;
pressionelivellodelmarehPa=pressionelivellodelmare/100;

Grazie comunque!
Devo solo lasciarlo in esecuzione e vedere se mi segna valori troppo sballati.
Lo scarto 1 e lo scarto 2 li ho messi a 300 comunque, ovvero 3hPa
quindi se la pressione, tra quei valori che rileva, varia di 3hPa in circa 1 minuto, (tempo che impiega a fare il for 30 volte) allora esclude quel valore. E’ impossibile che in un minuto la pressione vari all’improvviso di 3hPa. Solo che andava bene per 10 minuti, ora mi ha segnato un valore sballato e l’ha messo come pressione massima… un valore impossibile, era a 1015 hPa e segna la pressione massima 1200… lo avrebbe dovuto scartare
Forse devo mettere il calcolo della pressionelivellodelmare dentro il ciclo for?
Adesso provo a rimettere lo scarto molto più basso

Nemmeno mettendolo più basso, a sto punto non so più che fare per risolvere sto problema, ditemi voi…
cioè ci sono delle interferenze: metti caso la pressione è sempre 1015 per 10 minuti, poi all’improvviso arriva un picco di 1200, oppure segna 700 e così facendo mi aggiorna il valore della pressione massima o minima con quei valori assolutamente falsi…

#define LETTURE 30
#define SCARTO  300

#define MAX(A,B) ((A>B)?A:B)
#define MIN(A,B) ((A<B)?A:B)

static uint32_t current = 0;

uint8_t errata(uint32_t pressione, uint32_t attesa)
{
    uint32_t absolute = MAX(pressione, attesa) - MIN(pressione, attesa);
    return absolute > SCARTO ? 1 : 0;
}

uint32_t getPressure()
{
    uint32_t pressioni[LETTURE];
    uint32_t totale;
    uint32_t media;
    uint8_t k;  
    uint8_t i;
    
    for (i = 0, totale = 0; i < LETTURE; ++i)
    {
        pressioni[i] = barometer.GetPressure();
        totale += pressioni[i];
    }
    
    media = totale / LETTURE;
    
    for (i = 0, k = 0; i < LETTURE; ++i)
    {
        if ( errata(pressioni[i], media) )
        { 
            totale -= pressioni[i]; 
            ++k;
        }
    }

    return totale / (LETTURE - k);
}

void setup()
{
    current = getPressure();
    current += getPressure();
    current += getPressure();
    current += getPressure();
    current >>= 2; 
}

void loop()
{
    uint8_t retry = 255;
    uint32_t tmpPressure;
    
    do
    {
        tmpPressure = getPressure();
    }while( errata(tmpPressure, current) && --retry > 0);
    
    current = tmpPressure;
    
}

ho ripulito, devi avere piu cura del tuo codice.

Spiega meglio quello che accade, cosa stai facendo, perchè e cosa vorresti ottenere.

Innanzitutto grazie della sistemata! Non sono ancora a livelli da fare una pulizia simile! Comunque quello che accade é semplice, se per esempio la pressione in hPa é 1015, a volte e non so perché mi succede che all improvviso passi a 1200 o 700 senza ragione e cosi facendo, quei valori mi vanno a segnare le minime o le massime definitive... visto che non possono essere ritoccate pressioni tanto sballate :) Quello che voglio fare é semplicemente evitare ció ;) Tramite sw voglio eliminare quei valori sballati, ecco perché la media e cosi via!

Se hai guardato bene il codice avrai notato che nella funzione loop(), prima di assegnare il nuovo valore a quello corrente controlla che la media non sia fallita, in caso contrario ripete la lettura per massimo 255 volte e poi comunque assegna. Questo doppio controllo lo puoi modificare come piu ti piace, ad esempio se il valore si discosta da quello attuale potresti riprovare dopo S secondi per massimo N tentativi.

Sarebbe comodo sapere quanti valori sbaglia, posta un log che ottieni.

Si ho visto :wink: Grazie mille comunque, adesso lo provo così!
In ogni caso qui c’è il LOG del mio datalog, ovvero del valore finale di pressione, non del barometer.getpressure, però se vuoi darci un’occhiata eccolo, sbaglia poco alla fine, almeno qui
Sono circa i valori ogni ciclo di loop, ovvero 2 secondi in questo caso

asadas.txt (21.7 KB)

bhe niente di grave, con il codice proposto dovresti risolvere. cerca di evitare i float/double, portano solo ad errori.