File: //opt/af/src/components/data_analysis.py
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import matplotlib.pyplot as plt
import seaborn as sns
from io import StringIO
import os
import datetime
import json
from ai.agent import AIAgent
from database.mysql_connector import MySQLConnector
from sftp.sftp_client import SFTPClient
def render_data_analysis():
"""
Rendert die Streamlit-Komponente für die Datenanalyse.
"""
st.header("Datenanalyse")
# Erstelle eine Instanz des AI-Agenten, MySQL-Connectors und SFTP-Clients
agent = AIAgent()
db_connector = MySQLConnector()
sftp_client = SFTPClient()
# Erstelle Tabs für verschiedene Datenquellen
tabs = st.tabs(["Lokale Datei", "Datenbank", "SFTP", "Kombinierte Analyse"])
# Session State für Dataframes
if 'dataframes' not in st.session_state:
st.session_state.dataframes = {
'local': None,
'mysql': None,
'sftp': None,
'combined': None
}
# Tab 1: Lokale Datei
with tabs[0]:
process_local_file(agent)
# Tab 2: Datenbankdaten
with tabs[1]:
process_database_data(db_connector, agent)
# Tab 3: SFTP-Daten
with tabs[2]:
process_sftp_data(sftp_client, agent)
# Tab 4: Kombinierte Analyse
with tabs[3]:
process_combined_data(agent)
def process_local_file(agent):
"""
Verarbeitet eine lokal hochgeladene Datei.
"""
st.subheader("Lokale Dateianalyse")
# Datei-Upload
uploaded_file = st.file_uploader("CSV-Datei hochladen", type=["csv"])
if uploaded_file is not None:
# Lese die CSV-Datei
df = load_csv_file(uploaded_file)
# Speichere das DataFrame im Session State
st.session_state.dataframes['local'] = df
# Analyse der Daten
analyze_dataframe(df, "Lokale Datei", agent)
else:
st.info("Bitte laden Sie eine CSV-Datei hoch")
def process_database_data(db_connector, agent):
"""
Verarbeitet Daten aus der MySQL-Datenbank.
"""
st.subheader("Datenbankanalyse")
# SQL-Abfrage eingeben
sql_query = st.text_area("SQL-Abfrage eingeben:", height=100)
# Abfrage ausführen
if st.button("Abfrage ausführen"):
if sql_query:
with st.spinner("Daten werden aus der Datenbank abgerufen..."):
results = db_connector.execute_query(sql_query)
if results:
df = pd.DataFrame(results)
st.success(f"{len(df)} Datensätze erfolgreich abgerufen")
# Speichere das DataFrame im Session State
st.session_state.dataframes['mysql'] = df
# Analyse der Daten
analyze_dataframe(df, "Datenbank", agent)
else:
st.error("Fehler bei der Abfrage oder keine Ergebnisse")
else:
st.warning("Bitte geben Sie eine SQL-Abfrage ein")
# Tabellenliste anzeigen
if st.checkbox("Verfügbare Tabellen anzeigen"):
with st.spinner("Tabellen werden abgerufen..."):
tables = db_connector.execute_query("SHOW TABLES")
if tables:
table_names = [list(table.values())[0] for table in tables]
st.write("Verfügbare Tabellen:")
st.write(table_names)
# Tabellenstruktur anzeigen
selected_table = st.selectbox("Tabellenstruktur anzeigen:", table_names)
if selected_table:
columns = db_connector.execute_query(f"DESCRIBE {selected_table}")
if columns:
st.write(f"Struktur von {selected_table}:")
columns_df = pd.DataFrame(columns)
st.dataframe(columns_df)
def process_sftp_data(sftp_client, agent):
"""
Verarbeitet Daten vom SFTP-Server.
"""
st.subheader("SFTP-Datenanalyse")
# Verbindung zum SFTP-Server herstellen
if st.button("Mit SFTP-Server verbinden"):
with st.spinner("Verbindung zum SFTP-Server wird hergestellt..."):
if sftp_client.connect():
st.success("Verbindung zum SFTP-Server hergestellt")
# Liste der Dateien anzeigen
files = sftp_client.list_files()
if files:
# Filtere nur CSV-Dateien
csv_files = [f for f in files if f.lower().endswith('.csv')]
if csv_files:
selected_file = st.selectbox("CSV-Datei auswählen:", csv_files)
if st.button("Datei herunterladen und analysieren"):
with st.spinner(f"Datei {selected_file} wird heruntergeladen..."):
# Kombiniere Pfade
remote_path = os.path.join(sftp_client.remote_path, selected_file)
local_path = sftp_client.download_file(remote_path)
if local_path:
st.success(f"Datei erfolgreich heruntergeladen: {local_path}")
# Lese die CSV-Datei
df = pd.read_csv(local_path)
# Speichere das DataFrame im Session State
st.session_state.dataframes['sftp'] = df
# Analyse der Daten
analyze_dataframe(df, "SFTP", agent)
else:
st.error("Fehler beim Herunterladen der Datei")
else:
st.warning("Keine CSV-Dateien gefunden")
else:
st.error("Fehler beim Abrufen der Dateien")
else:
st.error("Fehler bei der Verbindung zum SFTP-Server")
def process_combined_data(agent):
"""
Führt eine kombinierte Analyse von Daten aus verschiedenen Quellen durch.
"""
st.subheader("Kombinierte Datenanalyse")
# Prüfe, welche Datenquellen verfügbar sind
available_sources = {}
for source, df in st.session_state.dataframes.items():
if df is not None:
available_sources[source] = df
if not available_sources:
st.info("Keine Daten verfügbar. Bitte laden Sie zuerst Daten aus mindestens zwei Quellen.")
return
# Wenn mindestens zwei Datenquellen verfügbar sind
if len(available_sources) >= 2:
# Wähle Datenquellen für die Kombination
selected_sources = st.multiselect(
"Datenquellen auswählen:",
list(available_sources.keys()),
default=list(available_sources.keys())[:2]
)
if len(selected_sources) >= 2:
# Optionen für das Kombinieren der Daten
join_type = st.selectbox(
"Verbindungstyp:",
["Inner Join", "Left Join", "Right Join", "Outer Join"]
)
# Wähle Schlüsselspalten für das Joining
join_columns = {}
for source in selected_sources:
join_columns[source] = st.selectbox(
f"Schlüsselspalte für {source}:",
available_sources[source].columns
)
# Button zum Kombinieren der Daten
if st.button("Daten kombinieren"):
with st.spinner("Daten werden kombiniert..."):
# Starte mit dem ersten DataFrame
result_df = available_sources[selected_sources[0]].copy()
# Kombiniere mit den anderen DataFrames
for i in range(1, len(selected_sources)):
source = selected_sources[i]
df = available_sources[source]
# Bestimme den Join-Typ
how = join_type.lower().split()[0]
# Führe den Join durch
result_df = result_df.merge(
df,
left_on=join_columns[selected_sources[0]],
right_on=join_columns[source],
how=how,
suffixes=(f"_{selected_sources[0]}", f"_{source}")
)
# Speichere das kombinierte DataFrame
st.session_state.dataframes['combined'] = result_df
# Analyse der kombinierten Daten
analyze_dataframe(result_df, "Kombinierte Daten", agent)
else:
st.warning("Bitte wählen Sie mindestens zwei Datenquellen aus")
else:
st.info("Mindestens zwei Datenquellen sind erforderlich für eine kombinierte Analyse")
def load_csv_file(file_obj):
"""
Lädt und verarbeitet eine CSV-Datei.
"""
try:
# Versuche, die CSV-Datei zu lesen
df = pd.read_csv(file_obj)
return df
except Exception as e:
st.error(f"Fehler beim Lesen der CSV-Datei: {str(e)}")
return None
def analyze_dataframe(df, source_name, agent):
"""
Führt Datenanalyse und Visualisierung für ein DataFrame durch.
"""
if df is None or df.empty:
st.warning("Keine Daten verfügbar für die Analyse")
return
# Zeige die Daten an
with st.expander("Datenvorschau", expanded=True):
st.dataframe(df.head(10))
# Grundlegende Statistiken
with st.expander("Datenstatistiken", expanded=True):
col1, col2 = st.columns(2)
with col1:
st.write(f"Anzahl der Zeilen: {len(df)}")
st.write(f"Anzahl der Spalten: {len(df.columns)}")
# Fehlende Werte
missing_values = df.isnull().sum()
if missing_values.sum() > 0:
st.write("Fehlende Werte:")
st.write(missing_values[missing_values > 0])
else:
st.write("Keine fehlenden Werte gefunden")
with col2:
# Datentypen
st.write("Datentypen:")
st.write(df.dtypes)
# Datenvorverarbeitung
with st.expander("Datenvorverarbeitung", expanded=False):
st.subheader("Datenvorverarbeitung")
# Optionen für die Vorverarbeitung
preprocessing_options = st.multiselect(
"Vorverarbeitungsoptionen:",
[
"Fehlende Werte behandeln",
"Ausreißer erkennen",
"Normalisierung",
"Kategorische Variablen kodieren"
]
)
processed_df = df.copy()
if "Fehlende Werte behandeln" in preprocessing_options:
st.write("Fehlende Werte behandeln:")
# Wähle numerische Spalten
numeric_cols = processed_df.select_dtypes(include=np.number).columns.tolist()
selected_cols = st.multiselect("Spalten auswählen:", numeric_cols, default=numeric_cols)
# Methode für die Behandlung von fehlenden Werten
method = st.selectbox(
"Methode:",
["Entfernen", "Mittelwert", "Median", "Modus", "Konstant"]
)
if st.button("Fehlende Werte behandeln"):
if method == "Entfernen":
processed_df = processed_df.dropna(subset=selected_cols)
elif method == "Mittelwert":
for col in selected_cols:
processed_df[col] = processed_df[col].fillna(processed_df[col].mean())
elif method == "Median":
for col in selected_cols:
processed_df[col] = processed_df[col].fillna(processed_df[col].median())
elif method == "Modus":
for col in selected_cols:
processed_df[col] = processed_df[col].fillna(processed_df[col].mode()[0])
elif method == "Konstant":
const_value = st.number_input("Konstanter Wert:", value=0)
for col in selected_cols:
processed_df[col] = processed_df[col].fillna(const_value)
st.write("Ergebnis nach der Behandlung:")
st.dataframe(processed_df.head())
if "Ausreißer erkennen" in preprocessing_options:
st.write("Ausreißer erkennen:")
# Wähle numerische Spalten
numeric_cols = processed_df.select_dtypes(include=np.number).columns.tolist()
selected_col = st.selectbox("Spalte auswählen:", numeric_cols)
if st.button("Ausreißer erkennen"):
# Z-Score-Methode für Ausreißererkennung
z_scores = np.abs((processed_df[selected_col] - processed_df[selected_col].mean()) / processed_df[selected_col].std())
outliers = processed_df[z_scores > 3]
if len(outliers) > 0:
st.write(f"Gefundene Ausreißer ({len(outliers)} Zeilen):")
st.dataframe(outliers)
# Boxplot für Ausreißer
fig = px.box(processed_df, y=selected_col, title=f"Boxplot für {selected_col}")
st.plotly_chart(fig)
else:
st.write("Keine Ausreißer gefunden")
# Erweiterte Visualisierung
with st.expander("Datenvisualisierung", expanded=True):
st.subheader("Datenvisualisierung")
# Wähle den Visualisierungstyp
chart_type = st.selectbox(
"Visualisierungstyp:",
[
"Liniendiagramm",
"Balkendiagramm",
"Streudiagramm",
"Histogramm",
"Heatmap",
"Zeitreihe",
"Box-Plot",
"Pie-Chart",
"3D-Streudiagramm"
]
)
# Visualisierungsoptionen basierend auf dem ausgewählten Typ
if chart_type in ["Liniendiagramm", "Balkendiagramm", "Streudiagramm"]:
x_column = st.selectbox("X-Achse:", df.columns)
y_column = st.selectbox("Y-Achse:", df.columns)
color_column = st.selectbox("Farbe (optional):", ["Keine"] + list(df.columns))
color = None if color_column == "Keine" else color_column
if chart_type == "Liniendiagramm":
fig = px.line(df, x=x_column, y=y_column, color=color)
elif chart_type == "Balkendiagramm":
fig = px.bar(df, x=x_column, y=y_column, color=color)
else: # Streudiagramm
fig = px.scatter(df, x=x_column, y=y_column, color=color)
st.plotly_chart(fig)
elif chart_type == "Histogramm":
column = st.selectbox("Spalte:", df.columns)
bins = st.slider("Anzahl der Bins:", min_value=5, max_value=100, value=20)
fig = px.histogram(df, x=column, nbins=bins)
st.plotly_chart(fig)
elif chart_type == "Heatmap":
# Nur numerische Spalten für die Heatmap
numeric_cols = df.select_dtypes(include=np.number).columns.tolist()
if len(numeric_cols) > 1:
selected_cols = st.multiselect("Spalten auswählen:", numeric_cols, default=numeric_cols[:5])
if selected_cols:
# Berechne die Korrelationsmatrix
corr = df[selected_cols].corr()
# Erstelle die Heatmap
fig = px.imshow(
corr,
text_auto=True,
color_continuous_scale="RdBu_r",
title="Korrelationsmatrix"
)
st.plotly_chart(fig)
else:
st.warning("Nicht genügend numerische Spalten für eine Heatmap")
elif chart_type == "Zeitreihe":
# Prüfe, ob es Datumsspalten gibt
date_cols = [col for col in df.columns if df[col].dtype == 'datetime64[ns]' or 'date' in col.lower() or 'time' in col.lower()]
if date_cols:
date_column = st.selectbox("Datumsspalte:", date_cols)
value_column = st.selectbox("Wertspalte:", df.select_dtypes(include=np.number).columns)
# Konvertiere die Datumsspalte, falls erforderlich
try:
if df[date_column].dtype != 'datetime64[ns]':
df[date_column] = pd.to_datetime(df[date_column])
# Sortiere nach Datum
temp_df = df.sort_values(by=date_column)
# Erstelle das Zeitreihendiagramm
fig = px.line(
temp_df,
x=date_column,
y=value_column,
title=f"Zeitreihe: {value_column} über {date_column}"
)
st.plotly_chart(fig)
# Einfache Zeitreihenanalyse
if st.checkbox("Einfache Zeitreihenanalyse anzeigen"):
# Berechne rollenden Durchschnitt
window = st.slider("Fensterbreite für rollenden Durchschnitt:", min_value=2, max_value=30, value=7)
# Erstelle das DataFrame für die Analyse
analysis_df = temp_df[[date_column, value_column]].copy()
analysis_df['Rolling Average'] = analysis_df[value_column].rolling(window=window).mean()
# Erstelle das Diagramm
fig = px.line(
analysis_df,
x=date_column,
y=[value_column, 'Rolling Average'],
title=f"Zeitreihenanalyse mit {window}-Perioden rollendem Durchschnitt"
)
st.plotly_chart(fig)
except Exception as e:
st.error(f"Fehler bei der Zeitreihenanalyse: {str(e)}")
else:
st.warning("Keine geeigneten Datumsspalten gefunden für die Zeitreihenanalyse")
elif chart_type == "Box-Plot":
y_column = st.selectbox("Y-Achse (numerisch):", df.select_dtypes(include=np.number).columns)
x_column = st.selectbox("X-Achse (kategoriell, optional):", ["Keine"] + list(df.columns))
if x_column == "Keine":
fig = px.box(df, y=y_column)
else:
fig = px.box(df, x=x_column, y=y_column)
st.plotly_chart(fig)
elif chart_type == "Pie-Chart":
value_column = st.selectbox("Wertspalte:", df.select_dtypes(include=np.number).columns)
name_column = st.selectbox("Namensspalte:", df.columns)
# Limitiere die Anzahl der Einträge für bessere Lesbarkeit
top_n = st.slider("Top N Einträge anzeigen:", min_value=3, max_value=20, value=10)
# Aggregiere die Daten
pie_data = df.groupby(name_column)[value_column].sum().reset_index()
pie_data = pie_data.sort_values(by=value_column, ascending=False).head(top_n)
fig = px.pie(
pie_data,
values=value_column,
names=name_column,
title=f"Verteilung von {value_column} nach {name_column}"
)
st.plotly_chart(fig)
elif chart_type == "3D-Streudiagramm":
# Nur wenn es mindestens 3 numerische Spalten gibt
numeric_cols = df.select_dtypes(include=np.number).columns.tolist()
if len(numeric_cols) >= 3:
x_column = st.selectbox("X-Achse:", numeric_cols, index=0)
y_column = st.selectbox("Y-Achse:", numeric_cols, index=min(1, len(numeric_cols)-1))
z_column = st.selectbox("Z-Achse:", numeric_cols, index=min(2, len(numeric_cols)-1))
color_column = st.selectbox("Farbe (optional):", ["Keine"] + list(df.columns))
color = None if color_column == "Keine" else color_column
fig = px.scatter_3d(
df,
x=x_column,
y=y_column,
z=z_column,
color=color,
title="3D-Streudiagramm"
)
st.plotly_chart(fig)
else:
st.warning("Nicht genügend numerische Spalten für ein 3D-Streudiagramm")
# KI-Analyse
with st.expander("KI-Analyse", expanded=True):
st.subheader("KI-gestützte Datenanalyse")
# Analyseoptionen
analysis_type = st.selectbox(
"Analysetyp:",
[
"Allgemeine Datenanalyse",
"Zeitreihenvorhersage",
"Korrelationsanalyse",
"Ausreißererkennung",
"Segmentierung",
"Benutzerdefinierte Analyse"
]
)
if analysis_type == "Benutzerdefinierte Analyse":
# Analyseanfrage
analysis_query = st.text_area("Analyseanfrage:", height=100)
# Analysieren-Button
if st.button("Analysieren"):
if analysis_query:
with st.spinner("Daten werden analysiert..."):
# Führe die Analyse durch
context = {
"source": source_name,
"shape": df.shape,
"columns": list(df.columns),
"dtypes": str(df.dtypes),
"sample_data": df.head(5).to_dict()
}
analysis = agent.analyze_data(context, analysis_query)
# Zeige die Analyseergebnisse an
st.markdown("### Analyseergebnisse")
st.write(analysis)
else:
st.warning("Bitte geben Sie eine Analyseanfrage ein")
else:
# Vordefinierte Analysen
if st.button("Analysieren"):
with st.spinner(f"{analysis_type} wird durchgeführt..."):
# Erstelle die Analyseabfrage basierend auf dem ausgewählten Typ
if analysis_type == "Allgemeine Datenanalyse":
query = "Führe eine allgemeine Datenanalyse durch. Identifiziere wichtige Muster, Trends und Statistiken."
elif analysis_type == "Zeitreihenvorhersage":
query = "Analysiere zeitliche Muster in den Daten. Identifiziere Trends, Saisonalität und zyklische Komponenten."
elif analysis_type == "Korrelationsanalyse":
query = "Identifiziere und erkläre Korrelationen zwischen den Variablen. Welche Variablen haben starke Beziehungen zueinander?"
elif analysis_type == "Ausreißererkennung":
query = "Identifiziere potenzielle Ausreißer in den Daten und erkläre, warum sie als Ausreißer betrachtet werden könnten."
elif analysis_type == "Segmentierung":
query = "Identifiziere natürliche Segmente oder Cluster in den Daten. Wie können die Daten sinnvoll gruppiert werden?"
# Führe die Analyse durch
context = {
"source": source_name,
"shape": df.shape,
"columns": list(df.columns),
"dtypes": str(df.dtypes),
"sample_data": df.head(5).to_dict(),
"analysis_type": analysis_type
}
analysis = agent.analyze_data(context, query)
# Zeige die Analyseergebnisse an
st.markdown("### Analyseergebnisse")
st.write(analysis)