Python und die IPython-Shell

  • Programmiersprache Python
  • Darüber IPython Shell (Ein- und Ausgabemaske) für benutzerfreundliche(re) Ein- und Ausgabe
  • Befehle werden einfach eingegeben und direkt ausgeführt
  • Ergebnis wird direkt angezeigt
  • Ausgabe kann mit einem Semikolon (";") unterdrückt werden
In [1]:
"Hallo Welt"
Out[1]:
'Hallo Welt'
In [2]:
42
Out[2]:
42
In [3]:
42;
  • Es können mehrere Befehle auf einmal ausgeführt werden
  • Befehle werden durch manuelle Zeilenumbrüche (Strg+<return>-Taste) oder Semikolon (";") getrennt werden
  • Nach einem manuellen Zeilenumbruch müssen zwei Zeilenumbrüche folgen, um den/die Befehle abzusetzen
  • Es wird nur das Ergebnis des letzten Befehls ausgegeben
In [4]:
"Hallo Welt"; "Hallo Welt2"
"Hallo Welt3"
42
Out[4]:
42
  • Kommentare sind zur Dokumentation des Quellcodes gedacht
  • Sie beginnen mit einer Raute (Nummernzeichen, "Hashtag", Doppelkreuz) ("#")
  • Werden oft dazu zweckentfremdet, Quellcode zu "deaktivieren"
In [5]:
# Dies ist ein Kommentar

Einführung in die Programmiersprache Python

Zahlentypen und Datentypen

Ganze Zahl

  • engl.: integer
  • Python-Typ: int
In [6]:
1
Out[6]:
1

Gleitkommazahl

  • engl.: floating point number
  • Python-Typ: float
  • Das "Komma" ist ein Punkt (".")
  • Exponentialdarstellung: <m>e<exp> entspricht ${\tt <\!\!m\!\!>} \cdot 10^{{\tt }}$
In [7]:
1.
Out[7]:
1.0
In [8]:
1.3
Out[8]:
1.3
In [9]:
3.3e-4
Out[9]:
0.00033

Komplexe Zahlen

  • engl.: complex number
  • Python-Typ: complex
In [10]:
1j
Out[10]:
1j

Wahrheitswerte

  • Boolesche Werte (wahr oder falsch)
  • engl. Boolean value
  • Python-Typ: bool
In [11]:
True
Out[11]:
True
In [12]:
False
Out[12]:
False

Das Nichts

  • (eindeutiger) Typ und Wert für alles, das nicht existiert
  • engl. none
  • Python-Typ: NoneType
  • Jupyter versteckt None bzw. den NoneType als Ausgabe
In [13]:
None

None ist das Ergebnis, wenn es (eigentlich) kein Ergebnis gibt -- wie bei der print-Funktion:

In [14]:
print(print("a"))
a
None

Text

  • Zeichenkette
  • engl.: string of characters, kurz: string
  • Python-Typ: str
In [15]:
'Hallo'
Out[15]:
'Hallo'
In [16]:
"Welt"
Out[16]:
'Welt'
In [17]:
"""
Mehr-
zeiliger
Text!
"""
Out[17]:
'\nMehr-\nzeiliger\nText!\n'

Liste

  • Mehrfachvorkommende Elemente und Reihenfolge spielen eine Rolle
  • veränderbare Liste (später)
  • Python-Typ: list
In [18]:
[ 1, 2, "Peter", 15.7, 1. + 2.j, 2 ]
Out[18]:
[1, 2, 'Peter', 15.7, (1+2j), 2]

Tupel

  • Mehrfachvorkommende Elemente und Reihenfolge spielen eine Rolle
  • unveränderliche Liste
  • Python-Typ: tuple
  • Klammern sind optional
In [19]:
( 1, 2, "Peter" )
Out[19]:
(1, 2, 'Peter')
In [20]:
1, 2, "Peter"
Out[20]:
(1, 2, 'Peter')

Rechenoperationen mit einfachen Zahlen

In [21]:
5 + 4.6
Out[21]:
9.6
In [22]:
5 - 2.7
Out[22]:
2.3
In [23]:
5 * 4
Out[23]:
20
In [24]:
2j * 4j
Out[24]:
(-8+0j)
In [25]:
7 / 5
Out[25]:
1.4

Zahlen werden immer als endliche Kommazahlen gespeichert. Stichwort Gleitkommaarithmetik, säter in der Vorlesung.

In [26]:
1 / 17
Out[26]:
0.058823529411764705
In [27]:
15 // 2 # Ganzzahlige Division
#int(15 / 2)
Out[27]:
7
In [28]:
5 ** 2 # Potenz
Out[28]:
25
In [29]:
25 ** (1/2) # Achtung: Ergebnis keine ganze Zahl!
Out[29]:
5.0
In [30]:
15 % 2 # Modulo
Out[30]:
1

In- und Dekrement-Operationen

In [31]:
a = 1;
a += 1;
a
Out[31]:
2
In [32]:
a -= 3;
a
Out[32]:
-1

Klammerung bevorzugt, sonst gilt Potenz > Punkt- > Strichrechnung

Nur Runde Klammern "(" bzw. ")"

In [33]:
2 * 3 + 1
Out[33]:
7
In [34]:
2 * (3 + 1)
Out[34]:
8
In [35]:
2 + 2 * 2 ** 2
Out[35]:
10

Logische Operationen, Boole'sche Algebra

Logisches "und" (and)

In [36]:
True and True, True and False, False and True, False and False
Out[36]:
(True, False, False, False)

"oder" (or)

In [37]:
True or True, True or False, False or True, False or False
Out[37]:
(True, True, True, False)

Invertieren (not)

In [38]:
not True, not False
Out[38]:
(False, True)

Ausschließliches Oder (xor)

In [39]:
True ^ True, True ^ False, False ^ True, False ^ False
Out[39]:
(False, True, True, False)

Vergleiche

In [40]:
1 == 2, not 1 == 2, 1 > 2, 1 < 2
Out[40]:
(False, True, False, True)
In [41]:
"Hans" == "Hans", "Hans" == "hans", "Hans" == "Peter"
Out[41]:
(True, False, False)

in-Operator

In [42]:
1 in [ 1, 2, 3 ], 1 in ( 0, 2, 4 )
Out[42]:
(True, False)

Kompliziertere Funktionen, Konstanten, Operatoren

Modul 'numpy' einbinden, um mehr Funktionalität zu erhalten

In [43]:
import numpy as np; # Später genauer
In [44]:
np.sqrt(25)
Out[44]:
5.0
In [45]:
np.pi
Out[45]:
3.141592653589793
In [46]:
np.e
Out[46]:
2.718281828459045
In [47]:
np.exp(2)
Out[47]:
7.3890560989306504
In [48]:
np.log(1)
Out[48]:
0.0
In [49]:
np.abs(-5)
Out[49]:
5

Weitere Funktionen "entdecken" mit der Tabulator-Taste (unter dem Hütchen "^")

Eingabe:

[ ]: np. <tab>

In [50]:
np.log10(1000)
Out[50]:
3.0

Es gibt auch "nicht-mathematische" Funktionen

Diese müssen nicht unbedingt einen Wert zurück liefern

In [51]:
type("Hallo?")
Out[51]:
str
In [52]:
print("Hallo?") # Achtung: kein "Out"
Hallo?
In [53]:
np.linspace(0, 1, 5)
Out[53]:
array([ 0.  ,  0.25,  0.5 ,  0.75,  1.  ])

Zeichenkettenformatierung

In [54]:
"{0}-{1}, etwa {2}".format("Gauß", "Algorithmus", 1810);

Formatierung angeben (Typ, Länge/Stellen) (Achtung: falsche Typangabe führt zu Fehlermeldung)

In [55]:
"|{0:6d}|, |{1:5.2f}%| |{2:>10s}|".format(1810, 1.2, "Hallo")
Out[55]:
'|  1810|, | 1.20%| |     Hallo|'

Argumentlisten

In [56]:
liste = [ 42, 17 ];
print(liste);
[42, 17]
In [57]:
print(*liste);
42 17

Ist äquivalent zu

In [58]:
print(liste[0], liste[1]);
42 17
In [59]:
"{0} zu {1}".format(*liste)
Out[59]:
'42 zu 17'

Hilfe

In [60]:
help(np.linspace);
Help on function linspace in module numpy.core.function_base:

linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
    Return evenly spaced numbers over a specified interval.
    
    Returns `num` evenly spaced samples, calculated over the
    interval [`start`, `stop`].
    
    The endpoint of the interval can optionally be excluded.
    
    Parameters
    ----------
    start : scalar
        The starting value of the sequence.
    stop : scalar
        The end value of the sequence, unless `endpoint` is set to False.
        In that case, the sequence consists of all but the last of ``num + 1``
        evenly spaced samples, so that `stop` is excluded.  Note that the step
        size changes when `endpoint` is False.
    num : int, optional
        Number of samples to generate. Default is 50. Must be non-negative.
    endpoint : bool, optional
        If True, `stop` is the last sample. Otherwise, it is not included.
        Default is True.
    retstep : bool, optional
        If True, return (`samples`, `step`), where `step` is the spacing
        between samples.
    dtype : dtype, optional
        The type of the output array.  If `dtype` is not given, infer the data
        type from the other input arguments.
    
        .. versionadded:: 1.9.0
    
    Returns
    -------
    samples : ndarray
        There are `num` equally spaced samples in the closed interval
        ``[start, stop]`` or the half-open interval ``[start, stop)``
        (depending on whether `endpoint` is True or False).
    step : float, optional
        Only returned if `retstep` is True
    
        Size of spacing between samples.
    
    
    See Also
    --------
    arange : Similar to `linspace`, but uses a step size (instead of the
             number of samples).
    logspace : Samples uniformly distributed in log space.
    
    Examples
    --------
    >>> np.linspace(2.0, 3.0, num=5)
    array([ 2.  ,  2.25,  2.5 ,  2.75,  3.  ])
    >>> np.linspace(2.0, 3.0, num=5, endpoint=False)
    array([ 2. ,  2.2,  2.4,  2.6,  2.8])
    >>> np.linspace(2.0, 3.0, num=5, retstep=True)
    (array([ 2.  ,  2.25,  2.5 ,  2.75,  3.  ]), 0.25)
    
    Graphical illustration:
    
    >>> import matplotlib.pyplot as plt
    >>> N = 8
    >>> y = np.zeros(N)
    >>> x1 = np.linspace(0, 10, N, endpoint=True)
    >>> x2 = np.linspace(0, 10, N, endpoint=False)
    >>> plt.plot(x1, y, 'o')
    [<matplotlib.lines.Line2D object at 0x...>]
    >>> plt.plot(x2, y + 0.5, 'o')
    [<matplotlib.lines.Line2D object at 0x...>]
    >>> plt.ylim([-0.5, 1])
    (-0.5, 1)
    >>> plt.show()

Besser in IPython (beenden mit q-Taste):

In [61]:
?np.linspace
In [62]:
np.linspace(0, 1, 5, retstep = True)
Out[62]:
(array([ 0.  ,  0.25,  0.5 ,  0.75,  1.  ]), 0.25)

Variablen deklarieren

  • Variablen sind Platzhalter, die Werte annehmen können
  • Nicht im mathematischen Sinne: Variablen sind veränderbar

Zuweisung

In [63]:
a = 1;
In [64]:
a
Out[64]:
1
In [65]:
b = 1.; s = "Hallo Welt";
b, s
Out[65]:
(1.0, 'Hallo Welt')

Was kenne ich im Moment?

In [66]:
%whos
Variable   Type      Data/Info
------------------------------
a          int       1
b          float     1.0
liste      list      n=2
np         module    <module 'numpy' from '/ho<...>kages/numpy/__init__.py'>
s          str       Hallo Welt

Das Modul np erscheint auch als Variable

In [67]:
type(a)
Out[67]:
int

Um das Ergebnis zu sehen print verwenden oder die Variable am Ende wiederholen

In [68]:
a = 1;
print(a);
print("a = {0}".format(a));
a
1
a = 1
Out[68]:
1

Achtung: das Folgende ist keine logisch falsche Aussage, sondern eine Zuweisung

In [69]:
a = a + 1;
print(a);
2

Erste einfache eigene Funktionen

In [70]:
f = lambda x: 1 / (1 + x**2);
print(f(0), f(1), f(-1))
%whos
1.0 0.5 0.5
Variable   Type        Data/Info
--------------------------------
a          int         2
b          float       1.0
f          function    <function <lambda> at 0x7fd8807a5d90>
liste      list        n=2
np         module      <module 'numpy' from '/ho<...>kages/numpy/__init__.py'>
s          str         Hallo Welt

Module

  • Module sind Sammlungen von Variablen
  • Diese Variablen können Konstanten, Funktionen, etc. sein
  • Sie erweitern die Funktionalität der Sprache
In [71]:
import numpy;
numpy.cos(1)
Out[71]:
0.54030230586813977
In [72]:
%whos
Variable   Type        Data/Info
--------------------------------
a          int         2
b          float       1.0
f          function    <function <lambda> at 0x7fd8807a5d90>
liste      list        n=2
np         module      <module 'numpy' from '/ho<...>kages/numpy/__init__.py'>
numpy      module      <module 'numpy' from '/ho<...>kages/numpy/__init__.py'>
s          str         Hallo Welt

Alles wieder auf Anfang setzen (nichts ist geladen)

In [73]:
%reset -f
In [74]:
%whos
Interactive namespace is empty.
In [75]:
import numpy as np;

Python Listen, Tupel und Dictionaries

Zugriff

Der Zugriffoperator sind eckige Klammern "[" und "]". Es wird von Null ab indiziert.

In [76]:
a = [ 1, 2, 3, 4, 5, 6, 7, 8 ]
b = 5, 6, 7, 8
a[0], b[2], a[3]
Out[76]:
(1, 7, 4)

Negaive Indizes zählen von hinten

Dabei indiziert -1 das letzte Element, -2 das Vorletzte, usw..

In [77]:
a[-1], a[-2]
Out[77]:
(8, 7)

Slicing

Zugriff auf Teillisten/-tupel

Der "Endindex" wird nicht mitgenommen, ist also exklusiv.

In [78]:
a[1:3] # Index 1 bis zu dem vor 3, d.h. 2.-3. Element
Out[78]:
[2, 3]

Slicing behält den Typ bei (d.h. lists bleiben lists, tuples bleiben tuples, etc.)

In [79]:
b[1:3]
Out[79]:
(6, 7)

Die Endindizes können weggelassen werden, wenn das erste bzw. letzte Argument gewählt werden soll

In [80]:
a[:2], a[0:2], a[2:], a[2:8]
Out[80]:
([1, 2], [1, 2], [3, 4, 5, 6, 7, 8], [3, 4, 5, 6, 7, 8])

In größeren Schritten

In [81]:
a[1:5:2]
Out[81]:
[2, 4]
In [82]:
a[::2]
Out[82]:
[1, 3, 5, 7]
In [83]:
b[::3]
Out[83]:
(5, 8)

Rückwärts

In [84]:
a[-1:-5:-1]
Out[84]:
[8, 7, 6, 5]

Erzeugung als Ranges

Erzeuge die Zahlen 5 (inklusive) bis 10 (exklusive) in Zweierschritten

In [85]:
a = range(5, 10, 2); 
list(a)
Out[85]:
[5, 7, 9]

Zuweisung

Tuples sind unveränderbar

In [86]:
a[1] = 3
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-86-2779ae474d90> in <module>()
----> 1 a[1] = 3

TypeError: 'range' object does not support item assignment

Listen sind veränderbar

In [87]:
a = list(a);
print(a);
a[1] = 3
print(a)
[5, 7, 9]
[5, 3, 9]

Achtung: Alle Variablen sind Referenzen:

In [88]:
b = a;
b[1] = 5;
print(a) # Auch hier geändert!
[5, 5, 9]

Ausweg: Kopieren

In [89]:
b = list(a);
b[1] = 7;
print(a, b)
[5, 5, 9] [5, 7, 9]

join und split

In [90]:
print(", ".join([ "a", "b", "c" ]))
a, b, c
In [91]:
"Hallo Welt ??".split(" ")
Out[91]:
['Hallo', 'Welt', '??']

numpy arrays: Matrizen, Vektoren und Lineare Algebra

In [92]:
import numpy as np;
import scipy.linalg as la;

Wieso ein zusätzlicher Listentyp?

In [93]:
# Ein 3x2-Array in python
a1 = [ 1, 2, 3 ];
b1 = [ 4, 5, 6 ];
c1 = [ a1, b1 ];
print(c1)
[[1, 2, 3], [4, 5, 6]]

Schönere Darstellung

In [94]:
c = np.array(c1);
print(c);
[[1 2 3]
 [4 5 6]]

Mehrdimensionalität

In [95]:
c[1, 1]
Out[95]:
5
In [96]:
d = np.array([c, c])
d
Out[96]:
array([[[1, 2, 3],
        [4, 5, 6]],

       [[1, 2, 3],
        [4, 5, 6]]])
In [97]:
d[1, 1, 1]
Out[97]:
5
In [98]:
np.array([ d, d ])
Out[98]:
array([[[[1, 2, 3],
         [4, 5, 6]],

        [[1, 2, 3],
         [4, 5, 6]]],


       [[[1, 2, 3],
         [4, 5, 6]],

        [[1, 2, 3],
         [4, 5, 6]]]])
In [99]:
print(a.ndim, c.ndim, d.ndim) # Anzahl der "Dimensionen"
print(a.shape, c.shape, d.shape) # Anzahl der Elemente pro "Dimension"
print(a.size, c.size, d.size) # Anzahl der Elemente
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-99-b9493b355410> in <module>()
----> 1 print(a.ndim, c.ndim, d.ndim) # Anzahl der "Dimensionen"
      2 print(a.shape, c.shape, d.shape) # Anzahl der Elemente pro "Dimension"
      3 print(a.size, c.size, d.size) # Anzahl der Elemente

AttributeError: 'list' object has no attribute 'ndim'

Direkte Anwenung von Funktionen (komponentenweise)

In [100]:
a * b
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-100-50927f39610b> in <module>()
----> 1 a * b

TypeError: can't multiply sequence by non-int of type 'list'

Operationen

In [101]:
a, b, c
Out[101]:
([5, 5, 9], [5, 7, 9], array([[1, 2, 3],
        [4, 5, 6]]))
In [102]:
a + b
Out[102]:
[5, 5, 9, 5, 7, 9]

Automatische Expansion fehlender Dimension

Achtung: Dies kann eine Falle sein!

In [103]:
a + c
Out[103]:
array([[ 6,  7, 12],
       [ 9, 10, 15]])

Diese Zeile ist zu verstehen als

In [104]:
np.array([a, a]) + c
Out[104]:
array([[ 6,  7, 12],
       [ 9, 10, 15]])

Aber:

In [105]:
a + np.array([ 1, 2 ])
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-105-af80bcedeac2> in <module>()
----> 1 a + np.array([ 1, 2 ])

ValueError: operands could not be broadcast together with shapes (3,) (2,) 

Weitere Operationen

In [106]:
a - b, a / b
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-106-e2b2deb923ae> in <module>()
----> 1 a - b, a / b

TypeError: unsupported operand type(s) for -: 'list' and 'list'
In [107]:
np.cos(a)
Out[107]:
array([ 0.28366219,  0.28366219, -0.91113026])
In [108]:
f = lambda x: 1 / (1 + x**2);
f(a)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-108-29404e4cfb9c> in <module>()
      1 f = lambda x: 1 / (1 + x**2);
----> 2 f(a)

<ipython-input-108-29404e4cfb9c> in <lambda>(x)
----> 1 f = lambda x: 1 / (1 + x**2);
      2 f(a)

TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'
In [109]:
a = np.array([ True, True, False, False ]);
b = np.array([ True, False, True, False ]);

Leider:

In [110]:
a or b
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-110-428fd34566eb> in <module>()
----> 1 a or b

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Ausweg:

In [111]:
np.logical_or(a, b)
Out[111]:
array([ True,  True,  True, False], dtype=bool)

Eigenschaften und Methoden

In [112]:
c
Out[112]:
array([[1, 2, 3],
       [4, 5, 6]])
In [113]:
c.T, c.conj()
Out[113]:
(array([[1, 4],
        [2, 5],
        [3, 6]]), array([[1, 2, 3],
        [4, 5, 6]]))
In [114]:
#print(c.transpose())
print(c.diagonal())
print(c.real)
print(c.imag)
#print(np.tril(c))
#print(np.triu(c))
[1 5]
[[1 2 3]
 [4 5 6]]
[[0 0 0]
 [0 0 0]]
In [115]:
print(c.min())
print(c.max())
print(c.min(0))
print(c.min(1))
print(c.mean())
print(c.sum())
print(c.prod())
1
6
[1 2 3]
[1 4]
3.5
21
720

Erzeugung

Umwandung von Listen, Tupeln und Ranges

In [116]:
np.array([ 1, 2, 3 ]), np.array((1, 2, 3))
Out[116]:
(array([1, 2, 3]), array([1, 2, 3]))

Oder direkt mit der numpy Funktion arange

In [117]:
np.arange(3, 10, 2)
Out[117]:
array([3, 5, 7, 9])

Feste Werte

In [118]:
np.zeros(( 3, 2 ))
Out[118]:
array([[ 0.,  0.],
       [ 0.,  0.],
       [ 0.,  0.]])
In [119]:
np.ones(( 4, 3 )) * 42
Out[119]:
array([[ 42.,  42.,  42.],
       [ 42.,  42.,  42.],
       [ 42.,  42.,  42.],
       [ 42.,  42.,  42.]])

Linspace

Verteile gleichmäßig Einträge

In [120]:
np.linspace(0, 1)
Out[120]:
array([ 0.        ,  0.02040816,  0.04081633,  0.06122449,  0.08163265,
        0.10204082,  0.12244898,  0.14285714,  0.16326531,  0.18367347,
        0.20408163,  0.2244898 ,  0.24489796,  0.26530612,  0.28571429,
        0.30612245,  0.32653061,  0.34693878,  0.36734694,  0.3877551 ,
        0.40816327,  0.42857143,  0.44897959,  0.46938776,  0.48979592,
        0.51020408,  0.53061224,  0.55102041,  0.57142857,  0.59183673,
        0.6122449 ,  0.63265306,  0.65306122,  0.67346939,  0.69387755,
        0.71428571,  0.73469388,  0.75510204,  0.7755102 ,  0.79591837,
        0.81632653,  0.83673469,  0.85714286,  0.87755102,  0.89795918,
        0.91836735,  0.93877551,  0.95918367,  0.97959184,  1.        ])
In [121]:
np.linspace(0, 1, 15)
Out[121]:
array([ 0.        ,  0.07142857,  0.14285714,  0.21428571,  0.28571429,
        0.35714286,  0.42857143,  0.5       ,  0.57142857,  0.64285714,
        0.71428571,  0.78571429,  0.85714286,  0.92857143,  1.        ])

Reshaping

Uminterpretation der Daten

In [122]:
a = np.arange(12) + 1;
a
Out[122]:
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12])
In [123]:
a.reshape(12, 1)
Out[123]:
array([[ 1],
       [ 2],
       [ 3],
       [ 4],
       [ 5],
       [ 6],
       [ 7],
       [ 8],
       [ 9],
       [10],
       [11],
       [12]])
In [124]:
a.reshape(1, 12)
Out[124]:
array([[ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12]])
In [125]:
a.reshape(3, 4)
Out[125]:
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])
In [126]:
a.reshape(3, 1, 4)
Out[126]:
array([[[ 1,  2,  3,  4]],

       [[ 5,  6,  7,  8]],

       [[ 9, 10, 11, 12]]])
In [127]:
a.reshape(3, 2, 2)
Out[127]:
array([[[ 1,  2],
        [ 3,  4]],

       [[ 5,  6],
        [ 7,  8]],

       [[ 9, 10],
        [11, 12]]])
In [128]:
a.reshape(3, -1) # Eine Dimension kann automatisch ergänzt werden, mit dem Rest, der Fehlt
Out[128]:
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])

Das Produkt der Längen muss der Anzahl der Elemente entsprechen!

Als Formel: np.prod(a.shape) == a.size

In [129]:
a.reshape(3, 2, 1)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-129-22807da8fbd9> in <module>()
----> 1 a.reshape(3, 2, 1)

ValueError: cannot reshape array of size 12 into shape (3,2,1)

Achtung: Auch hier wird nicht kopiert!

In [130]:
b = a.reshape(12, 1)
b[2] = -1; a, b
Out[130]:
(array([ 1,  2, -1,  4,  5,  6,  7,  8,  9, 10, 11, 12]), array([[ 1],
        [ 2],
        [-1],
        [ 4],
        [ 5],
        [ 6],
        [ 7],
        [ 8],
        [ 9],
        [10],
        [11],
        [12]]))

Zusammensetzen

In [131]:
a = np.array([ 1, 2 ])
b = np.array([ 3, 4 ])
np.concatenate((a, b), 0)
Out[131]:
array([1, 2, 3, 4])
In [132]:
np.concatenate((a, b), 1)
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-132-48eac27a7ca2> in <module>()
----> 1 np.concatenate((a, b), 1)

IndexError: axis 1 out of bounds [0, 1)
In [133]:
a = a.reshape(1, 2);
b = b.reshape(1, 2);
In [134]:
np.concatenate((a, b), 0)
Out[134]:
array([[1, 2],
       [3, 4]])
In [135]:
np.concatenate((a, b), 1)
Out[135]:
array([[1, 2, 3, 4]])

Zugriff

In [136]:
a = np.linspace(0, 3, 7);
a
Out[136]:
array([ 0. ,  0.5,  1. ,  1.5,  2. ,  2.5,  3. ])

Direktes Indizieren

Wie bei list, tuple, etc.

In [137]:
a[0], a[5], a[-5]
Out[137]:
(0.0, 2.5, 1.0)

Slicing wie bei Listen

In [138]:
a[1::2]
Out[138]:
array([ 0.5,  1.5,  2.5])

Indexlisten, Wiederholung der Indizes sind möglich, Dopplungen und negative Indizes erlaubt

In [139]:
ind = [ 1, 2, 2, 5, -4 ];
a[ind]
Out[139]:
array([ 0.5,  1. ,  1. ,  2.5,  1.5])

Logische Indizierung

In [140]:
ind = [ True, False, False, True, False, False, True ];
a[ind]
Out[140]:
array([ 0. ,  1.5,  3. ])

Bestimme alle ganzzahligen Einträge

In [141]:
ind = a == a.round();
ind, a[ind], a[a == a.round()]
Out[141]:
(array([ True, False,  True, False,  True, False,  True], dtype=bool),
 array([ 0.,  1.,  2.,  3.]),
 array([ 0.,  1.,  2.,  3.]))

Plots

Nötiger Vorspann

In [142]:
import matplotlib.pylab as plt;
# Nur für die Darstellung auf der Webseite, im Programm nicht nötig
%matplotlib inline
In [143]:
x = np.linspace(-1, 1);
f = lambda x: x ** 3;
g = lambda x: np.sin(np.pi * x);
h = lambda x: 1 / x;
In [144]:
plt.plot(x, f(x));
In [145]:
plt.plot(x, g(x));
In [146]:
plt.plot(x, h(x));
In [147]:
plt.plot(x, f(x), 'm');
In [148]:
plt.plot(x, f(x), 'm', x, g(x), 'r--', x, h(x) / 60, 'k:');
In [149]:
plt.plot(x, f(x), 'm', x, g(x), 'r--', x, h(x) / 60, 'k:');
# Beschriftungen (wichtig!)
plt.legend([ 'f(x)', 'g(x)', 'h(x)/60' ]);
plt.xlabel("x");
plt.ylabel("y");

Logarithmische Plots

In [150]:
x = np.linspace(1e-16, 1)
f = lambda x: x**2
In [151]:
plt.semilogx(x, f(x))
Out[151]:
[<matplotlib.lines.Line2D at 0x7fd869d0d470>]
In [152]:
plt.semilogy(x, f(x))
Out[152]:
[<matplotlib.lines.Line2D at 0x7fd869d6bc50>]
In [153]:
plt.loglog(x, f(x))
Out[153]:
[<matplotlib.lines.Line2D at 0x7fd869ddc0b8>]

Lineare Algebra

In [154]:
A = np.arange(9).reshape((3, 3));
B = np.identity(3);
C = np.diag([1, 2, 3]);
print("A =\n", A, "\nB =\n", B, "\nC =\n", C)
A =
 [[0 1 2]
 [3 4 5]
 [6 7 8]] 
B =
 [[ 1.  0.  0.]
 [ 0.  1.  0.]
 [ 0.  0.  1.]] 
C =
 [[1 0 0]
 [0 2 0]
 [0 0 3]]

Beachte, dass die array-Einträge den Anfangstyp behalten!

In [155]:
A.dtype, B.dtype
Out[155]:
(dtype('int64'), dtype('float64'))

Auch bei der Zuweisung!

In [156]:
A[0,0] = 5.5; B[0,0] = 5.5;
A[0,0], B[0,0] # Achtung, der A-Eintrag ist ganzzahlig!
Out[156]:
(5, 5.5)
In [157]:
D = la.circulant([1, 2, 3])
D
Out[157]:
array([[1, 3, 2],
       [2, 1, 3],
       [3, 2, 1]])

Komplexe Matrizen

In [158]:
Z = np.diag([ 4, 4, 4 ]) + 0j;
Z
Out[158]:
array([[ 4.+0.j,  0.+0.j,  0.+0.j],
       [ 0.+0.j,  4.+0.j,  0.+0.j],
       [ 0.+0.j,  0.+0.j,  4.+0.j]])

Vektoren

In [159]:
x = np.array((1, 2, 1)) + 0. # Ein 3-elementiges array (ohne 2. Dimension)

Können auch explizit $1 \times n$ oder n $\times$ 1 groß macht werden:

In [160]:
y = x.reshape(1, -1)
z = x.reshape(-1, 1)
y, z
Out[160]:
(array([[ 1.,  2.,  1.]]), array([[ 1.],
        [ 2.],
        [ 1.]]))

Multiplikation und Inverse Matrix

In Python ist die Matrix-Multiplikation (Matrix mal Matrix, Matrix mal Vektor, Vektor mal Vektor, ...) mit dem @-Operator realisiert:

In [161]:
A @ B, A @ x, x @ A
Out[161]:
(array([[ 27.5,   1. ,   2. ],
        [ 16.5,   4. ,   5. ],
        [ 33. ,   7. ,   8. ]]),
 array([  9.,  16.,  28.]),
 array([ 17.,  16.,  20.]))

Achtung: Ein einfaches * ist eine komponentenweise Operation (s.o.)

In [162]:
A * B
Out[162]:
array([[ 27.5,   0. ,   0. ],
       [  0. ,   4. ,   0. ],
       [  0. ,   0. ,   8. ]])

Inverse Matrizen rechnen wir durch das Lösen linearer Gleichungssysteme:

In [163]:
Ainv = la.solve(A, np.eye(3))
Ainv, Ainv @ A, A @ Ainv # Identität bis auf Rundungsfehler
Out[163]:
(array([[ 0.2       , -0.4       ,  0.2       ],
        [-0.4       , -1.86666667,  1.26666667],
        [ 0.2       ,  1.93333333, -1.13333333]]),
 array([[  1.00000000e+00,   1.94289029e-16,   4.44089210e-16],
        [ -1.33226763e-15,   1.00000000e+00,  -1.77635684e-15],
        [  4.44089210e-16,   1.11022302e-15,   1.00000000e+00]]),
 array([[  1.00000000e+00,   0.00000000e+00,   0.00000000e+00],
        [  5.55111512e-17,   1.00000000e+00,  -2.22044605e-16],
        [  0.00000000e+00,   0.00000000e+00,   1.00000000e+00]]))

Kontrollstrukturen

Blöcke in Python

Blöcke fassen mehrere Befehle auf einer Ebene zusammen. Befehle eines Blockes werden gemeinsam hintereinander ausgeführt. Es gibt Ausnahmen, die ein vorzeitiges Beenden der Ausführung eines zusammenhängenden Blockes hervorrufen.

  • Der Start eines Blockes wird durch einen Doppelpunkt (":") festgelegt
  • Zusammenhängende Befehle werden durch gemeinsames Einrückungen festgelegt:
    Basisebene
    block:
        erste Ebene
        innerer block:
            zweite Ebene
            weiterer Befehl
        wieder auf der ersten Ebene
        nächster Block:
            neue 2. Ebene
            weiter ...
    Basisebene

Abfragen

Teste eine Bedingung (Wahrheitswert) und springe in Abhängingkeit des Wertes.

Format:

if <Wahrheitswert1>:
    # Code, der ausgeführt wird, wenn <Wahrheitswert1> True ist
elif <Wahrheitswert2>:
    # Code, der ausgeführt wird, wenn <Wahrheitswert1> False
    # und <Wahrheitswert2> True ist
elif ....:
    # usw.
else:
    # Code, der ausgeführt wird, wenn alle Wahrheitswerte vorher
    # False waren.
In [164]:
a, b = np.random.randint(0, 5, 2);
print("a: {0}, b: {1}, Maximum:".format(a, b));

if a > b:
    print("a");
elif a < b:
    print("b");
else:
    print("Beide gleich.");
a: 2, b: 4, Maximum:
b
  • Mehr Beispiele folgen
  • Python kennt kein switch/case

for-Schleife

Im Vorhineinvorher festgelegte Iteration. Es wird über eine Indexliste Iteriert, der aktuelle Index kann über eine Variable zugegriffen werden.

Format:

for <index> in <indexliste>:
    # Code, der für jeden <index> in <indexliste> ausgeführt
    # wird.
    # Der Wert der Variable <index> hat den entsprechenden Wert.
In [165]:
for kk in range(10):
    print(kk);
0
1
2
3
4
5
6
7
8
9
In [166]:
typen = [ "zu Fuß", "Fahrrad", "Auto", "Zug" ];
for typ in typen:
    print(typ);
zu Fuß
Fahrrad
Auto
Zug

Iteration über Paare

In [167]:
liste = list(zip(typen, [ 4, 25, 50, 100 ]));
liste
Out[167]:
[('zu Fuß', 4), ('Fahrrad', 25), ('Auto', 50), ('Zug', 100)]
In [168]:
for typ, geschwindigkeit in liste:
    print("{0:10s}: {1:3d} km/h".format(typ, geschwindigkeit))
zu Fuß    :   4 km/h
Fahrrad   :  25 km/h
Auto      :  50 km/h
Zug       : 100 km/h

Elemente in einer Liste aufzählen:

In [169]:
for index, typ in enumerate(typen):
    print("Typ {0}: {1}".format(index, typ));
Typ 0: zu Fuß
Typ 1: Fahrrad
Typ 2: Auto
Typ 3: Zug

while-Schleife

Schleife mit immer wieder neu getestetem Abbruch.

Format:

while <Wahrheitswert>:
    # Code, der so lange ausgeführt wird, wie Wahrheitswert True
    # ist.

Spiel: Wir würfeln so lange mit einem sechs-seitigen Würfel, bis die Summe der Würfe 21 übersteigt.

In [170]:
summe = 0;
runde = 0;
while summe <= 21:
    wurf = np.random.randint(1, 7);
    runde += 1;
    summe += wurf;
    print("Wurf {0}: {1}, Summe: {2:2d}".format(runde, wurf, summe));
print("Fertig nach {0} Würfen.".format(runde));
Wurf 1: 6, Summe:  6
Wurf 2: 6, Summe: 12
Wurf 3: 4, Summe: 16
Wurf 4: 2, Summe: 18
Wurf 5: 4, Summe: 22
Fertig nach 5 Würfen.

Vorzeitiges Beenden

Bei Schleifen können Durchläufe vorzeitig beendet werden oder die ganze Schleife vorzeitig beendet werden.

  • continue beendet den aktuellen Durchlauf und startet die Schleife von vorn.
    • Bei for-Schleifen, wird der nächste Index verwendet, falls vorhanden. Anderenfalls wird die Schleife beendent.
    • Bei while-Schleifen wird die Bedingung neu geprüft.
  • break beendet die gesamte Iteration.

Spiel:

  • Zwei Spieler würfeln je sieben mal abwechselnd.
  • Würfelt einer der beiden Spieler eine eins oder zwei, ist die Runde ungültig.
  • Ansonsten werden die gewürfelten Punkte jedem Spieler gut geschrieben.
  • Sollte einer der Spieler über 15 Punkte erreichen, hat er verloren.
  • Ansonsten gewinnt am Ende der sieben Runden der Spieler mit der höheren Punktzahl.
In [171]:
punkte = np.array([ 0, 0 ])
for runde in range(1, 8):
    wuerfe = np.random.randint(1, 7, 2);
    print("Runde {0:2d}: Sp. 1 würfelt {1}, Sp. 2 würfelt eine {2}.".format(
            runde, *wuerfe));
    if wuerfe.min() < 3:
        print("Runde ungültig.");
        continue;
    punkte += wuerfe;
    print("Punktestand: {0} zu {1}".format(punkte[0], punkte[1]));
    if punkte.max() > 21:
        break;
print("Endstand nach {0} Runden: {1} zu {2}".format(runde, punkte[0], punkte[1]))
Runde  1: Sp. 1 würfelt 5, Sp. 2 würfelt eine 5.
Punktestand: 5 zu 5
Runde  2: Sp. 1 würfelt 1, Sp. 2 würfelt eine 5.
Runde ungültig.
Runde  3: Sp. 1 würfelt 3, Sp. 2 würfelt eine 2.
Runde ungültig.
Runde  4: Sp. 1 würfelt 4, Sp. 2 würfelt eine 3.
Punktestand: 9 zu 8
Runde  5: Sp. 1 würfelt 1, Sp. 2 würfelt eine 6.
Runde ungültig.
Runde  6: Sp. 1 würfelt 4, Sp. 2 würfelt eine 6.
Punktestand: 13 zu 14
Runde  7: Sp. 1 würfelt 2, Sp. 2 würfelt eine 6.
Runde ungültig.
Endstand nach 7 Runden: 13 zu 14

Fehlermeldungen

Eine weitere Möglichkeit, Programme oder Schleifen vorzeitig zu beenden, sind Fehlermeldungen. Diese dürfen an einer beliebtigen Stelle des Programms stehen.

In [172]:
print("Hallo 1");
raise(Exception("Dies ist eine Fehlermeldung"));
print("Hallo 2");
Hallo 1
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-172-f6906093ffa1> in <module>()
      1 print("Hallo 1");
----> 2 raise(Exception("Dies ist eine Fehlermeldung"));
      3 print("Hallo 2");

Exception: Dies ist eine Fehlermeldung
In [173]:
wurf = np.random.randint(1, 7);
if wurf % 2 == 0:
    raise(Exception("Gerade würfe werden nicht akzeptiert ({0})".format(wurf)))
wurf
Out[173]:
3

Assertations schließen eine Fallunterscheidung mit ein. Der Fehler wird ausgelöst, wenn die Bedingung False ist.

In [174]:
wurf = np.random.randint(1, 7);
assert wurf % 2 == 1, "Gerade würfe werden nicht akzeptiert ({0})".format(wurf)
wurf
Out[174]:
1

Funktionen

Funktionen fassen einen eine Reihe von Anweisungen zusammen, die über einen bestimmten Namen aufgerufen werden können. Funktionen geben immer einen Wert zurück, der falls nicht explizit angegeben None ist.

Wir kennen bereits eine Form einfacher eigener Funktionen: solche, die mit lambda definiert werden.

In [175]:
myPower = lambda x, a: x ** a;
myPower(2, 10)
Out[175]:
1024

Kompliziertere Funktionen, etwa solche die sich nicht, oder nicht einfach in einem einzelnen Befehl ausdrücken lassen, können mit def erzeugt werden.

Format:

def <funktionsname>(<arg1>, <arg2>, ...):
    # Code der durch den Aufruf von <funktionsname>(...)
    # ausgeführt wird.
    # Gegebenenfalls:
    return <wert>;
In [176]:
def myPower(x, a):
    print("Meine eigene Potenzfunktion wird ausgeführt.");
    return x**a;
myPower(2, 10)
Meine eigene Potenzfunktion wird ausgeführt.
Out[176]:
1024

Eine Hilfe (Dokumentation) zur Funktion kann als Zeichenkette als erste Zeile des Funktionskörpers definiert werden.

In [177]:
def myCommentedFunction(text):
    """
    Dies ist der Hilfetext zu `myCommentedFunction`.
    Blah.
    """
    print(text)
help(myCommentedFunction)
?myCommentedFunction
Help on function myCommentedFunction in module __main__:

myCommentedFunction(text)
    Dies ist der Hilfetext zu `myCommentedFunction`.
    Blah.

Optionale Argumente

In [178]:
def myPower(x, a = 2): # Wenn nicht angegeben, ist a = 2
    return x**a;
myPower(2), myPower(2, 2), myPower(2, 3)
Out[178]:
(4, 4, 8)
In [179]:
def myPower(x = 2, a = 2): # Wenn nicht angegeben, sind x = 2 und a = 2
    return x**a;
myPower(), myPower(3), myPower(3, 3)
Out[179]:
(4, 9, 27)
In [180]:
# Nur das zweite Argument angeben (dann explizit mit Namen)
myPower(a = 3)
Out[180]:
8

Namensräume in Funktionen

Funktionen haben ihren eigenen Namensraum, können (nur lesend) auf den sie umgebenden Namensraum zugreifen.

In [181]:
a = 15;
def fun():
    print(a);
    x = 16;
fun()
print(x)
15
[ 1.  2.  1.]

Wenn eine Variable in einer Funktion zugewiesen wird, die auch außerhalb existiert, wird innerhalb der Funktion eine zweite Version der Variable erstellt.

In [182]:
a = 15;
def fun():
    a = 16;
fun();
print(a)
15

Die Funktion locals() gibt einem ein dict aller lokal in einer Funktion definierten Variablen. Die Variablennamen sind die Schlüssel, deren Werte die Werte des dicts.

In [183]:
def fun():
    print(locals());
    a = 15;
    print(locals());
fun();
{}
{'a': 15}