RobTillaart / Statistic

Statistic library for Arduino includes sum, average, variance and std deviation
MIT License
27 stars 11 forks source link

Missing running statistic feature #20

Closed ghost closed 1 year ago

ghost commented 1 year ago

Similar thing can done with runningAverage library but there is issue i need to calculate only std_dev but without getAverage() in config getStandardDeviation() alway return NaN. In current Statistic library need reset() to calculate stream data std_dev.

float std_dev;
if (stats.count == 10) {
  std_dev = stats.unbiased_stdev();
  stats.reset();
}

I need to calculate only standard deviation of over last 10 input .

RobTillaart commented 1 year ago

Thanks for the issue.

The - https://github.com/RobTillaart/RunningAverage - library should not give NaN after 10 inputs normally.

When RA.count == 1 ==> you get NaN (by definition).

When the count is above 1 (till 10 ) you should get a number larger or equal to 0. By making the size of the RA object 10 you can get the stddev after every add(). Note: to calculate the stddev the average is needed internally.

The way you calculate stddev over the last 10 inputs works for this library, but it is not meant for a ```running stddev()**.

Does this help? Or can you elaborate on your question?

ghost commented 1 year ago

I need to need clarify everything in similar manner, both Statistic and runningAverage working as aspected. But for std_dev i prefer runningAverage because Statistic currently not have FILO algorithm.

RunningAverage stats(5);

This work

ESP_LOGD("Stats", "%f %f %f", stats.getStandardDeviation(), stats.getAverage(), stats.getMaxInBuffer());
[output] Stats 0.01 0.25 0.26

This not work

ESP_LOGD("Stats", "%f %f", stats.getStandardDeviation(), stats.getMaxInBuffer());
[output] Stats nan 0.26
ghost commented 1 year ago

Forgot runningAverage library, the main purpose of issue is to implement specific window size FILO algorithm in RobTillaart/Statistic.

RobTillaart commented 1 year ago

The purpose of Statistic is calculate stats over a large set of data, not to support a small window, or subset of the data. In fact Statistic does not store individual data internally, so it cannot calculate the stddev over the last n elements of that large set.

RobTillaart commented 1 year ago

I cannot explain immediately why printing 3 parameters work while printing two does not. Need to dive into the code to see if I can reproduce.

RobTillaart commented 1 year ago

Can you run this sketch and post the output? (15 lines will do)

//    FILE: ra_test.ino
//  AUTHOR: Rob Tillaart
// PUPROSE: test

#include "RunningAverage.h"

RunningAverage myRA(10);
int samples = 0;

void setup(void)
{
  Serial.begin(115200);
  Serial.println("Demo RunningAverage lib");
  Serial.print("Version: ");
  Serial.println(RUNNINGAVERAGE_LIB_VERSION);

  myRA.clear(); //  explicitly start clean

  for (int i = 0; i < 10; i++)
  {
    myRA.add(i * 0.01 + 1 );
    Serial.print(myRA.getCount());
    Serial.print("\t");
    Serial.print(myRA.getAverage(), 3);
    Serial.print("\t");
    Serial.print(myRA.getStandardDeviation(), 3);
    Serial.print("\t");
    Serial.println(myRA.getMaxInBuffer(), 3);
  }
}

void loop(void)
{
  myRA.add(random(100) * 0.01 + 1 );

  Serial.print(myRA.getCount());
  Serial.print("\t");
  Serial.print(myRA.getAverage(), 3);
  Serial.print("\t");
  Serial.print(myRA.getStandardDeviation(), 3);
  Serial.print("\t");
  Serial.println(myRA.getMaxInBuffer(), 3);
  delay(1000);
}

// -- END OF FILE --
RobTillaart commented 1 year ago

Then the same for this sketch

//    FILE: ra_test.ino
//  AUTHOR: Rob Tillaart
// PUPROSE: test

#include "RunningAverage.h"

RunningAverage myRA(10);
int samples = 0;

void setup(void)
{
  Serial.begin(115200);
  Serial.println("Demo RunningAverage lib");
  Serial.print("Version: ");
  Serial.println(RUNNINGAVERAGE_LIB_VERSION);

  myRA.clear(); //  explicitly start clean

  for (int i = 0; i < 10; i++)
  {
    myRA.add(i * 0.01 + 1 );
    Serial.print(myRA.getStandardDeviation(), 3);
    Serial.print("\t");
    Serial.println(myRA.getMaxInBuffer(), 3);
  }
}

void loop(void)
{
  myRA.add(random(100) * 0.01 + 1 );
  Serial.print(myRA.getStandardDeviation(), 3);
  Serial.print("\t");
  Serial.println(myRA.getMaxInBuffer(), 3);
  delay(1000);
}

// -- END OF FILE --
ghost commented 1 year ago

Esphome need some modification after that i provide you output result.

RobTillaart commented 1 year ago

Both sketches return an NAN on the first line when count == 1.

ESP_LOGD("Stats", "%f %f", stats.getStandardDeviation(), stats.getMaxInBuffer());

This line has too little information to reproduce.

The best way to analyze is to provide a minimal sketch that shows the problem.

RobTillaart commented 1 year ago

Esphome need some modification after that i provide you output result.

I have no experience with Esphome, however on an ESP32 both libraries works as intended.

ESP_LOGD("Stats", "%f %f", stats.getStandardDeviation(), stats.getMaxInBuffer());

what happens if you assign the values to a variable first?

volatile float std = stats.getStandardDeviation();  //  made volatile to suppress compiler optimizations.
volatile float mib = stats.getMaxInBuffer());
ESP_LOGD("Stats", "%f %f", std, mid);
RobTillaart commented 1 year ago

ESP_LOGD("Stats", "%f %f", stats.getStandardDeviation(), stats.getMaxInBuffer()); [output] Stats nan 0.26

Also try

ESP_LOGD("Stats", "%f %f", stats.getStandardDeviation(), stats.getCount());

please post output

ghost commented 1 year ago

Can you run this sketch and post the output? (15 lines will do)

//    FILE: ra_test.ino
//  AUTHOR: Rob Tillaart
// PUPROSE: test

#include "RunningAverage.h"

RunningAverage myRA(10);
int samples = 0;

void setup(void)
{
  Serial.begin(115200);
  Serial.println("Demo RunningAverage lib");
  Serial.print("Version: ");
  Serial.println(RUNNINGAVERAGE_LIB_VERSION);

  myRA.clear(); //  explicitly start clean

  for (int i = 0; i < 10; i++)
  {
    myRA.add(i * 0.01 + 1 );
    Serial.print(myRA.getCount());
    Serial.print("\t");
    Serial.print(myRA.getAverage(), 3);
    Serial.print("\t");
    Serial.print(myRA.getStandardDeviation(), 3);
    Serial.print("\t");
    Serial.println(myRA.getMaxInBuffer(), 3);
  }
}

void loop(void)
{
  myRA.add(random(100) * 0.01 + 1 );

  Serial.print(myRA.getCount());
  Serial.print("\t");
  Serial.print(myRA.getAverage(), 3);
  Serial.print("\t");
  Serial.print(myRA.getStandardDeviation(), 3);
  Serial.print("\t");
  Serial.println(myRA.getMaxInBuffer(), 3);
  delay(1000);
}

// -- END OF FILE --
[01:08:39][D][VERSION:167]: 0.4.3
[01:08:39][D][Test:172]: -0.000 1.000 nan 1.000
[01:08:39][D][Test:172]: 0.000 1.005 0.007 1.010
[01:08:39][D][Test:172]: 0.000 1.010 0.010 1.020
[01:08:39][D][Test:172]: 0.000 1.015 0.013 1.030
[01:08:39][D][Test:172]: 0.000 1.020 0.016 1.040
[01:08:39][D][Test:172]: 0.000 1.025 0.019 1.050
[01:08:40][D][Test:172]: 0.000 1.030 0.022 1.060
[01:08:40][D][Test:172]: 0.000 1.035 0.024 1.070
[01:08:40][D][Test:172]: 0.000 1.040 0.027 1.080
[01:08:40][D][Test:172]: 0.000 1.045 0.030 1.090
[01:08:40][D][Test:175]: 0.000 1.049 0.026 1.090
ghost commented 1 year ago

Then the same for this sketch

//    FILE: ra_test.ino
//  AUTHOR: Rob Tillaart
// PUPROSE: test

#include "RunningAverage.h"

RunningAverage myRA(10);
int samples = 0;

void setup(void)
{
  Serial.begin(115200);
  Serial.println("Demo RunningAverage lib");
  Serial.print("Version: ");
  Serial.println(RUNNINGAVERAGE_LIB_VERSION);

  myRA.clear(); //  explicitly start clean

  for (int i = 0; i < 10; i++)
  {
    myRA.add(i * 0.01 + 1 );
    Serial.print(myRA.getStandardDeviation(), 3);
    Serial.print("\t");
    Serial.println(myRA.getMaxInBuffer(), 3);
  }
}

void loop(void)
{
  myRA.add(random(100) * 0.01 + 1 );
  Serial.print(myRA.getStandardDeviation(), 3);
  Serial.print("\t");
  Serial.println(myRA.getMaxInBuffer(), 3);
  delay(1000);
}

// -- END OF FILE --
[01:10:53][D][VERSION:167]: 0.4.3
[01:10:53][D][Test:172]: nan 1.000
[01:10:53][D][Test:172]: 0.007 1.010
[01:10:53][D][Test:172]: 0.010 1.020
[01:10:53][D][Test:172]: 0.013 1.030
[01:10:53][D][Test:172]: 0.016 1.040
[01:10:53][D][Test:172]: 0.019 1.050
[01:10:53][D][Test:172]: 0.022 1.060
[01:10:53][D][Test:172]: 0.024 1.070
[01:10:53][D][Test:172]: 0.027 1.080
[01:10:53][D][Test:172]: 0.030 1.090
[01:10:53][D][Test:175]: 0.182 1.620
ghost commented 1 year ago

ESP_LOGD("Stats", "%f %f", stats.getStandardDeviation(), stats.getMaxInBuffer()); [output] Stats nan 0.26

Also try

ESP_LOGD("Stats", "%f %f", stats.getStandardDeviation(), stats.getCount());

please post output The issue is so wierd... The Got distance: 0.84 m are output values provided to runningAverage.


[01:14:25][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.84 m
[01:14:25][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.83 m
[01:14:25][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.71 m
[01:14:25][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.54 m
[01:14:25][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.44 m
[01:14:25][D][sensor:093]: 'Ping Sensor': Sending state 0.84845 m with 3 decimals of accuracy
[01:14:25][D][Stats:072]: nan -5486124068793694774077193376186634490938385057925026367753787985335901674201208705258107428552676834622700901594284127560530828493162262565205269470179449410856558896118393471584152670587577693885047222545890015691339878440394965043791461872188297303737712264679017607178892013979610612203518046997839872.00
[01:14:25][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.68 m
[01:14:25][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.63 m
[01:14:25][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.63 m
[01:14:25][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.47 m
[01:14:26][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.81 m
[01:14:26][D][sensor:093]: 'Ping Sensor': Sending state 0.81734 m with 3 decimals of accuracy
[01:14:26][D][Stats:072]: nan -5486124068793694774077193376186634490938385057925026367753787985335901674201208705258107428552676834622700901594284127560530828493162262565205269470179449410856558896118393471584152670587577693885047222545890015691339878440394965043791461872188297303737712264679017607178892013979610612203518046997839872.00
ghost commented 1 year ago

Issue solves when using this

ESP_LOGD("Stats", "%.2f %.2f %.2f", stats.getStandardDeviation(), stats.getCount(), stats.getAverage());
[01:20:26][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.65 m
[01:20:26][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.58 m
[01:20:26][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.56 m
[01:20:26][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.63 m
[01:20:26][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.60 m
[01:20:26][D][sensor:093]: 'Ping Sensor': Sending state 0.65478 m with 3 decimals of accuracy
[01:20:26][D][Stats:072]: 0.05 0.00 0.70
[01:20:26][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.63 m
[01:20:26][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.80 m
[01:20:26][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.61 m
[01:20:26][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.77 m
[01:20:27][D][ultrasonic.sensor:040]: 'Ping Sensor' - Got distance: 0.57 m
[01:20:27][D][sensor:093]: 'Ping Sensor': Sending state 0.81205 m with 3 decimals of accuracy
[01:20:27][D][Stats:072]: 0.07 0.00 0.70
ghost commented 1 year ago

It's better to move the issue to it's origin library.