patch: erweiterung SVG um logarithmische y-achse

Begonnen von justme1968, 16 Mai 2016, 22:28:10

Vorheriges Thema - Nächstes Thema

justme1968

anbei ein patch mit dem es möglich ist auf der y-achse eines svg plots eine logarithmische skala zu verwenden.

hintergrund ist aktuell ein HM helligkeitssenior mit einem messbereich von 0.01 lux (dunkel) bis 100000.00 lux (sonne). um die messwerte im tagesverlauf sinnvoll plotten zu können ist eine exponentielle skala nötig.

der patch implementiert folgendes:
- neues yscale schlüsselwort mit dem für eine achse eine exponentielle skalierung aktiviert wird
- die möglichkeit bei den tics jeweils das label weg zu lassen. es wird dann den aktuelle wert verwendet
- im svg.js ist die rückrechnung entsprechend angepasst

- zusätzlich wird in SVG_FwFn noch $idx das beim achsen zeichnen verwendet wird auf den bereich 0..max-1 statt 1..max umgestellt so wie es im folgenden beim plotten der punkte verwendet wird.


wenn tics im plot file von hand gesetzt werden dann müssen sie für die größte größenordnung gesetzt werden, die jeweils um eine größenordnung kleineren skalenbereiche werden automatisch daraus abgeleitet. d.h z..b im bereich 1000-10000 werden die tics vorgegeben, in den bereichen 0-10, 10-100 und 100-1000 werden automatisch gesetzt. ich vermute es gibt noch andere anwendungsfälle.


anbei ein beispiel plot file das eine quadratische kurve einmal mit einer logaritmsichen achse und ein mal mit einer linearen achse anzeigt.

set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title '<TL>'
set ytics       
set y2tics   
set grid ytics
set ylabel "log"
set y2label "lin"
               
#set y2tics ("" 2000, "" 4000, "" 6000, "" 10000)
               
set xrange [0:100]
               
set yrange [0:10000]
set y2range [0:10000]
               
set yscale log
#set y2scale log
               
#logProxy Func:logProxy_xy2Plot([[0,0],[5,5*5],[10,100],[20,20*20],[30,30*30],[40,40*40],[50,50*50],[60,60*60],[70,70*70],[80,80*80],[90,90*90],[100,100*100]])
#logProxy Func:logProxy_xy2Plot([[0,0],[5,5*5],[10,100],[20,20*20],[30,30*30],[40,40*40],[50,50*50],[60,60*60],[70,70*70],[80,80*80],[90,90*90],[100,100*100]])
               
plot "<IN>" using 1:2 axes x1y1 title 'log' ls l2 lw 1 with lines,\
plot "<IN>" using 1:2 axes x1y2 title 'lin' ls l0 lw 1 with lines,\



ps: die falsche x-coordinate im plot value auf den screenshots bitte ignorieren. das ist ein ganz anderes problem. das muss ich mir noch anschauen.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

rudolfkoenig

Zitatum die messwerte im tagesverlauf sinnvoll plotten zu können ist eine exponentielle skala nötig.
Zitat https://xkcd.com/1162/ : "Log scales are for the quitters" :)

Mit "perl fhem.pl fhem.cfg.demo" kriege ich:
ZitatPrototype mismatch: sub main::log10: none vs ($) at ./FHEM/98_SVG.pm line 198, <$fh> line 101.
Subroutine log10 redefined at ./FHEM/98_SVG.pm line 192, <$fh> line 101.
Prototype mismatch: sub main::log10 ($) vs none at ./FHEM/90_at.pm line 7.
90_at.pm, Zeile 7: "use POSIX;"

Kannst du bitte testen, ob man die Definition einer eigenen log10 sparen kann?

Sehe ich richtig, das man diese Skala nicht im SVG-Editor einstellen kann? Wir koennten dafuer Y-Axis missbrauchen, indem man 2 neue Werte (right+log,left+log oder so aehnlich) einfuehrt.

betateilchen

Wenn ihr eh schon an der Skalierung der y-Achsen rumschraubt, würde ich mir wünschen, dass irgendjemand einen einstellbaren Offset einbaut, damit bei einer automatischen Berechnung der Achseneinteilung nicht immer der min- und max-Wert als unterer und oberer Skalenwert verwendet werden, was dazu führt, dass die plots am Rand "kleben".

Viele Grüsse aus Serbien.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

Markus Bloch

Zitat von: betateilchen am 17 Mai 2016, 13:16:17
Wenn ihr eh schon an der Skalierung der y-Achsen rumschraubt, würde ich mir wünschen, dass irgendjemand einen einstellbaren Offset einbaut, damit bei einer automatischen Berechnung der Achseneinteilung nicht immer der min- und max-Wert als unterer und oberer Skalenwert verwendet werden, was dazu führt, dass die plots am Rand "kleben".

Viele Grüsse aus Serbien.

Das würde ich mir ebenfalls wünschen. Ich hatte es schon mehrmals selber versucht, bin aber dran gescheitert.

Gruß
Markus
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

justme1968

#4
@rudi: netter spruch. das problem ist aber nicht das papier sonder die bildschirmgröße :)

anbei eine aktualisierte version:
- log10 ist in SVG_log10 umbenannt. im gegensatz zur POSIX version werden -inf und nan direkt ausgeschlossen und das ergebniss auf einen sehr kleinen wert gesetzt. das spart dann die fehlerbehandlung bei jedem aufruf, ist aber SVG spezifisch.

- ich habe deinen vorschlag die log auswahl bei y-axis einzubauen probiert. das funktioniert zwar, ist aber nicht konfliktfrei weil man z.b. eine kurve auf left legen kann und eine zweite auf left+log. das ist aber sinnlos.

- statt dessen habe ich je eine checkbox bei range eingebaut. dann lässt es ich pro achse genau ein mal auswählen. ausserdem macht es im gegensatz zum vorschlag oben den plot editor nicht breiter.

- ansonsten habe ich in den patch jetzt noch eingebaut das display plot values auch mit x-y plots funktioniert. dazu habe ich mir erlaubt den x_mul parameter in den attributen in t_mul umzubenennen. so heisst auch die variable auf perl seite. x_mul wird dann für die x-y plots verwendet. die perl variable heisst ebenfalls so. polar plots fehlen noch. da habe ich aber noch keine idee.

@betateilichen: das geht doch jetzt schon wenn man für xrange im plotfile die {...} perl code version verwendet. hier kannst du dann auf $data{minX} und $data{maxX} einen festen offset anwenden oder dynamisch x% rand lassen.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

justme1968

#5
beim einbauen in den ploteditor sind mir noch zwei dinge aufgefallen:

- mit write .gplot file wird die url geändert und man kann nicht mehr einfach ein reload der seite machen

- ytics und yrange wird nur für die ersten beiden achsen unterstützt. alle weiteren gehen verloren. hatten wir dazu nicht schon mal einen patch?
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

rudolfkoenig

Die Loesung mit dem Checkbox finde ich gut.

Ich kriege aber mit der neuen Version (und z.Bsp. mit fhem.cfg.demo) die Meldung:
PERL WARNING: Use of uninitialized value $xmin in concatenation (.) or string at ./FHEM/98_SVG.pm line 1829.
Da ich nach 5 Minuten nicht auf die Idee kam, wieso , moechte ich Dich bitten, das noch zu beheben.

justme1968

problem gefunden. der patch den ich hoch geladen hatte war nicht von der aller letzten version sondern von einem zwischenstand.

der patch hier: https://forum.fhem.de/index.php/topic,53487.msg451825.html#msg451825 ist aktualisiert.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

justme1968

ach ja. noch eine Kleinigkeit:

in der jahres ansicht ist die formatierung des datums bei show plot values komisch. nur der tag wird mit 0 aufgefüllt. ich würde vorschlagen den monat auch aufzufüllen und das jahr noch dran zu hängen. dann sieht man sofort das es ein datum ist.

also in svg.js dann
ts = (pad0(d.getMonth()+1))+"."+pad0(d.getDate()+"."+(d.getYear()+1900));stattts = (d.getMonth()+1)+"."+pad0(d.getDate());

die in der monats skalierung wäre vielleicht auch ein ts = (pad0(d.getMonth()+1))+"."+pad0(d.getDate())+". "+pad0(d.getHours())+":"+pad0(d.getMinutes()); gut. dann ist es einheitlich und der monat ist sichtbar. sinnvoll z.b. bei endPlotNow.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

rudolfkoenig

Habs eingecheckt, zunaechst ohne deine Vorschlaege fuer svg.js.

Das Ergebnis ist noch nicht ganz optimal, wie man das bei dem Anhang sehen kann:
- die Beschriftung links ist "falsch".
- die gruene Kurve ist auch nicht log-skaliert, erst wenn ich den Checkbox links anhake, wird die linke Skala und die Kurve richtig skaliert.
- die Beschriftung rechts ist bei der Standard-SVG-Hoehe nicht lesbar, da zu viel Text. Ich waere auch mit weniger Linien zufrieden.

justme1968

- was meinst du mit 'die beschriftung links ist falsch'? log ist doch nur für die rechte achse aktiviert.

- hab es repariert. wer kommt denn auch auf die idee die achten nicht in der richtigen reihenfolge zu verwenden :)
  die rück rechnung auf die korrekte achse hat gefehlt.

- wenn man den plot nicht größer (höher) macht sollte man die ytics vorgeben. z.b. set y2tics ("" 2000, "" 4000, "" 6000, "" 10000) oder set y2tics ("" 1000, "" 4000, "" 10000)dann passt es besser. ich habe gerade noch keine idee wie man das automatisch machen ohne zu viel umzustellen.

- die oben vorgeschlagenen änderungen für die label bei show plot values hab ich auch mal mit in den patch getan :)

- der patch repariert noch ein problem mit den x-y plots das mir beim testen aufgefallen ist. es wurden keine min und max werte berechnet.

dabei ist mir aufgefallen das zur zeit fileLog/dbLog/logProxy min/max/... berechnen und in $data stecken, das dann aber von SVG nicht verwendet wird sondern hier noch mal min und max für die automatische skalierung berechnet wird.

das reload nach dem speichern nicht geht ist beim testen wirklich nervig...
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

rudolfkoenig

Zitat- was meinst du mit 'die beschriftung links ist falsch'? log ist doch nur für die rechte achse aktiviert.
Stimmt, und auf der linken Seite ist eine nicht-log Skala, die verwirrt, weil die Daten nicht passen. Solange nur eine Seite konfiguriert ist, sollten die beiden Achsenbeschriftungen identisch sein. Ist bei nicht-log auch so.

Zitat- wenn man den plot nicht größer (höher) macht sollte man die ytics vorgeben. z.b.
Das ist zwar eine Loesung, aber etwas ungluecklich, weil per "default" haesslich ist.

Ich habe den Patch eingecheckt.

betateilchen

#12
Zitat von: justme1968 am 17 Mai 2016, 20:41:53
@betateilichen: das geht doch jetzt schon wenn man für xrange im plotfile die {...} perl code version verwendet. hier kannst du dann auf $data{minX} und $data{maxX} einen festen offset anwenden oder dynamisch x% rand lassen.

Danke für den Tipp, das funktioniert. (abgesehen davon, dass Du yrange meintest, aber xrange geschrieben hast)

{"[".($data{min3}-.1).":".($data{max3}+.1)."]"}

Aber irgendwas, das ein bisschen anwenderfreundlicher (im Sinne von "auch Otto Normalverbraucher" versteht das) ist, wäre an dieser Stelle sicher nicht schlecht.

Jetzt noch eine Frage zum Schluss: Wie finde ich denn die absoluten min und max Werte unabhängig von einer bestimmten Datenreihe? $data{min} funktioniert nicht. gefunden: $data{minAll} und $data{maxAll}
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

justme1968

das x stand in dem fall für y,y2,y3,... :) und sollte eigentlich ein grosses X werden.

ich könnte mir vorstelle das man eine option einbaut die fest oben und unten z.b. x pixel abstand lässt. das könnte man einfach einbauen. alles was auf die plot werte geht wäre deutlich schwieriger. wenn rudi so eine option akzeptiert baue ich einen patch.

allgemeine extrema über alle datenreihen gibt es nicht. die datenreihen aller achsen in einen topf zu schmeissen wäre aber vielleicht auch nicht aussagekräftig. man könnte aber achsenweise zusammenfassen. rudi? wobei das mit einer x-pixel rand option vermutlich dann auch hinfällig ist?

bis dahin kannst du vielleicht auf etwas wie minNum($data{min1},$data{min2},$data{min3})und maxNum(...) ausweichen.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

betateilchen

Zitat von: justme1968 am 18 Mai 2016, 20:46:42
bis dahin kannst du vielleicht auf etwas wie minNum($data{min1},$data{min2},$data{min3})und maxNum(...) ausweichen.

siehe oben: "gefunden: $data{minAll} und $data{maxAll}"

Es macht schon manchmal Sinn, die min/max Werte über mehrere Datenreihen zu ermitteln, solange sie an der gleichen y-Achse geplottet werden.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!