Teile diesen Beitrag:

Hallo liebe Brainies,

ich erkläre euch hier, was lineare Regression ist und wie ihr lineare Regression in Python umsetzen könnt. Natürlich liefere ich den Python-Code direkt mit, so dass ihr diesen direkt übernehmen könnt.

Lineare Regression ist den meisten vermutlich schon einmal begegnet. Grundsätzlich geht es darum, eine Variable Y durch eine oder mehrere andere Variablen X1, X2, …, Xn zu bestimmen bzw. vorherzusagen. Die zu bestimmende Variable Y nennt man auch Regressant, Kriterium oder einfach abhängige Variable (weil sie von den X abhängt), die für die Vorhersage verwendeten Variablen nennt man Prädiktoren oder Regressoren.

Die Abhängigkeit zwischen dem Regressant Y und den Prädiktoren X wird als linear angenommen. Y sollte metrisch sein, also so etwas wie Alter, Gewicht, Größe, Preis usw. Für Kategoriale Variablen wie Geschlecht, Lieblingsfarbe oder ja/nein-Fragen eignet sich die lineare Regression nicht, da kommt dann zum Beispiel die logistische Regression ins Spiel.

Es gibt noch viele Varianten der Regression, zum Beispiel in denen Polynome höheren Grads statt Geraden (das sind übrigens Polynome 1.Grads) verwendet werden.

Was ist lineare Regression?

Fangen wir einfach an. Nehmen wir erstmal nur eine Prädiktor-Variable X. Der Zusammenhang zwischen X und Y wird als linear angenommen. Haben wir nun einen Datensatz mit Beobachtungen, dann wollen wir eine Gerade durch die Punkte (xi, yi) legen. Und zwar so, dass die Gerade möglichst nahe an den Punkten liegt. D.h. die Summe der Abweichungen zwischen Gerade und den Punkten soll minimiert werden. Nun benutzt man nicht die einfache Abweichung, sondern die quadrierte, weil eine solche Minimierung statistisch schönere Eigenschaften hat. Man spricht daher von einem Kleinste-Quadrate-Schätzer (engl. OLS = ordinary-least-square).

Man kennt vielleicht noch die Geradengleichung aus der Schule: g(x) = m*x + c, wobei m die Steigung und c der y-Achsen-Abschnitt ist.

Bei der linearen Regression sieht es ganz ähnlich aus:

Y ~ b1*X + b0 + ε

Hier schreibt man kein Gleichheitszeichen hin, weil X und Y Zufallsvariablen sind. Man sagt dann, dass Y verteilt ist wie b1*X + b0 + ε. Die bs heißen Regressionskoeffizienten. Im Vergleich zur Geradengleichung kommt noch der Term ε (= epsilon) dazu. Damit wird der Störterm oder Fehler bezeichnet, weil die Punkte ja nicht direkt auf der Geraden liegen.

Haben wir nun eine Stichprobe aus Paaren (xi,yi), man spricht auch von Realisierung der Zufallsvariablen, dann gilt yi = b1*xi + b0 + εi.

Nun versucht man, b1 und b0 so zu bestimmen, dass die Summe der quadrierten Fehler minimal wird. Zum Glück müssen wir das nicht per Hand machen, sondern lassen den Computer rechnen.

Das Bestimmtheitsmaß R²

Für die Güte der linearen Regression benutzt man das Bestimmtheitsmaß R². Das liegt zwischen 0 (= überhaupt kein linearer Zusammenhang) und 1 (= perfekter linearer Zusammenhang). Hast Du einen negativen Wert oder etwas über 1 raus, dann ist es garantiert falsch.

R² kann aber auch trügerisch sein, wie Du in den Beispielen sehen wirst. Da bekommen wir ein hohes R², aber trotzdem ist das Modell eigentlich falsch, weil der Zusammenhang nicht linear ist.

linearer Zusammenhang zwischen zwei Variablen

Im ersten Bild ist ein linearer Zusammenhang zu sehen. Das Bestimmtheitsmaß R² ist mit 0.91 sehr hoch und zeigt, dass x und y stark zusammenhängen. Im zweiten Bild besteht ein quadratischer Zusammenhang von x und y, d.h. die Punkte (x,y) liegen auf einer Parabeln. Die lineare Regression malt trotzdem einfach eine Gerade durch die Punkte. Damit unterschätzen wir die y-Werte für kleine und große x, für mittlere x wird y überschätzt. Trotzdem ist das Bestimmtheitsmaß mit 0.94 sogar noch höher.

Multiple lineare Regression

Meistens hat man nicht nur einen Prädiktor X, sondern eine ganze Reihe davon. Das Prinzip bleibt aber gleich. Hat man zwei Prädiktoren X1 und X2, dann legt man eine Ebene durch die Punkte (x1i, x2i, yi) und bestimmt die Regressionskoeffizienten b0, b1, b2 mit der Gleichung yi = b0 + b1*x1i + b2 * x2i + εi.

Allgemein schreibt man Y ~ B*X + ε.

Was sind die Voraussetzungen für lineare Regression?

Damit die lineare Regression vernünftige Ergebnisse liefert, müssen einige Voraussetzungen erfüllt sein. Natürlich kann man immer eine Gerade so gut wie möglich durch eine Punktwolke legen, aber man möchte ja auch einige Statistiken wie das Bestimmtheitsmaß haben oder vielleicht einen Hypothesentest machen, der prüft, ob die Regressionskoeffizienten ungleich 0 sind.

Lineare Beziehung zwischen den Variablen

Das haben wir schon weiter oben gesehen. Y sollte linear von X abhängen, sonst macht das ganze Modell keinen Sinn.

Wenig Mulitkolinearität

Haben wir mehr als einen Prädiktor, dann sollten die Prädiktoren nicht zu stark miteinander zusammenhängen. Das Problem ist nämlich sonst, das die Regressionskoeffizienten nicht eindeutig sind.

Homoskedastizität der Residuen

Na, auf Anhieb richtig ausgesprochen? Kann ich mir kaum vorstellen. Der Zungenbrecher Homoskedastizität der Residuen bedeutet einfach nur, dass die Varianz des Fehlers ε überall gleich ist. Es sollte nicht sein, dass kleine Werte von y auch eine kleinere Fehlerstreuung bedeutet und große Werte von y dann mit deutlich größerer Fehlerstreuung einhergeht, oder umgekehrt. Im Bild sieht man es, wenn die Breite der Punktwolke (xi,yi) überall ungefähr gleich ist.

Normalverteilung der Residuen

Die Fehler εi sollten in etwa normalverteilt sein. Sind sie es, dann ist übrigens auch die Homoskedastizität erfüllt, da die Fehler alle aus der gleichen N(0, σ²)-Verteilung stammen. Auch große Ausreißer, auf die die Regression sensibel reagiert, sollten dann nur sehr selten auftreten.

Regression mit dem Package scikit-learn

Es gibt natürlich verschiedene Möglichkeiten, die lineare Regression in Python umzusetzen. Eine Möglichkeit ist mit dem Package scikit-learn gegeben. Das Tolle an scikit-learn ist, dass in dem Package auch noch jede Menge weiterer Algorithmen implementiert sind, die alle genauso funktionieren.

Scikit-learn setzt auf numpy auf, d.h. Daten sollten als Arrays vorliegen und auch Ausgabewerte sind im numpy-Format, wie wir nachher sehen werden

Mit der Funktion LinearRegression wird zuerst ein Modell erzeugt, das dann mit fit an die Daten angepasst wird. Dabei muss der Prädiktor x als zweidimensionales Array vorliegen (auch wenn x in dem Beispiel eigentlich eindimensional ist), daher müssen wir x noch mit reshape in die benötigte Form bringen. In dem meisten Fällen ist x ja sowieso mehrdimensional, weil man eine multiple Regression macht.

import numpy as np
from sklearn.linear_model import LinearRegression
 
x = np.array([1, 2, 3, 4])= x.reshape(-1, 1)
 
y = np.array([2, 5, 7, 9])
 
model = LinearRegression()
model.fit(x, y)

Nun, bisher sieht man noch nichts. Die Regressions-Koeffizienten wurden aber durch fit berechnet, sie sind im Modell gespeichert und zwar in den Variablen intercept_ und coef_.

print("y-Achsen-Abschnitt:", model.intercept_)
print("Steigung:", model.coef_)
print("R²:", model.score(x, y))

Achtung, coef_ ist hier ein Array mit einer Zahl drin, während intercept_ nur eine Zahl ist. Auch hier sieht man wieder, dass der Algorithmus auf mehrere Prädiktoren X ausgelegt ist.

print(type(model.intercept_))
# <class 'numpy.float64'>
print(type(model.coef_))
# <class 'numpy.ndarray'>

Beispielcode für scikit-learn Linear Regression

Im folgenden Codebeispiel habe ich die Erzeugung von x und y angepasst. Zudem gibt es am Ende eine kleine Grafik mit den Punkten und der Regressionsgerade. Den gesamten Code könnt ihr euch auch hier herunterladen: linreg.py

Zuerst ziehen wir x aus einer Normalverteilung mit Mittelwert mu1 und Standardabweichung sigma1. Anschließend erzeugen wir y genau so, wie es laut Modell von x abhängt:

yi = b0 + b1 * xi + εi, wobei εi aus einer Normalverteilung mit Mittelwert 0 und Standardabweichung sigmaError gezogen wird.

import numpy as np
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
 
# Stichprobengröße= 100
 
# ziehe x aus Normalverteilung
mu1 = 10
sigma1 = 3= np.random.normal(loc=mu1, scale=sigma1, size=n)
 
# erzeuge y
b1 = 2
b0 = 5
sigmaError = 2= b1 * x + b0 + np.random.normal(loc=0.0, scale=sigmaError, size=n)
formelText = (
"y = "str(b1)"*x+"str(b0)"+ ε mit ε~N(0,"str(sigmaError * sigmaError)")"
)
 
# für die lineare Regression aus sklearn muss x eine Spalte sein= x.reshape((-1, 1))
 
# berechne lineare Regression
model = LinearRegression()
model.fit(x, y)
 
r_sq = model.score(x, y)
intercept = model.intercept_
slope = model.coef_
print("intercept:", intercept)
print("slope:", slope)
print("coefficient of determination:", r_sq)
 
# plot
plt.scatter(x, y, alpha=0.5)
plt.title(formelText)
plt.xlabel("x")
plt.ylabel("y")= (min(x), max(x))
plt.plot(t, model.predict(t), "-r")
ax = plt.gca()
plt.text(
    0.95,
    0.05,
    "R² = " + str(round(r_sq, 2)),
    horizontalalignment="right",
    verticalalignment="center",
    transform=ax.transAxes,
)
plt.show()

Die Grafik habe ich hier über matplotlib erzeugt. Eine Punktwolke entspricht dem Scatterplot plt.scatter. Ergänzt habe ich Titel und Achsenbeschriftungen. Dann folgt die Regressionsgerade, für die ich zuerst das Minimum und Maximum von x nehme und für diese beiden Werte die y-Werte der Regressionsgerade berechne (mit model.predict). Als letztes folgt noch ein Text mit dem Bestimmtheitsmaß R². Damit dieser unten rechts in der Grafik erscheint, musste ich mit ax die Achsen auf Prozent konvertieren.

Statt des linearen Zusammenhangs können wir y auch anders erzeugen, zum Beispiel als quadratischer Zusammenhang. So können wir schauen, wie die lineare Regression auf Verletzung der Grundvoraussetzung „linearer Zusammenhang“ reagiert.

# alternativ quadratischer Zusammenhang zwischen x und y= np.random.uniform(-1, 3, n)
sigmaError = 1= 3 * x * x + 1 + np.random.normal(loc=0.0, scale=sigmaError, size=n)
formelText = "y = 3*x² + 1" + "+ ε mit ε~N(0," + str(sigmaError * sigmaError) + ")"

Eurer Phantasie sind hier keine Grenzen gesetzt. Hier noch ein Beispiel für einen Wurzel-Zusammenhang.

# alternativ Wurzel-Zusammenhang zwischen x und y= np.random.uniform(-1, 200, n)
sigmaError = 0.5= np.sqrt(3 * x + 5) + np.random.normal(loc=0.0, scale=sigmaError, size=n)
formelText = "y = sqrt(3*x+5) " + "+ ε mit ε~N(0," + str(sigmaError * sigmaError) + ")"

Codebeispiel Lineare Regression mit mehreren Variablen

Kommen wir zu einem realistischeren Datensatz. Hier habe ich den Fish Market Datensatz von Kaggle heruntergeladen. Dieser kleine Datensatz mit 159 Datenpunkten besteht aus Gewicht, mehreren Größe-Messungen und Art. Einlesen geht ganz einfach mit pandas read_csv.

Machen wir zuerst nochmal eine einfach Regression, also mit einer Variablen (Python-Code: linreg2.py). Interessant ist das Bild, bei dem man schön sehen kann, dass der Zusammenhang nicht wirklich linear ist.

import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
 
fish = pd.read_csv("fish.csv")
print(fish.shape)
print(fish.columns)
 
x = pd.DataFrame(fish["Length1"])= pd.DataFrame(fish["Weight"])
 
model = LinearRegression()
model.fit(x, y)
 
intercept = model.intercept_[0]
slope = model.coef_[0, 0]
r_sq = model.score(x, y)
print("intercept:", intercept)
print("slope:", slope)
print("coefficient of determination:", r_sq)
 
# Plot
plt.scatter(fish["Length1"], fish["Weight"], alpha=0.7)
plt.title("Fisch Markt")
plt.xlabel("Länge1")
plt.ylabel("Gewicht")
# Regressionsgerade hinzufügen= np.array([min(x.loc[:, "Length1"]), max(x.loc[:, "Length1"])])= t.reshape(-1, 1)
plt.plot(t, model.predict(t), "-r")
plt.show()

Wollen wir nun das Gewicht anhand der Größen vorhersagen, dann müssen wir einfach nur das x anpassen und ein Array aus mehreren Attributen basteln. Hier der Code linreg3.py als Download

import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
import matplotlib.lines as mlines
import matplotlib.transforms as mtransforms
 
fish = pd.read_csv("fish.csv")
print(fish.shape)
print(fish.columns)
 
x = fish[["Length1", "Length2", "Length3", "Height"]]
y = pd.DataFrame(fish["Weight"])
 
model = LinearRegression()
model.fit(x, y)
 
intercept = model.intercept_[0]
slope = model.coef_[0, 0]
r_sq = model.score(x, y)
print("intercept:", intercept)
print("slope:", slope)
print("coefficient of determination:", r_sq)
 
yhat = model.predict(x)
yhat[yhat < 0] = 0
 
# Plot
plt.scatter(fish["Weight"], yhat, alpha=0.7)
plt.title("Fisch Markt")
plt.xlabel("Gewicht")
plt.ylabel("Prognose")
plt.xlim(0, 1700)
plt.ylim(0, 1700)
ax = plt.gca()
line = mlines.Line2D([0, 1], [0, 1], color="red")
transform = ax.transAxes
line.set_transform(transform)
ax.add_line(line)
plt.show()

Bei mehr als 3 Dimensionen können wir nicht mehr als Grafik darstellen. Was man aber machen kann, ist ein Plot mit den tatsächlichen Y-Werten auf der einen Achse und den vorhergesagten Y-Werten auf der anderen Achse. Bei einem linearen Zusammenhang streuen die Punkte gleichmäßig um die Winkelhalbierende.

Hier sehen wir gut, dass die Form der Punktwolke eher an eine Wurzelfunktion erinnert. Das heißt, kleinere Gewichte werden eher überschätzt, da die Punkte über der Winkelhalbierenden sind. Größere Gewichte hingegen werden eher unterschätzt, weil die Punkte sich unter der Winkelhalbierenden befinden.

So, ich hoffe, ihr habt ein bisschen was über einen der Brot- und Butter-Algorithmen jedes Data Scientisten gelernt.

Happy coding,

Euer Holger

 

Titelfoto by Anders Jildén on Unsplash

Teile diesen Beitrag: