Hier ist der angepasste Code, der die Anforderungen aus den Kommentarzeilen umsetzt. Der Code enthält nun einen Fortschrittsbalken für das Einlesen und Verarbeiten der Dateien, entfernt das Threading und filtert die Symbole gemäß den angegebenen Bedingungen.
import os
import pandas as pd
import numpy as np
import csv
from tqdm import tqdm
# Pfade definieren
data_collection_dir = './Data Collection'
news_sentiment_dir = './Data Collection/News Sentiment'
proceed_data_dir = './Data Preprocessing/ProceedData'
sp500_file = './Data Collection/historical_all_sp500_companies.csv'
# Sicherstellen, dass der Ausgabeordner existiert
os.makedirs(proceed_data_dir, exist_ok=True)
# Alle CSV-Dateien im News Sentiment Ordner finden
news_files = [os.path.join(news_sentiment_dir, f) for f in os.listdir(news_sentiment_dir) if f.endswith('.csv')]
# Fehlerprotokollierung
error_log = []
# Datenstruktur der Dateien
columns = ["title", "url", "time_published", "author_1", "author_2", "author_3", "summary", "banner_image", "source",
"category_within_source", "source_domain", "overall_sentiment_score", "overall_sentiment_label",
"ticker", "relevance_score", "ticker_sentiment_score", "ticker_sentiment_label"]
# Mapping der Symbole, die dasselbe Unternehmen repräsentieren
symbol_mapping = {
'FOXA': 'FOX',
'GOOG': 'GOOGL'
}
# Symbolliste aus S&P 500 CSV filtern
sp500_df = pd.read_csv(sp500_file)
df_filtered = sp500_df[((sp500_df['Stock Start'].isna()) | (sp500_df['Stock Start'] <= pd.to_datetime('2022-03-01'))) &
((sp500_df['Stock End'].isna()) | (sp500_df['Stock End'] >= pd.to_datetime('2024-08-01')))]
filtered_symbols = df_filtered['Symbol'].unique()
# Funktion zum Verarbeiten der Dateien
def process_file(file):
symbol = os.path.splitext(os.path.basename(file))[0] # Extrahiere das Symbol aus dem Dateinamen
mapped_symbol = symbol_mapping.get(symbol, symbol) # Verwende das Mapping, wenn vorhanden
# Datei nur verarbeiten, wenn das Symbol in der gefilterten Liste ist
if mapped_symbol not in filtered_symbols:
return None, None
try:
# Überprüfung, ob die Datei nur die Spaltenbezeichnungen enthält und keine Daten
with open(file, 'r', encoding='utf-8') as f:
lines = f.readlines()
if len(lines) <= 1: # Datei enthält nur die Spaltennamen oder ist leer
start_date = pd.to_datetime('2022-02-28')
end_date = pd.to_datetime('2024-08-01')
full_date_range = pd.date_range(start=start_date, end=end_date, freq='D')
df = pd.DataFrame({
'Symbol': mapped_symbol,
'date': full_date_range,
'Relevance': 0,
'NewsSentimentScore': 0,
'News': 0
})
return df, None
else:
df = pd.read_csv(file, encoding='utf-8', quotechar='"', quoting=csv.QUOTE_ALL)
# Überprüfen auf fehlende Spalten
missing_cols = set(columns) - set(df.columns)
if missing_cols:
return None, f"Fehlende Spalten in Datei {file}: {missing_cols}"
# Doppelte Einträge löschen
df.drop_duplicates(inplace=True)
# Fehler in den Daten finden
for idx, row in df.iterrows():
error_cols = row[row.isnull()].index.tolist()
if error_cols:
if 'ticker' in error_cols or 'time_published' in error_cols:
df.drop(index=idx, inplace=True)
else:
df.loc[idx, error_cols] = np.nan
# Spalten umbenennen
df.rename(columns={
'ticker': 'Symbol',
'relevance_score': 'Relevance',
'ticker_sentiment_score': 'NewsSentimentScore'
}, inplace=True)
# Konvertieren von 'time_published' zu Datum
df['date'] = pd.to_datetime(df['time_published'].str[:8], format='%Y%m%d')
# Nur benötigte Spalten auswählen
df = df[['Symbol', 'Relevance', 'NewsSentimentScore', 'date']]
# Symbol anpassen, falls erforderlich
df['Symbol'] = mapped_symbol
# Gruppieren auf Tagesebene und Berechnung des NewsSentimentScore gemäß den Anforderungen
def compute_sentiment_score(x):
num_positive = (x['NewsSentimentScore'] > 0).sum()
num_negative = (x['NewsSentimentScore'] < 0).sum()
return num_positive - num_negative
grouped = df.groupby(['Symbol', 'date'])
agg_df = grouped.agg({
'Relevance': 'sum',
'NewsSentimentScore': compute_sentiment_score,
'Symbol': 'size' # Anzahl der Nachrichten (News)
}).rename(columns={'Symbol': 'News'}).reset_index()
return agg_df, None
except Exception as e:
return None, f"Fehler beim Verarbeiten der Datei {file}: {e}"
# Fortschrittsanzeige für das Einlesen und Verarbeiten der Dateien
all_dfs = []
for file in tqdm(news_files, desc="Dateien werden verarbeitet"):
df, error = process_file(file)
if df is not None:
all_dfs.append(df)
if error is not None:
error_log.append(error)
# Überprüfen, ob Daten vorhanden sind
if all_dfs:
combined_df = pd.concat(all_dfs, ignore_index=True)
else:
print("Keine Daten zum Verarbeiten.")
exit()
# Gesamtzahl der Schlagzeilen im Untersuchungszeitraum
total_news_period = combined_df['News'].sum()
combined_df['NewsSentimentScore'] = combined_df['NewsSentimentScore'] / total_news_period
# Sicherstellen, dass der Index eindeutig ist
combined_df.drop_duplicates(subset=['Symbol', 'date'], inplace=True)
# Erstellen eines vollständigen Datumsbereichs
start_date = pd.to_datetime('2022-03-01')
end_date = pd.to_datetime('2024-08-01')
full_date_range = pd.date_range(start=start_date, end=end_date, freq='D')
# Alle Symbole ermitteln
all_symbols = combined_df['Symbol'].unique()
# MultiIndex aus allen Symbolen und dem vollständigen Datumsbereich erstellen
index = pd.MultiIndex.from_product([all_symbols, full_date_range], names=['Symbol', 'date'])
# Reindexieren des DataFrames, um alle Kombinationen von Symbolen und Daten zu erhalten
combined_df.set_index(['Symbol', 'date'], inplace=True)
combined_df = combined_df.reindex(index)
combined_df.reset_index(inplace=True)
# Fehlende Werte mit 0 auffüllen
combined_df[['Relevance', 'NewsSentimentScore', 'News']] = combined_df[['Relevance', 'NewsSentimentScore', 'News']].fillna(0)
# Symbolersetzungen (BF-A -> BF-B, BRK-A -> BRK-B, etc.)
combined_df['Symbol'] = combined_df['Symbol'].replace({'BF-A': 'BF-B', 'BRK-A': 'BRK-B'})
# Doppelte Einträge vermeiden für FOXA/GOOG und ihre Aliase:
combined_df = combined_df.drop_duplicates(subset=['Symbol', 'date'])
# Dateien speichern
combined_df.to_csv(os.path.join(proceed_data_dir, 'historical_news_sentiment_proceed.csv'), index=False)
# Fehlerprotokoll ausgeben
if error_log:
print("Fehlerprotokoll:")
for error in error_log:
print(error)
else:
print("Keine Fehler gefunden.")
Änderungen im Code:
- Fortschrittsbalken: Der Fortschrittsbalken wird durch die Verwendung von
tqdm
beim Durchlaufen der news_files
implementiert.
- Entfernung von Threading: Der Code verwendet kein Threading mehr, was die Komplexität reduziert.
- Filterung der Symbole: Die Filterung der Symbole erfolgt direkt nach dem Einlesen der S&P 500-Daten, bevor die Dateien verarbeitet werden.