Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • p.anaguano/informatik2022
  • a.engelke/informatik2022
  • p.schleinzer/informatik2022
  • n.rosenkranz/informatik2022
  • w.weber/informatik2022
  • xuhao.zhang/informatik2022
  • h.el-menuawy/informatik2022
  • saifalla.ibrahim/informatik2022
  • d.krause/informatik2022
  • p.schilling/informatik2022
  • j.tolke/informatik2022
  • f.luckau/informatik2022
  • danish.ahmad/informatik2022
  • emir.sagdani/informatik2022
  • d.griedel/informatik2022
  • j.mahnke/informatik2022
  • l.poehler/informatik2022
  • christoph.wrede/informatik2022
  • y.kummert/informatik2022
  • alexander.reisch/informatik2022
  • t.dickel/informatik2022
  • ni.petersen/informatik2022
  • markus.werner/informatik2022
  • s.mouammar/informatik2022
  • j.jahns/informatik2022
  • m.figueroa-castillo/informatik2022
  • b.hannan/informatik2022
  • v.lapschiess/informatik2022
  • j.hegner/informatik2022
  • g.paraschiv/informatik2022
  • e.abkaimov/informatik2022
  • l.krogmann/informatik2022
  • d.mizyed/informatik2022
  • h.almasri/informatik2022
  • a.mickan/informatik2022
  • f.shikh-alshabab/informatik2022
  • j.feldbausch/informatik2022
  • l.abdel-kader/informatik2022
  • jan.seibt/informatik2022
  • e.goekmen/informatik2022
  • nathanael.schenk/informatik2022
  • r.reksius/informatik2022
  • edmont.schulze/informatik2022
  • a.singh/informatik2022
  • p.christensen/informatik2022
  • m.woidt/informatik2022
46 results
Show changes
Showing
with 55937 additions and 0 deletions
import numpy as np
def activeDisplacement(BC, K, F):
"""
Take Boundary conditions, global stiffness matrix and forces. return active displacements, {Ua}.
Parameters
----------
BC : list
Boundary conditions.
K : ndarray
Global stiffness matrix.
F : list
External forces of truss.
Returns
-------
Ua : ndarray
Active (Unconstrained) displacements matrix, {Ua}.
"""
BCnew = [(x[0]-1)*2+x[1]-1 for x in BC]
BCval = [x[2] for x in BC]
Fnew = np.zeros(K.shape[0])
for i,bc in enumerate(BCnew):
Fnew += BCval[i]*K[:,bc]
for node in F:
Fnew[(node[0]-1)*2] += node[1]
Fnew[(node[0]-1)*2+1] += node[2]
Ft = np.delete(Fnew, BCnew)
Knew = np.delete(K, BCnew, axis=0)
Knew = np.delete(Knew, BCnew, axis=1)
Ua = np.linalg.solve(Knew, Ft)
return Ua
#%% Need to change
#
#
# def constrainedForces(BC, K, Ua):
# '''
# Take Boundary conditions, global stiffness matrix and active displacements. return reaction forces, {Fc}.
# Parameters
# ----------
# BC : list
# Boundary conditions.
# K : numpy array
# Global stiffness matrix.
# Ua : numpy array
# Active (Unconstrained) displacements matrix, {Ua}.
# Returns
# -------
# Fc : numpy array
# Reaction forces of truss (constrained forces matrix, {Fc}).
# '''
# nd = K.shape[0]
# dele = []
# # BCnew1 = [x[0]*2-1 for x in BC]
# # BCnew2 = [x[0]*2-2 for x in BC]
# # BCnew = BCnew1 + BCnew2
# BCnew = [(x[0]-1)*2+x[1]-1 for x in BC]
# for x in range(nd):
# if not x in BCnew:
# dele.append(x)
# Knew = np.delete(K, BCnew, axis=1)
# Knew = np.delete(Knew, dele, axis=0)
# Fc = np.matmul(Knew, Ua)
# return Fc
#%%
\ No newline at end of file
%% Cell type:markdown id:97636b3e tags:
# <font color='blue'>**Übung 4 - Evolutionärer Algorithmus**</font>
(Diese Übung gehört zur Vorlesungseinheit 5)
## <font color='blue'>**Problemstellung: Strukturdimensionierung eines Fachwerk-Kragarms**</font>
### <font color='blue'>**Problembeschreibung**</font>
Die Stäbe eines Fachwerk-Kragarms sollen möglichst materialsparend ausgelegt werden. Der Kragarm soll 1.000.000 N Gewichtskraft (ca. 100t) bei 10 m Distanz tragen können. Die Geometrie und der Auslegungslastfall sind der Skizze dargestellt:
%% Cell type:markdown id:5968f168 tags:
![](Pics/Truss_layout_annotated.png)
%% Cell type:markdown id:d6928185 tags:
Der Querschnitt der Stäbe soll so bestimmt werden, dass die Streckgrenze des Materials mit einem Sicherheitsfaktor von **S = 2** bei dem beschriebenen Lastfall nicht erreicht wird. Dabei soll dennoch möglichst wenig Material eingesetzt werden. Für die Berechnung der auftretenden Spannungen steht bereits ein Tool zur Verfügung, zudem kann das Eigengewicht der Stäbe für den Lastfall vernachlässigt werden. Folgendes Material soll verwendet werden:
| **Baustahl E360** | |
| --- | --- |
| Streckgrenze $R_p$ | 360 MPa |
| E-Modul $E$ | 210 GPa |
| Dichte $\varrho$ | 7.85 t/m³ |
### <font color='blue'>**Modellbildung**</font>
Das Fachwerk wird mithilfe eines bereitgestellten Tools `FETool` modelliert.
* Es erhält Informationen über die Knotenkoordinaten, welche Knoten verbunden sind, E-Modul und Querschnittsfläche der Verbindungen (Stäbe), sowie Randbedingungen und Kräfte.
* Es liefert Verschiebungen und Spannungen. Für die Problemstellung ist dabei hauptsächlich die Spannung relevant.
Die Nutzung des Tools `FETool` wird in einem separaten Notebook genauer erklärt.
Die Optimierung der Querschnittsflächen im Hinblick auf minimales Gewicht soll mit einem evolutionären Algorithmus geschehen, wobei das Framework `deap` genutzt wird. Die Entwurfsvariablen sind die in einer Liste angeordneten Stabquerschnitte. Damit ergibt sich der Typ für die Individuen der Population. Im Abschnitt Algorithmierung wird die Konfiguration des evolutionären Algorithmus in genauer erläutert.
### <font color='blue'>**Algorithmierung**</font>
Wir werden das Framework `deap` für den evolutionären Algorithmus nutzen. Dieses Paket bringt bereits vorgefertigte evolutionäre Algorithmen und Methoden bzw. Operatoren mit, bei denen wir nun eine Auswahl treffen müssen.
#### <font color='blue'>**evolutionärer Algorithmus**</font>
Als Startpunkt eignet sich der Standardalgorithmus `deap.algorithms.eaSimple()`. Dieser bildet alle typischen Schritte eines evolutionären Algorithmus (vgl. Theorie) ab. Er erhält folgende Parameter:
| Parameter | Bedeutung |
| --: | :-- |
| pop | in deap erstelle Population |
| toolbox | in deap erstelle Toolbox (mit registrierten Methoden) |
| cxpb | Crossover-Wahrscheinlichkeit (Vorschlag: 0.5) |
| mutpb | Mutations-Wahrscheinlichkeit (Vorschlag: 0.4) |
| ngen | Anzahl an Generationen (Vorschlag: 75-150) |
| stats | in deap ersteller Statistik-Container |
| halloffame | in deap ersteller Hall-of-Fame-Container (speichert die besten Individuen) |
| verbose | Ausgabe von Informationen zur aktuellen Generation bei `true`. |
Er gibt folgende Rückgabewerte:
| Parameter | Bedeutung |
| --: | :-- |
| pop | Population der letzten Generation |
| log | Protokoll über die Generationen (z.B. beste Fitness) |
Nun muss noch festgelegt werden, wie die Population, bzw. die Individuen aussehen und erstellt werden, wie die Fitness evaluiert wird und welche Methoden für die *crossover, mutate* und *select*-Schritte zum Einsatz kommen.
#### <font color='blue'>**Individuen und Population**</font>
Die Querschnittsflächen der Stäbe in m² werden von Fließkommazahlen repräsentiert. Ein Individuum sollte daher eine Liste mit einer Gleitkommazahl pro Stab sein. Die Erstellung mithilfe der `Creator`-Klasse und registrierungen in der Toolbox geschehen analog zu Beispielen, z.B. Notebook der Einheit 4 (Probalistische Methoden) mit dem Rucksackproblem.
Es gibt zwei Unterschiede:
* Die Fitness wird minimiert. Daher wird der Wichtungsfaktor -1 gewählt, (statt 1 im Rucksackproblem)
* Die Gene bestehen aus Gleitkommazahlen (anstatt bool) und müssen daher mit einer anderen Funktion initialisiert werden. Die Methode `random.uniform(min, max)` aus dem Standardpaket `random` liefert eine zufällige Gleitkommazahl zwischen den angegebenen Grenzen. Als Intervallgrenzen sollten die groben zu erwartenden Größenordnungen der Stabquerschnitte verwendet werden, um die Konvergenz zu beschleunigen. (Vorschlag: 1e-6 - 0.05)
Mit der Populationsgröße kann experimentiert werden, um den Kompromiss zwischen schneller Berechnung einer Generation und Anzahl von Generationen (sowie Größe des "Genpools") zu finden. Ein guter Startwert für diese Problemstellung mit 11 Freiheitsgeraden ist eine Populationsgröße von 150.
#### <font color='blue'>**Methoden für *mate, mutate* und *selection***</font>
Für **mate** können wir z.B. die Methode deap.`deap.tools.cxUniform` verwenden, die Nachkommen erstellt, indem bei jedem Gen auf Basis einer Wahrscheinlichkeit (`indpb`, Vorschlag 0.5) der Wert eines der beiden Eltern übernommen wird.
Bei **mutate** benötigen wir eine Methode, die Fließkommazahlen in die Population verändern kann (bei *mate* mit *cxUniform* werden sie nur ausgetauscht). Hierfür bietet sich `deap.tools.mutGaussian` an. Diese addiert einen Zufallswert, der mit einer Gaußverteilung bestimmt wird, auf das Gen. Die Parameter der Gaußverteilung (`mu`,`sigma`) bestimmen den Mittelwert und die Standardabweichung, und damit den Bereich, aus dem die Zufallszahlen kommen. Wenn die Streuung zu groß gewählt wird, hat es der Algorithmus schwer, feine Änderungen abzubilden. Ist sie zu klein, finden zu langsam Veränderungen statt. Hier muss ggf. Fallabhängig etwas experimentiert werden (Empfehlung: mu = 0, sigma = 0.0005). Die Wahrscheinlichkeit `indpb` (Vorschlag 0.2) bestimmt die Wahrscheinlichkeit, mit der ein einzelnes Gen verändert wird.
Als **selection** verwenden wir wie beim Rucksackproblem `deap.tools.selTournament`, z.B. mit einer `tournsize`von 4.
Diese Methoden werden in der Toolbox registriert.
#### <font color='blue'>**Evaluierungsfunktion**</font>
Die Funktion für `evaluate` müssen wir selbst definieren, bevor wir sie in der Toolbox registrieren können. Als **Fitness** dient das Gewicht. Wir nennen die Funktion daher `evaluateWeight`. Das Gewicht können wir uns von dem `FETool` berechnen. Dazu benötigt die Funktion `evaluateWeight` neben dem Individuum (den Querschnittsflächen) die übrigen Informationen über das Fachwerk als Parameter. Hier bietet es sich an, die Informationen über das Fachwerk (abgesehen von den zu verändernden Querschnittsflächen) in einer selbst definierten Klasse `TrussData` zu bündeln. So kann der Funktion das gesamte Fachwerk über ein zuvor definiertes Objekt übergeben werden. (Die Alternative wäre, dass jede relevante Größe des Fachwerks, also Knoten, Verbindungen, Steifigkeiten etc. der Funktion separat übergeben werden müssen).
Wenn die Funktion nur das Gewicht bestimmt, würden die Stäbe bei der Optimierung schnell negative Flächen bekommen, da dies das Gewicht minimiert. Es gilt aber die **Restriktion**, dass die maximal zulässigen Spannungen nicht überschritten werden dürfen. Es muss daher in der Funktion berücksichtigt werden, ob ein Fachwerk so definiert ist, dass die Spannungen eingehalten werden. Die Spannungen können mit dem `FETool` berechnet werden. Wenn zulässige Spannungen überschritten werden, muss die Fitness künstlich herabgesetzt, bzw. das Gewicht erhöht, werden (Penalty). Dabei sollte die Größe der Strafgewichte abhängig von der Stärke der Überschreitung sein, damit der Algorithmus *lernt, was besonders schlecht ist*. Hier muss gegebenfalls auch etwas experimentiert werden, um gute Ergebnisse zu erzielen. Eine Empfehlung ist, für jeden Stab, bei dem die Spannung überschritten wird **100kg + (Faktor der Überschreitung)\*10000kg** dem Gesamtgewicht hinzuzufügen.
Je nach Lastfall können Nullstäbe auftreten. Deshalb sollte ebenfalls eine Strafe eingebaut werden, falls die Stabquerschnitte negativ werden sollten. Eine Empfehlung ist: Für jeden negativen Stab **1000kg + (Betrag des Querschnitts)\*100000kg**.
Nach Implementierung dieser Funktion kann sie ebenfalls in der Toolbox registriert werden.
#### <font color='blue'>**Ergebnisse der Berechnung**</font>
Wir verwenden die **Hall-Of-Fame**, um das beste Fachwerk zu archivieren. Zusätzlich verwenden wir ein `Stats`-Objekt, um den Konvergenzverlauf der Generationen auszuwerten (vgl. Rucksackbeispiel). Nach Ablauf der Optimierung sollen die Stabquerschnitte und ermittelten Spannungen des besten Fachwerks ausgegeben und grafisch dargestellt werden: die Funktionen aus dem `FETool` stehen bereit. Zudem soll der Verlauf der minimalen Gewichte über die Iterationen dargestellt werden.
%% Cell type:markdown id:13ea1a48 tags:
### <font color='blue'>**Umsetzung**</font>
Import der nötigen Module. Bei random legen wir einen Seed fest, damit wir zum Testen der sogennanten Hyperparameter (Populationsgröße, Mutationswahrscheinlichkeit etc.) immer mit den gleichen Ausgangsgrößen arbeiten. Für den praktischen Einsatz muss dies nicht zwingend gemacht werden.
%% Cell type:code id:acb7c105 tags:
``` python
import random
random.seed(42)
import numpy
import time
from deap import base
from deap import creator
from deap import tools
from deap import algorithms
import FETool
```
%% Cell type:markdown id:58dab1f9 tags:
Klasse zum Buendeln der konstanten Daten des Fachwerks. Es gehen ein die Knotenkoordinaten, die Verbindungen, die Elastizität, Dichte und maximal zulässige Spannung (Sicherheitsfaktor bereits berücksichtigt) für die Stäbe, sowie die Lagerbedingungen und Kräfte.
%% Cell type:code id:f0efdbac tags:
``` python
class TrussData:
def __init__("?"):
"?"
```
%% Cell type:markdown id:c4a2c908 tags:
Das Fachwerk aus der Problemstellung wird definiert und in einem Objekt der zuvor erstellten Klasse gespeichert. Als Einheiten werden konsequent die SI-Basiseinheiten genutzt. Die Querschnittsflächen werden erst später als Population für den evolutionären Algorithmus erstellt.
%% Cell type:code id:129b9abf tags:
``` python
Coord = "?"
ElmCon = "?"
E = "?" # E-Modul von Stahl in Pa. Fuer alle Elemente
rho = "?" # Dichte von Stahl in Pa. Fuer alle Elemente
BC = "?" # Fest-Los-Lagerung
F = "?" #Lastfall
sigma_max = "?" # maximal zulässige Spannung von E360 mit S = 2 in Pa (180 MPa). Fuer alle Elemente
fachwerk = TrussData("?")
```
%% Cell type:markdown id:f0d129c2 tags:
Definition der Funktion `evaluateWeight`.
%% Cell type:code id:b3bdd7de tags:
``` python
def evaluateWeight(individual, td):
# individual bezieht sich auf die Flaechen, td steht fuer truss data und beinhaltet die Informationen des Fachwerks
weight = FETool."?"
# Ermittlung der Spannungen
U, sigma, df = FETool."?"
# Strafmassen (Penalty) fuer Ueberschreiten der maximal zulaessigen Spannung oder negativer Flaechen.
"?"
return weight,
```
%% Cell type:markdown id:a443721e tags:
Setup der Toolbox von deap:
%% Cell type:code id:4000bf83 tags:
``` python
creator.create"?"
creator.create"?"
toolbox = "?"
# Erstellen der Definition der Gene (zufaellige float zwischen Grenzwerten),
toolbox.register"?"
# Individuen (ein Gen des erstellten Typs pro Stab)
"?"
# Population (Liste von Individuen)
"?"
# Registrierung der Funktionen fuer den genetischen Algorithmus.
"?"
```
%% Cell type:markdown id:1539e4a6 tags:
Definition der Population, Hall-Of-Fame, und Statistik.
%% Cell type:code id:2d6fa221 tags:
``` python
pop = "?"
hof = "?"
stats = tools.Statistics(lambda ind: ind.fitness.values)
stats.register("avg", numpy.mean)
stats.register("std", numpy.std)
stats.register("min", numpy.min)
stats.register("max", numpy.max)
```
%% Cell type:markdown id:3de9e6f9 tags:
Ausführen des evolutionären Algorithmus und stoppen der Laufzeit mithilfe von `time`.
%% Cell type:code id:670b556d tags:
``` python
start_time = time.time()
"?"
end_time = time.time() - start_time
```
%% Cell type:markdown id:57df0a8b tags:
Ausgabe der Informationen über Laufzeit, Querschnitte und Spannungen. Da die Spannungen nicht mehr vorliegen, müssen sie noch einmal mit den Querschnitten des besten Fachwerks berechnet werden.
%% Cell type:code id:44c54be5 tags:
``` python
print("elapsed time: " + str(end_time))
print("Querschnitte der Stäbe:")
print("?")
"?"
print("Spannungen der Stäbe:")
print("?")
```
%% Cell type:markdown id:5ac1f363 tags:
Darstellung des besten Fachwerks mit Querschnitten.
%% Cell type:code id:204afbfe tags:
``` python
FETool."?"
```
%% Cell type:markdown id:87d8e1c5 tags:
Darstellung der Spannungen im besten Fachwerk. Wenn die Optimierung ein gutes Ergebnis liefert, sind alle Stäbe maximal ausgelastet (dunkle Farben). Ein heller Stab bedeutet, dass dieser (für den untersuchten Lastfall) einen kleineren Querschnitt bekommen könnte.
%% Cell type:code id:c0a9e994 tags:
``` python
FETool."?"
```
%% Cell type:markdown id:9acd9755 tags:
Konvergenzverlauf der niedrigsten Gewichte und Ausgabe des niedrigsten Gewichts. Zum Vergleich: Für das oben gegebene Fachwerk wurde mithilfe einer hier nicht näher behandelten Methode ein Minimalwert von **3140 kg** ermittelt, an diesen sollte sich der evolutionäre Algorithmus annähern.
%% Cell type:code id:6ead3426 tags:
``` python
print("Bestes Gewicht: " + "?")
# Konvergenzplot
import matplotlib.pyplot as plt
"?"
```
%% Cell type:markdown id:305d68e9 tags:
Experimentiere gerne mit den Parametern herum und analysiere, ob es die Lösung und Lösungsgeschwindigkeit verbessert, oder verschlechtert. Starte am besten für jeden Versuch das gesamte Notebook neu, damit die Tools der Toolbox immer sauber registriert werden können.
Selbstverständlich können auch beliebige andere Fachwerkdefinitionen und Lastfälle analysiert werden.
%% Cell type:markdown id:5875a0f7 tags:
### <font color='blue'>**Anregung zum selbst Programmieren:**</font>
Wir haben das Fachwerk bislang nur für einen Lastfall ausgelegt. Was muss man ändern, um mehrere Lastfälle berücksichtigen zu können? Nimm diese Änderung vor. Als 2. Lastfall soll an Knoten 2 eine Kräft von 2 MN wirken, die nun aber nicht senkrecht nach unten wirkt, sondern um 30° in x-Richtung gedreht ist. Das Fachwerk soll beide Lastfälle erfüllen. Das Programm soll so implementiert werden, dass eine beliebige Anzahl von Lastfällen in der Fachwerk-Definition aufgenommen werden kann.
%% Cell type:markdown id:28b9f724 tags:
# <font color='blue'>**Übung 4 - Evolutionärer Algorithmus**</font>
(Diese Übung gehört zur Vorlesungseinheit 5)
## <font color='blue'>**Problemstellung: Strukturdimensionierung eines Fachwerk-Kragarms**</font>
### <font color='blue'>**Problembeschreibung**</font>
Die Stäbe eines Fachwerk-Kragarms sollen möglichst materialsparend ausgelegt werden. Der Kragarm soll 1.000.000 N Gewichtskraft (ca. 100t) bei 10 m Distanz tragen können. Die Geometrie und der Auslegungslastfall sind der Skizze dargestellt:
%% Cell type:markdown id:6d978673 tags:
![](Pics/Truss_layout_annotated.png)
%% Cell type:markdown id:43cfd897 tags:
Der Querschnitt der Stäbe soll so bestimmt werden, dass die Streckgrenze des Materials mit einem Sicherheitsfaktor von **S = 2** bei dem beschriebenen Lastfall nicht erreicht wird. Dabei soll dennoch möglichst wenig Material eingesetzt werden. Für die Berechnung der auftretenden Spannungen steht bereits ein Tool zur Verfügung, zudem kann das Eigengewicht der Stäbe für den Lastfall vernachlässigt werden. Folgendes Material soll verwendet werden:
| **Baustahl E360** | |
| --- | --- |
| Streckgrenze $R_p$ | 360 MPa |
| E-Modul $E$ | 210 GPa |
| Dichte $\varrho$ | 7.85 t/m³ |
### <font color='blue'>**Modellbildung**</font>
Das Fachwerk wird mithilfe eines bereitgestellten Tools `FETool` modelliert.
* Es erhält Informationen über die Knotenkoordinaten, welche Knoten verbunden sind, E-Modul und Querschnittsfläche der Verbindungen (Stäbe), sowie Randbedingungen und Kräfte.
* Es liefert Verschiebungen und Spannungen. Für die Problemstellung ist dabei hauptsächlich die Spannung relevant.
Die Nutzung des Tools `FETool` wird in einem separaten Notebook genauer erklärt.
Die Optimierung der Querschnittsflächen im Hinblick auf minimales Gewicht soll mit einem evolutionären Algorithmus geschehen, wobei das Framework `deap` genutzt wird. Die Entwurfsvariablen sind die in einer Liste angeordneten Stabquerschnitte. Damit ergibt sich der Typ für die Individuen der Population. Im Abschnitt Algorithmierung wird die Konfiguration des evolutionären Algorithmus in genauer erläutert.
### <font color='blue'>**Algorithmierung**</font>
Wir werden das Framework `deap` für den evolutionären Algorithmus nutzen. Dieses Paket bringt bereits vorgefertigte evolutionäre Algorithmen und Methoden bzw. Operatoren mit, bei denen wir nun eine Auswahl treffen müssen.
#### <font color='blue'>**evolutionärer Algorithmus**</font>
Als Startpunkt eignet sich der Standardalgorithmus `deap.algorithms.eaSimple()`. Dieser bildet alle typischen Schritte eines evolutionären Algorithmus (vgl. Theorie) ab. Er erhält folgende Parameter:
| Parameter | Bedeutung |
| --: | :-- |
| pop | in deap erstelle Population |
| toolbox | in deap erstelle Toolbox (mit registrierten Methoden) |
| cxpb | Crossover-Wahrscheinlichkeit (Vorschlag: 0.5) |
| mutpb | Mutations-Wahrscheinlichkeit (Vorschlag: 0.4) |
| ngen | Anzahl an Generationen (Vorschlag: 75-150) |
| stats | in deap ersteller Statistik-Container |
| halloffame | in deap ersteller Hall-of-Fame-Container (speichert die besten Individuen) |
| verbose | Ausgabe von Informationen zur aktuellen Generation bei `true`. |
Er gibt folgende Rückgabewerte:
| Parameter | Bedeutung |
| --: | :-- |
| pop | Population der letzten Generation |
| log | Protokoll über die Generationen (z.B. beste Fitness) |
Nun muss noch festgelegt werden, wie die Population, bzw. die Individuen aussehen und erstellt werden, wie die Fitness evaluiert wird und welche Methoden für die *crossover, mutate* und *select*-Schritte zum Einsatz kommen.
#### <font color='blue'>**Individuen und Population**</font>
Die Querschnittsflächen der Stäbe in m² werden von Fließkommazahlen repräsentiert. Ein Individuum sollte daher eine Liste mit einer Gleitkommazahl pro Stab sein. Die Erstellung mithilfe der `Creator`-Klasse und registrierungen in der Toolbox geschehen analog zu Beispielen, z.B. Notebook der Einheit 4 (Probalistische Methoden) mit dem Rucksackproblem.
Es gibt zwei Unterschiede:
* Die Fitness wird minimiert. Daher wird der Wichtungsfaktor -1 gewählt, (statt 1 im Rucksackproblem)
* Die Gene bestehen aus Gleitkommazahlen (anstatt bool) und müssen daher mit einer anderen Funktion initialisiert werden. Die Methode `random.uniform(min, max)` aus dem Standardpaket `random` liefert eine zufällige Gleitkommazahl zwischen den angegebenen Grenzen. Als Intervallgrenzen sollten die groben zu erwartenden Größenordnungen der Stabquerschnitte verwendet werden, um die Konvergenz zu beschleunigen. (Vorschlag: 1e-6 - 0.05)
Mit der Populationsgröße kann experimentiert werden, um den Kompromiss zwischen schneller Berechnung einer Generation und Anzahl von Generationen (sowie Größe des "Genpools") zu finden. Ein guter Startwert für diese Problemstellung mit 11 Freiheitsgeraden ist eine Populationsgröße von 150.
#### <font color='blue'>**Methoden für *mate, mutate* und *selection***</font>
Für **mate** können wir z.B. die Methode deap.`deap.tools.cxUniform` verwenden, die Nachkommen erstellt, indem bei jedem Gen auf Basis einer Wahrscheinlichkeit (`indpb`, Vorschlag 0.5) der Wert eines der beiden Eltern übernommen wird.
Bei **mutate** benötigen wir eine Methode, die Fließkommazahlen in die Population verändern kann (bei *mate* mit *cxUniform* werden sie nur ausgetauscht). Hierfür bietet sich `deap.tools.mutGaussian` an. Diese addiert einen Zufallswert, der mit einer Gaußverteilung bestimmt wird, auf das Gen. Die Parameter der Gaußverteilung (`mu`,`sigma`) bestimmen den Mittelwert und die Standardabweichung, und damit den Bereich, aus dem die Zufallszahlen kommen. Wenn die Streuung zu groß gewählt wird, hat es der Algorithmus schwer, feine Änderungen abzubilden. Ist sie zu klein, finden zu langsam Veränderungen statt. Hier muss ggf. Fallabhängig etwas experimentiert werden (Empfehlung: mu = 0, sigma = 0.0005). Die Wahrscheinlichkeit `indpb` (Vorschlag 0.2) bestimmt die Wahrscheinlichkeit, mit der ein einzelnes Gen verändert wird.
Als **selection** verwenden wir wie beim Rucksackproblem `deap.tools.selTournament`, z.B. mit einer `tournsize`von 4.
Diese Methoden werden in der Toolbox registriert.
#### <font color='blue'>**Evaluierungsfunktion**</font>
Die Funktion für `evaluate` müssen wir selbst definieren, bevor wir sie in der Toolbox registrieren können. Als **Fitness** dient das Gewicht. Wir nennen die Funktion daher `evaluateWeight`. Das Gewicht können wir uns von dem `FETool` berechnen. Dazu benötigt die Funktion `evaluateWeight` neben dem Individuum (den Querschnittsflächen) die übrigen Informationen über das Fachwerk als Parameter. Hier bietet es sich an, die Informationen über das Fachwerk (abgesehen von den zu verändernden Querschnittsflächen) in einer selbst definierten Klasse `TrussData` zu bündeln. So kann der Funktion das gesamte Fachwerk über ein zuvor definiertes Objekt übergeben werden. (Die Alternative wäre, dass jede relevante Größe des Fachwerks, also Knoten, Verbindungen, Steifigkeiten etc. der Funktion separat übergeben werden müssen).
Wenn die Funktion nur das Gewicht bestimmt, würden die Stäbe bei der Optimierung schnell negative Flächen bekommen, da dies das Gewicht minimiert. Es gilt aber die **Restriktion**, dass die maximal zulässigen Spannungen nicht überschritten werden dürfen. Es muss daher in der Funktion berücksichtigt werden, ob ein Fachwerk so definiert ist, dass die Spannungen eingehalten werden. Die Spannungen können mit dem `FETool` berechnet werden. Wenn zulässige Spannungen überschritten werden, muss die Fitness künstlich herabgesetzt, bzw. das Gewicht erhöht, werden (Penalty). Dabei sollte die Größe der Strafgewichte abhängig von der Stärke der Überschreitung sein, damit der Algorithmus *lernt, was besonders schlecht ist*. Hier muss gegebenfalls auch etwas experimentiert werden, um gute Ergebnisse zu erzielen. Eine Empfehlung ist, für jeden Stab, bei dem die Spannung überschritten wird **100kg + (Faktor der Überschreitung)\*10000kg** dem Gesamtgewicht hinzuzufügen.
Je nach Lastfall können Nullstäbe auftreten. Deshalb sollte ebenfalls eine Strafe eingebaut werden, falls die Stabquerschnitte negativ werden sollten. Eine Empfehlung ist: Für jeden negativen Stab **1000kg + (Betrag des Querschnitts)\*100000kg**.
Nach Implementierung dieser Funktion kann sie ebenfalls in der Toolbox registriert werden.
#### <font color='blue'>**Ergebnisse der Berechnung**</font>
Wir verwenden die **Hall-Of-Fame**, um das beste Fachwerk zu archivieren. Zusätzlich verwenden wir ein `Stats`-Objekt, um den Konvergenzverlauf der Generationen auszuwerten (vgl. Rucksackbeispiel). Nach Ablauf der Optimierung sollen die Stabquerschnitte und ermittelten Spannungen des besten Fachwerks ausgegeben und grafisch dargestellt werden: die Funktionen aus dem `FETool` stehen bereit. Zudem soll der Verlauf der minimalen Gewichte über die Iterationen dargestellt werden.
%% Cell type:markdown id:9ec4cb07 tags:
### <font color='blue'>**Umsetzung**</font>
Import der nötigen Module. Bei random legen wir einen Seed fest, damit wir zum Testen der sogennanten Hyperparameter (Populationsgröße, Mutationswahrscheinlichkeit etc.) immer mit den gleichen Ausgangsgrößen arbeiten. Für den praktischen Einsatz muss dies nicht zwingend gemacht werden.
%% Cell type:code id:acb7c105 tags:
``` python
import random
import numpy
import time
from deap import base
from deap import creator
from deap import tools
from deap import algorithms
import FETool
```
%% Cell type:markdown id:c13d1e3f tags:
Klasse zum Bündeln/Kapseln der konstanten Daten des Fachwerks. Es gehen ein: die Knotenkoordinaten, die Satbverbindungen, der Elastizitätsmodul, die Dichte und die maximal zulässige Spannung (Sicherheitsfaktor bereits berücksichtigt) für die Stäbe, sowie die Lagerbedingungen und Kräfte.
%% Cell type:code id:f0efdbac tags:
``` python
class TrussData:
def __init__(self,Coord, ElmCon, E, rho, BC, F, sigma_max):
self.Coord = Coord
self.ElmCon = ElmCon
self.E = E
self.rho = rho
self.BC = BC
self.F = F
self.sigma_max = sigma_max
```
%% Cell type:markdown id:117b7a1d tags:
Das Fachwerk aus der Problemstellung wird definiert und in einem Objekt der zuvor erstellten Klasse gespeichert. Als Einheiten werden konsequent die SI-Basiseinheiten genutzt. Die Querschnittsflächen werden erst später als Population für den evolutionären Algorithmus erstellt.
%% Cell type:code id:129b9abf tags:
``` python
Coord = [(0,0),(2,0),(6,0),(10,0),(0,2),(4,2),(8,2)]
ElmCon = [(1,2),(2,3),(3,4),(1,5),(5,2),(2,6),(6,3),(3,7),(7,4),(5,6),(6,7)]
E = [210e9 for x in range(len(ElmCon))] # E-Modul von Stahl in Pa. Fuer alle Elemente
rho = [ 7850 for x in range(len(ElmCon))] # Dichte von Stahl in Pa. Fuer alle Elemente
BC = [[1,1,0],[1,2,0],[5,1,0]] # Fest-Los-Lagerung
F = [[4,0,-1000000]] # Lastfall
sigma_max = [1.8e8 for x in range(len(ElmCon))] # maximal zulässige Spannung von E360 mit S = 2 in Pa (180 MPa). Fuer alle Elemente
fachwerk = TrussData(Coord, ElmCon, E, rho, BC, F, sigma_max)
```
%% Cell type:markdown id:c92ce955 tags:
Definition der Funktion `evaluateWeight`.
%% Cell type:code id:b3bdd7de tags:
``` python
def evaluateWeight(individual, td):
# individual bezieht sich auf die Flaechen, td steht fuer truss data und beinhaltet die Informationen des Fachwerks
weight = FETool.TotalWeight(td.Coord, td.ElmCon, td.rho, individual)
# Ermittlung der Spannungen
U, sigma, df = FETool.Run(td.Coord, td.ElmCon, individual, td.E, td.BC, td.F)
# Strafmassen (Penalty) fuer Ueberschreiten der maximal zulaessigen Spannung oder negativer Flaechen.
for elem_stress, max_stress in zip(sigma, td.sigma_max):
if abs(elem_stress) > max_stress:
weight += 100 + ( abs(elem_stress) / max_stress )*10000
for cross_section in individual:
if cross_section < 0:
weight += 1000 + ( 100000 * abs(cross_section) )
return weight,
```
%% Cell type:markdown id:2211898b tags:
Setup der Toolbox von deap:
%% Cell type:code id:4000bf83 tags:
``` python
creator.create( "FitnessMin", base.Fitness, weights=(-1.0,) )
creator.create( "Individual", list, fitness=creator.FitnessMin )
toolbox = base.Toolbox()
toolbox.register( "attr_init", random.uniform, 1e-6, 0.05) # Erstellen der Definition der Gene (zufaellige float zwischen Grenzwerten),
toolbox.register( "individual", tools.initRepeat, creator.Individual,
toolbox.attr_init, len(fachwerk.ElmCon) ) # Individuen (ein Gen des erstellten Typs pro Stab)
toolbox.register( "population", tools.initRepeat, list, toolbox.individual ) # Population (Liste von Individuen)
toolbox.register("evaluate", evaluateWeight, td = fachwerk)
toolbox.register("mate", tools.cxUniform, indpb=0.5)
toolbox.register("mutate", tools.mutGaussian, mu=0.0, sigma=0.0005, indpb=0.2)
toolbox.register("select", tools.selTournament, tournsize=4)
```
%% Cell type:markdown id:209b39d5 tags:
Definition der Population, Hall-Of-Fame, und Statistik.
%% Cell type:code id:2d6fa221 tags:
``` python
pop = toolbox.population( n=150 )
hof = tools.HallOfFame( 10 )
stats = tools.Statistics(lambda ind: ind.fitness.values)
stats.register( "avg", numpy.mean )
stats.register( "std", numpy.std )
stats.register( "min", numpy.min )
stats.register( "max", numpy.max )
random.seed(42)
start_time = time.time()
pop, log = algorithms.eaSimple(pop, toolbox,
cxpb=0.5, mutpb=0.4, ngen=100,
stats=stats, halloffame=hof, verbose=True)
end_time = time.time() - start_time
```
%% Output
gen nevals avg std min max
0 150 294147 1.39529e+06 7162.25 1.65964e+07
1 114 33008.5 23984.4 6880.18 141780
2 104 12525.1 7902.59 5848.96 41592.2
3 95 7922.15 2479.46 5848.96 20824
4 93 6883.1 943.373 5550.12 16740.5
5 106 6748.1 2075.6 4610.86 16886.6
6 102 6615.82 2894.74 4357.86 17931.9
7 111 6273.49 3279.18 4360.89 26783.2
8 100 5801.85 3614.56 4249.12 34995.5
9 103 5713.65 3666.29 4124.04 25173.3
10 106 5761.38 3899.73 4061.2 24896
11 118 4846.25 3158.62 4040.05 26335.1
12 113 5320.35 3255.44 3974.07 16066.7
13 102 5083.04 3227.47 3954.41 24599.6
14 104 5183.99 3251.35 3933.41 15475
15 105 5111.35 3389.89 3929.19 24908.2
16 89 5209.29 3543.84 3892.26 24517.8
17 105 5058.82 3421.24 3892.26 24378.7
18 106 4905.6 3043.39 3853.77 14784.9
19 105 4608.33 2607.36 3853.77 15102.1
20 117 5091.39 3586.56 3837.1 25608.5
21 108 5271.83 3932.65 3819.3 24512.9
22 105 5392.25 4577.19 3805.12 24988.7
23 105 5306.97 3819.05 3788.12 24260.5
24 104 5223.81 4115.48 3766.92 24438.6
25 95 5131.1 4253.12 3753.63 35780.2
26 101 5533.06 4432.66 3733.44 25403.5
27 93 5088.22 3452.96 3695.43 15754.1
28 98 4874.85 3458.44 3694.7 24779.2
29 112 4781.67 3781.1 3681.46 26299.6
30 107 5740.63 5376.65 3644.11 36041.1
31 104 5082.23 3536.56 3613.42 15089.3
32 116 5286.03 4332.73 3568.47 25829.3
33 116 5936.63 4802.75 3552.68 24942.7
34 112 4799.03 3905.03 3534.5 24443.6
35 94 5479.61 4711.33 3534.5 25200.5
36 114 4949.38 4265.25 3506.22 24754.4
37 97 5176.14 4367.83 3483.27 25008.8
38 101 4986.6 4491.93 3473.13 24495.7
39 123 5602.39 4979.48 3457.59 25351.1
40 108 5871.6 6104.89 3443.37 35103.1
41 98 5285.33 4614.05 3412.36 24451.8
42 92 5012.92 4471.53 3394.86 25339
43 102 5042.47 4127.12 3391.43 24845.1
44 102 5373.63 4707.7 3390.98 24275.3
45 114 5661.73 4799.75 3376.66 24667.1
46 109 5839.28 5301.75 3373.68 24947.8
47 117 5774.63 5558.48 3349.73 34626.4
48 107 5612.21 5587.74 3331.1 34833.3
49 111 5881.33 5089.04 3323.51 24938
50 121 7235.99 7370.51 3323.82 35474.2
51 111 5576.12 5985.6 3299.25 35738.9
52 106 6231.62 6359.11 3289.7 46110.2
53 115 5624.11 5218.37 3287.53 24526.4
54 110 5720.19 6341.2 3277.26 44266.1
55 102 6204.87 6865.51 3253.73 47369.1
56 107 6077.71 6472.33 3255 35263.4
57 106 5745.94 5624.9 3248.61 34315.6
58 97 5426.1 5295.49 3233.84 25249.9
59 120 5692.59 5561.79 3230.61 34127.5
60 108 5637.59 5591.05 3230.61 24960.9
61 119 5930.14 5636.25 3216.72 25335.2
62 104 6267.72 5909.57 3211.87 35748.3
63 109 5978.71 6016.05 3205.98 25180
64 105 6094.7 6330.27 3195.36 36156.9
65 110 6825.24 6590.77 3195.36 33874.9
66 93 5930.98 6354.76 3194.4 46533.1
67 93 5513.37 6036.54 3186.42 34259.1
68 97 6892.31 7729.23 3188.9 34643.9
69 108 6842.36 6840.76 3174.42 34113.1
70 117 6093.84 6062.61 3174.42 34101.9
71 96 6968.86 7692.51 3174.42 45259.3
72 97 8147.03 8423.71 3171.98 44809.1
73 108 6207.86 6056.03 3171.23 35898.3
74 106 7424.33 7981.71 3167.83 44044.6
75 100 6436.7 7135.9 3167.06 45516.9
76 97 6378.84 6796.04 3166.79 44789.6
77 116 7162.71 7794.09 3166.79 43929.7
78 87 6434.21 7532.87 3163.96 45057.7
79 106 7454.6 8511.02 3156.35 35097.2
80 106 7241.22 8513.57 3155.39 46080.6
81 108 7941.22 8298.03 3152.95 46281.1
82 93 8300.84 9530.52 3152.37 46476.5
83 92 6188.22 6382.66 3150.68 34488.5
84 110 7100.4 8070.66 3150.68 56566.3
85 98 8033.53 7783.97 3150.68 35653.1
86 104 7395.92 8461.26 3148.73 46201.5
87 112 6747.77 7613.37 3148.65 35595.4
88 109 6837.13 7938.82 3148.65 44508.2
89 107 7154.96 7794.76 3147.54 36809.1
90 109 7170.96 7607.45 3147.54 36005.4
91 103 6955.09 7477.78 3147.18 35395.4
92 111 7840.81 8387.53 3147.18 35713
93 118 8115.97 8763.4 3146.51 46485.7
94 104 7588.05 8261.65 3146.36 37213.4
95 89 7884.97 8971.77 3146.36 36073.1
96 123 7746.17 8436.02 3145.93 35535.6
97 93 6577.9 7794.62 3145.93 55323.5
98 118 7769.18 8837.05 3144.9 45358.1
99 112 8900.75 9647.34 3144.74 46510.1
100 104 8336.82 9409.49 3144.74 44732.2
%% Cell type:markdown id:833ba7d8 tags:
Ausgabe der Informationen über Laufzeit, Querschnitte und Spannungen. Da die Spannungen nicht mehr vorliegen, müssen sie noch einmal mit den Querschnitten des besten Fachwerks berechnet werden.
%% Cell type:code id:44c54be5 tags:
``` python
print("elapsed time: " + str(end_time))
print("Querschnitte der Stäbe:")
print(hof[0])
U, sigma, df = FETool.Run(fachwerk.Coord, fachwerk.ElmCon, hof[0], fachwerk.E, fachwerk.BC, fachwerk.F)
print("Spannungen der Stäbe:")
print(sigma)
```
%% Output
elapsed time: 56.202693700790405
Querschnitte der Stäbe:
[0.02784072011118374, 0.01666976164396534, 0.005560237575056562, 0.005560082948428443, 0.007870985999307434, 0.007858505396871666, 0.007857624503361627, 0.007901917275704577, 0.007916343258966485, 0.022238201401396786, 0.011118498970914804]
Spannungen der Stäbe:
[-1.79593056e+08 -1.79966580e+08 -1.79848430e+08 -1.79853432e+08
1.79674257e+08 -1.79959609e+08 1.79979784e+08 -1.78970940e+08
1.78644801e+08 1.79870662e+08 1.79880396e+08]
%% Cell type:markdown id:fa27daf7 tags:
Darstellung des besten Fachwerks mit Querschnitten.
%% Cell type:code id:204afbfe tags:
``` python
FETool.Post.ShowTrussCross(fachwerk.Coord, fachwerk.ElmCon, hof[0])
```
%% Output
%% Cell type:markdown id:9e35404f tags:
Darstellung der Spannungen im besten Fachwerk. Wenn die Optimierung ein gutes Ergebnis liefert, sind alle Stäbe maximal ausgelastet (dunkle Farben). Ein heller Stab bedeutet, dass dieser (für den untersuchten Lastfall) einen kleineren Querschnitt bekommen könnte.
%% Cell type:code id:c0a9e994 tags:
``` python
FETool.Post.ShowTrussStress(fachwerk.Coord, fachwerk.ElmCon, sigma)
```
%% Output
%% Cell type:markdown id:17f6960c tags:
Konvergenzverlauf der niedrigsten Gewichte und Ausgabe des niedrigsten Gewichts. Zum Vergleich: Für das oben gegebene Fachwerk wurde mithilfe einer hier nicht näher behandelten Methode ein Minimalwert von **3140 kg** ermittelt, an diesen sollte sich der evolutionäre Algorithmus annähern.
%% Cell type:code id:6ead3426 tags:
``` python
print("Bestes Gewicht: " + str(hof[0].fitness))
# Konvergenzplot
import matplotlib.pyplot as plt
iteration = list(range(len(log.select("min"))))
plt.plot(iteration, log.select("min"))
plt.grid()
plt.show()
```
%% Output
Bestes Gewicht: (3144.738574407551,)
%% Cell type:markdown id:8001908e tags:
Experimentiere gerne mit den Parametern herum und analysiere, ob es die Lösung und Lösungsgeschwindigkeit verbessert, oder verschlechtert. Starte am besten für jeden Versuch das gesamte Notebook neu, damit die Tools der Toolbox immer sauber registriert werden können.
Selbstverständlich können auch beliebige andere Fachwerkdefinitionen und Lastfälle analysiert werden.
%% Cell type:markdown id:d614538d tags:
### <font color='blue'>**Anregung zum selbst Programmieren:**</font>
Wir haben das Fachwerk bislang nur für einen Lastfall ausgelegt. Was muss man ändern, um mehrere Lastfälle berücksichtigen zu können? Nimm diese Änderung vor. Als 2. Lastfall soll an Knoten 2 eine Kräft von 2 MN wirken, die nun aber nicht senkrecht nach unten wirkt, sondern um 30° in x-Richtung gedreht ist. Das Fachwerk soll beide Lastfälle erfüllen. Das Programm soll so implementiert werden, dass eine beliebige Anzahl von Lastfällen in der Fachwerk-Definition aufgenommen werden kann.
source diff could not be displayed: it is too large. Options to address this: view the blob.
Semester_2/Einheit_06/Pics/dataframe.webp

40.4 KiB

%% Cell type:markdown id:03c85993 tags:
# <font color='blue'>**Übung 5 - Datenanalyse - Pandas**</font>
(Diese Übung gehört zur Vorlesungseinheit 6)
## <font color='blue'>**Problemstellung: Analyse von PKW-Verbrauchsdaten**</font>
### <font color='blue'>**Problembeschreibung**</font>
Eine umfangreiche frei verfügbare Datenbank über Verbrauchsdaten von gut 46.000 PKW-Modelle wird von der US-Regierung unter https://www.fueleconomy.gov/feg/ws/index.shtml zur Verfügung gestellt (Eine bereits etwas aufbereitete Form liegt beweits als "vehicles.csv" im Verzeichnis dieser Übung). Diese soll mithilfe des Pakets pandas weiter aufbereitet und untersucht werden. Der Verbrauch ist wie in den USA üblich in **miles per gallon** angegeben. Dies soll zu **l/100km** umgewandelt werden. Außerdem sollen Datenlücken sinnvoll gefüllt werden. Der **Hubraum** (displ) ist bereits in Liter angegeben. Wenn in dieser Übung ohne weitere Angabe von **Verbrauch** gesprochen wird, ist der **kombinierte Verbrauch** (Stadt- und Land) gemeint. **Fueltype** in der bereitgestellten Datenbasis bezieht sich auf den **Primärkraftstoff** (Hybridfahrzeuge erscheinen als Verbenner). Bei Elektrofahrzeugen ist der Verbrauch in **miles per gallon gasoline equivalents** angegeben (Hintergrund für Interessierte https://www.caranddriver.com/research/a31863350/mpge/).
Nach einer grundsätzlichen Vertrautmachung mit der Datenbasis sollen folgende **Fragestellungen** beantwortet werden:
1) Zusammenhang zwischen Hubraum und Verbrauch, sowie Jahr und Verbrauch bei allen PKW (z.B. Korrelation, Scatter-plot, Liniendiagramm mit Median-Verbrauch über die Zeit)
2) Zusammenhang der Verbrauchsdaten vom verwendeten Kraftstoff (Boxplot mit Quartilen)
3) Vergleich der Verbrauchsdaten der deutschen Marken Audi, BMW, Mercedes-Benz, Porsche und Volkswagen (Boxplot mit Quartilen)
4) Vergleich der Verbrauchsdaten der 5 häufigsten Fahrzeugklassen (Boxplot mit Quartilen, Klassen automatisiert ermitteln)
5) Ermitteln der 15 verbrauchsarmsten Fahrzeuge eines gewählten Herstellers (oder Klasse) unter Ausschluss bestimmter Kraftstoffarten (z.B. ausgenommen Elektrofahrzeuge)
### <font color='blue'>**Modellbildung und Algorithmierung**</font>
Das Paket Pandas übernimmt die Methoden zur Datenspeicherung und Auswertung, die wir zum Beantworten der Fragestellungen verwenden und zum Teil kombinieren müssen. Dies wird im Bereich Umsetzung für jede der Fragen separat erklärt. Dies passt zu der eher interaktiven Anwendung bei der Datenauswertung eines neuen Datensatzes. Hat man regelmäßig Datensätze nach gleichem Format, können die pandas-Methoden natürlich auch in Algorithmen verwendet werden (z.B. monatliche Statistiken über Verkaufszahlen).
### <font color='blue'>**Umsetzung**</font>
Zunächst importieren wir das Paket pandas und lesen die CSV-Datei in einen Dataframe ein (Da wir später hauptsächlich in einer Kopie arbeiten, nennen wir diesen z.B. `df_orig`). Von diesem lassen wir uns zunächst die ersten Einträge anzeigen, um den Aufbau der Datenbank zu erkennen.
%% Cell type:code id:2d37eba9 tags:
``` python
import pandas as pd
df_orig = pd."?"
df_orig."?"
```
%% Cell type:markdown id:eda1a1b9 tags:
Für jedes Fahrzeugmodell stehen die Einträge **id** (fortlaufende Nummer), **make** (Hersteller), **model** (Modell), **year** (Jahr), **VClass** (Fahrzeugtyp/Fahrzeugklasse), **cylinders** (Zylinderanzahl), **displ** (Hubraum in l), **fuelType** (primärer Kraftstoff), **city**, **highway** und **combined** (Verbrauch in mpg für Stadt, Land und kombiniert) zur Verfügung.
#### <font color='blue'>**Aufbereitung**</font>
Als erstes sollen die Daten aufbereitet werden. Wir definieren dazu eine Funktion, die den Verbrauch in mpg zu l/100km, gerundet auf eine Nachkommastelle, umrechnet. Dazu benötigen wir die Umrechnungsfaktoren:
| Imperial | Metrisch |
| :--- | :--- |
| 1 mile | 1.61 km |
| 1 gallon | 3.79 l |
%% Cell type:code id:801061e9 tags:
``` python
def mpg_to_lp100km("?"):
return "?"
```
%% Cell type:markdown id:bf5e39b2 tags:
Diese Funktion können wir nun mit `apply`auf die Einträge der Verbauchsspalten anwenden. Dazu kopieren wir zunächst den eingelesenen Dataframe, um die Originaldaten nicht zu beeinflussen. Die ermittelten Spalten werden in der Kopie gespeichert und ersetzen den ursprünglichen Wert. Wir prüfen den Datensatz, um zu erkennen, ob es funktioniert hat.
%% Cell type:code id:b905c9a2 tags:
``` python
df = df_orig.copy()
df["?"] = df_orig["?"].apply("?")
"?"
```
%% Cell type:markdown id:e87738ba tags:
Als nächstes sollen Datenlücken behandelt werden. Zum Finden der Lücken hilft die `info`-Funktion
%% Cell type:code id:90cc4bae tags:
``` python
df."?"
```
%% Cell type:markdown id:c8413d12 tags:
Man sieht, dass es **46186 Einträge** gibt, für **Zylinderanzahl** und **Hubraum** aber nur **45680** bzw. **45682**. Auch wenn bereits die Vermutung naheliegt, dass diese Einträge zu Elektrofahrzeugen gehören, auf die diese Motordaten nicht zutreffen, untersuchen wir dies, indem wir uns die betreffenden Einträge anzeigen lassen. Dazu nutzen wir die selektive Auswahl und wählen mithilfe von `isnull()` nur die Einträge aus dem Dataframe aus, bei denen der Wert für die Zylinder nicht vorhanden ist. Da wir diesen noch weiter verwenden, speichern wir ihn ab.
%% Cell type:code id:3b66e4aa tags:
``` python
df_nulls = df[ "?".isnull() ]
df_nulls
```
%% Cell type:markdown id:5d830244 tags:
Es sieht so aus, als wären dies alles Elektrofahrzeuge. Um sicher zu sein, lassen wir mithilfe von `describe()` Informationen über die vorhandenen fuelTypes anzeigen.
%% Cell type:code id:474ac122 tags:
``` python
df_nulls[""]."?"
```
%% Cell type:markdown id:b33853f1 tags:
Nur 503 der 506 Einträge haben den Fueltype "Electricity". Um herauszufinden, was es mit diesen Daten auf sich hat, verwenden wir `unique()` um alle darin vorkommenden Werte zu erhalten.
%% Cell type:code id:6c2684f0 tags:
``` python
"?"
```
%% Cell type:markdown id:8321c2fa tags:
Um die 3 Benzin-Fahrzeuge mit fehlenden Informationen zum Motor anzuzeigen, filtern wir den Dataframe nach dem Fueltype.
%% Cell type:code id:fc1c3f5f tags:
``` python
"?"
```
%% Cell type:markdown id:300b0882 tags:
Hier verzichten wir zunächst darauf, den fehlenden Daten über den Hubraum nachzugehen.
Es muss nun entschieden werden, wie wir mit den fehlenden Daten umgehen wollen, um möglichst aussagekräftige Daten zu behalten.
Eine Möglichkeit (die wir in dieser Übung verwenden) ist, allen Elektrofahrzeugen bei Zylinderanzahl und Hubraum den Wert `0.0` einzutragen und die wenigen übrigen Datensätze, in denen diese Informationen fehlen, zu entfernen.
Wir beginnen mit dem Entfernen aller Einträge, bei denen Zylinder keinen Wert hat, obwohl es keine Elektrofahrzeuge sind. Einträge entfernen können wir mit mit der Dataframe-Methode `drop([liste von indizes], inplace = True)`. Nun benötigen wir eine Liste von Indizes. Wir sehen diese zwar in der Tabelle vom Schritt zuvor, können diese aber auch automatisch ermitteln. Ein Dataframe hat das Attribut `index`, in dem eine Liste aller vorhandenen Indizes gespeichert ist. Wir können also dieses Attribut des im Schritt zuvor gefilterten dataframe verwenden, um der Drop-Methode die Indizes zu übergeben. Wir überprüfen das Ergebnis mit `info()`.
%% Cell type:code id:7f4c738f tags:
``` python
delete_df = "?"
df."?"( "?", inplace = True )
"?"
```
%% Cell type:markdown id:b3d9faef tags:
Nun sind diese 3 Fahrzeuge aus dem Dataframe entfernt. Wir haben bisher nicht die fehlenden Hubraum-Daten untersucht. Da wir bereits wissen, dass wir bei den Elektrofahrzeugen die Werte auf `0.0` setzen werden, müssen wir lediglich überprüfen, ob es noch Einträge ohne angegebenen Hubraum gibt, die keine Elektrofahrzeuge sind. Dazu filtern wir den Dataframe mit einer kombinierten Bedingung. (Achtung, im Zusammenhang mit dem Filtern von Dataframes werden die Operatoren `&` für `and` und `|` für `or` benutzt, und jede Bedingung muss in Klammern gesetzt werden).
%% Cell type:code id:c8521251 tags:
``` python
df[ ( "?" ) "?" ( "?" ) ]
```
%% Cell type:markdown id:fe26b2d4 tags:
Dieser Dataframe ist leer. Das bedeutet, alle übrigenen fehlenden Daten gehören zu Elektrofahrzeugen. Wir können somit die Methode `fillna(0.0, inplace = True)` auf den gesamten Dataframe anwenden und wissen, dass dies nur noch Elektrofahrzeuge bearbeit. Wir überprüfen das Ergebnis mit `info()`.
%% Cell type:code id:cd124b9b tags:
``` python
df."?"("?", inplace = True )
df."?"
```
%% Cell type:markdown id:c6cb248c tags:
#### <font color='blue'>**Datenanalyse**</font>
Nun können wir die Daten nach belieben analysieren. Dabei orientieren wir uns an den Fragestellungen aus der Problembeschreibung.
<font color='blue'>*1) Zusammenhang zwischen Hubraum und Verbrauch, sowie Jahr und Verbrauch bei allen PKW (z.B. Korrelation, Scatter-plot, Liniendiagramm mit Median-Verbrauch über die Zeit)*
Zunächst erstellen wir eine Korrelationsmatrix.
%% Cell type:code id:3dca5c9e tags:
``` python
df."?"
```
%% Cell type:markdown id:73b47921 tags:
Aus diesen Werten erkennen wir bereits, dass eine recht **starke Korrelation zwischen Hubraum und und Verbrauch** besteht, und dass eine **schwache negative Korrelation zwischen dem Jahr und dem Verbrauch** besteht. Das kann man so interpretieren, dass in späteren Jahren tendenziell verbrauchsärmere Autos entwickelt wurden als in früheren, aber viel Streuung vorhanden ist. Diesen Zusammenhang wollen wir mithilfe von Scatter-Plots noch genauer untersuchen. Zunächst erstellen wir den Scatterplot (alle x- und y-Punkte) für Hubraum und Verbrauch, bei dem eine recht starke Korrelation (0.8) ermittelt wurde, als Referenz.
%% Cell type:code id:292e5918 tags:
``` python
import matplotlib.pyplot as plt
plt.rcParams.update({'font.size': 10, 'figure.figsize': (10, 8)}) # Diese zwei Zeilen vergrößern die Plots, die pandas generiert
df.plot(kind="?", x="?", y="?" )
```
%% Cell type:markdown id:af04ff5d tags:
Man kann gut die Tendenz erkennen, dass größerer Hubraum tendenziell mit größerem Verbrauch einhergeht.
Betrachten wir den Plot für die jährliche Entwicklung, bei der die Korrelation mit dem Betrag 0.29 kleiner ist:
%% Cell type:code id:9c0601a7 tags:
``` python
"?"
```
%% Cell type:markdown id:34f255df tags:
Hier sieht man deutlich **größere Streuung**. Dennoch ist auch hier die Tendenz zu erkennen.
Eine weitere aufschlussreiche Grafik ist ein Plot des Medianverbrauchs. Der Median ist der Wert, der die oberen 50% von den unteren 50% trennt. Für einen solchen Plot hat pandas keine direkte Methode implementiert, wir müssen den Plot also manuell erstellen.
Durch Nutzung der Methode `groupby()` können wir die Daten nach Jahr gruppieren (standardmäßig werden die Gruppen aufsteigend sortiert). Mit dem Indexoperator können wir daraus den Verbrauchswert auswählen und mit `median()` dann den Median aller Verbrauchswerte in dem entsprechenden Jahr erhalten. Die Verwendung dieser Methoden ergibt:
%% Cell type:code id:6130ecbc tags:
``` python
df.groupby("year")["combined"].median()
```
%% Cell type:markdown id:10e21ceb tags:
Es handelt sich dabei um einen pandas-Datentyp. Wir können daraus mittels `.keys()` eine Liste der Jahre erhalten, und per Konvertierung zu einer Liste eine Liste der dazugehörigen Werte. Diese können wie gewohnt mit plt geplotted werden.
%% Cell type:code id:2b267cf1 tags:
``` python
temp_medians = "?"
years = temp_medians."?"
combined_yearly_median = "?"("?")
plt."?"
"?"
```
%% Cell type:markdown id:d4057fb9 tags:
In diesem Diagramm wird die sinkende Tendenz des Verbrauchs noch einmal sichtbar. Es bleibt aber zu beachten, dass hier nur Fahrzeugmodelle ausgewertet werden. Es werden keine Aussagen über die Verkaufszahlen und Flottenstärke gemacht.
<font color='blue'>*2) Zusammenhang der Verbrauchsdaten vom verwendeten Kraftstoff (Boxplot mit Quartilen)*
Für diesen Plot können wir direkt die Methode `boxplot`von pandas verwenden
%% Cell type:code id:e576a84c tags:
``` python
df."?"(column="?", by="?")
```
%% Cell type:markdown id:f49c89df tags:
<font color='blue'>*3) Vergleich der Verbrauchsdaten der deutschen Marken Audi, BMW, Mercedes-Benz, Porsche und Volkswagen (Boxplot mit Quartilen)*
Hier kann wieder der Boxplot verwendet werden, allerdings muss zuvor der Dataframe gefiltert werden. Das Kriterium ist, ob der Hersteller einem der 5 Hersteller entspricht. Man könnte dazu wie bereits bei der Datenaufbereitung eine kombinierte Vergleichsoperation aufbauen, allerdings wird der Code dann schnell lang. Stattdessen gibt es mit `isin()` eine Methode, die für jedes Element überprüft, ob es in der übergebenen Liste enthalten ist. Anstelle also nach allen gesuchten Marken händisch zu überprüfen, erstellen wir eine Liste mit allen gewünschten Marken und verwenden die einfache (nicht kombinierte) Bedingung mit `isin()`.
%% Cell type:code id:76c128ad tags:
``` python
german_makes = "?"
df[ "?" ].boxplot("?")
```
%% Cell type:markdown id:3f7d3807 tags:
<font color='blue'>*4) Vergleich der Verbrauchsdaten der 5 häufigsten Fahrzeugklassen (Boxplot mit Quartilen, Klassen automatisiert ermitteln)*
Prinzipiell ist das Vorgehen wie bei den Marken. Wir erstellen lediglich die Liste von Fahrzeugklassen nicht selbst, sondern nutzen pandas, um die 5 häufigsten Fahrzeugklassen zu ermitteln. Dazu können wir die Methode `value_counts()` auf die Serie VClass anwenden. Das Ergebnis ist ähnlich wie bei `groupby()` eine Zusammenstellung der Kategorie mit der jeweiligen Anzahl, absteigend sortiert. Unsere 5 gesuchten Klassen sind die ersten 5 Keys.
%% Cell type:code id:31415b78 tags:
``` python
counts = "?"
# counts # entkommentieren zum Betrachten des Ergebnisses
```
%% Cell type:code id:65ce7e2f tags:
``` python
classes = counts.keys()["?"]
"?".boxplot("?")
```
%% Cell type:markdown id:c69ceeac tags:
<font color='blue'> *5) Ermitteln der 15 verbrauchsarmsten Fahrzeuge eines gewählten Herstellers (oder Klasse) unter Ausschluss bestimmter Kraftstoffarten (z.B. ausgenommen Elektrofahrzeuge)*
Hier können wir wieder mehrere Funktionen kombinieren. Zunächst filtern wir mithilfe einer kombinierten Bedingung den Dataframe nach "nicht-Elektrofahrzeugen" und den gewählten Marken. Auf das Ergebnis wenden wir die Methode `sort_values()`an, wobei wir den Verbrauch als Kriterium angeben. Dies sortiert den Dataframe nach aufsteigendem Verbrauch. Von diesem Ergebnis wiederum lassen wir die ersten 15 Einträge mittel `head()` anzeigen.
%% Cell type:code id:32c6d93f tags:
``` python
"?"
```
%% Cell type:markdown id:6ab19c5c tags:
Damit sind alle Anregungen aus der Problemstellung abgearbeitet.
### <font color = "blue"> **Anregungen zum selbst Programmieren**
Es gibt unzählige weitere Möglichkeiten, diese Daten auszuwerten. Überlege dir interessante Fragestellungen und werte sie aus.
source diff could not be displayed: it is too large. Options to address this: view the blob.
Australia,9887846 ,9999199 ,10100991 ,10218321 ,10348070 ,10570420 ,10770864 ,10986535 ,11218144 ,11359807 ,11402769
Austria,4179743,4158169,4190297,4220228,4246571,4261752,4277716,4287213,4296197,4308915,4324983
Belgium,5267437 ,5288959 ,5309245 ,5334527 ,5367561 ,5403126 ,5442557 ,5484429 ,5527684 ,4996609 ,5622157
Canada,,15829173,15834015,16146667,16303914,16478759,16601231,16802833,16807738,17101813,17379104
Czech Republic,5264218 ,5236563 ,5236715 ,5239664 ,5248431 ,5261005 ,5298196 ,5331165 ,5349616 ,5363971 ,5347235
Denmark,2714208,2721084,2727505,2734113,2741613,2750422,2763125,2779431,2791452,2804046,2813740
Finland,2657304 ,2661379 ,2666839 ,2674534 ,2683230 ,2693213 ,2703697 ,2714661 ,2726360 ,2736860 ,2748733
France,30510073,30655533,30789154,32147490,32390087,32587979,32770860,33208315,33384930,33598633,33723892
Germany,42165633 ,42191801 ,42175657 ,42147222 ,42098034 ,42013740 ,41943545 ,41818073 ,41698651 ,41639177 ,41637080
Greece,5547887,5557795,5576249,5596119,5617014,5639693,5659890,5683662,5707653,5709818,5699936
Hungary,5337873 ,5323906 ,5312629 ,5304434 ,5292002 ,5287080 ,5275839 ,5267925 ,5257424 ,5241821 ,5226007
Iceland,143125,144184,145169,146407,148689,151096,154563,157300,157694,158446,159211
Ireland,1954407 ,1994513 ,2024954 ,2062080 ,2107143 ,2118677 ,2203852 ,2235183 ,2251410 ,2301435 ,2312938
Italy,29406500,29554847,29819637,30085571,30224823,30412846,30669543,30892645,31052925,31213168,31308292
Japan,65047000 ,65185000 ,65313000 ,65390000 ,65419017 ,65440000 ,65461000 ,65441000 ,65380000 ,65730000 ,65615000
Korea,23655780,23799133,23853954,23947171,24029575,24112093,24190904,24265213,24334223,24837101,24964884
Luxembourg,225230 ,227291 ,228580 ,230260 ,236986 ,240395 ,244192 ,248665 ,252660 ,257221 ,263033
Mexico,51143166,51765793,52368927,52187289,52708439,53219640,53723982,54216256,54696909,55166362,59163070
Netherlands,8133318 ,8177101 ,8212118 ,8239547 ,8256803 ,8269478 ,8293326 ,8329391 ,8371513 ,8412317 ,8447477
New Zealand,2005120,2037900,2064200,2082670,2101930,2157580,2176700,2198270,2223070,2240560,2253000
Norway,2282132 ,2296145 ,2308408 ,2322293 ,2338238 ,2355346 ,2377481 ,2404199 ,2431447 ,2459456 ,2486999
Poland,19871665,19711782,19704178,19703582,19703200,19698704,19704140,19720950,19738587,19755664,19883870
Portugal,5343969 ,5377218 ,5408377 ,5434916 ,5453850 ,5469158 ,5478768 ,5484684 ,5489510 ,5490336 ,5511961
Slovak Republic,2767030,2767855,2768929,2771332,2773308,2775353,2777871,2782450,2787987,2793033,2772570
Spain,20629952 ,21163499 ,21543353 ,21864746 ,22196988 ,22531907 ,22926377 ,23199728 ,23316596 ,23428062 ,23719209
Sweden,4500683,4513681,4529014,4545081,4561202,4589734,4619006,4652637,4691668,4725326,4756021
Switzerland,3712121 ,3738824 ,3762609 ,3786406 ,3806626 ,3829380 ,3866480 ,3915181 ,3955240 ,3992708 ,4032409
Turkey,,34753495,35023500,35484500,35945763,35945763,35209723,35615946,36098842,36679806,37191315
United Kingdom,29753779 ,30331409 ,30506793 ,30664861 ,30817364 ,30976313 ,31145537 ,31331174 ,31518326 ,31726030 ,32115485
United States,141943484,146817188,147773459,149495099,150913782,152261670,154509976,154662649,155915671,157539944,158635141
Australia,9753133 ,9873447 ,9990513 ,10121438 ,10257418 ,10444622 ,10660917 ,10888385 ,11124254 ,11260747 ,11280804
Austria,3959567,3909120,3949825,3986296,4019354,4037171,4054214,4068047,4079093,4095337,4118035
Belgium,5042288 ,5066885 ,5087176 ,5111325 ,5143821 ,5181408 ,5224309 ,5268651 ,5312221 ,5370234 ,5413801
Canada,,15532438,15538572,15842787,15995582,16170723,16326141,16524504,16526676,16826122,17113541
Czech Republic,5005508 ,4966706 ,4974740 ,4980913 ,5002648 ,5026184 ,5082934 ,5136377 ,5157197 ,5168799 ,5158210
Denmark,2654146,2662423,2670135,2677292,2685846,2696662,2712666,2732020,2743286,2756582,2766776
Finland,2537597 ,2544916 ,2552893 ,2562077 ,2572350 ,2583742 ,2596787 ,2611653 ,2625067 ,2638416 ,2652534
France,28827658,28974588,29111526,30371081,30608686,30804161,30982280,31158647,31331380,31531113,31670391
Germany,40274676 ,40344879 ,40356014 ,40353627 ,40339961 ,40301166 ,40274292 ,40184283 ,40103606 ,40112425 ,40206663
Greece,5440113,5448582,5464401,5486632,5508165,5532047,5553895,5576740,5597465,5600067,5590131
Hungary,4836980 ,4818456 ,4804113 ,4793115 ,4784579 ,4779078 ,4769562 ,4763050 ,4756900 ,4743901 ,4731724
Iceland,143450,144287,145401,147170,151202,156576,160896,162068,159936,160006,160364
Ireland,1928276 ,1969123 ,2002778 ,2047093 ,2101876 ,2121171 ,2197483 ,2214847 ,2216444 ,2268429 ,2269831
Italy,27587242,27766223,28068608,28376804,28526888,28718441,28949747,29152423,29287403,29413274,29512404
Japan,62244000 ,62250000 ,62307000 ,62297000 ,62348977 ,62330000 ,62310000 ,62251000 ,62130000 ,62327000 ,62184000
Korea,23983838,24126185,24228209,24190906,24267609,24344276,24415883,24481480,24540316,24942339,25039557
Luxembourg,218820 ,221009 ,223020 ,224740 ,232100 ,235792 ,239607 ,244835 ,249406 ,254619 ,261820
Mexico,50683083,51274171,51844576,50814582,51238427,51654642,52066743,52466262,52853788,53229849,56519797
Netherlands,7971967 ,8015471 ,8045914 ,8065979 ,8077407 ,8088514 ,8112073 ,8156396 ,8203476 ,8243482 ,8282871
New Zealand,1934010,1971300,1998300,2017900,2037540,2070700,2092180,2117570,2144670,2164590,2180100
Norway,2241934 ,2256107 ,2269049 ,2284070 ,2301981 ,2325788 ,2359690 ,2395053 ,2426752 ,2460849 ,2498871
Poland,18760788,18506749,18486430,18470253,18453855,18426775,18411501,18414926,18428742,18444373,18654577
Portugal,4991590 ,5030247 ,5066308 ,5094339 ,5115742 ,5129937 ,5138807 ,5142566 ,5148203 ,5146643 ,5030437
Slovak Republic,2611921,2611306,2611124,2613490,2615872,2618284,2623127,2629804,2636938,2642240,2631752
Spain,19779378 ,20387085 ,20801989 ,21173289 ,21561262 ,21942724 ,22356882 ,22628444 ,22672420 ,22724864 ,23099012
Sweden,4408445,4427107,4446656,4466311,4486550,4523523,4563921,4603710,4649014,4690244,4726834
Switzerland,3549089 ,3575029 ,3601539 ,3628696 ,3652502 ,3679359 ,3727014 ,3786675 ,3830566 ,3877426 ,3922253
Turkey,,35418484,35666000,36123000,36574211,36574211,35376533,35901154,36462470,37043182,37532954
United Kingdom,28953126 ,28930648 ,29193035 ,29394997 ,29595506 ,29805033 ,30033723 ,30263920 ,30508636 ,30772582 ,31140669
United States,135301432,141957038,143037260,144947584,146394361,147922764,150336755,150464902,151840906,152449134,153596908
,Country,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012
Australia,19640979.0,19872646,20091504,20339759,20605488,21015042,21431781,21874920,22342398,22620554,22683573
Austria,8139310.0,8067289,8140122,8206524,8265925,8298923,8331930,8355260,8375290,8404252,8443018
Belgium,10309725.0,10355844,10396421,10445852,10511382,10584534,10666866,10753080,10839905,10366843,11035958
Canada,,31361611,31372587,31989454,32299496,32649482,32927372,33327337,33334414,33927935,34492645
Czech Republic,10269726.0,10203269,10211455,10220577,10251079,10287189,10381130,10467542,10506813,10532770,10505445
Denmark,5368354.0,5383507,5397640,5411405,5427459,5447084,5475791,5511451,5534738,5560628,5580516
Finland,5194901.0,5206295,5219732,5236611,5255580,5276955,5300484,5326314,5351427,5375276,5401267
France,59337731.0,59630121,59900680,62518571,62998773,63392140,63753140,64366962,64716310,65129746,65394283
Germany,82440309.0,82536680,82531671,82500849,82437995,82314906,82217837,82002356,81802257,81751602,81843743
Greece,10988000.0,11006377,11040650,11082751,11125179,11171740,11213785,11260402,11305118,11309885,11290067
Hungary,10174853.0,10142362,10116742,10097549,10076581,10066158,10045401,10030975,10014324,9985722,9957731
Iceland,286575.0,288471,290570,293577,299891,307672,315459,319368,317630,318452,319575
Ireland,3882683.0,3963636,4027732,4109173,4209019,4239848,4401335,4450030,4467854,4569864,4582769
Italy,56993742.0,57321070,57888245,58462375,58751711,59131287,59619290,60045068,60340328,60626442,60820696
Japan,127291000.0,127435000,127620000,127687000,127767994,127770000,127771000,127692000,127510000,128057000,127799000
Korea,47639618.0,47925318,48082163,48138077,48297184,48456369,48606787,48746693,48874539,49779440,50004441
Luxembourg,444050.0,448300,451600,455000,469086,476187,483799,493500,502066,511840,524853
Mexico,101826249.0,103039964,104213503,103001871,103946866,104874282,105790725,106682518,107550697,108396211,115682867
Netherlands,16105285.0,16192572,16258032,16305526,16334210,16357992,16405399,16485787,16574989,16655799,16730348
New Zealand,3939130.0,4009200,4062500,4100570,4139470,4228280,4268880,4315840,4367740,4405150,4433100
Norway,4524066.0,4552252,4577457,4606363,4640219,4681134,4737171,4799252,4858199,4920305,4985870
Poland,38632453.0,38218531,38190608,38173835,38157055,38125479,38115641,38135876,38167329,38200037,38538447
Portugal,10335559.0,10407465,10474685,10529255,10569592,10599095,10617575,10627250,10637713,10636979,10542398
Slovak Republic,5378951.0,5379161,5380053,5384822,5389180,5393637,5400998,5412254,5424925,5435273,5404322
Spain,40409330.0,41550584,42345342,43038035,43758250,44474631,45283259,45828172,45989016,46152926,46818221
Sweden,8909128.0,8940788,8975670,9011392,9047752,9113257,9182927,9256347,9340682,9415570,9482855
Switzerland,7261210.0,7313853,7364148,7415102,7459128,7508739,7593494,7701856,7785806,7870134,7954662
Turkey,,70171979,70689500,71607500,72519974,72519974,70586256,71517100,72561312,73722988,74724269
United Kingdom,58706905.0,59262057,59699828,60059858,60412870,60781346,61179260,61595094,62026962,62498612,63256154
United States,277244916.0,288774226,290810719,294442683,297308143,300184434,304846731,305127551,307756577,309989078,312232049
,,Country,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012
Australia,female,9887846.0,9999199,10100991,10218321,10348070,10570420,10770864,10986535,11218144,11359807,11402769
Australia,male,9753133.0,9873447,9990513,10121438,10257418,10444622,10660917,10888385,11124254,11260747,11280804
Australia,total,19640979.0,19872646,20091504,20339759,20605488,21015042,21431781,21874920,22342398,22620554,22683573
Austria,female,4179743.0,4158169,4190297,4220228,4246571,4261752,4277716,4287213,4296197,4308915,4324983
Austria,male,3959567.0,3909120,3949825,3986296,4019354,4037171,4054214,4068047,4079093,4095337,4118035
Austria,total,8139310.0,8067289,8140122,8206524,8265925,8298923,8331930,8355260,8375290,8404252,8443018
Belgium,female,5267437.0,5288959,5309245,5334527,5367561,5403126,5442557,5484429,5527684,4996609,5622157
Belgium,male,5042288.0,5066885,5087176,5111325,5143821,5181408,5224309,5268651,5312221,5370234,5413801
Belgium,total,10309725.0,10355844,10396421,10445852,10511382,10584534,10666866,10753080,10839905,10366843,11035958
Canada,female,,15829173,15834015,16146667,16303914,16478759,16601231,16802833,16807738,17101813,17379104
Canada,male,,15532438,15538572,15842787,15995582,16170723,16326141,16524504,16526676,16826122,17113541
Canada,total,,31361611,31372587,31989454,32299496,32649482,32927372,33327337,33334414,33927935,34492645
Czech Republic,female,5264218.0,5236563,5236715,5239664,5248431,5261005,5298196,5331165,5349616,5363971,5347235
Czech Republic,male,5005508.0,4966706,4974740,4980913,5002648,5026184,5082934,5136377,5157197,5168799,5158210
Czech Republic,total,10269726.0,10203269,10211455,10220577,10251079,10287189,10381130,10467542,10506813,10532770,10505445
Denmark,female,2714208.0,2721084,2727505,2734113,2741613,2750422,2763125,2779431,2791452,2804046,2813740
Denmark,male,2654146.0,2662423,2670135,2677292,2685846,2696662,2712666,2732020,2743286,2756582,2766776
Denmark,total,5368354.0,5383507,5397640,5411405,5427459,5447084,5475791,5511451,5534738,5560628,5580516
Finland,female,2657304.0,2661379,2666839,2674534,2683230,2693213,2703697,2714661,2726360,2736860,2748733
Finland,male,2537597.0,2544916,2552893,2562077,2572350,2583742,2596787,2611653,2625067,2638416,2652534
Finland,total,5194901.0,5206295,5219732,5236611,5255580,5276955,5300484,5326314,5351427,5375276,5401267
France,female,30510073.0,30655533,30789154,32147490,32390087,32587979,32770860,33208315,33384930,33598633,33723892
France,male,28827658.0,28974588,29111526,30371081,30608686,30804161,30982280,31158647,31331380,31531113,31670391
France,total,59337731.0,59630121,59900680,62518571,62998773,63392140,63753140,64366962,64716310,65129746,65394283
Germany,female,42165633.0,42191801,42175657,42147222,42098034,42013740,41943545,41818073,41698651,41639177,41637080
Germany,male,40274676.0,40344879,40356014,40353627,40339961,40301166,40274292,40184283,40103606,40112425,40206663
Germany,total,82440309.0,82536680,82531671,82500849,82437995,82314906,82217837,82002356,81802257,81751602,81843743
Greece,female,5547887.0,5557795,5576249,5596119,5617014,5639693,5659890,5683662,5707653,5709818,5699936
Greece,male,5440113.0,5448582,5464401,5486632,5508165,5532047,5553895,5576740,5597465,5600067,5590131
Greece,total,10988000.0,11006377,11040650,11082751,11125179,11171740,11213785,11260402,11305118,11309885,11290067
Hungary,female,5337873.0,5323906,5312629,5304434,5292002,5287080,5275839,5267925,5257424,5241821,5226007
Hungary,male,4836980.0,4818456,4804113,4793115,4784579,4779078,4769562,4763050,4756900,4743901,4731724
Hungary,total,10174853.0,10142362,10116742,10097549,10076581,10066158,10045401,10030975,10014324,9985722,9957731
Iceland,female,143125.0,144184,145169,146407,148689,151096,154563,157300,157694,158446,159211
Iceland,male,143450.0,144287,145401,147170,151202,156576,160896,162068,159936,160006,160364
Iceland,total,286575.0,288471,290570,293577,299891,307672,315459,319368,317630,318452,319575
Ireland,female,1954407.0,1994513,2024954,2062080,2107143,2118677,2203852,2235183,2251410,2301435,2312938
Ireland,male,1928276.0,1969123,2002778,2047093,2101876,2121171,2197483,2214847,2216444,2268429,2269831
Ireland,total,3882683.0,3963636,4027732,4109173,4209019,4239848,4401335,4450030,4467854,4569864,4582769
Italy,female,29406500.0,29554847,29819637,30085571,30224823,30412846,30669543,30892645,31052925,31213168,31308292
Italy,male,27587242.0,27766223,28068608,28376804,28526888,28718441,28949747,29152423,29287403,29413274,29512404
Italy,total,56993742.0,57321070,57888245,58462375,58751711,59131287,59619290,60045068,60340328,60626442,60820696
Japan,female,65047000.0,65185000,65313000,65390000,65419017,65440000,65461000,65441000,65380000,65730000,65615000
Japan,male,62244000.0,62250000,62307000,62297000,62348977,62330000,62310000,62251000,62130000,62327000,62184000
Japan,total,127291000.0,127435000,127620000,127687000,127767994,127770000,127771000,127692000,127510000,128057000,127799000
Korea,female,23655780.0,23799133,23853954,23947171,24029575,24112093,24190904,24265213,24334223,24837101,24964884
Korea,male,23983838.0,24126185,24228209,24190906,24267609,24344276,24415883,24481480,24540316,24942339,25039557
Korea,total,47639618.0,47925318,48082163,48138077,48297184,48456369,48606787,48746693,48874539,49779440,50004441
Luxembourg,female,225230.0,227291,228580,230260,236986,240395,244192,248665,252660,257221,263033
Luxembourg,male,218820.0,221009,223020,224740,232100,235792,239607,244835,249406,254619,261820
Luxembourg,total,444050.0,448300,451600,455000,469086,476187,483799,493500,502066,511840,524853
Mexico,female,51143166.0,51765793,52368927,52187289,52708439,53219640,53723982,54216256,54696909,55166362,59163070
Mexico,male,50683083.0,51274171,51844576,50814582,51238427,51654642,52066743,52466262,52853788,53229849,56519797
Mexico,total,101826249.0,103039964,104213503,103001871,103946866,104874282,105790725,106682518,107550697,108396211,115682867
Netherlands,female,8133318.0,8177101,8212118,8239547,8256803,8269478,8293326,8329391,8371513,8412317,8447477
Netherlands,male,7971967.0,8015471,8045914,8065979,8077407,8088514,8112073,8156396,8203476,8243482,8282871
Netherlands,total,16105285.0,16192572,16258032,16305526,16334210,16357992,16405399,16485787,16574989,16655799,16730348
New Zealand,female,2005120.0,2037900,2064200,2082670,2101930,2157580,2176700,2198270,2223070,2240560,2253000
New Zealand,male,1934010.0,1971300,1998300,2017900,2037540,2070700,2092180,2117570,2144670,2164590,2180100
New Zealand,total,3939130.0,4009200,4062500,4100570,4139470,4228280,4268880,4315840,4367740,4405150,4433100
Norway,female,2282132.0,2296145,2308408,2322293,2338238,2355346,2377481,2404199,2431447,2459456,2486999
Norway,male,2241934.0,2256107,2269049,2284070,2301981,2325788,2359690,2395053,2426752,2460849,2498871
Norway,total,4524066.0,4552252,4577457,4606363,4640219,4681134,4737171,4799252,4858199,4920305,4985870
Poland,female,19871665.0,19711782,19704178,19703582,19703200,19698704,19704140,19720950,19738587,19755664,19883870
Poland,male,18760788.0,18506749,18486430,18470253,18453855,18426775,18411501,18414926,18428742,18444373,18654577
Poland,total,38632453.0,38218531,38190608,38173835,38157055,38125479,38115641,38135876,38167329,38200037,38538447
Portugal,female,5343969.0,5377218,5408377,5434916,5453850,5469158,5478768,5484684,5489510,5490336,5511961
Portugal,male,4991590.0,5030247,5066308,5094339,5115742,5129937,5138807,5142566,5148203,5146643,5030437
Portugal,total,10335559.0,10407465,10474685,10529255,10569592,10599095,10617575,10627250,10637713,10636979,10542398
Slovak Republic,female,2767030.0,2767855,2768929,2771332,2773308,2775353,2777871,2782450,2787987,2793033,2772570
Slovak Republic,male,2611921.0,2611306,2611124,2613490,2615872,2618284,2623127,2629804,2636938,2642240,2631752
Slovak Republic,total,5378951.0,5379161,5380053,5384822,5389180,5393637,5400998,5412254,5424925,5435273,5404322
Spain,female,20629952.0,21163499,21543353,21864746,22196988,22531907,22926377,23199728,23316596,23428062,23719209
Spain,male,19779378.0,20387085,20801989,21173289,21561262,21942724,22356882,22628444,22672420,22724864,23099012
Spain,total,40409330.0,41550584,42345342,43038035,43758250,44474631,45283259,45828172,45989016,46152926,46818221
Sweden,female,4500683.0,4513681,4529014,4545081,4561202,4589734,4619006,4652637,4691668,4725326,4756021
Sweden,male,4408445.0,4427107,4446656,4466311,4486550,4523523,4563921,4603710,4649014,4690244,4726834
Sweden,total,8909128.0,8940788,8975670,9011392,9047752,9113257,9182927,9256347,9340682,9415570,9482855
Switzerland,female,3712121.0,3738824,3762609,3786406,3806626,3829380,3866480,3915181,3955240,3992708,4032409
Switzerland,male,3549089.0,3575029,3601539,3628696,3652502,3679359,3727014,3786675,3830566,3877426,3922253
Switzerland,total,7261210.0,7313853,7364148,7415102,7459128,7508739,7593494,7701856,7785806,7870134,7954662
Turkey,female,,34753495,35023500,35484500,35945763,35945763,35209723,35615946,36098842,36679806,37191315
Turkey,male,,35418484,35666000,36123000,36574211,36574211,35376533,35901154,36462470,37043182,37532954
Turkey,total,,70171979,70689500,71607500,72519974,72519974,70586256,71517100,72561312,73722988,74724269
United Kingdom,female,29753779.0,30331409,30506793,30664861,30817364,30976313,31145537,31331174,31518326,31726030,32115485
United Kingdom,male,28953126.0,28930648,29193035,29394997,29595506,29805033,30033723,30263920,30508636,30772582,31140669
United Kingdom,total,58706905.0,59262057,59699828,60059858,60412870,60781346,61179260,61595094,62026962,62498612,63256154
United States,female,141943484.0,146817188,147773459,149495099,150913782,152261670,154509976,154662649,155915671,157539944,158635141
United States,male,135301432.0,141957038,143037260,144947584,146394361,147922764,150336755,150464902,151840906,152449134,153596908
United States,total,277244916.0,288774226,290810719,294442683,297308143,300184434,304846731,305127551,307756577,309989078,312232049
Year "Average" "Min USD/EUR" "Max USD/EUR" "Working days"
2016 0.901696 0.864379 0.959785 247
2015 0.901896 0.830358 0.947688 256
2014 0.753941 0.716692 0.823655 255
2013 0.753234 0.723903 0.783208 255
2012 0.778848 0.743273 0.827198 256
2011 0.719219 0.671953 0.775855 257
2010 0.755883 0.686672 0.837381 258
2009 0.718968 0.661376 0.796495 256
2008 0.683499 0.625391 0.802568 256
2007 0.730754 0.672314 0.775615 255
2006 0.797153 0.750131 0.845594 255
2005 0.805097 0.740357 0.857118 257
2004 0.804828 0.733514 0.847314 259
2003 0.885766 0.791766 0.963670 255
2002 1.060945 0.953562 1.165773 255
2001 1.117587 1.047669 1.192748 255
2000 1.085899 0.962649 1.211827 255
1999 0.939475 0.848176 0.998502 261
File added
File added
File added
File added
source diff could not be displayed: it is too large. Options to address this: view the blob.
%% Cell type:markdown id:52dace14 tags:
# <font color='blue'>**Übung 6 - Datenanalyse - Pandas**</font>
(Diese Übung gehört zur Vorlesungseinheit 7)
## <font color='blue'>**Problemstellung: Erstellen und Modifizieren eines Pandas-Datensatzes**</font>
In der letzten Übung wurde ein großer Datensatz eingelesen und viel analysiert. Im Gegensatz dazu, wird in dieser kürzeren Übung ein kleiner Datensatz angelegt, modifiziert und weniger Auswertungen gemacht. Diese Übung sollte auf jeden Fall selbst ausprobiert werden, bevor die Lösung betrachtet wird.
### <font color='blue'>**Problembeschreibung**</font>
Folgende Ergebisse einer Umfrage einer Umfrage liegen vor. Dieser Datensatz soll manuell als Dataframe angelegt werden. Es gibt 6 Personen (um nicht so viel eintragen zu müssen) und 3 Fragen. Die erste Frage ist nur eine ja/nein Frage, die anderen Fragen haben Punkte zwischen 1 und 5.
| Person | Frage 1 | Frage 2 | Frage 3 |
|---|---|---|---|
| Person 1 | ja | 3 | 5 |
| Person 2 | ja | 1 | 1 |
| Person 3 | nein | 3 | 4 |
| Person 4 | nein | 2 | 4 |
| Person 5 | ja | 2 | 5 |
| Person 6 | ja | 4 | 2 |
Erstelle einen Pandas Dataframe mit den vorliegenden Daten. Die Spalte Person soll dabei der Index sein, die Fragenbezeichnung der Spaltenname.
Erstelle nun mithilfe von Pandas Auswertungen: Durchschnittswerte der Fragen und Diagramme, wie viele Personen die Punktzahl vergeben haben (vgl. Histogramm).
Erstelle zum Schluss eine neue Spalte/Serie mit der durchschnittlichen Bewertung der beiden Punktefragen (pro Person). Ggf. musst du auf den index achten. Gibt es eine Korrelation zwischen der Antwort auf Frage 2 und der durchschnittlichen Bewertung?
### <font color='blue'>**Umsetzung**</font>
%% Cell type:markdown id:f118b640 tags:
Dataframe:
%% Cell type:code id:f04c682a tags:
``` python
```
%% Cell type:markdown id:cadd1033 tags:
Durchschnitte
%% Cell type:code id:ea4577ab tags:
``` python
```
%% Cell type:markdown id:b06bb1f0 tags:
Plot:
%% Cell type:code id:552008f3 tags:
``` python
```
%% Cell type:markdown id:87a8674e tags:
Durchschnittliche Punktzahl pro Person:
%% Cell type:code id:197bf9e7 tags:
``` python
```
%% Cell type:markdown id:125e0f8f tags:
Korrelation:
%% Cell type:code id:b6c2eaf4 tags:
``` python
```
%% Cell type:markdown id:d24fe0d8 tags:
# <font color='blue'>**Übung 6 - Datenanalyse - Pandas**</font>
(Diese Übung gehört zur Vorlesungseinheit 7)
## <font color='blue'>**Problemstellung: Erstellen und Modifizieren eines Pandas-Datensatzes**</font>
In der letzten Übung wurde ein großer Datensatz eingelesen und viel analysiert. Im Gegensatz dazu, wird in dieser kürzeren Übung ein kleiner Datensatz angelegt, modifiziert und weniger Auswertungen gemacht. Diese Übung sollte auf jeden Fall selbst ausprobiert werden, bevor die Lösung betrachtet wird.
### <font color='blue'>**Problembeschreibung**</font>
Folgende Ergebisse einer Umfrage einer Umfrage liegen vor. Dieser Datensatz soll manuell als Dataframe angelegt werden. Es gibt 6 Personen (um nicht so viel eintragen zu müssen) und 3 Fragen. Die erste Frage ist nur eine ja/nein Frage, die anderen Fragen haben Punkte zwischen 1 und 5.
| Person | Frage 1 | Frage 2 | Frage 3 |
|---|---|---|---|
| Person 1 | ja | 3 | 5 |
| Person 2 | ja | 1 | 1 |
| Person 3 | nein | 3 | 4 |
| Person 4 | nein | 2 | 4 |
| Person 5 | ja | 2 | 5 |
| Person 6 | ja | 4 | 2 |
Erstelle einen Pandas Dataframe mit den vorliegenden Daten. Die Spalte Person soll dabei der Index sein, die Fragenbezeichnung der Spaltenname.
Erstelle nun mithilfe von Pandas Auswertungen: Durchschnittswerte der Fragen und Diagramme, wie viele Personen die Punktzahl vergeben haben (vgl. Histogramm).
Erstelle zum Schluss eine neue Spalte/Serie mit der durchschnittlichen Bewertung der beiden Punktefragen (pro Person). Ggf. musst du auf den index achten. Gibt es eine Korrelation zwischen der Antwort auf Frage 2 und der durchschnittlichen Bewertung?
### <font color='blue'>**Umsetzung**</font>
%% Cell type:markdown id:b802cb5f tags:
Dataframe:
%% Cell type:code id:76d8f081 tags:
``` python
import pandas as pd
F1 = pd.Series([True, True, False, False, True, True])
F2 = pd.Series([3,1,3,2,2,4])
F3 = pd.Series([5,1,4,4,5,2])
df = pd.DataFrame(data={"Frage 1":F1,"Frage 2":F2,"Frage 3":F3})
df.index = ["Person 1", "Person 2", "Person 3", "Person 4", "Person 5", "Person 6"]
df
```
%% Output
Frage 1 Frage 2 Frage 3
Person 1 True 3 5
Person 2 True 1 1
Person 3 False 3 4
Person 4 False 2 4
Person 5 True 2 5
Person 6 True 4 2
%% Cell type:markdown id:e1a6aca4 tags:
Durchschnitte
%% Cell type:code id:8cd5023d tags:
``` python
df.mean()
```
%% Output
Frage 1 0.666667
Frage 2 2.500000
Frage 3 3.500000
dtype: float64
%% Cell type:markdown id:f12a6ac9 tags:
Plot:
%% Cell type:code id:bfc83f6d tags:
``` python
df["Frage 3"].plot(kind="hist")
```
%% Output
<AxesSubplot:ylabel='Frequency'>
%% Cell type:markdown id:54426b00 tags:
Durchschnittliche Punktzahl pro Person:
%% Cell type:code id:59f2f5c6 tags:
``` python
mean = (F2+F3)/2
mean.index = ["Person 1", "Person 2", "Person 3", "Person 4", "Person 5", "Person 6"]
df["Durchschnitt"] = mean
df
```
%% Output
Frage 1 Frage 2 Frage 3 Durchschnitt
Person 1 True 3 5 4.0
Person 2 True 1 1 1.0
Person 3 False 3 4 3.5
Person 4 False 2 4 3.0
Person 5 True 2 5 3.5
Person 6 True 4 2 3.0
%% Cell type:markdown id:53d382b2 tags:
Korrelation:
%% Cell type:code id:3e1f3b22 tags:
``` python
df.corr()
```
%% Output
Frage 1 Frage 2 Frage 3 Durchschnitt
Frage 1 1.000000e+00 8.199540e-17 -0.235702 -0.184637
Frage 2 8.199540e-17 1.000000e+00 0.174078 0.636364
Frage 3 -2.357023e-01 1.740777e-01 1.000000 0.870388
Durchschnitt -1.846372e-01 6.363636e-01 0.870388 1.000000
%% Cell type:markdown id:f0b13807-b3b2-4f55-9581-9f8d4ffaa3b5 tags:
### <font color='blue'>**Grundlagen Multiprocessing**</font>
Gegenstand dieses Abschnitt ist die parallele Nutzung von mehrfach vorhandenen CPU-Hardware-Resourcen, wie sie in Multiproceesing-Computern vorhanden sind:
* mehrere Prozessoren
* mehrere Kerne
* mehrere Threading-Einheiten
Ziel ist es dabei, die parallele Ausführung mehrerer Prozessen und die Auführung von Programmen zu beschleunigen.
Die gleichzeitige Ausführung mehrerer Prozesse wird durch die CPU-Verwaltung realisiert, die dafür sorgt, dass die Resource CPU geleichmäßig - hier in Form von Zeitscheiben (unterer Balken) - auf die Prozesse verteilt. Das Management der Prozessumschaltung übernimmt das Prozessmanagement des Betriebssystems.
<div>
<img src="./Pics/Prozessverwaltung.png" width="700"/>
</div>
Mit 2 CPUs oder Kernen könnte es so aussehen.
<div>
<img src="./Pics/Prozessverwaltung-2CPUs.png" width="700"/>
</div>
#### <font color='blue'>**Begrifflichkeiten**</font>
* **Prozess** Unter Prozess wird der Ablauf eines Programms in der vollständigen Umgebung bezeichnet einschließlich der vom Betriebssystem bereitgestellte Ressourcen (Prozessumgebung): Identifikation, abgeschotter Arbeitsspeicherbereich für Code und Daten bis hin zur Prozessor-Zuteilung. Diese Betriebsmittel werden als zum Prozess zugehörig betrachtet.
Einem Prozess steht zur Laufzeit ein Speicherbereich zur Verfügung, der sich in mehrere Bereiche (Segmente) gliedert:
* Code-Segment / text-segment: Dieser Bereich enthält den Programmcode mit den ausführbaren Anweisungen. Er ist von fester Größe und üblicherweise nur mit Lesezugriff versehen.
* Daten-Segment: In diesem Bereich befinden sich die globalen Variablen, die durch den Programmierer festgelegt werden. Seine Größe ist daher fest. Da aber neben Konstanten auch variable Daten (initialisiert oder uninitialisiert) darin enthalten sind, hat man hier Lese- und Schreibzugriff.
* Heap-Segment: Programme könne in diesem Bereich zur Laufzeit zusammenhängende Speicherabschnitte dynamisch anfordern und diese in beliebiger Reihenfolge wieder freigegeben.
* Stack-Segment: In diesem Bereich liegen automatisch angeforderte Speicherabschnitte (z.B. für lokale Variablen bei einem Unterprogramm-Aufruf), die in der umgekehrten Reihenfolge wieder freigegeben werden müssen, in der sie angefordert wurden (LIFO: Last InFirst Out-Prinzip). Die Verwaltung ist daher sehr einfach, dafür kann bei unvorsichtigem Vorgehen der Speicherplatz schnell an seine Grenzen stoßen.
* Interne Prozessdaten: Dieser Bereich enthält die Daten, die der Kern für diesen Prozess benötigt. Hierzu gehören die Beschreibungen der vom Prozess allokierten Ressourcen, z.B. geöffnete Dateien, die Prozess-Attribute, z.B. Besitzer und Berechtigungen, und der Prozess-Zustand (Kontext), z.B. Register-Inhalte, Speicherbereich, etc.
<div>
<img src="./Pics/Adressraum.png" width="400"/>
</div>
Für einen Prozess werden üblicherweise Zustände definiert:
* rechenbereit / ready: Der Prozess wartet auf die Zuteilung der CPU zur (weiteren) Ausführung.
* laufend / running: Der Prozess befindet sich in der CPU in der Ausführung.
* wartend / waiting (sleeping): Der Prozess wartet auf ein Ereignis, das erfolgt sein muss, damit der Prozess wieder rechenbereit ist. z.B. Festplattenzugriff, Benutzereingabe usw.
* untot / zombie: Der Prozess wurde beendet, aber noch nicht aus der Prozessliste entfernt. Er konnte nur seine Beendigung noch nicht an den Elternprozess mitteilen.
<div>
<img src="./Pics/Prozesszustaende.png" width="401"/>
</div>
* Die **Prozess-Übergänge** sind von 1-2 dispatch, von 2-3 block, von 3-1 wake up und von 2-1 interrupt.
* **Thread** Ein Thread ist ein leichter Prozess, in dem Sinne, dass er sich mit dem Elternprozess, von dem er gestartet wurde, und eventuell weiteren Threads die Betriebsmittel teilt. Daher erfordern Threads weniger Verwaltungsarbeit als vollständige Prozesse, insbesondere beim Wechselvorgang von einem Thread zum nächsten. Ein Thread ("Faden") ist ein Ausführungsstrang innerhalb eines Prozesses. Threads können quasi parallel ausgeführt werden, erfordern jedoch unter Umständen eine Synchronisation, wenn gemeinsam auf die gleichen globalen Daten zugegriffen werden soll (Daten-Konsistenz). Der Zugriff auf gemeinsame Daten ist auch der normale Weg, Informationen zwischen den Threads auszutauschen.
<div>
<img src="./Pics/Threads.png" width="800"/>
</div>
%% Cell type:markdown id:f7963865-1729-4de7-8b59-4fa7630fa9af tags:
### <font color='blue'>**Amdahlsches Gesetz**</font>
Was lässt sich durch meherer parallele Ausführungseinheiten erreichen? Wikipedia schreibt:
Sei $t_P$ der Anteil der Laufzeit der parallelen Teilstücke eines Programms, dann ist $t_{S}$ der sequentielle Anteil, und die Gesamtlaufzeit $T$ ergibt sich bei Ausführung auf einem Kern aus der Summe:
$$
T = t_P + t_S
$$
Seien $t_P$ der parallele Anteil eines Programms und $n_{P}$ die Anzahl der Prozessoren, welche zur Berechnung eingesetzt werden können. Dann ist $t_{S}$ der sequentielle Anteil und $(t_{P}/n_{P})$ der beschleunigte parallele Anteil. Die Gesamtzeit und damit die Gesamtbeschleunigung setzt sich aus der Summe der einzelnen Glieder zusammen, und daher gilt für den Speedup $\eta _{S}$:
$$
\eta_S = {T \over t_S + t_P/n_P} \leq { T \over t_S } = { T \over { T − t_P } }
$$
<div>
<img src="./Pics/AmdahlsLaw.png" width="600"/>
</div>
Es ist zu beobachten, dass die Beschleunigung bei steigender Prozessoranzahl immer stärker vom sequentiellen Anteil des Algorithmus und der Prozessorkommunikation abhängt. Amdahl argumentierte, dass durch Parallelisierung zusätzliche Kosten wie etwa für die Kommunikation und zur Synchronisierung zwischen den Prozessoren anfallen. Damit erweitert sich die Ungleichung um einen Synchronisierungs-Zeitaufwand $ t_{O(n_P)} $ t_{{O(n_{P})}}, der dies berücksichtigt und daher mit steigendem $n$ zunimmt.
$$
\eta_S = { T \over t_S + t_{O(n_P)} + t_P/n_P } \leq { T \over t_S } = { T \over T − t_P }
$$
Durch die Einführung des neuen Parameters konvergiert die Kurve nicht mehr gegen $ T/t_{S}$, sondern erreicht ein Maximum, um dahinter wieder abzufallen. Dieser Effekt wird auch in der Praxis beobachtet: Bei genügend großer Anzahl Prozessoren übersteigt der Aufwand, das Problem zu übertragen, zu synchronisieren und zurückzusenden, den Rechenaufwand, der durch die zusätzlichen Kerne abgenommen wird.
%% Cell type:markdown id:78480fdf-603c-48e5-b671-5b634c94fbaa tags:
### <font color='blue'>**Threads in Python**</font>
In Python gibt es zwei verschiedene Implementierungen für Threads:
* Das Modul **_thread** (Python 3) betrachtet Threads als Funktionen
* Das Modul **threading** implementiert Threads als eigenständige Objekte und erlaubt komplexere parallele Verarbeitungen
Das **threading**-Modul stellt die wesentlichen Methoden zur Verfügung:
* `threading.activeCount()` - Gibt die Anzahl der aktiven Thread-Objekte zurück.
* `threading.currentThread()` - Gibt das aktuelle Thread-Objekt zurück, das dem Kontrollfaden des Aufrufers entspricht.
* `threading.enumerate()` - Gibt eine Liste aller Thread-Objekte zurück, die derzeit aktiv sind.
Zusätzlich zu diesen Methoden verfügt das Modul über die Klasse `Thread`, die das Threading implementiert. Die Methoden, die von der Thread-Klasse bereitgestellt werden, sind wie folgt:
* `run()` - Die Methode ist der Einstiegspunkt für einen Thread.
* `start()` - Die Methode startet einen Thread durch den Aufruf der `run`-Methode.
* `join([time])` - Die Methode wartet auf die Beendigung von Threads.
* `isAlive()` - Die Methode prüft, ob ein Thread noch ausgeführt wird.
* `getName()` - Die Methode gibt den Namen eines Threads zurück.
* `setName()` - Die Methode setzt den Namen eines Threads.
%% Cell type:code id:ecb959e7-16b1-44f2-8219-a05eb29e05bb tags:
``` python
import threading
import time
global_counter = 0
# Funktion der Threads
def increment(increment_by):
global global_counter
local_counter = global_counter
local_counter += increment_by
time.sleep(1)
global_counter = local_counter
print( f'{threading.current_thread().name} inkrementiert global_counter by {increment_by}, global_counter: {global_counter}' )
# Erzeugen der Threads
thread1 = threading.Thread(target=increment, args=(5,))
thread2 = threading.Thread(target=increment, args=(10,))
# Start der Threads ... warten, dass alle Threads beendet sind
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print( f'Der final Wert von global_counter ist {global_counter}' )
```
%% Output
Thread-5 inkrementiert global_counter by 5, global_counter: 5
Thread-6 inkrementiert global_counter by 10, global_counter: 10
Der final Wert von global_counter ist 10
%% Cell type:markdown id:94d0b857-5764-4110-818a-1f045b222e19 tags:
Alternative Implementierung durch Ableiten und Überladen des Konstruktors und der `run`-Methode
%% Cell type:code id:66996edc-f9b2-40aa-b202-df3813816c1c tags:
``` python
import threading
import time
global_counter = 0
class myThread( threading.Thread ):
def __init__(self, name, ink):
threading.Thread.__init__(self)
self.name = name
self.inkrement = ink
def run(self):
print ("Starten von " + self.name)
increment( self.inkrement )
print ("Beenden von " + self.name)
# Erzeugen der Threads
thread1 = myThread("Thread-1", 5)
thread2 = myThread("Thread-2", 10)
# Start der Threads ... warten, dass alle Threads beendet sind
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print( f'Der final Wert von global_counter ist {global_counter}' )
```
%% Output
Starten von Thread-1
Starten von Thread-2
Thread-2 inkrementiert global_counter by 10, global_counter: 10
Beenden von Thread-2
Thread-1 inkrementiert global_counter by 5, global_counter: 5
Beenden von Thread-1
Der final Wert von global_counter ist 5
%% Cell type:markdown id:bbaeadbf-1e37-4dba-b840-f36e55af044d tags:
#### <font color='blue'>**Lock**</font>
Wenn man sich die Ausgeben anschaut, beobachtet man das Problem, dass nicht der erwartete Wert von 15 herauskommt. Was geschieht?
Der erste Thread liest Variable `global_counter` ein, die noch den Wert 0 hat, dann geht er "schlafen". Dann liest auch der zweite Thread die Variable `global_counter` ein, die immer noch den Wert 0 hat, da der erste Thread sie nicht mehr erhöhen konnte. Die beiden Threads speichern anschließend jeweils der lokalen aufinkrementierten Wert ab.
Probleme der vorherigen Art kann man dadurch lösen, indem man "kritische Abschnitte" (Critical Sections) mit **Lock**-Objekten markiert. Sie werden dadurch **atomar**, d.h. sie können nicht aufgesplittet werden und müssen als Ganzes ausgeführt werden, bevor ein anderer Thread weiterarbeiten darf.
Das mit Python bereitgestellte Threading-Modul enthält diesen einfach zu implementierenden Sperrmechanismus, mit dem Sie Threads synchronisieren können. Eine neue Sperre bzw. Lock wird durch den Aufruf der Methode `Lock()` des Moduls erzeugt, die die neue Sperre bzw. Lock-Objekt zurückgibt.
Für das Lock-Objekte gibt es die Methoden:
* `acquire([blocking])` - Die Methode des Lock-Objekts wird verwendet, um die Threads zu zwingen, synchron zu laufen. Mit dem optionalen Parameter `blocking` können Sie steuern, ob der Thread auf den Erwerb der Sperre wartet. Wenn blocking auf 0 gesetzt ist, kehrt der Thread sofort mit dem Wert 0 zurück, wenn die Sperre nicht erlangt werden kann, und mit 1, wenn die Sperre erlangt wurde. Wenn blocking auf 1 gesetzt ist, blockiert der Thread und wartet auf die Freigabe der Sperre.
* `release()` - Die Methode des Lock-Objekts wird verwendet, um die Sperre aufzuheben, wenn sie nicht mehr benötigt wird.
%% Cell type:code id:add15328-8c75-48d2-b930-d5d6e5d9c89a tags:
``` python
import threading
import time
global_counter = 0
global_lock = threading.Lock()
# Funktion der Threads
def increment_locked(increment_by):
global global_counter
global_lock.acquire(1)
local_counter = global_counter
local_counter += increment_by
time.sleep(1)
global_counter = local_counter
global_lock.release()
print( f'{threading.current_thread().name} inkrementiert global_counter by {increment_by}, global_counter: {global_counter}' )
def increment_locked_2(increment_by):
global global_counter
with global_lock:
local_counter = global_counter
local_counter += increment_by
time.sleep(1)
global_counter = local_counter
print( f'{threading.current_thread().name} inkrementiert global_counter by {increment_by}, global_counter: {global_counter}' )
class myThread( threading.Thread ):
def __init__(self, name, ink):
threading.Thread.__init__(self)
self.name = name
self.inkrement = ink
def run(self):
print ("Starten von " + self.name)
increment_locked_2( self.inkrement )
print ("Beenden von " + self.name)
# Erzeugen der Threads
thread1 = myThread("Thread-1", 5)
thread2 = myThread("Thread-2", 10)
# Start der Threads ... warten, dass alle Threads beendet sind
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print( f'Der final Wert von global_counter ist {global_counter}' )
```
%% Output
Starten von Thread-1
Starten von Thread-2
Thread-1 inkrementiert global_counter by 5, global_counter: 5
Beenden von Thread-1
Thread-2 inkrementiert global_counter by 10, global_counter: 15
Beenden von Thread-2
Der final Wert von global_counter ist 15
%% Cell type:markdown id:cfa6c159-45fc-43ea-9dfe-03e76c7120f6 tags:
#### <font color='blue'>**Queue**</font>
Eine gute Möglichkeit zum Austausch von Daten ist die Verwendung einer Queue. Mit dem Queue-Modul kann man ein neues Thread-sicheres Queue-Objekt erstellen, das eine bestimmte Anzahl an Elementen aufnehmen kann. Es gibt folgende Methoden zur Kontrolle der Warteschlange -
* `get()` - hiermit wird ein Element aus der Warteschlange entfernt und zurückgegeben.
* `put()` - Die Methode fügt ein Element zu einer Warteschlange hinzu.
* `qsize()` - Die Funktion gibt die Anzahl der Elemente zurück, die sich derzeit in der Warteschlange befinden.
* `empty()` - Die Funktion gibt True zurück, wenn die Warteschlange leer ist; andernfalls False.
* `full()` - Die Funktion gibt True zurück, wenn die Warteschlange voll ist, andernfalls False.
Für bestimmte Fälle gibt es die Parameter `blocking = [true|false]` und `timeout`
* `blocking=true` - lässt die Rückgabe von `put` oder `get` warten, bis wieder Platz in der Queue oder ein Rückgabewert in der Queue enthalten ist.
* `blocking=false` - für ggf. zu den Ausnahmen/Exceptions `Full` bzw. `Empty`.
* `timeout=value` - gibt die Wartezeit, bis ggf. Ausnahmen/Exceptions `Full` bzw. `Empty` ausgeworfen werden.
Der Datenaustausch zwischen den Threads erfolgt dann nach dem Producer-Consumer-Prinzip, d.h. ein Thread fügt Werte in die Queue und ein anderer entnimmt diese Werte.
%% Cell type:code id:caa0897d-e6ff-4147-b6d7-32b6fcb5e583 tags:
``` python
import threading
import time
from queue import Queue
global_queue = Queue()
# Funktion der Threads
def increment_locked(increment_by, name):
global global_queue
if name.endswith( '1' ):
local_counter = 0
local_counter += increment_by
time.sleep(1)
global_queue.put(local_counter)
else:
local_counter = global_queue.get()
local_counter += increment_by
time.sleep(1)
global_queue.put(local_counter)
print( f'{threading.current_thread().name} inkrementiert global_counter by {increment_by}' )
class myThread( threading.Thread ):
def __init__(self, name, ink):
threading.Thread.__init__(self)
self.name = name
self.inkrement = ink
def run(self):
print ("Starten von " + self.name)
increment_locked( self.inkrement, self.name )
print ("Beenden von " + self.name)
# Erzeugen der Threads
thread1 = myThread("Thread-1", 5)
thread2 = myThread("Thread-2", 10)
# Start der Threads ... warten, dass alle Threads beendet sind
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print( f'Der final Wert von global_counter ist {global_queue.get()}' )
```
%% Output
Starten von Thread-1
Starten von Thread-2
Thread-1 inkrementiert global_counter by 5
Beenden von Thread-1
Thread-2 inkrementiert global_counter by 10
Beenden von Thread-2
Der final Wert von global_counter ist 15
%% Cell type:markdown id:d8c1741e-9010-4615-9170-531c58360c14 tags:
#### <font color='blue'>**Thread-Anwendungsbeispiel**</font>
Auffinden vorhandener Netzwerkgeräte mittles `ping`:
%% Cell type:code id:d1972637-4f80-43ee-b4bc-cda3cae078e0 tags:
``` python
import os, re, time
received_packages = re.compile( r"(\d) empfangen" )
status = ( "no response", "alive but losses", "alive" )
time_start = time.time()
for suffix in range(50,60):
ip = "192.168.178."+str(suffix)
ping_out = os.popen("ping -q -c2 "+ip,"r")
print( "pinging", ip )
while True:
line = ping_out.readline()
if not line: break
n_received = received_packages.findall(line)
if n_received:
print( f" ... {ip}: {status[int(n_received[0])]}" )
print( f"Laufzeit: {time.time()-time_start}" )
```
%% Output
pinging 192.168.178.50
... 192.168.178.50: no response
pinging 192.168.178.51
... 192.168.178.51: no response
pinging 192.168.178.52
... 192.168.178.52: no response
pinging 192.168.178.53
... 192.168.178.53: no response
pinging 192.168.178.54
... 192.168.178.54: no response
pinging 192.168.178.55
... 192.168.178.55: no response
pinging 192.168.178.56
... 192.168.178.56: no response
pinging 192.168.178.57
... 192.168.178.57: no response
pinging 192.168.178.58
... 192.168.178.58: no response
pinging 192.168.178.59
... 192.168.178.59: no response
Laufzeit: 110.17310190200806
%% Cell type:code id:36f732a8-21ee-490a-a601-90a7c275ed94 tags:
``` python
import os, re, threading
class ip_check(threading.Thread):
def __init__ (self,ip):
threading.Thread.__init__(self)
self.ip = ip
self.__successful_pings = -1
def run(self):
ping_out = os.popen("ping -q -c2 "+self.ip,"r")
while True:
line = ping_out.readline()
if not line: break
n_received = re.findall(received_packages,line)
if n_received:
self.__successful_pings = int(n_received[0])
def status(self):
if self.__successful_pings == 0:
return "no response"
elif self.__successful_pings == 1:
return "alive, but 50 % package loss"
elif self.__successful_pings == 2:
return "alive"
else:
return "shouldn't occur"
received_packages = re.compile(r"(\d) empfangen")
time_start = time.time()
check_results = []
for suffix in range(50,60):
ip = "192.168.178."+str(suffix)
current = ip_check(ip)
check_results.append(current)
current.start()
for el in check_results:
el.join()
print( f"Status from {el.ip} is {el.status()}")
print( f"Laufzeit: {time.time()-time_start}" )
```
%% Output
Status from 192.168.178.50 is no response
Status from 192.168.178.51 is no response
Status from 192.168.178.52 is no response
Status from 192.168.178.53 is no response
Status from 192.168.178.54 is no response
Status from 192.168.178.55 is no response
Status from 192.168.178.56 is no response
Status from 192.168.178.57 is no response
Status from 192.168.178.58 is no response
Status from 192.168.178.59 is no response
Laufzeit: 11.028608560562134
%% Cell type:markdown id:0aa2f21f-bd92-4ccd-9650-89ca4a645393 tags:
### <font color='blue'>**Multiprocessing in Python**</font>
Multiprocessing bezieht sich auf die gleichzeitige Ausführung mehrerer Prozesse, was für die Beschleunigung des Codes und die Bearbeitung großer Datensätze und Aufgaben äußerst nützlich sein kann. Durch die parallele Ausführung kann ein Auftrag in mehrere kleinere Teile aufgeteilt werden, die gleichzeitig verarbeitet werden können. Bei der typischen Ausführung mit einem Prozessor sinken die Zeitkosten von N x M, wobei M die Zeit pro Einzelprozess (d. h. Zeiteinheiten) ist, auf (N / C ) x M, wobei C die Anzahl der CPU-Kerne ist. Wenn man bedenkt, dass N für umfangreiche Daten recht groß werden kann, kann die Ausführungszeit deutlich reduziert werden. Dies kann besonders hilfreich sein, wenn der Computer über mehrere Kerne oder Prozessoren verfügt. Darüber hinaus kann das Multiprocessing die Leistung von E/A-intensiven Aufgaben verbessern, indem sie auf verschiedene Prozesse verteilt werden.
* Threads sind Teilprozesse und sind immer abhängig von dem dazugehörigen Hauptprozess.
Endet dieser, enden alle Threads oder der Hauptprozess kann erst enden, wenn alle seine Threads beendet wurden.
* Threads werden nicht (immer) gezielt auf mehrere vorhandene Prozessoren verteilt, damit ist echtes paralleles Arbeiten nicht möglich
* Echte Prozesse werden automatisch vom Betriebssystem auf alle vorhandenen Prozessoren gleichmäßig verteilt
* Kinderprozesse teilen sich keinen gemeinsamen globalen Namensraum mit ihrem Elternprozess und können nach seiner Beendigung weiterlaufen.
Das Modul `multiprocessing` ist fast exakt wie `threading` aufgebaut, aber statt Threads erzeugt es echte Prozesse, Die meisten Funktionen und Prinzipien der `threading.Thread`-Objekte gelten auch für die `multiprocessing.Process`-Objekte.
Die Grundeinheit ist hier der `Process`. Jeder `Process` hat seine eigene Kopie des Python-Interpreters und seinen eigenen Speicherplatz, so dass mehrere Aufgaben gleichzeitig ohne Konflikte ausgeführt werden können.
Die Klasse `Process` wird wie folgt gehandhabt: der aktuelle Prozess wird vollständig kopiert; es wird eine neue Prozess-Identifikation erstellt; die Aufgabe läuft als unabhängiger Kindprozess.
Je nach Plattform unterstützt das Multiprocessing drei Möglichkeiten, einen Prozess zu starten:
* **spawnen**: Der Elternprozess startet einen neuen Python-Interpreter-Prozess. Der Kindprozess erbt nur die Ressourcen, die notwendig sind, um die `run()`-Methode des Prozessobjekts auszuführen. Insbesondere werden keine unnötigen Dateideskriptoren und Handles vom Elternprozess geerbt. Das Starten eines Prozesses mit dieser Methode ist im Vergleich zur Verwendung von fork oder forkserver recht langsam. (Die Voreinstellung unter **Windows**.)
* **fork**: Der Elternprozess verwendet `os.fork()`, um den Python-Interpreter zu forken. Der Kindprozess ist, wenn er beginnt, praktisch identisch mit dem Elternprozess. Alle Ressourcen des Elternprozesses werden an den Kindprozess vererbt. Beachten Sie, dass das sichere Forken eines Multithread-Prozesses problematisch ist. (Die Voreinstellung unter **Unix**.)
* **forkserver**: Wenn das Programm startet und die Startmethode forkserver auswählt, wird ein Serverprozess gestartet. Von da an, wann immer ein neuer Prozess benötigt wird, verbindet sich der Elternprozess mit dem Server und fordert ihn auf, einen neuen Prozess zu forken. Der forkserver-Prozess ist single threaded, so dass es sicher ist, os.fork() zu verwenden. Es werden keine unnötigen Ressourcen vererbt. (Verfügbar auf den meisten Unix-Plattformen.)
%% Cell type:markdown id:69c88997-dc21-4318-86d3-8d8e1edbcff8 tags:
**Version 0:** Mit einem `global_counter`
%% Cell type:code id:979ebea7-b7e3-4921-b40c-f01f73bebe2b tags:
``` python
import multiprocessing as mp
global_counter = 0
# Funktion der Prozesse
def increment(increment_by):
global global_counter
name = mp.current_process().name
local_counter = global_counter
local_counter += increment_by
time.sleep(1)
global_counter = local_counter
print( f'{name} inkrementiert global_counter by {increment_by}, global_counter: {global_counter}\n' )
if __name__ == '__main__':
# Erzeugen der Prozesse
proc1 = Process(target=increment, args=( 5,))
proc2 = Process(target=increment, args=(10,))
# Start der Prozesse ... warten, dass alle Prozess beendet sind
proc1.start()
proc2.start()
proc1.join()
proc2.join()
print( f'Der final Wert von global_counter ist {global_counter}' )
```
%% Output
Process-63 inkrementiert global_counter by 5, global_counter: 5
Process-64 inkrementiert global_counter by 10, global_counter: 10
Der final Wert von global_counter ist 0
%% Cell type:markdown id:a0fd63df-ea81-40db-b2e9-bfdc4b0a6dfb tags:
Klappt nicht, da `global_counter` für jeden Prozess individuell ist.
#### <font color='blue'>**Shared Memory**</font>
**Version 1:** Nutzung einer prozessübergreifenden Variable:
* `Value(typecode_or_type, *args, lock=True)` - Datentyp `typecode_or_type`, `*args` werden an den Konstruktor des Datentyps weitergegeben. Wenn `lock` True ist (default), wird ein neues rekursives Lock-Objekt erstellt, um den Zugriff auf den Wert zu synchronisieren. Wenn `lock` False ist, wird der Zugriff auf das zurückgegebene Objekt nicht automatisch durch eine Sperre geschützt, so dass es nicht unbedingt "prozesssicher" ist. Der Zugriff auf den Wert erfolgt über das `value` Attribut.
* `Array(typecode_or_type, size_or_initializer, *, lock=True)` - `typecode_or_type` bestimmt den Typ der Elemente. Wenn `size_or_initializer` eine ganze Zahl ist, bestimmt sie die Länge des Arrays, und das Array wird anfangs auf Null gesetzt. Andernfalls ist `size_or_initializer` eine Sequenz, die zur Initialisierung des Arrays verwendet wird und deren Länge die Länge des Arrays bestimmt. `lock` wie oben bei `Value`.
%% Cell type:code id:5ad631b1-ae74-4cbd-8f8c-41a91c91b4ef tags:
``` python
import multiprocessing as mp
# Funktion der Prozesse
def increment(increment_by, global_lock, global_counter):
name = mp.current_process().name
#with global_lock:
#with global_counter.get_lock():
local_counter = global_counter.value
local_counter += increment_by
time.sleep(1)
global_counter.value = local_counter
print( f'{name} inkrementiert global_counter by {increment_by}, global_counter: {global_counter}\n' )
if __name__ == '__main__':
global_counter = mp.Value('i', 0)
global_lock = mp.Lock()
# Erzeugen der Prozesse
procs = []
for i in range(1,5):
procs.append( mp.Process(target=increment, args=( 5*i, global_lock, global_counter)) )
# Start der Prozesse ... warten, dass alle Threads beendet sind
for proc in procs:
proc.start()
for proc in procs:
proc.join()
print( f'Der final Wert von global_counter ist {global_counter.value}' )
```
%% Output
Process-59 inkrementiert global_counter by 5, global_counter: <Synchronized wrapper for c_int(5)>
Process-60 inkrementiert global_counter by 10, global_counter: <Synchronized wrapper for c_int(10)>
Process-61 inkrementiert global_counter by 15, global_counter: <Synchronized wrapper for c_int(15)>
Process-62 inkrementiert global_counter by 20, global_counter: <Synchronized wrapper for c_int(20)>
Der final Wert von global_counter ist 20
%% Cell type:markdown id:16606755-ad32-426d-95c9-0f739110263b tags:
**Version 2:** Mit einer prozesssicheren Queue
%% Cell type:code id:fa748c7f-a215-40ae-a664-e034f8c452f1 tags:
``` python
import multiprocessing as mp
import time
# Funktion der Prozesse
def increment_locked(increment_by):
global global_queue
name = mp.current_process().name
if name.endswith( '1' ):
local_counter = 0
local_counter += increment_by
time.sleep(1)
global_queue.put(local_counter)
else:
local_counter = global_queue.get()
local_counter += increment_by
time.sleep(1)
global_queue.put(local_counter)
print( f'{name} inkrementiert global_counter by {increment_by}\n' )
class myProcess( mp.Process ):
def __init__(self, name, ink):
mp.Process.__init__(self)
self.name = name
self.inkrement = ink
def run(self):
print ("Starten von " + self.name)
increment_locked( self.inkrement )
print ("Beenden von " + self.name)
if __name__ == '__main__':
print( mp.cpu_count() )
global_queue = mp.Queue()
# Erzeugen der Prozesse
procs = []
for i in range(1,5):
procs.append( myProcess(f'Proc-{i}', i*5) )
# Start der Prozesse ... warten, dass alle Threads beendet sind
for proc in procs:
proc.start()
for proc in procs:
proc.join()
print( f'Der final Wert von global_counter ist {global_queue.get()}' )
```
%% Output
16
Starten von Proc-1
Starten von Proc-2
Starten von Proc-3
Starten von Proc-4
Proc-1 inkrementiert global_counter by 5
Beenden von Proc-1
Proc-2 inkrementiert global_counter by 10
Beenden von Proc-2
Proc-3 inkrementiert global_counter by 15
Beenden von Proc-3
Proc-4 inkrementiert global_counter by 20
Beenden von Proc-4
Der final Wert von global_counter ist 50
%% Cell type:markdown id:596e5ff6-594e-49b4-84e0-17ac4cdcdb4d tags:
#### <font color='blue'>**Hilfreiche Funktionen des Moduls**</font>
* `active_children()`: Gibt die Liste aller aktiven Kinder des aktuellen Prozesses zurück.
* `cpu_count()`: Gibt die Anzahl der CPUs im System zurück.
* `current_process()`: Gibt das Process-Objekt zurück, das dem aktuellen Prozess entspricht.
* `parent_process()`: Gibt das Process-Objekt zurück, das dem Elternprozess von current_process() entspricht. Für den Hauptprozess ist parent_process gleich None.
#### <font color='blue'>**Pipes**</font>
Beim Multiprocessing ist eine Pipe eine Verbindung zwischen zwei Prozessen. Sie wird verwendet, um Daten von einem Prozess zu senden, die von einem anderen Prozess empfangen werden.
Eine Pipe durch ein Paar von Verbindungsobjekten realisiert, die von der Klasse multiprocessing.connection.Connection bereitgestellt werden.
Mit der Erstellung einer Pipe werden zwei Verbindungsobjekte erzeugt: das erste zum Senden, das zweite zum Empfangen.
Eine Pipe kann auch als Duplex-Verbindung konfiguriert werden, so dass jedes Verbindungsobjekt sowohl Daten senden als auch empfangen kann.
Sowohl eine Pipe als auch eine Queue können verwendet werden, um Objekte und Daten zwischen Prozessen zu senden und zu empfangen.
Eine Pipe ist einfacher als eine Queue. Es handelt sich um einen Mechanismus auf niedrigerer Ebene, der zunächst die explizite Herstellung von Verbindungen zwischen einem Prozesspaar und dann das explizite Senden und Empfangen von Daten zwischen den Prozessen beinhaltet.
Eine Queue hingegen ist ein High-Level-Konstruktor, der wie eine lokale Datenstruktur behandelt werden kann, die zufällig von mehreren Prozessen gemeinsam genutzt wird.
Wichtig ist, dass eine Queue für die Verwendung mit mehreren **Produzenten** und mehreren **Konsumenten** konzipiert ist, während eine Pipe nur für ein **Paar von Prozessen** gedacht ist.
Die einfachere Natur von Pipes kann sie jedoch bei für die gemeinsamen Nutzung von Daten zwischen zwei Prozesseneffizienter und potenziell schneller machen.
%% Cell type:code id:629f9ac6-9148-4d6a-bc92-9334b3f1f8aa tags:
``` python
import multiprocessing as mp
import time
# Funktion der Prozesse
def increment(increment_by, pipes):
name = mp.current_process().name
proc_id = int( name[-1] )
if name.endswith( '1' ):
local_counter = 0
local_counter += increment_by
time.sleep(1-0.1*proc_id)
conn = pipes[proc_id][1]
conn.send(local_counter)
else:
conn = pipes[proc_id-1][0]
local_counter = conn.recv()
local_counter += increment_by
time.sleep(1-0.1*proc_id)
conn = pipes[proc_id][1]
conn.send(local_counter)
conn.send(local_counter)
pipes[proc_id-1][1].send(local_counter)
print( f'{name} inkrementiert global_counter by {increment_by}: {local_counter}\n' )
class myProcess( mp.Process ):
def __init__(self, name, ink, pipes):
mp.Process.__init__(self)
self.name = name
self.inkrement = ink
self.pipes = pipes
def run(self):
print ("Starten von " + self.name)
increment( self.inkrement, self.pipes )
print ("Beenden von " + self.name)
if __name__ == '__main__':
print( mp.cpu_count() )
pipes = {}
for i in range(1,5):
pipes[i] = mp.Pipe()
# Erzeugen der Prozesse
procs = []
for i in range(1,5):
procs.append( myProcess(f'Proc-{i}', i*5, pipes) )
# Start der Prozesse ... warten, dass alle Threads beendet sind
for proc in procs:
proc.start()
for proc in procs:
proc.join()
print( f'Der final Wert von global_counter ist {pipes[4][0].recv()}' )
print( f'Der final Wert von global_counter ist {pipes[4][0].recv()}' )
print( f'Der final Wert von global_counter ist {pipes[3][0].recv()}' )
print( f'Der final Wert von global_counter ist {pipes[3][0].recv()}' )
```
%% Output
16
Starten von Proc-1
Starten von Proc-2
Starten von Proc-3
Starten von Proc-4
Proc-1 inkrementiert global_counter by 5: 5
Beenden von Proc-1
Proc-2 inkrementiert global_counter by 10: 15
Beenden von Proc-2
Proc-3 inkrementiert global_counter by 15: 30
Beenden von Proc-3
Proc-4 inkrementiert global_counter by 20: 50
Beenden von Proc-4
Der final Wert von global_counter ist 50
Der final Wert von global_counter ist 50
Der final Wert von global_counter ist 30
Der final Wert von global_counter ist 50