EINFACHE R-ANWEISUNGEN Erzeugen von einfachen Zahlenfolgen und Zufallszahlen Alle hier erzeugten Daten sollen in einer Variable gespeichert und ausgegeben werden Wie können auf einfachem Weg die Zahlen von 100 bis 200 erzeugt werden? num = 100:200 _Ergebnis:_ [1] 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 [20] 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 [39] 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 [58] 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 [77] 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 [96] 195 196 197 198 199 200 Wie können die Zahlen 100 bis 200 mit Abstand 2 (also 100, 102, 104,..) erzeugt werden? : die Funktion SEQ könnte hier hilfreich sein. seq(1,100,2) seq(100, 200, 2) Erzeuge 100 Normalverteilte und 100 Gleichverteilte Zahlen Normalverteilt: rnorm(100) Gleichverteilt: runif(100) EINFACHE BERECHNUNGEN / EINFACHE AUSWAHL VON ZAHLEN Bitte für diesen Teil die normalverteilten Zahlen von 1.1.3 verwenden und die Ergebnisse wieder in einer VARIABLE speichern. - x - y Folgende Kennzahlen berechnen: Mittelwert, Median, Minimum, Maximum, Standardabweichung Alle Zahlen mit 100 multiplizieren Nur die ersten 10 Zahlen auswählen Nur die Werte auswählen, die größer 0 sind EINFACHE GRAFISCHE DARSTELLUNGEN Erstellen eines einfachen Liniendiagramms aus den normalverteilten Zahlen von 1.1.2 So wird ein Bild eingefügt: [Figure caption.] Erstellen eines einfachen Boxplots dieser Zahlen Der Boxplot soll rot eingefärbt werden KOMPLEXE DATENTYPEN Bitte eine Variable erzeugen, die die Wochentage als Factor speichert Diese soll verwendet werden, um einen Data Frame zu erzeugen, der zu jedem Wochentag die Anzahl der Stunden im regulären Stundenplan der Klasse abbildet, gemeinsam mit einer Variable, die die Position des Tages, also MO=1, DI=2, beinhaltet KOMBINATION AUS DEN OBEREN BEREICHEN Erzeuge ein Data Frame, dass 2 Spalten mit jeweils 1000 Normalverteilten Werten beinhaltet als 3. Spalte soll eine Variable Geschlecht mit den Ausprägungen ’m’ und ’w’ vergeben werden Diese Werte sollen als Faktoren gespeichert werden : - Mit dem Befehl REP kann man eine Liste erzeugen, mit dem ein Wert eine bestimmte Anzahl oft wiederholt wird - Der Befehl C fügt mehrere Listen zu einer zusammen - mit dem Befehl CBIND kann man mehrere Spalten zusammenfügen Für die einzelnen Spalten sollen sprechende Bezeichnungen vergeben werden Erzeuge eine Grafik, die einen Boxplot für ’m’ und einen für ’w’ enthält, den einen rot, den anderen grün
VORBEREITUNG/INSTALLATION Python: Version 3.7.4 Es sind keine Probleme bei der Installation aufgetreten Employees - Datenbank Die Datenbank ist bereits installiert und auf dem neusten Stand. MySQL-Connector Command pip install mysql-connector ausgeführt. Keine Probleme GRUNDLEGENDE OPERATIONEN MIT DER DATENBANK Wie wird die Verbindung zur employees-Datenbank erzeugt? Das folgende Beispielprogramm verbindet sich zum lokal gehosteten MySQL-Server und konfiguriert die employees-Datenbank als aktuell verwendete Datenbank: import mysql.connector mydb = mysql.connector.connect( host="127.0.0.1", user="root", passwd="root", database="employees" ) Recherche: Was bedeutet CRUD? CRUD ist ein Akronym der vier Grundlegenden Speicheroperationen: - Create - Read - Update - Delete Beispiele con CRUD-Anweisungen anhand der employees-Tabelle Create employee INSERT INTO employees VALUES(100, '1985-01-01', 'Max', 'Mustermann', 'M', '2002-01-01'); Read from employees SELECT * FROM employees WHERE emp_no = 100; Update info(e.g. salary): UPDATE salaries SET to_date=NOW() WHERE YEAR(to_date)=9999 AND emp_no = 10000; -- Set end date of old one first INSERT INTO salaries VALUES(10000, 90000, NOW(), '9999-01-01'); Delete employee DELETE FROM employees WHERE emp_no=100; RECHERCHE: ALTERNATIVEN FÜR DEN DATENBANKZUGRIFF AUF MYSQL Grundsätzlich gibt es 2 Konzepte, um mit Programmen auf SQL Datenbanksysteme zuzugreifen: Klassisch Diese Variante wird häufig auch als Direktzugriff bezeichnet. Hier wird die Datenbank vom Programm mittels eines Connectors angesprochen. Der Connector stellt die Verbindung zur Datenbbank her und überbringt die Resultate in verarbeitbare Form. Das Programm muss diese dann weiter verarbeiten. z.B.: mysql-connector, PyMySQL ORM (Object-relational Mapping) Diese Variante stellt zum Zugriff auf die Datenbank Objekte und Klassen zur Verfügung. Man kann also direkt mit den Daten arbeiten, ohne dass Die SQL-Statements werden vom ORM Mapper generiert. z.B.: SQLAlchemy, PonyORM, DjangoORM VERWALTUNGS-TOOL FÜR EMPLOYEES Ein einfaches interaktives Verwaltungs-Tool für Employees soll erstellt werden, das folgende Funktionen unterstützt: 1. Eingabe eines Mitarbeiters 2. Löschen eines Mitarbeiters (über die emp_no) 3. Suche (und Ausgabe) eines Mitarbeiters (über die emp_no) 4. Suche (und Ausgabe) der Mitarbeiter, wo ein bestimmter Text in Vor- oder Nachnamen vorkommt Der Einfachheit halber wurde das ORM-Model aus Übung 3 verwendet. Das Programm befindet sich in der Beilage
VORBEREITUNG/INSTALLATION Python: Version 3.7.4 SQLAlchemy: Version 1.3.2 Es sind bei der Installation keine Probleme aufgetreten Aufbau der Klassenstruktur Die Installation der Employees-Datenbank wurde von der letzten Übung übernommen. Die Klassenstruktur wurde der Angabe entsprechend angelegt. PROGRAMM/QUELLCODE: from sqlalchemy import * from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class Employees(Base): __tablename__ = 'employees' emp_no = Column(Integer, primary_key=True) birth_date = Column(Date) first_name = Column(String) last_name = Column(String) gender = Column(String) hire_date = Column(Date) def __str__(self): return 'Employees: ' + str(self.emp_no) + " " + str( self.birth_date) + " " + self.first_name + " " + self.last_name + " " + self.gender + " " + str( self.hire_date) class Departments(Base): __tablename__ = 'departments' dept_no = Column(String, primary_key=True) dept_name = Column(String) def __str__(self): return 'Departments: ' + self.dept_no + " " + self.dept_name class Dept_emp(Base): __tablename__ = 'dept_emp' emp_no = Column(Integer, ForeignKey('employees.emp_no'), primary_key=True) dept_no = Column(String, ForeignKey('departments.dept_no'), primary_key=True) from_date = Column(Date) to_date = Column(Date) emp = relationship("Employees", back_populates="dept_emp") dept = relationship("Departments", back_populates="dept_emp") def __str__(self): return 'Dept_emp: ' + str(self.emp_no) + " " + self.dept_no + " " + str(self.from_date) + " " + str( self.to_date) Departments.dept_emp = relationship("Dept_emp", back_populates="dept") Employees.dept_emp = relationship("Dept_emp", back_populates="emp") class Salary(Base): __tablename__ = 'salaries' emp_no = Column(Integer, ForeignKey('employees.emp_no'), primary_key=True) salary = Column(Integer) from_date = Column(Date, primary_key=True) to_date = Column(Date) emp = relationship("Employees", back_populates="salary") def __str__(self): return 'Salary: ' + str(self.emp_no) + " " + str(self.salary) + " " + str(self.from_date) + " " + str( self.to_date) Employees.salary = relationship("Salary", back_populates="emp", cascade="delete") AUFGABEN Einfache Abfragen Bitte erstelle folgende SQLALCHEMY-Abfragen: 1. Die ältesten 100 Mitarbeiter (: Es gibt ein order_by und limit) 2. Wie viele Mitarbeiter heißen mit Vornamen “Christoper”? (: Es gibt eine Funktion count()) 3. Bitte von allen Mitarbeitern nur den Vor- und Nachnamen ausgeben (dass schon als Ergebnis der Abfrage nur diese beiden Spalten verfügbar sind) 4. Hole alle Gehaltssprünge des Mitarbeiters mit der emp_no “10580” 5. Hole alle Abteilungsnamen (: Es gibt eine Funktion distinct) Bei einigen Abfragen ist es sinnvoll limit zu verwenden, also z.B. session.query(hoeren).limit(2).all(). Code # Setup database first print("Please enter the database credentials first:") user = input("user: ") pass = input("password: ") host = input("host: ") table = input("Table: ") if table == "": table = "employees" engine = create_engine("mysql://" + str(user) + ":" + str(pass) + "@127.0.0.1:3306/" + str(table)) Session = sessionmaker() Session.configure(bind=engine) session = Session() # Actual queries begin here print("a.)") emps = session.query(Employees).order_by(Employees.birth_date).limit(10) for e in emps: print(str(e.emp_no) + " " + e.first_name + " " + str(e.birth_date)) print("b.)") cnt = session.query(Employees).filter_by(first_name="Christoper").count() print("There are " + str(cnt) + " people whose first name is Christoper") print("c.)") emps = session.query(Employees).with_entities(Employees.first_name, Employees.last_name).limit(10) for e in emps: print(e.first_name + " " + e.last_name) print("d.)") salaries = session.query(Salary).filter_by(emp_no=105800).all() for s in salaries: print("salary: "+str(s.salary)+" from:"+str(s.from_date) + " to: " + str(s.to_date)) print("e.)") departments = session.query(Departments).with_entities(Departments.dept_name).all() for dep in departments: print(dep.dept_name) Result a.) 87461 Moni 1952-02-01 406121 Supot 1952-02-01 207658 Kiyokazu 1952-02-01 237571 Ronghao 1952-02-01 65308 Jouni 1952-02-01 91374 Eishiro 1952-02-01 59884 Fan 1952-02-02 51486 Jianwen 1952-02-02 61382 Kristof 1952-02-02 33131 Reinhold 1952-02-02 b.) There are 239 people whose first name is Christoper c.) Georgi Facello Bezalel Simmel Parto Bamford Chirstian Koblick Kyoichi Maliniak Anneke Preusig Tzvetan Zielinski Saniya Kalloufi Sumant Peac Duangkaew Piveteau d.) salary: 85698 from:1992-05-10 to: 1993-05-10 salary: 87173 from:1993-05-10 to: 1994-05-10 salary: 88306 from:1994-05-10 to: 1995-05-10 salary: 92058 from:1995-05-10 to: 1996-05-09 salary: 93271 from:1996-05-09 to: 1997-05-09 salary: 96430 from:1997-05-09 to: 1998-05-09 salary: 98473 from:1998-05-09 to: 1999-05-09 salary: 98613 from:1999-05-09 to: 2000-05-08 salary: 100598 from:2000-05-08 to: 2001-05-08 salary: 100323 from:2001-05-08 to: 2002-05-08 salary: 104047 from:2002-05-08 to: 9999-01-01 e.) Customer Service Development Finance Human Resources Marketing Production Quality Management Research Sales Abfragen über mehrere Tabellen Bitte selbst 3 Abfragen über zumindest 2 Tabellen (er-)finden und umsetzen. Code print("Employee with the highest salary:") emp = session.query(Salary).order_by(desc(Salary.salary)).first() print(str(emp.emp.emp_no) + ": " + emp.emp.first_name + " " + emp.emp.last_name + " - " + str(emp.salary)) print("\nDepartment of the employee with the lowest salary:") emp = session.query(Salary).filter_by(to_date='9999-01-01').order_by(Salary.salary).first().emp dept = session.query(Departments).filter_by(dept_no=emp.dept_emp[0].dept_no).first() print(emp.first_name + " " + emp.last_name + ": " + dept.dept_name) print("\nSalary of the oldest employee:") emp = session.query(Employees).order_by(asc(Employees.birth_date)).first() print(emp.first_name + " " + emp.last_name + ": " + str(emp.salary[-1].salary)) Result Employee with the highest salary: 43624: Tokuyasu Pesch - 158220 Department of the employee with the lowest salary: Olivera Baek: Production Salary of the oldest employee: Jouni Pocchiola: 65562 Neuen Mitarbeiter erstellen 1. Sich selbst als Mitarbeiter hinzufügen (Was passiert, wenn man keine emp_no angibt?) 2. Natürlich möchte man auch ein aktuelles Gehalt beziehen. Dies bitte über das erstellte Employees-Objekt erstellen 3. Bitte auch in eine gewünschte Abteilung einschreiben, dies soll auch wieder über das erstellte Employees-Objekt geschehen Code newemp = Employees(emp_no=1, birth_date=date(2001, 1, 1), first_name="Philip", last_name="Graf", gender="M", hire_date=datetime.now().date()) # birthdate has been GDPR'd :-) session.add(newemp) salary = Salary(emp_no=1, salary=60000, from_date=datetime.now().date() - timedelta(days=1), to_date=date(9999, 1, 1)) session.add(salary) newemp.salary.append(salary) department = Dept_emp(emp_no=1, dept_no="d001", from_date=datetime.now().date(), to_date=date(9999, 1, 1)) session.add(department) newemp.dept_emp.append(department) session.commit() Falls keine emp_no angegeben wird, wird folgende Exception ausgelöst: _sqlalchemy.exc.IntegrityError: (MySQLdb._exceptions.IntegrityError) (1364, “Field ’emp_no’ doesn’t have a default value”) [SQL: ’INSERT INTO employees (birth_date, first_name, last_name, gender, hire_date) VALUES (%s, %s, %s, %s, %s)’] [parameters: (datetime.date(2001, 1, 1), ’Philip’, ’Graf’, ’M’, datetime.date(2019, 7, 16))] (Background on this error at: http://sqlalche.me/e/gkpj)_ Aktualisieren von Daten 1. Bitte bei sich selbst eine ordentliche Gehaltserhöhung vornehmen (ausgehend vom Employees-Objekt) 2. Natürlich soll das alte Gehalt nicht verloren gehen, sondern nur der entsprechende Zeitraum geändert werden Code emp = session.query(Employees).filter_by(emp_no=1).first() print("\Current salary: " + str(emp.salary[-1].salary)) me.salary[-1].to_date = datetime.now() new_salary = Salary(emp_no=1, salary=100000, from_date=datetime.now().date(), to_date=date(9999, 1, 1)) session.add(new_salary) me.salary.append(new_salary) print("New salary: " + str(me.salary[-1].salary)) session.commit() Result Current salary: 60000 New salary: 100000 Löschen von Daten 1. Bitte sich selbst wieder aus der Datenbank entfernen 2. Was passiert mit dem Gehalts- bzw. Abteilungs-Eintrag? Code emp = session.query(Employees).filter_by(emp_no=1) emp.delete() session.commit()   Beide Einträge werden mit dem Löschen des Employees auch entfernt, da die Option delete=cascade gesetzt wurde. OBJEKTORIENTIERTE PROGRAMMIERUNG IN PYTHON __str__ __str__ ist im Prinzip die TOSTRING-Methode in Python. Diese Methode wurde schon beim Aufbau der Klassenstruktur implementiert (siehe ) Weitere “Magic Methods” - __init__(self, [...): Der Constructor einer Klasse - __del__(self): Der Destructor einer Klasse - __cmp__(self, other): Dient dazu ein Objekt mit einem anderen zu vergleichen. Gibt einen negativen Integer zurück, wenn self <other, 0 wenn self == other und einen positiven Integer wenn self >other. - __len__(self): Gibt die Länge des Objekts der Klasse zurück. Wird mit len(object) aufgerufen. Eine vollständige Sammlung kann unter https://rszalski.github.io/magicmethods abgerufen werden. RECHERCHE ZU DEN TECHNOLOGIEN Welche grundlegenden Unterschiede findet man dazu im Netz? Bitte eine kleine Gegenüberstellung erstellen. OBJECT-RELATIONAL MAPPING(ORM): - + Auf die Datenbank kann objektorientiert zugegriffen werden - + Verschiedene Datenbanktypen können mit gleicher Syntax angesprochen werden - + Genaue Kenntnis der SQL-Syntax ist nicht notwendig - - Eine Optimierung der Abfragen ist nicht möglich DIREKTABFRAGEN MIT SQL: - + Bei komplexeren Statements ist klarer, was in der Datenbank passiert - + Kann händisch optimiert werden - - Aufwand: SQL-Statements müssen selbst geschrieben werden - - Beim Wechsel der dahinterliegenden Datenbank müssen alle Abfragen geändert werden