Temperatursummen in der Imkerei

phenology

#21

You’ve asked for it, darling. Ick geh den Query mal Schritt für Schritt durch (wer nicht Flux lernen will, sollte hier vmtl. mit dem Lesen abbrechen.)

jan = from(bucket: "dwd_cdc")

Wir nehmen uns mal die “dwd_cdc”-Datenbank, aaaber tun das Ergebniss dieses ja noch weiter spezifizierten Queries in eine Art Variable und nennen sie “jan”.

  |> range(start:2019-01-01T00:00:00Z, stop: 2019-01-31T23:59:59Z)

Joar, hier würde mensch sonst[tm] “$range” in die Klammern schreiben: Eine Variable, die Grafana anhand des aktuell gewählten Zeitfensters Flux-gerecht bereitstellt. Aber wir wollen ja mal nur den Januar betrachten. Unsere DB denkt in UTC, der DWD auch: Schick.

  |> filter(fn: (r) =>
      r._measurement == "dwd_cdc_temp_2m_c" and
      r._field == "value" and
      r.sta_name == "$COMMON_CDC_NAME" and
      r.quality_level == "2" and
      r.produkt == "10_minutes"
     )

Lasst uns unsere Datenbank filtern! “r” definieren wir als unsere response. “interne” Variablen beginnen mit nem Undersore; das ist für _measurement, _field und _value gegeben. Übrige/individuelle tags werden ohne
_ angegeben. Der Wert aus der Variable $CDC_COMMON_NAME stellt das Grafana über unser Station-Dropdown bereit (hier könnte auch nen Stationsname “hardcodet” stehen).

  |> aggregateWindow(every: 1d, fn: mean)

Ist ne Helper-Function, die eigentlich erstmal window() aufruft, und denn die einzelnen Series wieder group()t. Damit lassen sich auch verschiedene Messrhytmen “normalisieren” und vergleichen. In diesem Fall machen wir aus unseren “alle 10min-Daten” die gewünschten Tagesmittelwerte.

  |> keep(columns: ["_value", "_time"])

Lasst uns unnötiges Wegschmeißen.

  |> filter(fn: (r) => r._value > 0)

Ah, wir interessieren uns ja nur für Tagesmittelwerte über 0°C! Also filtern wir mal auf die. Das macht ja auch jetzt erst Sinn, weil wir vorher noch nicht auf Tagesmittelwerte aggregiert haben.

  |> map(fn: (r) => ({
    _time: r._time,
    _value: r._value * 0.5
    }))

Tja, dann müssen wa noch unseren Wert gewichten. Ist ja Januar, also mit 0.5. _time muss mit aufgerufen werden, damits überlebt.

Ok, dasselbe für

feb = from(bucket: "dwd_cdc")

und analog für

rest = from(bucket: "dwd_cdc")

Jetz dieses union():

union(tables: [jan, feb, rest])
  |> sort(columns: ["_time"], desc: false)

Sortierung ist wichtig, sonst kommt etwa der 1.-11. Februar zuerst in die Tabelle und dananch erst der 1.-31. Januar und das arme Grafana ist sehr verwirrt und malt plötzlich Kreise. (Srsly. Grafana supports time-travel!)

  |> map(fn: (r) => ({
    _time: r._time,
    _value: r._value,
    foo: "GTS Tag"
    }))

Boar, diese Beschriften von einzelnen Datenserien ist die Hölle mit Grafana & Flux. Hier fügen wir an jeden Messwert noch nen key-value-Päarchen, damit wir die Serie als “Tag”(eswert) wiedererkennen können.

  |> yield(name: "bars")

Weil wir hier grafisch Balken nehmen, heissts “bars” und es wird die Series an genau dieser Stelle per yield() “vermeldet”, also als separate result-Tabelle ausgeworfen und gemalt, ehe wir sie weiter modizifieren. Somit haben wir jetz erstmal alle gewichteten Tagesmittelwerte > 0°C im Bild.

  |> cumulativeSum(columns: ["_value"])

Wir wollen aber auch die kummulierte Summe aller Werte! (Bei single stats: Nur sum() )

  |> map(fn: (r) => ({
    _time: r._time,
    _value: r._value,
    foo: "GTS Total"
    }))

Also benennen wir unser lustiges Feld nochmal um und brauchen es nicht nochmal yield(en), weil wir jetzt ja am Ende sind und damit eh unsere/(die) eine Ergebnistabelle rendern.


#22

Sorry for the buzz, aber wir können jetzt auch die Grünlandtemperatur-Berechnung mit der 10-Tage-Vorhersage weiterführen und dies auch auf die Kältesumme anwenden. Gibts nun in einem einzelnen Panel integriert im Referenz-Dashboard für die Grünlandtemperatursumme, funktioniert auch für Bremen (und auch für den Brocken, wenns etwas kälter sein darf):

(Standbild des dashboards)

💃 … ja, das sind dann auch nur im positiven je Zeitraum drei und für die Vorhersage je nochmal 3x2 = 6 Teilabfragen in einen Query (Kältesumme läuft separat). [Edit: Ein Glück müssen wa nun nicht mehr für die zwei Darstellungen (je Tag vs. akkumulierten Gesamtwert) noch einen Satz Queries fahren. :]

[Edit: Was die Populations-Abschätzung anbelangt muss ich leider feststellen, dass Flux zwar viel mehr tolles Neues kann, aber auch noch einige Grundlagen noch nicht. Zu diesen gehört leider leider auch noch das Rechnen mit Exponenten. 🙄]


#23

english tl;dr
FLUX 0.12.0 (as deployed on swarm.hiveeyes) is not yet capable of functions like pow(), logx() or even sqrt(). Therefore to adopt the Bretschko (“Heuvel”) formula for oviposition rate, it should be substituted with a Taylor 3rd order polynome.


Aber wir haben *, /, - und + … was braucht es mehr! ;)

Um den Exponenten wegzubekommen, paßt ein Taylor-Polynom (hier ohne Restglied). Eine Polynom dritter Ordnung wurde gewählt, da die Annäherung als zunächst ausreichend angesehen wird. Dafür braucht es vier zuvor ausgerechnete Koeffizienten an einem Bezugswert x0. Hier wird als x0 = 10°C genommen, da das Polynom im interessierenden Wertebereich von 1°C bis 25°C mit den Koeffizienten von 10°C am besten “paßt”.

Mein eigenes Taylor-Reihen-Wissen reichte nicht mehr ganz, so daß dank @bty seit gestern also ein Näherungs-Polynom existiert, das man in FLUX verwenden könnte:

Formeln und kleine Herleitung

FIXME: also notate in mathjax

Der Term 22,8295*x^1,4254 (die Eiablagerate nach Bretschko, Bergmann…) hier vereinfacht “Heuvel-Formel” genannt) muß durch eine sinnvolle Näherung ohne Potenz ersetzt werden. x ist die Tageshöchsttemperatur.

f(x) = 22,8295 * x^1,4254
a = 22,8295
b = 1,4254
f(x) = a * x^b

Die gleich benötigten ersten drei Ableitungen dieser Funktion lauten:

f'(x) = a*b*x^(b-1)
f''(x) = a*b*(b-1)*x^(b-2)
f'''(x) = a*b*(b-1)*(b-2)*x^(b-3)

Taylor-Polynom 3. Ordnung hierfür:

T f(x) = f(x0)+f'(x0)*(x-x0) + f''(x0)/2 *(x-x0)*(x-x0) + f'''(x0)/6 * (x-x0) * (x-x0) * (x-x0)

Um damit nun rechnen zu können, werden

  • der Funktionswert (spätere Koeffizienten) für den Wert x0 berechnet, sowie die ersten drei Ableitungen an x0.

  • für x0 = 10°C ergeben sich:

    f(x0) = 607,989949
    f’(x0) = 86,662887
    f’’(x0) = 3,686639
    f’’’(x0) = -0,211834

  • dann wird eingesetzt (das wäre auch der Term, der im FLUX-query steht, solange es kein POW() gibt; die zwei Divisionen dort kann man da noch ‘kürzen’ und natürlich zu Konstanten machen, auch könnten (max(temp) -10) sowie dessen mehrfache Multiplikationen einen Schritt vorher natürlich auch Variablen zugewiesen werden), :

607,989949 + 86,662887*(max(temp) -10) + 3,686639/2 * (max(temp) -10) * (max(temp) -10) + -0,211834/6 * (max(temp) -10) * (max(temp) -10) * (max(temp) -10) 
Tagesmax. Eier/d; “Heuvel” Eier/d; Taylor-Poly 3.Ordn.
1°C 23 3
2°C 61 51
3°C 109 104
4°C 165 162
5°C 226 225
6°C 294 293
7°C 366 366
8°C 442 442
9°C 523 523
10°C 608 608
11°C 696 696
12°C 788 788
13°C 884 884
14°C 982 982
15°C 1084 1083
16°C 1188 1187
17°C 1295 1293
18°C 1405 1401
19°C 1518 1512
20°C 1633 1624
21°C 1751 1737
22°C 1871 1852
23°C 1993 1969
24°C 2118 2086
25°C 2245 2204


Dann fehlen noch

  • der Stärke des Überwinterungsvolkes (Konstante bzw. dashboard-ad-hoc-Variable),

  • die Mortalitätsrate (einfaches lineares Glied),

  • und die auflaufende Brut, die cumulativeSum der Formel oben ab 21 Tage nach Beginn (1.2. oder ab nachgewiesenener Brut)

Vielleicht brauchen wir für letzteres gar kein time shift, aber das soll @wtf entscheiden! ;)


#24

Yeah. Hab das mal etwas umgeknetet, zur Ermittlung das Temperatur-Tagesmaxima zugrunde gelegt und ruf erstmal nur den Zeitraum 1.2. bis 31.12.2019 auf, soweit erstmal in meinem Sandkasten-Dashboard zu finden: 🤓 Labor / Eiablage & geschlüpfte Bienen nach Heuvel (2019). Die referenzierte DWD-Station ist dort einstellbar.

(Standbild des dashboards)

Bitte schaut mal jemand vom Fachlichen drauf ob das grob Sinn ergibt. (Ne Vorhersage klebt da auch dran; der “Knick” in einigen Stationen sieht erstmal aus wie nen Rechenfehler in der Vorhersage, aber bspw. Berlin hatte gestern & heute (!) 15-16°C, vgl. Hamburg für “ohne Knick”.)

Wenn des stimmig erscheint stünde wohl an als nächstes mit der Mortalitätsrate noch ne Kurve “Brut” zu bauen, am Besten um 20 Tage versetzt, wenn ich @mde’s Beitrag nochmal studiere.


#25

Hmmm, bei der maximal en Eianzahl pro Tag streiten sich die Gelehrten, früher waren es mal 1500, dann das 2-fache Körpergewicht, nun hört man 2000 oder das 6-fache Gewicht. Wie auch immer, jedenfalls ist das das Maximum im Mai / Juni! Wenn wir da an einem warmen Tag jetzt schon mit 1000 Eiern rankommen macht mich das stutzig, die müssen nicht nur gelegt werden, sondern auch gepflegt und gefüttert werden, und dass es dann – am nächten Tag – auf 150 runter geht kann ich mir auch nicht vorstellen. Befürchte so dynamisch ist dass System Bienenvolk dann doch nicht. Aber nur ein leichter Zweifel ohne Zahlen die das widerlegen im Hintergrund …


#26

Danke @wtf - gefällt mir wieder einmal sehr gut! :)

Nein - denn die hast Du ja bereits mit der orange-farbigen Kurve gebaut , yeah! 8)

  • Als zweite Kurve braucht es die “Gesamtzahl Bienen”, die startet per fixem Wert am besten als einstellbare Variable ab Tag 1. (10000 wäre ein sinnvoller default)

  • ab dem zweiten Tag wird diese Gesamtzahl jeden Tag mit 0,975 multipliziert - die Mortalität.

  • die auflaufende Brut findet in beiden Kurven Eingang: 20 Tage nach Eiablage (am 21. Tag des Ei) verschwindet diese Anzahl von “Brut” und wird “Anzahl Bienen” zugeschlagen (welche mit 0,975 multipliziert wird, s. o.).

  • darüber hinaus interessiert das Verhältnis Brut zu Bienen: ab >= 90% Brut/Bienen möchte man das markiert sehen (paßt daher als gauge und anderes panel).

Wenn das fertig ist, wollen wir noch den Anteil verdeckelter Brut an Gesamt-Brut sehen, aber dazu später … ;)


#27

zur erläuterung der zusammenhänge im bienenvolk beginne ich hier eine literatursammlung zum thema:

  • josef bretschko - naturgemäße bienenzucht
    hab ich in meinem archiv, bei interesse geb ich den link weiter.
  • ferdinand gerstung - das grundgesetz der brut- und volksentwicklung
    pdf, 14Mb hier
  • ludwig armbruster - imkerbetriebslehre der erzeugnung
    html hier

#29

Die Knicke in der Kurve düften einigermaßen normal sein, weil die Temperatur sich ja sprunghaft ändert. Wenn ich mal die Formel von Heuvel zugrunde lege, werden die 2000 Eier am Tag bei 23-24 °C erreicht. Bei den hier ja auch im Sommer ab und an möglichen 30 °C wären es dann schon 3000. Einzelne Tage sollten da nicht das Problem sein, das dürfte sich recht schnell wegmitteln. Aber wenn es mal 2-3 Wochen über 25 °C ist könnte das Volk unrealistisch groß werden, wobei es (beginnend bei 0) immer noch über einen Monat von 30 °C braucht, um ein Volk von 40000 Bienen zu erreichen.

In https://doi.org/10.1016/j.ecolmodel.2013.06.005 findet man recht interessante Daten die hier hilfreich sein könnten (auch wenn deren Modell deutlich komplizierter ist), vor allem in den angegebenen Referenzen:

  • Todesraten von Eiern, Larven und Puppen, die könnte man vermutlich recht einfach einbauen
  • Maxima von 30000 verdeckelten Zellen (die sollten sich einfach ausrechen lassen, wenn man die Legerate schrittweise und nicht auf einen Schlag zu Bienen umwandelt
  • Volksstärke bis um die 50000
  • Maximale Legerate (Gleichung 10 und Werte aus Tabelle 4) um die 1000

#30

gerade scheint die Balkendarstellung kaputt zu sein, kannst Du @wtf mal nachsehen; es waren keine edits… @andreas: hast Du FLUX geupdated?


#31

Es ist alles beim alten, in jüngster Zeit kam nix rein. Die relevanten Softwarestände sind:

  • elbanco: InfluxDB 1.7.3
  • eltiempo: InfluxDB 1.7.2

#32

mir auch grad unklar worans lag. aber es ist jetzt besser; ich konnte sinnvollerweise noch den zweiten “aggregateWindow” ausm mosmix-query rausnehmen und in den join() tun (hats besser gemacht). traditionell hatt ich so nen effekt AFAIR mal bei “doppelte ergebnisse” (bspw. bei nicht differenziertem qualitätsniveau) und/oder “irgendwas mit windowing/mittelwert-bildung in hohen zoomstufen”.

leider nicht viel schlauer geworden, aber habe gerne gestochert!

ps.: müsste man jetz nochmal nen debug-modus anwerfen und gucken ob die aggregation so weiterhin sinn ergibt.[tm]