Effiziente Python Pandas Stock Beta Berechnung auf vielen Dataframes

Ich habe viele (4000+) CSVs von Aktiendaten (Datum, Open, High, Low, Close), die ich in einzelne Pandas Dataframes importiere, um eine Analyse durchzuführen. Ich bin neu in Python und möchte eine rollende 12month Beta für jede Aktie zu berechnen, fand ich einen Beitrag, um rollende Beta zu berechnen ( Python-Pandas berechnen Rolling-Beta mit Rolling gelten für Groupby-Objekt in vectorized Mode ), aber wenn in meinem Code unten verwendet wird Über 2,5 Stunden! Angesichts der Tatsache, dass ich die gleichen Berechnungen in SQL-Tabellen in weniger als 3 Minuten laufen kann, ist dies zu langsam.

Wie kann ich die Leistung von meinem unten Code verbessern, um das von SQL zu entsprechen? Ich verstehe Pandas / Python hat diese Fähigkeit. Meine aktuelle Methode Schleifen über jede Zeile, die ich weiß, verlangsamt Leistung, aber ich bin nicht bewusst von einer aggregierten Möglichkeit, eine Rolling-Fenster Beta-Berechnung auf einem Dataframe durchzuführen.

Hinweis: Die ersten 2 Schritte des Ladens der CSVs in einzelne Dataframes und die Berechnung der täglichen Renditen dauert nur noch 20 Sekunden. Alle meine CSV-Dataframes werden im Wörterbuch 'FilesLoaded' mit Namen wie 'XAO' gespeichert.

Ihre Hilfe wäre sehr geschätzt! Vielen Dank 🙂

import pandas as pd, numpy as np import datetime import ntpath pd.set_option('precision',10) #Set the Decimal Point precision to DISPLAY start_time=datetime.datetime.now() MarketIndex = 'XAO' period = 250 MinBetaPeriod = period # *********************************************************************************************** # CALC RETURNS # *********************************************************************************************** for File in FilesLoaded: FilesLoaded[File]['Return'] = FilesLoaded[File]['Close'].pct_change() # *********************************************************************************************** # CALC BETA # *********************************************************************************************** def calc_beta(df): np_array = df.values m = np_array[:,0] # market returns are column zero from numpy array s = np_array[:,1] # stock returns are column one from numpy array covariance = np.cov(s,m) # Calculate covariance between stock and market beta = covariance[0,1]/covariance[1,1] return beta #Build Custom "Rolling_Apply" function def rolling_apply(df, period, func, min_periods=None): if min_periods is None: min_periods = period result = pd.Series(np.nan, index=df.index) for i in range(1, len(df)+1): sub_df = df.iloc[max(i-period, 0):i,:] if len(sub_df) >= min_periods: idx = sub_df.index[-1] result[idx] = func(sub_df) return result #Create empty BETA dataframe with same index as RETURNS dataframe df_join = pd.DataFrame(index=FilesLoaded[MarketIndex].index) df_join['market'] = FilesLoaded[MarketIndex]['Return'] df_join['stock'] = np.nan for File in FilesLoaded: df_join['stock'].update(FilesLoaded[File]['Return']) df_join = df_join.replace(np.inf, np.nan) #get rid of infinite values "inf" (SQL won't take "Inf") df_join = df_join.replace(-np.inf, np.nan)#get rid of infinite values "inf" (SQL won't take "Inf") df_join = df_join.fillna(0) #get rid of the NaNs in the return data FilesLoaded[File]['Beta'] = rolling_apply(df_join[['market','stock']], period, calc_beta, min_periods = MinBetaPeriod) # *********************************************************************************************** # CLEAN-UP # *********************************************************************************************** print('Run-time: {0}'.format(datetime.datetime.now() - start_time)) 

2 Solutions collect form web for “Effiziente Python Pandas Stock Beta Berechnung auf vielen Dataframes”

Generieren zufällige Bestandsdaten
20 Jahre monatliche Daten für 4.000 Aktien

 dates = pd.date_range('1995-12-31', periods=480, freq='M', name='Date') stoks = pd.Index(['s{:04d}'.format(i) for i in range(4000)]) df = pd.DataFrame(np.random.rand(480, 4000), dates, stoks) 

 df.iloc[:5, :5] 

Bildbeschreibung hier eingeben


Rollenfunktion
Gibt Groupby-Objekt zurück, das bereit ist, benutzerdefinierte Funktionen anzuwenden
Siehe Quelle

 def roll(df, w): # stack df.values w-times shifted once at each stack roll_array = np.dstack([df.values[i:i+w, :] for i in range(len(df.index) - w + 1)]).T # roll_array is now a 3-D array and can be read into # a pandas panel object panel = pd.Panel(roll_array, items=df.index[w-1:], major_axis=df.columns, minor_axis=pd.Index(range(w), name='roll')) # convert to dataframe and pivot + groupby # is now ready for any action normally performed # on a groupby object return panel.to_frame().unstack().T.groupby(level=0) 

Beta-Funktion
Verwenden Sie die geschlossene Formulierung der OLS-Regression
Angenommen, Spalte 0 ist Markt
Siehe Quelle

 def beta(df): # first column is the market X = df.values[:, [0]] # prepend a column of ones for the intercept X = np.concatenate([np.ones_like(X), X], axis=1) # matrix algebra b = np.linalg.pinv(XTdot(X)).dot(XT).dot(df.values[:, 1:]) return pd.Series(b[1], df.columns[1:], name='Beta') 

Demonstration

 rdf = roll(df, 12) betas = rdf.apply(beta) 

Zeitliche Koordinierung

Bildbeschreibung hier eingeben


Validierung
Vergleiche Berechnungen mit OP

 def calc_beta(df): np_array = df.values m = np_array[:,0] # market returns are column zero from numpy array s = np_array[:,1] # stock returns are column one from numpy array covariance = np.cov(s,m) # Calculate covariance between stock and market beta = covariance[0,1]/covariance[1,1] return beta 

 print(calc_beta(df.iloc[:12, :2])) -0.311757542437 

 print(beta(df.iloc[:12, :2])) s0001 -0.311758 Name: Beta, dtype: float64 

Beachten Sie die erste Zelle
Ist der gleiche Wert wie validierte Berechnungen oben

 betas = rdf.apply(beta) betas.iloc[:5, :5] 

Bildbeschreibung hier eingeben


Antwort auf Kommentar
Vollständiges Arbeitsbeispiel mit simulierten Mehrfachdatenbanken

 num_sec_dfs = 4000 cols = ['Open', 'High', 'Low', 'Close'] dfs = {'s{:04d}'.format(i): pd.DataFrame(np.random.rand(480, 4), dates, cols) for i in range(num_sec_dfs)} market = pd.Series(np.random.rand(480), dates, name='Market') df = pd.concat([market] + [dfs[k].Close.rename(k) for k in dfs.keys()], axis=1).sort_index(1) betas = roll(df.pct_change().dropna(), 12).apply(beta) for c, col in betas.iteritems(): dfs[c]['Beta'] = col dfs['s0001'].head(20) 

Bildbeschreibung hier eingeben

Mit einem Generator zur Verbesserung der Speicher-Effizienz

Simulierte Daten

 m, n = 480, 10000 dates = pd.date_range('1995-12-31', periods=m, freq='M', name='Date') stocks = pd.Index(['s{:04d}'.format(i) for i in range(n)]) df = pd.DataFrame(np.random.rand(m, n), dates, stocks) market = pd.Series(np.random.rand(m), dates, name='Market') df = pd.concat([df, market], axis=1) 

Beta-Berechnung

 def beta(df, market=None): # If the market values are not passed, # I'll assume they are located in a column # named 'Market'. If not, this will fail. if market is None: market = df['Market'] df = df.drop('Market', axis=1) X = market.values.reshape(-1, 1) X = np.concatenate([np.ones_like(X), X], axis=1) b = np.linalg.pinv(XTdot(X)).dot(XT).dot(df.values) return pd.Series(b[1], df.columns, name=df.index[-1]) 

Rollenfunktion
Dies gibt einen Generator zurück und wird viel mehr Speicher effizient

 def roll(df, w): for i in range(df.shape[0] - w + 1): yield pd.DataFrame(df.values[i:i+w, :], df.index[i:i+w], df.columns) 

Setzen Sie alles zusammen

 betas = pd.concat([beta(sdf) for sdf in roll(df.pct_change().dropna(), 12)], axis=1).T 

Validierung

OP beta calc

 def calc_beta(df): np_array = df.values m = np_array[:,0] # market returns are column zero from numpy array s = np_array[:,1] # stock returns are column one from numpy array covariance = np.cov(s,m) # Calculate covariance between stock and market beta = covariance[0,1]/covariance[1,1] return beta 

Versuchsaufbau

 m, n = 12, 2 dates = pd.date_range('1995-12-31', periods=m, freq='M', name='Date') cols = ['Open', 'High', 'Low', 'Close'] dfs = {'s{:04d}'.format(i): pd.DataFrame(np.random.rand(m, 4), dates, cols) for i in range(n)} market = pd.Series(np.random.rand(m), dates, name='Market') df = pd.concat([market] + [dfs[k].Close.rename(k) for k in dfs.keys()], axis=1).sort_index(1) betas = pd.concat([beta(sdf) for sdf in roll(df.pct_change().dropna(), 12)], axis=1).T for c, col in betas.iteritems(): dfs[c]['Beta'] = col dfs['s0000'].head(20) 

Bildbeschreibung hier eingeben

 calc_beta(df[['Market', 's0000']]) 0.0020118230147777435 

HINWEIS:
Die Berechnungen sind gleich

  • Pandas - FillNa mit einer anderen Spalte
  • Pandas to_csv call setzt ein Komma vor
  • Slice / Split String Serie an verschiedenen Positionen
  • Identifizieren Sie Cluster, die durch Delta nach links und unterschiedliches Delta nach rechts verknüpft sind
  • Wie zählt man die Anzahl der Vorkommen in einer von zwei Spalten
  • Wie macht man Zackenbarsch und Achse die gleiche Länge?
  • Auswahl der Beobachtung von datetime64 [ns] Typ in bestimmten Zeitbereich
  • Hinzufügen von berechneten Spalten zu einem Dataframe in Pandas
  • Gemischte Arten von Elementen in der Spalte von DataFrame
  • Pandas: Spalte mit den aktuellsten Werten hinzufügen
  • Wie man diesen Pandas-Code optimiert, um schneller zu laufen
  • Python ist die beste Programmiersprache der Welt.