Marek4321's picture
Update app.py
4fb4b3c verified
import streamlit as st
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import requests
import json
import io
# Konfiguracja
st.set_page_config(page_title="🤖 DataSense Agent", layout="wide")
st.title("🤖 DataSense Agent")
st.write("Agent analityczny")
# Session state
if 'df' not in st.session_state:
st.session_state.df = None
if 'file_info' not in st.session_state:
st.session_state.file_info = None
if 'analysis_history' not in st.session_state:
st.session_state.analysis_history = []
if 'questions_count' not in st.session_state:
st.session_state.questions_count = 0
if 'quick_question' not in st.session_state:
st.session_state.quick_question = ""
# API
DEEPSEEK_API_URL = "https://api.deepseek.com/v1/chat/completions"
def smart_csv_detection(file_content):
"""Inteligentne wykrywanie formatu CSV"""
separators = [';', ',', '\t', '|']
encodings = ['utf-8', 'cp1250', 'latin1', 'iso-8859-1']
st.info("🔍 Analizuję format pliku...")
try:
sample_text = None
for encoding in encodings:
try:
file_content.seek(0)
sample_text = file_content.read(2000).decode(encoding, errors='ignore')
break
except:
continue
if not sample_text:
return None, "Nie można określić kodowania pliku"
file_content.seek(0)
except Exception as e:
return None, f"Błąd odczytu pliku: {e}"
results = []
for separator in separators:
for encoding in encodings:
try:
file_content.seek(0)
test_df = pd.read_csv(
file_content,
sep=separator,
encoding=encoding,
nrows=5,
quotechar='"',
skipinitialspace=True,
on_bad_lines='skip'
)
num_cols = len(test_df.columns)
quality_score = 0
if num_cols > 1:
quality_score += num_cols * 10
problematic_names = sum(1 for col in test_df.columns if ';' in str(col) or ',' in str(col))
quality_score -= problematic_names * 50
if num_cols > 1 and len(test_df) > 0:
first_row = test_df.iloc[0]
valid_cells = sum(1 for val in first_row if pd.notna(val) and str(val).strip())
quality_score += valid_cells * 5
results.append({
'separator': separator,
'encoding': encoding,
'columns': num_cols,
'quality': quality_score,
'sample_df': test_df,
'column_names': list(test_df.columns)
})
except Exception as e:
continue
if not results:
return None, "Nie udało się wykryć formatu pliku"
best_result = max(results, key=lambda x: x['quality'])
st.success(f"✅ Wykryty format:")
st.write(f"**Separator:** '{best_result['separator']}'")
st.write(f"**Kodowanie:** {best_result['encoding']}")
st.write(f"**Kolumny:** {best_result['columns']}")
st.write(f"**Jakość:** {best_result['quality']} punktów")
if len(results) > 1:
with st.expander("🔧 Inne możliwe formaty", expanded=False):
for i, result in enumerate(sorted(results, key=lambda x: x['quality'], reverse=True)[:3]):
if result != best_result:
st.write(f"Opcja {i+1}: separator='{result['separator']}', kodowanie={result['encoding']}, kolumny={result['columns']}")
return best_result, None
def load_csv_with_config(file_content, config):
"""Wczytaj pełny CSV z wykrytą konfiguracją"""
try:
file_content.seek(0)
df = pd.read_csv(
file_content,
sep=config['separator'],
encoding=config['encoding'],
quotechar='"',
skipinitialspace=True,
on_bad_lines='skip'
)
return df, None
except Exception as e:
return None, f"Błąd wczytywania: {e}"
def safe_display_data(df, max_rows=5):
"""Bezpieczne wyświetlanie danych"""
try:
if len(df) <= max_rows:
st.dataframe(df, use_container_width=True)
else:
st.dataframe(df.head(max_rows), use_container_width=True)
st.info(f"Pokazano {max_rows} z {len(df)} wierszy")
except:
st.warning("⚠️ Problem z wyświetlaniem tabeli. Pokazuję jako tekst:")
for i in range(min(max_rows, len(df))):
row_data = []
for col in df.columns[:6]:
try:
val = df.iloc[i][col]
row_data.append(f"{col}: {val}")
except:
row_data.append(f"{col}: [błąd]")
st.text(f"Wiersz {i+1}: {' | '.join(row_data)}")
def generate_interpretation(question, result, code, api_key):
"""Generuje interpretację wyników przez AI"""
if not api_key:
return "⚠️ Brak klucza API - interpretacja niedostępna"
prompt = f"""
Jako ekspert analizy danych, przygotuj zwięzłą interpretację wyników w języku polskim:
Pytanie użytkownika: {question}
Wynik analizy: {str(result)[:800] if result is not None else "Brak wyniku"}
Użyty kod: {code}
Przygotuj interpretację w formacie:
## 📊 Podsumowanie
[1-2 zdania o głównym wyniku]
## 🔍 Kluczowe wnioski
- [Wniosek 1]
- [Wniosek 2]
- [Wniosek 3 jeśli jest]
## 💡 Sugestie dalszych analiz
[Konkretne pomysły na kolejne pytania]
Bądź konkretny i praktyczny. Używaj prostego języka.
"""
try:
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
payload = {
"model": "deepseek-chat",
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.4,
"max_tokens": 600
}
response = requests.post(DEEPSEEK_API_URL, headers=headers, json=payload, timeout=30)
if response.status_code == 200:
return response.json()['choices'][0]['message']['content']
else:
return f"❌ Błąd API interpretacji: {response.status_code}"
except Exception as e:
return f"❌ Błąd interpretacji: {str(e)}"
def simple_query_ai(prompt, api_key):
"""Zapytanie do AI"""
if not api_key:
return None
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
payload = {
"model": "deepseek-chat",
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.3,
"max_tokens": 800
}
try:
response = requests.post(DEEPSEEK_API_URL, headers=headers, json=payload, timeout=30)
if response.status_code == 200:
return response.json()['choices'][0]['message']['content']
else:
st.error(f"API Error: {response.status_code}")
return None
except Exception as e:
st.error(f"Connection error: {e}")
return None
def extract_python_code(text):
"""Wyciągnij kod Python"""
if "```python" in text:
start = text.find("```python") + 9
end = text.find("```", start)
return text[start:end].strip()
elif "```" in text:
start = text.find("```") + 3
end = text.find("```", start)
return text[start:end].strip()
else:
return text.strip()
def safe_execute_code(code, df):
"""Wykonaj kod bezpiecznie z szczegółowymi logami"""
# Logi wykonania
st.subheader("🔍 Logi wykonania:")
log_container = st.container()
with log_container:
st.write("**Krok 1:** Sprawdzanie bezpieczeństwa kodu...")
# Usuń problematyczne linijki
problematic_patterns = ['read_csv', 'pd.read', 'pandas.read', 'to_csv', 'read_excel', 'open(', 'file(']
lines = code.split('\n')
clean_lines = []
removed_lines = []
for line in lines:
if not any(pattern in line for pattern in problematic_patterns):
clean_lines.append(line)
else:
removed_lines.append(line.strip())
if removed_lines:
st.warning(f"🛡️ Usunięto {len(removed_lines)} potencjalnie niebezpiecznych linijek:")
for line in removed_lines:
st.code(line, language='python')
else:
st.success("✅ Kod jest bezpieczny")
cleaned_code = '\n'.join(clean_lines)
st.write("**Krok 2:** Przygotowanie środowiska wykonania...")
# Bezpieczne środowisko z matplotlib
safe_globals = {
'pd': pd,
'np': np,
'plt': plt,
'df': df,
'len': len,
'sum': sum,
'max': max,
'min': min,
'abs': abs,
'round': round,
'str': str,
'int': int,
'float': float,
'list': list,
'dict': dict,
'sorted': sorted,
'enumerate': enumerate,
'range': range
}
st.success(f"✅ Dostępne moduły: pd, np, plt, df")
st.write("**Krok 3:** Wykonywanie kodu...")
safe_locals = {'result': None, 'fig': None}
try:
# Wykonaj cały kod naraz (bezpieczniej dla wielolinijkowych struktur)
progress_bar = st.progress(0)
progress_bar.progress(0.3)
# Spróbuj wykonać cały kod
exec(cleaned_code, safe_globals, safe_locals)
progress_bar.progress(1.0)
st.success("✅ Kod wykonany pomyślnie")
# Sprawdź co zostało utworzone
result = safe_locals.get('result')
fig = safe_locals.get('fig')
st.write("**Krok 4:** Sprawdzenie wyników...")
if result is not None:
result_type = type(result).__name__
if hasattr(result, 'shape'):
st.info(f"📊 Wynik: {result_type} o rozmiarze {result.shape}")
else:
st.info(f"📊 Wynik: {result_type}")
else:
st.warning("⚠️ Brak wyniku w zmiennej 'result'")
if fig is not None:
st.success("📈 Wykres został utworzony")
# Sprawdź czy matplotlib ma aktywne figury
if plt.get_fignums():
if fig is None:
fig = plt.gcf() # Pobierz aktualną figurę
st.info("📈 Wykrywam wykres matplotlib")
return result, fig, None
except Exception as e:
st.error(f"❌ Błąd wykonania: {str(e)}")
return None, None, str(e)
# Sidebar
with st.sidebar:
st.header("⚙️ Konfiguracja")
api_key = st.text_input("🔑 DeepSeek API Key", type="password")
st.header("📁 Upload pliku")
uploaded_file = st.file_uploader("Wybierz plik CSV", type=['csv', 'txt'])
if uploaded_file:
st.info("📋 Aplikacja automatycznie wykryje format pliku")
# Opcja ręcznego wyboru
manual_override = st.checkbox("🔧 Ręczne ustawienia (zaawansowane)")
if manual_override:
manual_sep = st.selectbox("Separator", [';', ',', '\t', '|'])
manual_enc = st.selectbox("Kodowanie", ['utf-8', 'cp1250', 'latin1'])
st.session_state.manual_config = {
'separator': manual_sep,
'encoding': manual_enc,
'columns': 0,
'quality': 0,
'sample_df': None,
'column_names': []
}
# Historia i szybkie pytania
if st.session_state.df is not None:
st.header("📊 Szybkie pytania")
# Podstawowe pytania
if st.button("📏 Ile wierszy?"):
st.session_state.quick_question = "Ile jest wierszy w danych?"
if st.button("📋 Jakie kolumny?"):
st.session_state.quick_question = "Pokaż nazwy wszystkich kolumn"
if st.button("🔢 Statystyki"):
st.session_state.quick_question = "Pokaż podstawowe statystyki numeryczne"
# Szybkie wykresy
st.subheader("📈 Szybkie wykresy")
numeric_cols = st.session_state.df.select_dtypes(include=[np.number]).columns
categorical_cols = st.session_state.df.select_dtypes(include=['object']).columns
if len(numeric_cols) > 0:
if st.button("📊 Histogram pierwszej kolumny"):
first_numeric = numeric_cols[0]
st.session_state.quick_question = f"Stwórz histogram dla kolumny {first_numeric}"
if len(categorical_cols) > 0:
if st.button("📈 Wykres kategorii"):
first_categorical = categorical_cols[0]
st.session_state.quick_question = f"Stwórz wykres słupkowy pokazujący ile jest każdej kategorii w kolumnie {first_categorical}"
if len(numeric_cols) >= 2:
if st.button("🔍 Scatter plot"):
col1, col2 = numeric_cols[0], numeric_cols[1]
st.session_state.quick_question = f"Stwórz scatter plot dla kolumn {col1} i {col2}"
# Historia
if st.session_state.analysis_history:
st.header("📜 Historia")
st.write(f"Wykonanych analiz: **{len(st.session_state.analysis_history)}**")
# Ostatnie pytanie z info o wykresie
if st.session_state.analysis_history:
last = st.session_state.analysis_history[-1]
st.write("**Ostatnie pytanie:**")
st.write(f"_{last['question'][:50]}..._")
if last.get('has_plot', False):
st.write("📈 *Zawierało wykres*")
# Statystyki historii
total_plots = sum(1 for analysis in st.session_state.analysis_history if analysis.get('has_plot', False))
st.write(f"**Wykresy utworzone:** {total_plots}")
# Najczęstsze słowa kluczowe
all_questions = [analysis['question'].lower() for analysis in st.session_state.analysis_history]
common_words = []
for question in all_questions:
if 'balans' in question: common_words.append('balans')
if 'wykres' in question or 'histogram' in question: common_words.append('wykresy')
if 'średnia' in question: common_words.append('średnie')
if 'top' in question: common_words.append('rankingi')
if common_words:
unique_words = list(set(common_words))
st.write(f"**Popularne tematy:** {', '.join(unique_words)}")
# Main interface
col1, col2 = st.columns([3, 1])
with col1:
if uploaded_file and st.button("🚀 WCZYTAJ I ANALIZUJ PLIK", type="primary"):
with st.spinner("🔍 Analizuję format pliku..."):
# Sprawdź czy użytkownik wybrał ręczne ustawienia
if hasattr(st.session_state, 'manual_config') and st.session_state.get('manual_override', False):
config = st.session_state.manual_config
st.info("🔧 Używam ręcznych ustawień")
else:
# Automatyczne wykrywanie
config, error = smart_csv_detection(uploaded_file)
if error:
st.error(f"❌ {error}")
st.stop()
# Wczytaj pełny plik
with st.spinner("📊 Wczytuję dane..."):
df, error = load_csv_with_config(uploaded_file, config)
if error:
st.error(f"❌ {error}")
st.stop()
st.session_state.df = df
st.session_state.file_info = config
# Pokaż wyniki
st.success(f"✅ SUKCES! Wczytano {df.shape[0]:,} wierszy × {df.shape[1]} kolumn")
# Info o kolumnach
st.write("📋 **Kolumny:**")
col_info = []
for i, col in enumerate(df.columns):
dtype = str(df[col].dtype)
non_null = df[col].count()
col_info.append(f"{i+1}. **{col}** ({dtype}) - {non_null:,}/{len(df):,} wartości")
# Pokaż kolumny w kolumnach dla czytelności
col_chunks = [col_info[i:i+3] for i in range(0, len(col_info), 3)]
for chunk in col_chunks:
cols = st.columns(len(chunk))
for i, info in enumerate(chunk):
cols[i].write(info)
# Podgląd danych
st.write("📄 **Podgląd danych:**")
safe_display_data(df)
# Sekcja analizy
if st.session_state.df is not None:
st.header("🔍 Analiza danych")
df = st.session_state.df
# Pokaż historię analiz jeśli są
if st.session_state.analysis_history:
with st.expander(f"📜 Historia analiz ({len(st.session_state.analysis_history)})", expanded=False):
for i, analysis in enumerate(reversed(st.session_state.analysis_history[-5:])): # Ostatnie 5
idx = len(st.session_state.analysis_history) - i
st.write(f"**{idx}. {analysis['question']}**")
# Ikona wykresu jeśli był
if analysis.get('has_plot', False):
st.write("📈 *Zawierał wykres*")
# Krótki wynik
result_preview = str(analysis['result'])[:100]
if len(str(analysis['result'])) > 100:
result_preview += "..."
st.write(f"Wynik: {result_preview}")
# Timestamp
if 'timestamp' in analysis:
st.caption(f"⏰ {analysis['timestamp'].strftime('%H:%M:%S')}")
st.write("---")
# Formularz do pytań
with st.form("analysis_form", clear_on_submit=True):
# Sprawdź czy jest szybkie pytanie
default_question = ""
if hasattr(st.session_state, 'quick_question') and st.session_state.quick_question:
default_question = st.session_state.quick_question
st.session_state.quick_question = ""
question = st.text_area(
"💬 Zadaj pytanie o dane:",
height=100,
placeholder="np. Histogram balansu, Top 10 zawodów, Rozkład wieku klientów",
key=f"question_input_{st.session_state.questions_count}",
value=default_question
)
col_btn1, col_btn2 = st.columns(2)
with col_btn1:
submit_button = st.form_submit_button("🧠 ANALIZUJ", type="primary")
with col_btn2:
clear_history = st.form_submit_button("🗑️ Wyczyść historię")
# Wyczyść historię jeśli kliknięto
if clear_history:
st.session_state.analysis_history = []
st.success("✅ Historia wyczyszczona")
st.rerun()
# Przetwórz pytanie
if submit_button and api_key and question.strip():
st.session_state.questions_count += 1
# Przygotuj informacje o danych
columns_info = f"Kolumny: {', '.join(df.columns)}"
dtypes_info = f"Typy danych: {dict(df.dtypes)}"
sample_data = []
for col in df.columns[:8]: # Pierwsze 8 kolumn
try:
if df[col].dtype in ['object', 'string']:
unique_vals = df[col].dropna().unique()[:3]
sample_data.append(f"{col}: {list(unique_vals)}")
else:
stats = f"min={df[col].min()}, max={df[col].max()}, średnia={df[col].mean():.2f}"
sample_data.append(f"{col}: {stats}")
except:
sample_data.append(f"{col}: [błąd próbkowania]")
sample_info = "Przykładowe dane: " + " | ".join(sample_data)
# Prompt dla AI z zachętą do tworzenia wykresów
prompt = f"""
Odpowiedz na pytanie o dane używając kodu Python. WAŻNE: Jeśli pytanie może być lepiej zobrazowane wykresem, stwórz go!
PYTANIE: {question}
INFORMACJE O DANYCH:
{columns_info}
{dtypes_info}
Rozmiar: {df.shape[0]} wierszy × {df.shape[1]} kolumn
{sample_info}
ZASADY:
1. DataFrame nazywa się 'df' i jest już załadowany
2. NIE używaj pd.read_csv(), pd.read_excel() ani podobnych
3. Wynik zapisz w zmiennej 'result'
4. Używaj pandas, numpy, matplotlib (plt)
5. Kod ma być prosty i skuteczny
6. WYKRES: Jeśli pytanie dotyczy rozkładów, trendów, porównań - ZAWSZE utwórz wykres w zmiennej 'fig'
KIEDY TWORZYĆ WYKRESY:
- Rozkłady wartości → histogram: plt.hist()
- Porównania kategorii → wykres słupkowy: plt.bar()
- Trendy w czasie → wykres liniowy: plt.plot()
- Korelacje → scatter plot: plt.scatter()
- Top N → wykres słupkowy
- Statystyki grupowe → wykres słupkowy lub pudełkowy
PRZYKŁADY KODÓW Z WYKRESAMI:
# Histogram rozkładu
result = df['balance'].describe()
fig, ax = plt.subplots(figsize=(10, 6))
ax.hist(df['balance'], bins=30, alpha=0.7)
ax.set_title('Rozkład balansu klientów')
ax.set_xlabel('Balans')
ax.set_ylabel('Liczba klientów')
# Wykres słupkowy dla kategorii
result = df['job'].value_counts().head(10)
fig, ax = plt.subplots(figsize=(12, 6))
result.plot(kind='bar', ax=ax)
ax.set_title('Top 10 zawodów')
ax.set_xlabel('Zawód')
ax.set_ylabel('Liczba osób')
plt.xticks(rotation=45)
# Porównanie grup
result = df.groupby('job')['balance'].mean().head(10)
fig, ax = plt.subplots(figsize=(12, 6))
result.plot(kind='bar', ax=ax)
ax.set_title('Średni balans według zawodu')
ax.set_ylabel('Średni balans')
plt.xticks(rotation=45)
PRZYKŁADY BEZ WYKRESÓW (tylko liczby):
- Liczba wierszy: result = len(df)
- Średnia: result = df['kolumna'].mean()
- Filtrowanie: result = len(df[df['kolumna'] > 100])
Zwróć TYLKO kod Python (bez wyjaśnień):
"""
# Zapytaj AI
with st.spinner("🤖 AI generuje kod..."):
st.info("🔄 Wysyłam zapytanie do DeepSeek API...")
ai_response = simple_query_ai(prompt, api_key)
if ai_response:
st.success("✅ Otrzymano odpowiedź od AI")
code = extract_python_code(ai_response)
st.subheader("🔧 Wygenerowany kod:")
st.code(code, language='python')
# Wykonaj z logowaniem
with st.spinner("⚡ Wykonuję analizę..."):
result, fig, error = safe_execute_code(code, df)
# Wyniki
if error:
st.error(f"❌ Błąd końcowy: {error}")
st.write("**Możliwe rozwiązania:**")
st.write("- Sprawdź nazwy kolumn")
st.write("- Upewnij się że kolumna zawiera dane liczbowe")
st.write("- Spróbuj prostsze pytanie")
else:
st.subheader("📊 WYNIK:")
if result is not None:
if isinstance(result, (int, float)):
st.metric("Wynik", f"{result:,}")
elif isinstance(result, str):
st.write(f"**{result}**")
elif isinstance(result, pd.DataFrame):
st.write("**Tabela wyników:**")
safe_display_data(result, max_rows=20)
elif isinstance(result, pd.Series):
st.write("**Serie danych:**")
safe_display_data(result.to_frame(), max_rows=20)
else:
st.write("**Wynik:**")
st.text(str(result)[:1000])
# WYKRESY - ulepszone wyświetlanie
if fig is not None:
st.subheader("📈 Wykres:")
try:
plt.tight_layout()
st.pyplot(fig, use_container_width=True)
st.success("✅ Wykres wyświetlony pomyślnie")
except Exception as plot_error:
st.error(f"❌ Błąd wyświetlania wykresu: {plot_error}")
finally:
plt.close(fig)
elif any(word in question.lower() for word in ['wykres', 'histogram', 'rozkład', 'porównaj', 'pokaż', 'wizualiz']):
st.info("💡 Zapytanie sugeruje wykres, ale AI go nie utworzyło. Spróbuj poprosić bezpośrednio o wykres.")
# INTERPRETACJA AI
st.subheader("🧠 Interpretacja AI:")
with st.spinner("🔮 AI interpretuje wyniki..."):
interpretation = generate_interpretation(question, result, code, api_key)
st.markdown(interpretation)
# Zapisz do historii
analysis_record = {
'question': question,
'code': code,
'result': result,
'interpretation': interpretation,
'timestamp': pd.Timestamp.now(),
'has_plot': fig is not None
}
st.session_state.analysis_history.append(analysis_record)
# Zachęć do kolejnych pytań
st.markdown("---")
st.success("✅ Analiza zakończona! Możesz zadać kolejne pytanie powyżej.")
# Sugestie kolejnych pytań na podstawie wyniku
if isinstance(result, pd.DataFrame) and len(result) > 1:
st.info("💡 Sugestia: Możesz zapytać o wykres dla tych wyników")
elif isinstance(result, (int, float)) and 'balans' in question.lower():
st.info("💡 Sugestia: Spróbuj 'Pokaż histogram balansu' lub 'Rozkład balansu klientów'")
elif 'top' in question.lower() or 'najwyższ' in question.lower():
st.info("💡 Sugestia: Możesz poprosić o wykres słupkowy dla tych wyników")
else:
st.error("❌ Nie udało się uzyskać odpowiedzi od AI")
st.write("**Możliwe przyczyny:**")
st.write("- Błąd klucza API")
st.write("- Problem z połączeniem internetowym")
st.write("- Przeciążenie serwera DeepSeek")
elif submit_button and not api_key:
st.warning("⚠️ Wprowadź klucz API DeepSeek w lewym panelu")
elif submit_button and not question.strip():
st.warning("⚠️ Wprowadź pytanie do analizy")
with col2:
st.header("📊 Informacje")
if st.session_state.df is not None:
df = st.session_state.df
# Podstawowe metryki
st.metric("📊 Wiersze", f"{len(df):,}")
st.metric("📋 Kolumny", len(df.columns))
# Informacje o pliku
if st.session_state.file_info:
st.subheader("📁 Format pliku")
info = st.session_state.file_info
st.write(f"**Separator:** `{info['separator']}`")
st.write(f"**Kodowanie:** {info['encoding']}")
# Typy danych
st.subheader("🏷️ Typy kolumn")
type_counts = df.dtypes.value_counts()
for dtype, count in type_counts.items():
st.write(f"**{str(dtype)}:** {count}")
# Braki danych
missing_data = df.isnull().sum()
missing_cols = missing_data[missing_data > 0]
if len(missing_cols) > 0:
st.subheader("⚠️ Braki danych")
for col, missing in missing_cols.items():
pct = (missing / len(df)) * 100
st.write(f"**{col}:** {missing:,} ({pct:.1f}%)")
else:
st.success("✅ Brak braków danych")
# Kolumny numeryczne - podstawowe statystyki
numeric_cols = df.select_dtypes(include=[np.number]).columns
if len(numeric_cols) > 0:
st.subheader("🔢 Statystyki numeryczne")
for col in numeric_cols[:5]:
try:
mean_val = df[col].mean()
min_val = df[col].min()
max_val = df[col].max()
st.write(f"**{col}:**")
st.write(f" Średnia: {mean_val:.2f}")
st.write(f" Zakres: {min_val:.2f} - {max_val:.2f}")
except:
st.write(f"**{col}:** błąd obliczeń")
else:
st.info("👆 Wczytaj plik CSV aby zobaczyć statystyki")
# Footer
st.markdown("---")
st.markdown("🔧 **Funkcje:**")
col_f1, col_f2 = st.columns(2)
with col_f1:
st.markdown("• **Automatyczne wykrywanie** formatu CSV")
st.markdown("• **Obsługa separatorów:** `;` `,` `tab` `|`")
st.markdown("• **Różne kodowania:** UTF-8, CP1250, Latin1")
st.markdown("• **Inteligentne wyświetlanie** (omija błędy)")
st.markdown("• **Szczegółowe logi** wykonania kodu")
with col_f2:
st.markdown("• **Interpretacja AI** wyników analizy")
st.markdown("• **Historia pytań** w sesji")
st.markdown("• **Automatyczne wykresy** (histogram, bar, scatter)")
st.markdown("• **Szybkie wykresy** jednym kliknięciem")
st.markdown("• **Ciągłość analizy** - kolejne pytania")
st.info("💡 **Tip:** Pytaj o 'histogram', 'wykres', 'rozkład', 'porównanie' - AI automatycznie utworzy odpowiedni wykres!")
# Debug info
if st.session_state.analysis_history:
plots_count = sum(1 for analysis in st.session_state.analysis_history if analysis.get('has_plot', False))
st.caption(f"🎯 W tej sesji: {len(st.session_state.analysis_history)} analiz, {plots_count} wykresów")