Künstliche Intelligenz: Python - Listen, Tupeln, Reguläre Ausdrücke und mehr


 

Einführung

Im Artikel "Künstliche Intelligenz - Python erlernen" wird Python anhand von objektorientierten Beispielen eingeführt. Nach Studium dieses Artikels können wir zwar einfache Beispiele selbst programmieren, aber es fehlen einige Sprachelemente, die für eine effektive Programmierung notwendig sind. Diese sollen in diesem Artikel noch vorgestellt werden.

Files 

Daten sind für eine Organisation überlebenswichtig, um das Business zu gewährleisten. Wenn Daten verloren gehen, so kann die Organisation ihren Betrieb einstellen. Computer übernehmen das Handling von Daten, sei es um sie zu speichern oder sie zu einem anderen Zeitpunkt wieder bereitzustellen. Für die Speicherung von Daten werden Files verwendet. Aus der Sicht des Computers ist ein File nichts anderes als eine Sammlung von Daten, die einem Programm zur Verfügung gestellt wird:
  • Daten, die einem File abgespeichert werden, werden permanent gespeichert. Wird der Computer abgeschaltet, so befinden sich die Daten weiterhin auf beispielsweise einer Hard Disk. Die Daten können so zu einem späteren Zeitpunkt wieder verwendet werden
  • Daten in einem File können manipuliert werden. Einem File können jederzeit Daten hinzugefügt oder unnötige Daten können entfernt werden. Zudem können die Daten geändert werden.
  • Daten in einem File können von mehreren Programmen geteilt werden.
  • Files sind geeignet, um grosse Datenmengen abzuspeichern
Python kennt im Prinzip zwei Arten von Dateien: Text-Dateien und Binäre Dateien. Text-Dateien werden genutzt, um Zeichen oder Strings abzuspeichern. Binäre Dateien werden hingegen genutzt, um Daten in Form von Bytes abzuspeichern. Die Interpretation wie die Bytes dann zu verwenden sind, wird vom Programm übernommen. Ein Textprogramm wird Text als Bytefolge abspeichern während ein Bild- oder Videoprogramm Bilder, Audio oder Videodateien als Bytefolge abspeichert.

Reguläre Ausdrücke

Oftmals müssen aus einer Datenmenge bestimmte Informationen extrahiert werden. Um an solche Informationen zu gelangen, muss eine Suchoperation auf dieser Datenmenge ausgeführt werden. Denn Daten liegen oftmals nicht in einer exakt wiederverwendbaren Form vor. Als Beispiel diene eine CSV Datei, die tabellarisch aufgebaut ist und in einer Spalte, die Namen aller Fussballer einer Mannschaft enthält. Es sollen nun alle Fussballer herausgesucht werden, die mit dem Buchstaben A beginnen. Reguläre Ausdrücke sind geeignet, um solche Operationen auf Daten auszuführen.

Ein regulärer Ausdruck ist ein String der spezielle Symbole und Zeichen enthält, die dazu diesen Informationen aus einer gegebenen Datenmenge zu extrahieren. Dieser String kann verwendet werden, um Informationen zu suchen, auf übereinstimmende Informeationen zu testen (match) und Information zu finden und aufzuteilen (find and split). Ein regulärte Ausdruck wird oftmals regex genannt.

Listen und Tupeln

Als Sequenz ist ein Datentyp der eine Gruppe von Elementen repräsentiert. Der Zweck einer Sequenz ist es, eine Gruppe von Elementen zu speichern und zu verarbeiten. In Python werden solche Sequenzen in Form von Strings, Listen, Tupeln und Dictionaries unterstützt (ohne das zusätzliche Module importiert werden müssen). Alle Arten von Sequenzen haben gemeinsame Operationen (Indexierung, Slicing). 

Exceptions

Listen

Eine Liste ist vergleichbar zu einem Array, welches aus einer Gruppe von Elementen oder Items besteht. Wie ein Array kann eine Liste auch Elemente speichern. Der grosse Unterschied zu einem Array ist, dass der Typ der Elemente in einer Liste unterschiedlich sein kann. Daher sind Listen oftmals nützlicher als ein Array. Listen sind wahrscheinlich der am meisten genutzte Datentyp in Python.

Basics


# Liste erzeugen
eineListe = ['Hallo', 'Welt!', 'Die', 'Temperatur', 'ist', 22, 'Grad', 'draussen']
# Eine leere Liste wird folgendermassen erzeugt
eineLeereListe = []
# Listen können einfach mit print gedruckt werden
print(eineListe)
# ['Hallo', 'Welt!', 'Die', 'Temperatur', 'ist', 22, 'Grad', 'draussen']
# Mit Angabe des Index wird nur ein Element gedruckt
print(eineListe[0])
# Hallo
# Die allgemeinste Methode, um auf Elemente zuzugreifen, nennt sich Slicing
print(eineListe[0:3:1])
# ['Hallo', 'Welt!', 'Die']
# Dies ist aequivalent mit der Schreibweise
print(eineListe[:3:])
# Slicing setzt sich aus [start: stop: stepsize] zusammen
# Wenn im Slicing ein Wert nicht angegeben wird, dann wird
# für stepsize 1 angenommen, für start 0 und für stop n-1
#
# Printen von speziellen Elementen
print ('First= %s, Last= %s' % (eineListe[0], eineListe[7]))
# Ein %d würde für eine beliebige Dezimalzahl stehen

Mit der Range Funktion kann eine Sequenz von Ganzzahlen erzeugt werden unter Angabe des Startwertes, des Stoppwertes und der Schrittweite.

# range(start, stop, stepsize)
print(range(0, 10, 1))
# range(0, 10)
for i in range(3, 8, 2):
    print(i)
# 3, 5, 7
# oder analog:
myList = list(range(3,8,2))
print (myList)
# [3, 5, 7]

Wird im obigen Beispiel die Range-Funktion nicht in eine Liste umgewandelt, so wird ein Range-Objekt zurückgegeben. Der Zugriff auf die einzelnen Elemente erfolgt via For-Schleife oder While-Schleife.

In einem nächsten Schritt befassen wir uns mit:
  • dem Verändern von Elementen in einer Liste (Update)
  • dem Entfernen von Elementen in einer Liste (Remove)
  • dem Zusammenfügen von Listen (Concatenate)
  • dem Aliasing und dem Klonen  
# Updating a list
aListe = list(range(1, 5))
print(aListe)
# [1, 2, 3, 4]
aListe.append('Thomas')
print(aListe)
# [1, 2, 3, 4, 'Thomas']
aListe[4]='Hildegard'
print(aListe)
# [1, 2, 3, 4, 'Hildegard']
aListe[0:5:2] = 10, 11, 12
print(aListe)
# [10, 2, 11, 4, 12]
# Removing elements from a list
del aListe[4]
print(aListe)
# [10, 2, 11, 4]
aListe.remove(10)
# Concatenation of lists
ls1 = [1, 2]
ls2 = [3, 4]
ls3 = [5, 6]
print(ls1+ls2+ls3)
# [1, 2, 3, 4, 5, 6]
# Repetition
print(ls1*2)
# [1, 2, 1, 2]
# print(ls1*ls2) das geht nicht
# Memebership
print(2 in ls1)
# True
print(4 not in ls3)
# True
# Aliasing
alias = ls2
ls2[1] = 100
print(alias)
# [3, 100]
# Cloning
clone = ls1[:]
ls1[0] = 99
print(clone)
# [1, 2]
# oder aber auch:
clone2 = ls3.copy()

Nebst der copy-Methode gibt es jede Menge anderer Methoden, die Operationen auf Listen durchführen. Nachfolgend eine Liste:



Methode Beispiel Beschreibung
sum list.sum() Summer aller Elemente
index list.index(element) Erster Index eines Listenelements
append list.append(element) Einfügen eines Elementes am Ende
insert list.insert(index, element) Einfügen eines Elementes an der Position index
copy list.copy() Kopiert alle Elemente in eine neue Liste
extend list.extend(liste2) Anfügen einer Liste zu einer Liste
count list.count(element) Anzahl der Vorkommnisse eines Elements in der Liste
remove list.remove(element) Löschen eines Elements in der Liste
pop list.pop() Rückgabe und Entfernen des letzten Elements
sort list.sort() Sortieren einer Liste
reverse list.reverse() Umkehren der Reihenfolge
clear list.clear() Löschen aller Elemente

Komplexere Beispiele

Damit das Arbeiten mit Listen verständlicher wird, sind Beispiele notwendig. In einem ersten Schritt, wollen wir die sort - Methode mit dem bekannten Bubble-Sort und dem ultraschnellen Quick-Sort vergleichen. 

Quicksort

Der Algorithmus basiert auf folgendem in Worten formulierten Algorithms:

1. Auftrennung der Liste in zwei Teillisten
Die zwei Teillisten werden die linke und die rechte Teilliste genannt. Zur Aufteilung wird ein Pivotelemente verwendet. Alle Elemente kleiner als das Pivotelemente kommen in die linke Liste und alle anderen in die rechte Teilliste. Elemente gleich dem Pivotelement werden zufällig entweder in die linke oder rechte Teilliste getan.
2. Der Algorithmus wird nun für die linke und rechte Teilliste wiederholt. Die linke Teilliste enthält immer kleinere Elemte als die rechte Teilliste. So kann rekursiv immer wieder die Liste aufgespalten werden, bis die Teillisten eine Länge eins oder null haben.

Nachfolgend eine Umsetzung in Python

import random
import time


class QuickSort:

    def __init__(self, daten):
        self.daten = daten

    def __teile(self, links, rechts):
        i = links
        j = rechts - 1
        pivot = self.daten[rechts]
        while i < j:

            while i < rechts and self.daten[i] < pivot:
                i = i + 1
            while j > links and self.daten[j] > pivot:
                j = j - 1

            if i < j:
                tausch = self.daten[j]
                self.daten[j] = self.daten[i]
                self.daten[i] = tausch

        tausch = self.daten[i]
        self.daten[i] = self.daten[rechts]
        self.daten[rechts] = tausch
        return i

    def __sort (self, links, rechts):

        if links < rechts:
            teiler = self.__teile(links, rechts)
            self.__sort(links, teiler-1)
            self.__sort(teiler+1, rechts)

    def start(self):
        self.__sort(links=0,rechts=len(self.daten)-1)


daten = random.sample(range(100000), 100000)

quicksort = QuickSort(daten.copy())
startTime = time.time_ns()
quicksort.start()
endTime = time.time_ns()

print(endTime-startTime)
startTime = time.time_ns()
daten.sort()
endTime = time.time_ns()
print(endTime-startTime)

Die Laufzeiten für den nicht optimierten Quick-Sort Algorithmus sind aber deutlich schlechter als die der integrierten Sort-Methode für Listen:

267698750 ns
14988647 ns

Bubble-Sort

Der Bubble-Sort können wir uns einfacher vorstellen.

1. Es wird links in der Liste begonnen und wenn das linke Element grösser ist als das rechte, so werden die Positionen vertauscht. Das Element steigt also wie eine Blase nach oben.


import random
import time

class BubbleSort:
    def __init__(self, daten):
        self.daten = daten

    def __sort(self):
        i = 0
        j = 0
        while i < len(self.daten)-1:
            j = i + 1
            while j < len(self.daten)-1:
                el1 = self.daten[i]
                el2 = self.daten[j]

                if el1 > el2:
                    self.daten[j] = el1
                    self.daten[i] = el2
                j = j + 1
            i = i + 1

    def start(self):
        self.__sort()


daten = random.sample(range(100), 100)

bubbleSort = BubbleSort(daten.copy())
startTime = time.time_ns()
bubbleSort.start()
endTime = time.time_ns()
print(endTime-startTime)
startTime = time.time_ns()
daten.sort()
endTime = time.time_ns()
print(endTime-startTime)

Die Liste als Menge betrachtet

Manchmal ist es nützlich zu wissen, welche Elemente in zwei Listen doppelt vorkommen. Dazu kann eine Liste in ein Set umgewandelt werden. Sets sind Mengen und wie wir aus der elementaren Mathematik wissen, können Mengen vereinigt, geschnitten oder voneinander abgezogen werden. Wie dies konkret aussieht, zeigt folgendes Beispiel:

ls1 = [2, 4, 6, 7, 3, 5, 1, 8, 3, 4, 6, 7, 1, 9]
s1 = set(ls1)
ls2 = [2, 4, 6, 5, 1, 8, 6, 2, 9, 0, 6, 1, 3, 5, 2, 4]
s2 = set(ls2)
print(s1.union(s2))
# {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
print(s1.intersection(s2))
# {1, 2, 3, 4, 5, 6, 8, 9} Dies sind dann effektiv die doppelten Elemente
print(s1.difference(s2))
# {7} Dieses Element kommt nur in s1 vor

<<to be continued>>

Tupeln

Ein Tupel ist eine Python Sequenz die eine Gruppe von Elementen oder Items abspeichert. Tupel sind vergleichbar mit Listen, nur dass sie nicht veränderlich sind. Einmal erzeugt, behalten sie ihren Wert. Alle Listenoperationen, die sich mit der Veränderung von Werten beschäftigen sind nicht anwendbar.

<<to be continued>>

Dictionaries

Ein Dictionary (Wörterbuch) repräsentiert eine Gruppe von Elementen in der Form von Schlüssel-Werte Paare. Wer Java kennt, kennt sicher auch die Properties oder aber auch die Hashtable Klasse, die ähnliche Funktionen zur Verfügung stellen, wie hier der Datentyp Dictionary in Python. Python verwendet die geschweiften Klammern, um Dictionaries zu erzeugen:

dict = {'Key1': 'Value1', 'Key2': 'Value2', 'Key3': 'Value3'}
# Beispiel
myPerson = {'Alter': 30, 'Name': 'Muster', 'Vorname': 'Erika', 'Groesse' : 175}
print('Name der Person= ', myPerson['Name'])
print('Alter der Person= ', myPerson['Alter'])

Nachfolgend als Beispiel die wichtigsten Operationen:

# Anzahl der Paare
print(len(myPerson))
# 4
myPerson['Groesse'] = 176
# verändert den Wert eines Schlüssels
myPerson['Status'] = 'verheiratet'
# Hinzufügen eines neuen Schlüssel-Werte Paares
del myPerson['Status']
# Löschen eines Schlüssel-Werte Paares
'Vorname' in myPerson
# True

Folgende Methoden stehen zur Verfügung:

Methode Beispiel Beschreibung
clear dict.clear() Löschen aller Elemente
copy dict.copy() Erstellen einer Kopie
fromkeys dict.fromkeys(s [,v]) Erstellen eines Dictionaries aus Schlüsseln s (als Liste), denen alle der optionale Wert v zugewiesen wird
get dict.get(k [,v]) Rückgabe des Wertes mit Schlüssel k - wenn nicht vorhanden wird der optionale Wert v zurückgegeben
items dict.items() Kopiert alle Elemente in eine neue Liste
keys dict.keys() Rückgabe der Schlüssel-Liste
values dict.values() Rückgabe der Werte-Liste
update dict.update(x) Fügt alle Elemente des Dictionaries x zu dict hinzu
pop dict.pop(k [,v]) Entfernt k und sein Wert aus dem Dictionary und gibt den Wert zurück. Wenn k nicht gefunden wird, so wird der optionale Wert v zurückgeben. Ansonsten wird ein KeyError ausgelöst
setdefault dict.setdefault(k [,v]) Wird k gefunden, so wird der Wert zurückgegeben. Wenn k nicht vorhanden ist wird (k,v) in dict gespeichert

Files

<<to be continued>>

Reguläre Ausdrücke

Python stellt mit dem Modul re ein eigenes Modul für reguläre Ausdrücke zur Verfügung. Das Modul enthält viele Methoden wie compile(), search(), match(), findall(), split() usw., die dazu verwendet werden, um Informationen aus gegebenen Daten zu extrahieren. Vom Aufbau her ist ein regulärer Ausdruck ein String, der Zeichen und spezielle Symbole enthält. 

Raw Strings

Ein normaler String wird in Python als Zeichenkette zwischen zwei Hochkommas definiert. Darin können aber Formatanweisungen enthalten sein, die eine Interpretation schon vorher vorwegnehmen. Beispielsweise wird der String 'Hallo Welt\nDer Morgen ist schön' beim Verwenden mit print eine neue Zeile nach 'Hallo Welt' machen. Reguläre Ausdrücke benutzen deshalb Raw Strings. Diese werden gar nicht 



<<to be continued>>


Keine Kommentare:

Kommentar veröffentlichen