Lektion 3 – Eigene Funktionen, Datentyp struct

In Lektion 3 des MATLAB-Kurses von matlab-tutorials.de lernen Sie im ersten Teil, wie Sie in MATLAB eigene Funktionen erstellen. Im zweiten Teil lernen Sie den Datentyp struct kennen. Abschließend zeige ich am Beispiel eines Aktienkurses, wie Sie das Gelernte anwenden können.

Funktionen in MATLAB definieren und aufrufen

Definition einer eigenen Funktion

Eigene Funktionen erstellen Sie in MATLAB über den Menüpunkt „New“ -> „Function“:

Bildschirmfoto 2016-02-08 um 21.48.31

Erstellen Sie nun eine neue Funktion. Daraufhin erscheint im Editor eine leere und unbenannte Funktion:

Bildschirmfoto 2016-02-08 um 21.50.34

In der ersten Zeile der Funktion steht zunächst das Schlüsselwort „function“. Danach stehen die Ausgabeargumente der Funktion. Nach dem Gleichheitszeichen steht der Name der Funktion. Über diesen Namen können Sie die Funktion später aufrufen. In den Klammern nach dem Namen stehen die Eingabeargumente.

Speichern Sie nun die Funktion unter „meineErsteFunktion.m“. Der Funktionsname muss in MATLAB immer dem Dateinamen entsprechen. Mit einem Rechtsklick auf den bisherigen Funktionsnamen können Sie die Funktion über „Replace function name by file name“ automatisch umbenennen: Daraufhin sollte die erste Zeile so aussehen:

function [ output_args ] = meineErsteFunktion( input_args )

Wir füllen nun die Funktion mit Leben. Die Funktion soll zwei Vektoren a und b aufnehmen und den Mittelwert und die Standardabweichung des Betrags der Differenz der beiden Vektoren ausgeben.

function [mittelwert, standardabweichung] = meineErsteFunktion(a, b)
%[mittelwert, standardabweichung] = meineErsteFunktion(a,b) berechnet
%Mittelwert und Standardabweichung von abs(b-a)
% geschrieben von Benjamin Becker, veröffentlicht unter
% MATLAB-Tutorials.de

differenz = abs(b-a);
mittelwert = mean(differenz);
standardabweichung = std(differenz);

end

Zeilen 2 und 3 enthalten eine kurze Beschreibung der Funktion. Diese kann später in der Hilfe angezeigt werden.

Die Zeilen 3 und 4 enthalten eine ausführlichere Beschreibung der Funktion. Tippen Sie meineErsteFunktion in die Kommandozeile und drücken Sie anschließend auf die Taste F1. Dadurch greifen Sie auf eine automatisch generierte Hilfe für Ihre Funktion zu:

Bildschirmfoto 2016-02-09 um 18.28.47

In Zeile 7 wird eine Hilfsvariable differenz erzeugt. Diese existiert in einem eigenen Workspace innerhalb der Funktion. Das bedeutet, dass die Variable nicht mehr vorhanden ist, sobald die Ausführung der Funktion beendet ist.

In den Zeilen 8 und 9 wird den Ausgabeparametern mittelwert und standardabweichung ein Wert zugewiesen. Sie können Ihre eigene Funktion nun aufrufen, solange sie sich in Ihrem aktuellen Verzeichnis oder im MATLAB-Suchpfad befindet. Tippen Sie dafür z.B. folgendes in die Kommandozeile:

>> meineErsteFunktion(1:10,15:2:33)

ans =

   18.5000

Funktionen mit mehreren Ausgabeparametern aufrufen

Wie Sie im letzten Beispiel gesehen haben, erhielten Sie nur den ersten Ausgabeparameter zurück. Wenn Sie beide Parameter benötigen, können Sie die Funktion folgendermaßen aufrufen:

>>  [mittel,standardabw] = meineErsteFunktion(1:10,15:2:33)

mittel =

   18.5000


standardabw =

    3.0277

Sie weisen die beiden Ausgaben damit den Variablen mittel und standardabw zu.

Falls Sie nur den zweiten Ausgabeparameter benötigen, können Sie die Funktion folgendermaßen aufrufen:

>>  [~,standardabw] = meineErsteFunktion(1:10,15:2:33)

standardabw =

    3.0277

Definieren von Funktionen mit variabler Anzahl von Eingabeparametern

In MATLAB können Funktionen so definiert werden, dass die Anzahl der übergebenen Eingangsparameter variabel ist. Dabei können die weiter hinten stehenden Eingangsparameter beim Aufruf der Funktion weggelassen werden. Dafür wird die MATLAB-Funktion nargin benötigt. Sie zählt bei Aufruf innerhalb einer Funktion die Anzahl der übergebenen Eingangsparameter. Die folgende Funktion verlangt mindestens 2 Eingangsparameter und es können höchstens 4 Eingangsparameter übergeben werden:

function [x_t] = funktionMitVariablemEingang(v0,t,x0,a)
%funktionMitVariablemEingang(v0,t,x0,a0): Ort nach Zeit t bei Geschw. v0
%   Als weitere Parameter können der Ort zum Zeitpunkt 0 x0 und die
%   Beschleunigung a übergeben werden. Wenn diese nicht
%   übergeben werden, wird für beide jeweils der Wert 0 angenommen.

if nargin < 3
x0 = 0; % wird ausgeführt, wenn weniger als 3 Eingangsargumente
end
if nargin < 4
a = 0; % wird ausgeführt, wenn weniger als 4 Eingangsargumente
end
x_t = x0 + t*v0 + 1/2*a*t.^2;

end

In den Zeilen 7-9 sowie 10-13 habe ich bedingte Anweisungen verwendet. Diese sind jeweils nach folgendem Schema aufgebaut:

if Bedingung
Anweisungen
end

Die Anweisungen werden nur dann ausgeführt, wenn die Bedingung erfüllt ist. Die Bedingung in Zeile 7 ist erfüllt, falls weniger als 3 Eingabeargumente übergeben wurden. In diesem Fall soll der Eingabeparameter x0 auf den Wert 0 gesetzt werden.

Definieren von Funktionen mit Parameter-Wert-Aufruf

In MATLAB gibt es eine weitere Möglichkeit, die Eingabeparameter an eine Funktion zu übergeben, der sogenannte Parameter-Wert-Aufruf. Ein Beispiel ist die in Lektion 2 vorgestellte Funktion plot:

>> plot(x,y,'color','g','linestyle','--','linewidth',2)

Die Parameter color, linestyle und linewidth können dabei in beliebiger Reihenfolge und jeweils gefolgt von einem Wert übergeben werden.

Zur Implementierung eigener Funktionen mit einer Übergabe nach dem gleichen Schema steht in MATLAB die Klasse inputParser zur Verfügung. Auf den Begriff der Klasse werde ich noch bei der Behandlung der objektorientierten Programmierung in Teil II dieses Kurses zurückkommen. Sie müssen den Begriff aber im aktuellen Zusammenhang noch nicht verstehen, um die Klasse anzuwenden. Im folgenden Beispiel definieren wir eine Funktion mit Parameter-Wert-Übergabe:

function [output] = funktionParameterWert(a,b,varargin)
%funktionParameterWert(a,b,'parameter','wert') gibt Eingangsparameter zurück
%   'parameter' kann 'c' und 'd' sein. Werden für die Parameter keine Werte
%   übergeben, so werden die Standardwerte c=3 und d=4 gesetzt.
p = inputParser;
p.addParameter('c',3); % definiert Eingabeparameter c mit Standardwert 3
p.addParameter('d',4); % definiert Eingabeparameter d mit Standardwert 4
p.parse(varargin{:}); % interpretiert die übergebenen Parameter

c = p.Results.c;
d = p.Results.d;

output = [a b c d];
end

Sie können den Funktionstext kopieren und die Funktion selbst erstellen oder die Funktion aus den Downloads zu dieser Lektion nutzen. Rufen Sie die Funktion folgendermaßen auf und schauen Sie sich die Ausgabe von Matlab an:

Beispiel 1:

>> funktionParameterWert(1,2,'c',50)

ans =

     1     2    50     4

Beispiel 2:

>> funktionParameterWert(1,2)

ans =

     1     2     3     4

Beispiel 3:

>> funktionParameterWert(1,2,'d',pi,'c',50)

ans =

    1.0000    2.0000   50.0000    3.1416

Der Datentyp struct

Es ist häufig sinnvoll, mehrere Variablen in einer Struktur zusammenzufassen statt sie „lose“ im Workspace herumliegen zu haben. Dafür steht in MATLAB der Datentyp struct zur Verfügung.

Erzeugen einer Struktur

Eine Variable vom Typ struct können Sie beispielsweise folgendermaßen erzeugen:

>> a.feld1 = [1 2 3]

a = 

    feld1: [1 2 3]

Sie haben damit die Variable a erzeugt und im gleichen Schritt die Variable feld1 erzeugt und diese in die Struktur eingefügt. Wenn Sie nun eine zweite Variable anderesFeld mit dem Wert 15 hinzufügen möchten, können  Sie dies über folgenden Befehl erzeugen:

>> a.anderesFeld = 15

a = 

          feld1: [1 2 3]
    anderesFeld: 15

Die untergeordneten Variablen können dabei wieder vom Typ struct sein. Dadurch werden baumartige Strukturen möglich.

Eine weitere Möglichkeit zur Erzeugung einer Struktur ist der Befehl struct. Sie gleiche Struktur wie im obigen Beispiel können Sie damit folgendermaßen erzeugen:

>> a = struct('feld1',[1 2 3],'anderesFeld',15)

Zugriff auf Strukturen

Auf die einzelnen Variablen einer Struktur greifen Sie mit der gleichen Notation zu wie bei der Erzeugung:

>> a.feld1

ans =

     1     2     3

Es gibt noch eine weitere Möglichkeit auf Strukturen zuzugreifen. Diese bietet sich an, wenn man den Namen der untergeordneten Variablen in einer Variablen vom Typ char gespeichert hat:

>> feldName = 'anderesFeld'; %erzeugt Variable vom Typ char
>> a.(feldName)

ans =

    15

Diese Möglichkeit bietet sich dann an, wenn der Feldname erst während des Laufens eines MATLAB-Skripts oder einer MATLAB-Funktion definiert werden soll oder wenn eine Schleife über mehrere Feldnamen laufen soll.

Feldnamen einer Struktur ermitteln

Die Felnamen einer Struktur erhalten Sie über den Befehl fieldnames:

>> feldnamen = fieldnames(a)

feldnamen = 

    'feld1'
    'anderesFeld'

>> feldnamen{2}

ans =

anderesFeld

Die Funktion gibt eine Variable vom Typ cell zurück. Mit den geschweiften Klammern können Sie auf die einzelnen Elemente zugreifen. Mehr über den Datentyp cell erfahren Sie in Lektion 4.

Einen Befehl auf alle Elemente einer Struktur anwenden

Wenn Sie einen Befehl auf alle Elemente einer Struktur anwenden wollen, können Sie dies über den Befehl structfun tun. Mehr zu structfun finden Sie in diesem Blogbeitrag.

Anwendungsbeispiel: Börsenkurs der Amazon-Aktie darstellen

Import des Aktienkurses aus csv-Datei

In den Download zu dieser Lektion finden Sie im Unterordner „Beispiel Amazon Aktie“ die Datei amazon.csv. Die Datei enthält Börsenkurse der Aktie von Amazon, die ich von yahoo finance heruntergeladen habe. Sie können die Datei in einem Texteditor (z.B. dem von MATLAB) öffnen und sich den Aufbau ansehen. MATLAB bietet die Möglichkeit Daten aus csv-Dateien (Comma Separated Values) zu importieren. Dazu müssen Sie mit der rechten Maustaste auf die Datei klicken und dann den Menüpunkt „Import Data…“ auswählen:

Kontextmenü Import Data

Daraufhin erscheint ein Fenster, in dem Sie den Import genauer definieren können. Ich musste keine Anpassungen vornehmen, und das Fenster sah so aus:

Menüpunkt Import Selection - srcset= Generate Function“ width=“612″ height=“391″>

Klicken Sie, wie im Bild oben zu sehen, auf den Button „Import Selection“ und dann auf „Generate Function“. Dadurch wird eine MATLAB-Funktion erzeugt, mit der Sie den Import gleich aufgebauter Dateien aus einem Skript oder einer anderen Funktion heraus durchführen können. So können Sie jederzeit die Datei amazon.csv durch eine andere gleich aufgebaute Datei oder eine aktuellere Version ersetzen.

Die Import-Funktion sieht so aus:

function [Date1,Open1,High,Low,Close1,Volume,AdjClose] = importAktienkurs(filename, startRow, endRow)
%IMPORTFILE Import numeric data from a text file as column vectors.
%   [DATE1,OPEN1,HIGH,LOW,CLOSE1,VOLUME,ADJCLOSE] = IMPORTFILE(FILENAME)
%   Reads data from text file FILENAME for the default selection.
%
%   [DATE1,OPEN1,HIGH,LOW,CLOSE1,VOLUME,ADJCLOSE] = IMPORTFILE(FILENAME,
%   STARTROW, ENDROW) Reads data from rows STARTROW through ENDROW of text
%   file FILENAME.
%
% Example:
%   [Date1,Open1,High,Low,Close1,Volume,AdjClose] =
%   importfile('amazon.csv',2, 1861);
%
%    See also TEXTSCAN.

% Auto-generated by MATLAB on 2016/02/26 18:24:29

%% Initialize variables.
delimiter = ',';
if nargin<=2
startRow = 2;
endRow = inf;
end

%% Format string for each line of text:
%   column1: text (%s)
%	column2: double (%f)
%   column3: double (%f)
%	column4: double (%f)
%   column5: double (%f)
%	column6: double (%f)
%   column7: double (%f)
% For more information, see the TEXTSCAN documentation.
formatSpec = '%s%f%f%f%f%f%f%[^\n\r]';

%% Open the text file.
fileID = fopen(filename,'r');

%% Read columns of data according to format string.
% This call is based on the structure of the file used to generate this
% code. If an error occurs for a different file, try regenerating the code
% from the Import Tool.
dataArray = textscan(fileID, formatSpec, endRow(1)-startRow(1)+1, 'Delimiter', delimiter, 'HeaderLines', startRow(1)-1, 'ReturnOnError', false);
for block=2:length(startRow)
frewind(fileID);
dataArrayBlock = textscan(fileID, formatSpec, endRow(block)-startRow(block)+1, 'Delimiter', delimiter, 'HeaderLines', startRow(block)-1, 'ReturnOnError', false);
for col=1:length(dataArray)
dataArray{col} = [dataArray{col};dataArrayBlock{col}];
end
end

%% Close the text file.
fclose(fileID);

%% Post processing for unimportable data.
% No unimportable data rules were applied during the import, so no post
% processing code is included. To generate code which works for
% unimportable data, select unimportable cells in a file and regenerate the
% script.

%% Allocate imported array to column variable names
Date1 = dataArray{:, 1};
Open1 = dataArray{:, 2};
High = dataArray{:, 3};
Low = dataArray{:, 4};
Close1 = dataArray{:, 5};
Volume = dataArray{:, 6};
AdjClose = dataArray{:, 7};

Speichern Sie diese Datei unter dem Namen „importAktienkurs.m“. Die Datei hat nun einen anderen Namen als die Funktion, die standardmäßig „importfile“ heißt. Da es sicher nicht Ihre letzte Import-Funktion sein wird, ist es sinnvoll ihr einen anderen Namen zu geben. Sie können den Funktionsnamen anschließend automatisch an den Dateinamen anpassen, indem Sie mit der rechten Maustaste auf den Funktionsnamen klicken und dann den Menüpunkt „replace function name by file name“ wählen. Anschließend müssen Sie die Funktion nochmals speichern.

Replace function name by file name

Darstellung des Aktienkurses als Linienplot

Für die Darstellung des Aktienkurses habe ich ein eigenes Skript mit folgendem Inhalt geschrieben:

% Kurse aus csv-Datei laden
dateiname = 'amazon.csv';

[Date1,Open1,High,Low,Close1,Volume,AdjClose] = importAktienkurs(dateiname);

% Kurse in struct-Variable überführen
aktien.AMZ = struct('date',datenum(Date1),'open',Open1,'high',High,'low',Low,'close',Close1);

% Alle Variablen bis auf aktien löschen
clearvars -except aktien

% plot erzeugen
plot(aktien.AMZ.date,[aktien.AMZ.low,aktien.AMZ.high])
% Datum auf x-Achse darstellen
datetick('x')
% Legende erzeugen
legend('AMZ.DE (Tagestief)','AMZ.DE (Tageshoch)')
% Achsenbeschriftungen erzeugen
xlabel('Datum')
ylabel('Kurs in Euro')

In Zeile 3 benutze ich die vorher erzeugte Import-Funktion importAktienkurs um die csv-Datei mit den Börsenkursen zu importieren.

In Zeile 7 speichere ich die verschiedenen Kurse in einer struct-Variable. Der Befehl datenum wandelt dabei das als Text vorliegende Datum in fortlaufende Zahlen um. Diese Zahlen werden von vielen MATLAB-Funktionen im Zusammenhang mit Datums- und Zeitwerten verwendet.

In Zeile 15 sorge ich dafür, dass in dem Plot die fortlaufenden Zahlen auf der x-Achse wieder als Datum interpretiert werden. Dies geschieht über den Befehl datetick.

Das erzeugte Diagramm sieht dann so aus:

Aktienkurs Amazon Aktie

Download der Beispiele

Alle Beispiele dieser Lektion können Sie hier herunterladen.

Zusammenfassung

MATLAB-Funktionen besten aus einer Datei deren Dateinamen dem Namen der Funktion entspricht. Die Funktion wird in einem Block definiert, an dessen Beginn das Schlüsselwort function steht.

Mit der Funktion nargin können Sie innerhalb von Funktionen abfragen, wie viele Eingangsparameter übergeben wurden.

Über die MATLAB-Klasse inputParser können Sie Funktionen mit Parameter-Wert-Aufrufen definieren.

Variablen vom Typ struct können Sie entweder über
variablenname.feldname = wert
oder über die Funktion
struct('feldname1',wert1,'feldname2',wert2) erzeugen.

Quiz

Hier gelangen Sie zu einem kurzen Quiz mit Fragen zu Lektion 3.

Fortsetzung

Hier geht es weiter mit Lektion 4 des MATLAB-Kurses.

Hier gelangen Sie zum Inhaltsverzeichnis von Teil 1.