Neues Feature: Durchschnitt, Median, Differenz, prozentualer Anstieg

Begonnen von Damian, 12 Januar 2019, 23:18:58

Vorheriges Thema - Nächstes Thema

Damian

Zitat von: Christoph Morrison am 16 Januar 2019, 18:08:54
Hallo Damian,

ohne diesen Thread vorher gelesen zu haben, hatte ich mir just heute DOIF um den Median erweitert, einen Patch findest hier.

Gruß
Christoph

Ja, das ist eine gute Sache - kann ich glatt übernehmen. Allerdings hast du es in der Aggregationsfunktion eingebaut.

Hier geht es um die Auswertung vergangener X-Werte eines Readings. Dazu müssen die Werte des jeweiligen Readings in einer internen Queue gesammelt werden.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Christoph Morrison

Zitat von: Damian am 16 Januar 2019, 18:19:10
Ja, das ist eine gute Sache - kann ich glatt übernehmen. Allerdings hast du es in der Aggregationsfunktion eingebaut.
Hier geht es um die Auswertung vergangener X-Werte eines Readings. Dazu müssen die Werte des jeweiligen Readings in einer internen Queue gesammelt werden.

Ich weiß, ich wollte das auch in der Aggregatsfunktion haben (um Temperaturwerte zu glätten) und kannte den Thread bis eben nicht. Die Berechnung des Median könntest du aber in eine Subroutine auslagern und dann auch bei den vergangenen Werten nutzen.

Damian

Zitat von: Christoph Morrison am 16 Januar 2019, 18:31:49
Ich weiß, ich wollte das auch in der Aggregatsfunktion haben (um Temperaturwerte zu glätten) und kannte den Thread bis eben nicht. Die Berechnung des Median könntest du aber in eine Subroutine auslagern und dann auch bei den vergangenen Werten nutzen.

Klar, den Median wollte ich sowieso einbauen.

Wo hast du denn deine Temperaturwerte zum Auswerten über die Aggregatsfunktion abgelegt?
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Christoph Morrison

Zitat von: Damian am 16 Januar 2019, 18:34:19
Klar, den Median wollte ich sowieso einbauen.

Wo hast du denn deine Temperaturwerte zum Auswerten über die Aggregatsfunktion abgelegt?

Ich hab mehrere Temperaturdevices auf meinem Grundstück, die jeweils ein Reading temperature haben. Darin ist die Temperatur ohne Gradzeichen abgelegt. Die hole ich mir alle zusammen und berechne den Medianwert, da gelegentlich mal eines nicht aktualisiert wird und ein arithmetisches Mittel dann sehr stark abweichende Werte liefern würde. Hier mal der Code in meiner Testumgebung:


define dummy.0 dummy
define dummy.1 dummy
define dummy.2 dummy
define dummy.4 dummy

setstate dummy.0 1
setstate dummy.0 2019-01-16 14:36:43 state 1

setstate dummy.1 2.5
setstate dummy.1 2019-01-16 14:40:04 state 2.5

setstate dummy.2 3
setstate dummy.2 2019-01-15 21:13:59 state 3

setstate dummy.4 4
setstate dummy.4 2019-01-16 15:48:43 state 4


Und die DOIFs mit denen ich getest habe:

define doif.0 DOIF ##
attr doif.0 state [#median:"dummy.*":state]
attr doif.0 verbose 5

define doif.1 DOIF ##
attr doif.1 state [@median:"dummy.*":state]
attr doif.1 verbose 5

setstate doif.0 2.75
setstate doif.0 2019-01-16 15:48:43 state 2.75

setstate doif.1 dummy.0,dummy.1,dummy.2,dummy.4
setstate doif.1 2019-01-16 15:48:43 state dummy.0,dummy.1,dummy.2,dummy.4


sash.sc

#34
pah hat doch mal eine subroutine geschrieben.....

Gesendet von meinem E6653 mit Tapatalk

sub movingAverage($$$){
   my ($name,$reading,$avtime) = @_;
   my $hash = $defs{$name};
   my @new = my ($val,$time) = (ReadingsVal($name,$reading,undef),ReadingsTimestamp($name,$reading,undef));
   my ($cyear, $cmonth, $cday, $chour, $cmin, $csec) = $time =~ /(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2}):(\d{2})/;
   my $ctime = $csec+60*$cmin+3600*$chour;
   my $num;
   my $arr;
   #-- initialize if requested
   if( ($avtime eq "-1") ){
     $hash->{helper}{history}=undef;
   }
   #-- test for existence
   if( !$hash->{helper}{history}){
      #Log 1,"ARRAY CREATED";
      push(@{$hash->{helper}{history}},\@new);
      $num = 1;
      $arr=\@{$hash->{helper}{history}};
   } else {
      $num = int(@{$hash->{helper}{history}});
      $arr=\@{$hash->{helper}{history}};
      my $starttime = $arr->[0][1];
      my ($syear, $smonth, $sday, $shour, $smin, $ssec) = $starttime =~ /(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2}):(\d{2})/;
      my $stime = $ssec+60*$smin+3600*$shour;
      #-- correct for daybreak
      $stime-=86400
        if( $stime > $ctime);
      if( ($num < 25)&&( ($ctime-$stime)<$avtime) ){
        #Log 1,"ARRAY has $num elements, adding another one";
        push(@{$hash->{helper}{history}},\@new);
      }else{
        shift(@{$hash->{helper}{history}});
        push(@{$hash->{helper}{history}},\@new);
      }
    }
    #-- output and average
    my $average = 0;
    for(my $i=0;$i<$num;$i++){
      $average+=$arr->[$i][0];
      Log 4,"[$name moving average] Value = ".$arr->[$i][0]." Time = ".$arr->[$i][1];
    }
    $average=sprintf( "%5.3f", $average/$num);
    #--average
    Log 4,"[$name moving average] calculated over $num values is $average"; 
    return $average;
}

Raspi 4B+ Bullseye ;LaCrosse; HomeMatic; MapleCUL; ZigBee; Signalduino ESP32 ; Shellys; MQTT2; Grafana mit Influxdb

Ellert

#35
Zitat von: Damian am 16 Januar 2019, 18:06:14
Kannst du ein konkretes Beispiel machen, wie du es dir vorstellst?
Für ein Reading temperature werden bei jedem Event die gemessenen Werte (y) als Zeitreihe in fortgeschrieben, wie beim Glätten aber mit Zeitstempel (x).
Zurückgegeben werden die Werte, mit der die Lage der besten Geraden in dem Datenfeld beschrieben wird. Für die Gerade y = a0 + a1x

würde [Sensor:temperature:reg10] dann a0, a1, xm, ym,,Standartabweichung, Korrelationskoeffizent als kommagetrennte Werte zurückliefern, berechnet aus 10 Wertepaaren.

Wenn die Ausgabeformatierung verwendet wird, dann sollten $a0, $a1, $xm, $ym, $stabw, $corr zur Verfügung stehen um ein eigenes Ausgabeformat zu erstellen.

Diese Werte werden durch die Perlfunktion Statistics::lineFit bereitgestellt https://metacpan.org/pod/release/RANDERSON/Statistics-LineFit-0.01/lib/Statistics/LineFit.pm

Die Mittelwerte xm, ym müssten noch berechnet werden.


Damian

Zitat von: Ellert am 16 Januar 2019, 20:28:32
Für ein Reading temperature werden bei jedem Event die gemessenen Werte (y) als Zeitreihe in fortgeschrieben, wie beim Glätten aber mit Zeitstempel (x).
Zurückgegeben werden die Werte, mit der die Lage der besten Geraden in dem Datenfeld beschrieben wird. Für die Gerade y = a0 + a1x

würde [Sensor:temperature:reg10] dann a0, a1, xm, ym,,Standartabweichung, Korrelationskoeffizent als kommagetrennte Werte zurückliefern, berechnet aus 10 Wertepaaren.

Wenn die Ausgabeformatierung verwendet wird, dann sollten $a0, $a1, $xm, $ym, $stabw, $corr zur Verfügung stehen um ein eigenes Ausgabeformat zu erstellen.

Diese Werte werden durch die Perlfunktion Statistics::lineFit bereitgestellt https://metacpan.org/pod/release/RANDERSON/Statistics-LineFit-0.01/lib/Statistics/LineFit.pm

Die Mittelwerte xm, ym müssten noch berechnet werden.
Es müsste also für die Berechnung jeweils ein Wertepaar aus Zeitpunkt=X und dem eigentlichen Wert=Y in der Queue gespeichert werden.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Zitat von: Christoph Morrison am 16 Januar 2019, 19:14:36
Ich hab mehrere Temperaturdevices auf meinem Grundstück, die jeweils ein Reading temperature haben. Darin ist die Temperatur ohne Gradzeichen abgelegt. Die hole ich mir alle zusammen und berechne den Medianwert, da gelegentlich mal eines nicht aktualisiert wird und ein arithmetisches Mittel dann sehr stark abweichende Werte liefern würde. Hier mal der Code in meiner Testumgebung:


define dummy.0 dummy
define dummy.1 dummy
define dummy.2 dummy
define dummy.4 dummy

setstate dummy.0 1
setstate dummy.0 2019-01-16 14:36:43 state 1

setstate dummy.1 2.5
setstate dummy.1 2019-01-16 14:40:04 state 2.5

setstate dummy.2 3
setstate dummy.2 2019-01-15 21:13:59 state 3

setstate dummy.4 4
setstate dummy.4 2019-01-16 15:48:43 state 4


Und die DOIFs mit denen ich getest habe:

define doif.0 DOIF ##
attr doif.0 state [#median:"dummy.*":state]
attr doif.0 verbose 5

define doif.1 DOIF ##
attr doif.1 state [@median:"dummy.*":state]
attr doif.1 verbose 5

setstate doif.0 2.75
setstate doif.0 2019-01-16 15:48:43 state 2.75

setstate doif.1 dummy.0,dummy.1,dummy.2,dummy.4
setstate doif.1 2019-01-16 15:48:43 state dummy.0,dummy.1,dummy.2,dummy.4


Das Zwischenspeichern der Werte in Dummys könntest du dir zukünftig sparen und da, wo du die bereinigten Werte benötigst mit  [device:temperature:med7] darauf zugreifen.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Christoph Morrison

Zitat von: Damian am 16 Januar 2019, 20:42:07
Das Zwischenspeichern der Werte in Dummys könntest du dir zukünftig sparen und da, wo du die bereinigten Werte benötigst mit  [device:temperature:med7] darauf zugreifen.

Äh, das ist temporärer Code aus meiner Testumgebung, kein produktiver Code ...

Ellert

Zitat von: Damian am 16 Januar 2019, 20:38:55
Es müsste also für die Berechnung jeweils ein Wertepaar aus Zeitpunkt=X und dem eigentlichen Wert=Y in der Queue gespeichert werden.
Ja, wobei es flexibler wäre, wenn [Sensor:temperature:ref10,5] nur 2 Referenzen auf @x, und @y liefert, die man dann in einem DOIF_Reading beliebig auswerten könnte und man ist nicht festgelegt auf einen bestimmten Algorithmus und könnte noch Wichtungen vornehmen.
ref10.5 bedeutet dann es werden 2 Referenzen geliefert auf 2 Arrays mit je 10 Elementen, die nächsten Referenzen werden erst geliefert, wenn das Array um 5 Elemente weiter geschoben ist. Dann wird die Berechnung nur alle 5 Werte ausgeführt, damit könnte man die Zahl zeitintensiver Berechnungen reduzieren.

Damian

Zitat von: Ellert am 17 Januar 2019, 14:56:05
Ja, wobei es flexibler wäre, wenn [Sensor:temperature:ref10,5] nur 2 Referenzen auf @x, und @y liefert, die man dann in einem DOIF_Reading beliebig auswerten könnte und man ist nicht festgelegt auf einen bestimmten Algorithmus und könnte noch Wichtungen vornehmen.
ref10.5 bedeutet dann es werden 2 Referenzen geliefert auf 2 Arrays mit je 10 Elementen, die nächsten Referenzen werden erst geliefert, wenn das Array um 5 Elemente weiter geschoben ist. Dann wird die Berechnung nur alle 5 Werte ausgeführt, damit könnte man die Zahl zeitintensiver Berechnungen reduzieren.

Ich befürchte, dass der allgemeingültige Ansatz zwar sehr viele Möglichkeiten bietet, aber von 99 % der User als zu komplex gesehen wird. Ich schätze, dass bereits der event-aggregator die meisten in seinen Möglichkeiten überfordert. Zudem wäre die bisherige Semantik bzgl. des Rückgabewertes von [device:reading] verletzt - es wäre kein einfacher Wert (Skalar) mehr, den man in den üblichen Bedingungen auswerten kann.

Was brauchen die meisten?

Ich denke, sie wollen Ausreißer eliminieren und das mit einfacher Syntax wie z. B. [device:reading:med] (med wie Median)

Damit würde ich zunächst anfangen.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Morgennebel

Zitat von: Damian am 12 Januar 2019, 23:18:58
Ich habe mal eine neue Option für Readingangaben (s wie smoothing) eingebaut. Man kann jetzt den Wert eines Readings über die letzten X-Werte glätten lassen.

Entspricht dies dem https://wiki.fhem.de/wiki/Gleitende_Mittelwerte_berechnen_und_loggen?

Danke, -MN
Einziger Spender an FHEM e.V. mit Dauerauftrag seit >= 24 Monaten

FHEM: MacMini/ESXi, 2-3 FHEM Instanzen produktiv
In-Use: STELLMOTOR, VALVES, PWM-PWMR, Xiaomi, Allergy, Proplanta, UWZ, MQTT,  Homematic, Luftsensor.info, ESP8266, ESERA

Damian

#42
Zitat von: Morgennebel am 17 Januar 2019, 17:37:21
Entspricht dies dem https://wiki.fhem.de/wiki/Gleitende_Mittelwerte_berechnen_und_loggen?

Danke, -MN

Im wesentlichen ja. Die Zeitspanne wird nicht angegeben, da muss der User selbst überlegen, wie viele Werte er auswerten will.

Es gibt sicherlich unzählige Funktionen, die so etwas leisten. Den Durchschnitt zu berechnen ist nichts besonderes, wobei der Median für Ausreißer viel interessanter ist.

Der eigentlicher Vorteil dieser Funktion ist, dass man bestehende Abfragen z. B. [aussen:temperature] <0 in [aussen:temperature:s7] <0 ändern kann, ohne weitere Definitionen oder Funktionsaufrufe irgendwo vornehmen zu müssen.


Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

sash.sc

Zitat von: Morgennebel am 17 Januar 2019, 17:37:21
Entspricht dies dem https://wiki.fhem.de/wiki/Gleitende_Mittelwerte_berechnen_und_loggen?

Danke, -MN
Das habe ich etwas vorher angeführt....

Das ist die Funktion für die myutils..... [emoji6]

Gesendet von meinem E6653 mit Tapatalk

Raspi 4B+ Bullseye ;LaCrosse; HomeMatic; MapleCUL; ZigBee; Signalduino ESP32 ; Shellys; MQTT2; Grafana mit Influxdb

Damian

Zitat von: sash.sc am 17 Januar 2019, 18:28:03
Das habe ich etwas vorher angeführt....

Das ist die Funktion für die myutils..... [emoji6]

Gesendet von meinem E6653 mit Tapatalk

ja, die DOIF-Erweiterung ist in erster Linie für DOIF-Nutzer interessant (oder die es werden wollen).

Wenn jemand bisher kein DOIF genutzt hat, kann natürlich trotzdem gern relativ einfach bereinigte Readings zum Plotten erzeugen:

https://forum.fhem.de/index.php/topic,95759.msg888625.html#msg888625
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF