Drawing Drawing


Notebook zur

Anwendung Emergenzbasierter Statistik I:

Geldmengenaggregate, Aktienindizes, Häuserpreise - eine empirische Analyse makroökonomischer Größen mithilfe Emergenzbasierter Statistik


http://zes.dhbw-vs.de
von André Kuck und Hans Frischhut


Das folgende Notebook gibt einen Einblick in die Möglichkeiten des Einsatzes emergenzbasierter Statistik am Beispiel der Analyse makroökonomischer Daten. Es soll als Lehrmaterial zur Vermittlung emergenter Methoden dienen.
Es ist in sehr kurzer Zeit entstanden und wir bitten daher um Nachsicht wegen sicher vorhandener orthographischer und anderer formaler Fehler.

Inhalt

  • Einführung: Ein erstes emergentes Gesetz über Geldmengenwachstum
  • Schritt 1: Einlesen der Meta-Gesetze
  • Schritt 2: Generierung von endogenen Variablen und Features
  • Schritt 3: Erstellung eines KnowledgeNets
  • Schritt 4: Erstellung von Emergenten Modellen
  • Zusammenfassung: Wie können diese Ergebnisse verwendet werden

Das Notebook lässt sich auf dem Methoden-Server des ZES nachrechnen und weiterentwickeln.

Einführung: Ein erstes emergentes Gesetz über Geldmengenwachstum

Um noch einmal kurz die Grundbegriffe der emergenzbasierten Statistik zu rekapitulieren, wollen wir mit einem kleinen emergenten Gesetz über das Geldmengenwachstum beginnen.

Lesen wir zuerst die ML-Toolbox und die Datenbank ein,

In [1]:
from ML_Tool_029 import *
set_global_rel(0.8,plot_rel=False)
source="C:/Users/Frischhut/Desktop/Lehrveranstaltung Emergente Gesetze/Projektarbeit_Ruth/Projektarbeit Yannik Ruth_1.csv"
#source='C:/Users/Kuck/Documents/kuck/Python/YannikRuth/Projektarbeit Yannik Ruth_1.csv' #Hier liegt die Datei
data = pd.read_csv(source,sep=',',parse_dates=False)
data=copy.deepcopy(data.loc[~data.M1_US.isnull()])
change_cent=False
def add_century(x):
    global change_cent
    if x[4:]=="00":
        change_cent=True
    if change_cent:
        return x[:4]+"20"+x[4:]
    else:
        return x[:4]+"19"+x[4:]
data["DATE"]=data["DATE"].apply(add_century)
data["DATE"]=pd.to_datetime(data["DATE"],format="%b-%Y")
data.index=data["DATE"]
#data=copy.deepcopy(data.loc[~data.C_REX.isnull()])
#data.index=np.arange(len(data))
data['C']=True
data['null']=False
Data-Source ../../Daten
Models are saved to Directory: ../Models_temp/
Global RelLevel 0.8
Global MinObs 16

und werfen einen kurzen Blick auf den Verlauf der einzelnen Zeitreihen:

In [26]:
df=data
zeile=1
spalte=1
pl=0
plt.figure(figsize=(20, 15))
dataseries=['C_SU', 'Case_home', 'Case_10', 'C_DJ', 'M1_US', 'M2_US',
       'C_DAX', 'C_KDAX', 'C_REX', 'M1_DE', 'M2_DE', 'M3_DE', 'C_FT', 'M1_GB',
       'M2_GB', 'M3_GB', 'C_NI', 'M0_JP']
for asset in dataseries:
    #print(asset,df[asset].pct_change().mean())
    zeilen=(len(dataseries)//3)+1
    spalten=3
    #print(pl+1,zeile,spalte)
    plt.subplot(zeilen,spalten,pl+1)
    plt.plot(df.loc[~df[asset].isnull()]['DATE'],df[asset].loc[~df[asset].isnull()],label=asset)
    pl=pl+1
    plt.legend()
plt.show()

Die Daten in diesem Beispiel bestehen aus Monatsdaten einer kleinen Auswahl von unterschiedlichen Assets:

  • 'C_SU': S&P500 Aktienindex
  • 'C_DJ': Dow Jones Aktienindex
  • 'Case_home': Case-Shiller Home Price Index (Häuserpreisindex in den USA)
  • 'Case_10': Case-Shiller Home Price Index für die 10 größten Städte der USA
  • 'C_DAX': DAX Aktienindex
  • 'C_KDAX': Kurs-DAX
  • 'C_REX': Deutscher Rentenindex
  • 'C_FT': FTSE500 Aktienindex
  • 'C_NI': Nikkei225 Aktienindex

Zusätzlich dazu werden Monatsdaten für eine kleine Anzahl von Geldmengenaggregaten verwendet:

  • M1_US und M2_US: Geldmengenaggregate M1/M2 der USA
  • M1_DE, M2_DE und M3_DE: Geldmengenaggregate M1/M2/M3 Deutschlands
  • M1_GB, M2_GB und M3_GB: Geldmengenaggregate M1/M2/M3 Großbritanniens
  • M0_JP: Geldmengenaggregat M0 Japans

Wenn wir nun die Frage nach den Wachstumsraten der Geldmenge M2 für Deutschland (M2_DE.pct_change()) stellen, so können wir feststellen, dass die Geldmenge in manchen Monaten gewachsen und in anderen geschrumpft ist:

In [27]:
plt.plot(data.DATE,data.M2_DE.pct_change())
plt.show()

Wenn wir aber nach einer Fenstergröße T suchen, für die die rollierende mittlere Wachstumsrate der Geldmenge immer größer (oder kleiner) ist als Null, dann stellen wir fest dass:

  • In jeder Sequenz der Länge 32 (und größer) die mittlere Wachstumsrate der Geldmenge größer ist als 0. Wir nennen die Länge der Sequenz, für die Aussage über eine Relation über Funktionen von Sequenzen immer wahr ist T, die Größe der Emergenzmenge.
  • Dies war bisher in 12.56+1 nicht überlappenden Sequenzen der Länge 32 der Fall. Die Anzahl von Bestätigungen des ersten Auftauchens eines solchen Musters nennen wir DiV, den Grad der Induktiven Bestätigung.

Eine solche bisher immer wahre Relation von Funktionen über Sequenzen von Messungen nennen wir ein emergentes Gesetz.

In [28]:
aktlaw=LLaws(data,'df.M2_DE.pct_change()',bed_y='df.C')
#aktlaw.create_emergence_movie('ot',fps=1,LagList=range(1,len(data)//2-1,10))
aktlaw.PlotLaws('ot')
display(aktlaw.DescribeLaw()[['Target','Bench','Bed_y','R','Bed_x','TU_ot','Div(TU_ot)','Min(TU_ot)','Max(TU_ot)']])
Target Bench Bed_y R Bed_x TU_ot Div(TU_ot) Min(TU_ot) Max(TU_ot)
0 df.M2_DE.pct_change() 0 df.C > None 32 12.5625 0.001077 0.010954

Wir haben an dieser Stelle die Größen DiV und T noch einmal kurz eingeführt, weil sie für die weitergehenden Berechnungen von zentraler Bedeutung sind.

Schritt 1: Einlesen der Meta-Gesetze

Eines der zentralen Ergebnisse der emergenzbasierten Statistik ist die Entdeckung von empirischen Gesetzen über die Güte von Prognoseprozessen.

Wenn man mit zum Zeitpunkt t DiV mal bestätigten Gesetzen mit Emergenzmenge T prognostiziert, dass das Muster zum Zeitpunkt t+T wieder zu beobachten sein wird, dann kann man zum Zeitpunkt t+T überprüfen, ob diese Prognose richtig war.

Dies haben wir für sehr viele Datensätze und Prognoseprobleme getan und die Anzahl von Gesetzen mit den Eigenschaften (DiV,T) und die Anzahl richtiger Prognosen gezählt.

Den Anteil bestätigter Prognosen an den gesamten Prognose nennen wir Rel oder Reliability. Man kann nun feststellen, dass es für Prognosen mit Gesetzen aus einer (DiV,T)-Klasse emergente Gesetze über die Mindestreliability nach einer bestimmten Anzahl von Prognosen gibt. Wir nennen diese Gesetze Meta-Gesetze.

Mit Hilfe dieser Meta-Gesetze können wir die Anforderungen an die Anzahl von notwendigen Bestätigungen einer T-Klasse von Gesetzen empirisch festlegen, wenn wir eine bestimmte Mindestreliability verlangen:

In [16]:
set_global_rel(0.8,plot_rel=True)
Global RelLevel 0.8
Global MinObs 16

Die erzeugte Grafik zeigt, wie häufig ein Muster - in Abhängigkeit von der Emergenzmenge T - aufgetreten sein muss, um in eine Kategorie zu fallen, die bisher immer eine bestimmte Mindestprognosegüte hatte (hier Reliability von 0.8 --> mindestens 80% richtige Vorhersagen nach einer bestimmten Anzahl von Prognosen mit Gesetzen dieser Art).

Ein Muster, dass (wie im obigen Beispiel) sich in Sequenzen der Länge T=32 immer finden ließ, muss also mindestens 9 mal aufgetreten (oder 8 mal bestätigt worden) sein, um zu den Gesetzen mit einer Reliability von 0.8 zu gehören. Wenn ein Muster nach 265 Beobachtungen emergiert so genügen bereits 4 Bestätigungen.

Bei unseren weiteren Berechnungen steuern die Meta-Gesetze im Hintergrund alle Berechnungen. Auf diese Art werden die Mindestanforderungen an die Anzahl von Bestätigungen von verwendeten Gesetzen festgelegt werden.

Eine genauere Erklärung und Beispiele von Meta-Gesetzen lassen sich auf unserer Internetseite finden.

Wir wollen an dieser Stelle noch einmal festhalten, dass

dieser Ansatz auf einem rein empirischen Fundament basiert:

Die Aussage, dass die durchschnittliche monatliche Wachstumsrate der Geldmenge M2 nach jeder Sequenz von $T=32$ Monaten größer ist als Null, ist eine rein empirische Aussage.

Weiterhin wissen wir aus unseren Metaanalysen, dass die Prognosen, dass ein Muster der Größe $T=32$ das $DiV=12,56$ mal bestätigt wurde, auch beim nächsten Mal wieder auftritt, (nach Sequenzen von mindestens 16384 Prognosen) bisher immer zumindest zu 80% richtig waren. Auch dies ist ein empirisches Faktum.

Wenn wir also die Reliability auf 80% setzen so stellen wir sicher, dass wir nur Gesetze finden, für die die Prognose, dass das Muster auch beim nächsten Mal zu beobachten sein wird, bisher immer in mindestens 80% der Fälle richtig war.

Schritt 2: Generierung von endogenen Variablen und Features

Aus den oben bereits dargestellten Größen erzeugen wir eine Reihe von Prognosezielen (endogene Variablen) und potentiell erklärenden Variablen oder Features.

Eine endogene Variable, also eine Variable, die man vorhersagen möchte, ist z.B. die Veränderung der M1-Geldmenge in Deutschland im nächsten Monat, oder aber die Wachstumsrate des DAX in den nächsten 2 Monaten usw.

Ein Beispiel für ein Feature ist die durchschnittliche Veränderung der M3-Geldmenge in den USA in den letzten 3 Monaten oder die durchschnittliche Wachstumsrate in den letzten 12 Monaten etc.
Die dahinterliegende Vermutung ist, dass evtl. in Abhängigkeit dieser Größe emergente Gesetze für endogene Variablen gefunden werden können.

In [2]:
IndexList=['C_SU','C_DJ','Case_home','Case_10','C_DAX','C_KDAX','C_REX','C_FT','C_NI'] #Liste der verwendeten Indizes
#GeldList=[['M1_US','M2_US'],['M1_US','M2_US'],['M1_US','M2_US'],['M1_US','M2_US'],['M1_DE','M2_DE','M3_DE'],['M1_DE','M2_DE','M3_DE'],['M1_DE','M2_DE','M3_DE'],['M1_GB','M2_GB','M3_GB'],['M0_JP']]
GeldL=[['M1_US','M2_US'],['M1_DE','M2_DE','M3_DE'],['M1_GB','M2_GB','M3_GB'],['M0_JP']] #Liste der Geldmengen
endo=[] #Initialisierung der Liste mit Größen, die prognostiziert werden sollen
exo=[]  #Initialisierung der Liste mit exogenen Variablen
for aktindex in IndexList:
    endo+=['df.'+aktindex+'.pct_change().shift(-1)'] #Ein-Monats-Rendite
    endo+=['df.'+aktindex+'.pct_change(2).shift(-2)'] #Zwei-Monats-Rendite
    endo+=['df.'+aktindex+'.pct_change(3).shift(-3)'] #Drei-Monats-rendite
    
    for lag in [1,2,3,4,5,6,8,12,16,24,36]:
        exo+=['df.'+aktindex+'.pct_change().rolling(window='+str(lag)+').mean()'] #Rollierende Mittelwerte
        if lag>2:
            exo+=['df.'+aktindex+'.pct_change().rolling(window='+str(lag)+').std()']
            exo+=['df.'+aktindex+'.pct_change().rolling(window='+str(lag)+').std()']#Rollierende Standardabweichungen 
        
for geld in GeldL:
    for aktgeld in geld:
        endo+=['df.'+aktgeld+'.pct_change().shift(-1)'] #Ein-Monatsveränderungsrate der Geldmente
        endo+=['df.'+aktgeld+'.pct_change(2).shift(-2)'] #Zwei-Monatsveränderungsrate der Geldmente
        endo+=['df.'+aktgeld+'.pct_change(3).shift(-3)']  #Drei-Monatsveränderungsrate der Geldmente

        for lag in [1,2,3,4,5,6,8,12,16,24,36]:
            exo+=['df.'+aktgeld+'.pct_change().rolling(window='+str(lag)+').mean()']
            if lag>2:
                exo+=['df.'+aktgeld+'.pct_change().rolling(window='+str(lag)+').std()']
                
    
exo=DataFrame(exo,columns=['Genstring'])
data.index=np.arange(len(data))
data['C']=True
data['null']=False

Die Daten werden in eine Stichprobe, die zur Suche nach Emergenten Gesetzen verwendet wird (within-sample Beobachtungen) und eine Stichprobe (out-of-sample Beobachtungen/Evaluierungsdaten), die für die Überprüfung der Gesetze verwendet wird, aufgeteilt.

Die Evaluierungsstichprobe umfassen die letzten 5 Jahre (2011 bis 2016) der Beobachtungen in der Datenbank. So können die gefundenen emergenten Gesetze anhand von bisher "ungesehenen Daten" überprüft werden und ein tatsächliches Anwendungsszenario simuliert werden.

In [5]:
BisT=len(data)-60 #Aufteilung: 5 Jahre (60 Monate) zur Evaluierung / Rest der Beobachtungen zur Schätzung 

# Größe, gesamte Anzahl Beobachtungen in Schätzstichprobe,Anzahl in Evaluierungsstichprobe, Gesamt
for feld in data.columns: 
    print(feld,data.iloc[:BisT][feld].count(),data.iloc[BisT:][feld].count(),data[feld].count())
DATE 627 60 687
C_SU 627 60 687
Case_home 435 60 495
Case_10 291 60 351
C_DJ 627 60 687
M1_US 627 60 687
M2_US 627 60 687
C_DAX 618 60 678
C_KDAX 280 60 340
C_REX 237 60 297
M1_DE 375 60 435
M2_DE 375 60 435
M3_DE 375 60 435
C_FT 327 60 387
M1_GB 294 60 354
M2_GB 290 60 350
M3_GB 290 60 350
C_NI 327 60 387
M0_JP 495 60 555
C 627 60 687
null 627 60 687

Schritt 3: Erstellung eines KnowledgeNets

Im nächsten Schritt wird auf Basis der Daten ein sog. KnowledgeNet berechnet.

Die verwendeten Ziele (endogene Variablen) für das KnowledgeNet sind in diesem Fall die zukünftigen prozentualen Veränderungen im nächsten Monat, in 2 Monaten und in 3 Monaten der oben bereits beschriebenen Größen.

df.C_SU.pct_change().shift(-1): Prozentuale Veränderung des S&P500 im nächsten Monat
df.C_SU.pct_change(2).shift(-2): Prozentuale Veränderung des S&P500 über die nächsten 2 Monate
df.C_SU.pct_change(3).shift(-3): Prozentuale Veränderung des S&P500 über die nächsten 3 Monate

  • KnowledgeNets sind Mengen von Objekten, die bisher bezüglich eines oder mehrerer Ziele in Peergroups immer verschiedene Mittelwerte aufwiesen.
  • Objekte sind Regeln zur Auswahl von Zeitpunkten - in diesem Beispiel werden einzelne Monate in den Daten ausgewählt.

Beispiel für ein solches Objekt ist in diesem Fall die Auswahl der Zeitpunkte anhand einer Regel wie dieser:
Man betrachtet nur die Monate, in denen die Veränderung der M1-Geldmenge in den USA im Schnitt im letzten Jahr im 80%-100% Quantil (expandierend berechnet) war, d.h. in den höchsten 20% der Veränderungsraten.

categorize(df.M1_US.pct_change().rolling(window=12).mean(),5)==5

Das expandierende Quantil wird mithilfe der categorize-Funktion berechnet. Im Netz werden noch deutliche komplizierte Objektregeln erzeugt.

  • Die Objektregeln werden während des Lernprozesses von unserem KnowledgeNet-Algorithmus durch kreative Suche erstellt.
  • In der Anwendung sind sie eine Menge von "interessanten", zumindest bezüglich einer Zielvariablen immer von anderen unterscheidbaren Objekten.
  • Sie haben sich als solides Fundament für Modellbildung und die Ableitung von Handlungsstrategien erwiesen. (Sie erfüllen so die Funktion der unsupervised learning layer in deep-neural-nets.)
  • Sie werden als fundamentale Regeln mit einer Reihe potentiell interessierender Eigenschaften im KnowledgeWarehouse abgespeichert.
  • Natives Multiziel-Lernen in KnowledgeNets ist möglich, d.h. es können ohne Probleme beliebig viele Ziele berücksichtigt werden:
    In diesem Beispiel werden eine Vielzahl von unterschiedlichen Zielen bearbeitet.

Eine etwas detailliertere Erklärung von KnowledgeNets findet man ebenfalls auf unserer Internetseite.

In [5]:
knet=Knowledge_Net(data.iloc[:BisT],endo,exo,file_name="makro_test",limitdepth=5,law_OR=True,eval_data=data)
In [ ]:
knet.create_unique_features()
Creation of Unique Features:
df.C_SU.pct_change().rolling(window=1).mean(): cardinal var without parameters
 categorization [1, 2, 3, 4, 5] 5
...
df.Case_home.pct_change().rolling(window=16).mean(): cardinal var without parameters
 categorization [1, 2, 3, 4, 5] 5
...
df.M0_JP.pct_change().rolling(window=36).std(): cardinal var without parameters
 categorization [1, 2, 3, 4, 5] 5

All Features 8820

Features 4638 Unique Feature No. 8820 (categorize(df.M0_JP.pct_change().rolling(window=3                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
All Features: 8820 Unique Features: 4638

Am Anfang der Zellenausgabe erscheinen Meldungen zur Erstellung von exogenen Variablen (Features). Dabei wird jede Größe in die bereits angesprochenen expandierenden Quantile unterteilt, die als Ausgangsobjekte im Netz verwendet werden.

In [7]:
knet.create_net()
feature 4638 of 4638 (categorize(df.M0_JP.pct_change().rolling(window=36).std(),5)==3.0) feat-list 87                                                                                                                                                                                                                                                     
Evaluation Object No. 87

KnowledgeNet for the prediction goal(s)

Die grafische Übersicht am Ende der Ausgabe zeigt die gefundenen Objekte für alle im Netz verwendeten Ziele.

Jede einzelne Grafik zeigt Punkte, die Eigenschaften von Objekten graphisch darstellen: Den Durchschnittwert einer einzelnen endogenen Variablen für dieses Objekt und seine Größe.

In der oberen linken Grafik zeigen die Punkte z.B. die prozentuale Veränderung des S&P500 im nächsten Monat auf der Ordinate. (df.C_SU.pct_change().shift(-1)). An der Abszisse der Grafik ist jeweils die Größe des Objekts, d.h. die Anzahl Beobachtungen, die mit dieser Objektregel ausgewählt werden, abzulesen. Ein Wert von 125 ist so zu interpretieren, dass 125 Monate im Schätzsample durch die Regel ausgewählt wurden.

Es sei noch einmal erwähnt, dass diese Objekte sich dadurch auszeichnen, dass sie in Bezug auf den Rollierenden T-Mittelwert einer der endogenen Variablen gegenüber allen ungefähr "gleichgroßen" Objekten immer unterschieden haben.

Ein einfaches Beispiel eines Objekts oder einer Auswahlregel ist das Folgende:

'~((categorize(df.C_KDAX.pct_change().rolling(window=1).mean(),3)==1.0))'

Mit dieser Regel werden Monate ausgewählt, bei denen die prozentuale Veränderung des Kurs-DAX im letzten Monat nicht (~ Negation) im untersten Drittel (==1.0) der Beobachtungen lag (mit expandierenden Quantilen berechnet).


Man kann nun sehr leicht überprüfen, wie gut sich der Mittelwert der gefundenen Objekte für die einzelnen Ziele out-of-sample "übertragt", d.h. ob der Mittelwert mit den neuen/ungesehen Daten ähnlich hoch ist.

Dabei ist aber zu bedenken, dass Gesetze nur in Bezug auf die Rangordnung der Mittelwerte mindestens eines Ziels gefunden wurden. Es kann also gar nicht erwartet werden, dass sich die Größe der Mittelwerte 1:1 in die Zukunft überträgt.

Empirisch wissen wir nur, dass die Gesetze über die Ordnungsbeziehung der Mittelwerte zu einer Klasse von Gesetzen gehören, die sich (salopp formuliert) in mindestens 80% der Fälle wiederholt.

Wir suchen diese Objekte aber auch gar nicht mit dem Ziel, dass wir aus den Ergebnissen deduktiv etwas über zukünftige Werte der Ziele ableiten wollen. Sie sind nur in dem Sinne "interessant", dass wir so eine Menge von bisher immer unterschiedlicher Situationen charakterisieren können.

Trotzdem wollen wir an dieser Stelle eine Grafik erzeugen, die den Mittelwerten des jeweiligen Objekts in der Schätzstichprobe den Mittelwert in der Evaluierungsstichprobe gegenüberstellt.

Wir werden sehen, dass die Objekte Information über die relative und absolute Höhe der Mittelwerte der endogen Variablen in der Evaluierungsstichprobe enthalten. (Wir verfügen auch über Algorithmen, mit denen man Objekte aus KnowledgeNets direkt für die Entscheidungsfindung verwenden kann. Diese werden aber in diesem Notebook nicht vorgestellt.)

In [45]:
# Ausgabe der Scatterplotts zwischen Mittelwert eines Objekts in der 
# Schätzstichprobe und in der Evaluierungsstichprobe für alle Ziele
df=data
zeile=1
spalte=1
pl=0
plt.figure(figsize=(20, 45))
plt.subplots_adjust(hspace=0.4)
for goal in endo:
    zeilen=(len(endo)//3)+1
    spalten=3
    a=copy.deepcopy(knet.ResultsList[0]).sort_values(['objective','mean'],ascending=False)
    a=a.loc[(a.objective==goal)
    plt.subplot(zeilen,spalten,pl+1)
    plt.plot(a['mean'],a['EvalMean'],'.',label='Observed')
    plt.plot(a['mean'],a['mean'],'-',label='Bisector')
    text=str(a['mean'].cov(a['EvalMean'])/a['mean'].var())
    plt.title(goal+' b='+text)
    plt.ylabel('Evaluation')
    #plt.xlabel('Estimation')
    #plt.plot(df.loc[~df[asset].isnull()]['Date'],logrend(df[asset].loc[~df[asset].isnull()]).expanding().sum(),label=asset)
    pl=pl+1
    plt.legend()
plt.xlabel('Estimation')
plt.show()