Grafische Interpolation und Bestapproximation von numerischen Wertepaaren. Mit anderen Worten: Wir wollen Punkte auf einer Zeichenebene über verschiedene Verfahren miteinander verbinden.
3. Fassung Berlin Apr 10
Daniel Krompass
Beta (Quellcode kann via Email angefordert werden)
Ich habe mehr Werkzeuge, als meine Freundin Schuhe hat.
Bevor es los geht, möchte ich mich bei den Plattformbetreibern für das regelmäßige Update meiner Dokumentation bedanken. Mit dem Erscheinen der Beta-Version wurden die «prototypischen Schlampereien» bei der Rechnung mit Gleitkommazahlen durch geeignete Rundungstechniken besser konditioniert. Aus dem Inhalt:
Einleitung zur Spezifikation, Zielgruppe, Motivation und Ausblick.
In Teil I vermitteln wir die grundlegende Anwendung an konkreten Beispielen.
In Teil II vermitteln wir die fortgeschrittenen Techniken an konkreten Beispielen.
Informelle Spezifikation
Es ist nach einem kleinen aber flexiblem PHP-Programm zum Zeichnen von kartesischen Diagrammen gesucht. Wir folgen dabei einer intuitiven Vorgehensweise: Eine Zeichenebene dient als Container für unterschiedliche Diagrammtypen, so z.B. Linien-, Punkt- oder Kurvendiagramme. Jeder derartigen Komponente werden numerische Wertepaare übergeben, die dann über die jeweiligen Verfahren interpoliert oder approximiert werden. Wir wollen hier eine stückweise lineare Interpolation (Polygonzug), sowie eine natürliche Spline-Interpolation von Wertepaaren realisieren. Wir werden auch sehen, wie sich sogar Balkendiagramme mit diesem Kontext vertragen können.
Zielgruppe
Jeder kann den PHP-Code privat wie auch kommerziell nutzen. Die Anwendung aus Teil I) ist einfach und für jeden verständlich. Haftungsansprüche sind ausgeschlossen. Für den Einsteiger können folgend einige grundlegende Gedankengänge am konkreten Beispiel lehrreich sein. Bei ausreichender Resonanz oder konkreten Fragen kann die Dokumentation auch intensiver ausgearbeitet werden, so dass der Interessierte näher an die Problemlösung herangeführt werden kann. Die Umsetzung in PHP und das individuelle Erweiterungspotenzial der vorliegenden Lösung aus Teil II) richtet sich hingegen an fortgeschrittene Programmierer mit teilweise erforderlichen Kenntnissen aus der numerischen Mathematik. Als praktisch orientierte Begleitlektüre kann Numerische Mathematik von Michael Knorrenschild hilfreich sein.
Motivation
Das folgende Programm zeigt, wie mit PHP die obige Anforderung umgesetzt werden kann. Der Programmumfang umfasst keine 1000 Zeilen. Somit ist der geübte Programmierer in der Lage, das Programm an die jeweiligen Bedürfnisse anzupassen.
Ausblick
Neben den hier vorgestellten Interpolationsverfahren spielt ebenso die Ausgleichsrechnung (Bestapproximation) eine praktische Rolle, deren Graphen im Unterschied dazu nicht zwangsläufig durch alle Wertepaare hindurchgehen. Vielmehr ist man an einer bestmöglichen (gewichteten) Annäherung durch einen Graphen mit vorgegebenen Eigenschaften interessiert. Je nach freien Kapazitäten komme ich darauf zurück.
Beispiel 1
Es gibt Fälle, bei denen es nicht erwünscht ist, dass der kleinste Y-Wert stets auf der X-Achse, oder der kleinste X-Wert stets auf der Y-Achse liegt. Wir werden weiter unten weitere Probleme ansprechen, die mit den folgenden Verfahren gelöst werden können. Um die Achsenbeschriftungen (nicht die Achsen selbst), und somit auch den gesamten Graphen horizontal und/oder vertikal zu stauchen (unsinnigerweise auch strecken), können zwei weitere Parameter für die X- bzw. Y-Stauchung angegeben werden. Mit 1 2 # Zeichenebene
3 $g = new Graph();
4 5 # Komponenten
6 $c1 = new GraphPoints(0x000000);
7 $c2 = new GraphLinear(0x0000FF);
8 $c3 = new GraphNaturalSpline(0xFF0000);
9 10 # Komponenten binden
11 $g->AddComponent($c1);
12 $g->AddComponent($c2);
13 $g->AddComponent($c3);
14 15 # Wertepaare
16 $c1->AddPoint(-1, 5);
17 $c1->AddPoint(0, -2);
18 $c1->AddPoint(1, 9);
19 $c1->AddPoint(2, -4);
20 21 $c2->AddPoint(-1, 5);
22 $c2->AddPoint(0, -2);
23 $c2->AddPoint(1, 9);
24 $c2->AddPoint(2, -4);
25 26 $c3->AddPoint(-1, 5);
27 $c3->AddPoint(0, -2);
28 $c3->AddPoint(1, 9);
29 $c3->AddPoint(2, -4);
30
erhalten wir eine horizontale und eine vertikale Stauchung von 50 Pixel.
Beispiel 2
Durch die Angabe eines weiteren Parameters können wie eine sog. Sicht definieren. Dabei geben wir ein Array mit den jeweiligen Koordinaten des Sichtfensters an. Teile des Graphen, die teilweise außerhalb des Fensters liegen, werden abgeschnitten. Schauen wir uns folgendes Beispiel an. Mit 1 2 include ("graph.php");
3 4 # Zeichenebene
5 $g = new Graph();
6 7 # Komponenten
8 $c1 = new GraphPoints(0x000000);
9 $c2 = new GraphLinear(0x0000FF);
10 $c3 = new GraphNaturalSpline(0xFF0000);
11 12 # Komponenten binden
13 $g->AddComponent($c1);
14 $g->AddComponent($c2);
15 $g->AddComponent($c3);
16 17 # Wertepaare
18 $c1->AddPoint(-1, 5);
19 $c1->AddPoint(0, -2);
20 $c1->AddPoint(1, 9);
21 $c1->AddPoint(2, -4);
22 23 $c2->AddPoint(-1, 5);
24 $c2->AddPoint(0, -2);
25 $c2->AddPoint(1, 9);
26 $c2->AddPoint(2, -4);
27 28 $c3->AddPoint(-1, 5);
29 $c3->AddPoint(0, -2);
30 $c3->AddPoint(1, 9);
31 $c3->AddPoint(2, -4);
32
Die sog. Balkendiagramme sind etwas artfremd, und lassen sich daher nicht so offensichtlich in den vorgegeben mathematischen Kontext sinnvoll einbinden. Die beiden zuletzt vorgestellten Verfahrenstechniken werden uns dabei helfen. Betrachten wir zunächst in gewohnter Weise: 1 2 # Zeichenebene
3 $g = new Graph();
4 5 # Komponenten
6 $c1 = new GraphBar(0x000000, 0x0000FF, 30);
7 8 # Komponenten binden
9 $g->AddComponent($c1);
10 11 # Wertepaare
12 $c1->AddPoint(-1, 4);
13 $c1->AddPoint(0, 7);
14 $c1->AddPoint(1, 2);
15 $c1->AddPoint(2, 9);
16 $c1->AddPoint(3, 8);
17 $c1->AddPoint(4, 4);
18 $c1->AddPoint(5, 2);
19 20 # Ausgabe
21 $image = $g->Draw(0x000000, 0xFFFFCF, 500, 250, 6, 7);
22 header("Content-type: image/gif");
23 imagegif($image);
24
Der Konstruktor nimmt zwei RGB-Farbwerte für den links-recht-Farbverlauf, sowie die Balkenbreite von 30 Pixel entgegen. Als Ausgabe erhalten wir:
Es fallen zwei unschöne Dinge auf. Der linke Balken überschneidet unsere Y-Achse und es fehlen zwei Balken. Das erste Problem lösen wir mit einer horizontalen Stauchung von - sagen wir 20 Pixel. Mit 1 2 $image = $g->Draw(0x000000, 0xFFFFCF, 500, 250, 6, 7, "", "", 20, 0);
3
erhalten wir:
Damit unsere kleinsten Balken sichtbar werden, definieren wir eine angemessene Sicht. Mit 1 2 $c1 = new GraphBar(0x000000, 0x0000FF, 30);
3 $image = $g->Draw(0x000000, 0xFFFFCF, 500, 250, 6, 9, "", "",
4 20, 0,array(-1, 0, 5, 9));
5
und den entsprechenden Wertepaaren erhalten wir die gewünschte Ausgabe: