Tag Archives: MATLAB

Schicke Grafiken in LaTeX mit pgfplots (1)

Vor einiger Zeit hatte ich im Beitrag Schicke Grafiken aus Matlab in LaTeX Möglichkeiten ausgelotet aus mit Matlab erzeugten Daten schicke Plots in LaTeX zu generieren. Sehr vielversprechend war dort das Paket pgfplots bei dem sich in den letzten Monaten einiges getan hat. Es liegt mittlerweile in Version 1.1 vor und hat neben dem Einbinden externer Datenquellen noch so manches dazugelernt. Ich möchte in diesem und einem folgenden Artikel an zwei Beispielen zeigen, wie man mit pgfplots tolle Ergebnisse produzieren kann.

Zunächst bleiben wir bei Matlab. Aus Matlab heraus ist es vergleichsweise einfach das .table-Format zu erzeugen, aus dem pgfplots Werte einlesen kann. Im einfachsten Fall benutzt man die Funktion dlmwrite. Will man auch noch Header und Kommentarzeilen in das Zwischenformat packen, muss man bisschen mehr Aufwand betreiben, ein entsprechendes Skript ist aber in kurzer Zeit fertiggestellt und bleibt dem geneigten Leser als Hausaufgabe überlassen. ;-) Meine Datei dahl_2008-08-19_05.table beginnt jedenfalls wie folgt:

time datadownsampled datadecimated dataresampled impactdownsampled impactdecimated impactresampled
7.2 0.0042314 0.0031181 0.0017701 7.6889e-005 7.5995e-005 4.3056e-005
7.201 0.010326 0.0048359 0.0049109 8.081e-005 8.0715e-005 8.7923e-005
7.202 0.013369 -0.0069568 -0.006471 7.9993e-005 8.087e-005 7.8394e-005
7.203 -0.011055 -0.010981 -0.011697 6.9248e-005 7.0035e-005 7.2734e-005

Es folgen knapp fünfhundert weitere Zeilen. Wie man bereits am Header sieht ist es empfehlenswert ein Downsampling der zu plottenden Daten vorzusehen, wenn eine Datenreihe mehr als ein paar hundert Elemente enthält. Diese spezielle Datei enthält in der ersten Spalte die Zeit, in den Spalten zwei bis vier ein Signal und in den Spalten fünf bis sieben ein zweites Signal. Für beide Signale wurden je drei verschiedene Downsampling-Verfahren ausgetestet, das jedoch nur nebenbei und zur Erläuterung, für den Plot werde ich nur die Spalten time und datadownsampled nutzen.

In LaTeX-Dokument sollten die notwendigen Pakete geladen sein, in diesem Fall sieht das bei mir so aus:

usepackage{tikz}
usepackage{pgfplots}
pgfplotsset{every axis plot/.append style={very thick}}

Die Einbindung selbst erfolgt wie gewohnt in einer float-Umgebung, die das ganze zentriert und auch noch label und Bildunterschrift bereit stellt. Statt includegraphics fügt man aber einfach eine tikzpicture-Umgebung ein:

begin{figure}[htb]
	centering
	begin{tikzpicture}
		begin{axis}[
			xlabel=Zeit (s),
			ylabel=Kraft (N),
			width=12cm,
			height=8cm]
		addplot[smooth,color=blue]
			table[x=time,y=datadownsampled] {plots/dahl_2008-08-19_05.table};
		end{axis}
	end{tikzpicture}
	caption{Rohdaten Messung texttt{dahl_2008-08-19_05}}
	label{fig:rohdaten_dahl_2008-08-19_05.txt}
end{figure}

Wichtig für das Lesen aus der externen .table-Datei ist die Anweisung table in der Zeile beginnend mit addplot. Das Ergebnis ist eine Vektorgrafik. Die Achsen sind wunderbar mit der im restlichen LaTeX-Dokument verwendeten Schrift beschriftet und zwar immer in der richtigen Größe. Mit extern als .eps oder ähnlichen Formaten eingebundenen Grafiken ist so etwas kaum möglich. Das Ergebnis spricht für sich:

Tiefenanordnung von Linien in Matlab-Plots

Heute geht es mal um ein sehr spezielles Problem mit einer Lösung auf die ich gestern durch Probieren gekommen bin und sehr erstaunt war, dass das so funktioniert und offenbar sogar so vorgesehen ist. Zunächst ein paar Worte zum ursprünglichen Problem.

Ich arbeite im Institut mit Matlab 6.1, zwar schon etwas in die Jahre gekommen aber brauchbar. Für die Auswertung meiner Messdaten habe ich ein GUI programmiert wo ich zwei verschiedene Sachen gleichzeitig in einem axes-Objekt darstellen will. Normalerweise kann man hier mit den Funktionen hold und plot arbeiten, aber mir war das zu unflexibel und ich habe daher gleich die LowLevel-Funktionen benutzt. Ich habe in dem GUI das gewünschte axes-Objekt. Direkt darunter in der Objekthierarchie kommen bereits die line-Objekte, die auch plot erzeugt. Von Hand kann man sich die mit dem Befehl line erzeugen, die landen dann in dem aktuellen axes-Objekt, das man zuvor mit axes(axeshandle) festlegen sollte. Bei der Erzeugung der line-Objekte speichere ich das line-Handle um später direkt wieder drauf zugreifen und beispielsweise die zugrundeliegenden Daten ändern zu können.

Soweit so gut, nun ist es so, dass ich nicht bestimmen kann, wann der Benutzer die eine Sache zum Plotten anklickt und wann die andere. Oben erscheint aber immer die zuletzt gewählte. Genauso verhält sich der Befehl plot. Was zuerst geplottet wird, landet hinten, die nachfolgenden Sachen darüber. Das ist bei vielen Darstellungen egal, bei einigen aber nicht.

Der Trick um die Darstellungsreihenfolge – oder englisch z-Order – zu ändern klingt ein wenig wie »von hinten durch die Brust ins Auge«, funktioniert aber wunderbar. Um zu verstehen, was dort gemacht werden muss, schaut man sich am besten nochmal die Objekthierarchie an. Ein axes-Objekt beherbergt beliebig viele line-Objekte. Die Handles dieser Objekte kann man sich ausgeben lassen:

linehandles = get(gca, 'Children')

Die einzelnen Elemente des Vektors linehandles haben genau die Reihenfolge der Anordnung in z-Richtung wobei das erste Element das zu oberst angezeigte ist. Es spricht nichts dagegen, die Elemente umzuordnen und dem axes-Objekt wieder zuzuweisen, z.B. in umgekehrter Reihenfolge:

set(gca, 'Children', flipud(get(gca, 'Children')))

Ich war einigermaßen überrascht, dass das tatsächlich so funktioniert, da ich in der ausführlichen Matlab-Hilfe nie darüber gestolpert bin. Dass das wirklich so vorgesehen ist, sagt einem Matlab selbst, wenn man versucht der Eigenschaft Children etwas anderes als ein umgeordnetes selbst zuzuweisen:

??? Error using ==> set
Children may only be set to a permutation of itself.

Damit das ganze noch ein wenig anschaulicher wird, habe ich noch ein kleines Skript geschrieben, dass die Sache nochmal illustriert:

% test data
a = rand(5,1)
b = rand(5,1)
c = rand(5,1)
% line handles
hp = plot([a b c])                  % hp in order we plotted
ha = get(gca, 'Children')           % reverse order
% vectors of line handles equal?
all(hp==ha)                         % not equal
all(hp==flipud(ha))                 % should be equal, see above
all(sort(hp)==sort(ha))             % also equal
% thick lines
set(ha, 'LineWidth', 10)
title('original order')
legend('a','b','c')                 % order as in plot call
print(gcf, '-dpng', 'z-order-original.png')
% color of the most front line, should be c
get(ha(1), 'Color')                 % should be red aka [1 0 0]
% now flip lines, put c in the back
set(gca, 'Children', [ha(2) ha(3) ha(1)])
title('flipped order')
print(gcf, '-dpng', 'z-order-flipped.png')
% color of the most front line, should be b
ha = get(gca, 'Children')
get(ha(1), 'Color')                 % should be green aka [0 0.5 0]

Schaut man sich hier die erzeugten PNG-Bilder an, erkennt man, dass die Linie für die Datenreihe c zunächst ganz vorn und dann ganz hinten dargestellt wird:

Schicke Grafiken aus Matlab in LaTeX

Ich arbeite mit LaTeX und ich arbeite mit Matlab. Bei meiner letzten Arbeit habe ich die Grafiken in Matlab einfach als .eps-Datei exportiert und unverändert in meine TeX-Dateien eingebunden. Das funktioniert problemlos, sieht aber nicht so schick aus, wie es könnte. Da ich für andere Grafiken das wunderbare Paket PGF und TikZ von Till Tantau benutze, habe ich heute Möglichkeiten ausgelotet wie man mit PGF Matlab-Grafiken schick machen könnte. Gründe dafür werden in der Doku von PGF genügend angeführt:

However, there are a number of reasons why you may wish to invest time and energy into mastering the PGF commands for creating plots:

  • Virtually all plots produced by »external programs« use different fonts from the one used in your document.
  • Even worse, formulas will look totally different, if they can be rendered at all.
  • Line width will usually be too large or too small.
  • Scaling effects upon inclusion can create a mismatch between sizes in the plot and sizes in the text.
  • The automatic grid generated by most programs is mostly distracting.
  • The automatic ticks generated by most programs are cryptic numerics. (Try adding a tick reading »π« at the right point.)
  • Most programs make it very easy to create »chart junk« in a most convenient fashion. All show, no content.
  • Arrows and plot marks will almost never match the arrows used in the rest of the document.

The above list is not exhaustive, unfortunately.

Neben PGF selbst gibt es das Paket pgfplots von Christian Feuersänger, das es leicht macht, einheitliche Plots zu erstellen und sogar logarithmische oder semi-logarithmische Achsen ermöglicht. Leider scheint man bei pgfplots im Gegensatz zu PGF selbst noch keine externen Dateien mit den zu plottenden Werten einbinden zu können. Das Skript matlab2pgfplots.m funktioniert leider nicht mit dem steinalten Matlab 6.1 hier im Institut, also habe ich noch nach weiteren Möglichkeiten gesucht.

Paul Wagenaar hat zwei interessante Tools geschrieben. Mathfig2pgf ist ebenfalls ein Matlab-Skript zur Umwandlung in PGF, funktioniert aber leider auch nicht mit Matlab 6.1. Das zweite Tool ist Eps2pgf und erinnert nicht von ungefähr an PSfrag. Beispiele für Ergebnisse von eps2pgf kann man sich auf fauskes.net ansehen.

Da ich nicht nur ein paar wenige Werte zu plotten habe, bin ich mir noch nicht sicher, welche Variante ich wählen werde. In Frage kommt außerdem noch die LowLevel-Variante mit PGF allein. Ein Beispiel wie man dort externe Datenquellen, z.B. den Output von gnuplot einbindet ist dokumentiert, allerdings muss man sich dann wieder Achsen etc. alles selbst bauen, also wird es wahrscheinlich die Variante mit eps2pgf werden, es sei denn pgfplots wird um die Funktion zur Nutzung externer Datenquellen erweitert. Kommt Zeit, kommt Rat, so dringend ist das Problem ja nicht.

MinusPiPiTransform

Da wird sich doch der eine oder andere fragen, was diese seltsame Überschrift zu bedeuten hat. *gg* Also heute in diesem Kino nicht etwa das Wasserlassen von Kleinkindern sondern:
Wie transformiere ich Winkel in das Intervall [−π, π]?

Das Problem stellte sich mir vor einigen Wochen im Rahmen meiner Studienarbeit das erste mal. Ich hatte zunächst einen Algorithmus entwickelt, der sich in einer Schleife so lange dem Zielintervall näherte, bis der Winkel transformiert war. Das ist zuverlässig, leicht zu durchschauen und langweilig. Meine Idee für eine elegante einzeilige Lösung war ein wenig mit der Signum-Funktion rumzuspielen und durch geschicktes Anwenden von Runden und aller vier Grundrechenarten die Geschichte mit der Schleife zu umgehen.

Im Prinzip gehe ich dabei ähnlich vor wie im Schleifenalgorithmus: ich addiere oder subtrahiere solange Vielfache von 2π bis ich im Zielintervall bin. Will ich das in einer Zeile tun, muss ich das notwendige Vielfache rausbekommen und das Vorzeichen.

Stellt man sich den allseits beliebten Zahlenstrahl vor – vorstellen mit Zettel und Stift ist leichter ;-) – erkennt man die Zuordnung:

  • [−5π, −3π] : 2
  • [−3π, −π] : 1
  • [−π, π] : 0
  • [π, 3π] : −1
  • [3π, 5π] : −2

Das Intervall [−π, π] um 0 rum stört. Das erste was ich deshalb tue: ich addiere sign(inAngle)*π zu meinem Einganswinkel inAngel. Dadurch schaffe ich mir zwar einen toten Bereich um 0 rum, aber die neuen Abschnitte haben ihre Grenzen bei vollen Vielfachen von 2π, so dass ich nun durch 2π teilen kann. Das Ergebnis runde ich derart, dass ich auf die nächste volle Zahl in Richtung 0 runde, d.h. ich runde bei positiven Zahlen ab und bei negativen auf oder kurz gesagt: ich schneide einfach die Nachkommastellen des Divisionsergebnisses ab. ;-) Damit habe ich schon die gesuchten Koeffizienten mit richtigem Vorzeichen. Die multipliziere ich dann wieder mit 2π und das Ergebnis ziehe ich vom Eingangswinkel ab, fertig. In Simulink sieht das dann aus wie auf dem folgenden Bild (zum Vergrößern anklicken):

Transformation von minus PI bis PI in Simulink

In Matlab liest sich das wie folgt:

function outAngle = MinusPiPiTransform(inAngle)
    outAngle = inAngle - ...
        fix( (inAngle + sign(inAngle)*pi) / (2*pi) ) * ...
        2*pi;
end%function

Diese Lösung hatte ich wie gesagt vor ein paar Wochen entwickelt. Heute brauchte ich das ganze wieder, allerdings in C# – kein Problem. Einfach den Teil aus dem Matlab-Skript in C# übersetzt und dann sieht das ganze so aus:

public double MinusPiPiTransform(double inAngle) {
    return inAngle -
        Math.Truncate((inAngle + Math.Sign(inAngle) * Math.PI) / (2 * Math.PI)) *
        2 * Math.PI;
}