#!/usr/bin/env python # %% Imports from telnetlib import BM # imports überall im Code möglich, aber die Konvention ist alle benötigten import statements # gleich zu Beginn einer Datei zu machen # numpy ist ein Python-Modul für Numerik, das sowohl Funktionalität als auch Effizienz bietet import numpy as np # pandas ist sehr gut zum Arbeiten mit tabellarischen Daten, egal ob csv, xls oder xlsx from numpy.typing import NDArray as array from numpy import float64 as float import pandas as pd from pandas.core.dtypes.dtypes import time # plotting settings pd.plotting.register_matplotlib_converters() # matplotlib ist ein sehr umfangreiches Modul zum Erstellen von Visualisierungen/Plots import matplotlib.pyplot as plt %matplotlib inline # seaborn erleichtert das Erstellen von oft verwendeten Plot-Typen; # es basiert selbst auf matplotlib und man kann beides kombinieren # eine schöne Einführung in Seaborn: https://www.kaggle.com/learn/data-visualization import seaborn as sns # %% load data data = pd.read_csv("../data/melb_data.csv").dropna() # filter data: Less than 400 area, and max 100 data points data = data[(data["BuildingArea"] < 400) ][:100][["BuildingArea", "Price"]] ax = sns.scatterplot(x=data['BuildingArea'], y=data['Price']) data.head() # %% prepare data with useless math extra values because we need to add one as a factor to all of these X = [] Y = [] # aufbereitung, x braucht noch den konstanten eins faktor for _, row in data.iterrows(): X.append([1]+ [row['BuildingArea']]) Y.append(row['Price']) X = np.array(X) Y = np.array(Y) # %% solve the linear thing w_ana = np.linalg.solve(X.T @ X , X.T @ Y) print(f"w_ana: {w_ana}") # %% define that h function, this is just f(x) = mx + b def h(weights: array[float], x): """ x can be a float or an array because numpy does it all the same the return type depends on the type of x """ return weights[0] + weights[1] * x # %% plot the h function combined with the calculated wieghts ax = sns.scatterplot(x=data['BuildingArea'], y=data['Price']) xplot = [min(data['BuildingArea']), max(data['BuildingArea'])] yplot = [h(w_ana, x) for x in xplot] sns.lineplot(x=xplot, y=yplot, ax=ax) # %% Bewertungsfunktion def j(weights: array[float], x: array[float] ,y: array[float]) -> float: # angeblich sollen x,y UNBEDINGT numpy arrays sein, idk warum, sehen für mich aus wie floats, nicht wie arrays errw = y- h(weights, x=y) # pyright hat eigentlich recht, aber irgendwie kann ich doch nen array reinschmeißen auch wenns nen float frisst return 1.0/(2.0 * len(errw) * (errw @ errw)) # example usage j(w_ana, np.array([1.1,1.3]), np.array([2.4,2.6])) # %% calculate score of analytic approach # pyright sagt to_numpy ist unknown, ist es aber aus irgendeinem grund nicht, python doof x = data['BuildingArea'].to_numpy(copy=True) y = data['Price'].to_numpy(copy=True) j_ana = j(w_ana, x=x, y=y) print('Kosten der analytischen Lösung: {}'.format(j_ana)) # %% define grad_dsc functions # Gradient Descent # Let's be honest, no idea what I'm really doing here... def __gradsc_iter(weights: array[float], alpha: float, x: array[float], y: array[float]) -> array[float]: errw: array[float] = y - h(x=x, weights=weights) # weis nicht warum aber das geht doch datentypen mäßig return np.array([ weights[0] + alpha / len(x) * sum(errw), weights[1] + alpha / len(x) * errw @ x ]) def grad_dsc(weights: array[float], alpha: float, x, y, n: int) -> tuple[array[float], array[float]]: j_all = [j(weights,x,y)] for i in range(n): w = __gradsc_iter(weights, alpha, x, y) j_all.append(j(weights,x,y)) return weights, np.array(j_all) # %% no idea what this is w_tmp, j_tmp = grad_dsc(np.array([1e5,1000.0]),alpha=1e-9, x=data["BuildingArea"].to_numpy(), y=data["Price"].to_numpy(), n=1) j_tmp[1] / j_tmp[0] # %% do the actual gradient descent w_init = np.array([1e6, 1000.]) x = np.array(data['BuildingArea']) y = np.array(data['Price']) w_gd_1e4, J_all_1e4 = grad_dsc(weights=w_init, alpha=np.float64(0.0001), x=x, y=y, n=100000) print('w_gd_1e4: {}'.format(w_gd_1e4)) print('Vergleich zu Startkosten: {}'.format(J_all_1e4[-1]/J_all_1e4[0])) print('Vergleich zu analytischer Lösung: {}'.format(J_all_1e4[-1]/j_ana)) print('(w0_gd - w0_ana)/w0_ana: {}'.format((w_gd_1e4[0]-w_ana[0])/w_ana[0])) print('(w1_gd - w1_ana)/w1_ana: {}'.format((w_gd_1e4[1]-w_ana[1])/w_ana[1])) # again, no idea what these values mean?