Python Codes Zusammenfassung
Auf dieser Seite, habe ich eine Reihe an verschiedenen Codes aufgelistet, um zukünftigen Data Scientisten ein breites Spektrum an Beispielen anzubieten, zur Bewältigung üblicher Aufgaben im Bereich des Umgangs mit Daten.
Ich hoffe, dass meine Leserschaft findet, wonach Sie sucht 🔥
Inhaltsverzeichnis
- Python-Basics für Data-Scientisten
- F-Strings
- List Comprehensions
- Dictionaries
- String Manipulation
- Installierung von Packages
- Hilfe
- Umgang mit Pfaden
- Speichern von Daten
- Lesen von Daten
- DataFrame erstellen
- Umwandlungen
- Selektion & Filtrierung von Zeilen & Spalten
- Selektion 1 Spalte in einem
DatenFrame
- Selektion mehrerer Spalten in einem
DataFrame
- Selektion UND Filter von Zeilen via 1 Bedingung innerhalb eines
DataFrames
- Selektion UND Filter von Zeilen via mehreren Bedingungen innerhalb eines
DataFrames
- Selektiere nur Zeilen mit Missing-Values
- Selektion mittels “Accessor Operators”
loc
&iloc
- Ersetze Werte innerhalb einer Spalte
- Die Unterschiede im Slicing beim Verwenden von
loc
VS.iloc
- Selektion 1 Spalte in einem
- Sortierung & Filtrierung
- Eine simple Daten-Exploration
- Anzahl an Zeilen herausfinden
- Anzahl an Zeilen & SPalten herausfinden
- Gesamten Datasatz ansehen
- Nur die ersten 5 Beobachtungen des Datensatzes ansehen
- Nur die letzten 5 Beobachtungen des Datensatzes ansehen
- Zusammenfassung des Datensatzes
- Nur
unique()
-Werte innerhalb der Spalten anzeigen lassen - Median & Durchschnitt für eine bestimmte Spalte berechnen
- Anwendung einer Custom-Funktion
f(x)
, aber nur auf bestimmte Zeilen im Datensatz mittelsnumpy
- Daten-Cleaning
- Finde den
Data Type
einer Variablen heraus UND die Anzahl an Missings pro Spalte - Finde den
Data Type
aller möglichen Objekte in Python - Lösche 1 ODER mehrere Spalten
- Umbenennung von 1 ODER mehrerer Spalten
- Setze ODER lösche den Row-Index mittels
.reset_index()
und.set_index()
- Erstelle eine neue Spalte mit
unique IDs
- Wende eine
function
auf eine Spalte imDataFrame
an - Umgang mit Missings
- Zähle die totale Anzahl an Missings innerhalb des
DataFrames
- Zähle die Missings pro Spalte Columns
- Zähle alle Non-Missings, aber NUR, falls die Spalten keine
0
-Werte enthalten (sonst geht es nicht…) - Zeige nur diejenigen Zeilen, die Missings enthalten
- Ersetze alle Missings mit
0
-Werten - Ersetze alle Non-Missings in einer Spalte mit Missings
- Missing-Imputationen
- Zähle die totale Anzahl an Missings innerhalb des
- Duplikate in den Daten
- Ausreisser
- Das Merging & Splitting von Datensätzen
- Skalierung von Variablen
- Finde den
- Fortgeschrittene Daten-Exploration
- Exploration von Zeitereihen-Daten
- Anwendung einer “If”-Bedingung auf einen
DataFrame
- Daten-Visualisierung
- Nützliche “Tricks”, auf denen ich gestossen bin
- Mathe-Tricks für “Hacks”
- Python “Magic Commands”
Python-Basics für Data-Scientisten
F-Strings
F-strings können in Python verwendet werden, um Ergebnisse, die in Variablen enthalten sind, dynamisch anzuzeigen.
- Beispiel zum Einsatz eines
F-Strings
:
import math
def day_converter(hour_lag):
"""
What does this function do?:
This Function will convert the index of the cross-correlation to the 'real lag' and also
converst the hours blagged behind into a more human readable lagged value, which will
be in 'days' and 'hours' instead of only 'hours'.
Why is it important?:
Suppose we know that the **index's 167th lag** has the highest correlation. From this fact,
we can conclude that the "true" 168th lag - which corresponds to the 167th index of the lag
in the list - is the most important lag that we are interested in. However, the problem that
we have is: what day & hour corresponds to the 168th lag (here: 168 hours, because our data
is the HOURLY price, e.g. the data has the unit 'hours') in the past? This is where this
converter comes into play.
Example:
--> Input: day_converter(168)
> 'Converted, the 168-th lag is 7 days and 0 hours in the past.'
"""
true_lag = hour_lag + 1
days = math.floor(true_lag/24)
diff = true_lag - (days *24)
print("Converted, the {0}-th lag is {1} days and {2} hours in the past.".format(true_lag, days, diff))
day_converter(169) # call the function, to check if it worked? --> you can play around and change the input
# --> it is always correct and shows the power of f-strings!
List Comprehensions
List Comprehensions sind ein Konzept, die zu den beliebtesten und einzigartigsten Funktionen in Python gehören. Es ist im Grunde ein “For Loop”, bei der eine Funktion auf jedes der Elemente im Loop angewendet wird. Der Output einer “List Comprehension” eine List
.
Beispiele:
List comprehension, bei der folgende
function
angewendet wird: x2
squares = [n**2 for n in range(10)] # apply f(x) = x^2 on a list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
squares
## [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
print(type(squares)) # Check output: should be a 'list'
## <class 'list'>
List Comprehensions sind besonders nützlich, wenn man über jedes Element einer Spalte iterieren möchte und dabei eine Funktion auf diese anwenden möchten.
List comprehension mit einem
if
-Statement:
planets = ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune'] # this is the list we will loop through
short_planets = [planet for planet in planets if len(planet) < 6]
short_planets
## ['Venus', 'Earth', 'Mars']
Dictionaries
Hinzufügen neuer Key-Value-Paare zu einem Dictionary
dictionary_name[key] = value
Vergleich zweier Datensätze bezüglich deren Zeilen & Spalten
Der Kontext:
Hier vergleichen wir zweiDataFrames
anhand ihres Ähnlichkeitsgrades. Die Schlüsselfrage, die ich mit dem nachstehenden Code zu beantworten versucht habe, lautet: War ich in der Lage, denselbenDataFrame
nach einer Datenbereinigung zu replizieren, relativ zu einem Referenz-Datensatz?
### Step 1): create an empty(!!) dictionary where we will store the different results of the error-difference
### between the two ddsets.
result = dict()
### Step 2) let's select one error-metric of our choice. Here: "Mean-Squared-Error"
from sktime.performance_metrics.forecasting import MeanSquaredError
end = "2021-03-12 22:00:00+00:00" # let's create the dataframe of the same length as PW's benchmark-dataframe
for col in df_joffs_replikation: # we iterate over each column(!!)
result[col] = MeanSquaredError(df_JM[col][:end]- df_PW[col][:end], square_root=True) # For EACH column passiert
# folgendes: the dictionary-KEYS will be saved as the columns' name (in the empty dictionary), while the
# dictionary-VALUES will be saved as the Outputs of the MeanSquaredError()-Function.
# > Important to note: We slice our ddset until PW's benchmark-ddset (via the `end`-variable, which denotes
# the 12.03.2021, e.g. the end of PW's notebook), when performing the "vector"-substraction!!
### Step 3) Check what the results are
result # check, if it worked?
print(result.keys()) # further check: did it REALLY work for all columns? --> yes, since it printed out all the 21 keys!
### Step 4) Berechne den durchschnittlichen Fehler (= meine DF-Values - PW's DF-Values)
"""
> Ziel: Durchschnittlich begangener Fehler (= MAPE) pro Variable (= Spalte):
--> Wir wollen überprüfen, wie gross die Wert-Abweichungen von Philipp's Notebook VS. Joffrey's Notebook
sind (jeweils für <u>jeden</u> Wert, in allen 18 Spalten). Dh, **uns interessiert die _Verteilung_ der
Abweichungen** (also, wie gross im Durchschnitt sind die Fehler und wie hoch ist deren Streuung).
Für den durchschnittlichen Fehler / Abweichung meiner replizierten Tabelle VS. diejenige von Philipps habe ich
den *Mean Absolute Percentage Error (MAPE)* gewählt (siehe 'Step 2' oben).
"""
for value in result.values(): # iteriere über den oben erstellten Dictionary und berechne die durchschnittliche
# absolute Abweichung für jede Spalte
print(value.mean()) # sollte 18 Resultate printen, da wir 18 Spalten überprüfen --> Wir erwarten hier überall
# etwa die Ziffer "0", was also jeweils einem durchschnittlichen (absoluten) Fehler von '0' entspricht, PRO Spalte!
### Step 5): Berechnung der Standardabweichung der (absoluten) Abweichungen für jede Spalte
for value in result.values():
print(value.std())
-
Hinweis: Der obige Code kann nur auf
DataFrames
ausgeführt werden, die die folgenden Bedingungen erfüllen:- Alle
Spalten
von beiden DataFrames sollten die gleichen Namen besitzen! - Beide DFs sollten die gleiche Anzahl von Zeilen (= Beobachtungen) haben. Dies habe ich durch die Verwendung einer Variable namens
end
erreicht, die überpdf_JM[col][:end]
verwendet wurde.
- Alle
String Manipulation
Umwandlung einer Liste aus Strings in einen einzigen string
# step 0: create a list with many strings in it
planets = ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']
# Step 1: Transform the whole list into a single "string"
str(planets) # should output: "['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']"
# Step 2: Now, we replace all characters that are unnecessary - such as ' [ and ] -such that we return a whole string,
# only separated by commas, with no whitespace in between them:
## "['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune']"
n = str(planets).replace("'", "").replace('[', '').replace(']', '').replace(' ', '') # replace everthing by empty-strings
print(n) # Final output
## Mercury,Venus,Earth,Mars,Jupiter,Saturn,Uranus,Neptune
Aufspaltung von Strings
an einem spezifischen Punkt
timestamps = time_series_str_column.str.split("+", expand=True)
### Erklärung der Inputs:
# > "+" is the Buchstabe, at which we will split the string.
# > "expand = True" says that we want to keep both parts of the string that was being split.
String
halbieren
Wir definieren 2 functions
, welche den string
jeweils halbieren:
def splitstring_p1(value):
string1, string2 = value[:len(value) // 2], value[len(value) // 2:]
return string1
def splitstring_p2(value):
string1, string2 = value[:len(value) // 2], value[len(value) // 2:]
return string2
Konvertierung eines Strings in ein Date Time
-Format
df['Date-Time'] = pd.to_datetime(df['Date-Time'], format='%Y-%m-%d %H:%M:%S')
df['Date-Time'] # check, if it worked?
Installierung von Packages
Installierung Packages via conda
Conda
ist ein sogenannter “Package Manager”, der vor der Installation eines gewünschten Pakets jeweils prüft, welche Abhängigkeiten erforderlich sind, damit das Paket verwendet werden kann.
- Beipsiel: Wenn man zum Beispiel das Package
sktime
verwendet, dann kannconda
erkennen, dass es einen Konflikt mit der aktuellen Version von - beispielsweise - dem PackageTornado
geben könnte. Daher wirdconda
nicht nursktime
herunterladen, sondern auch das PaketTornado
auf eine neuere Version bringen, damit es mitsktime
kompatibel wird. - Hinweis: Eine Installation via
conda
kann manchmal relativ lange dauern, da - insbesondere bei grossen Projekten - viele Abhängigkeiten überprüft werden müssen! - Wichtig: Für die Verwendung von
conda
sollte auf das Terminal zurückgegriffen werden.
Beispiel zur Verwendung
conda
’s via einem “Virtual Environment”
Um ein bestimmtes Package - z.B.sktime
- in eine bestehende virtuelle Umgebung namens"myenv"
zu installieren, muss folgender Code ins Terminal eingegeben werden:
conda install --name myenv sktime
Beispiel zur Verwendung
conda
’s via einem “Global Environment”
Um das Paketsktime
in die aktuelle (globale) Umgebung (= dh “normal”), muss folgender Code ins Terminal eingegeben werden:
conda install sktime
Installierung von Packages via pip
Was sind die Nachteile von der Installation von Packages via
pip
statt einer Installation viaconda
?
pip
prüft nicht im Hintergrund, ob die verschiedenen Versionen der verschiedenen Packages, die man verwendet, kompatibel sind. Daher kann die Verwendung vonpip
ein Projekt “kaputt machen”, wenn man ein bestehendes Package aktualisiert ODER ein neues Package installiert…
Um ein Paket mit pip
zu installieren, muss folgender Code ins Terminal eingegeben werden:
pip install myPackage
Installierung von Packages // Modulen oder Sub-Modulen
import numpy as np # import eines Package // Modules
import matplotlib.pyplot as plt
from matplotlib.dates import date2num # das ist ein Beispiel zum import eines "Sub-Modules".
import pandas as pd
from scipy import stats
from datetime import datetime
De-Installierung von Packages via pip
pip uninstall fbprophet prophet cmdstanpy
Hilfe & Dokumentation gesucht?
help()
Umgang mit Pfaden
Warum ist der Umgang mit Pfaden so wichtig?
Weil man im Verlauf eines Data-Science Projektes stets Modelle, DFs oder Dateien laden ODER speichern muss. Für den Umgang mit Pfaden empfiehlt sich, das Packageos
zu beherrschen, welches bereits in “Base-Python” eingebaut ist.
Die Hauptformate, in denen Python-Files geladen & gespeichert werden, sind:
- Das
.csv
-Format. - Das
.pkl
-Format. - etc…
- Es gibt etliche andere Formate, aber die das
.csv
&.pkl
sind diejenigen Formate, auf welche man sehr häufig antrifft
- Es gibt etliche andere Formate, aber die das
Setze den Pfad, an dem dein Computer die Daten / trainierten Modelle speichern soll
# Read data // Load data
import os # this is the library that can handle paths
save_folder = os.path.expanduser(os.path.join("~", # our User's "home"-directory --> for my Mac it is: "/Users/jomaye"
"Dokumente", # Next, we jump 1 directory called "Dokumente" further below
"Programming")) # Allgemeint: each string after a Komma is a new directory you can set. This can go on infinitively ;)
save_folder # check if it worked? --> yes! Now you see your path =)
## '/Users/jomaye/Dokumente/Programming'
Speichern von Daten
Speichern als .csv
-Datei
dynamic_name = "name_of_a_specific_characteristic_of_the_variable_that_you_want_to_save" # this is needed for "dynamisches speichern" via F-String
YOUR_VAR_NAME.to_csv(f"Name-of-the-CSV-{dynamic_name}.csv") # I use f-strings since it allows me to adapt the CSV-filenames to a specific characteristic, that I used for a model / method etc...
Lesen von Daten
Um Datensätze zu laden, benötigt man das Package pandas
. In einigen Fällen können zusätzliche Packages erforderlich sein.
Laden einer Excel
-Datei
epex_df = pd.read_excel("./Data_V2/Preis_aktuell_Spot_EEX_CH-19-ver-mac.xlsx", # plug in the correct path
header=[1], # The dataset Column-names beginnt ab 2. Zeile (--> 1. Zeile ist der Titel des Excel-Files)
sheet_name='Prices', # If you have more than 1 Excel-Sheet within the Excel-File, you need to specify
# which sheet you want to load
engine='openpyxl') # This input will (sometimes) be needed if you load data from an Excel-File via
# a Windows-Computer, otherwise it can print an error!
epex_df # output the ddset a
Laden einer .csv
-Datei
test = pd.read_csv(
"C:/Users/u235051/Downloads/ETS_Database_v38/ETS_Database_v38.csv",
sep='\t' # Im CSV-File waren die Spalten via "Tab"-Taste separiert, deshalb diese option zwingend anzugeben ist (ansonsten Error!)
)
test # check, if it worked?
Laden von Daten via einer Webseiten-URL
Um auf einen als .csv
-Datei gespeicherten Datensatz auf einer Website zugreifen zu können, müssen wir - neben pandas
- ein zusätzliches Package namens requests
verwenden.
Schritt 1:
Zunächst muss die .csv
-Datei auf der Webseite angefagt werden. Dies erreicht man, indem man eine Abfrage an den Remote-Server- // Webseite sendet, auf der die .csv
-Datei gespeichert ist.
import requests # load the library needed
download_url = "https://raw.githubusercontent.com/fivethirtyeight/data/master/nba-elo/nbaallelo.csv" # absolute URL
target_csv_path = "nba_all_elo.csv" # name of the .csv-file that contains the data
response = requests.get(download_url) # using an API that "gets" (= http-protocol language) the data from the server
response.raise_for_status() # Check that the request was successful
with open(target_csv_path, "wb") as f:
f.write(response.content)
print("Download ready.")
Schritt 2:
Nachdem man Zugriff auf die .csv
-Datei bekommt, müssen die Daten nun via pandas
geladen werden.
import pandas as pd # load the library needed
nba = pd.read_csv("nba_all_elo.csv") # load the data --> ddset is called 'nba'
type(nba) # check if it worked? --> should output: <class 'pandas.core.frame.DataFrame'>
Nun kann ein erster Blick auf den DataFrame
geworfen werden:
nba.head()
Laden einer Pickel
-Datei
filename = "Put_Your_Pickle-File_Name_Here" # Alternativ: os.path.join(data_folder_variable_where_pkl_is_saved, filename)
test_loaded_pkl = pickle.load(open(filename, 'rb'))
DataFrame erstellen
df = pd.DataFrame({'LoadCH': ts_loadFc[time_index_hourly],
'LoadD': ts_DE_loadFc.resample('H').mean()[time_index_hourly],
'LoadF': ts_FR_loadFc[time_index_hourly],
'LoadIT': ts_IT_loadFc[time_index_hourly],
'GenCH': ts_genFc[time_index_hourly],
'GenD': ts_DE_genFc[time_index_hourly],
'GenF': ts_FR_genFc[time_index_hourly],
'GenIT': ts_IT_genFc[time_index_hourly],
'RenGenCH': ts_RenGenAllCH[time_index_hourly], # we take the aggregated renewable generation
'RenGenD': ts_RenGenAllDE[time_index_hourly],
'RenGenF': ts_RenGenAllFR[time_index_hourly],
'RenGenIT': ts_RenGenAllIT[time_index_hourly],
'TransFromDach': ts_TransFromDACH[time_index_hourly],
'TransToDach': ts_TransToDACH[time_index_hourly],
'TransToIT': ts_TransToIT[time_index_hourly],
'SeasonAndProduct': ts_season_prod[time_index_hourly], # TO-DO: re-name this column, since its name is confusing!
'tInfoDaySin' : ts_timeInfoDayI_4forecast[time_index_hourly],
'tInfoDayCos' : ts_timeInfoDayII_4forecast[time_index_hourly],
'tInfoYearSin' : ts_timeInfoYearI_4forecast[time_index_hourly],
'tInfoYearCos' : ts_timeInfoYearII_4forecast[time_index_hourly],
'PricesCH': ts_DA_Price[time_index_hourly]})
Umwandlungen
Konvertierung einer Series
zu einem Array
Series.to_numpy() # this also works on a column of a dataframe =)
Konvertierung einer Pandas Series
zu einem DataFrame
import pandas as pd
s = pd.Series(["a", "b", "c"],
name="vals")
s.to_frame()
Selektion & Filtrierung von Zeilen & Spalten
Kontext & Relevanz
Wenn man mitDataFrames
viapandas
arbeitet, wird eine der wichtigsten Anforderungen (beim Umgang mit Daten) sein, wie man bestimmte Spalten aus dem Datensatz selektiert UND wie man Teilmengen vom Datensatz ausgibt.
Für den Anfang - und um sich mit den gängigsten Techniken vertraut zu machen - empfehle ich es, sich dieses Youtube-Video anzusehen: Einsteiger-Tutorial zum Filtern & Auswählen von Spalten.
Führe die untere Notebook-Zelle aus, um die kommenden Beispiele ausführen zu können:
import pandas as pd # load package needed
file_path = './data/filter-and-selection/sample_orders.csv' # type in the path-location of your data
dd = pd.read_csv(file_path) # load the data
dd.head() # check if it worked? --> yes! --> should print the first 5 rows of your ddset
dd.info() # check also all data-types
Selektion 1 Spalte in einem DatenFrame
In diesem Beispiel wähle ich die Spalte order_id
aus meinem Datensatz dd
aus:
print(
dd['order_id'] # take the dataframe 'dd' and print me only the column called 'order_id' --> this is a subset
)
Möglichkeit 2: Selektion einer Spalte via dot-notation
Ein Nachteil der dot-notation
-Methode ist, dass sie nicht funktioniert, WENN die Spalten Whitespaces
enthalten!
test = dd.order_id # ACHTUNG: funktioniert nicht, wenn es Leerschläge gibt!!
print(test)
Selektiere mehrerer Spalten in einem DataFrame
Ähnlich wie bei R
, muss ein “Set” an den Spaltenselektor übergeben werden.
print(
dd[['order_id', 'order_total']] # take the dataframe 'dd' and print me only the columns called 'order_id' && 'order_total'
)
Selektion UND Filter von Zeilen via 1 Bedingung innerhalb eines DataFrames
Angenommen, wir wollen die Zeile mit order_id == 1004
auswählen:
print(
dd[
dd['order_id'] == 1004 # note that this INNER bracket will run a function that searches through and would only print
# a Boolean-List of "True" or "False" for all rows --> example-video: ab 2:36-3:34 --> https://www.youtube.com/watch?v=htyWDxKVttE
] # This outer selector will tell Python: "Select" only the row where the column 'order_id' == 1004
)
# short-version:
print(dd[dd['order_id'] == 1004])
Selektion UND Filter von Zeilen via mehreren Bedingungen innerhalb eines DataFrames
Nun wollen wir die Zeilen auswählen, bei denen "order_total" >= 50.00
UND "order_date" == 12/1/17
erfüllt sind. Im Grunde ist es dasselbe, wie bei der Auswahl & Filtrierung mit einer Bedingung, mit dem Unterschied, dass hier die INNEREN Klammern noch mit einer zusätzlichen (...)
Klammer (für jede Bedingung!) umgeschlossen werden.
print(
dd[
(dd['order_total'] >= 50) & (dd['order_date'] == '12/1/17') # in contrast to **one condition**, we just wrap up a (...) for each condition
] # This outer selector will tell Python: "Select" only the row where the column 'order_id' == 1004
)
Alternative mit
"OR"
: Statt"AND"
zu verwenden, kann - zum Beispiel - auch die"OR"
-Bedingung verwendet werden.
print(dd[(dd['order_total'] >= 50) | (dd['order_date'] == '12/1/17')])
Selektiere nur Zeilen mit Missing-Values
null_data = df[df.isnull().any(axis = 1)] # this will only select the rows that contain at least one missing-value
null_data # check, if it worked?
Selektion mittels “Accessor Operators” iloc
& loc
Das Package pandas
verwendet sogenannte Accessor-Operators
, um DFs zu filtern. Es gibt 2 Arten von Accessor-Operators
:
iloc
(= basierend auf der Postition (= Nummer) des jeweiligen Index der Zeile oder Spalte),- Hinweis: Die Reihenfolge der Inputs, wenn man
iloc
verwendet, sind:- ‘Zeilen’, und dann
- ‘Spalten’.
- Hinweis: Die Reihenfolge der Inputs, wenn man
loc
(= basierend auf die Index-Namen der Zeilen und Spalten desDataFrames
).- Hinweis: TDie Reihenfolge der Inputs, wenn man
loc
, ist:- ‘Zeilen’, und dann
- ‘Spalten’.
- Hinweis: TDie Reihenfolge der Inputs, wenn man
Beispiel: Selektion 1ster Zeile und 5ter Spalte mittels iloc
Um iloc
verwenden zu können, muss man die Position des Zeilen- und Spalten-Index kennen.
- Hinweis:
iloc
&loc
werden oft verwendet, um einen bestimmten Wert in einem Datensatz zu selektieren. Allerdings könnenloc
undiloc
auch ganze Zeilen // Beobachtungen selektieren und nicht nur einen bestimmten Wert innerhalb einer Zeile.
test = dd.iloc[0,4] # Reihenfolge der inputs == 1) 'rows' (--> "0" == 1st row), then 2) 'columns'
# (--> "4" == 5th column)
print(test) # check if it worked? --> should print the value '65'
Beispiel: Selektion 1ster Zeile und 5ter Spalte mittels loc
Um loc
verwenden zu können, muss man die Namen der Spalten und Zeilen kennen.
- Wichtiger Hinweis:
- Der Standard-Name von Zeilen innerhalb eines DFs sind einfach gewöhnliche Zahlen-Abfolgen, also zum Beispiel
0,1,2...,10,11,12,...
. - Und nun kommt das Verwirrendste: der Index einer Zeile sind EBENFALLS Zahlenabfolgen, wie
0,1,2...,10,11,12,...
!- Daher ist es in der Regel so, dass
loc
undiloc
DENSELBEN ersten Input haben, um Zeilen zu selektieren! 🤓
- Daher ist es in der Regel so, dass
- Der Standard-Name von Zeilen innerhalb eines DFs sind einfach gewöhnliche Zahlen-Abfolgen, also zum Beispiel
test = dd.loc[0,'order_total'] # Reihenfolge der inputs == 1) 'rows' (--> "0" == NAME der 1st row),
# then 2) 'columns' (--> "order_total" == NAME der gewünschten 5th column)
print(test) # check if it worked? --> should print the value '65'
Beispiel: Selektion mittels Schwellenwert via loc
Diese Selektionstechnik kann nützlich sein, wenn man einige Werte in bestimmten Spalten ersetzen möchte mit Schwellenwerten (also von einer kontinuierliechen Skala zu einer diskreten Skala wechselt…) → siehe ‘Ausreisser’-Kapitel
# Only display all rows, where the 'pressure'-column is > than the threshold of 1051 bar
df_weather.loc[df_weather.pressure > 1051, 'pressure']
Beispiel: Selektion aller Zeilen via iloc
#####
## Möglichkeit 1: selection of only 1 row
test_row1 = dd.iloc[0,] # Important: 1) rows, then 2) columns --> we want the entire 1st row, which includes ALL columns
# Note: das Komma NACH dem "0" zeigt an, dass wir ALLE columns selektieren wollen!
#####
## Möglichkeit 2: selection of > 1 row --> notice the additional wrap with [...] WITHIN iloc[]!
test_multiRow = dd.iloc[[0,1,2,3,5,8],] # '[0,1,2,3,5,8]' will select the '1st, 2nd, 3rd, 4th, 6th and 9th' row
# while also selecting ALL columns simultaneously
#####
## check if it worked? --> yes!
print(test_row1) # should print only 1 row BUT with ALL the column --> weird output, because the columns sind abgebildet als rows xD
print(test_multiRow)
Beispiel: Selektion aller Zeilen via loc
Tipp: Von allen Optionen, empfehle ich “Möglichkeit 3” in den unteren Beispielen!
#####
## Möglichkeit 1: selection of only 1 row
test_row1 = dd.loc[0,] # das Komma NACH dem "0" zeigt an, dass wir ALLE columns selektieren wollen!
#####
## Möglichkeit 2: selection of > 1 row --> notice the additional wrap [...] WITHIN loc[]!
test_multiRow = dd.loc[[0,1,2,3,5,8],] # Weil - per default - die 'row-labels' (= name des Indexes
# der Zeilen) dieselben sind, wie die Position, ist der Code
# für 'loc' derselbe, wie für 'iloc' hier...
#####
## Möglichkeit 3: Beste & schönste Solution (meiner Meinung nach!)
rows = list(range(0,16)) # will create a list that goes from 0 to 99 --> this will be for the row-labels
columns = ['order_id', 'order_date', 'order_total'] # this will be for the column-labels
# Pro-Tipp: columns = list(data.columns)
df = dd.loc[rows, columns]
#####
## check if it worked? --> yes!
print(test_row1) # should print only 1 row BUT with ALL the column --> weird output, because the columns sind abgebildet als rows xD
print(test_multiRow)
print(df)
Ersetze Werte innerhalb einer Spalte
df.loc[df.ungewichtet > 1, 'ungewichtet'] = 1 # hier werde ich alle Werte der Spalte "ungewichtet" > 1 mit dem Wert "1" ersetzen!
Die Unterschiede im Slicing beim Verwenden von iloc
VS. loc
Merke
Wenn maniloc
verwendet, wird der Bereich 0:5 die Einträge0,...,4
selektieren, d.h. es wird hier EXKLUSIV indiziert. Auf der anderen Seite indiziertloc
hingegen INKLUSIV. Also wird der gleiche Bereich 0:5 die Einträge0,...,5
auswählen!!!
Wenn man also mit loc
und iloc
den GLEICHEN Bereich selektieren möchte, müssen man die range()
-Funktion entsprechend leicht anpassen.
Beispiel:
## Möglichkeit 1: with 'iloc'
iloc_test = dd.iloc[0:5,0] # row-position == 0:5 --> first 5 rows; EXCLUDES '5' from the range "0,1,2,3,4,5"
# --> hence range(0:5) results in --> "0,1,2,3,4"
# column-position == 0 --> 1st row --> remember: indexing in
# Python starts at '0'!
# IMPORTANT: 'iloc' uses the 'Python stdlib' indexing scheme, where the first element of the range is
# included and the last one excluded. So 0:5 will select entries 0,...,4 (= these are the first *5*
# entries!!).
## Möglichkeit 2: to get the SAME output with 'loc', we need a slightly DIFFERENT range!
loc_test = dd.loc[0:4,'order_id'] # row-position == 0:4 --> first 5 rows; INCLUDES '4'
# --> hence range(0:4) results in --> "0,1,2,3,4"
## check if the output are the same, even though "range()" has slightly different inputs? --> yes!
print(iloc_test)
print(loc_test)
Sortierung & Filtrierung
Sortierung der (Pearson-) Korrelation vom höchsten zum tiefsten Wert?
### Find the correlations' ranking for the day-ahead electricity price and the rest of the features:
# Step 1: Create a Pearson-Korrelation Matrix out of your dataframe:
correlations = df.corr(method='pearson') # the variable 'correlations' is a dataframe!
# Step 2: use 'sort_values' to sort the column from "most important" (highest value) to "least important":
print(correlations['pricesCH'].sort_values(ascending=False).to_string())
Beispiel: Selektiere nur die höchste Korrelation und - anschliessend - sortiere die Werte und - schliesslich - verwendet print()
, um diese anzuzeigen
highly_correlated = correlations[correlations > 0.75]
print(highly_correlated[highly_correlated < 1.0].stack().to_string())
Eine simple Daten-Exploration
Warum ist eine Daten-Exploration (vor dem Modellierungs-Prozess) so wichtig?
Nachdem man Python angewiesen hat, den Datensatz zu lesen, ist das Erste, was man tun sollte, sich (sehr) intensiv mit dem Datensatz vertraut zu machen. Das ist Schlüssel, sonst wird man nicht in der Lage sein, eine gute Datenanalyse durchzuführen!
Ein gutes Beispiel, um die Relevanz von Daten-Explorationen zu unterstreichen, wäre meine Masterarbeit: dort habe ich beinahe 3 Wochen damit verbracht, meinen Datensatz zunächst kennenzulernen. Beispielsweise, waren die Spalten im Datensatz mit kryptischen Abkürzungen bezeichnet, was eine Modellierung zunächst verunmöglicht hätte!
Anzahl an Zeilen herausfinden
len(nba) # to get the number of observations // rows
# note: 'nba' is the name of the ddset
Anzahl Zeilen & Spalten herausfinden
nba.shape # to get number of rows AND columns
# note: 'nba' is the name of the ddset
Gesamter Datensatz ansehen
nba # just type in the name of the variable in which your dataframe is stored in --> 'nba' is the name of your dataframe here
Nur die ersten 5 Beobachtungen des Datensatzes ansehen
nba.head() # head() is often used to check whether your dataset really contains data you care about
# here we check: does the ddset really contains data about the NBA?
Hinweis: Wenn man viele Spalten im Datensatz hat, wird Python sie nicht alle anzeigen, wenn man diese mit head()
abbilden möchte Allerdings können in pandas
die Standard-Einstellungen angepasst werden:
pd.set_option("display.max.columns", None) # this will tell Python: "show me ALL the columns!"
nba.head() # execute 'head()' again to check if the setting changed correclty? --> yes!
Nur die letzten 5 Beobachtungen des Datensatzes ansehen
nba.tail() # View last 5 rows
# Viewing very specific rows is also possible:
nba.tail(3) # Here, we view the last 3 rows
Zusammenfassung des Datensatzes
Es ist zu bemerken, dass - für die Abbildung von “Summary-Statistiken” - das zusätzliche Package numpy
benötigt wird.
import numpy as np # load numpy for summary-statistics
nba.describe().round(2) # results will be rounded onto 2 digits
Hinweis: Wenn der Datensatz Spalten vom Typ Object
enthält, benötigen Sie eine etwas andere Version der Funktion describe()
, damit man “Summary Statistics” anzeigen kann.
nba.describe(include=object) # if you have some weird columns being of the type 'object'
Nur unique()
-Werte innerhalb der Spalten anzeigen lassen
Warum benötigt man
unique()
?Unique()
kann nützlich sein, wenn man alle eindeutigen Kategorien innerhalb einer Spalte filtrieren möchte. Man kann diese “unique categories” dann innerhalb einer neuen (Listen-) Variable abspeichern, um - mit Hilfe einesFor Loops
- eine Funktion auf die jeweiligen Kategorien anzuwenden.
gapminder['continent'].unique()
Median & Durchschnitt für eine bestimmte Spalte berechnen
Für den Durchschnitt, wird folgender Code benötigt:
mean_points = reviews.points.mean() # calculate the mean of the column 'points' within the ddset 'reviews'
Für den Median:
median_points = reviews.points.median() # calculate the median of the column 'points' within the ddset 'reviews'
Anwendung einer Custom-Funktion f(x)
, aber nur auf bestimmte Zeilen im Datensatz mittels numpy
import numpy as np # In order to be able to perform a transformation, we will need the `numpy`-package
# set-up:
x = np.arange(10) # this is our column 'x' --> Output of this: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
condlist = [x<3, x>5] # set of conditions, that need to be fullfilled --> which are the value-ranges, on which you will apply
# a custom-function [which will be defined next]? --> all numbers below 3 AND all numbers above 5
choicelist = [x, x**2] # the custom-function you will apply here: x^2
# output:
np.select(condlist, choicelist, default=np.nan) # apply x^2 on: x < 3 AND x > 5
Daten-Cleaning
Finde den Data Type
einer Variablen heraus UND die Anzahl an Missings in einer Spalte pro Spalte
Mit dem folgenden einfachen Code können wir herausfinden, ob die Spalten vom Typ integer
, string
, boolean
etc. sind.
nba.info() # this will output all the types of each column in your dataset & how many NON-missings you have per column
# note: Pay attention to any columns that are from the type 'object'! --> lese bemerkung unten...
Hinweis: Es kann sein, dass Spalten vom Typ object
sind. In der Praxis bedeutet das oft, dass alle Werte in einer object
-Spalte vom Typ string
sind. Wenn man auf object
-Spalten stösst, wird dringend empfohlen, sie in einen geeigneteren Data Type
zu konvertieren, da sonst einige der Funktionen auf object
-Spalten nicht funktionieren…
Finde den Data Type
aller möglichen Objekte in Python:
x = 5
print(type(x))
Lösche 1 ODER mehrere Spalten
Wichtige Syntax bei der Zeilen- & Spalten-Bezeichnung
Beachte, dassaxis = 1
die Spalten bezeichnet, währendaxis = 0
die Zeilen bezeichnet.
df_energy = df_energy.drop(['tInfoDaySin', 'tInfoDayCos', 'tInfoYearSin', 'tInfoYearCos'], axis=1)
Umbenennung von 1 ODER mehrerer Spalten
df2 = df.rename({'oldName1': 'newName1', 'oldName2': 'newName2'}, axis='columns') # not all columns have to be renamed, only those with a new name
Setze ODER lösche den Row-Index mittels .reset_index()
und .set_index()
df_energy = df_energy.set_index('Date-Time') # to set the index
df_energy = df_energy.reset_index() # to reset the index --> df.reset_index(drop= True) will drop the index, which would
# otherwise become a new column instead of just dropping it!
Erstelle eine neue Spalte mit unique IDs
- For a single Column:
df['id'] = df.groupby(['date']).ngroup() # of Course, you could simply use 1 column fo the assignment of an unique ID.
- For multiple Columns:
df['id'] = df.groupby(['LastName','FirstName']).ngroup() # here, we use the column 'LastName' & 'FirstName' together, to create
# a unique ID.
# Quelle: https://stackoverflow.com/questions/45685254/q-pandas-how-to-efficiently-assign-unique-id-to-individuals-with-multiple-ent
Wende eine function
auf eine Spalte im DataFrame
an
Dies kann nützlich sein, wenn man eine Spalte eines DataFrames
umwandeln muss:
# Step 1: Define any function, that you will need to be applied, in order to transform
def my_function():
... # hier kommen die verschiedenen Anweisungen
... # hier kommen die verschiedenen Anweisungen
return some_variable
# Step 2: Apply this newly specified function on your column of your data-frame, that you wish to transform
df['new_column'] = df['column_1'].apply(my_function)
Umgang mit Missings
Zähle die totale Anzahl an Missings innerhalb des DataFrames
print('There are {} missing values or NaNs in df_energy.'
.format(df_energy.isnull().values.sum()))
Zähle die Missings pro Spalte
df_energy.isnull().sum(axis=0) # outputs the number of NaNs for each column
Zähle alle Non-Missings, aber NUR, falls die Spalten keine 0
-Werte enthalten
Achtung: der “Code-Trick” unten funktioniert nur, wenn die Spalten keine Werte enthalten, die ‘0’ sind! Das liegt daran, dass die Zahl ‘0’ - als boolean
- als False
ausgegeben wird, und wir daher die FALSCHE Anzahl fehlender Werte erhalten würden!
Daher sollte man den folgenden Code nur verwenden, WENN man sichergestellt hat, dass meine (zu analysierenden) Spalten KEINE “0”-Werte enthalten!
# Display the number of non-missing values in each column
print('Non-zero values in each column:\n', df_energy.astype(bool).sum(axis=0), sep='\n')
Da die obige Notebook-Zelle nur die korrekte Anzahl an Missings ausgibt, WENN die Spalte keine “0”-Werte enthält, habe ich einen Code aufgelistet, der zählt, wie viele “0”-Werte in jeder Spalte enthalten sind:
(df_energy == 0).astype(int).sum(axis=0) # count the numbers of '0s' in each column [axis = 0, for columns...]
Zeige nur diejenigen Spalten, die Missing enthalten
# Display the rows with null // missing values:
df_energy[df_energy.isnull().any(axis=1)].tail()
Ersetze alle Missings mit 0
-Werten
df['ColumnWithMissings'] = df_tot['ColumnWithMissings'].fillna(0) # replaces all missing-values within the column with 0s.
Ersetze alle Non-Missings in einer Spalte mit Missings
Nehmen wir an, wir verüfgen über eine Zeitreihe, die einen Zeilen-Index mit Zeit-Daten entählt!
### Step 1: define the range in which you want to replace values
start = '2020-01-01' # 01. Januar 2020 --> ab hier wollen wir die values der Time-Series mit Missings ersetzen
stop = '2020-08-01' # 01. August 2020 --> bis zu diesem Datum sollen die Missings eingefügt werden
### Step 2: replace the values with missings via the ".loc[row-indexer, column-indexer]"
df.loc[start:stop, 'y_hat'] = None # This will replace all the values within the 'y_hat'-column - in the range from
# 01.01.2020-01.08.2020 with Missing-values (instead of "normal"-values)
Missing-Imputationen
Missing Interpolation für Zeitreihen
# Fill null values using interpolation:
df_energy.interpolate(method='linear', limit_direction='forward', inplace=True, axis=0) # since we have
Duplikate in den Daten
Zähle alle Duplikate im DataFrame
temp_energy = df_energy.duplicated(keep='first').sum()
print('There are {} duplicate rows in df_energy based on all columns.'
.format(temp_energy))
Lösche Duplikate
# Variante 1: mit reset_index & neuer set_index
df_weather_2 = df_weather.reset_index().drop_duplicates(
subset=['time', 'city_name'], # Drop the duplicate, if all the rows are the same // have the same
# values (Achtung: we only look at the duplicates in the 'time' &
# 'city_name'-column from this analysis!).
keep='last').set_index('time') # if you have duplicate, keep only the last of the duplicated-rows.
# Variante 2: man dropt den "alten" Index (VOR merge) und neuem set_index
df_unique_dates = df_ferien.drop_duplicates(
subset='datum', # only consider the column "datum" [= column that has duplicates] when dropping the duplicates
keep='first').reset_index(drop=True) # reset the index, otherwise you get weird indizes (mit 10'000 für manche)
# 'drop = True' means that we do not keep the "old" index as a separate
# 'column'
df_unique_dates
Ausreisser
Zeichne einen Boxplot
import seaborn as sns
sns.boxplot(x=df_weather['pressure'])
plt.show()
- Schlüssel-Frage:
Gibt es Ausreisser? → Ja / Nein
Der “Trick”, um die obige Frage zu beantworten, lautet:
Mache eine “Summary Statistics” mit deinen Daten und - falls du es z.B. mit Temperatur-Daten zu tun hast - dann solltest du mal googlen: “was sind die Höchst-Temperaturen auf der Erde?”, und anschliessend auf Wikipedia gehen, um zu beurteilen, ob die Daten-Werte in der Spalte “Temperatur” auch tatsächlich realistisch sind ODER ob diese Ausreisser enthält?
- Beispiel:
Selbst ein Druck von etwa 100'000 hPa oder 10 MPa, der in der obigen Abbildung deutlich sichtbar ist, entspricht einem Wert, der größer ist, als der atmosphärische Druck der Venus. Entsprechend können wir nun jeden Wert in der Spalte “Druck” als “NaN”, der höher als 1051 hPa ist, was knapp über dem höchsten Luftdruck-Wert liegt, der jemals auf der Iberischen Halbinsel gemessenen wurde. Während Ausreißer auf der niedrigen Seite im obigen Boxplot nicht sichtbar sind, ist es eine gute Idee, auch dennoch diejenigen Werte zu ersetzen, die kleiner als 931 hPa sind, was dem niedrigsten - jemals auf der iberischen Halbinsel - aufgezeichneten Luftdruck-Wert entspricht.
Schritt 2:
Wenn die Antwort auf die obige Frage “ja” lautet, dann ersetzt man diejenigen Werte, welche über dem festgelegten Ausreisser-Wert liegen, aufNaN
s, damit diese in der späteren Modellierung & bei der Analyse nicht berücksichtigt werden (denn diese würden die Ergebnisse verfälschen).
# Replace outliers in the `Set_Name_of_Column_with_the_Outlier_hier`-column with `NaN`s
df_weather.loc[df_weather.pressure > 1051, 'pressure'] = np.nan
df_weather.loc[df_weather.pressure < 931, 'pressure'] = np.nan
Das Merging & Splitting von Datensätzen
Wichtiger Hinweis zu Merging:
Wenn man 2DataFrames
mergen (= zusammenführen) möchte, von denen Einer kleiner ist, als der Andere, dann wird es NICHT möglich sein, den größeren Datensatz mitmerge()
kleiner zu machen! Ich habe eine Menge Zeit damit verbracht, bisher aber leider ohne Erfolg. Es gibt allerdings dennoch eine Alternative-Lösung zu diesem Problem: man muss nur die Duplikate des größerenDataFrames
löschen (siehe KapitelDuplikate
, um den Datensatz dennoch kleiner zu machen =)
Aufsplittung eines grossen Datensatzes in einen kleineren Datensatz via Kategorien
# Split the df_weather into 5 dataframes (one for each of the 5 cities):
df_1, df_2, df_3, df_4, df_5 = [x for _, x in df_weather.groupby('city_name')]
dfs = [df_1, df_2, df_3, df_4, df_5]
Merging von 2 Datensätzen via ihrem Row-Index
Relevanz?:
Man kann diese Art von Merging nur verwenden, WENN beide DFs den gleichen Zeilen-Index haben! Mit dieser Art von Merging, lassen sich neue Spalten aus einem anderen DF hinzufügen!
# Let's merge all the y- & X-Variables from the training-set together:
test = pd.merge(
y_train, # der Trainingsdatensatz für die y-Variable
x_train, # der Trainingsdatensatz für alle X-Variablen
how="outer",
left_on=y_train.index, # merging via index // row-label des DataFrames der y-Variable
right_on=x_train.index, # merging via index // row-label des DataFrames der x-Variablen
).set_index('key_0') # optional: da wir hier eine Zeitreihe haben, dessen Row-Index den Column-Name 'key_0' animmt
# beim Merging, wird die Spalte 'key_0' hier als neuer Row-Index für den gemerged Dataframe
# gesetzt.
test # check if it worked
Gleichzeitiges Merging von mehreren Datensätzen
Ausgangssituation:
Nehmen wir an, dass wir - zunächst - 2 Datensätze haben, einen für das Wetter und einen für die Energiepreise. Außerdem handelt es sich bei diesen beiden Datensätzen um Zeitreihen. Daher haben sie den gleichen Zeitindex (= Zeilenbezeichnung), formatiert in UTC.
- Schritt 1: Aufsplittung des Wetter-Datensatzes, sortiert nach Städten…
# Split the df_weather into 5 dataframes (one for each of the 5 cities):
df_1, df_2, df_3, df_4, df_5 = [x for _, x in df_weather.groupby('city_name')]
dfs = [df_1, df_2, df_3, df_4, df_5]
- Schritt 2: Merging der 5 Teildatensätze mit dem Energie-Preis-Datensatz…
# Step 1: save a copy, in case you do a wrong merging!
df_final = df_energy
# Step 2: make a for-loop, to merge all the 6 datasets simultaneously
for df in dfs: # here, we loop through every city-group of our list of data frames (see step 1)
city = df['city_name'].unique() # we store the names of the 5 cities - as a list - in a variable
city_str = str(city).replace("'", "").replace('[', '').replace(']', '').replace(' ', '') # we perform some
# string-manipulation to eliminate all the characters that are not necessary
df = df.add_suffix('_{}'.format(city_str)) # we re-name the columns, by adding the name of the city to each column.
df_final = df_final.merge(df, # this is the merging-part!
on=['time'], # we want to merge via the index // row-label of both datasets --> since
# they are both in UTC-time, this will work!
how='outer') # 'outer' means: we want the 'union' --> see this youtube-video for a
# good explanation: https://www.youtube.com/watch?v=h4hOPGo4UVU
df_final = df_final.drop('city_name_{}'.format(city_str), axis=1) # let's drop some columns that we don't need anymore
# Step 3: "final results"-check
df_final.columns # show the merging-results, by displaying all the column-names --> DONE! =)
Schritt 3: Führe abschliessende Checks durch
- Schlüssel-Frage:
Hat das Merging nun tatsächlich geklappt? → Ja / Nein ? - "Trick", um diese Frage zu beantworten: Schaue auf die Missings & Duplikate.
# Display the number of NaNs and duplicates in the final dataframe
print('There are {} missing values or NaNs in df_final.'
.format(df_final.isnull().values.sum()))
temp_final = df_final.duplicated(keep='first').sum()
print('\nThere are {} duplicate rows in df_energy based on all columns.'
.format(temp_final))
Abschliessende Beurteilung zur Frage: “hat das Merging nun tatsächlich geklappt”?
- Wenn man keine Missings UND keine Duplikate hat, dann sollte das Merging funktioniert haben und die Antwort auf die obige Frage sollte “ja” lauten.
- Wenn die Antwort auf die obige Frage “Nein” lautet, muss man zu Schritt 2 zurückkehren und versuchen herauszufinden, was beim Merging der Datensätze falsch gemacht wurde.
Skalierung von Variablen
Bei der Anwendung von maschinellem Lernen im Bereich des Deep Learnings, lautet eine Voraussetzung, um solche Modelle nutzen zu können, dass wir unsere Variablen zunächst skalieren.
Die üblichen Methoden zur Skalierung von Variablen können auf diesem Stack-Exchange Post gefunden werden.
Normalisierung
Wenn man eine Variable “normalisiert”, dann bedeutet das, dass der Bereich der möglichen Werte zwischen 0 und 1 liegt (= wird auch “Definitionsbereich” in Mathe genannt).
Skaliere alle X-Variablen und die Y-Variable
from sklearn.preprocessing import MinMaxScaler # Der Name der Klasse für Normalisierung = 'MinMaxScaler' (--> mega weird xDD)
# step 1: initialize the Class
scaler_X = MinMaxScaler(feature_range=(0, 1))
scaler_y = MinMaxScaler(feature_range=(0, 1))
# step 2: wende den Scaler uf die X- & Y-Variablen (dabei nur auf das Training-Set) an (Achtung: funktioniert nur auf numy-arrays,
# aber nicht auf Series // Data-Frame Columns!)
scaler_X.fit(X_compl_df[:train_end_idx]) # alternativ: scaler_y.fit(X_train.to_numpy().reshape(-1, 1))
scaler_y.fit(y_compl_df[:train_end_idx]) # alternativ: scaler_y.fit(y_train.to_numpy().reshape(-1, 1))
# step 3: nach dem "fit" haben wir die Werte noch nicht als "DataFrame"-Typ gespeichert, weshalb wir nun noch 'transform' anwenden
X_norm = scaler_X.transform(X_compl_df)
y_norm = scaler_y.transform(y_compl_df)
Fortgeschrittene Daten-Exploration
Erstelle eine Korrelations-Matrix via einem DataFrame
# Step 1: construct a 'Pearson Correlation Matrix' as a Dataframe:
correlations = df.corr(method='pearson')
# Step 2: Load Libraries & plot Pearson correlation matrix:
import matplotlib.pyplot as plt
import seaborn as sns
fig = plt.figure(figsize=(24, 24)) #
sns.heatmap(correlations, annot=True, fmt='.2f') # import seaborn as sns
plt.title('Pearson Correlation Matrix')
plt.show()
Exploration von Zeitreihen-Daten
Indexierung
Wenn man es mit irgendwelchen “Zeit”-Variablen zu tun hat, muss man bei der Datenbereinigung in Python häufig auf sogenannte Date-Time Objects
zurückgreifen.
Erstelle einen Zeitreihen-Index
start = '2020-01-01'
stop = '2021-01-01' # ACHTUNG: wird am 31.12.2020 enden
ts_index = pd.date_range(start, stop, freq = 'h', closed = 'left', tz = 'UTC')
ts_index
(Optionale) Erweiterung: Index-Slicing
Der Nachteil am obigen Index ist, dass er immer erst ab 0:00 Uhr beginnt. Das Problem dabei ist, dass man nicht immer einen Index benötigen wird, welcher punktgenau um 0:00 Uhr beginnt, sondern vielleicht einmal um 23:00 Uhr.
Die Lösung
Verwende die slicing-Technik, um ab der ersten Beobachtung vom Jahr 2020, um 00:00 zu starten, beispielsweise so für einenDataFrame
:DF2 = DF1.loc[:'2019-05-26 13:00:00+00:00']
. Im Falle einerseries
, würde das Slicing besipielsweise so aussehen:ts_index[4:]
.
Kopiere einen Index als separate Spalte
p_train = p_train.reset_index()
p_train['date'] = p_train['Date-Time']
p_train.set_index('Date-Time')
Wandle eine “Pseudo”-Zeitreihen-Spalte in eine Date-Time
-Spalte um und setze diese dann als Index vom Datensatz
Wenn man mit Zeitreihen-Daten arbeitet, wird es oft vorkommen, dass man die Spalte “Datum” - welche oftmals vom Typ string
ist - in eine Spalte vom Typ `Date-Time" umwandeln muss!
df_energy['Date-Time'] = pd.to_datetime(df_energy['Date-Time'], utc=True, infer_datetime_format=True)
df_energy = df_energy.set_index('Date-Time') # set the 'Date-Time'-Column as the index // row-label of our data-frame
Falls man das Format selbst setzen möchte:
#convert Delivery day to a date time column
epex_df['Delivery day'] = pd.to_datetime(epex_df['Delivery day'], format = '%Y%m%d')
Konvertiere eine Date-Time
-Spalte in einen string
df['Date-Time'] = df['Date-Time'].apply(lambda x: x.strftime('%Y-%m-%d %H:%M:%S%z'))
Wieso würde man dies tun?
Manchmal werden wir auf das Problem antreffen, dass wir verschiedene Zeitreihen zusammenführen wollen, die aber unterschiedliche Date-Time
-Formate haben!
Ein Beispiel von 2 Datensätzen mit unterschiedlichen
Date-Time
-Formaten:
Beispielsweise wäre die Spalte “Datum” imDataFrame_1
im Format “26-10-2022”, während die Spalte “Datum” imDataFrame_2
in einem unterschiedlichen Format, z.B. “2022-10-26”.
Daher müssen wir, BEVOR wir das universelle UTC
-Datum-Zeit-Format anwenden, jeden einzelnen (separaten) DataFrame
in das GLEICHE Datum-Zeit-Format
bringen!
Da Python wirklich gut darin ist, eine solche Art von Transformationen im String
-Format zu handhaben, können wir dies zu unserem Vorteil nutzen und:
1) Zunächst die einzelnen "Datum"-Spalten - <u>während alle noch im `string`-Format sind</u> - in ein Format bringen, welches **uniform** ist, über alle (separaten) `DataFrames` hinweg.
2) Anschliessend können die (separaten) `DataFrames` ins `UTC`-Format umgewandelt werden.
3) Dann steht einem Merging nichts mehr im Wege! =)
Um zu sehen, wie man Strings
manipuliert, empfehle ich stark, das Kapitel String-Manipulation in diesem Post zu lesen.
Erstelle eine range()
von Datums
Beginn & Ende einer “Datums”-Periode festlegen, wobei die Standardfrequenz hier “täglich” ist.
import pandas as pd
pd.date_range(start='1/1/2018', end='1/08/2018')
- Alternativ könnten wir es auch so machen:
# Step 1: We need a starting & endpoint for the range of dates
the_start_date = '2019-01-01'
the_end_date = str(datetime.today().date() + timedelta(days = 2)) # this is simply "today" + 2 days into the future
# Step 2: Let's create the range of dates based on the starting- & end-point
date_range_of_interest = pd.date_range(the_start_date, # starting date-point (= is fixed)
the_end_date, # end date-point (= can change dynamically // updates with new data from entsoe)
freq = 'h', # we want to have 'hours'
closed = 'left', # The input 'closed' let's you choose whether you want to include the start and end dates.
# --> 'left' will exclude the end-date, which is good, since this date is in the "future" (= today + 2)!
tz = 'Europe/Berlin') # this is the time-zone
Mit 15min Zeitabständen
date_range_15Min = pd.date_range(the_start_date,
the_end_date,
freq = '15Min', # only thing that changes --> will be used for Germany, since DE has quarterly-houred observations
closed = 'left',
tz = 'Europe/Berlin')
Plotten einer Zeitreihe
Schritt 1:
Umwandlung der “Datum”-Spalte (imstring
-Format) in einedate-time
-Spalte.
from datetime import datetime as dt # this is the package we need to convert a column into a 'date-time'
# Step 1: Define a function that is reading the date-column correctly
def parse_date(date):
data=str(date)
if date=="": # this condition will never be true, since the column 'Date-Time' NEVER has an empty string;
### raise exception
return None
else:
return pd.to_datetime(date, format='%Y-%m-%d %H:%M:%S', yearfirst = True, utc = True)
# Step 2: apply the above function on the Column 'Date-Time' to transform the column into a 'date-time'-type
raw_dd["Date-Time"] = raw_dd["Date-Time"].apply(parse_date)
Schritt 2:
Setze nun dieData Time
-Spalte als Index des Datensatzes fest, ansonsten wird das Plotten nicht funktionieren.
# set the date-column as our row-label:
raw_dd = raw_dd.set_index("Date-Time")
Schritt 3:
Visualisierung der Zeitreihe
from sktime.utils.plotting import plot_series # use sktime and the 'plot_series'-module for this task
# Define our y-variable (= DA-Prices in CH):
y = raw_dd["pricesCH"]
plot_series(y) # visual check if it worked? --> yes!
Bonus:
Ein alternativer Ansatz, um die Zeitreiehe zu visualisieren, könnte man die folgende Funktion verwenden:
# Define a function to plot different types of time-series:
def plot_series(df=None, column=None, series=pd.Series([]), # the 'series'-Input will be useful, if we choose to plot the series in a monthly- or weekly-frequency, instead of hourly...
label=None, ylabel=None, title=None, start=0, end=None):
"""
Plots a certain time-series which has either been loaded in a dataframe
and which constitutes one of its columns or it a custom pandas series
created by the user. The user can define either the 'df' and the 'column'
or the 'series' and additionally, can also define the 'label', the
'ylabel', the 'title', the 'start' and the 'end' of the plot.
"""
sns.set()
fig, ax = plt.subplots(figsize=(30, 12))
ax.set_xlabel('Time', fontsize=16)
if column:
ax.plot(df[column][start:end], label=label)
ax.set_ylabel(ylabel, fontsize=16)
if series.any():
ax.plot(series, label=label)
ax.set_ylabel(ylabel, fontsize=16)
if label:
ax.legend(fontsize=16)
if title:
ax.set_title(title, fontsize=24)
ax.grid(True)
return ax
Auto-Korrelation & Partielle-Autokorrelation
Die Visualisierung der partiellen Autokorrelation der Strompreis-Zeitreihen zeigt, dass die direkte Beziehung zwischen einer Beobachtung zu einer bestimmten Stunde (t) am stärksten mit den Beobachtungen zu den Zeitschritten t-1, t-2, t-24 und t-25 ist und danach abnimmt.
- Schlussfolgerung aus der Visualisierung: Daher werden wir die 25 vorangegangenen Werte jeder Zeitreihe bei der Modellierung der Preisentwicklung verwenden.
# Step 1: load library
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf # to plot
# Step 2: Plot autocorrelation and partial autocorrelation plots
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(10, 6))
plot_acf(df_energy['pricesCH'], lags=50, ax=ax1)
plot_pacf(df_energy['pricesCH'], lags=50, ax=ax2)
plt.tight_layout()
plt.show()
Cross-Korrelation (Yt, Xt - k)
Es wäre definitiv vorteilhafter, wenn wir nur bestimmte vergangene Werte / Lags (Beobachtungen zu bestimmten vergangenen Zeitpunkten) einer bestimmten Variablen verwenden würden, basierend auf der Cross-Korrelation zwischen dem Strompreis und jedem anderen Variablen im Datensatz. Auf der unteren Graphik sehen wir - zum Beispiel - die Cross-Korrelation zwischen dem Strompreis und der erneuerbaren Energie-Angebot in der Schweiz. Wir sehen, dass es viele Zeitverschiebungen gibt, deren Korrelation nahe bei Null liegt und die weggelassen werden könnten.
from statsmodels.tsa.stattools import ccf # to plot the cross-correlation function
# Step 2: plot the cross-correlation between the day-ahead price "today" and with each of the first 50-lags of the column 'RenGen'
cross_corr = ccf(df_energy['RenGenCH'], df_energy['pricesCH'])
plt.plot(cross_corr[0:50])
plt.show()
Feature Engineering
Rechnen (Addition & Subtraktion) mit Date-Time
-Objekten
Wieso ist das Rechnen mit
Date-Time
-Objekten wichtig?
Bei Zeitreihen-Analysen wird es oftmals der Fall sein, dass man Zeitpunkte in der Zukunft oder in der Vergangenheit definiert. Daher ist es wichtig, dass man lernt, wie man Berechnungen im Zusammenhang mitDate-Time
-Objekten durchführt, insbesondere auch dank dem Packagedatetime
.
Genauer Zeitpunkt “jetzt” / “heute”
import datetime # Da wir mit "Zeit" arbeiten, brauche ich das Package "datetime"
jetzt = datetime.datetime.now() # current time
print(jetzt) # check: should output the current time
Addition, um zukünftigen Zeitpunkt zu berechnen, relativ zu “jetzt”
import datetime # Da wir mit "Zeit" arbeiten, brauche ich das Package "datetime"
jetzt = datetime.datetime.now() # current time
added_time = datetime.timedelta(hours= 1, minutes= 1) # anzahl an Stunden & Minuten, die du dazu addieren willst
ende = jetzt + added_time
print(ende) # check: should output "current time + 5h 2 min"
Subtraktion, um vergangenen Zeitpunkt zu berechnen, relativ zu “jetzt”
import datetime # Da wir mit "Zeit" arbeiten, brauche ich das Package "datetime"
fixe_zeit = datetime.datetime(2021, 7, 1, 8, 12) # datetime(year, month, day, hour, minute, second, microsecond),
# wobei die ersten 3 Argumente (Jahr, Monat, Tag) OBLIGATORISCH sind! --> hier interessiert uns die letzten 2 Inputs,
# nämlich die "8" und die "12" --> diese wiederspiegeln meine fixe 8h 12 min Arbeitszeit bei der SBB
added_time = datetime.timedelta(hours= 1, minutes= 1) # anzahl an Stunden, die du noch arbeiten musst
wie_lange_noch = fixe_arbeitszeit - added_time
print(wie_lange_noch.strftime("%H%M")) # check: should output 7h 11min --> yes!
Herausfinden, an welchem Wochentag (Mo / Di / Mi etc…) ein bestimmtes Datum war
Wenn man z.B. wissen will, welcher “Wochentag” z.B. der 21.03.2020
war, kann man einfach folgenden Code verwenden:
import datetime
fixe_zeit = datetime.datetime(2020, 12, 12) # datetime(year, month, day, hour, minute, second, microsecond)
fixe_zeit.weekday()
# --- Gemäss Documentation
# 0 = Monday
# 1 = Tuesday
# 2 = Wednesday
# 3 = Thursday
# 4 = Friday
# 5 = Saturday
# 6 = Sunday
addierte_anzahl_tage = datetime.timedelta(days = 28)
next_day = fixe_zeit + addierte_anzahl_tage
next_next_day = next_day + addierte_anzahl_tage
next_next_next_day = next_next_day + addierte_anzahl_tage
next_next_next_next_day = next_next_next_day + addierte_anzahl_tage
next_next_next_next_day
Wir sehen, dass der “21.03.2020” ein Samstag war. Ich habe es mit dem Kalender überprüft: das ist korrekt =)
Trends
Wöchentlicher gleitender Durchschnitt
### Step 1): Berechne - aus deiner stündlichen(!!) Zeitreihe - einen 1 wöchigen rolling mean:
rolling = p_train['pricesCH'].rolling(24*7, center=True).mean() # this is the rolling (weekly) mean
### Step 2): Define a function to plot different data-types (for time-series):
def plot_series(df=None, column=None, series=pd.Series([]), # the 'series'-Input will be useful, if we choose to
# plot the series in a monthly- or weekly-frequency,
# instead of hourly...
label=None, ylabel=None, title=None, start=0, end=None):
"""
Plots a certain time-series which has either been loaded in a dataframe
and which constitutes one of its columns or it a custom pandas series
created by the user. The user can define either the 'df' and the 'column'
OR the 'series' and additionally, can also define the 'label', the
'ylabel', the 'title', the 'start' and the 'end' of the plot.
"""
sns.set()
fig, ax = plt.subplots(figsize=(30, 12))
ax.set_xlabel('Time', fontsize=16)
if column:
ax.plot(df[column][start:end], label=label)
ax.set_ylabel(ylabel, fontsize=16)
if series.any():
ax.plot(series, label=label)
ax.set_ylabel(ylabel, fontsize=16)
if label:
ax.legend(fontsize=16)
if title:
ax.set_title(title, fontsize=24)
ax.grid(True)
return ax
### Step 3): Plot the rolling mean
ax = plot_series(p_train, 'pricesCH', label='Hourly', ylabel='Actual DA-Price (€/MWh)',
title='Day-Ahead Hourly Electricity Price and Weekly Rolling Mean')
ax.plot(rolling, linestyle='-', linewidth=2, label='Weekly rolling mean') # add the rolling mean to the hourly time-series
plt.show()
Saisonalität
Wöchentliche Saisonalität
### Step 1: Define a function to plot different data-types (for time-series):
def plot_series(df=None, column=None, series=pd.Series([]), # the 'series'-Input will be useful, if we choose to
# plot the series in a monthly- or weekly-frequency,
# instead of hourly...
label=None, ylabel=None, title=None, start=0, end=None):
"""
Plots a certain time-series which has either been loaded in a dataframe
and which constitutes one of its columns or it a custom pandas series
created by the user. The user can define either the 'df' and the 'column'
OR the 'series' and additionally, can also define the 'label', the
'ylabel', the 'title', the 'start' and the 'end' of the plot.
"""
sns.set()
fig, ax = plt.subplots(figsize=(30, 12))
ax.set_xlabel('Time', fontsize=16)
if column:
ax.plot(df[column][start:end], label=label)
ax.set_ylabel(ylabel, fontsize=16)
if series.any():
ax.plot(series, label=label)
ax.set_ylabel(ylabel, fontsize=16)
if label:
ax.legend(fontsize=16)
if title:
ax.set_title(title, fontsize=24)
ax.grid(True)
return ax
### Step 2: Plot the (hourly) Time Series over 2 weeks:
ax = plot_series(p_train, 'pricesCH', label='Hourly', ylabel='Actual Price (€/MWh)',
start= 1, end=1 + 24 * 14, # zoom in into 2 weeks, 750-765
title='Hourly Electricity Price (Zoomed - 2 Weeks)')
plt.show()
Volatilität
### Step 1): calculate the volatility (= 'change'-variable)
change = (p_train['pricesCH'].div(p_train['pricesCH'].shift(1)) - 1)*100 # Divide the 'pricesCH'-colum with the
# lagged version of itself, substract 1, then multiply by 100, to get the growth-rate in %. Store it into a
# new variable called 'change'.
### Step 2): Plot the percentage of the hourly change in the actual electricity price
ax = plot_series(series=change, ylabel='Hourly Change (%)',
title='Percentage of the hourly change in the day-ahead electricty price')
plt.show()
Tägliche Volatilität
Here, we “Re-Sample” our hourly Volatility into daily volatility:
# Plot the electricity price (monthly frequence) along with its 1-year lagged series
# Step 1: Aggregate the data
daily_volatility = p_train['change'].asfreq('d') # here, we aggregated the hourly DA-price over days -> save it as a series
# Step 2: Define a function to plot different data-types (for time-series):
def plot_series(df=None, column=None, series=pd.Series([]), # the 'series'-Input will be useful, if we choose to plot the series in a monthly- or weekly-frequency, instead of hourly...
label=None, ylabel=None, title=None, start=0, end=None):
"""
Plots a certain time-series which has either been loaded in a dataframe
and which constitutes one of its columns or it a custom pandas series
created by the user. The user can define either the 'df' and the 'column'
OR the 'series' and additionally, can also define the 'label', the
'ylabel', the 'title', the 'start' and the 'end' of the plot.
"""
sns.set()
fig, ax = plt.subplots(figsize=(30, 12))
ax.set_xlabel('Time', fontsize=16)
if column:
ax.plot(df[column][start:end], label=label)
ax.set_ylabel(ylabel, fontsize=16)
if series.any():
ax.plot(series, label=label)
ax.set_ylabel(ylabel, fontsize=16)
if label:
ax.legend(fontsize=16)
if title:
ax.set_title(title, fontsize=24)
ax.grid(True)
return ax
# Step 3: Add the aggregated data to the plot
ax = plot_series(series=daily_volatility, ylabel='Weekly Volatility',
title='DAILY Volatility on the Day-Ahead Electricity Price')
# Step 4: final plot is ready
plt.show()
Dummy Variablen
Erstelle einen “normalen” Dummy
In Python benötigen man die where
-Methode, um einen Dummy zu erstellen.
where
in Python ist äquivalent zur ifelse()
-Funktion in R
:
import numpy as np
np.where(maupayment['log_month'] == maupayment['install_month'], 'install', 'existing')
“Flagg” einen Dummy nach einem Merging
Dies kann nützlich sein, wenn man nachverfolgen muss, welche der Variablen Teil des “linken” und welche Teil des “rechten” Datensatzes war (beim Merging). Dabei wird buchstäblich eine neue Spalte erstellt, welche _merge
-Spalte heisst und eine Dummy-Variable darstellt.
Beispiel 1:
Erstelle einen Dummy für die “Ferien”-Spalte.
df2 = df.merge(df_year_2019,
on='date', # in this case, we merge over the "date"-column; note that 'df' has 365 rows, while 'df_year_2019' only 270 rows
how='outer', # we need to set
indicator=True) # the flag is "indicator"
df2 # we get a ddset with 360 rows. On the outside, the 'testo'-ddset seems NOT to be different form the 'df',
# however, the purpose of the merge was to create a new dummy-variable to see from which dataset it came from.
# step 2: we want some more meaningful values within the 'merge'-column --> apply an if-statement onto the (string-) column
df2.loc[df2['_merge'] == 'both', 'ferien'] = 1 # Here, we create a column 'Ferien', which will be equal to "1", if
# the value in the '_merge'-column is equal to the string "both"
df2.loc[df2['_merge'] != 'both', 'ferien'] = 'False' # Ferien-Dummy equals zero otherwise
Erstelle Dummy-Variablen für Zeitreiehen-Daten
Im Folgenden werde ich - anhand einer “Datums”-Spalte im Date-Time
-Format - eine Dummy-Variable erstellen (in einer neuen Spalten), um z.B. Stunden, Viertel usw. in einer separaten Spalte als Dummy abzubilden.
Dummy für Stunden
raw_dd['Hour'] = raw_dd['Date-Time'].apply(lambda x: x.hour) # Werte von 0 [= Stunde 0:00-1:00] bis 23 [= Stunde 23:00-24:00]
Dummy für Quartale
raw_dd['Quarter'] = raw_dd['Date-Time'].apply(lambda x: x.quarter) # Werte von 1 [Januar bis März] bis 4 [Oktober bis Dezember]
Dummy für Tage
raw_dd['day_of_week'] = raw_dd['Date-Time'].apply(lambda x: x.weekday()) # Werte von 0 [= Montag] bis 6 [= Sonntag]
Anwendung einer “If”-Bedingung auf einen DataFrame
- Hier ist eine gute Quelle zum Thema: https://datatofish.com/if-condition-in-pandas-dataframe/
Daten-Visualisierung
Das Plotten & Abspeichern von Bildern
# Step 1: import the library matplotlib for visualization
import matplotlib.pyplot as plt # for the settings of the graph, such as title, windows-size etc...
# Step 2: make the plot
plt.figure(figsize=(14,6))
fig, ax = plot_series(y_train[10290:10320], # only take the last 30 observations from the trainings-set
y_test,
y_pred,
labels=["y_train", "y_test", "y_pred"])
plt.xticks(rotation=90) # rotates Beschriftung der X-Achse um 90-Grad, damit man überhaupt die X-Achse lesen kann
# Step 3: save the plot
fig.savefig("monochrome-2-test.png") # save the graph
Erstelle mehrere Plots mittels For-Loops
# step 1: create a list of all the columns, you want to iterate through
cov_list = X_train.columns # I select all columns in my dataframe 'X_train'
# step 2:
for i in cov_list:
print(i) # just as a check, to see how the loop progresses
covariate_name = i # everytime I iterate over a new variable, I store it into this variable --> I will use this variable later to save each
covariate = X_train[i] # I need each variable to be of type "Series" when creating a plot
x = list(range(0, 720, 24)) # this will put a tick-mark after each day
x.append(719) # I also add '719', since the range()-function does not include this number
fig, ax = plot_series(covariate[-720:]) # this plots the last month
plt.xticks(x); # to only display some dates
plt.xticks(rotation=90); # we need to rotate the x-axis, otherwise you cannot read it
fig.savefig("visualisation_30_days-{}.png".format(covariate_name)) # save the graph for each column. The "trick" here is, that we can use
# f-strings to create a unique name for each file =)
Erstelle ein Histogram-Plot // Verteilungs-Plot
# Plot the histogram of the day-ahead electricity price
ax = p_train['pricesCH'].plot.hist(bins=18, alpha=0.65)
Python “Magic Commands”
Übersicht zu allen Spalten
Relevanz
Wenn wir versuchen, große Datensätze in Python auf dem Bildschirm anzuzeigen, wird es - zu Beginn - nicht möglich sein, alle Spalten zu sehen, weil einfach nicht genug Platz vorhanden ist, um alle Spalten anzuzeigen. Wir können jedoch einige Standardeinstellungen inpandas
ändern, um ALLE Spalten sichtbar zu machen:
import pandas as pd
pd.set_option('display.max_rows', 500)
pd.set_option("display.max.columns", None) # this will tell Python: "show me ALL the columns!"
pd.set_option('display.max_columns', 500)
pd.set_option('display.width', 1000)
Mit HTML arbeiten innerhalb eines Jupyter-Notebooks
Wie verwendet man anchor-tags, um Links innerhalb desselben Dokumentes zu verlinken?
Klicke hier, um am Anfang dieses Jupyter-Notebooks zu gelangen. Dies wird erreicht durch:
- Die Verwendungeines
<a>
-HTML-Tags, sowie - Die Benutzung einer eindeutigen ID für den
<a>
-Tag, UND - Ein (optionales)
CSS
-Styling auf dem<a>
-Tag.
<a id="gebe-hier-passenden-id-namen" style="color:black; text-decoration: none;">Hier kommt der Text, auf welchem du zeigen willst...</a>
[And this is the text which will re-direct the user that clicks on this link](#top)
Teile Variablen zwischen Notebooks
Mit folgendem Magic Command
kann man eine beliebige Variable zwischen verschiedenen Jupyter-Notebooks austauschen. Dazu muss lediglich die ursprüngliche Variable mit dem magischen Befehl %store myVariableIwannaPass
übergeben. Um die Variable anschliessend im anderen Notebook abzurufen, muss man denselben Befehl %store
mit dem (zusätzlichen) Parameter r
übergeben.
myData = "The World Makes Sense"
%store myData
Nachdem man die Variable “myData” mit den “Magic Command” gespeichert hat (siehe obige Code-Cell), braucht man bloss ein neues Jupyter-Notebook zu öfnnen und folgenden Code einzugeben:
%store -r myData # step one: run this line in the first cell
myData # step 2: run this line in the second cell
Nützliche “Tricks”, auf denen ich gestossen bin
Reshape(-1, 1)
Was macht
reshape(-1,1)
?
Wenn man einen array
oder eine list
hat, etwa so: [1,2,3,4,5]
…
Würde nun Reshape(-1,1)
auf [1,2,3,4,5]
angewendet werden, würde eine neue Liste von Listen erstellt, mit nur einem Element in jeder Unterliste, z.B. wird die Ausgabe [[1], [2], [3], [4], [5]]
sein.
Siehe zu diesem Thema auch diese Stack-Overflow Antwort.
Mathe-Tricks für Hacks
Funktion, um Zahlen stets abzurunden
Mit der round
-Funktion kann man auf- ODER abrunden, wie wir es normalerweise tun:
round(47/24, ndigits = 2) # runde auf 2 Nachkommastellen
Bei der Verwendung der floor
-Funktion wird das Ergebnis jedoch IMMER nach unten gerundet, wie das folgende Beispiel zeigt:
import math # um 'floo'
math.floor(47/24)
Berechne den Median
Da es keine eingebaute Funktion für den Median gibt, kann man eine Funktion schreiben, die den Median berechnen kann:
# Step 1: make a function for the case "median calculation for an ODD (= ungerade) list of numbers"
def _median_odd(xs) -> float:
return sorted(xs)[len(xs) // 2]
# Step 2: make a function for the case "median calculation for an EVEN (= gerade) list of numbers"
def _median_even(xs) -> float:
sorted_xs = sorted(xs)
hi_midpoint = len(xs) // 2
return (sorted_xs[hi_midpoint - 1] + sorted_xs[hi_midpoint]) / 2
# Step 3: Use the 2 above functions to finally be able to build the median-function
def median(v) -> float:
return _median_even(v) if len(v) % 2 == 0 else _median_odd(v)
assert median([1, 10, 2, 9, 5]) == 5 # check if the above function was written correctly (you can change the number '5' and see
# what happens, if this expression becomes incorrect)
Fun
In diesem Abschnitt werde ich nur einige wichtige Konzepte anhand meiner “Erfindungen” darstellen:
import random
import numpy as np
df = pd.DataFrame(np.random.randn(10, 2),
columns=['Col1', 'Col2'])
df['X'] = pd.Series(['A', 'A', 'A', 'A', 'A',
'B', 'B', 'B', 'B', 'B'])
df
for i,el in enumerate(list(df.columns.values)[:-1]):
a = df.boxplot(el, by ='type')
Arbeitszeit Berechnung
Um zu sehen, wie viele Stunden & Minuten ich heute noch arbeiten muss, habe ich - als Übung - eine Funktion hours_to_go
selbst erfasst, welche mir die Anzahl an noch zu verbleibenden Arbeitsstunden am Tag berechnet:
4*30*24
import datetime # Da wir mit "Zeit" arbeiten, brauche ich das Package "datetime"
def hours_to_go(Stunden, Minuten): # Input: wie viele Stunden & Minuten hast du heute bereits gearbeitet?
"""gibt an, wie lange ich noch heute "Schaffen" muss --> gebe dafür bloss die Anzahl stunden & minuten ein, die du "heute"
bereits gearbeitet hast
> hours_to_go(3,5) --> 0507
interpretation: ich habe fix 8h 12min zu arbeiten. Davon ziehe ich nun 3h 5 min ab --> 0507 heisst, ich habe noch 5h 7min
zu arbeiten, was stimmt!"""
fixe_arbeitszeit = datetime.datetime(2021, 7, 1, 8, 12) # datetime(year, month, day, hour, minute, second, microsecond),
# wobei die ersten 3 Argumente (Jahr, Monat, Tag) OBLIGATORISCH sind! --> hier interessiert uns die letzten 2 Inputs,
# nämlich die "8" und die "12" --> diese wiederspiegeln meine fixe 8h 12 min Arbeitszeit bei der SBB
gearbeitet = datetime.timedelta(hours= Stunden, minutes= Minuten) # anzahl an Stunden, die du noch arbeiten musst
wie_lange_noch = fixe_arbeitszeit - gearbeitet
print(wie_lange_noch.strftime("%H%M"))
hours_to_go(0,5) # call the function to output, how many hours are left to work
Um zu sehen, um welche Uhrzeit ich heute Feierabend machen darf, habe ich - als Übung - eine Funktion arbeit_ende
selbst erfasst:
import datetime # Da wir mit "Zeit" arbeiten, brauche ich das Package "datetime"
def arbeit_ende(Stunden, Minuten):
"""gibt an, wann ich heute mit "Schaffen" fertig bin --> gebe dafür bloss die Anzahl stunden & minuten ein, die du "heute"
noch arbeiten musst, zum Beispiel arbeite ich heute noch '4h 44 min':
> arbeit_ende(4, 44) --> 2021-07-01 17:53:02.907698 """
jetzt = datetime.datetime.now() # current time
added_time = datetime.timedelta(hours= Stunden, minutes= Minuten) # anzahl an Stunden, die du noch arbeiten musst
ende = jetzt + added_time
print(ende)
arbeit_ende(8,7) # call the function to output, until when I need to work today