Python (12.11.2019 - 14.11.2019 in Graz)

Standardthemen

Die Datentypen, und das “Normale” an Python waren schnell erklärt. Ich kanns nicht lassen, bevor die Anfängerthemen vorbei sind, auf dem Besten von Python herumzureiten: Iteration und Generatoren. Hier hatte ich eine kleine Demo gehackt - zum x-ten mal. Das ganze Fibonacci-Zeug gibts jetzt als Live-Hacking-Screenplay

Das Hauptthema: Numerik, NumPy

Siehe dazu auch ein Jupyter Notebook “BigPlan”.

Der Chef hat mir bei einem Vorgespräch ein Übungsbeispiel für die Teilnehmer mitgegeben: ausgehend von einem Spektralbild (sagt man so?), verwende den K-Means Clusteringalgorithmus, um die Bereiche auf dem Bild zu kategorisieren (die abgebildeten Stücke zu erkennen).

Ich hab mir erlaubt, für die Kursvorbereitung 1 so quasi als Appetizer das Problem etwas zu reduzieren: Farbreduktion eines Bildes (auf 8 Farben). Das Programm (siehe unten) verwendet

  • Pillow, um Bilddaten zu lesen und schreiben. Die Library interoperiert nahtlos mit NumPy, was sicher kein Zufall ist.

  • NumPy, um die Alpha-Plane des Ausgangsbildes abzuschneiden und zu restoren.

  • Den K-Means Algorithmus aus scikit-learn, um das Clustering jemand anders machen zu lassen.

../../../../_images/veggie.png

Ausgangsbild veggie.png: viele Farben

../../../../_images/veggie-reduced.png

Reduziertes Bild veggie-reduced.png: Acht Farben

Das Programm ist überschaubar - es verwendet nur Libraries und macht nichts selbst (das ist der Plan, immer, beim Programmieren). Die Ausdrucksstärke von Python macht sich hier bemerkbar durch z.B. die Slice-Syntax (wegschneiden der Alpha-Plane), oder beim Iterieren mittels enumerate().

#!/usr/bin/env python

import numpy
import PIL.Image
from sklearn.cluster import KMeans

import sys


img = PIL.Image.open('veggie.png')

imgarray = numpy.array(img)
nrows, ncols, nrgba = imgarray.shape

# disregard alpha plane for clustering
alpha = imgarray[:,:,3:]
rgb = imgarray[:,:,0:3]

# change view to a linear sequence of (r,g,b) points. (K-Means cannot
# take arbitrarily dimensioned spaces, he likes it linear.)
linear_rgb = rgb.reshape((nrows*ncols, 3))

km = KMeans(n_clusters=8)
km.fit(linear_rgb)

# reduce: let cluster centers be their members
for idx, label in enumerate(km.labels_):
    linear_rgb[idx] = km.cluster_centers_[label]

# stack saved alpha plane on top of it
imgarray = numpy.concatenate((rgb, alpha), axis=2)

reduced_img = PIL.Image.fromarray(imgarray, 'RGBA')
reduced_img.save('veggie-reduced.png')

Lesen von .mat Files

Das Spektralbild liegt im .mat Format vor - was immer das ist, hat wahrscheinlich mit MATLAB zu tun. Etwas Recherche hat ergeben, dass die Funktion scipy.io.loadmat() das kann. Hier ein kleines Testprogramm,

#!/usr/bin/python

from scipy.io import loadmat
import sys


mat = loadmat(sys.argv[1])

print(type(mat['imnData']))
print(mat['imnData'])
print(mat['imnData'].dtype)

# obviously "imnData" is what we want
data = mat['imnData']
# ...

Lösen einer Uni-Übung

Ein Teilzeitmitarbeiterin der Firma, sie studiert Physik neben der Arbeit, muss für eine Übung … was weiss ich … machen. Wie auch immer, der Input für ihre Arbeit liegt in folgendem bekackten Inputformat vor, das es zu parsen gilt. War eine nette Zwischendurch-Gruppenarbeit.

 ---------------------------- Atom information ----------------------------
     atom    #        X              Y              Z            mass
 --------------------------------------------------------------------------
    O        1  0.0000000E+00  0.0000000E+00 -1.2662399E-01  1.5994910E+01
    H        2  1.4391972E+00  0.0000000E+00  1.0048070E+00  1.0078250E+00
    H        3 -1.4391972E+00  0.0000000E+00  1.0048070E+00  1.0078250E+00
 --------------------------------------------------------------------------


          --------------------------------------------------------
          MASS-WEIGHTED PROJECTED HESSIAN (Hartree/Bohr/Bohr/Kamu)
          --------------------------------------------------------


               1            2            3            4            5            6            7            8            9
   ----- ----- ----- ----- -----
    1    4.05054E+01
    2   -1.61610E-24  0.00000E+00
    3    1.20781E-07  8.08051E-25  2.83024E+01
    4   -8.06829E+01  4.42629E-24 -4.65256E+01  3.52600E+02
    5   -7.69570E-24  0.00000E+00  2.91733E-24  2.04388E-23  0.00000E+00
    6   -6.34292E+01  8.04780E-25 -5.63758E+01  2.19019E+02  6.41217E-24  2.11622E+02
    7   -8.06829E+01  3.21912E-24  4.65256E+01 -3.11752E+01  1.04198E-23  3.36702E+01  3.52600E+02
    8   -8.14839E-24  0.00000E+00  2.71613E-24  1.96373E-23  8.40456E-19  7.21369E-24  1.24236E-23  0.00000E+00
    9    6.34292E+01 -1.60956E-24 -5.63758E+01 -3.36702E+01 -1.84350E-23  1.29686E+01 -2.19019E+02 -1.92365E-23  2.11622E+02
    

Der Code, mit dem wir nach einigen Runden Nachdenkens einigermaßen zufrieden waren, sieht so aus,

import numpy as np


def load_dat(filename):
    '''load a .dat file (whatever that is) into a numpy matrix and returns
    that matrix.
    '''

    matrix_lines = []

    with open(filename) as f:
        for line in f:
            if '----- ----- ----- ----- -----' in line:
                matrix_lines = f.readlines()
                break
        else:
            raise RuntimeError('file format vergeigt')

    # split matrix_lines into elements
    matrix_elements = []
    for l in matrix_lines:
        if len(l.strip()) == 0:
            continue
        elems = l.split()
        del elems[0] # line-number column, unnecessary
        matrix_elements.append(elems)

    # determine dimensions of (triangular?) matrix 
    x = len(matrix_elements)
    y = max((len(l) for l in matrix_elements))

    matrix = np.zeros((x,y))
    for row_no, row in enumerate(matrix_elements):
        matrix[row_no,0:len(row)] = row

    return matrix

if __name__ == '__main__':
    print(load_dat(sys.argv[1]))

Footnotes

1

Ich hatte von Bildverarbeitung keine Ahnung