Initial commit: Eurojackpot analysis and prediction system

This repository contains a comprehensive Eurojackpot lottery analysis and prediction system including:
- Historical data analysis and processing
- ML-based prediction models
- Automated weekly tip generation
- Position and range analysis tools
- Notification system for results

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
cbazzaandClaude Sonnet 4.5 committed 2025-12-16 15:53:01 +01:00
commit 70e0638dee
67 files changed
+8491869

No files matched your search

@@ -0,0 +1,188 @@
#!/usr/bin/env python3
"""
Eurojackpot Bereichskombinationen-Analyse
Analysiert die Kombinationen von Zahlenbereichen in den gezogenen 5er-Kombinationen.
Focus auf Z1-Z4 wie gewünscht.
"""
import pandas as pd
from collections import Counter, defaultdict
def get_range_for_number(number):
"""Bestimmt den Bereich für eine gegebene Zahl."""
if 1 <= number <= 10:
return 'A(1-10)'
elif 11 <= number <= 20:
return 'B(11-20)'
elif 21 <= number <= 30:
return 'C(21-30)'
elif 31 <= number <= 40:
return 'D(31-40)'
elif 41 <= number <= 50:
return 'E(41-50)'
else:
return 'Unknown'
def analyze_range_combinations():
"""Analysiert Bereichskombinationen in Eurojackpot-Ziehungen."""
# Daten laden
df = pd.read_csv("/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/data/AlleEurojackpotzahlen.csv", sep=';')
print(f"🎲 EUROJACKPOT BEREICHSKOMBINATIONEN-ANALYSE")
print(f"Anzahl analysierte Ziehungen: {len(df)}")
print("="*60)
# Analyse für Z1-Z4 (wie gewünscht)
z_columns_z1_z4 = ['Z1', 'Z2', 'Z3', 'Z4']
# Alle Kombinationen sammeln
combinations_z1_z4 = []
range_patterns_z1_z4 = []
for _, row in df.iterrows():
# Bereiche für Z1-Z4 bestimmen
ranges = [get_range_for_number(row[col]) for col in z_columns_z1_z4]
range_pattern = '|'.join(sorted(ranges)) # Sortiert für einheitliche Muster
combinations_z1_z4.append(tuple(ranges))
range_patterns_z1_z4.append(range_pattern)
# Auch vollständige Z1-Z5 Analyse
z_columns_all = ['Z1', 'Z2', 'Z3', 'Z4', 'Z5']
combinations_all = []
range_patterns_all = []
for _, row in df.iterrows():
ranges = [get_range_for_number(row[col]) for col in z_columns_all]
range_pattern = '|'.join(sorted(ranges))
combinations_all.append(tuple(ranges))
range_patterns_all.append(range_pattern)
# Häufigkeitsanalyse
print("\n📊 ANALYSE Z1-Z4 (4 Zahlen):")
print("="*40)
pattern_counts_z1_z4 = Counter(range_patterns_z1_z4)
print(f"Häufigste Bereichskombinationen (Z1-Z4):")
for i, (pattern, count) in enumerate(pattern_counts_z1_z4.most_common(15), 1):
percentage = (count / len(df)) * 100
print(f"{i:2}. {pattern:30} {count:3}x ({percentage:4.1f}%)")
print(f"\n📊 VERGLEICH: ANALYSE Z1-Z5 (alle 5 Zahlen):")
print("="*40)
pattern_counts_all = Counter(range_patterns_all)
print(f"Häufigste Bereichskombinationen (Z1-Z5):")
for i, (pattern, count) in enumerate(pattern_counts_all.most_common(15), 1):
percentage = (count / len(df)) * 100
print(f"{i:2}. {pattern:35} {count:3}x ({percentage:4.1f}%)")
# Analyse der Bereichsverteilung in Kombinationen
print(f"\n🎯 BEREICHSVERTEILUNG IN KOMBINATIONEN:")
print("="*50)
# Wie oft kommt jeder Bereich in Z1-Z4 vor?
range_in_combination_counts = defaultdict(int)
for combination in combinations_z1_z4:
for range_name in set(combination): # set() um Duplikate zu vermeiden
range_in_combination_counts[range_name] += 1
print("Häufigkeit der Bereiche in Z1-Z4 Kombinationen:")
for range_name in ['A(1-10)', 'B(11-20)', 'C(21-30)', 'D(31-40)', 'E(41-50)']:
count = range_in_combination_counts[range_name]
percentage = (count / len(df)) * 100
print(f"{range_name}: {count:3} Kombinationen ({percentage:4.1f}%)")
# Analyse: Wie viele verschiedene Bereiche pro Kombination?
print(f"\n📈 BEREICHSVIELFALT PRO KOMBINATION (Z1-Z4):")
print("="*45)
diversity_counts = defaultdict(int)
for combination in combinations_z1_z4:
unique_ranges = len(set(combination))
diversity_counts[unique_ranges] += 1
for num_ranges in sorted(diversity_counts.keys()):
count = diversity_counts[num_ranges]
percentage = (count / len(df)) * 100
print(f"{num_ranges} verschiedene Bereiche: {count:3} Kombinationen ({percentage:4.1f}%)")
# Spezielle Muster
print(f"\n🔍 SPEZIELLE MUSTER (Z1-Z4):")
print("="*35)
# Alle aus dem gleichen Bereich
same_range_count = sum(1 for combo in combinations_z1_z4 if len(set(combo)) == 1)
print(f"Alle 4 Zahlen aus gleichem Bereich: {same_range_count} ({(same_range_count/len(df)*100):.1f}%)")
# Alle aus verschiedenen Bereichen (4 verschiedene)
all_different_count = sum(1 for combo in combinations_z1_z4 if len(set(combo)) == 4)
print(f"Alle 4 Zahlen aus verschiedenen Bereichen: {all_different_count} ({(all_different_count/len(df)*100):.1f}%)")
# Benachbarte Bereiche-Analyse
print(f"\n🏠 BENACHBARTE BEREICHE-ANALYSE (Z1-Z4):")
print("="*40)
adjacent_patterns = {
'A+B': 0, # 1-10 + 11-20
'B+C': 0, # 11-20 + 21-30
'C+D': 0, # 21-30 + 31-40
'D+E': 0, # 31-40 + 41-50
}
for combination in combinations_z1_z4:
ranges_set = set(combination)
if 'A(1-10)' in ranges_set and 'B(11-20)' in ranges_set:
adjacent_patterns['A+B'] += 1
if 'B(11-20)' in ranges_set and 'C(21-30)' in ranges_set:
adjacent_patterns['B+C'] += 1
if 'C(21-30)' in ranges_set and 'D(31-40)' in ranges_set:
adjacent_patterns['C+D'] += 1
if 'D(31-40)' in ranges_set and 'E(41-50)' in ranges_set:
adjacent_patterns['D+E'] += 1
for pattern, count in adjacent_patterns.items():
percentage = (count / len(df)) * 100
print(f"Benachbarte Bereiche {pattern}: {count:3} Kombinationen ({percentage:4.1f}%)")
# Export der Ergebnisse
print(f"\n💾 EXPORT DER ERGEBNISSE:")
print("="*30)
# DataFrame für Z1-Z4 Kombinationen erstellen
results_data = []
for i, row in df.iterrows():
ranges_z1_z4 = [get_range_for_number(row[col]) for col in z_columns_z1_z4]
pattern = '|'.join(sorted(ranges_z1_z4))
diversity = len(set(ranges_z1_z4))
results_data.append({
'datum': row['datum'],
'Z1': row['Z1'],
'Z2': row['Z2'],
'Z3': row['Z3'],
'Z4': row['Z4'],
'Z1_bereich': ranges_z1_z4[0],
'Z2_bereich': ranges_z1_z4[1],
'Z3_bereich': ranges_z1_z4[2],
'Z4_bereich': ranges_z1_z4[3],
'bereichsmuster': pattern,
'anzahl_bereiche': diversity
})
results_df = pd.DataFrame(results_data)
output_file = "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/bereichskombinationen_z1_z4.csv"
results_df.to_csv(output_file, sep=';', index=False)
print(f"✅ Detailergebnisse gespeichert: bereichskombinationen_z1_z4.csv")
return pattern_counts_z1_z4, pattern_counts_all
if __name__ == "__main__":
analyze_range_combinations()
@@ -0,0 +1,217 @@
#!/usr/bin/env python3
"""
Eurojackpot Bereichsanalyse
Analysiert, in welchen Zahlenbereichen am häufigsten Zahlen gezogen wurden.
"""
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from collections import Counter
def load_drawn_numbers(filepath):
"""Lädt die gezogenen Eurojackpot-Zahlen."""
try:
df = pd.read_csv(filepath, sep=';')
print(f"Gezogene Zahlen geladen: {len(df)} Ziehungen")
print(f"Spalten: {list(df.columns)}")
return df
except Exception as e:
print(f"Fehler beim Laden: {e}")
return None
def analyze_number_ranges(df):
"""Analysiert die Häufigkeit von Zahlen in verschiedenen Bereichen."""
# Alle gezogenen Zahlen sammeln (Z1-Z5)
all_numbers = []
for col in ['Z1', 'Z2', 'Z3', 'Z4', 'Z5']:
all_numbers.extend(df[col].tolist())
print(f"Gesamtanzahl gezogene Zahlen: {len(all_numbers)}")
# Häufigkeit jeder Zahl
number_counts = Counter(all_numbers)
# Bereiche definieren
ranges = {
'1-10': (1, 10),
'11-20': (11, 20),
'21-30': (21, 30),
'31-40': (31, 40),
'41-50': (41, 50)
}
# Analyse pro Bereich
range_analysis = {}
for range_name, (start, end) in ranges.items():
numbers_in_range = [num for num in all_numbers if start <= num <= end]
range_analysis[range_name] = {
'anzahl_ziehungen': len(numbers_in_range),
'prozent': (len(numbers_in_range) / len(all_numbers)) * 100,
'haeufigste_zahl': max(number_counts.items(),
key=lambda x: x[1] if start <= x[0] <= end else 0),
'durchschnitt': np.mean(numbers_in_range) if numbers_in_range else 0,
'zahlen_im_bereich': sorted(set(numbers_in_range))
}
return range_analysis, number_counts
def create_visualizations(range_analysis, number_counts, output_dir):
"""Erstellt Visualisierungen der Analyse."""
# 1. Balkendiagramm: Häufigkeit pro Bereich
plt.figure(figsize=(12, 8))
ranges = list(range_analysis.keys())
counts = [range_analysis[r]['anzahl_ziehungen'] for r in ranges]
percentages = [range_analysis[r]['prozent'] for r in ranges]
plt.subplot(2, 2, 1)
bars = plt.bar(ranges, counts, color=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FECA57'])
plt.title('Anzahl gezogener Zahlen pro Bereich', fontsize=14, fontweight='bold')
plt.ylabel('Anzahl Ziehungen')
plt.xticks(rotation=45)
# Prozente auf Balken anzeigen
for bar, pct in zip(bars, percentages):
plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 5,
f'{pct:.1f}%', ha='center', va='bottom', fontweight='bold')
# 2. Heatmap: Häufigkeit einzelner Zahlen
plt.subplot(2, 2, 2)
numbers = list(range(1, 51))
frequencies = [number_counts.get(num, 0) for num in numbers]
# Als 5x10 Matrix darstellen
freq_matrix = np.array(frequencies).reshape(5, 10)
sns.heatmap(freq_matrix, annot=True, fmt='d', cmap='YlOrRd',
xticklabels=list(range(1, 11)),
yticklabels=[f'{i*10+1}-{(i+1)*10}' for i in range(5)])
plt.title('Häufigkeit einzelner Zahlen', fontsize=14, fontweight='bold')
# 3. Liniendiagramm: Häufigkeit aller Zahlen
plt.subplot(2, 2, 3)
plt.plot(numbers, frequencies, marker='o', linewidth=2, markersize=4)
plt.title('Häufigkeitsverteilung aller Zahlen (1-50)', fontsize=14, fontweight='bold')
plt.xlabel('Zahl')
plt.ylabel('Häufigkeit')
plt.grid(True, alpha=0.3)
# Bereiche farblich markieren
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FECA57']
for i, (range_name, color) in enumerate(zip(range_analysis.keys(), colors)):
start = i * 10 + 1
end = (i + 1) * 10
plt.axvspan(start, end, alpha=0.2, color=color, label=range_name)
plt.legend()
# 4. Pie Chart: Prozentuale Verteilung
plt.subplot(2, 2, 4)
plt.pie(percentages, labels=ranges, autopct='%1.1f%%',
colors=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FECA57'],
startangle=90)
plt.title('Prozentuale Verteilung nach Bereichen', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.savefig(f'{output_dir}/eurojackpot_bereichsanalyse.png', dpi=300, bbox_inches='tight')
plt.show()
def print_detailed_analysis(range_analysis, number_counts):
"""Gibt detaillierte Analyseergebnisse aus."""
print("\n" + "="*60)
print("🎯 EUROJACKPOT BEREICHSANALYSE")
print("="*60)
total_numbers = sum(analysis['anzahl_ziehungen'] for analysis in range_analysis.values())
for range_name, analysis in range_analysis.items():
print(f"\n📊 BEREICH {range_name}:")
print(f" Anzahl Ziehungen: {analysis['anzahl_ziehungen']:,}")
print(f" Prozentanteil: {analysis['prozent']:.2f}%")
print(f" Durchschnittswert: {analysis['durchschnitt']:.1f}")
# Top 3 Zahlen in diesem Bereich
start, end = map(int, range_name.split('-'))
range_numbers = [(num, count) for num, count in number_counts.items()
if start <= num <= end]
range_numbers.sort(key=lambda x: x[1], reverse=True)
print(f" Top 3 Zahlen: ", end="")
for i, (num, count) in enumerate(range_numbers[:3]):
print(f"{num} ({count}x)", end="")
if i < 2 and i < len(range_numbers) - 1:
print(", ", end="")
print()
# Allgemeine Statistiken
print(f"\n📈 ALLGEMEINE STATISTIKEN:")
print(f" Gesamte gezogene Zahlen: {total_numbers:,}")
print(f" Durchschnitt pro Bereich: {total_numbers/5:.1f}")
# Häufigste und seltenste Zahlen insgesamt
most_common = number_counts.most_common(5)
least_common = number_counts.most_common()[-5:]
print(f"\n🔥 HÄUFIGSTE ZAHLEN GESAMT:")
for i, (num, count) in enumerate(most_common, 1):
print(f" {i}. Zahl {num}: {count} mal gezogen")
print(f"\n❄️ SELTENSTE ZAHLEN GESAMT:")
for i, (num, count) in enumerate(reversed(least_common), 1):
print(f" {i}. Zahl {num}: {count} mal gezogen")
# Empfehlungen
print(f"\n💡 ERKENNTNISSE:")
best_range = max(range_analysis.keys(), key=lambda x: range_analysis[x]['prozent'])
worst_range = min(range_analysis.keys(), key=lambda x: range_analysis[x]['prozent'])
print(f" • Bester Bereich: {best_range} ({range_analysis[best_range]['prozent']:.1f}%)")
print(f" • Schwächster Bereich: {worst_range} ({range_analysis[worst_range]['prozent']:.1f}%)")
print(f" • Unterschied: {range_analysis[best_range]['prozent'] - range_analysis[worst_range]['prozent']:.1f} Prozentpunkte")
def main():
"""Hauptfunktion."""
# Pfade
input_file = "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/data/AlleEurojackpotzahlen.csv"
output_dir = "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot"
# Daten laden
print("🔄 Lade Eurojackpot-Daten...")
df = load_drawn_numbers(input_file)
if df is None:
print("❌ Fehler beim Laden der Daten!")
return
# Analyse durchführen
print("\n🔍 Führe Bereichsanalyse durch...")
range_analysis, number_counts = analyze_number_ranges(df)
# Ergebnisse ausgeben
print_detailed_analysis(range_analysis, number_counts)
# Visualisierungen erstellen
print(f"\n📊 Erstelle Visualisierungen...")
try:
create_visualizations(range_analysis, number_counts, output_dir)
print(f"✅ Diagramm gespeichert: {output_dir}/eurojackpot_bereichsanalyse.png")
except Exception as e:
print(f"⚠️ Visualisierung konnte nicht erstellt werden: {e}")
print("💡 Installieren Sie matplotlib und seaborn: pip install matplotlib seaborn")
# CSV-Export der Analyse
results_df = pd.DataFrame.from_dict(range_analysis, orient='index')
results_df.to_csv(f'{output_dir}/bereichsanalyse_ergebnisse.csv', sep=';')
print(f"✅ Ergebnisse gespeichert: {output_dir}/bereichsanalyse_ergebnisse.csv")
if __name__ == "__main__":
main()
+208
View File
@@ -0,0 +1,208 @@
#!/usr/bin/env python3
"""
Eurojackpot Positionsanalyse
Analysiert, welche Bereiche an welchen Positionen (Z1, Z2, Z3, Z4, Z5) am häufigsten stehen.
"""
import pandas as pd
from collections import defaultdict
import numpy as np
def get_range_for_number(number):
"""Bestimmt den Bereich für eine gegebene Zahl."""
if 1 <= number <= 10:
return 'A(1-10)'
elif 11 <= number <= 20:
return 'B(11-20)'
elif 21 <= number <= 30:
return 'C(21-30)'
elif 31 <= number <= 40:
return 'D(31-40)'
elif 41 <= number <= 50:
return 'E(41-50)'
else:
return 'Unknown'
def analyze_positions():
"""Führt eine detaillierte Positionsanalyse durch."""
# Daten laden
df = pd.read_csv("/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/data/AlleEurojackpotzahlen.csv", sep=';')
print(f"🎯 EUROJACKPOT POSITIONSANALYSE")
print(f"Anzahl analysierte Ziehungen: {len(df)}")
print("="*60)
# Positionen definieren
positions = ['Z1', 'Z2', 'Z3', 'Z4', 'Z5']
ranges = ['A(1-10)', 'B(11-20)', 'C(21-30)', 'D(31-40)', 'E(41-50)']
# Matrix für Positions-Bereich-Kombinationen
position_range_matrix = defaultdict(lambda: defaultdict(int))
# Daten sammeln
for _, row in df.iterrows():
for pos in positions:
range_name = get_range_for_number(row[pos])
position_range_matrix[pos][range_name] += 1
# 1. Übersichtstabelle erstellen
print(f"\n📊 POSITIONS-BEREICH-MATRIX:")
print("="*60)
print(f"{'Position':<8} {'A(1-10)':<12} {'B(11-20)':<12} {'C(21-30)':<12} {'D(31-40)':<12} {'E(41-50)':<12}")
print("-" * 60)
for pos in positions:
line = f"{pos:<8} "
for range_name in ranges:
count = position_range_matrix[pos][range_name]
percentage = (count / len(df)) * 100
line += f"{count:3}({percentage:4.1f}%) "
print(line)
# 2. Beste Bereiche pro Position
print(f"\n🏆 BESTE BEREICHE PRO POSITION:")
print("="*40)
for pos in positions:
best_range = max(ranges, key=lambda r: position_range_matrix[pos][r])
best_count = position_range_matrix[pos][best_range]
best_percentage = (best_count / len(df)) * 100
# Alle Bereiche für diese Position sortiert
sorted_ranges = sorted(ranges, key=lambda r: position_range_matrix[pos][r], reverse=True)
print(f"\n{pos}: {best_range} führt mit {best_count} ({best_percentage:.1f}%)")
print(f" Ranking: ", end="")
for i, range_name in enumerate(sorted_ranges, 1):
count = position_range_matrix[pos][range_name]
percentage = (count / len(df)) * 100
print(f"{i}.{range_name}({percentage:.1f}%)", end="")
if i < len(sorted_ranges):
print(" > ", end="")
print()
# 3. Beste Positionen pro Bereich
print(f"\n🎯 BESTE POSITIONEN PRO BEREICH:")
print("="*40)
for range_name in ranges:
best_position = max(positions, key=lambda p: position_range_matrix[p][range_name])
best_count = position_range_matrix[best_position][range_name]
best_percentage = (best_count / len(df)) * 100
# Alle Positionen für diesen Bereich sortiert
sorted_positions = sorted(positions, key=lambda p: position_range_matrix[p][range_name], reverse=True)
print(f"\n{range_name}: Position {best_position} führt mit {best_count} ({best_percentage:.1f}%)")
print(f" Ranking: ", end="")
for i, pos in enumerate(sorted_positions, 1):
count = position_range_matrix[pos][range_name]
percentage = (count / len(df)) * 100
print(f"{i}.{pos}({percentage:.1f}%)", end="")
if i < len(sorted_positions):
print(" > ", end="")
print()
# 4. Spezielle Analysen
print(f"\n📈 SPEZIELLE POSITIONSANALYSEN:")
print("="*45)
# Niedrige vs. hohe Bereiche pro Position
for pos in positions:
low_ranges = position_range_matrix[pos]['A(1-10)'] + position_range_matrix[pos]['B(11-20)']
high_ranges = position_range_matrix[pos]['D(31-40)'] + position_range_matrix[pos]['E(41-50)']
middle_range = position_range_matrix[pos]['C(21-30)']
low_pct = (low_ranges / len(df)) * 100
high_pct = (high_ranges / len(df)) * 100
middle_pct = (middle_range / len(df)) * 100
print(f"{pos}: Niedrig(A+B)={low_pct:4.1f}% | Mitte(C)={middle_pct:4.1f}% | Hoch(D+E)={high_pct:4.1f}%")
# 5. Gleichverteilungs-Analyse
print(f"\n⚖️ GLEICHVERTEILUNGS-ANALYSE:")
print("="*35)
expected_per_range = len(df) / 5 # Erwartete Gleichverteilung
print(f"Erwartete Gleichverteilung pro Bereich und Position: {expected_per_range:.1f}")
print(f"\nAbweichungen von der Gleichverteilung:")
for pos in positions:
print(f"\n{pos}:")
for range_name in ranges:
actual = position_range_matrix[pos][range_name]
deviation = actual - expected_per_range
deviation_pct = (deviation / expected_per_range) * 100
symbol = "📈" if deviation > 0 else "📉" if deviation < 0 else "⚖️"
print(f" {range_name}: {actual:3} ({deviation:+4.1f}, {deviation_pct:+5.1f}%) {symbol}")
# 6. Heatmap-Daten für Export
print(f"\n💾 DATENEXPORT:")
print("="*20)
# Erstelle eine Matrix für bessere Visualisierung
matrix_data = []
for pos in positions:
row_data = {'Position': pos}
for range_name in ranges:
count = position_range_matrix[pos][range_name]
percentage = (count / len(df)) * 100
row_data[range_name] = count
row_data[f"{range_name}_Prozent"] = percentage
matrix_data.append(row_data)
matrix_df = pd.DataFrame(matrix_data)
# Zusätzliche Statistiken hinzufügen
stats_data = []
for _, row in df.iterrows():
row_data = {'datum': row['datum']}
for pos in positions:
row_data[f"{pos}_Zahl"] = row[pos]
row_data[f"{pos}_Bereich"] = get_range_for_number(row[pos])
stats_data.append(row_data)
stats_df = pd.DataFrame(stats_data)
# Export
matrix_output = "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/positions_matrix.csv"
stats_output = "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/positions_details.csv"
matrix_df.to_csv(matrix_output, sep=';', index=False)
stats_df.to_csv(stats_output, sep=';', index=False)
print(f"✅ Positionsmatrix gespeichert: positions_matrix.csv")
print(f"✅ Detaildaten gespeichert: positions_details.csv")
# 7. Strategische Empfehlungen
print(f"\n💡 STRATEGISCHE EMPFEHLUNGEN:")
print("="*35)
# Finde die besten Kombinationen pro Position
recommendations = {}
for pos in positions:
best_range = max(ranges, key=lambda r: position_range_matrix[pos][r])
recommendations[pos] = best_range
print(f"Optimale Bereichsauswahl pro Position:")
for pos, best_range in recommendations.items():
count = position_range_matrix[pos][best_range]
percentage = (count / len(df)) * 100
print(f" {pos}: {best_range} ({percentage:.1f}%)")
# Berechne theoretische Erfolgswahrscheinlichkeit
theoretical_success = 1.0
for pos, best_range in recommendations.items():
prob = position_range_matrix[pos][best_range] / len(df)
theoretical_success *= prob
print(f"\nTheoretische Erfolgswahrscheinlichkeit dieser Kombination: {theoretical_success*100:.4f}%")
return position_range_matrix, matrix_df
if __name__ == "__main__":
analyze_positions()
@@ -0,0 +1,54 @@
#!/usr/bin/env python3
"""
Einfache Eurojackpot Bereichsanalyse
"""
import pandas as pd
from collections import Counter
def analyze_ranges():
"""Analysiert Zahlenbereiche in Eurojackpot-Daten."""
# Daten laden
df = pd.read_csv("/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/data/AlleEurojackpotzahlen.csv", sep=';')
print(f"Anzahl Ziehungen: {len(df)}")
# Alle Zahlen sammeln
all_numbers = []
for col in ['Z1', 'Z2', 'Z3', 'Z4', 'Z5']:
all_numbers.extend(df[col].tolist())
# Bereiche definieren
ranges = {
'1-10': list(range(1, 11)),
'11-20': list(range(11, 21)),
'21-30': list(range(21, 31)),
'31-40': list(range(31, 41)),
'41-50': list(range(41, 51))
}
print(f"\nGesamt gezogene Zahlen: {len(all_numbers)}")
print("="*50)
# Analyse pro Bereich
for range_name, range_numbers in ranges.items():
count = sum(1 for num in all_numbers if num in range_numbers)
percentage = (count / len(all_numbers)) * 100
print(f"{range_name:6}: {count:4} Zahlen ({percentage:5.1f}%)")
print("="*50)
# Häufigste Zahlen
number_counts = Counter(all_numbers)
print("\nHäufigste 10 Zahlen:")
for num, count in number_counts.most_common(10):
print(f"Zahl {num:2}: {count:3} mal")
print("\nSeltenste 10 Zahlen:")
for num, count in number_counts.most_common()[-10:]:
print(f"Zahl {num:2}: {count:3} mal")
if __name__ == "__main__":
analyze_ranges()
@@ -0,0 +1,270 @@
#!/usr/bin/env python3
"""
Eurojackpot Treffer-Analyse (umschlüsselte Werte)
Analysiert, wo die größten Treffer-Wahrscheinlichkeiten bei den umschlüsselten Werten liegen.
"""
import pandas as pd
from collections import Counter, defaultdict
import numpy as np
def analyze_hit_probabilities():
"""Analysiert die Treffer-Wahrscheinlichkeiten der umschlüsselten Werte."""
# Umschlüsselte Daten laden
df = pd.read_csv("/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/AlleEurojackpotzahlen_umschluesselt.csv", sep=';')
print("🎯 TREFFER-ANALYSE UMSCHLÜSSELTE WERTE (1-10)")
print("="*60)
print(f"Analysierte Ziehungen: {len(df)}")
# 1. Einzelne Gruppen-Wahrscheinlichkeiten pro Position
print(f"\n🏆 HÖCHSTE TREFFER-WAHRSCHEINLICHKEITEN PRO POSITION:")
print("="*55)
positions = ['z1', 'z2', 'z3', 'z4', 'z5']
position_stats = {}
for pos in positions:
group_counts = Counter(df[pos])
total = len(df)
# Beste Gruppe für diese Position
best_group = max(group_counts, key=group_counts.get)
best_count = group_counts[best_group]
best_probability = (best_count / total) * 100
position_stats[pos] = {
'best_group': best_group,
'probability': best_probability,
'count': best_count,
'all_groups': group_counts
}
print(f"\n{pos.upper()}: Gruppe {best_group} führt mit {best_probability:.1f}% ({best_count}/{total})")
# Top 3 für diese Position
top_3 = group_counts.most_common(3)
print(f" Top 3: ", end="")
for i, (group, count) in enumerate(top_3):
prob = (count / total) * 100
print(f"{i+1}.Gruppe {group}({prob:.1f}%)", end="")
if i < 2:
print(" > ", end="")
print()
# 2. Beste Gesamtkombination
print(f"\n🔥 OPTIMAL-KOMBINATION (höchste Einzelwahrscheinlichkeiten):")
print("="*60)
optimal_combination = []
total_probability = 1.0
for pos in positions:
best_group = position_stats[pos]['best_group']
probability = position_stats[pos]['probability'] / 100
optimal_combination.append(best_group)
total_probability *= probability
print(f"{pos}: Gruppe {best_group} ({position_stats[pos]['probability']:.1f}%)")
optimal_string = '-'.join(map(str, optimal_combination))
print(f"\nOptimal-Kombination: {optimal_string}")
print(f"Theoretische Wahrscheinlichkeit: {total_probability*100:.6f}%")
print(f"Das entspricht etwa 1 in {1/total_probability:,.0f} Ziehungen")
# 3. Tatsächlich aufgetretene häufigste Kombinationen
print(f"\n📊 REAL AUFGETRETENE HÄUFIGSTE KOMBINATIONEN:")
print("="*50)
combination_counts = Counter(df['kombination_umschluesselt'])
print(f"Top 20 real aufgetretene Kombinationen:")
for i, (combination, count) in enumerate(combination_counts.most_common(20), 1):
probability = (count / len(df)) * 100
print(f"{i:2}. {combination:15} {count}x ({probability:.2f}%)")
# 4. Bereichs-Kombinationen mit höchster Wahrscheinlichkeit
print(f"\n🎲 BEREICHS-KOMBINATIONEN MIT HÖCHSTER WAHRSCHEINLICHKEIT:")
print("="*60)
# Niedrig (1-3), Mittel (4-7), Hoch (8-10) Kombinationen
range_combinations = defaultdict(int)
for _, row in df.iterrows():
ranges = []
for pos in positions:
val = row[pos]
if 1 <= val <= 3:
ranges.append('N') # Niedrig
elif 4 <= val <= 7:
ranges.append('M') # Mittel
else:
ranges.append('H') # Hoch
range_pattern = ''.join(ranges)
range_combinations[range_pattern] += 1
print(f"Häufigste Bereichsmuster (N=Niedrig1-3, M=Mittel4-7, H=Hoch8-10):")
sorted_patterns = sorted(range_combinations.items(), key=lambda x: x[1], reverse=True)
for i, (pattern, count) in enumerate(sorted_patterns[:15], 1):
probability = (count / len(df)) * 100
pattern_readable = pattern.replace('N', 'Niedrig').replace('M', 'Mittel').replace('H', 'Hoch')
print(f"{i:2}. {pattern:5} ({pattern_readable:25}) {count:3}x ({probability:5.1f}%)")
# 5. Positions-spezifische Empfehlungen
print(f"\n💡 POSITIONS-SPEZIFISCHE EMPFEHLUNGEN:")
print("="*45)
recommendations = {}
for pos in positions:
group_counts = position_stats[pos]['all_groups']
total = len(df)
# Top 3 Gruppen für maximale Abdeckung
top_groups = [group for group, count in group_counts.most_common(3)]
top_coverage = sum(group_counts[group] for group in top_groups)
coverage_percentage = (top_coverage / total) * 100
recommendations[pos] = {
'top_groups': top_groups,
'coverage': coverage_percentage
}
print(f"\n{pos.upper()}: Empfohlene Gruppen {top_groups}")
print(f" Abdeckung: {coverage_percentage:.1f}% aller Ziehungen")
# Wahrscheinlichkeitsverteilung
print(f" Verteilung: ", end="")
for group in top_groups:
prob = (group_counts[group] / total) * 100
print(f"Gruppe {group}({prob:.1f}%)", end="")
if group != top_groups[-1]:
print(", ", end="")
print()
# 6. Strategische Kombinationen
print(f"\n🎯 STRATEGISCHE KOMBINATIONEN FÜR MAXIMALE TREFFER:")
print("="*55)
# Berechne verschiedene Strategien
strategies = {
'Konservativ': {
'z1': [1, 2], # Top 2 der Position z1
'z2': [3, 4], # Top 2 der Position z2
'z3': [5, 6], # Top 2 der Position z3
'z4': [7, 8], # Top 2 der Position z4
'z5': [9, 10] # Top 2 der Position z5
},
'Ausgewogen': {
'z1': [1, 2, 3], # Top 3 jeder Position
'z2': [3, 4, 5],
'z3': [4, 5, 6, 7],
'z4': [6, 7, 8],
'z5': [8, 9, 10]
},
'Optimal': {} # Wird basierend auf tatsächlichen Daten gefüllt
}
# Optimal-Strategie basierend auf echten Top-3 pro Position
for pos in positions:
top_3_groups = recommendations[pos]['top_groups']
strategies['Optimal'][pos] = top_3_groups
for strategy_name, strategy in strategies.items():
if strategy: # Nur wenn Strategie gefüllt ist
print(f"\n{strategy_name}-Strategie:")
total_combinations = 1
coverage_per_position = []
for pos in positions:
recommended_groups = strategy[pos]
pos_stats = position_stats[pos]['all_groups']
total_pos = len(df)
# Abdeckung dieser Gruppen
coverage = sum(pos_stats.get(group, 0) for group in recommended_groups)
coverage_pct = (coverage / total_pos) * 100
coverage_per_position.append(coverage_pct)
total_combinations *= len(recommended_groups)
print(f" {pos}: Gruppen {recommended_groups} ({coverage_pct:.1f}% Abdeckung)")
avg_coverage = np.mean(coverage_per_position)
print(f" Durchschnittliche Abdeckung: {avg_coverage:.1f}%")
print(f" Mögliche Kombinationen: {total_combinations:,}")
# 7. Heiße und kalte Zahlen
print(f"\n🔥❄️ HEISSE UND KALTE GRUPPEN:")
print("="*35)
# Alle Gruppen über alle Positionen sammeln
all_groups = []
for pos in positions:
all_groups.extend(df[pos].tolist())
group_total_counts = Counter(all_groups)
total_appearances = len(all_groups)
expected_per_group = total_appearances / 10 # 10 Gruppen
print(f"Erwartete Häufigkeit pro Gruppe: {expected_per_group:.1f}")
print(f"\nHeisse Gruppen (über Erwartung):")
hot_groups = []
for group in range(1, 11):
actual = group_total_counts.get(group, 0)
deviation = actual - expected_per_group
if deviation > 0:
hot_groups.append((group, actual, deviation))
hot_groups.sort(key=lambda x: x[2], reverse=True)
for group, count, deviation in hot_groups:
percentage = (count / total_appearances) * 100
print(f" Gruppe {group}: {count} (+{deviation:.1f}, {percentage:.1f}%)")
print(f"\nKalte Gruppen (unter Erwartung):")
cold_groups = []
for group in range(1, 11):
actual = group_total_counts.get(group, 0)
deviation = actual - expected_per_group
if deviation < 0:
cold_groups.append((group, actual, deviation))
cold_groups.sort(key=lambda x: x[2])
for group, count, deviation in cold_groups:
percentage = (count / total_appearances) * 100
print(f" Gruppe {group}: {count} ({deviation:.1f}, {percentage:.1f}%)")
# 8. Export der Empfehlungen
print(f"\n💾 EMPFEHLUNGEN EXPORT:")
print("="*25)
# Erstelle Empfehlungs-DataFrame
recommendation_data = []
# Für jede Position die besten Empfehlungen
for pos in positions:
pos_recommendations = recommendations[pos]
for group in pos_recommendations['top_groups']:
prob = (position_stats[pos]['all_groups'][group] / len(df)) * 100
recommendation_data.append({
'position': pos,
'gruppe': group,
'wahrscheinlichkeit_prozent': prob,
'anzahl_auftreten': position_stats[pos]['all_groups'][group],
'empfehlung_rang': pos_recommendations['top_groups'].index(group) + 1
})
rec_df = pd.DataFrame(recommendation_data)
output_file = "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/treffer_empfehlungen_umschluesselt.csv"
rec_df.to_csv(output_file, sep=';', index=False)
print(f"✅ Empfehlungen gespeichert: treffer_empfehlungen_umschluesselt.csv")
return position_stats, recommendations
if __name__ == "__main__":
analyze_hit_probabilities()
+358
View File
@@ -0,0 +1,358 @@
# Eurojackpot Automation System
Vollautomatisches System für wöchentliche Tipp-Generierung und Learning nach Ziehungen.
## 📋 Überblick
Das System besteht aus 2 Hauptkomponenten:
1. **Weekly Tip Generator** - Generiert automatisch Tipps vor Ziehungen
2. **Auto Update & Learn** - Aktualisiert Daten und lernt nach Ziehungen
## 🚀 Installation
### 1. Virtuelle Umgebung aktivieren
```bash
cd "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot"
source venv/bin/activate
```
### 2. Scripts ausführbar machen
```bash
chmod +x scripts/automation/*.py
```
## 🎯 Manuelle Nutzung
### Tipps generieren
```bash
# Normale Generierung (prüft ob nötig)
python scripts/automation/weekly_tip_generator.py
# Erzwinge Generierung
python scripts/automation/weekly_tip_generator.py --force
# Generiere 20 Tipps statt 10
python scripts/automation/weekly_tip_generator.py --num-tips 20
# Zeige nur Historie
python scripts/automation/weekly_tip_generator.py --history
```
### Nach Ziehung aktualisieren
```bash
# Kompletter Workflow: Update + Evaluation + Learning
python scripts/automation/auto_update_and_learn.py
```
## ⏰ Automatisierung mit Cron
### Setup
1. Öffne Crontab:
```bash
crontab -e
```
2. Füge folgende Zeilen hinzu:
```bash
# Pfad zur virtuellen Umgebung
EUROJACKPOT_PATH="/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot"
# Montag & Donnerstag 09:00 Uhr: Tipps generieren (vor Ziehung)
0 9 * * 1,4 cd "$EUROJACKPOT_PATH" && source venv/bin/activate && python scripts/automation/weekly_tip_generator.py >> logs/tips_generation.log 2>&1
# Dienstag & Freitag 21:00 Uhr: Update nach Ziehung
0 21 * * 2,5 cd "$EUROJACKPOT_PATH" && source venv/bin/activate && python scripts/automation/auto_update_and_learn.py >> logs/auto_update.log 2>&1
```
3. Speichern (ESC, :wq in vim)
### Cron Zeitplan
```
Montag 09:00 → Tipps für Dienstag-Ziehung generieren
Dienstag 20:00 → Ziehung findet statt
Dienstag 21:00 → Auto-Update & Learning
Donnerstag 09:00 → Tipps für Freitag-Ziehung generieren
Freitag 20:00 → Ziehung findet statt
Freitag 21:00 → Auto-Update & Learning
```
### Logs prüfen
```bash
# Erstelle Log-Verzeichnis
mkdir -p logs
# Zeige letzte Tipps-Generierung
tail -50 logs/tips_generation.log
# Zeige letzte Updates
tail -50 logs/auto_update.log
```
## 📊 Dateien & Verzeichnisse
```
data/
├── generated_tips/ # Generierte Tipps
│ ├── weekly_tips_*.csv # Tipp-Dateien
│ └── generation_history.json # Historie
│
├── performance_reports/ # Performance-Reports
│ └── report_*.txt # Reports nach Ziehungen
│
├── learning_log.json # Learning-Historie
└── backups/ # Automatische Backups
logs/
├── tips_generation.log # Cron Log für Generierung
└── auto_update.log # Cron Log für Updates
```
## 🔍 Features im Detail
### Weekly Tip Generator
**Intelligente Prüfung:**
- Generiert nur wenn nötig (> 3 Tage seit letzter Generierung)
- Berücksichtigt Wochentage (Montag/Donnerstag vor Ziehungen)
- Vermeidet unnötige Generierungen
**Historie-Tracking:**
- Speichert alle Generierungen
- Trackt Confidence & Quality Scores
- Ermöglicht Performance-Analyse über Zeit
**Ausgabe:**
```
📊 GENERIERUNGS-HISTORIE
======================================================================
Nr Datum Tips Confidence Quality Datei
----------------------------------------------------------------------
1 2025-01-20 09:00 10 0.5823 0.6734 weekly_tips_20250120_090000.csv
2 2025-01-16 09:00 10 0.5891 0.6812 weekly_tips_20250116_090000.csv
```
### Auto Update & Learn
**4-Schritt-Workflow:**
1. **Daten aktualisieren**
- Lädt neueste Ziehung von API
- Erstellt automatisches Backup
- Validiert Daten
2. **Neue Ziehung prüfen**
- Erkennt automatisch neue Ziehungen
- Verhindert Doppel-Verarbeitung
- Extrahiert Ziehungsdaten
3. **Tipps evaluieren**
- Vergleicht letzte Tipps mit Ziehung
- Zählt Treffer (Main + Euro)
- Ermittelt beste Strategien
4. **Learning Update**
- Passt ML-Modelle an
- Aktualisiert Predictions
- Speichert Performance-Metriken
**Evaluation Output:**
```
🎯 EVALUIERE LETZTE TIPPS
======================================================================
Tip Strategie Main Euro Gesamt Bewertung
------------------------------------------------------------
#1 PURE-AI 2 1 3 ✅ Gut
#2 HYBRID-OPT 3 0 3 ✅ Gut
#3 PURE-PATTERN 1 1 2 👍 OK
...
🏆 Bester Tipp: #2 (3 Main + 0 Euro)
```
## 📈 Performance-Reports
Reports werden automatisch nach jeder Ziehung erstellt:
```
======================================================================
EUROJACKPOT PERFORMANCE REPORT
======================================================================
Erstellt: 2025-01-17 21:05:23
ZIEHUNG VOM 2025-01-17
----------------------------------------------------------------------
Hauptzahlen: [3, 9, 12, 24, 39]
Eurozahlen: [5, 10]
EVALUATION DER TIPPS
----------------------------------------------------------------------
Datei: weekly_tips_20250115_090000.csv
Tipps evaluiert: 10
Avg Main Matches: 1.80
Avg Euro Matches: 0.60
Bester Tipp: #5
- Main Treffer: 3
- Euro Treffer: 1
======================================================================
```
## 🛠️ Troubleshooting
### "No module named 'requests'"
```bash
cd "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot"
source venv/bin/activate
pip install requests beautifulsoup4 pandas numpy scikit-learn
```
### "Permission denied"
```bash
chmod +x scripts/automation/*.py
```
### Cron läuft nicht
```bash
# Prüfe Cron-Service
launchctl list | grep cron
# Prüfe System-Logs
tail -f /var/log/system.log | grep cron
# Test manuell
cd "/path/to/eurojackpot"
source venv/bin/activate
python scripts/automation/weekly_tip_generator.py
```
### Logs sind leer
Stelle sicher dass das logs-Verzeichnis existiert:
```bash
mkdir -p logs
```
## 🎯 Best Practices
### 1. Teste manuell vor Cron
```bash
# Test 1: Tipps generieren
python scripts/automation/weekly_tip_generator.py --force
# Test 2: Update & Learn
python scripts/automation/auto_update_and_learn.py
```
### 2. Überwache Logs regelmäßig
```bash
# Wöchentlich prüfen
tail -100 logs/tips_generation.log
tail -100 logs/auto_update.log
```
### 3. Backup-Strategie
Backups werden automatisch erstellt, aber zusätzlich:
```bash
# Manuelles Backup
cp AlleEurojackpotzahlen.csv "AlleEurojackpotzahlen_$(date +%Y%m%d).csv"
```
### 4. Performance-Analyse
```bash
# Zeige Generierungs-Historie
python scripts/automation/weekly_tip_generator.py --history
# Analysiere Reports
cat data/performance_reports/report_*.txt | grep "Avg Main Matches"
```
## 📱 Benachrichtigungen (Optional)
Füge Benachrichtigungen zu Cron hinzu:
### macOS Notification
```bash
# In Cron-Zeile hinzufügen:
&& osascript -e 'display notification "Eurojackpot Tipps generiert" with title "Automation"'
```
### Email (wenn konfiguriert)
```bash
# In Cron-Zeile:
&& echo "Tipps generiert" | mail -s "Eurojackpot Update" your@email.com
```
## 🔄 Workflow-Diagramm
```
WOCHE
│
├─ Montag 09:00
│ └─ 🎲 Generiere Tipps für Dienstag
│ └─ weekly_tips_TIMESTAMP.csv
│
├─ Dienstag 20:00
│ └─ 🎰 Ziehung findet statt
│
├─ Dienstag 21:00
│ └─ 🔄 Auto-Update & Learning
│ ├─ Daten aktualisieren
│ ├─ Tipps evaluieren
│ ├─ Learning Update
│ └─ Report erstellen
│
├─ Donnerstag 09:00
│ └─ 🎲 Generiere Tipps für Freitag
│ └─ weekly_tips_TIMESTAMP.csv
│
├─ Freitag 20:00
│ └─ 🎰 Ziehung findet statt
│
└─ Freitag 21:00
└─ 🔄 Auto-Update & Learning
├─ Daten aktualisieren
├─ Tipps evaluieren
├─ Learning Update
└─ Report erstellen
```
## 💡 Tipps
1. **Erste Woche:** Nutze `--force` um initiale Tipps zu generieren
2. **Strategie-Mix:** Das System nutzt automatisch den optimalen Mix
3. **Learning:** Nach 4-5 Ziehungen beginnt das System besser zu werden
4. **Reports:** Analysiere Reports monatlich für Trend-Analyse
5. **Backup:** Die automatischen Backups werden im backups/ Ordner gespeichert
## 🆘 Support
Bei Problemen:
1. Prüfe Logs: `tail -100 logs/*.log`
2. Teste manuell: `python script.py`
3. Prüfe venv: `source venv/bin/activate && pip list`
4. Checke Cronjobs: `crontab -l`
---
**Version:** 1.0
**Erstellt:** 2025-01-25
**Kompatibel mit:** Ultimate AI-ML Generator V2.1
+445
View File
@@ -0,0 +1,445 @@
#!/usr/bin/env python3
"""
Automatischer Eurojackpot Update & Learning Workflow
Nach jeder Ziehung:
1. Aktualisiert Daten von API
2. Evaluiert letzte generierte Tipps
3. Real-Time Learning Update
4. Performance-Report
Verwendung:
python auto_update_and_learn.py
Oder als Cronjob (nach Ziehung):
0 21 * * 2,5 cd /path/to/eurojackpot && source venv/bin/activate && python scripts/automation/auto_update_and_learn.py
"""
import sys
import os
from datetime import datetime, timedelta
import json
import pandas as pd
# Path Setup
script_dir = os.path.dirname(os.path.abspath(__file__))
project_dir = os.path.dirname(os.path.dirname(script_dir))
sys.path.insert(0, project_dir)
from scripts.utils.update_from_api import EurojackpotAPIUpdater
from scripts.generators.ultimate_ai_ml_eurojackpot_generator import UltimateAIMLEurojackpotGenerator
from scripts.utils.notifier import EurojackpotNotifier
class AutoUpdateAndLearn:
"""Automatisches Update und Learning System."""
def __init__(self, data_dir: str):
self.data_dir = data_dir
# CSV file is in Eurojackpot/data folder
# data_dir = .../Eurojackpot/data
# We need .../Eurojackpot/data/AlleEurojackpotzahlen.csv
self.data_file = os.path.join(data_dir, "AlleEurojackpotzahlen.csv")
self.tips_dir = os.path.join(data_dir, "generated_tips")
self.reports_dir = os.path.join(data_dir, "performance_reports")
self.learning_log = os.path.join(data_dir, "learning_log.json")
os.makedirs(self.reports_dir, exist_ok=True)
# Initialisiere Notifier
self.notifier = EurojackpotNotifier()
print("🤖 AUTOMATISCHES UPDATE & LEARNING SYSTEM")
print("=" * 70)
def load_learning_log(self) -> dict:
"""Lädt Learning Log."""
if os.path.exists(self.learning_log):
with open(self.learning_log, 'r') as f:
return json.load(f)
return {"updates": []}
def save_learning_log(self, log: dict):
"""Speichert Learning Log."""
with open(self.learning_log, 'w') as f:
json.dump(log, f, indent=2)
def check_for_new_draw(self) -> dict:
"""
Prüft ob neue Ziehung verfügbar ist.
Returns:
Dict mit neuer Ziehung oder None
"""
print("\n🔍 PRÜFE AUF NEUE ZIEHUNG")
print("=" * 70)
log = self.load_learning_log()
# Lade aktuelle Daten
try:
df = pd.read_csv(self.data_file, sep=';')
df['datum'] = pd.to_datetime(df['datum'], format='%Y-%m-%d')
latest_draw = df.iloc[0] # Neueste Ziehung (sortiert absteigend)
latest_date = latest_draw['datum']
print(f" 📅 Neueste Ziehung in Daten: {latest_date.strftime('%Y-%m-%d')}")
# Prüfe ob schon verarbeitet
if log["updates"]:
last_processed = log["updates"][-1].get("draw_date")
if last_processed == latest_date.strftime('%Y-%m-%d'):
print(f" ⏭️ Bereits verarbeitet")
return None
print(f" ✅ Neue Ziehung gefunden!")
return {
'date': latest_date,
'Z1': int(latest_draw['Z1']),
'Z2': int(latest_draw['Z2']),
'Z3': int(latest_draw['Z3']),
'Z4': int(latest_draw['Z4']),
'Z5': int(latest_draw['Z5']),
'SZ1': int(latest_draw['SZ1']),
'SZ2': int(latest_draw['SZ2'])
}
except Exception as e:
print(f" ❌ Fehler beim Prüfen: {e}")
return None
def update_data(self) -> bool:
"""
Aktualisiert Daten von API.
Returns:
True bei Erfolg
"""
print("\n📥 AKTUALISIERE DATEN VON API")
print("=" * 70)
try:
updater = EurojackpotAPIUpdater(self.data_file)
success = updater.update(api_name='all', create_backup=True)
if success:
print(" ✅ Daten erfolgreich aktualisiert")
else:
print(" ⚠️ Update ohne neue Daten")
return success
except Exception as e:
print(f" ❌ Fehler beim Update: {e}")
return False
def evaluate_tips(self, new_draw: dict) -> dict:
"""
Evaluiert letzte generierte Tipps gegen neue Ziehung.
Args:
new_draw: Dict mit neuer Ziehung
Returns:
Evaluierungs-Ergebnisse
"""
print("\n🎯 EVALUIERE LETZTE TIPPS")
print("=" * 70)
# Finde neueste Tipps-Datei
if not os.path.exists(self.tips_dir):
print(" ⚠️ Keine Tipps zum Evaluieren")
return {}
tip_files = sorted(
[f for f in os.listdir(self.tips_dir) if f.endswith('.csv')],
reverse=True
)
if not tip_files:
print(" ⚠️ Keine Tipps-Dateien gefunden")
return {}
latest_tips_file = os.path.join(self.tips_dir, tip_files[0])
print(f" 📁 Evaluiere: {tip_files[0]}")
try:
tips_df = pd.read_csv(latest_tips_file)
# Extrahiere gezogene Zahlen
drawn_main = [new_draw[f'Z{i}'] for i in range(1, 6)]
drawn_euro = [new_draw['SZ1'], new_draw['SZ2']]
print(f" 🎲 Gezogene Zahlen: {drawn_main} + Euro {drawn_euro}")
print()
results = []
best_matches = {'main': 0, 'euro': 0, 'tip': None}
for _, tip in tips_df.iterrows():
# Parse Hauptzahlen
main_str = tip['Main_Numbers']
# Handle both formats: "[1, 2, 3]" and "1-2-3"
if main_str.startswith('['):
# Parse Python list format
import ast
tip_main = ast.literal_eval(main_str)
else:
# Parse dash-separated format
tip_main = [int(n) for n in main_str.split('-')]
# Parse Eurozahlen
euro_str = tip['Euro_Numbers']
if euro_str.startswith('['):
import ast
tip_euro = ast.literal_eval(euro_str)
else:
tip_euro = [int(n) for n in euro_str.split('-')]
# Zähle Treffer
main_matches = len(set(tip_main) & set(drawn_main))
euro_matches = len(set(tip_euro) & set(drawn_euro))
total_matches = main_matches + euro_matches
results.append({
'tip_number': tip['Tip_Number'],
'strategy': tip['Strategy'],
'main_matches': main_matches,
'euro_matches': euro_matches,
'total_matches': total_matches
})
# Track best
if total_matches > (best_matches['main'] + best_matches['euro']):
best_matches = {
'main': main_matches,
'euro': euro_matches,
'tip': tip['Tip_Number']
}
# Ausgabe
print(f" {'Tip':<5} {'Strategie':<15} {'Main':<6} {'Euro':<6} {'Gesamt':<8} {'Bewertung'}")
print(" " + "-" * 60)
for r in results:
rating = self._get_match_rating(r['main_matches'], r['euro_matches'])
print(f" #{r['tip_number']:<4} {r['strategy']:<15} "
f"{r['main_matches']:<6} {r['euro_matches']:<6} "
f"{r['total_matches']:<8} {rating}")
print()
print(f" 🏆 Bester Tipp: #{best_matches['tip']} "
f"({best_matches['main']} Main + {best_matches['euro']} Euro)")
return {
'file': tip_files[0],
'results': results,
'best': best_matches,
'avg_main_matches': sum(r['main_matches'] for r in results) / len(results),
'avg_euro_matches': sum(r['euro_matches'] for r in results) / len(results)
}
except Exception as e:
print(f" ❌ Fehler bei Evaluation: {e}")
import traceback
traceback.print_exc()
return {}
def _get_match_rating(self, main: int, euro: int) -> str:
"""Bewertung der Treffer."""
total = main + euro
if main == 5 and euro == 2:
return "🏆 JACKPOT!"
elif main == 5 and euro == 1:
return "💰 Klasse 2"
elif main == 5 and euro == 0:
return "💰 Klasse 3"
elif main == 4 and euro == 2:
return "💰 Klasse 4"
elif main == 4 and euro == 1:
return "💵 Klasse 5"
elif total >= 3:
return "✅ Gut"
elif total >= 2:
return "👍 OK"
else:
return "⚪ Niedrig"
def perform_learning_update(self, new_draw: dict) -> bool:
"""
Führt Real-Time Learning Update durch.
Args:
new_draw: Dict mit neuer Ziehung
Returns:
True bei Erfolg
"""
print("\n🧠 REAL-TIME LEARNING UPDATE")
print("=" * 70)
try:
# Initialisiere Generator
generator = UltimateAIMLEurojackpotGenerator(
self.data_file,
fast_mode=True
)
# Learning Update
generator.real_time_learner.learn_from_result(new_draw)
# Performance Tracking
generator.performance_tracker.evaluate_predictions(new_draw)
print(" ✅ Learning Update durchgeführt")
print(" 📊 Models wurden angepasst")
return True
except Exception as e:
print(f" ❌ Fehler beim Learning: {e}")
return False
def generate_report(self, new_draw: dict, evaluation: dict):
"""Generiert Performance-Report."""
print("\n📊 PERFORMANCE-REPORT")
print("=" * 70)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
report_file = os.path.join(
self.reports_dir,
f"report_{timestamp}.txt"
)
report = []
report.append("=" * 70)
report.append("EUROJACKPOT PERFORMANCE REPORT")
report.append("=" * 70)
report.append(f"Erstellt: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
report.append("")
report.append(f"ZIEHUNG VOM {new_draw['date'].strftime('%Y-%m-%d')}")
report.append("-" * 70)
report.append(f"Hauptzahlen: {[new_draw[f'Z{i}'] for i in range(1, 6)]}")
report.append(f"Eurozahlen: [{new_draw['SZ1']}, {new_draw['SZ2']}]")
report.append("")
if evaluation:
report.append("EVALUATION DER TIPPS")
report.append("-" * 70)
report.append(f"Datei: {evaluation['file']}")
report.append(f"Tipps evaluiert: {len(evaluation['results'])}")
report.append(f"Avg Main Matches: {evaluation['avg_main_matches']:.2f}")
report.append(f"Avg Euro Matches: {evaluation['avg_euro_matches']:.2f}")
report.append(f"Bester Tipp: #{evaluation['best']['tip']}")
report.append(f" - Main Treffer: {evaluation['best']['main']}")
report.append(f" - Euro Treffer: {evaluation['best']['euro']}")
report.append("")
report.append("=" * 70)
# Speichern
with open(report_file, 'w') as f:
f.write('\n'.join(report))
# Ausgabe
for line in report:
print(line)
print(f"\n📁 Report gespeichert: {os.path.basename(report_file)}")
def run(self):
"""Führt kompletten Workflow aus."""
print()
print("=" * 70)
print("START: AUTOMATISCHER UPDATE & LEARNING WORKFLOW")
print("=" * 70)
# 1. Update Daten
print("\n[SCHRITT 1/4] Daten aktualisieren")
self.update_data()
# 2. Prüfe auf neue Ziehung
print("\n[SCHRITT 2/4] Neue Ziehung prüfen")
new_draw = self.check_for_new_draw()
if not new_draw:
print("\n⏭️ Keine neue Ziehung - Workflow beendet")
return True
# 3. Evaluiere Tipps
print("\n[SCHRITT 3/4] Tipps evaluieren")
evaluation = self.evaluate_tips(new_draw)
# 4. Learning Update
print("\n[SCHRITT 4/4] Learning Update")
self.perform_learning_update(new_draw)
# Report
self.generate_report(new_draw, evaluation)
# Log Update
log = self.load_learning_log()
log["updates"].append({
"timestamp": datetime.now().isoformat(),
"draw_date": new_draw['date'].strftime('%Y-%m-%d'),
"evaluation": evaluation.get('best', {}),
"avg_matches": {
'main': evaluation.get('avg_main_matches', 0),
'euro': evaluation.get('avg_euro_matches', 0)
}
})
self.save_learning_log(log)
# Sende Benachrichtigung
try:
best_match = {
'main_matches': evaluation.get('best', {}).get('main', 0),
'euro_matches': evaluation.get('best', {}).get('euro', 0)
}
evaluation_summary = {
'avg_main': evaluation.get('avg_main_matches', 0),
'avg_euro': evaluation.get('avg_euro_matches', 0)
}
self.notifier.send_draw_results(new_draw, evaluation_summary, best_match)
except Exception as e:
print(f"⚠️ Benachrichtigung fehlgeschlagen: {e}")
print("\n" + "=" * 70)
print("✅ WORKFLOW ERFOLGREICH ABGESCHLOSSEN")
print("=" * 70)
return True
def main():
"""Hauptfunktion."""
import argparse
parser = argparse.ArgumentParser(
description="Automatisches Update & Learning nach Ziehung"
)
parser.add_argument(
'--data-dir',
type=str,
default="/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/data",
help='Daten-Verzeichnis'
)
args = parser.parse_args()
# Run Workflow
workflow = AutoUpdateAndLearn(args.data_dir)
success = workflow.run()
sys.exit(0 if success else 1)
if __name__ == "__main__":
main()
+278
View File
@@ -0,0 +1,278 @@
#!/usr/bin/env python3
"""
Wöchentlicher Eurojackpot Tipp-Generator
Automatisiert:
1. Prüft ob neue Tipps nötig sind (basierend auf letzter Generierung)
2. Generiert 10 Ultimate Tipps
3. Speichert mit Timestamp
4. Trackt Generierungs-Historie
Verwendung:
python weekly_tip_generator.py
Oder als Cronjob:
0 9 * * 2,5 cd /path/to/eurojackpot && source venv/bin/activate && python scripts/automation/weekly_tip_generator.py
"""
import sys
import os
from datetime import datetime, timedelta
import json
# Füge Parent-Verzeichnisse zum Path hinzu
script_dir = os.path.dirname(os.path.abspath(__file__))
project_dir = os.path.dirname(os.path.dirname(script_dir))
generators_dir = os.path.join(project_dir, 'scripts', 'generators')
sys.path.insert(0, project_dir)
sys.path.insert(0, generators_dir)
# Import des Ultimate Generators
from scripts.generators.ultimate_ai_ml_eurojackpot_generator import UltimateAIMLEurojackpotGenerator
from scripts.utils.notifier import EurojackpotNotifier
class WeeklyTipGenerator:
"""Automatischer wöchentlicher Tipp-Generator."""
def __init__(self, data_dir: str):
self.data_dir = data_dir
# CSV file is in Eurojackpot/data folder
# data_dir = .../Eurojackpot/data
# We need .../Eurojackpot/data/AlleEurojackpotzahlen.csv
self.data_file = os.path.join(data_dir, "AlleEurojackpotzahlen.csv")
self.tips_dir = os.path.join(data_dir, "generated_tips")
self.history_file = os.path.join(self.tips_dir, "generation_history.json")
os.makedirs(self.tips_dir, exist_ok=True)
# Initialisiere Notifier
self.notifier = EurojackpotNotifier()
print("🤖 AUTOMATISCHER WÖCHENTLICHER TIPP-GENERATOR")
print("=" * 70)
def load_history(self) -> dict:
"""Lädt Generierungs-Historie."""
if os.path.exists(self.history_file):
with open(self.history_file, 'r') as f:
return json.load(f)
return {"generations": []}
def save_history(self, history: dict):
"""Speichert Historie."""
with open(self.history_file, 'w') as f:
json.dump(history, f, indent=2)
def needs_new_tips(self) -> bool:
"""
Prüft ob neue Tipps nötig sind.
Logik:
- Eurojackpot: Dienstag & Freitag Ziehungen
- Generiere Tipps wenn:
a) Noch nie generiert
b) Letzte Generierung > 3 Tage her
c) Es ist Montag oder Donnerstag (vor Ziehung)
"""
history = self.load_history()
if not history["generations"]:
print(" ℹ️ Noch nie Tipps generiert")
return True
last_gen = history["generations"][-1]
last_date = datetime.fromisoformat(last_gen["timestamp"])
days_since = (datetime.now() - last_date).days
print(f" 📅 Letzte Generierung: {last_date.strftime('%Y-%m-%d %H:%M')}")
print(f" ⏱️ Vor {days_since} Tagen")
# Wenn > 3 Tage her
if days_since > 3:
print(" ✅ Mehr als 3 Tage her - neue Tipps nötig")
return True
# Prüfe Wochentag (0=Montag, 4=Freitag)
today = datetime.now().weekday()
# Montag (vor Dienstag-Ziehung)
if today == 0 and days_since >= 1:
print(" ✅ Montag - generiere für Dienstag-Ziehung")
return True
# Donnerstag (vor Freitag-Ziehung)
if today == 3 and days_since >= 1:
print(" ✅ Donnerstag - generiere für Freitag-Ziehung")
return True
print(" ⏭️ Keine neuen Tipps nötig")
return False
def generate_tips(self, num_tips: int = 10, force: bool = False) -> bool:
"""
Generiert neue Tipps.
Args:
num_tips: Anzahl Tipps
force: Ignoriere needs_new_tips Check
Returns:
True bei Erfolg
"""
print("\n🎯 TIPP-GENERIERUNG")
print("=" * 70)
# Check ob nötig
if not force and not self.needs_new_tips():
print("\n⏭️ Keine Generierung nötig")
return True
print(f"\n🚀 Generiere {num_tips} Ultimate Tipps...")
print("-" * 70)
try:
# Initialisiere Generator
generator = UltimateAIMLEurojackpotGenerator(
self.data_file,
fast_mode=True
)
# Generiere Tipps
tips = generator.generate_ultimate_tips(num_tips=num_tips)
if not tips:
print("\n❌ Keine Tipps generiert")
return False
# Speichere Tipps
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = os.path.join(
self.tips_dir,
f"weekly_tips_{timestamp}.csv"
)
generator.export_tips_to_csv(tips, output_file)
# Update Historie
history = self.load_history()
history["generations"].append({
"timestamp": datetime.now().isoformat(),
"num_tips": len(tips),
"file": os.path.basename(output_file),
"avg_confidence": sum(t['confidence'] for t in tips) / len(tips),
"avg_quality": sum(t['quality'] for t in tips) / len(tips)
})
self.save_history(history)
print("\n" + "=" * 70)
print("✅ TIPPS ERFOLGREICH GENERIERT")
print(f"📁 Datei: {os.path.basename(output_file)}")
print(f"📊 Anzahl: {len(tips)}")
print(f"🎯 Avg Confidence: {history['generations'][-1]['avg_confidence']:.4f}")
print(f"💎 Avg Quality: {history['generations'][-1]['avg_quality']:.4f}")
print("=" * 70)
# Sende Benachrichtigung
try:
# Finde besten Tipp (höchste Confidence)
best_tip = max(tips, key=lambda t: t.get('confidence', 0))
# Formatiere für Notification
best_tip_formatted = {
'main_numbers': best_tip.get('main_numbers', '?'),
'euro_numbers': best_tip.get('euro_numbers', '?'),
'confidence': best_tip.get('confidence', 0),
'strategy': best_tip.get('strategy', 'UNKNOWN')
}
timestamp_formatted = datetime.now().strftime('%Y-%m-%d %H:%M')
self.notifier.send_tips_generated(tips, timestamp_formatted, best_tip_formatted)
except Exception as e:
print(f"⚠️ Benachrichtigung fehlgeschlagen: {e}")
return True
except Exception as e:
print(f"\n❌ Fehler bei Generierung: {e}")
import traceback
traceback.print_exc()
return False
def show_history(self):
"""Zeigt Generierungs-Historie."""
history = self.load_history()
if not history["generations"]:
print("\n ℹ️ Noch keine Generierungen")
return
print("\n📊 GENERIERUNGS-HISTORIE")
print("=" * 70)
print(f"{'Nr':<4} {'Datum':<20} {'Tips':<6} {'Confidence':<12} {'Quality':<12} {'Datei'}")
print("-" * 70)
for i, gen in enumerate(reversed(history["generations"][-10:]), 1):
timestamp = datetime.fromisoformat(gen["timestamp"])
print(f"{i:<4} {timestamp.strftime('%Y-%m-%d %H:%M'):<20} "
f"{gen['num_tips']:<6} {gen['avg_confidence']:<12.4f} "
f"{gen['avg_quality']:<12.4f} {gen['file']}")
print("-" * 70)
print(f"Total: {len(history['generations'])} Generierungen")
def main():
"""Hauptfunktion."""
import argparse
parser = argparse.ArgumentParser(
description="Wöchentlicher Eurojackpot Tipp-Generator"
)
parser.add_argument(
'--force',
action='store_true',
help='Generiere Tipps auch wenn nicht nötig'
)
parser.add_argument(
'--num-tips',
type=int,
default=10,
help='Anzahl Tipps zu generieren (default: 10)'
)
parser.add_argument(
'--history',
action='store_true',
help='Zeige nur Historie'
)
parser.add_argument(
'--data-dir',
type=str,
default="/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/data",
help='Daten-Verzeichnis'
)
args = parser.parse_args()
# Initialisiere
generator = WeeklyTipGenerator(args.data_dir)
# Zeige Historie wenn gewünscht
if args.history:
generator.show_history()
return
# Generiere Tipps
success = generator.generate_tips(
num_tips=args.num_tips,
force=args.force
)
# Zeige Historie
generator.show_history()
sys.exit(0 if success else 1)
if __name__ == "__main__":
main()
+141
View File
@@ -0,0 +1,141 @@
import csv
from itertools import combinations
import time
print("=== EUROJACKPOT KOMBINATIONS-GENERATOR ===")
print("Erstellt alle möglichen Kombinationen mit Status (gezogen/nicht gezogen)")
print()
# Pfade
eurojackpot_file = '/Users/sebastianfrohlich/Downloads/EJ_ab_2018.csv'
output_file = '/Users/sebastianfrohlich/Desktop/Alle_Eurojackpot_Kombinationen_mit_Status.csv'
missing_file = '/Users/sebastianfrohlich/Desktop/Fehlende_Eurojackpot_Kombinationen.csv'
# Schritt 1: Einlesen der gezogenen Kombinationen
print("Schritt 1: Lade gezogene Eurojackpot-Kombinationen...")
existing_combinations = set()
try:
with open(eurojackpot_file, 'r', encoding='utf-8') as file:
lines = file.readlines()
for line in lines[1:]: # Header überspringen
parts = line.strip().split(';')
if len(parts) >= 6: # Mindestens Datum + 5 Zahlen
try:
numbers = []
for i in range(1, 6): # Spalten 1-5 (nach Datum)
if i < len(parts) and parts[i].strip().isdigit():
numbers.append(int(parts[i].strip()))
if len(numbers) == 5: # Vollständige Kombination
combo = frozenset(numbers)
existing_combinations.add(combo)
except (ValueError, IndexError):
continue
print(f" ✓ {len(existing_combinations)} gezogene Kombinationen geladen")
# Beispiele anzeigen
if existing_combinations:
print(" Beispiele:")
for i, combo in enumerate(list(existing_combinations)[:3]):
sorted_combo = sorted(list(combo))
print(f" {sorted_combo}")
if len(existing_combinations) > 3:
print(f" ... und {len(existing_combinations)-3} weitere")
except FileNotFoundError:
print(f" ⚠️ Datei nicht gefunden: {eurojackpot_file}")
existing_combinations = set()
except Exception as e:
print(f" ⚠️ Fehler beim Lesen: {e}")
existing_combinations = set()
print()
# Schritt 2: Generierung aller möglichen Kombinationen
print("Schritt 2: Generiere alle möglichen Kombinationen (5 aus 50)...")
total_combinations = 2118760 # C(50,5)
print(f" Gesamtanzahl zu verarbeitender Kombinationen: {total_combinations:,}")
all_combinations = []
count = 0
start_time = time.time()
for combo in combinations(range(1, 51), 5):
count += 1
# Fortschritt anzeigen
if count % 100000 == 0:
elapsed = time.time() - start_time
rate = count / elapsed if elapsed > 0 else 0
remaining = (total_combinations - count) / rate if rate > 0 else 0
print(f" Fortschritt: {count:,} / {total_combinations:,} "
f"({count/total_combinations*100:.1f}%) "
f"- {rate:,.0f}/s - noch ~{remaining/60:.1f} min")
# Prüfen ob Kombination bereits gezogen wurde
sorted_combo = sorted(combo)
is_drawn = 1 if frozenset(combo) in existing_combinations else 0
# Zur Liste hinzufügen (Z1, Z2, Z3, Z4, Z5, gezogen)
combination_row = sorted_combo + [is_drawn]
all_combinations.append(combination_row)
print(f" ✓ Alle {len(all_combinations):,} Kombinationen generiert")
print()
# Schritt 3: Statistiken berechnen
print("Schritt 3: Berechne Statistiken...")
drawn_combinations = sum(1 for combo in all_combinations if combo[5] == 1)
undrawn_combinations = len(all_combinations) - drawn_combinations
print(f" Gesamt mögliche Kombinationen: {len(all_combinations):,}")
print(f" Bereits gezogene Kombinationen: {drawn_combinations:,}")
print(f" Noch nicht gezogene Kombinationen: {undrawn_combinations:,}")
if len(all_combinations) > 0:
percentage = drawn_combinations / len(all_combinations) * 100
print(f" Anteil gezogener Kombinationen: {percentage:.6f}%")
print()
# Schritt 4: Hauptdatei speichern
print("Schritt 4: Speichere Hauptdatei...")
try:
with open(output_file, 'w', newline='', encoding='utf-8') as outfile:
writer = csv.writer(outfile, delimiter=';')
writer.writerow(['Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'gezogen']) # Header
for combo in all_combinations:
writer.writerow(combo)
print(f" ✓ Hauptdatei gespeichert: {output_file}")
except Exception as e:
print(f" ⚠️ Fehler beim Speichern der Hauptdatei: {e}")
# Schritt 5: Datei mit fehlenden Kombinationen speichern
print("Schritt 5: Speichere Datei mit fehlenden Kombinationen...")
try:
missing_combinations = [combo[:5] for combo in all_combinations if combo[5] == 0]
with open(missing_file, 'w', newline='', encoding='utf-8') as outfile:
writer = csv.writer(outfile, delimiter=';')
writer.writerow(['Z1', 'Z2', 'Z3', 'Z4', 'Z5']) # Header
for combo in missing_combinations:
writer.writerow(combo)
print(f" ✓ Datei mit fehlenden Kombinationen gespeichert: {missing_file}")
print(f" ✓ {len(missing_combinations):,} fehlende Kombinationen")
except Exception as e:
print(f" ⚠️ Fehler beim Speichern der fehlenden Kombinationen: {e}")
print()
print("=== FERTIG ===")
print("Erstellte Dateien:")
print(f"1. {output_file}")
print(f" - Alle {len(all_combinations):,} Kombinationen mit Status")
print(f"2. {missing_file}")
if 'missing_combinations' in locals():
print(f" - {len(missing_combinations):,} noch nicht gezogene Kombinationen")
print()
print("Format der Hauptdatei:")
print("Z1;Z2;Z3;Z4;Z5;gezogen")
print("1;2;3;4;5;0")
print("...")
@@ -0,0 +1,806 @@
#!/usr/bin/env python3
"""
Ultimativer Eurojackpot Tipp-Generator mit Multi-Ziehungs-Trend-Analyse
Kombiniert multiple Optimierungsstrategien:
- Historische Häufigkeitsanalyse
- Positionsbasierte Gewichtung
- Multi-Ziehungs-Trend-Analyse (NEU!)
- Zahlen-Momentum-Tracking
- Sequenzielle Abhängigkeiten
- Zyklische Muster-Erkennung
"""
import pandas as pd
import random
import numpy as np
from itertools import combinations
from collections import Counter, defaultdict, deque
import datetime
class UltimativerEurojackpotGenerator:
def __init__(self, data_path="/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/AlleEurojackpotzahlen.csv"):
self.data_path = data_path
self.df = None
self.drawn_combinations = set()
# Basis-Analyse (Original)
self.number_frequencies = Counter()
self.position_frequencies = defaultdict(Counter)
self.pattern_frequencies = Counter()
self.supernumber_frequencies = {'sz1': Counter(), 'sz2': Counter()}
self.number_distances = []
# Multi-Ziehungs-Trend-Analyse (NEU!)
self.number_sequences = defaultdict(list)
self.momentum_scores = {}
self.trend_predictions = {}
self.sequential_dependencies = defaultdict(lambda: defaultdict(int))
self.cycle_patterns = {}
self.hot_numbers = []
self.warm_numbers = []
self.cold_numbers = []
# Initialisierung
self.load_and_analyze_all_data()
def load_and_analyze_all_data(self):
"""Lädt Daten und führt alle Analysen durch."""
try:
self.df = pd.read_csv(self.data_path, sep=';')
# Chronologische Sortierung für Trend-Analyse
if 'Datum' in self.df.columns:
self.df['Datum'] = pd.to_datetime(self.df['Datum'], format='%d.%m.%Y')
self.df = self.df.sort_values('Datum')
print(f"🚀 ULTIMATIVER EUROJACKPOT GENERATOR")
print("=" * 60)
print(f"📊 Analysiere {len(self.df)} Ziehungen mit Multi-Trend-Analyse...")
# Basis-Analysen durchführen
self._perform_basic_analysis()
# Multi-Ziehungs-Trend-Analysen durchführen (NEU!)
self._perform_momentum_analysis()
self._perform_sequential_analysis()
self._perform_cycle_analysis()
self._generate_trend_predictions()
print(f"✅ Komplette Analyse abgeschlossen!")
self._print_ultimate_analysis_summary()
except Exception as e:
print(f"❌ Fehler beim Laden der Daten: {e}")
return False
return True
def _perform_basic_analysis(self):
"""Führt die ursprünglichen Basis-Analysen durch."""
for _, row in self.df.iterrows():
# Gezogene Kombinationen
combo = tuple(sorted([row['Z1'], row['Z2'], row['Z3'], row['Z4'], row['Z5']]))
self.drawn_combinations.add(combo)
# Zahlenfrequenzen
numbers = [row['Z1'], row['Z2'], row['Z3'], row['Z4'], row['Z5']]
for num in numbers:
self.number_frequencies[num] += 1
# Positionsfrequenzen
sorted_numbers = sorted(numbers)
for i, num in enumerate(sorted_numbers):
self.position_frequencies[f'pos_{i+1}'][num] += 1
# Muster analysieren
pattern = self._get_pattern(sorted_numbers)
self.pattern_frequencies[pattern] += 1
# Superzahlen
self.supernumber_frequencies['sz1'][row['SZ1']] += 1
self.supernumber_frequencies['sz2'][row['SZ2']] += 1
# Zahlenabstände
distances = [sorted_numbers[i+1] - sorted_numbers[i] for i in range(4)]
self.number_distances.extend(distances)
def _perform_momentum_analysis(self, window_size=15):
"""Führt Multi-Ziehungs-Momentum-Analyse durch (NEU!)"""
print(f"\n🔥 MOMENTUM-ANALYSE (Fenster: {window_size})")
# Zahlensequenzen über Zeit aufbauen
for number in range(1, 51):
sequence = []
for _, row in self.df.iterrows():
drawn_numbers = [row['Z1'], row['Z2'], row['Z3'], row['Z4'], row['Z5']]
sequence.append(1 if number in drawn_numbers else 0)
self.number_sequences[number] = sequence
# Momentum-Scores berechnen
momentum_results = {}
for number in range(1, 51):
recent_sequence = self.number_sequences[number][-window_size:]
hit_rate = sum(recent_sequence) / len(recent_sequence)
trend_score = self._calculate_trend_score(recent_sequence)
recency_score = self._calculate_recency_score(recent_sequence)
# Kombinierter Momentum-Score mit Gewichtung
momentum_score = (hit_rate * 0.4) + (trend_score * 0.4) + (recency_score * 0.2)
momentum_results[number] = {
'hit_rate': hit_rate,
'trend_score': trend_score,
'recency_score': recency_score,
'momentum_score': momentum_score,
'status': self._get_momentum_status(momentum_score)
}
self.momentum_scores = momentum_results
# Zahlen in Kategorien einteilen
sorted_momentum = sorted(momentum_results.items(),
key=lambda x: x[1]['momentum_score'], reverse=True)
self.hot_numbers = [num for num, data in sorted_momentum[:20]
if data['momentum_score'] > 0.35]
self.warm_numbers = [num for num, data in sorted_momentum[20:35]
if 0.2 <= data['momentum_score'] <= 0.35]
self.cold_numbers = [num for num, data in sorted_momentum[35:]
if data['momentum_score'] < 0.2][:15]
print(f"🔥 {len(self.hot_numbers)} heiße Zahlen identifiziert")
print(f"🌡️ {len(self.warm_numbers)} warme Zahlen identifiziert")
print(f"🧊 {len(self.cold_numbers)} kalte Zahlen identifiziert")
def _perform_sequential_analysis(self, look_back=3):
"""Analysiert sequenzielle Abhängigkeiten zwischen Ziehungen (NEU!)"""
print(f"\n🔗 SEQUENZIELLE ABHÄNGIGKEITEN (Look-back: {look_back})")
for i in range(look_back, len(self.df)):
current_numbers = set([self.df.iloc[i]['Z1'], self.df.iloc[i]['Z2'],
self.df.iloc[i]['Z3'], self.df.iloc[i]['Z4'],
self.df.iloc[i]['Z5']])
for j in range(1, look_back + 1):
prev_numbers = set([self.df.iloc[i-j]['Z1'], self.df.iloc[i-j]['Z2'],
self.df.iloc[i-j]['Z3'], self.df.iloc[i-j]['Z4'],
self.df.iloc[i-j]['Z5']])
for prev_num in prev_numbers:
for curr_num in current_numbers:
self.sequential_dependencies[f"lag_{j}"][f"{prev_num}_{curr_num}"] += 1
print(f"🔗 Sequenzielle Muster für {look_back} Ziehungen analysiert")
def _perform_cycle_analysis(self, max_cycle_length=15):
"""Analysiert zyklische Muster (NEU!)"""
print(f"\n🔄 ZYKLUS-ANALYSE")
pattern_sequence = []
for _, row in self.df.iterrows():
numbers = sorted([row['Z1'], row['Z2'], row['Z3'], row['Z4'], row['Z5']])
pattern = self._get_pattern(numbers)
pattern_sequence.append(pattern)
# Zyklen erkennen
self.cycle_patterns = {}
for cycle_length in range(3, max_cycle_length + 1):
cycles = self._find_pattern_cycles(pattern_sequence, cycle_length)
if cycles:
self.cycle_patterns[cycle_length] = cycles
cycle_count = sum(len(cycles) for cycles in self.cycle_patterns.values())
print(f"🔄 {cycle_count} zyklische Muster erkannt")
def _generate_trend_predictions(self):
"""Generiert Trend-basierte Vorhersagen (NEU!)"""
print(f"\n🎯 TREND-VORHERSAGEN GENERIEREN")
for number in range(1, 51):
if number in self.momentum_scores:
momentum_data = self.momentum_scores[number]
# Multi-Faktor-Vorhersage-Score
momentum_weight = momentum_data['momentum_score'] * 0.4
frequency_weight = (self.number_frequencies[number] / len(self.df)) * 0.3
trend_weight = max(0, momentum_data['trend_score']) * 0.3
prediction_score = momentum_weight + frequency_weight + trend_weight
self.trend_predictions[number] = {
'prediction_score': prediction_score,
'recommendation': self._get_prediction_recommendation(prediction_score),
'confidence': self._get_confidence_level(prediction_score)
}
print(f"🎯 Trend-Vorhersagen für alle 50 Zahlen generiert")
def _calculate_trend_score(self, sequence):
"""Berechnet Trend-Score für eine Zahlensequenz."""
if len(sequence) < 2:
return 0
x = np.arange(len(sequence))
y = np.array(sequence)
weights = np.exp(x / len(x)) # Neuere Ziehungen wichtiger
try:
coeffs = np.polyfit(x, y, 1, w=weights)
return coeffs[0] # Steigung = Trend
except:
return 0
def _calculate_recency_score(self, sequence):
"""Berechnet Recency-Score."""
try:
last_hit_index = len(sequence) - 1 - sequence[::-1].index(1)
recency = 1 - (len(sequence) - 1 - last_hit_index) / len(sequence)
return recency
except ValueError:
return 0
def _get_momentum_status(self, score):
"""Klassifiziert Momentum-Status."""
if score > 0.5:
return "🔥 SEHR HEISS"
elif score > 0.35:
return "🌡️ HEISS"
elif score > 0.2:
return "😐 WARM"
elif score > 0.1:
return "🧊 KÜHL"
else:
return "❄️ EISKALT"
def _get_prediction_recommendation(self, score):
"""Empfehlung basierend auf Vorhersage-Score."""
if score > 0.4:
return "SEHR EMPFOHLEN"
elif score > 0.25:
return "EMPFOHLEN"
elif score > 0.15:
return "NEUTRAL"
else:
return "VERMEIDEN"
def _get_confidence_level(self, score):
"""Konfidenz-Level für Vorhersagen."""
if score > 0.4:
return "HOCH"
elif score > 0.25:
return "MITTEL"
else:
return "NIEDRIG"
def _find_pattern_cycles(self, sequence, cycle_length):
"""Findet zyklische Muster."""
cycle_patterns = defaultdict(list)
for i in range(len(sequence) - cycle_length):
pattern = ''.join(sequence[i:i+cycle_length])
cycle_patterns[pattern].append(i)
return {pattern: positions for pattern, positions in cycle_patterns.items()
if len(positions) >= 2}
def _get_pattern(self, numbers):
"""Bestimmt N/M/H-Muster."""
pattern = []
for num in numbers:
if 1 <= num <= 15:
pattern.append('N')
elif 16 <= num <= 35:
pattern.append('M')
else:
pattern.append('H')
return ''.join(pattern)
def _print_ultimate_analysis_summary(self):
"""Gibt detaillierte Analyse-Zusammenfassung aus."""
print(f"\n📈 ULTIMATE ANALYSE-ZUSAMMENFASSUNG:")
print("=" * 55)
# Top Trend-Empfehlungen
print("\n🎯 TOP 10 TREND-EMPFEHLUNGEN:")
top_predictions = sorted(self.trend_predictions.items(),
key=lambda x: x[1]['prediction_score'], reverse=True)[:10]
for i, (number, data) in enumerate(top_predictions):
momentum_status = self.momentum_scores[number]['status']
print(f"{i+1:2}. Zahl {number:2}: Score {data['prediction_score']:.3f} "
f"({data['recommendation']}) {momentum_status}")
# Top Muster mit Trend-Integration
print(f"\n🎨 TOP MUSTER (kombiniert mit Trends):")
for pattern, count in self.pattern_frequencies.most_common(5):
percentage = (count / len(self.drawn_combinations)) * 100
print(f" {pattern}: {count}x ({percentage:.1f}%)")
# Zyklische Erkenntnisse
if self.cycle_patterns:
print(f"\n🔄 ERKANNTE ZYKLEN:")
total_cycles = sum(len(cycles) for cycles in self.cycle_patterns.values())
print(f" Insgesamt {total_cycles} zyklische Muster erkannt")
def generate_ultimate_combination(self):
"""Generiert ultimativ optimierte Kombination mit allen Methoden."""
max_attempts = 1000
for attempt in range(max_attempts):
numbers = []
# Strategie-Mix basierend auf Trends:
# 40% Top-Trend-Zahlen, 30% Heiße Zahlen, 20% Position-optimiert, 10% Balance
# 2 Zahlen aus Top-Trend-Empfehlungen (40%)
top_trend_numbers = [num for num, data in sorted(self.trend_predictions.items(),
key=lambda x: x[1]['prediction_score'], reverse=True)[:15]
if data['recommendation'] in ['SEHR EMPFOHLEN', 'EMPFOHLEN']]
if len(top_trend_numbers) >= 2:
trend_picks = random.sample(top_trend_numbers[:10], 2)
numbers.extend(trend_picks)
# 2 Zahlen aus heißen Zahlen (30%)
if len(self.hot_numbers) >= 2:
remaining_hot = [n for n in self.hot_numbers if n not in numbers]
if len(remaining_hot) >= 2:
hot_picks = random.sample(remaining_hot[:8], min(2, len(remaining_hot)))
numbers.extend(hot_picks)
# 1 Zahl positions-optimiert oder warm (30%)
remaining_slots = 5 - len(numbers)
if remaining_slots > 0:
if self.warm_numbers:
remaining_warm = [n for n in self.warm_numbers if n not in numbers]
if remaining_warm:
warm_pick = random.choice(remaining_warm[:5])
numbers.append(warm_pick)
# Auffüllen bis 5 Zahlen
while len(numbers) < 5:
available_numbers = [n for n in range(1, 51) if n not in numbers]
# Gewichtete Auswahl basierend auf Trend-Scores
weights = [self.trend_predictions[n]['prediction_score'] for n in available_numbers]
if sum(weights) > 0:
additional_number = random.choices(available_numbers, weights=weights)[0]
else:
additional_number = random.choice(available_numbers)
numbers.append(additional_number)
# Sortieren und validieren
numbers = sorted(numbers[:5])
if self._validate_ultimate_combination(numbers):
return numbers
# Fallback
return self._generate_fallback_combination()
def _validate_ultimate_combination(self, numbers):
"""Erweiterte Validierung mit Trend-Kriterien."""
# Basis-Validierung
if tuple(numbers) in self.drawn_combinations:
return False
if len(set(numbers)) != 5:
return False
# Trend-Validierung
hot_count = sum(1 for n in numbers if n in self.hot_numbers)
high_trend_count = sum(1 for n in numbers
if self.trend_predictions[n]['recommendation'] == 'SEHR EMPFOHLEN')
# Mindestens 1 heiße oder 1 sehr empfohlene Zahl
if hot_count == 0 and high_trend_count == 0:
return False
# Standard-Validierungen
distances = [numbers[i+1] - numbers[i] for i in range(4)]
if min(distances) < 2 or max(distances) > 18:
return False
even_count = sum(1 for n in numbers if n % 2 == 0)
if even_count == 0 or even_count == 5:
return False
total = sum(numbers)
if total < 80 or total > 170:
return False
return True
def _generate_fallback_combination(self):
"""Fallback mit Trend-Integration."""
numbers = []
# Basis: NMMHH mit Trend-Gewichtung
ranges = {
'N': [n for n in range(1, 16) if n in self.hot_numbers + self.warm_numbers] or list(range(1, 16)),
'M': [n for n in range(16, 36) if n in self.hot_numbers + self.warm_numbers] or list(range(16, 36)),
'H': [n for n in range(36, 51) if n in self.hot_numbers + self.warm_numbers] or list(range(36, 51))
}
numbers.extend(random.sample(ranges['N'][:10], 1))
numbers.extend(random.sample(ranges['M'][:15], 2))
numbers.extend(random.sample(ranges['H'][:10], 2))
return sorted(numbers)
def get_optimized_supernumbers(self):
"""Trend-optimierte Superzahlen-Auswahl."""
# Integration von Trend-Daten für Superzahlen
sz1_trends = {}
sz2_trends = {}
# Letzte 10 Ziehungen analysieren
recent_df = self.df.tail(10)
for sz1 in range(1, 13): # Eurojackpot SZ1: 1-12
recent_count = (recent_df['SZ1'] == sz1).sum()
total_count = self.supernumber_frequencies['sz1'][sz1]
trend_score = (recent_count / 10) * 0.6 + (total_count / len(self.df)) * 0.4
sz1_trends[sz1] = trend_score
for sz2 in range(1, 11): # Eurojackpot SZ2: 1-10
recent_count = (recent_df['SZ2'] == sz2).sum()
total_count = self.supernumber_frequencies['sz2'][sz2]
trend_score = (recent_count / 10) * 0.6 + (total_count / len(self.df)) * 0.4
sz2_trends[sz2] = trend_score
# Gewichtete Auswahl
sz1_candidates = list(sz1_trends.keys())
sz1_weights = list(sz1_trends.values())
sz1 = random.choices(sz1_candidates, weights=sz1_weights)[0]
sz2_candidates = list(sz2_trends.keys())
sz2_weights = list(sz2_trends.values())
sz2 = random.choices(sz2_candidates, weights=sz2_weights)[0]
return sz1, sz2
def generate_ultimate_tips(self, num_tips=10):
"""Generiert ultimative Tipps mit kompletter Multi-Trend-Integration."""
print(f"\n🚀 ULTIMATE TIPP-GENERIERUNG")
print("=" * 50)
print(f"🎯 Kombiniert ALLE Optimierungsstrategien:")
print(f" ✅ Historische Häufigkeitsanalyse")
print(f" ✅ Positionsbasierte Gewichtung")
print(f" ✅ Multi-Ziehungs-Momentum-Analyse")
print(f" ✅ Sequenzielle Abhängigkeiten")
print(f" ✅ Zyklische Muster-Erkennung")
print(f" ✅ Trend-Vorhersage-Algorithmus")
generated_tips = []
strategy_distribution = Counter()
print(f"\n🎲 GENERIERE {num_tips} ULTIMATE TIPPS:")
print("=" * 60)
print(f"{'Nr':<3} {'Zahlen':<20} {'Muster':<7} {'🔥':<3} {'🎯':<3} {'Status'}")
print("-" * 60)
attempts = 0
max_attempts = num_tips * 50
while len(generated_tips) < num_tips and attempts < max_attempts:
attempts += 1
combination = self.generate_ultimate_combination()
if combination and tuple(combination) not in [tuple(tip['zahlen']) for tip in generated_tips]:
pattern = self._get_pattern(combination)
# Trend-Analyse des Tipps
hot_count = sum(1 for n in combination if n in self.hot_numbers)
trend_count = sum(1 for n in combination
if self.trend_predictions[n]['recommendation'] in ['SEHR EMPFOHLEN', 'EMPFOHLEN'])
# Superzahlen
sz1, sz2 = self.get_optimized_supernumbers()
# Strategie-Klassifikation
if hot_count >= 3:
strategy = "🔥 MOMENTUM"
elif trend_count >= 3:
strategy = "🎯 TREND"
elif pattern in ['NMMHH', 'MNMHH', 'NHMHM']:
strategy = "🎨 MUSTER"
else:
strategy = "⚖️ BALANCE"
strategy_distribution[strategy] += 1
tip = {
'tipp_nr': len(generated_tips) + 1,
'zahlen': combination,
'z1': combination[0],
'z2': combination[1],
'z3': combination[2],
'z4': combination[3],
'z5': combination[4],
'sz1': sz1,
'sz2': sz2,
'muster': pattern,
'summe': sum(combination),
'hot_count': hot_count,
'trend_count': trend_count,
'strategy': strategy
}
generated_tips.append(tip)
# Status ausgeben
zahlen_str = f"{combination[0]:2}-{combination[1]:2}-{combination[2]:2}-{combination[3]:2}-{combination[4]:2}"
print(f"{len(generated_tips):2}. {zahlen_str:<20} {pattern:<7} {hot_count:<3} {trend_count:<3} {strategy}")
# Ultimate Zusammenfassung
print(f"\n🏆 ULTIMATE OPTIMIERUNGS-ZUSAMMENFASSUNG:")
print("=" * 50)
print(f"✅ {len(generated_tips)} Ultimate Tipps generiert")
print(f"🎯 Erfolgsrate: {(len(generated_tips)/attempts)*100:.1f}%")
print(f"📊 Durchschnitt {sum(tip['hot_count'] for tip in generated_tips)/len(generated_tips):.1f} heiße Zahlen pro Tipp")
print(f"🔮 Durchschnitt {sum(tip['trend_count'] for tip in generated_tips)/len(generated_tips):.1f} Trend-Zahlen pro Tipp")
# Strategie-Verteilung
print(f"\n📈 STRATEGIE-VERTEILUNG:")
for strategy, count in strategy_distribution.most_common():
print(f" {strategy}: {count} Tipps")
# Komplette Tipp-Ausgabe
print(f"\n🎯 ULTIMATE TIPP-EMPFEHLUNGEN:")
print("=" * 70)
print(f"{'Nr':<3} {'Hauptzahlen':<20} {'SZ1':<4} {'SZ2':<4} {'Muster':<7} {'Strategie':<12} {'Score'}")
print("-" * 70)
for tip in generated_tips:
zahlen_str = f"{tip['z1']:2}-{tip['z2']:2}-{tip['z3']:2}-{tip['z4']:2}-{tip['z5']:2}"
# Ultimate Score berechnen
ultimate_score = (tip['hot_count'] * 0.3 + tip['trend_count'] * 0.4 +
(5 if tip['muster'] in ['NMMHH', 'MNMHH'] else 3) * 0.3)
print(f"{tip['tipp_nr']:2}. {zahlen_str:<20} {tip['sz1']:<4} {tip['sz2']:<4} "
f"{tip['muster']:<7} {tip['strategy']:<12} {ultimate_score:.1f}")
# Export mit Ultimate Features
self._export_ultimate_tips(generated_tips)
return generated_tips
def _export_ultimate_tips(self, tips):
"""Exportiert Ultimate Tipps mit erweiterten Daten."""
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = f"/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/ultimate_tipps_{timestamp}.csv"
# Erweiterte Daten für Export
export_data = []
for tip in tips:
tip_data = tip.copy()
# Zusätzliche Trend-Daten hinzufügen
tip_data['trend_scores'] = [self.trend_predictions[n]['prediction_score']
for n in tip['zahlen']]
tip_data['avg_trend_score'] = np.mean(tip_data['trend_scores'])
tip_data['momentum_ratings'] = [self.momentum_scores[n]['status']
for n in tip['zahlen']]
export_data.append(tip_data)
tips_df = pd.DataFrame(export_data)
tips_df.to_csv(output_file, sep=';', index=False)
print(f"\n💾 ULTIMATE EXPORT:")
print("=" * 25)
print(f"✅ Ultimate Tipps gespeichert: ultimate_tipps_{timestamp}.csv")
print(f"🚀 Multi-Trend-Analyse integriert")
print(f"🎯 Höchste Optimierungsstufe erreicht")
print(f"📊 Erweiterte Trend-Daten enthalten")
def main():
"""Hauptfunktion für Ultimate Generator."""
print("🚀 ULTIMATE EUROJACKPOT GENERATOR")
print("🎯 Mit Multi-Ziehungs-Trend-Integration")
print("=" * 50)
# Generator initialisieren (lädt und analysiert automatisch alle Daten)
generator = UltimativerEurojackpotGenerator()
# Ultimate Tipps generieren
tips = generator.generate_ultimate_tips(10)
print(f"\n🏆 ULTIMATE OPTIMIERUNG ABGESCHLOSSEN!")
print("=" * 45)
print(f"🚀 10 Ultimate Tipps mit Multi-Trend-Analyse generiert")
print(f"📈 Maximale Trefferwahrscheinlichkeit durch:")
print(f" • Momentum-Analyse über 15 Ziehungen")
print(f" • Sequenzielle Abhängigkeiten")
print(f" • Zyklische Muster-Erkennung")
print(f" • Positionsbasierte Optimierung")
print(f" • Trend-Vorhersage-Algorithmus")
print(f"🍀 Viel Erfolg bei der nächsten Ziehung!")
# Zusätzliche Ultimate Insights
print(f"\n💡 ULTIMATE INSIGHTS:")
print("=" * 30)
# Top 5 Trend-Zahlen für nächste Ziehung
top_trend_numbers = sorted(generator.trend_predictions.items(),
key=lambda x: x[1]['prediction_score'], reverse=True)[:5]
print(f"🎯 TOP 5 TREND-ZAHLEN für nächste Ziehung:")
for i, (number, data) in enumerate(top_trend_numbers):
momentum_status = generator.momentum_scores[number]['status']
print(f" {i+1}. Zahl {number:2}: {data['recommendation']} {momentum_status}")
# Empfohlene Muster basierend auf Zyklen
if generator.cycle_patterns:
print(f"\n🔄 ZYKLUS-EMPFEHLUNG:")
# Finde das wahrscheinlichste nächste Muster basierend auf Zyklen
recent_patterns = []
for _, row in generator.df.tail(5).iterrows():
numbers = sorted([row['Z1'], row['Z2'], row['Z3'], row['Z4'], row['Z5']])
pattern = generator._get_pattern(numbers)
recent_patterns.append(pattern)
print(f" Letzte 5 Muster: {' → '.join(recent_patterns)}")
# Empfehlung basierend auf häufigsten Mustern
top_pattern = generator.pattern_frequencies.most_common(1)[0]
print(f" Empfohlenes Muster: {top_pattern[0]} ({(top_pattern[1]/len(generator.df))*100:.1f}% Erfolgsrate)")
# Momentum-Warnung
very_hot = [n for n in generator.hot_numbers
if generator.momentum_scores[n]['momentum_score'] > 0.5]
if very_hot:
print(f"\n🔥 MOMENTUM-ALERT:")
print(f" SEHR HEIßE Zahlen: {very_hot[:5]}")
print(f" → Mindestens 1-2 dieser Zahlen in Tipps verwenden!")
return tips
# Zusätzliche Utility-Funktionen für erweiterte Analyse
def analyze_tip_quality(generator, tip_numbers):
"""Analysiert die Qualität eines einzelnen Tipps."""
quality_score = 0
analysis = {}
# Momentum-Analyse
hot_count = sum(1 for n in tip_numbers if n in generator.hot_numbers)
analysis['hot_numbers'] = hot_count
quality_score += hot_count * 0.2
# Trend-Analyse
trend_scores = [generator.trend_predictions[n]['prediction_score'] for n in tip_numbers]
avg_trend = np.mean(trend_scores)
analysis['avg_trend_score'] = avg_trend
quality_score += avg_trend * 0.3
# Positions-Analyse
position_quality = 0
for i, num in enumerate(sorted(tip_numbers)):
pos_freq = generator.position_frequencies[f'pos_{i+1}'][num]
if pos_freq > 0:
position_quality += pos_freq
analysis['position_quality'] = position_quality
quality_score += (position_quality / len(generator.df)) * 0.2
# Muster-Analyse
pattern = generator._get_pattern(sorted(tip_numbers))
pattern_freq = generator.pattern_frequencies[pattern]
pattern_score = pattern_freq / len(generator.df)
analysis['pattern'] = pattern
analysis['pattern_score'] = pattern_score
quality_score += pattern_score * 0.3
analysis['total_quality_score'] = quality_score
analysis['quality_rating'] = get_quality_rating(quality_score)
return analysis
def get_quality_rating(score):
"""Konvertiert Quality-Score in Rating."""
if score > 0.8:
return "🏆 PREMIUM"
elif score > 0.6:
return "🥇 SEHR GUT"
elif score > 0.4:
return "🥈 GUT"
elif score > 0.2:
return "🥉 DURCHSCHNITT"
else:
return "⚠️ SCHWACH"
def predict_jackpot_probability(generator, tip_numbers):
"""Schätzt Jackpot-Wahrscheinlichkeit basierend auf Trends."""
base_probability = 1 / 139838160 # Mathematische Grundwahrscheinlichkeit
# Trend-Multiplikator berechnen
trend_multiplier = 1.0
for number in tip_numbers:
momentum_score = generator.momentum_scores[number]['momentum_score']
trend_score = generator.trend_predictions[number]['prediction_score']
# Zahlen mit hohem Momentum/Trend erhöhen die relative Wahrscheinlichkeit
number_multiplier = 1 + (momentum_score * 0.1) + (trend_score * 0.15)
trend_multiplier *= number_multiplier
# Pattern-Bonus
pattern = generator._get_pattern(sorted(tip_numbers))
pattern_frequency = generator.pattern_frequencies[pattern] / len(generator.df)
pattern_multiplier = 1 + (pattern_frequency * 0.2)
estimated_probability = base_probability * trend_multiplier * pattern_multiplier
return {
'base_probability': base_probability,
'trend_multiplier': trend_multiplier,
'pattern_multiplier': pattern_multiplier,
'estimated_probability': estimated_probability,
'improvement_factor': (estimated_probability / base_probability)
}
def export_detailed_analysis(generator, tips, filename_suffix="detailed"):
"""Exportiert detaillierte Analyse aller Tipps."""
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = f"/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/analysis_{filename_suffix}_{timestamp}.csv"
detailed_data = []
for tip in tips:
tip_analysis = analyze_tip_quality(generator, tip['zahlen'])
probability_analysis = predict_jackpot_probability(generator, tip['zahlen'])
detailed_entry = {
**tip,
**tip_analysis,
**probability_analysis,
'individual_momentum_scores': [generator.momentum_scores[n]['momentum_score'] for n in tip['zahlen']],
'individual_trend_scores': [generator.trend_predictions[n]['prediction_score'] for n in tip['zahlen']],
'number_frequencies': [generator.number_frequencies[n] for n in tip['zahlen']]
}
detailed_data.append(detailed_entry)
# DataFrame erstellen und exportieren
df_detailed = pd.DataFrame(detailed_data)
df_detailed.to_csv(output_file, sep=';', index=False)
print(f"\n📊 DETAILLIERTE ANALYSE EXPORTIERT:")
print(f" 📁 Datei: analysis_{filename_suffix}_{timestamp}.csv")
print(f" 📈 Enthält Quality-Scores, Trend-Analysen und Wahrscheinlichkeits-Schätzungen")
if __name__ == "__main__":
# Reproduzierbarer Zufallsseed
random.seed(42)
np.random.seed(42)
# Ultimate Generator starten
tips = main()
# Optional: Detaillierte Analyse exportieren
if tips:
print(f"\n📊 ERWEITERTE ANALYSE VERFÜGBAR:")
print("=" * 40)
# Beispiel: Analysiere ersten Tipp detailliert
if len(tips) > 0:
generator = UltimativerEurojackpotGenerator()
sample_tip = tips[0]['zahlen']
quality_analysis = analyze_tip_quality(generator, sample_tip)
probability_analysis = predict_jackpot_probability(generator, sample_tip)
print(f"\n🔍 BEISPIEL-ANALYSE für Tipp 1 ({sample_tip}):")
print(f" 🏆 Quality-Rating: {quality_analysis['quality_rating']}")
print(f" 📈 Quality-Score: {quality_analysis['total_quality_score']:.3f}")
print(f" 🔥 Heiße Zahlen: {quality_analysis['hot_numbers']}/5")
print(f" 🎯 Avg. Trend-Score: {quality_analysis['avg_trend_score']:.3f}")
print(f" 🎨 Muster: {quality_analysis['pattern']} (Score: {quality_analysis['pattern_score']:.3f})")
print(f" 📊 Verbesserungs-Faktor: {probability_analysis['improvement_factor']:.2f}x")
# Optional: Detaillierte Analyse aller Tipps exportieren
export_detailed_analysis(generator, tips)
+234
View File
@@ -0,0 +1,234 @@
#!/usr/bin/env python3
"""
Eurojackpot Tipp-Generator für NMMHH-Muster
Generiert 10 Tipp-Felder basierend auf dem erfolgreichsten NMMHH-Muster,
die keine bereits gezogenen Kombinationen enthalten.
"""
import pandas as pd
import random
from itertools import combinations
def load_drawn_numbers():
"""Lädt alle bereits gezogenen Kombinationen."""
df = pd.read_csv("/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/AlleEurojackpotzahlen.csv", sep=';')
drawn_combinations = set()
for _, row in df.iterrows():
# Sortierte Kombination für Vergleich
combo = tuple(sorted([row['Z1'], row['Z2'], row['Z3'], row['Z4'], row['Z5']]))
drawn_combinations.add(combo)
print(f"🚫 {len(drawn_combinations)} bereits gezogene Kombinationen geladen")
return drawn_combinations
def generate_nmmhh_numbers():
"""Generiert Zahlen nach dem NMMHH-Muster."""
# Bereichsdefinition basierend auf umschlüsselter Analyse
ranges = {
'N': list(range(1, 16)), # Niedrig: Gruppen 1-3 (Zahlen 1-15)
'M': list(range(16, 36)), # Mittel: Gruppen 4-7 (Zahlen 16-35)
'H': list(range(36, 51)) # Hoch: Gruppen 8-10 (Zahlen 36-50)
}
# NMMHH-Muster: 1 Niedrig, 2 Mittel, 2 Hoch
selected_numbers = []
# 1 Niedrige Zahl (Position z1)
selected_numbers.extend(random.sample(ranges['N'], 1))
# 2 Mittlere Zahlen (Positionen z2, z3)
selected_numbers.extend(random.sample(ranges['M'], 2))
# 2 Hohe Zahlen (Positionen z4, z5)
selected_numbers.extend(random.sample(ranges['H'], 2))
# Sortieren für Eurojackpot-Format
return sorted(selected_numbers)
def generate_optimized_nmmhh_numbers():
"""Generiert optimierte NMMHH-Zahlen basierend auf Positionsanalyse."""
# Optimierte Bereiche basierend auf Treffer-Analyse
optimized_ranges = {
'z1': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], # Niedrig (sehr wahrscheinlich)
'z2': [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25], # Niedrig-Mittel
'z3': [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35], # Mittel
'z4': [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45], # Mittel-Hoch
'z5': [41, 42, 43, 44, 45, 46, 47, 48, 49, 50] # Hoch (sehr wahrscheinlich)
}
selected_numbers = []
# Position z1: Niedrig
selected_numbers.append(random.choice(optimized_ranges['z1'][:10])) # Fokus auf 1-10
# Position z2: Niedrig-Mittel
selected_numbers.append(random.choice(optimized_ranges['z2'][5:])) # Fokus auf 16-25
# Position z3: Mittel
selected_numbers.append(random.choice(optimized_ranges['z3'])) # 21-35
# Position z4: Mittel-Hoch
selected_numbers.append(random.choice(optimized_ranges['z4'][5:])) # Fokus auf 36-45
# Position z5: Hoch
selected_numbers.append(random.choice(optimized_ranges['z5'])) # 41-50
# Duplikate vermeiden und sortieren
selected_numbers = list(set(selected_numbers))
# Falls durch Duplikat-Entfernung weniger als 5 Zahlen
while len(selected_numbers) < 5:
all_available = list(range(1, 51))
missing_number = random.choice([n for n in all_available if n not in selected_numbers])
selected_numbers.append(missing_number)
return sorted(selected_numbers[:5])
def generate_tips_nmmhh(num_tips=10):
"""Generiert Tipps basierend auf NMMHH-Muster ohne bereits gezogene Kombinationen."""
print("🎯 EUROJACKPOT TIPP-GENERATOR (NMMHH-MUSTER)")
print("="*50)
# Bereits gezogene Kombinationen laden
drawn_combinations = load_drawn_numbers()
# Häufigste Zahlen pro Position aus der Analyse
frequent_numbers = {
'z1': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
'z2': [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25],
'z3': [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35],
'z4': [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45],
'z5': [36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
}
print(f"\n📋 NMMHH-MUSTER BEDEUTUNG:")
print("N = Niedrig (1-15), M = Mittel (16-35), H = Hoch (36-50)")
print("Muster: 1 Niedrig + 2 Mittel + 2 Hoch = 14.7% Erfolgsrate!")
generated_tips = []
attempts = 0
max_attempts = 10000
print(f"\n🎲 GENERIERE {num_tips} OPTIMIERTE TIPPS:")
print("="*40)
while len(generated_tips) < num_tips and attempts < max_attempts:
attempts += 1
# Verschiedene Generierungsstrategien abwechseln
if attempts % 3 == 0:
tip = generate_optimized_nmmhh_numbers()
else:
tip = generate_nmmhh_numbers()
# Prüfen ob bereits gezogen
tip_tuple = tuple(sorted(tip))
if tip_tuple not in drawn_combinations and tip not in generated_tips:
generated_tips.append(tip)
# Muster-Verifikation
pattern = []
for num in tip:
if 1 <= num <= 15:
pattern.append('N')
elif 16 <= num <= 35:
pattern.append('M')
else:
pattern.append('H')
pattern_str = ''.join(pattern)
print(f"Tipp {len(generated_tips):2}: {tip[0]:2}-{tip[1]:2}-{tip[2]:2}-{tip[3]:2}-{tip[4]:2} (Muster: {pattern_str})")
if len(generated_tips) < num_tips:
print(f"\n⚠️ Nur {len(generated_tips)} von {num_tips} Tipps generiert nach {attempts} Versuchen")
else:
print(f"\n✅ Alle {num_tips} Tipps erfolgreich generiert!")
print(f"\n📊 MUSTER-ANALYSE DER GENERIERTEN TIPPS:")
print("="*45)
pattern_counts = {}
for tip in generated_tips:
pattern = []
for num in tip:
if 1 <= num <= 15:
pattern.append('N')
elif 16 <= num <= 35:
pattern.append('M')
else:
pattern.append('H')
pattern_str = ''.join(pattern)
pattern_counts[pattern_str] = pattern_counts.get(pattern_str, 0) + 1
for pattern, count in sorted(pattern_counts.items(), key=lambda x: x[1], reverse=True):
print(f"Muster {pattern}: {count} Tipps")
# Zusätzliche Superzahlen-Empfehlungen
print(f"\n🎲 SUPERZAHLEN-EMPFEHLUNGEN:")
print("="*30)
# Lade Superzahlen-Statistiken
df = pd.read_csv("/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/AlleEurojackpotzahlen.csv", sep=';')
sz1_counts = df['SZ1'].value_counts().head(5)
sz2_counts = df['SZ2'].value_counts().head(5)
print(f"Top 5 SZ1: {list(sz1_counts.index)}")
print(f"Top 5 SZ2: {list(sz2_counts.index)}")
# Empfohlene Superzahlen für die Tipps
recommended_sz1 = list(sz1_counts.index)[:3]
recommended_sz2 = list(sz2_counts.index)[:3]
print(f"\n🎯 KOMPLETTE TIPP-EMPFEHLUNGEN:")
print("="*40)
print(f"{'Tipp':<5} {'Hauptzahlen':<20} {'SZ1':<4} {'SZ2':<4}")
print("-" * 40)
final_tips = []
for i, tip in enumerate(generated_tips, 1):
sz1 = random.choice(recommended_sz1)
sz2 = random.choice(recommended_sz2)
tip_str = f"{tip[0]:2}-{tip[1]:2}-{tip[2]:2}-{tip[3]:2}-{tip[4]:2}"
print(f"{i:2}. {tip_str:<20} {sz1:<4} {sz2:<4}")
final_tips.append({
'tipp_nr': i,
'z1': tip[0],
'z2': tip[1],
'z3': tip[2],
'z4': tip[3],
'z5': tip[4],
'sz1': sz1,
'sz2': sz2,
'muster': ''.join(['N' if n <= 15 else 'M' if n <= 35 else 'H' for n in tip])
})
# Export der Tipps
print(f"\n💾 TIPPS EXPORTIEREN:")
print("="*25)
tips_df = pd.DataFrame(final_tips)
output_file = "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/eurojackpot_tipps_nmmhh.csv"
tips_df.to_csv(output_file, sep=';', index=False)
print(f"✅ 10 Tipps gespeichert: eurojackpot_tipps_nmmhh.csv")
print(f"📈 Basierend auf NMMHH-Muster mit 14.7% historischer Erfolgsrate")
print(f"🚫 Keine bereits gezogenen Kombinationen enthalten")
return final_tips
if __name__ == "__main__":
# Zufallsseed für reproduzierbare Ergebnisse (optional)
random.seed(42)
tips = generate_tips_nmmhh(10)
@@ -0,0 +1,234 @@
#!/usr/bin/env python3
"""
Eurojackpot Tipp-Generator für NMMHH-Muster
Generiert 10 Tipp-Felder basierend auf dem erfolgreichsten NMMHH-Muster,
die keine bereits gezogenen Kombinationen enthalten.
"""
import pandas as pd
import random
from itertools import combinations
def load_drawn_numbers():
"""Lädt alle bereits gezogenen Kombinationen."""
df = pd.read_csv("/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/AlleEurojackpotzahlen.csv", sep=';')
drawn_combinations = set()
for _, row in df.iterrows():
# Sortierte Kombination für Vergleich
combo = tuple(sorted([row['Z1'], row['Z2'], row['Z3'], row['Z4'], row['Z5']]))
drawn_combinations.add(combo)
print(f"🚫 {len(drawn_combinations)} bereits gezogene Kombinationen geladen")
return drawn_combinations
def generate_nmmhh_numbers():
"""Generiert Zahlen nach dem NMMHH-Muster."""
# Bereichsdefinition basierend auf umschlüsselter Analyse
ranges = {
'N': list(range(1, 16)), # Niedrig: Gruppen 1-3 (Zahlen 1-15)
'M': list(range(16, 36)), # Mittel: Gruppen 4-7 (Zahlen 16-35)
'H': list(range(36, 51)) # Hoch: Gruppen 8-10 (Zahlen 36-50)
}
# NMMHH-Muster: 1 Niedrig, 2 Mittel, 2 Hoch
selected_numbers = []
# 1 Niedrige Zahl (Position z1)
selected_numbers.extend(random.sample(ranges['N'], 1))
# 2 Mittlere Zahlen (Positionen z2, z3)
selected_numbers.extend(random.sample(ranges['M'], 2))
# 2 Hohe Zahlen (Positionen z4, z5)
selected_numbers.extend(random.sample(ranges['H'], 2))
# Sortieren für Eurojackpot-Format
return sorted(selected_numbers)
def generate_optimized_nmmhh_numbers():
"""Generiert optimierte NMMHH-Zahlen basierend auf Positionsanalyse."""
# Optimierte Bereiche basierend auf Treffer-Analyse
optimized_ranges = {
'z1': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], # Niedrig (sehr wahrscheinlich)
'z2': [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25], # Niedrig-Mittel
'z3': [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35], # Mittel
'z4': [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45], # Mittel-Hoch
'z5': [41, 42, 43, 44, 45, 46, 47, 48, 49, 50] # Hoch (sehr wahrscheinlich)
}
selected_numbers = []
# Position z1: Niedrig
selected_numbers.append(random.choice(optimized_ranges['z1'][:10])) # Fokus auf 1-10
# Position z2: Niedrig-Mittel
selected_numbers.append(random.choice(optimized_ranges['z2'][5:])) # Fokus auf 16-25
# Position z3: Mittel
selected_numbers.append(random.choice(optimized_ranges['z3'])) # 21-35
# Position z4: Mittel-Hoch
selected_numbers.append(random.choice(optimized_ranges['z4'][5:])) # Fokus auf 36-45
# Position z5: Hoch
selected_numbers.append(random.choice(optimized_ranges['z5'])) # 41-50
# Duplikate vermeiden und sortieren
selected_numbers = list(set(selected_numbers))
# Falls durch Duplikat-Entfernung weniger als 5 Zahlen
while len(selected_numbers) < 5:
all_available = list(range(1, 51))
missing_number = random.choice([n for n in all_available if n not in selected_numbers])
selected_numbers.append(missing_number)
return sorted(selected_numbers[:5])
def generate_tips_nmmhh(num_tips=10):
"""Generiert Tipps basierend auf NMMHH-Muster ohne bereits gezogene Kombinationen."""
print("🎯 EUROJACKPOT TIPP-GENERATOR (NMMHH-MUSTER)")
print("="*50)
# Bereits gezogene Kombinationen laden
drawn_combinations = load_drawn_numbers()
# Häufigste Zahlen pro Position aus der Analyse
frequent_numbers = {
'z1': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
'z2': [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25],
'z3': [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35],
'z4': [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45],
'z5': [36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
}
print(f"\n📋 NMMHH-MUSTER BEDEUTUNG:")
print("N = Niedrig (1-15), M = Mittel (16-35), H = Hoch (36-50)")
print("Muster: 1 Niedrig + 2 Mittel + 2 Hoch = 14.7% Erfolgsrate!")
generated_tips = []
attempts = 0
max_attempts = 10000
print(f"\n🎲 GENERIERE {num_tips} OPTIMIERTE TIPPS:")
print("="*40)
while len(generated_tips) < num_tips and attempts < max_attempts:
attempts += 1
# Verschiedene Generierungsstrategien abwechseln
if attempts % 3 == 0:
tip = generate_optimized_nmmhh_numbers()
else:
tip = generate_nmmhh_numbers()
# Prüfen ob bereits gezogen
tip_tuple = tuple(sorted(tip))
if tip_tuple not in drawn_combinations and tip not in generated_tips:
generated_tips.append(tip)
# Muster-Verifikation
pattern = []
for num in tip:
if 1 <= num <= 15:
pattern.append('N')
elif 16 <= num <= 35:
pattern.append('M')
else:
pattern.append('H')
pattern_str = ''.join(pattern)
print(f"Tipp {len(generated_tips):2}: {tip[0]:2}-{tip[1]:2}-{tip[2]:2}-{tip[3]:2}-{tip[4]:2} (Muster: {pattern_str})")
if len(generated_tips) < num_tips:
print(f"\n⚠️ Nur {len(generated_tips)} von {num_tips} Tipps generiert nach {attempts} Versuchen")
else:
print(f"\n✅ Alle {num_tips} Tipps erfolgreich generiert!")
print(f"\n📊 MUSTER-ANALYSE DER GENERIERTEN TIPPS:")
print("="*45)
pattern_counts = {}
for tip in generated_tips:
pattern = []
for num in tip:
if 1 <= num <= 15:
pattern.append('N')
elif 16 <= num <= 35:
pattern.append('M')
else:
pattern.append('H')
pattern_str = ''.join(pattern)
pattern_counts[pattern_str] = pattern_counts.get(pattern_str, 0) + 1
for pattern, count in sorted(pattern_counts.items(), key=lambda x: x[1], reverse=True):
print(f"Muster {pattern}: {count} Tipps")
# Zusätzliche Superzahlen-Empfehlungen
print(f"\n🎲 SUPERZAHLEN-EMPFEHLUNGEN:")
print("="*30)
# Lade Superzahlen-Statistiken
df = pd.read_csv("/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/AlleEurojackpotzahlen.csv", sep=';')
sz1_counts = df['SZ1'].value_counts().head(5)
sz2_counts = df['SZ2'].value_counts().head(5)
print(f"Top 5 SZ1: {list(sz1_counts.index)}")
print(f"Top 5 SZ2: {list(sz2_counts.index)}")
# Empfohlene Superzahlen für die Tipps
recommended_sz1 = list(sz1_counts.index)[:3]
recommended_sz2 = list(sz2_counts.index)[:3]
print(f"\n🎯 KOMPLETTE TIPP-EMPFEHLUNGEN:")
print("="*40)
print(f"{'Tipp':<5} {'Hauptzahlen':<20} {'SZ1':<4} {'SZ2':<4}")
print("-" * 40)
final_tips = []
for i, tip in enumerate(generated_tips, 1):
sz1 = random.choice(recommended_sz1)
sz2 = random.choice(recommended_sz2)
tip_str = f"{tip[0]:2}-{tip[1]:2}-{tip[2]:2}-{tip[3]:2}-{tip[4]:2}"
print(f"{i:2}. {tip_str:<20} {sz1:<4} {sz2:<4}")
final_tips.append({
'tipp_nr': i,
'z1': tip[0],
'z2': tip[1],
'z3': tip[2],
'z4': tip[3],
'z5': tip[4],
'sz1': sz1,
'sz2': sz2,
'muster': ''.join(['N' if n <= 15 else 'M' if n <= 35 else 'H' for n in tip])
})
# Export der Tipps
print(f"\n💾 TIPPS EXPORTIEREN:")
print("="*25)
tips_df = pd.DataFrame(final_tips)
output_file = "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/eurojackpot_tipps_nmmhh.csv"
tips_df.to_csv(output_file, sep=';', index=False)
print(f"✅ 10 Tipps gespeichert: eurojackpot_tipps_nmmhh.csv")
print(f"📈 Basierend auf NMMHH-Muster mit 14.7% historischer Erfolgsrate")
print(f"🚫 Keine bereits gezogenen Kombinationen enthalten")
return final_tips
if __name__ == "__main__":
# Zufallsseed für reproduzierbare Ergebnisse (optional)
random.seed(42)
tips = generate_tips_nmmhh(10)
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env python3
"""
Erstellt Beispiel-CSV-Dateien für das Eurojackpot-Processing
"""
import pandas as pd
import itertools
from datetime import datetime, timedelta
import random
def create_all_combinations():
"""Erstellt eine CSV mit allen möglichen 5-aus-50 Kombinationen (Beispiel mit ersten 100)."""
print("Erstelle Beispiel-Kombinationsdatei...")
# Für Demo: nur erste 100 Kombinationen von 5 aus 20 Zahlen
# (Vollständige 5-aus-50 Kombinationen wären über 2 Millionen!)
combinations = []
count = 0
for combo in itertools.combinations(range(1, 21), 5): # 5 aus 20 für Demo
combinations.append({
'Zahl1': combo[0],
'Zahl2': combo[1],
'Zahl3': combo[2],
'Zahl4': combo[3],
'Zahl5': combo[4]
})
count += 1
if count >= 100: # Limitierung für Demo
break
df = pd.DataFrame(combinations)
df.to_csv('alle_kombinationen_beispiel.csv', index=False)
print(f"Beispiel-Kombinationsdatei erstellt: {len(df)} Kombinationen")
return df
def create_drawn_numbers():
"""Erstellt eine CSV mit gezogenen Zahlen (Beispieldaten)."""
print("Erstelle Beispiel-Datei mit gezogenen Zahlen...")
drawn_numbers = []
start_date = datetime(2023, 1, 1)
# 20 zufällige Ziehungen erstellen
for i in range(20):
date = start_date + timedelta(days=i*7) # Wöchentliche Ziehungen
# 5 zufällige Zahlen zwischen 1 und 20 (ohne Wiederholung)
numbers = sorted(random.sample(range(1, 21), 5))
drawn_numbers.append({
'Datum': date.strftime('%Y-%m-%d'),
'Z1': numbers[0],
'Z2': numbers[1],
'Z3': numbers[2],
'Z4': numbers[3],
'Z5': numbers[4]
})
df = pd.DataFrame(drawn_numbers)
df.to_csv('gezogene_zahlen_beispiel.csv', index=False)
print(f"Beispiel-Datei mit gezogenen Zahlen erstellt: {len(df)} Ziehungen")
return df
if __name__ == "__main__":
print("=== Erstelle Beispiel-CSV-Dateien ===\n")
# Seed für reproduzierbare Ergebnisse
random.seed(42)
combinations_df = create_all_combinations()
drawn_df = create_drawn_numbers()
print("\n=== Beispieldateien erstellt ===")
print("- alle_kombinationen_beispiel.csv")
print("- gezogene_zahlen_beispiel.csv")
print("\nSie können jetzt das Hauptscript testen:")
+73
View File
@@ -0,0 +1,73 @@
#!/bin/bash
#
# Lädt Eurojackpot-Daten von lotto-datenbank.de
# Quelle: https://lotto-datenbank.de
#
echo "🔄 EUROJACKPOT CSV DOWNLOADER"
echo "=========================================="
echo "Quelle: lotto-datenbank.de"
echo "=========================================="
echo ""
# Ziel-Datei
TARGET_FILE="$HOME/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/data/AlleEurojackpotzahlen.csv"
BACKUP_DIR="$HOME/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/data/backups"
TEMP_FILE="/tmp/eurojackpot_download_$(date +%Y%m%d_%H%M%S).csv"
# CSV Download URL (aufsteigend sortiert seit 2012)
DOWNLOAD_URL="https://lotto-datenbank.de/download.php?p1=5&p3=0.75&p31=en"
echo "📥 Download von lotto-datenbank.de..."
echo " URL: $DOWNLOAD_URL"
echo ""
# Download mit curl
if curl -L -o "$TEMP_FILE" "$DOWNLOAD_URL" 2>/dev/null; then
echo " ✅ Download erfolgreich"
# Prüfe Dateigröße
FILE_SIZE=$(wc -c < "$TEMP_FILE")
echo " 📊 Dateigröße: $FILE_SIZE bytes"
if [ "$FILE_SIZE" -lt 1000 ]; then
echo " ❌ Datei zu klein - möglicherweise Fehler"
rm "$TEMP_FILE"
exit 1
fi
# Zeige erste Zeilen
echo ""
echo " 📋 Vorschau (erste 5 Zeilen):"
head -5 "$TEMP_FILE" | sed 's/^/ /'
echo ""
# Backup erstellen falls Datei existiert
if [ -f "$TARGET_FILE" ]; then
echo "💾 Erstelle Backup..."
mkdir -p "$BACKUP_DIR"
BACKUP_NAME="AlleEurojackpotzahlen.csv.backup_$(date +%Y%m%d_%H%M%S)"
cp "$TARGET_FILE" "$BACKUP_DIR/$BACKUP_NAME"
echo " ✅ Backup: $BACKUP_NAME"
echo ""
fi
# Verschiebe neue Datei
echo "💾 Speichere neue Datei..."
mv "$TEMP_FILE" "$TARGET_FILE"
# Zähle Ziehungen (Zeilen - Header)
LINE_COUNT=$(($(wc -l < "$TARGET_FILE") - 1))
echo " ✅ $LINE_COUNT Ziehungen gespeichert"
echo " 📁 Datei: $(basename "$TARGET_FILE")"
echo ""
echo "=========================================="
echo "✅ UPDATE ERFOLGREICH"
echo "=========================================="
else
echo " ❌ Download fehlgeschlagen"
rm -f "$TEMP_FILE"
exit 1
fi
+165
View File
@@ -0,0 +1,165 @@
#!/usr/bin/env python3
"""
Eurojackpot CSV Processor
Dieses Script liest zwei CSV-Dateien ein:
1. Alle möglichen Zahlenkombinationen (5 Zahlen)
2. Bereits gezogene Zahlen mit Datumsstempel
Es markiert in der ersten Datei alle bereits gezogenen Kombinationen mit 1 (sonst 0).
"""
import pandas as pd
import sys
from pathlib import Path
def load_combinations_file(filepath):
"""Lädt die Datei mit allen möglichen Kombinationen."""
try:
df = pd.read_csv(filepath)
print(f"Kombinationen geladen: {len(df)} Zeilen")
print(f"Spalten: {list(df.columns)}")
return df
except Exception as e:
print(f"Fehler beim Laden der Kombinationsdatei: {e}")
return None
def load_drawn_numbers_file(filepath):
"""Lädt die Datei mit bereits gezogenen Zahlen."""
try:
df = pd.read_csv(filepath)
print(f"Gezogene Zahlen geladen: {len(df)} Zeilen")
print(f"Spalten: {list(df.columns)}")
return df
except Exception as e:
print(f"Fehler beim Laden der gezogenen Zahlen: {e}")
return None
def create_combination_key(row, z_columns):
"""Erstellt einen eindeutigen Schlüssel aus den 5 Zahlen (sortiert)."""
numbers = [row[col] for col in z_columns]
return tuple(sorted(numbers))
def process_eurojackpot_data(combinations_file, drawn_numbers_file, output_file):
"""Hauptfunktion zur Verarbeitung der Eurojackpot-Daten."""
# CSV-Dateien laden
print("Lade Kombinationsdatei...")
combinations_df = load_combinations_file(combinations_file)
if combinations_df is None:
return False
print("\nLade Datei mit gezogenen Zahlen...")
drawn_df = load_drawn_numbers_file(drawn_numbers_file)
if drawn_df is None:
return False
# Spalten für Zahlen identifizieren
# Annahme: In der Kombinationsdatei sind die ersten 5 Spalten die Zahlen
# In der gezogenen Zahlen-Datei sind es Z1, Z2, Z3, Z4, Z5
# Für Kombinationsdatei - erste 5 numerische Spalten verwenden
numeric_cols = combinations_df.select_dtypes(include=['number']).columns
if len(numeric_cols) >= 5:
combo_z_columns = numeric_cols[:5].tolist()
else:
# Fallback: erste 5 Spalten nehmen
combo_z_columns = combinations_df.columns[:5].tolist()
print(f"Verwendete Spalten für Kombinationen: {combo_z_columns}")
# Für gezogene Zahlen - Z1 bis Z5 Spalten suchen
z_columns = [col for col in drawn_df.columns if col.startswith('Z') and col[1:].isdigit()]
z_columns = sorted(z_columns)[:5] # Ersten 5 Z-Spalten nehmen
if not z_columns:
# Fallback: nach Spalten mit "Zahl" im Namen suchen oder numerische Spalten
z_columns = [col for col in drawn_df.columns if 'zahl' in col.lower()][:5]
if not z_columns:
z_columns = drawn_df.select_dtypes(include=['number']).columns[:5].tolist()
print(f"Verwendete Spalten für gezogene Zahlen: {z_columns}")
# Set mit allen gezogenen Kombinationen erstellen
print("\nErstelle Set mit gezogenen Kombinationen...")
drawn_combinations = set()
for _, row in drawn_df.iterrows():
combo_key = create_combination_key(row, z_columns)
drawn_combinations.add(combo_key)
print(f"Anzahl eindeutige gezogene Kombinationen: {len(drawn_combinations)}")
# Neue Spalte für Markierungen hinzufügen
print("\nMarkiere gezogene Kombinationen...")
combinations_df['bereits_gezogen'] = 0
marked_count = 0
for idx, row in combinations_df.iterrows():
combo_key = create_combination_key(row, combo_z_columns)
if combo_key in drawn_combinations:
combinations_df.at[idx, 'bereits_gezogen'] = 1
marked_count += 1
print(f"Anzahl markierte Kombinationen: {marked_count}")
# Ergebnis speichern
print(f"\nSpeichere Ergebnis in: {output_file}")
combinations_df.to_csv(output_file, index=False)
# Statistiken ausgeben
total_combinations = len(combinations_df)
drawn_percentage = (marked_count / total_combinations) * 100 if total_combinations > 0 else 0
print(f"\n=== STATISTIKEN ===")
print(f"Gesamte Kombinationen: {total_combinations:,}")
print(f"Bereits gezogene Kombinationen: {marked_count:,}")
print(f"Prozentsatz bereits gezogen: {drawn_percentage:.4f}%")
print(f"Noch nicht gezogene Kombinationen: {total_combinations - marked_count:,}")
return True
def main():
"""Hauptfunktion mit Benutzerinteraktion."""
print("=== Eurojackpot CSV Processor ===\n")
# Dateipfade abfragen oder Standard verwenden
if len(sys.argv) >= 4:
combinations_file = sys.argv[1]
drawn_numbers_file = sys.argv[2]
output_file = sys.argv[3]
else:
print("Geben Sie die Dateipfade ein (oder drücken Sie Enter für Standard):")
combinations_file = input("Pfad zur Kombinationsdatei (alle_kombinationen.csv): ").strip()
if not combinations_file:
combinations_file = "alle_kombinationen.csv"
drawn_numbers_file = input("Pfad zur Datei mit gezogenen Zahlen (gezogene_zahlen.csv): ").strip()
if not drawn_numbers_file:
drawn_numbers_file = "gezogene_zahlen.csv"
output_file = input("Pfad für Ausgabedatei (kombinationen_markiert.csv): ").strip()
if not output_file:
output_file = "kombinationen_markiert.csv"
# Überprüfen ob Dateien existieren
if not Path(combinations_file).exists():
print(f"Fehler: Kombinationsdatei '{combinations_file}' nicht gefunden!")
return
if not Path(drawn_numbers_file).exists():
print(f"Fehler: Datei mit gezogenen Zahlen '{drawn_numbers_file}' nicht gefunden!")
return
# Verarbeitung starten
success = process_eurojackpot_data(combinations_file, drawn_numbers_file, output_file)
if success:
print(f"\n✅ Verarbeitung erfolgreich abgeschlossen!")
print(f"Ergebnis gespeichert in: {output_file}")
else:
print("\n❌ Fehler bei der Verarbeitung!")
if __name__ == "__main__":
main()
@@ -0,0 +1,220 @@
#!/usr/bin/env python3
"""
Eurojackpot CSV Processor - Angepasst für Semikolon-getrennte CSV-Dateien
Dieses Script liest zwei CSV-Dateien ein:
1. Alle möglichen Zahlenkombinationen (5 Zahlen)
2. Bereits gezogene Zahlen mit Datumsstempel
Es markiert in der ersten Datei alle bereits gezogenen Kombinationen mit 1 (sonst 0).
"""
import pandas as pd
import sys
from pathlib import Path
def detect_separator(filepath):
"""Erkennt das CSV-Trennzeichen automatisch."""
try:
with open(filepath, 'r', encoding='utf-8') as f:
first_line = f.readline()
if ';' in first_line and first_line.count(';') > first_line.count(','):
return ';'
return ','
except:
return ','
def load_combinations_file(filepath):
"""Lädt die Datei mit allen möglichen Kombinationen."""
try:
sep = detect_separator(filepath)
print(f"Erkanntes Trennzeichen für Kombinationen: '{sep}'")
df = pd.read_csv(filepath, sep=sep)
print(f"Kombinationen geladen: {len(df)} Zeilen")
print(f"Spalten: {list(df.columns)}")
return df
except Exception as e:
print(f"Fehler beim Laden der Kombinationsdatei: {e}")
return None
def load_drawn_numbers_file(filepath):
"""Lädt die Datei mit bereits gezogenen Zahlen."""
try:
sep = detect_separator(filepath)
print(f"Erkanntes Trennzeichen für gezogene Zahlen: '{sep}'")
df = pd.read_csv(filepath, sep=sep)
print(f"Gezogene Zahlen geladen: {len(df)} Zeilen")
print(f"Spalten: {list(df.columns)}")
return df
except Exception as e:
print(f"Fehler beim Laden der gezogenen Zahlen: {e}")
return None
def identify_number_columns(df, is_combinations=True):
"""Identifiziert die Spalten mit den Zahlen."""
columns = df.columns.tolist()
if is_combinations:
# Für Kombinationsdatei: Z1, Z2, Z3, Z4, Z5 suchen
z_cols = [col for col in columns if col.startswith('Z') and len(col) == 2 and col[1:].isdigit()]
z_cols = sorted(z_cols)[:5]
if len(z_cols) >= 5:
return z_cols
# Fallback: erste 5 numerische Spalten
numeric_cols = df.select_dtypes(include=['number']).columns[:5].tolist()
if len(numeric_cols) >= 5:
return numeric_cols
# Fallback: erste 5 Spalten
return columns[:5]
else:
# Für gezogene Zahlen: Z1-Z5 suchen (nicht SZ1, SZ2)
z_cols = [col for col in columns if col.startswith('Z') and len(col) == 2 and col[1:].isdigit()]
z_cols = [col for col in z_cols if not col.startswith('SZ')] # Superzahlen ausschließen
z_cols = sorted(z_cols)[:5]
if len(z_cols) >= 5:
return z_cols
# Fallback: numerische Spalten (ohne Datum)
numeric_cols = df.select_dtypes(include=['number']).columns
numeric_cols = [col for col in numeric_cols if 'datum' not in col.lower()][:5]
if len(numeric_cols) >= 5:
return numeric_cols.tolist()
# Letzter Fallback
return columns[:5]
def create_combination_key(row, z_columns):
"""Erstellt einen eindeutigen Schlüssel aus den 5 Zahlen (sortiert)."""
try:
numbers = [int(row[col]) for col in z_columns]
return tuple(sorted(numbers))
except:
return None
def process_eurojackpot_data(combinations_file, drawn_numbers_file, output_file):
"""Hauptfunktion zur Verarbeitung der Eurojackpot-Daten."""
# CSV-Dateien laden
print("Lade Kombinationsdatei...")
combinations_df = load_combinations_file(combinations_file)
if combinations_df is None:
return False
print("\nLade Datei mit gezogenen Zahlen...")
drawn_df = load_drawn_numbers_file(drawn_numbers_file)
if drawn_df is None:
return False
# Spalten für Zahlen identifizieren
combo_z_columns = identify_number_columns(combinations_df, is_combinations=True)
drawn_z_columns = identify_number_columns(drawn_df, is_combinations=False)
print(f"\nVerwendete Spalten für Kombinationen: {combo_z_columns}")
print(f"Verwendete Spalten für gezogene Zahlen: {drawn_z_columns}")
# Datencheck
print(f"\nErste Kombination: {combinations_df[combo_z_columns].iloc[0].tolist()}")
print(f"Erste gezogene Zahlen: {drawn_df[drawn_z_columns].iloc[0].tolist()}")
# Set mit allen gezogenen Kombinationen erstellen
print("\nErstelle Set mit gezogenen Kombinationen...")
drawn_combinations = set()
for _, row in drawn_df.iterrows():
combo_key = create_combination_key(row, drawn_z_columns)
if combo_key:
drawn_combinations.add(combo_key)
print(f"Anzahl eindeutige gezogene Kombinationen: {len(drawn_combinations)}")
# Beispiele anzeigen
if drawn_combinations:
print(f"Erste 5 gezogene Kombinationen: {list(drawn_combinations)[:5]}")
# Neue Spalte für Markierungen hinzufügen (falls noch nicht vorhanden)
if 'bereits_gezogen' not in combinations_df.columns:
combinations_df['bereits_gezogen'] = 0
else:
combinations_df['bereits_gezogen'] = 0 # Zurücksetzen
print("\nMarkiere gezogene Kombinationen...")
marked_count = 0
for idx, row in combinations_df.iterrows():
combo_key = create_combination_key(row, combo_z_columns)
if combo_key and combo_key in drawn_combinations:
combinations_df.at[idx, 'bereits_gezogen'] = 1
marked_count += 1
if marked_count <= 5: # Erste 5 Treffer anzeigen
print(f"Treffer gefunden: {combo_key}")
print(f"Anzahl markierte Kombinationen: {marked_count}")
# Ergebnis speichern
print(f"\nSpeichere Ergebnis in: {output_file}")
sep = detect_separator(combinations_file) # Gleiches Trennzeichen wie Eingabe verwenden
combinations_df.to_csv(output_file, sep=sep, index=False)
# Statistiken ausgeben
total_combinations = len(combinations_df)
drawn_percentage = (marked_count / total_combinations) * 100 if total_combinations > 0 else 0
print(f"\n=== STATISTIKEN ===")
print(f"Gesamte Kombinationen: {total_combinations:,}")
print(f"Bereits gezogene Kombinationen: {marked_count:,}")
print(f"Prozentsatz bereits gezogen: {drawn_percentage:.4f}%")
print(f"Noch nicht gezogene Kombinationen: {total_combinations - marked_count:,}")
return True
def main():
"""Hauptfunktion mit Benutzerinteraktion."""
print("=== Eurojackpot CSV Processor (Fixed) ===\n")
# Dateipfade abfragen oder Standard verwenden
if len(sys.argv) >= 4:
combinations_file = sys.argv[1]
drawn_numbers_file = sys.argv[2]
output_file = sys.argv[3]
else:
print("Geben Sie die Dateipfade ein (oder drücken Sie Enter für Standard):")
combinations_file = input("Pfad zur Kombinationsdatei: ").strip()
if not combinations_file:
combinations_file = "Alle_Eurojackpot_Kombinationen_mit_Status.csv"
drawn_numbers_file = input("Pfad zur Datei mit gezogenen Zahlen: ").strip()
if not drawn_numbers_file:
drawn_numbers_file = "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/data/AlleEurojackpotzahlen.csv"
output_file = input("Pfad für Ausgabedatei: ").strip()
if not output_file:
output_file = "Kombinationen_markiert_fixed.csv"
# Überprüfen ob Dateien existieren
if not Path(combinations_file).exists():
print(f"Fehler: Kombinationsdatei '{combinations_file}' nicht gefunden!")
return
if not Path(drawn_numbers_file).exists():
print(f"Fehler: Datei mit gezogenen Zahlen '{drawn_numbers_file}' nicht gefunden!")
return
# Verarbeitung starten
success = process_eurojackpot_data(combinations_file, drawn_numbers_file, output_file)
if success:
print(f"\n✅ Verarbeitung erfolgreich abgeschlossen!")
print(f"Ergebnis gespeichert in: {output_file}")
else:
print("\n❌ Fehler bei der Verarbeitung!")
if __name__ == "__main__":
main()
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env python3
"""
Vereinfachte Version des Eurojackpot Processors
Für spezifische Spaltenstrukturen
"""
import pandas as pd
def process_eurojackpot_simple(combinations_csv, drawn_numbers_csv, output_csv):
"""
Vereinfachte Verarbeitung mit festen Spaltenstrukturen
Parameter:
- combinations_csv: CSV mit allen Kombinationen (Spalten: Zahl1, Zahl2, Zahl3, Zahl4, Zahl5)
- drawn_numbers_csv: CSV mit gezogenen Zahlen (Spalten: Datum, Z1, Z2, Z3, Z4, Z5)
- output_csv: Ausgabedatei
"""
# Dateien laden
print("Lade Kombinationsdatei...")
combinations = pd.read_csv(combinations_csv)
print("Lade gezogene Zahlen...")
drawn = pd.read_csv(drawn_numbers_csv)
# Gezogene Kombinationen als Set erstellen (sortiert für Vergleich)
drawn_sets = set()
for _, row in drawn.iterrows():
# Annahme: Spalten Z1, Z2, Z3, Z4, Z5 enthalten die Zahlen
numbers = sorted([row['Z1'], row['Z2'], row['Z3'], row['Z4'], row['Z5']])
drawn_sets.add(tuple(numbers))
print(f"Gefundene gezogene Kombinationen: {len(drawn_sets)}")
# Neue Spalte hinzufügen
combinations['bereits_gezogen'] = 0
# Jede Kombination prüfen
marked = 0
for idx, row in combinations.iterrows():
# Annahme: erste 5 Spalten enthalten die Zahlenkombination
combo_cols = combinations.columns[:5]
numbers = sorted([row[col] for col in combo_cols])
if tuple(numbers) in drawn_sets:
combinations.at[idx, 'bereits_gezogen'] = 1
marked += 1
# Ergebnis speichern
combinations.to_csv(output_csv, index=False)
print(f"Verarbeitung abgeschlossen!")
print(f"Markierte Kombinationen: {marked}")
print(f"Ergebnis gespeichert in: {output_csv}")
# Beispielaufruf
if __name__ == "__main__":
process_eurojackpot_simple(
"alle_kombinationen.csv",
"gezogene_zahlen.csv",
"kombinationen_markiert.csv"
)
+308
View File
@@ -0,0 +1,308 @@
#!/usr/bin/env python3
"""
Eurojackpot Notification System
Sendet Benachrichtigungen via:
- Telegram Bot
- E-Mail (SMTP)
Konfiguration via config/notifications.json
"""
import os
import json
import requests
from typing import Optional, Dict, List
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import smtplib
class EurojackpotNotifier:
"""Benachrichtigungssystem für Eurojackpot-Events."""
def __init__(self, config_file: Optional[str] = None):
if config_file is None:
# Default config path
script_dir = os.path.dirname(os.path.abspath(__file__))
project_dir = os.path.dirname(os.path.dirname(script_dir))
config_file = os.path.join(project_dir, "config", "notifications.json")
self.config_file = config_file
self.config = self._load_config()
def _load_config(self) -> dict:
"""Lädt Konfiguration."""
if not os.path.exists(self.config_file):
return {
"telegram": {"enabled": False},
"email": {"enabled": False}
}
try:
with open(self.config_file, 'r') as f:
return json.load(f)
except Exception as e:
print(f"⚠️ Fehler beim Laden der Notification-Config: {e}")
return {
"telegram": {"enabled": False},
"email": {"enabled": False}
}
def send_tips_generated(self, tips: List[Dict], timestamp: str, best_tip: Dict):
"""
Benachrichtigung: Neue Tipps generiert.
Args:
tips: Liste der generierten Tipps
timestamp: Zeitstempel der Generierung
best_tip: Bester Tipp mit höchster Confidence
"""
# Formatiere Nachricht
main_numbers = best_tip.get('main_numbers', '?')
euro_numbers = best_tip.get('euro_numbers', '?')
confidence = best_tip.get('confidence', 0)
strategy = best_tip.get('strategy', 'UNKNOWN')
subject = f"🎲 {len(tips)} neue Eurojackpot-Tipps generiert!"
message = f"""🎲 NEUE EUROJACKPOT-TIPPS GENERIERT
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 Anzahl Tipps: {len(tips)}
⏰ Zeitpunkt: {timestamp}
🏆 BESTER TIPP (höchste Confidence):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔢 Hauptzahlen: {main_numbers}
⭐ Eurozahlen: {euro_numbers}
📈 Strategie: {strategy}
💎 Confidence: {confidence:.2%}
💡 Alle Tipps findest du in der CSV-Datei!
Viel Glück! 🍀
"""
# Sende via Telegram
if self.config.get("telegram", {}).get("enabled"):
self._send_telegram(message)
# Sende via Email
if self.config.get("email", {}).get("enabled"):
self._send_email(subject, message)
def send_draw_results(self, draw: Dict, evaluation: Dict, best_match: Dict):
"""
Benachrichtigung: Ziehung evaluiert.
Args:
draw: Gezogene Zahlen
evaluation: Evaluierungsergebnisse
best_match: Bester Tipp
"""
# Formatiere gezogene Zahlen
main = [draw[f'Z{i}'] for i in range(1, 6)]
euro = [draw['SZ1'], draw['SZ2']]
date = draw.get('date', 'unknown')
# Formatiere besten Tipp
best_main_matches = best_match.get('main_matches', 0)
best_euro_matches = best_match.get('euro_matches', 0)
total_matches = best_main_matches + best_euro_matches
# Bewertungs-Emoji
if total_matches >= 5:
emoji = "🎉🎉🎉"
rating = "FANTASTISCH!"
elif total_matches >= 4:
emoji = "🎊"
rating = "Sehr gut!"
elif total_matches >= 3:
emoji = "✅"
rating = "Gut!"
elif total_matches >= 2:
emoji = "👍"
rating = "OK"
else:
emoji = "⚪"
rating = "Nächstes Mal besser"
subject = f"🎰 Ziehung vom {date}: {total_matches} Treffer!"
message = f"""🎰 EUROJACKPOT-ZIEHUNG AUSGEWERTET
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📅 Datum: {date}
🎲 GEZOGENE ZAHLEN:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔢 Hauptzahlen: {'-'.join(map(str, main))}
⭐ Eurozahlen: {'-'.join(map(str, euro))}
{emoji} DEINE BESTEN TREFFER:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔢 Hauptzahlen: {best_main_matches} Treffer
⭐ Eurozahlen: {best_euro_matches} Treffer
🎯 Gesamt: {total_matches} Treffer
📊 Bewertung: {rating}
📈 DURCHSCHNITT ALLER TIPPS:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔢 Main: {evaluation.get('avg_main', 0):.1f} Treffer
⭐ Euro: {evaluation.get('avg_euro', 0):.1f} Treffer
💡 Details im Performance-Report!
"""
# Sende via Telegram
if self.config.get("telegram", {}).get("enabled"):
self._send_telegram(message)
# Sende via Email
if self.config.get("email", {}).get("enabled"):
self._send_email(subject, message)
def send_error(self, error_msg: str, context: str = ""):
"""
Benachrichtigung: Fehler aufgetreten.
Args:
error_msg: Fehlermeldung
context: Kontext (z.B. "Tipp-Generierung")
"""
subject = f"❌ Eurojackpot Fehler: {context}"
message = f"""❌ FEHLER IM EUROJACKPOT-SYSTEM
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📍 Kontext: {context}
🔴 Fehler: {error_msg}
💡 Bitte System-Logs prüfen!
"""
# Sende nur via Telegram (Fehler sind dringender)
if self.config.get("telegram", {}).get("enabled"):
self._send_telegram(message)
def _send_telegram(self, message: str):
"""Sendet Nachricht via Telegram Bot."""
try:
telegram_config = self.config.get("telegram", {})
bot_token = telegram_config.get("bot_token")
chat_id = telegram_config.get("chat_id")
if not bot_token or not chat_id:
return
url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
payload = {
"chat_id": chat_id,
"text": message,
"disable_web_page_preview": True
}
response = requests.post(url, json=payload, timeout=10)
if response.status_code != 200:
error_detail = response.json() if response.text else {}
print(f"⚠️ Telegram API Error: {error_detail}")
response.raise_for_status()
except Exception as e:
print(f"⚠️ Telegram-Benachrichtigung fehlgeschlagen: {e}")
def _send_email(self, subject: str, message: str):
"""Sendet E-Mail via SMTP."""
try:
email_config = self.config.get("email", {})
smtp_server = email_config.get("smtp_server")
smtp_port = email_config.get("smtp_port", 587)
smtp_user = email_config.get("smtp_user")
smtp_password = email_config.get("smtp_password")
from_email = email_config.get("from_email", smtp_user)
to_email = email_config.get("to_email")
if not all([smtp_server, smtp_user, smtp_password, to_email]):
return
# Erstelle E-Mail
msg = MIMEMultipart("alternative")
msg["Subject"] = subject
msg["From"] = from_email
msg["To"] = to_email
# Plain text
text_part = MIMEText(message, "plain", "utf-8")
msg.attach(text_part)
# Sende via SMTP
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.starttls()
server.login(smtp_user, smtp_password)
server.send_message(msg)
except Exception as e:
print(f"⚠️ E-Mail-Benachrichtigung fehlgeschlagen: {e}")
def test_notifications(self):
"""Testet alle konfigurierten Benachrichtigungen."""
print("\n🧪 TESTE BENACHRICHTIGUNGEN")
print("=" * 70)
test_message = """🧪 TEST-BENACHRICHTIGUNG
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Das Eurojackpot-Benachrichtigungssystem funktioniert! ✅
Wenn du diese Nachricht erhältst, ist alles korrekt konfiguriert.
"""
# Test Telegram
if self.config.get("telegram", {}).get("enabled"):
print("\n📱 Teste Telegram...")
self._send_telegram(test_message)
print(" ✅ Telegram-Nachricht gesendet")
# Test Email
if self.config.get("email", {}).get("enabled"):
print("\n📧 Teste E-Mail...")
self._send_email("🧪 Eurojackpot Test", test_message)
print(" ✅ E-Mail gesendet")
print("\n" + "=" * 70)
print("✅ Test abgeschlossen - prüfe deine Nachrichten!")
def main():
"""Test-Funktion."""
import argparse
parser = argparse.ArgumentParser(description="Eurojackpot Notifier Test")
parser.add_argument(
'--test',
action='store_true',
help='Sendet Test-Benachrichtigungen'
)
parser.add_argument(
'--config',
type=str,
help='Pfad zur Config-Datei'
)
args = parser.parse_args()
notifier = EurojackpotNotifier(config_file=args.config)
if args.test:
notifier.test_notifications()
else:
print("Nutze --test um Test-Benachrichtigungen zu senden")
if __name__ == "__main__":
main()
+243
View File
@@ -0,0 +1,243 @@
#!/usr/bin/env python3
"""
Einfacher Eurojackpot Data Updater
Manuelle Eingabe oder CSV-Import von neuen Ziehungen.
Perfekt als Fallback wenn Web-Scraping nicht funktioniert.
"""
import pandas as pd
from datetime import datetime
import shutil
import os
def load_existing_data(data_file):
"""Lädt existierende Daten."""
print("📂 Lade existierende Daten...")
if not os.path.exists(data_file):
print(" ⚠️ Datei existiert nicht - erstelle neue")
return pd.DataFrame(columns=['datum', 'Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'SZ1', 'SZ2'])
df = pd.read_csv(data_file, sep=';')
df['datum'] = pd.to_datetime(df['datum'], format='%Y-%m-%d', errors='coerce')
print(f" ✅ {len(df)} Ziehungen geladen")
if len(df) > 0:
print(f" 📅 Neueste: {df['datum'].max().strftime('%Y-%m-%d')}")
return df
def create_backup(data_file):
"""Erstellt Backup."""
if not os.path.exists(data_file):
return
print("\n💾 Erstelle Backup...")
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_dir = os.path.join(os.path.dirname(data_file), "backups")
os.makedirs(backup_dir, exist_ok=True)
backup_file = os.path.join(backup_dir, f"{os.path.basename(data_file)}.backup_{timestamp}")
shutil.copy2(data_file, backup_file)
print(f" ✅ Backup: {os.path.basename(backup_file)}")
def manual_input_mode():
"""Manuelle Eingabe von Ziehungen."""
print("\n⌨️ MANUELLE EINGABE")
print("=" * 70)
print("Format: YYYY-MM-DD Z1 Z2 Z3 Z4 Z5 SZ1 SZ2")
print("Beispiel: 2025-01-24 7 18 26 37 46 3 11")
print("Leer lassen zum Beenden.\n")
draws = []
while True:
user_input = input(f"Ziehung {len(draws) + 1}: ").strip()
if not user_input:
break
try:
parts = user_input.split()
if len(parts) != 8:
print(" ❌ Ungültiges Format. Bitte 8 Werte eingeben.")
continue
date_obj = datetime.strptime(parts[0], '%Y-%m-%d')
numbers = [int(parts[i]) for i in range(1, 6)]
euro_numbers = [int(parts[i]) for i in range(6, 8)]
# Validierung
if not all(1 <= n <= 50 for n in numbers):
print(" ❌ Hauptzahlen müssen zwischen 1 und 50 liegen.")
continue
if not all(1 <= n <= 12 for n in euro_numbers):
print(" ❌ Eurozahlen müssen zwischen 1 und 12 liegen.")
continue
if len(set(numbers)) != 5:
print(" ❌ Hauptzahlen müssen eindeutig sein.")
continue
if len(set(euro_numbers)) != 2:
print(" ❌ Eurozahlen müssen eindeutig sein.")
continue
# Sortiere Zahlen
numbers_sorted = sorted(numbers)
euro_sorted = sorted(euro_numbers)
draws.append({
'datum': date_obj,
'Z1': numbers_sorted[0],
'Z2': numbers_sorted[1],
'Z3': numbers_sorted[2],
'Z4': numbers_sorted[3],
'Z5': numbers_sorted[4],
'SZ1': euro_sorted[0],
'SZ2': euro_sorted[1]
})
print(f" ✅ Hinzugefügt: {date_obj.strftime('%Y-%m-%d')} | "
f"{numbers_sorted[0]}-{numbers_sorted[1]}-{numbers_sorted[2]}-{numbers_sorted[3]}-{numbers_sorted[4]} | "
f"{euro_sorted[0]}-{euro_sorted[1]}")
except ValueError as e:
print(f" ❌ Fehler: {e}")
continue
return draws
def csv_import_mode():
"""CSV-Import von Ziehungen."""
print("\n📁 CSV-IMPORT")
print("=" * 70)
print("CSV-Format: datum;Z1;Z2;Z3;Z4;Z5;SZ1;SZ2")
print("Beispiel: 2025-01-24;7;18;26;37;46;3;11\n")
csv_file = input("Pfad zur CSV-Datei: ").strip()
if not os.path.exists(csv_file):
print(f" ❌ Datei nicht gefunden: {csv_file}")
return []
try:
df_import = pd.read_csv(csv_file, sep=';')
df_import['datum'] = pd.to_datetime(df_import['datum'], format='%Y-%m-%d', errors='coerce')
draws = df_import.to_dict('records')
print(f" ✅ {len(draws)} Ziehungen aus CSV geladen")
# Zeige erste 3
for i, draw in enumerate(draws[:3]):
print(f" {i+1}. {draw['datum'].strftime('%Y-%m-%d')} | "
f"{draw['Z1']}-{draw['Z2']}-{draw['Z3']}-{draw['Z4']}-{draw['Z5']} | "
f"{draw['SZ1']}-{draw['SZ2']}")
if len(draws) > 3:
print(f" ... und {len(draws) - 3} weitere")
return draws
except Exception as e:
print(f" ❌ Fehler beim Laden: {e}")
return []
def merge_and_save(df_existing, new_draws, data_file):
"""Merged und speichert Daten."""
if not new_draws:
print("\n⚠️ Keine neuen Ziehungen zum Hinzufügen")
return False
print(f"\n🔀 Merge {len(new_draws)} neue Ziehung(en)...")
df_new = pd.DataFrame(new_draws)
df_combined = pd.concat([df_existing, df_new], ignore_index=True)
# Duplikate entfernen
before = len(df_combined)
df_combined = df_combined.drop_duplicates(subset=['datum'], keep='first')
after = len(df_combined)
if before - after > 0:
print(f" 🗑️ {before - after} Duplikat(e) entfernt")
# Sortieren
df_combined = df_combined.sort_values('datum', ascending=True).reset_index(drop=True)
# Speichern
print(f"\n💾 Speichere {len(df_combined)} Ziehungen...")
df_export = df_combined.copy()
df_export['datum'] = df_export['datum'].dt.strftime('%Y-%m-%d')
df_export.to_csv(data_file, sep=';', index=False)
print(f" ✅ Gespeichert: {data_file}")
print(f" 📊 Vorher: {len(df_existing)} | Neu: {len(df_combined) - len(df_existing)} | Gesamt: {len(df_combined)}")
return True
def main():
"""Hauptfunktion."""
print()
print("=" * 70)
print(" EUROJACKPOT SIMPLE UPDATER")
print(" Manuelle Eingabe oder CSV-Import")
print("=" * 70)
print()
# Datei-Pfad
default_file = "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/data/AlleEurojackpotzahlen.csv"
print(f"Standard-Datei: {default_file}")
use_default = input("\nStandard verwenden? (j/n): ").strip().lower()
if use_default in ['j', 'ja', 'y', 'yes', '']:
data_file = default_file
else:
data_file = input("Pfad zur Datei: ").strip()
print()
# Lade existierende Daten
df_existing = load_existing_data(data_file)
# Backup
create_backup(data_file)
# Eingabe-Modus wählen
print("\n📝 EINGABE-MODUS")
print("=" * 70)
print("1. Manuelle Eingabe (einzelne Ziehungen)")
print("2. CSV-Import (mehrere Ziehungen)")
print()
mode = input("Wahl (1/2): ").strip()
if mode == '2':
new_draws = csv_import_mode()
else:
new_draws = manual_input_mode()
# Merge und speichern
success = merge_and_save(df_existing, new_draws, data_file)
print()
print("=" * 70)
if success:
print("✅ UPDATE ERFOLGREICH")
else:
print("⚠️ KEINE ÄNDERUNGEN")
print("=" * 70)
if __name__ == "__main__":
main()
+556
View File
@@ -0,0 +1,556 @@
#!/usr/bin/env python3
"""
Eurojackpot API Data Updater
Lädt Eurojackpot-Ziehungen von verschiedenen APIs:
1. lottoAPI (https://lottoapi.herokuapp.com)
2. Lottoland API
3. Sazka.cz API (Fallback)
Features:
- Automatisches Laden von APIs
- Fallback auf andere APIs wenn primäre fehlschlägt
- Duplikate-Vermeidung
- Automatisches Backup
"""
import requests
import pandas as pd
from datetime import datetime
import shutil
import os
from typing import List, Dict, Optional
import time
class EurojackpotAPIUpdater:
"""Updater für Eurojackpot über APIs."""
def __init__(self, data_file: str):
self.data_file = data_file
self.backup_file = None
self.df_existing = None
# API-Endpoints
self.apis = {
'lottoapi': {
'url': 'https://lottoapi.herokuapp.com/eurojackpot-results/100',
'name': 'lottoAPI',
'parser': self._parse_lottoapi
},
'lottoland': {
'url': 'https://media.lottoland.com/api/drawings/euroJackpot',
'name': 'Lottoland API',
'parser': self._parse_lottoland
},
'sazka': {
'url': 'https://www.sazka.cz/api/draw-info/past-draws/eurojackpot',
'name': 'Sazka.cz API',
'parser': self._parse_sazka
}
}
print("🔄 EUROJACKPOT API UPDATER")
print("=" * 70)
print(f"📁 Datei: {os.path.basename(data_file)}")
print("=" * 70)
def load_existing_data(self) -> bool:
"""Lädt existierende Daten."""
print("\n📂 Lade existierende Daten...")
try:
if not os.path.exists(self.data_file):
print(f" ⚠️ Datei existiert nicht - erstelle neue")
self.df_existing = pd.DataFrame(
columns=['datum', 'Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'SZ1', 'SZ2']
)
return True
self.df_existing = pd.read_csv(self.data_file, sep=';')
# Konvertiere Datum
if 'datum' in self.df_existing.columns:
self.df_existing['datum'] = pd.to_datetime(
self.df_existing['datum'],
format='%Y-%m-%d',
errors='coerce'
)
print(f" ✅ {len(self.df_existing)} Ziehungen geladen")
if len(self.df_existing) > 0:
latest = self.df_existing['datum'].max()
oldest = self.df_existing['datum'].min()
print(f" 📅 Zeitraum: {oldest.strftime('%Y-%m-%d')} bis {latest.strftime('%Y-%m-%d')}")
return True
except Exception as e:
print(f" ❌ Fehler: {e}")
return False
def create_backup(self) -> bool:
"""Erstellt Backup."""
if not os.path.exists(self.data_file):
return True
try:
print("\n💾 Erstelle Backup...")
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_dir = os.path.join(os.path.dirname(self.data_file), "backups")
os.makedirs(backup_dir, exist_ok=True)
filename = os.path.basename(self.data_file)
self.backup_file = os.path.join(backup_dir, f"{filename}.backup_{timestamp}")
shutil.copy2(self.data_file, self.backup_file)
print(f" ✅ Backup: {os.path.basename(self.backup_file)}")
return True
except Exception as e:
print(f" ⚠️ Backup-Fehler: {e}")
return False
def fetch_from_api(self, api_name: str) -> List[Dict]:
"""
Holt Daten von spezifischer API.
Args:
api_name: Name der API ('lottoapi', 'lottoland', 'sazka')
Returns:
Liste von Ziehungen
"""
if api_name not in self.apis:
print(f" ❌ Unbekannte API: {api_name}")
return []
api = self.apis[api_name]
print(f"\n🌐 Versuche {api['name']}...")
print(f" URL: {api['url']}")
try:
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
}
response = requests.get(api['url'], headers=headers, timeout=15)
response.raise_for_status()
print(f" ✅ Antwort erhalten ({len(response.content)} bytes)")
# Parse mit spezifischem Parser
draws = api['parser'](response.json())
if draws:
print(f" ✅ {len(draws)} Ziehungen extrahiert")
else:
print(f" ⚠️ Keine Ziehungen extrahiert")
return draws
except requests.RequestException as e:
print(f" ❌ Netzwerkfehler: {e}")
return []
except Exception as e:
print(f" ❌ Fehler: {e}")
return []
def _parse_lottoapi(self, data: List[Dict]) -> List[Dict]:
"""
Parst lottoAPI Response.
Expected format:
[
{
"date": "2025-01-17",
"numbers": [3, 9, 12, 24, 39],
"euroNumbers": [5, 10]
},
...
]
"""
draws = []
try:
for item in data:
# Datum
date_str = item.get('date')
date_obj = datetime.strptime(date_str, '%Y-%m-%d')
# Hauptzahlen
numbers = sorted(item.get('numbers', []))
if len(numbers) != 5:
continue
# Eurozahlen
euro_numbers = sorted(item.get('euroNumbers', []))
if len(euro_numbers) != 2:
continue
draw = {
'datum': date_obj,
'Z1': numbers[0],
'Z2': numbers[1],
'Z3': numbers[2],
'Z4': numbers[3],
'Z5': numbers[4],
'SZ1': euro_numbers[0],
'SZ2': euro_numbers[1]
}
if self._validate_draw(draw):
draws.append(draw)
except Exception as e:
print(f" ⚠️ Parse-Fehler: {e}")
return draws
def _parse_lottoland(self, data: Dict) -> List[Dict]:
"""
Parst Lottoland API Response.
Expected format: {'last': {...}, 'next': {...}}
"""
draws = []
try:
# Lottoland gibt nur die letzte Ziehung zurück
if 'last' in data:
item = data['last']
# Datum extrahieren aus verschachteltem 'date' Objekt
date_info = item.get('date', {})
day = date_info.get('day')
month = date_info.get('month')
year = date_info.get('year')
if day and month and year:
date_obj = datetime(year, month, day)
else:
return draws
# Hauptzahlen
numbers = sorted(item.get('numbers', []))
if len(numbers) != 5:
return draws
# Eurozahlen
euro_numbers = sorted(item.get('euroNumbers', []))
if len(euro_numbers) != 2:
return draws
draw = {
'datum': date_obj,
'Z1': numbers[0],
'Z2': numbers[1],
'Z3': numbers[2],
'Z4': numbers[3],
'Z5': numbers[4],
'SZ1': euro_numbers[0],
'SZ2': euro_numbers[1]
}
if self._validate_draw(draw):
draws.append(draw)
print(f" ✅ Ziehung vom {date_obj.strftime('%Y-%m-%d')} extrahiert")
except Exception as e:
print(f" ⚠️ Parse-Fehler: {e}")
return draws
def _parse_sazka(self, data: Dict) -> List[Dict]:
"""
Parst Sazka.cz API Response.
Sazka hat zwei Steps:
1. Liste von Draw IDs holen
2. Für jede Draw ID Details holen
"""
draws = []
try:
# Erste Response enthält Liste von Ziehungen
if isinstance(data, list):
past_draws = data
elif isinstance(data, dict) and 'draws' in data:
past_draws = data['draws']
else:
return draws
print(f" 📊 {len(past_draws)} Ziehungen gefunden, hole Details...")
# Begrenzen auf letzte 10 Ziehungen (um Requests zu sparen)
for i, draw_item in enumerate(past_draws[:10]):
draw_id = draw_item.get('id') # Nicht 'drawId' sondern 'id'!
if not draw_id:
continue
# Hole Details für diese Ziehung
detail_url = f"https://www.sazka.cz/api/draw-info/draws/universal/eurojackpot/{draw_id}"
try:
response = requests.get(detail_url, timeout=10)
detail_data = response.json()
# Parse Detail
date_str = detail_data.get('date') # Nicht 'drawDate' sondern 'date'
date_obj = datetime.strptime(date_str, '%Y-%m-%d')
# Zahlen sind direkt in 'numbers' und 'euroNumbers'
numbers = sorted(detail_data.get('numbers', []))
euro_numbers = sorted(detail_data.get('euroNumbers', []))
if len(numbers) == 5 and len(euro_numbers) == 2:
draw = {
'datum': date_obj,
'Z1': numbers[0],
'Z2': numbers[1],
'Z3': numbers[2],
'Z4': numbers[3],
'Z5': numbers[4],
'SZ1': euro_numbers[0],
'SZ2': euro_numbers[1]
}
if self._validate_draw(draw):
draws.append(draw)
if (i + 1) % 5 == 0:
print(f" ⏳ {i + 1}/10 Details geladen...")
time.sleep(0.3) # Rate limiting
except Exception as e:
print(f" ⚠️ Fehler bei Draw ID {draw_id}: {e}")
continue
print(f" ✅ {len(draws)} gültige Ziehungen extrahiert")
except Exception as e:
print(f" ⚠️ Parse-Fehler: {e}")
return draws
def _validate_draw(self, draw: Dict) -> bool:
"""Validiert eine Ziehung."""
try:
required_fields = ['datum', 'Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'SZ1', 'SZ2']
if not all(field in draw for field in required_fields):
return False
main_numbers = [draw[f'Z{i}'] for i in range(1, 6)]
if not all(isinstance(n, int) and 1 <= n <= 50 for n in main_numbers):
return False
if len(set(main_numbers)) != 5:
return False
euro_numbers = [draw['SZ1'], draw['SZ2']]
if not all(isinstance(n, int) and 1 <= n <= 12 for n in euro_numbers):
return False
if len(set(euro_numbers)) != 2:
return False
if not isinstance(draw['datum'], datetime):
return False
return True
except Exception:
return False
def fetch_from_all_apis(self) -> List[Dict]:
"""
Versucht alle APIs nacheinander.
Returns:
Liste von Ziehungen
"""
print("\n🔍 SUCHE NACH DATEN VON APIs")
print("=" * 70)
all_draws = []
# Versuche alle APIs
for api_name in ['lottoapi', 'lottoland', 'sazka']:
draws = self.fetch_from_api(api_name)
if draws:
all_draws.extend(draws)
print(f" ✅ {len(draws)} Ziehungen von {self.apis[api_name]['name']}")
break # Erste erfolgreiche API nutzen
else:
print(f" ⏭️ Weiter zur nächsten API...")
time.sleep(1) # Pause zwischen APIs
if not all_draws:
print("\n ❌ Keine Daten von APIs erhalten")
return all_draws
def merge_with_existing(self, new_draws: List[Dict]) -> pd.DataFrame:
"""Merged neue Ziehungen mit existierenden Daten."""
print(f"\n🔀 Merge mit existierenden Daten...")
if not new_draws:
print(" ⚠️ Keine neuen Ziehungen zum Mergen")
return self.df_existing
df_new = pd.DataFrame(new_draws)
if self.df_existing is None or len(self.df_existing) == 0:
df_combined = df_new
print(f" ✅ Neue Datei erstellt mit {len(df_combined)} Ziehungen")
else:
df_combined = pd.concat([self.df_existing, df_new], ignore_index=True)
before_dedup = len(df_combined)
df_combined = df_combined.drop_duplicates(subset=['datum'], keep='first')
after_dedup = len(df_combined)
duplicates = before_dedup - after_dedup
if duplicates > 0:
print(f" 🗑️ {duplicates} Duplikat(e) entfernt")
df_combined = df_combined.sort_values('datum', ascending=True)
df_combined = df_combined.reset_index(drop=True)
new_entries = len(df_combined) - (len(self.df_existing) if self.df_existing is not None else 0)
print(f" ✅ Merge abgeschlossen:")
print(f" Vorher: {len(self.df_existing) if self.df_existing is not None else 0} Ziehungen")
print(f" Neue: {new_entries} Ziehungen")
print(f" Gesamt: {len(df_combined)} Ziehungen")
return df_combined
def save_data(self, df: pd.DataFrame) -> bool:
"""Speichert Daten."""
try:
print(f"\n💾 Speichere Daten...")
df_export = df.copy()
df_export['datum'] = df_export['datum'].dt.strftime('%Y-%m-%d')
column_order = ['datum', 'Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'SZ1', 'SZ2']
df_export = df_export[column_order]
df_export.to_csv(self.data_file, sep=';', index=False)
print(f" ✅ Gespeichert: {self.data_file}")
print(f" 📊 {len(df)} Ziehungen total")
if len(df) > 0:
latest = df['datum'].max()
oldest = df['datum'].min()
print(f" 📅 Zeitraum: {oldest.strftime('%Y-%m-%d')} bis {latest.strftime('%Y-%m-%d')}")
return True
except Exception as e:
print(f" ❌ Speicherfehler: {e}")
if self.backup_file and os.path.exists(self.backup_file):
print(" 🔄 Stelle Backup wieder her...")
shutil.copy2(self.backup_file, self.data_file)
print(" ✅ Backup wiederhergestellt")
return False
def update(self, api_name: str = 'all', create_backup: bool = True) -> bool:
"""
Führt komplettes Update durch.
Args:
api_name: 'all', 'lottoapi', 'lottoland', 'sazka'
create_backup: Backup erstellen
Returns:
True bei Erfolg
"""
print("\n🚀 STARTE UPDATE")
print("=" * 70)
if not self.load_existing_data():
return False
if create_backup:
self.create_backup()
if api_name == 'all':
new_draws = self.fetch_from_all_apis()
else:
new_draws = self.fetch_from_api(api_name)
if not new_draws:
print("\n❌ Keine Daten von APIs geladen")
return False
df_updated = self.merge_with_existing(new_draws)
success = self.save_data(df_updated)
print("\n" + "=" * 70)
if success:
print("✅ UPDATE ERFOLGREICH ABGESCHLOSSEN")
else:
print("❌ UPDATE FEHLGESCHLAGEN")
print("=" * 70)
return success
def main():
"""Hauptfunktion."""
import sys
print()
print("=" * 70)
print(" EUROJACKPOT API UPDATER")
print(" Unterstützt: lottoAPI, Lottoland, Sazka.cz")
print("=" * 70)
print()
default_file = "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/data/AlleEurojackpotzahlen.csv"
if len(sys.argv) > 1:
data_file = sys.argv[1]
api_name = sys.argv[2] if len(sys.argv) > 2 else 'all'
else:
print(f"Standard-Datei: {default_file}")
use_default = input("\nStandard verwenden? (j/n): ").strip().lower()
if use_default in ['j', 'ja', 'y', 'yes', '']:
data_file = default_file
else:
data_file = input("Pfad zur Datei: ").strip()
print("\n🌐 API WÄHLEN")
print("=" * 60)
print("1. Alle APIs versuchen (empfohlen)")
print("2. lottoAPI")
print("3. Lottoland API")
print("4. Sazka.cz API")
print()
api_choice = input("Wahl (1-4): ").strip()
api_map = {'1': 'all', '2': 'lottoapi', '3': 'lottoland', '4': 'sazka'}
api_name = api_map.get(api_choice, 'all')
print()
updater = EurojackpotAPIUpdater(data_file)
success = updater.update(api_name=api_name)
sys.exit(0 if success else 1)
if __name__ == "__main__":
main()
@@ -0,0 +1,449 @@
#!/usr/bin/env python3
"""
Eurojackpot Data Updater für eurojackpot-zahlen.eu
Optimierter Scraper für https://www.eurojackpot-zahlen.eu/eurojackpot-zahlenarchiv.php
Features:
- Automatisches Scraping aller historischen Ziehungen
- Duplikate-Vermeidung
- Automatisches Backup vor Update
- Validierung der Daten
- Fortschrittsanzeige
"""
import pandas as pd
import requests
from bs4 import BeautifulSoup
from datetime import datetime
import shutil
import os
from typing import List, Dict, Optional
class EurojackpotUpdater:
"""Updater für eurojackpot-zahlen.eu"""
def __init__(self, data_file: str):
self.data_file = data_file
self.url = "https://www.eurojackpot-zahlen.eu/eurojackpot-zahlenarchiv.php"
self.backup_file = None
self.df_existing = None
print("🔄 EUROJACKPOT DATA UPDATER")
print("=" * 70)
print(f"📁 Datei: {os.path.basename(data_file)}")
print(f"🌐 Quelle: {self.url}")
print("=" * 70)
def load_existing_data(self) -> bool:
"""Lädt existierende Daten."""
print("\n📂 Lade existierende Daten...")
try:
if not os.path.exists(self.data_file):
print(f" ⚠️ Datei existiert nicht - erstelle neue")
self.df_existing = pd.DataFrame(
columns=['datum', 'Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'SZ1', 'SZ2']
)
return True
self.df_existing = pd.read_csv(self.data_file, sep=';')
# Konvertiere Datum
if 'datum' in self.df_existing.columns:
self.df_existing['datum'] = pd.to_datetime(
self.df_existing['datum'],
format='%Y-%m-%d',
errors='coerce'
)
print(f" ✅ {len(self.df_existing)} Ziehungen geladen")
if len(self.df_existing) > 0:
latest = self.df_existing['datum'].max()
oldest = self.df_existing['datum'].min()
print(f" 📅 Zeitraum: {oldest.strftime('%Y-%m-%d')} bis {latest.strftime('%Y-%m-%d')}")
return True
except Exception as e:
print(f" ❌ Fehler: {e}")
return False
def create_backup(self) -> bool:
"""Erstellt Backup."""
if not os.path.exists(self.data_file):
return True
try:
print("\n💾 Erstelle Backup...")
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_dir = os.path.join(os.path.dirname(self.data_file), "backups")
os.makedirs(backup_dir, exist_ok=True)
filename = os.path.basename(self.data_file)
self.backup_file = os.path.join(backup_dir, f"{filename}.backup_{timestamp}")
shutil.copy2(self.data_file, self.backup_file)
print(f" ✅ Backup: {os.path.basename(self.backup_file)}")
return True
except Exception as e:
print(f" ⚠️ Backup-Fehler: {e}")
return False
def fetch_draws_from_web(self) -> List[Dict]:
"""
Scrapt alle Ziehungen von eurojackpot-zahlen.eu
Returns:
Liste von Ziehungen
"""
print(f"\n🌐 Lade Daten von {self.url}...")
try:
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
response = requests.get(self.url, headers=headers, timeout=15)
response.raise_for_status()
print(" ✅ Seite erfolgreich geladen")
print(" 🔍 Parse HTML-Struktur...")
soup = BeautifulSoup(response.content, 'html.parser')
# Neue Struktur: divs mit Klasse "zahlen_rahmen"
# Jeder zahlen_rahmen enthält: zahlen_hinweis01 (#), zahlen_hinweis02 (Datum),
# zahlen_hinweis03 (Zahlen), zahlen_hinweis04 (Eurozahlen)
containers = soup.find_all('div', class_='zahlen_rahmen')
if not containers:
print(" ❌ Keine zahlen_rahmen Container gefunden")
# Debug-Info
print(f" 🔍 Debug: Seite enthält {len(soup.find_all('div'))} divs")
print(f" 🔍 Debug: Erste 500 Zeichen: {response.text[:500]}")
return []
print(f" 📊 {len(containers)} Container gefunden")
print(" ⏳ Extrahiere Ziehungen...")
draws = []
# Überspringe ersten Container (Header)
for i, container in enumerate(containers[1:], 1):
try:
# Extrahiere Datum aus <time> Element
time_element = container.find('time', class_='zahlenarchiv_datum')
if not time_element:
continue
date_text = time_element.text.strip()
date_obj = self._parse_date(date_text)
# Extrahiere Hauptzahlen (zahlenarchiv_zahl)
main_number_divs = container.find_all('div', class_='zahlenarchiv_zahl')
main_numbers = [int(div.text.strip()) for div in main_number_divs if div.text.strip().isdigit()]
# Extrahiere Eurozahlen (zahlenarchiv_zz)
euro_number_divs = container.find_all('div', class_='zahlenarchiv_zz')
euro_numbers = [int(div.text.strip()) for div in euro_number_divs if div.text.strip().isdigit()]
# Validierung
if len(main_numbers) != 5:
print(f" ⚠️ Zeile {i+1}: Ungültige Anzahl Hauptzahlen ({len(main_numbers)})")
continue
if len(euro_numbers) != 2:
print(f" ⚠️ Zeile {i+1}: Ungültige Anzahl Eurozahlen ({len(euro_numbers)})")
continue
# Sortiere Hauptzahlen
main_numbers_sorted = sorted(main_numbers)
# Sortiere Eurozahlen
euro_numbers_sorted = sorted(euro_numbers)
draw = {
'datum': date_obj,
'Z1': main_numbers_sorted[0],
'Z2': main_numbers_sorted[1],
'Z3': main_numbers_sorted[2],
'Z4': main_numbers_sorted[3],
'Z5': main_numbers_sorted[4],
'SZ1': euro_numbers_sorted[0],
'SZ2': euro_numbers_sorted[1]
}
# Validiere
if self._validate_draw(draw):
draws.append(draw)
else:
print(f" ⚠️ Zeile {i+1}: Validierung fehlgeschlagen")
# Fortschrittsanzeige
if (i + 1) % 50 == 0:
print(f" ⏳ {i + 1}/{len(data_rows)} Zeilen verarbeitet...")
except Exception as e:
print(f" ⚠️ Zeile {i+1}: Fehler beim Parsen - {e}")
continue
print(f" ✅ {len(draws)} gültige Ziehungen extrahiert")
return draws
except requests.RequestException as e:
print(f" ❌ Netzwerkfehler: {e}")
return []
except Exception as e:
print(f" ❌ Fehler: {e}")
return []
def _parse_date(self, date_str: str) -> datetime:
"""
Parst Datum im Format "DD.MM.YYYY"
Args:
date_str: Datum als String
Returns:
datetime Objekt
"""
try:
# Format: "03.01.2025"
return datetime.strptime(date_str, '%d.%m.%Y')
except ValueError:
try:
# Fallback: "YYYY-MM-DD"
return datetime.strptime(date_str, '%Y-%m-%d')
except ValueError:
# Letzter Fallback: Aktuelles Datum
print(f" ⚠️ Konnte Datum nicht parsen: {date_str}")
return datetime.now()
def _validate_draw(self, draw: Dict) -> bool:
"""
Validiert eine Ziehung.
Args:
draw: Ziehungs-Dictionary
Returns:
True wenn valide
"""
try:
# Prüfe Pflichtfelder
required_fields = ['datum', 'Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'SZ1', 'SZ2']
if not all(field in draw for field in required_fields):
return False
# Prüfe Hauptzahlen (1-50)
main_numbers = [draw[f'Z{i}'] for i in range(1, 6)]
if not all(isinstance(n, int) and 1 <= n <= 50 for n in main_numbers):
return False
# Prüfe Eindeutigkeit
if len(set(main_numbers)) != 5:
return False
# Prüfe Eurozahlen (1-12)
euro_numbers = [draw['SZ1'], draw['SZ2']]
if not all(isinstance(n, int) and 1 <= n <= 12 for n in euro_numbers):
return False
# Prüfe Eindeutigkeit
if len(set(euro_numbers)) != 2:
return False
# Prüfe Datum
if not isinstance(draw['datum'], datetime):
return False
return True
except Exception:
return False
def merge_with_existing(self, new_draws: List[Dict]) -> pd.DataFrame:
"""
Merged neue Ziehungen mit existierenden Daten.
Args:
new_draws: Liste von neuen Ziehungen
Returns:
Zusammengeführter DataFrame
"""
print(f"\n🔀 Merge mit existierenden Daten...")
if not new_draws:
print(" ⚠️ Keine neuen Ziehungen zum Mergen")
return self.df_existing
# Erstelle DataFrame aus neuen Ziehungen
df_new = pd.DataFrame(new_draws)
# Kombiniere
if self.df_existing is None or len(self.df_existing) == 0:
df_combined = df_new
print(f" ✅ Neue Datei erstellt mit {len(df_combined)} Ziehungen")
else:
df_combined = pd.concat([self.df_existing, df_new], ignore_index=True)
# Entferne Duplikate (basierend auf Datum)
before_dedup = len(df_combined)
df_combined = df_combined.drop_duplicates(subset=['datum'], keep='first')
after_dedup = len(df_combined)
duplicates = before_dedup - after_dedup
if duplicates > 0:
print(f" 🗑️ {duplicates} Duplikat(e) entfernt")
# Sortiere nach Datum (aufsteigend)
df_combined = df_combined.sort_values('datum', ascending=True)
df_combined = df_combined.reset_index(drop=True)
# Statistiken
new_entries = len(df_combined) - (len(self.df_existing) if self.df_existing is not None else 0)
print(f" ✅ Merge abgeschlossen:")
print(f" Vorher: {len(self.df_existing) if self.df_existing is not None else 0} Ziehungen")
print(f" Neue: {new_entries} Ziehungen")
print(f" Gesamt: {len(df_combined)} Ziehungen")
return df_combined
def save_data(self, df: pd.DataFrame) -> bool:
"""
Speichert Daten in CSV.
Args:
df: DataFrame zum Speichern
Returns:
True bei Erfolg
"""
try:
print(f"\n💾 Speichere Daten...")
# Erstelle Kopie für Export
df_export = df.copy()
# Format Datum als String
df_export['datum'] = df_export['datum'].dt.strftime('%Y-%m-%d')
# Sortiere Spalten
column_order = ['datum', 'Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'SZ1', 'SZ2']
df_export = df_export[column_order]
# Speichere
df_export.to_csv(self.data_file, sep=';', index=False)
print(f" ✅ Gespeichert: {self.data_file}")
print(f" 📊 {len(df)} Ziehungen total")
if len(df) > 0:
latest = df['datum'].max()
oldest = df['datum'].min()
print(f" 📅 Zeitraum: {oldest.strftime('%Y-%m-%d')} bis {latest.strftime('%Y-%m-%d')}")
return True
except Exception as e:
print(f" ❌ Speicherfehler: {e}")
# Restore backup falls vorhanden
if self.backup_file and os.path.exists(self.backup_file):
print(" 🔄 Stelle Backup wieder her...")
shutil.copy2(self.backup_file, self.data_file)
print(" ✅ Backup wiederhergestellt")
return False
def update(self, create_backup: bool = True) -> bool:
"""
Führt komplettes Update durch.
Args:
create_backup: Backup vor Update erstellen
Returns:
True bei Erfolg
"""
print("\n🚀 STARTE UPDATE")
print("=" * 70)
# 1. Lade existierende Daten
if not self.load_existing_data():
return False
# 2. Backup erstellen
if create_backup:
self.create_backup()
# 3. Hole Ziehungen von Web
new_draws = self.fetch_draws_from_web()
if not new_draws:
print("\n❌ Keine Daten von Website geladen")
return False
# 4. Merge
df_updated = self.merge_with_existing(new_draws)
# 5. Speichern
success = self.save_data(df_updated)
print("\n" + "=" * 70)
if success:
print("✅ UPDATE ERFOLGREICH ABGESCHLOSSEN")
else:
print("❌ UPDATE FEHLGESCHLAGEN")
print("=" * 70)
return success
def main():
"""Hauptfunktion."""
import sys
print()
print("=" * 70)
print(" EUROJACKPOT DATA UPDATER")
print(" Quelle: eurojackpot-zahlen.eu")
print("=" * 70)
print()
# Standard-Pfad
default_file = "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/data/AlleEurojackpotzahlen.csv"
# Command-line Argumente
if len(sys.argv) > 1:
data_file = sys.argv[1]
else:
print(f"Standard-Datei: {default_file}")
print()
use_default = input("Standard verwenden? (j/n): ").strip().lower()
if use_default in ['j', 'ja', 'y', 'yes', '']:
data_file = default_file
else:
data_file = input("Pfad zur Datei: ").strip()
# Update durchführen
print()
updater = EurojackpotUpdater(data_file)
success = updater.update()
# Exit-Code
sys.exit(0 if success else 1)
if __name__ == "__main__":
main()
+681
View File
@@ -0,0 +1,681 @@
#!/usr/bin/env python3
"""
Eurojackpot Historical Data Updater
Lädt automatisch die neuesten Eurojackpot-Ziehungen von der offiziellen Website
und aktualisiert die lokale CSV-Datei.
Quellen:
- https://www.eurojackpot.de/de/eurojackpot/gewinnzahlen.html
- Oder alternative APIs/Websites
Features:
- Automatisches Scraping der neuesten Ziehungen
- Duplikate-Vermeidung
- Backup vor Update
- Validierung der neuen Daten
"""
import pandas as pd
import requests
from bs4 import BeautifulSoup
from datetime import datetime
import shutil
import os
import re
from typing import List, Dict, Optional
import time
class EurojackpotDataUpdater:
"""Aktualisiert historische Eurojackpot-Daten automatisch."""
def __init__(self, data_file: str):
"""
Initialisiert den Updater.
Args:
data_file: Pfad zur lokalen CSV-Datei
"""
self.data_file = data_file
self.backup_file = None
self.df_existing = None
# API/Scraping URLs
self.sources = {
'eurojackpot_de': 'https://www.eurojackpot.de/de/eurojackpot/gewinnzahlen.html',
'euro-jackpot_net': 'https://www.euro-jackpot.net/de/gewinnzahlen',
'lottode': 'https://www.lotto.de/eurojackpot/gewinnzahlen'
}
print("🔄 EUROJACKPOT DATA UPDATER")
print("=" * 60)
def load_existing_data(self) -> bool:
"""Lädt existierende Daten."""
try:
if not os.path.exists(self.data_file):
print(f"⚠️ Datei nicht gefunden: {self.data_file}")
print(" Erstelle neue Datei...")
self.df_existing = pd.DataFrame(columns=['datum', 'Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'SZ1', 'SZ2'])
return True
self.df_existing = pd.read_csv(self.data_file, sep=';')
# Datum als datetime
if 'datum' in self.df_existing.columns:
self.df_existing['datum'] = pd.to_datetime(
self.df_existing['datum'],
format='%Y-%m-%d',
errors='coerce'
)
print(f"✅ Existierende Daten geladen: {len(self.df_existing)} Ziehungen")
if len(self.df_existing) > 0:
latest = self.df_existing['datum'].max()
print(f" Neueste Ziehung: {latest.strftime('%Y-%m-%d') if pd.notna(latest) else 'Unbekannt'}")
return True
except Exception as e:
print(f"❌ Fehler beim Laden: {e}")
return False
def create_backup(self) -> bool:
"""Erstellt Backup der existierenden Datei."""
if not os.path.exists(self.data_file):
return True
try:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_dir = os.path.join(os.path.dirname(self.data_file), "backups")
os.makedirs(backup_dir, exist_ok=True)
filename = os.path.basename(self.data_file)
self.backup_file = os.path.join(backup_dir, f"{filename}.backup_{timestamp}")
shutil.copy2(self.data_file, self.backup_file)
print(f"✅ Backup erstellt: {os.path.basename(self.backup_file)}")
return True
except Exception as e:
print(f"⚠️ Backup-Fehler: {e}")
return False
def fetch_from_eurojackpot_de(self) -> List[Dict]:
"""
Scrapt Daten von eurojackpot.de
Returns:
Liste von Ziehungen als Dictionaries
"""
print("\n🌐 Versuche eurojackpot.de...")
try:
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
}
response = requests.get(self.sources['eurojackpot_de'], headers=headers, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.content, 'html.parser')
# Suche nach Ziehungsergebnissen
# Dies ist ein generisches Beispiel - muss an tatsächliche Website-Struktur angepasst werden
draws = []
# Beispiel-Parsing (muss angepasst werden!)
result_blocks = soup.find_all('div', class_='drawing-result')
for block in result_blocks[:10]: # Letzte 10 Ziehungen
try:
# Datum extrahieren
date_elem = block.find('span', class_='date')
if not date_elem:
continue
date_str = date_elem.text.strip()
date_obj = self._parse_german_date(date_str)
# Zahlen extrahieren
numbers = []
number_elems = block.find_all('span', class_='ball')
for num_elem in number_elems[:5]:
num = int(num_elem.text.strip())
numbers.append(num)
# Eurozahlen extrahieren
euro_numbers = []
euro_elems = block.find_all('span', class_='euro-ball')
for euro_elem in euro_elems[:2]:
euro_num = int(euro_elem.text.strip())
euro_numbers.append(euro_num)
if len(numbers) == 5 and len(euro_numbers) == 2:
draws.append({
'datum': date_obj,
'Z1': numbers[0],
'Z2': numbers[1],
'Z3': numbers[2],
'Z4': numbers[3],
'Z5': numbers[4],
'SZ1': euro_numbers[0],
'SZ2': euro_numbers[1]
})
except Exception as e:
continue
if draws:
print(f" ✅ {len(draws)} Ziehungen gefunden")
return draws
else:
print(" ⚠️ Keine Ziehungen gefunden (HTML-Struktur möglicherweise geändert)")
return []
except Exception as e:
print(f" ❌ Fehler: {e}")
return []
def fetch_from_euro_jackpot_net(self) -> List[Dict]:
"""
Scrapt Daten von euro-jackpot.net
Returns:
Liste von Ziehungen
"""
print("\n🌐 Versuche euro-jackpot.net...")
try:
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
}
response = requests.get(self.sources['euro-jackpot_net'], headers=headers, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.content, 'html.parser')
draws = []
# Parsing-Logik für diese Website
# (Platzhalter - muss an tatsächliche Struktur angepasst werden)
result_rows = soup.find_all('tr', class_='result-row')
for row in result_rows[:10]:
try:
cells = row.find_all('td')
if len(cells) < 8:
continue
# Datum
date_str = cells[0].text.strip()
date_obj = self._parse_german_date(date_str)
# Hauptzahlen
numbers = [int(cells[i].text.strip()) for i in range(1, 6)]
# Eurozahlen
euro_numbers = [int(cells[i].text.strip()) for i in range(6, 8)]
draws.append({
'datum': date_obj,
'Z1': numbers[0],
'Z2': numbers[1],
'Z3': numbers[2],
'Z4': numbers[3],
'Z5': numbers[4],
'SZ1': euro_numbers[0],
'SZ2': euro_numbers[1]
})
except Exception as e:
continue
if draws:
print(f" ✅ {len(draws)} Ziehungen gefunden")
else:
print(" ⚠️ Keine Ziehungen gefunden")
return draws
except Exception as e:
print(f" ❌ Fehler: {e}")
return []
def fetch_from_manual_input(self) -> List[Dict]:
"""
Manuelle Eingabe von neuen Ziehungen.
Returns:
Liste von Ziehungen
"""
print("\n⌨️ MANUELLE EINGABE")
print("=" * 60)
print("Gib die neuesten Ziehungen manuell ein.")
print("Format: YYYY-MM-DD Z1 Z2 Z3 Z4 Z5 SZ1 SZ2")
print("Beispiel: 2025-01-24 7 18 26 37 46 3 11")
print("Leer lassen zum Beenden.\n")
draws = []
while True:
user_input = input(f"Ziehung {len(draws) + 1}: ").strip()
if not user_input:
break
try:
parts = user_input.split()
if len(parts) != 8:
print(" ❌ Ungültiges Format. Bitte 8 Werte eingeben.")
continue
date_obj = datetime.strptime(parts[0], '%Y-%m-%d')
numbers = [int(parts[i]) for i in range(1, 6)]
euro_numbers = [int(parts[i]) for i in range(6, 8)]
# Validierung
if not all(1 <= n <= 50 for n in numbers):
print(" ❌ Hauptzahlen müssen zwischen 1 und 50 liegen.")
continue
if not all(1 <= n <= 12 for n in euro_numbers):
print(" ❌ Eurozahlen müssen zwischen 1 und 12 liegen.")
continue
if len(set(numbers)) != 5:
print(" ❌ Hauptzahlen müssen eindeutig sein.")
continue
if len(set(euro_numbers)) != 2:
print(" ❌ Eurozahlen müssen eindeutig sein.")
continue
draws.append({
'datum': date_obj,
'Z1': numbers[0],
'Z2': numbers[1],
'Z3': numbers[2],
'Z4': numbers[3],
'Z5': numbers[4],
'SZ1': euro_numbers[0],
'SZ2': euro_numbers[1]
})
print(f" ✅ Ziehung hinzugefügt: {date_obj.strftime('%Y-%m-%d')}")
except ValueError as e:
print(f" ❌ Fehler: {e}")
continue
if draws:
print(f"\n✅ {len(draws)} Ziehungen manuell eingegeben")
return draws
def _parse_german_date(self, date_str: str) -> datetime:
"""
Parst deutsches Datumsformat.
Args:
date_str: Datum als String (z.B. "24.01.2025" oder "24. Januar 2025")
Returns:
datetime Objekt
"""
# Entferne zusätzliche Leerzeichen
date_str = re.sub(r'\s+', ' ', date_str.strip())
# Monatsnamen-Mapping
months_de = {
'januar': 1, 'februar': 2, 'märz': 3, 'april': 4,
'mai': 5, 'juni': 6, 'juli': 7, 'august': 8,
'september': 9, 'oktober': 10, 'november': 11, 'dezember': 12
}
# Versuche verschiedene Formate
formats = [
'%d.%m.%Y',
'%d.%m.%y',
'%Y-%m-%d',
'%d/%m/%Y'
]
for fmt in formats:
try:
return datetime.strptime(date_str, fmt)
except ValueError:
continue
# Versuche mit Monatsnamen
for month_name, month_num in months_de.items():
if month_name.lower() in date_str.lower():
# Extrahiere Tag und Jahr
match = re.search(r'(\d{1,2})\.?\s+' + month_name + r'\s+(\d{4})', date_str, re.IGNORECASE)
if match:
day = int(match.group(1))
year = int(match.group(2))
return datetime(year, month_num, day)
# Fallback: aktuelles Datum
print(f" ⚠️ Konnte Datum nicht parsen: {date_str}, nutze aktuelles Datum")
return datetime.now()
def fetch_new_draws(self, source: str = 'auto') -> List[Dict]:
"""
Holt neue Ziehungen von der gewählten Quelle.
Args:
source: 'auto', 'eurojackpot_de', 'euro_jackpot_net', 'manual'
Returns:
Liste von neuen Ziehungen
"""
print(f"\n🔍 SUCHE NACH NEUEN ZIEHUNGEN (Quelle: {source})")
print("=" * 60)
all_draws = []
if source == 'auto':
# Versuche alle Quellen
sources_to_try = [
('eurojackpot_de', self.fetch_from_eurojackpot_de),
('euro_jackpot_net', self.fetch_from_euro_jackpot_net)
]
for source_name, fetch_func in sources_to_try:
draws = fetch_func()
if draws:
all_draws.extend(draws)
break # Erste erfolgreiche Quelle nutzen
time.sleep(1) # Pause zwischen Requests
if not all_draws:
print("\n⚠️ Automatisches Scraping fehlgeschlagen.")
print(" Möchtest du Daten manuell eingeben? (j/n): ", end='')
if input().lower() in ['j', 'ja', 'y', 'yes']:
all_draws = self.fetch_from_manual_input()
elif source == 'eurojackpot_de':
all_draws = self.fetch_from_eurojackpot_de()
elif source == 'euro_jackpot_net':
all_draws = self.fetch_from_euro_jackpot_net()
elif source == 'manual':
all_draws = self.fetch_from_manual_input()
else:
print(f"❌ Unbekannte Quelle: {source}")
return all_draws
def validate_draw(self, draw: Dict) -> bool:
"""
Validiert eine Ziehung.
Args:
draw: Ziehungs-Dictionary
Returns:
True wenn valide
"""
try:
# Prüfe Pflichtfelder
required_fields = ['datum', 'Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'SZ1', 'SZ2']
if not all(field in draw for field in required_fields):
return False
# Prüfe Hauptzahlen (1-50)
main_numbers = [draw[f'Z{i}'] for i in range(1, 6)]
if not all(1 <= n <= 50 for n in main_numbers):
return False
if len(set(main_numbers)) != 5: # Eindeutig
return False
# Prüfe Eurozahlen (1-12)
euro_numbers = [draw['SZ1'], draw['SZ2']]
if not all(1 <= n <= 12 for n in euro_numbers):
return False
if len(set(euro_numbers)) != 2: # Eindeutig
return False
# Prüfe Datum
if not isinstance(draw['datum'], datetime):
return False
return True
except Exception:
return False
def merge_with_existing(self, new_draws: List[Dict]) -> pd.DataFrame:
"""
Merged neue Ziehungen mit existierenden Daten.
Args:
new_draws: Liste von neuen Ziehungen
Returns:
Zusammengeführter DataFrame
"""
print(f"\n🔀 MERGE MIT EXISTIERENDEN DATEN")
print("=" * 60)
# Validiere neue Ziehungen
valid_draws = [draw for draw in new_draws if self.validate_draw(draw)]
if len(valid_draws) < len(new_draws):
invalid_count = len(new_draws) - len(valid_draws)
print(f"⚠️ {invalid_count} ungültige Ziehung(en) übersprungen")
if not valid_draws:
print("❌ Keine gültigen neuen Ziehungen zum Hinzufügen")
return self.df_existing
# Erstelle DataFrame aus neuen Ziehungen
df_new = pd.DataFrame(valid_draws)
# Kombiniere
if self.df_existing is None or len(self.df_existing) == 0:
df_combined = df_new
else:
df_combined = pd.concat([self.df_existing, df_new], ignore_index=True)
# Entferne Duplikate (basierend auf Datum + Zahlen)
before_dedup = len(df_combined)
df_combined = df_combined.drop_duplicates(
subset=['datum', 'Z1', 'Z2', 'Z3', 'Z4', 'Z5'],
keep='first'
)
after_dedup = len(df_combined)
duplicates_removed = before_dedup - after_dedup
if duplicates_removed > 0:
print(f"🗑️ {duplicates_removed} Duplikat(e) entfernt")
# Sortiere nach Datum
df_combined = df_combined.sort_values('datum', ascending=True)
df_combined = df_combined.reset_index(drop=True)
# Berechne neue Einträge
new_entries = len(df_combined) - len(self.df_existing) if self.df_existing is not None else len(df_combined)
print(f"✅ Merge abgeschlossen:")
print(f" Vorher: {len(self.df_existing) if self.df_existing is not None else 0} Ziehungen")
print(f" Neu hinzugefügt: {new_entries} Ziehungen")
print(f" Nachher: {len(df_combined)} Ziehungen")
return df_combined
def save_updated_data(self, df: pd.DataFrame) -> bool:
"""
Speichert aktualisierte Daten.
Args:
df: DataFrame zum Speichern
Returns:
True bei Erfolg
"""
try:
# Format Datum als String
df_to_save = df.copy()
df_to_save['datum'] = df_to_save['datum'].dt.strftime('%Y-%m-%d')
# Speichern
df_to_save.to_csv(self.data_file, sep=';', index=False)
print(f"\n💾 Daten gespeichert: {self.data_file}")
print(f" {len(df)} Ziehungen total")
if len(df) > 0:
latest = df['datum'].max()
print(f" Neueste Ziehung: {latest.strftime('%Y-%m-%d')}")
return True
except Exception as e:
print(f"❌ Fehler beim Speichern: {e}")
# Restore backup
if self.backup_file and os.path.exists(self.backup_file):
print("🔄 Stelle Backup wieder her...")
shutil.copy2(self.backup_file, self.data_file)
print("✅ Backup wiederhergestellt")
return False
def update(self, source: str = 'auto', dry_run: bool = False) -> bool:
"""
Führt komplettes Update durch.
Args:
source: Datenquelle ('auto', 'eurojackpot_de', 'euro_jackpot_net', 'manual')
dry_run: Wenn True, keine Änderungen speichern
Returns:
True bei Erfolg
"""
print(f"\n{'🧪 DRY RUN MODE' if dry_run else '🚀 UPDATE STARTEN'}")
print("=" * 60)
# 1. Lade existierende Daten
if not self.load_existing_data():
return False
# 2. Backup erstellen
if not dry_run:
self.create_backup()
# 3. Hole neue Ziehungen
new_draws = self.fetch_new_draws(source=source)
if not new_draws:
print("\n✅ Keine neuen Ziehungen gefunden - Daten sind aktuell")
return True
# 4. Merge mit existierenden Daten
df_updated = self.merge_with_existing(new_draws)
# 5. Speichern
if not dry_run:
return self.save_updated_data(df_updated)
else:
print("\n🧪 DRY RUN - Keine Änderungen gespeichert")
print(f" Würde {len(df_updated)} Ziehungen speichern")
return True
def main():
"""Hauptfunktion für interaktive Nutzung."""
print("=" * 70)
print(" EUROJACKPOT HISTORICAL DATA UPDATER")
print("=" * 70)
print()
# Konfiguration
default_data_file = "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/data/AlleEurojackpotzahlen.csv"
print("📁 KONFIGURATION")
print("=" * 60)
print(f"Standard-Datei: {default_data_file}")
print()
use_default = input("Standard-Datei verwenden? (j/n): ").lower().strip()
if use_default in ['j', 'ja', 'y', 'yes', '']:
data_file = default_data_file
else:
data_file = input("Pfad zur Datendatei: ").strip()
print()
print("🌐 DATENQUELLE WÄHLEN")
print("=" * 60)
print("1. Auto (versucht alle Quellen)")
print("2. eurojackpot.de")
print("3. euro-jackpot.net")
print("4. Manuelle Eingabe")
print()
source_choice = input("Wahl (1-4): ").strip()
source_map = {
'1': 'auto',
'2': 'eurojackpot_de',
'3': 'euro_jackpot_net',
'4': 'manual'
}
source = source_map.get(source_choice, 'auto')
print()
print("🧪 DRY RUN?")
print("=" * 60)
print("Dry Run = Keine Änderungen, nur Vorschau")
dry_run_choice = input("Dry Run aktivieren? (j/n): ").lower().strip()
dry_run = dry_run_choice in ['j', 'ja', 'y', 'yes']
print()
# Update durchführen
updater = EurojackpotDataUpdater(data_file)
success = updater.update(source=source, dry_run=dry_run)
print()
print("=" * 70)
if success:
print("✅ UPDATE ERFOLGREICH ABGESCHLOSSEN")
else:
print("❌ UPDATE FEHLGESCHLAGEN")
print("=" * 70)
return success
if __name__ == "__main__":
import sys
# Einfache CLI
if len(sys.argv) > 1:
# Kommandozeilen-Modus
data_file = sys.argv[1]
source = sys.argv[2] if len(sys.argv) > 2 else 'auto'
dry_run = '--dry-run' in sys.argv
updater = EurojackpotDataUpdater(data_file)
success = updater.update(source=source, dry_run=dry_run)
sys.exit(0 if success else 1)
else:
# Interaktiver Modus
main()
+460
View File
@@ -0,0 +1,460 @@
#!/usr/bin/env python3
"""
CSV-Validator für Lotto 6aus49 und Eurojackpot
Validiert CSV-Dateien auf:
- Korrekte Spaltenstruktur
- Datumformat und -konsistenz
- Zahlenbereich und Eindeutigkeit
- Wochentag (nur Mi/Sa für Lotto, nur Di/Fr für Eurojackpot)
- Chronologische Sortierung
- Duplikate
- Fehlende Werte
- Zukunftsdaten
"""
import pandas as pd
from datetime import datetime
import sys
import os
from typing import List, Dict, Tuple
class LottoCSVValidator:
"""Validator für Lotto 6aus49 CSV-Dateien."""
def __init__(self, csv_file: str):
self.csv_file = csv_file
self.df = None
self.errors = []
self.warnings = []
self.lottery_type = self._detect_lottery_type()
def _detect_lottery_type(self) -> str:
"""Erkennt ob Lotto oder Eurojackpot basierend auf Dateiname."""
basename = os.path.basename(self.csv_file).lower()
if 'eurojackpot' in basename:
return 'eurojackpot'
elif 'lotto' in basename:
return 'lotto'
else:
# Versuche anhand der Spalten zu erkennen
return 'unknown'
def load_csv(self) -> bool:
"""Lädt CSV-Datei."""
try:
if not os.path.exists(self.csv_file):
self.errors.append(f"❌ Datei existiert nicht: {self.csv_file}")
return False
self.df = pd.read_csv(self.csv_file, sep=';')
# Auto-detect wenn noch unknown
if self.lottery_type == 'unknown':
if 'SZ2' in self.df.columns:
self.lottery_type = 'eurojackpot'
elif 'SZ' in self.df.columns:
self.lottery_type = 'lotto'
else:
self.errors.append("❌ Kann Lottery-Typ nicht erkennen")
return False
return True
except Exception as e:
self.errors.append(f"❌ Fehler beim Laden: {e}")
return False
def validate_structure(self) -> bool:
"""Validiert Spaltenstruktur."""
if self.lottery_type == 'lotto':
expected_cols = ['datum', 'Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'Z6', 'SZ']
elif self.lottery_type == 'eurojackpot':
expected_cols = ['datum', 'Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'SZ1', 'SZ2']
else:
self.errors.append("❌ Unbekannter Lottery-Typ")
return False
actual_cols = list(self.df.columns)
if actual_cols != expected_cols:
self.errors.append(f"❌ Spaltenstruktur falsch")
self.errors.append(f" Erwartet: {expected_cols}")
self.errors.append(f" Gefunden: {actual_cols}")
return False
return True
def validate_dates(self) -> bool:
"""Validiert Datumsspalte."""
valid = True
# Prüfe auf leere Daten
empty_dates = self.df[self.df['datum'].isna() | (self.df['datum'] == '')]
if len(empty_dates) > 0:
self.errors.append(f"❌ {len(empty_dates)} Zeile(n) mit fehlendem Datum:")
for idx in empty_dates.index[:5]: # Zeige max 5
self.errors.append(f" Zeile {idx + 2}")
if len(empty_dates) > 5:
self.errors.append(f" ... und {len(empty_dates) - 5} weitere")
valid = False
# Konvertiere Datum
try:
self.df['datum'] = pd.to_datetime(self.df['datum'], format='%Y-%m-%d', errors='coerce')
except Exception as e:
self.errors.append(f"❌ Fehler beim Parsen der Datumsangaben: {e}")
return False
# Prüfe auf ungültige Datumsangaben
invalid_dates = self.df[self.df['datum'].isna()]
if len(invalid_dates) > 0:
self.errors.append(f"❌ {len(invalid_dates)} ungültige Datumsangaben")
valid = False
# Prüfe auf Zukunftsdaten
today = datetime.now()
future_dates = self.df[self.df['datum'] > today]
if len(future_dates) > 0:
self.errors.append(f"❌ {len(future_dates)} Datum/Daten in der Zukunft:")
for idx, row in future_dates.iterrows():
self.errors.append(f" Zeile {idx + 2}: {row['datum'].strftime('%Y-%m-%d')}")
valid = False
# Prüfe Wochentage
if self.lottery_type == 'lotto':
# Historisch:
# 1955-1965: Sonntags (und manchmal Montags bei Feiertagen)
# 1965-2000: Samstags
# Ab 2000: Mittwoch + Samstag
weekday_names = {0: 'Mo', 1: 'Di', 2: 'Mi', 3: 'Do', 4: 'Fr', 5: 'Sa', 6: 'So'}
else: # eurojackpot
# Eurojackpot: Dienstag + Freitag
weekday_names = {0: 'Mo', 1: 'Di', 2: 'Mi', 3: 'Do', 4: 'Fr', 5: 'Sa', 6: 'So'}
wrong_weekdays = []
for idx, row in self.df.iterrows():
if pd.notna(row['datum']):
weekday = row['datum'].weekday()
date = row['datum']
if self.lottery_type == 'lotto':
# Lotto: Historische Regeln
if date < datetime(1965, 9, 1):
# Bis Aug 1965: Sonntag (und Montag bei Feiertagen)
if weekday not in [0, 6]: # Mo, So
wrong_weekdays.append((idx, date))
elif date < datetime(2000, 12, 1):
# Sep 1965 - Nov 2000: Nur Samstag
if weekday != 5: # Sa
wrong_weekdays.append((idx, date))
else:
# Ab Dez 2000: Mittwoch + Samstag
if weekday not in [2, 5]: # Mi, Sa
wrong_weekdays.append((idx, date))
else:
# Eurojackpot: Dienstag + Freitag
if weekday not in [1, 4]: # Di, Fr
wrong_weekdays.append((idx, date))
if len(wrong_weekdays) > 0:
# Filtere mögliche Feiertags-Sonderziehungen (< 5 pro Zeitraum = OK)
if len(wrong_weekdays) <= 5:
self.warnings.append(f"⚠️ {len(wrong_weekdays)} Ziehung(en) am ungewöhnlichen Wochentag (möglicherweise Feiertage):")
for idx, date in wrong_weekdays:
weekday_name = weekday_names[date.weekday()]
self.warnings.append(f" Zeile {idx + 2}: {date.strftime('%Y-%m-%d')} ({weekday_name})")
else:
self.errors.append(f"❌ {len(wrong_weekdays)} Ziehung(en) am falschen Wochentag:")
for idx, date in wrong_weekdays[:5]:
weekday_name = weekday_names[date.weekday()]
self.errors.append(f" Zeile {idx + 2}: {date.strftime('%Y-%m-%d')} ({weekday_name})")
if len(wrong_weekdays) > 5:
self.errors.append(f" ... und {len(wrong_weekdays) - 5} weitere")
valid = False
return valid
def validate_numbers(self) -> bool:
"""Validiert Zahlenwerte."""
valid = True
if self.lottery_type == 'lotto':
main_cols = ['Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'Z6']
main_range = (1, 49)
sz_cols = ['SZ']
sz_range = (0, 9)
else: # eurojackpot
main_cols = ['Z1', 'Z2', 'Z3', 'Z4', 'Z5']
main_range = (1, 50)
sz_cols = ['SZ1', 'SZ2']
sz_range = (1, 12)
# Prüfe Hauptzahlen
for col in main_cols:
# Fehlende Werte
missing = self.df[self.df[col].isna()]
if len(missing) > 0:
self.errors.append(f"❌ {len(missing)} fehlende Werte in Spalte {col}")
valid = False
# Zahlenbereich
out_of_range = self.df[
(self.df[col] < main_range[0]) |
(self.df[col] > main_range[1])
]
if len(out_of_range) > 0:
self.errors.append(f"❌ {len(out_of_range)} Werte außerhalb {main_range} in {col}")
for idx, row in out_of_range.head(3).iterrows():
self.errors.append(f" Zeile {idx + 2}: {row[col]}")
valid = False
# Prüfe Superzahl(en)
for col in sz_cols:
missing = self.df[self.df[col].isna()]
if len(missing) > 0:
self.warnings.append(f"⚠️ {len(missing)} fehlende Werte in Spalte {col}")
out_of_range = self.df[
(self.df[col] < sz_range[0]) |
(self.df[col] > sz_range[1])
]
if len(out_of_range) > 0:
self.errors.append(f"❌ {len(out_of_range)} Werte außerhalb {sz_range} in {col}")
valid = False
# Prüfe Eindeutigkeit der Hauptzahlen pro Zeile
duplicate_numbers = []
for idx, row in self.df.iterrows():
main_numbers = [row[col] for col in main_cols if pd.notna(row[col])]
if len(main_numbers) != len(set(main_numbers)):
duplicate_numbers.append((idx, main_numbers))
if len(duplicate_numbers) > 0:
self.errors.append(f"❌ {len(duplicate_numbers)} Zeile(n) mit doppelten Hauptzahlen:")
for idx, numbers in duplicate_numbers[:5]:
self.errors.append(f" Zeile {idx + 2}: {numbers}")
if len(duplicate_numbers) > 5:
self.errors.append(f" ... und {len(duplicate_numbers) - 5} weitere")
valid = False
# Prüfe Sortierung der Hauptzahlen pro Zeile
unsorted_rows = []
for idx, row in self.df.iterrows():
main_numbers = [row[col] for col in main_cols if pd.notna(row[col])]
if main_numbers != sorted(main_numbers):
unsorted_rows.append((idx, main_numbers))
if len(unsorted_rows) > 0:
self.warnings.append(f"⚠️ {len(unsorted_rows)} Zeile(n) mit unsortierten Zahlen:")
for idx, numbers in unsorted_rows[:3]:
self.warnings.append(f" Zeile {idx + 2}: {numbers}")
# Prüfe Sortierung der Eurozahlen (nur Eurojackpot)
if self.lottery_type == 'eurojackpot':
unsorted_euro = []
for idx, row in self.df.iterrows():
if pd.notna(row['SZ1']) and pd.notna(row['SZ2']):
if row['SZ1'] > row['SZ2']:
unsorted_euro.append((idx, row['SZ1'], row['SZ2']))
if len(unsorted_euro) > 0:
self.warnings.append(f"⚠️ {len(unsorted_euro)} Zeile(n) mit unsortierten Eurozahlen")
return valid
def validate_duplicates(self) -> bool:
"""Prüft auf doppelte Datumssätze."""
duplicates = self.df[self.df.duplicated(subset=['datum'], keep=False)]
if len(duplicates) > 0:
self.errors.append(f"❌ {len(duplicates)} doppelte Datumseinträge gefunden:")
for idx, row in duplicates.head(5).iterrows():
self.errors.append(f" Zeile {idx + 2}: {row['datum'].strftime('%Y-%m-%d')}")
return False
return True
def validate_chronology(self) -> bool:
"""Prüft chronologische Sortierung."""
if len(self.df) < 2:
return True
unsorted = []
for i in range(len(self.df) - 1):
if self.df.iloc[i]['datum'] > self.df.iloc[i + 1]['datum']:
unsorted.append((i, self.df.iloc[i]['datum'], self.df.iloc[i + 1]['datum']))
if len(unsorted) > 0:
self.warnings.append(f"⚠️ {len(unsorted)} Stelle(n) mit falscher chronologischer Reihenfolge:")
for idx, date1, date2 in unsorted[:3]:
self.warnings.append(f" Zeile {idx + 2}: {date1.strftime('%Y-%m-%d')} > {date2.strftime('%Y-%m-%d')}")
return len(unsorted) == 0
def validate_completeness(self) -> bool:
"""Prüft auf Lücken in den Ziehungen."""
if len(self.df) < 2:
return True
if self.lottery_type == 'lotto':
# Lotto: 2x pro Woche (Mi, Sa)
expected_days = 3.5 # Durchschnitt
else:
# Eurojackpot: 2x pro Woche (Di, Fr)
expected_days = 3.5
gaps = []
for i in range(len(self.df) - 1):
date1 = self.df.iloc[i]['datum']
date2 = self.df.iloc[i + 1]['datum']
diff = (date2 - date1).days
# Wenn Lücke größer als 7 Tage, könnte Ziehung fehlen
if diff > 7:
gaps.append((i, date1, date2, diff))
if len(gaps) > 0:
self.warnings.append(f"⚠️ {len(gaps)} mögliche Lücke(n) in den Ziehungen (>7 Tage):")
for idx, date1, date2, diff in gaps[:5]:
self.warnings.append(f" Zeile {idx + 2}: {date1.strftime('%Y-%m-%d')} → {date2.strftime('%Y-%m-%d')} ({diff} Tage)")
if len(gaps) > 5:
self.warnings.append(f" ... und {len(gaps) - 5} weitere")
return True
def get_statistics(self) -> Dict:
"""Erstellt Statistiken."""
if self.df is None or len(self.df) == 0:
return {}
stats = {
'total_draws': len(self.df),
'date_range': (
self.df['datum'].min().strftime('%Y-%m-%d'),
self.df['datum'].max().strftime('%Y-%m-%d')
),
'years_covered': (self.df['datum'].max().year - self.df['datum'].min().year) + 1,
}
return stats
def validate_all(self) -> bool:
"""Führt alle Validierungen durch."""
print(f"\n{'='*70}")
print(f" CSV VALIDATOR")
print(f" Typ: {self.lottery_type.upper()}")
print(f"{'='*70}")
print(f"\n📁 Datei: {os.path.basename(self.csv_file)}")
if not self.load_csv():
return False
print(f"✅ Datei geladen: {len(self.df)} Zeilen")
# Alle Validierungen
checks = [
("Spaltenstruktur", self.validate_structure),
("Datumsangaben", self.validate_dates),
("Zahlenwerte", self.validate_numbers),
("Duplikate", self.validate_duplicates),
("Chronologie", self.validate_chronology),
("Vollständigkeit", self.validate_completeness),
]
print(f"\n🔍 VALIDIERUNG")
print("="*70)
all_valid = True
for check_name, check_func in checks:
try:
result = check_func()
status = "✅" if result else "❌"
print(f"{status} {check_name}")
if not result:
all_valid = False
except Exception as e:
print(f"❌ {check_name} - Fehler: {e}")
all_valid = False
# Statistiken
stats = self.get_statistics()
if stats:
print(f"\n📊 STATISTIKEN")
print("="*70)
print(f"Ziehungen gesamt: {stats['total_draws']}")
print(f"Zeitraum: {stats['date_range'][0]} bis {stats['date_range'][1]}")
print(f"Jahre: {stats['years_covered']}")
# Fehler ausgeben
if self.errors:
print(f"\n❌ FEHLER ({len(self.errors)})")
print("="*70)
for error in self.errors:
print(error)
# Warnungen ausgeben
if self.warnings:
print(f"\n⚠️ WARNUNGEN ({len(self.warnings)})")
print("="*70)
for warning in self.warnings:
print(warning)
# Zusammenfassung
print(f"\n{'='*70}")
if all_valid and not self.errors:
print("✅ VALIDIERUNG ERFOLGREICH - Keine Fehler gefunden!")
elif not self.errors and self.warnings:
print("✅ VALIDIERUNG OK - Nur Warnungen (keine kritischen Fehler)")
else:
print("❌ VALIDIERUNG FEHLGESCHLAGEN")
print("="*70)
return all_valid and len(self.errors) == 0
def main():
"""Hauptfunktion."""
import sys
# Standard-Dateien
default_files = {
'lotto': '/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/AlleLottozahlen.csv',
'eurojackpot': '/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/data/AlleEurojackpotzahlen.csv'
}
if len(sys.argv) > 1:
# Benutzerdefinierte Datei
csv_file = sys.argv[1]
validator = LottoCSVValidator(csv_file)
success = validator.validate_all()
sys.exit(0 if success else 1)
else:
# Validiere beide Standard-Dateien
print("\n🎲 Validiere beide Lottery-Dateien...\n")
results = {}
for lottery_type, csv_file in default_files.items():
if os.path.exists(csv_file):
validator = LottoCSVValidator(csv_file)
results[lottery_type] = validator.validate_all()
else:
print(f"\n⚠️ {lottery_type.upper()}: Datei nicht gefunden: {csv_file}")
results[lottery_type] = False
# Gesamtergebnis
print("\n" + "="*70)
print(" GESAMTERGEBNIS")
print("="*70)
for lottery_type, success in results.items():
status = "✅" if success else "❌"
print(f"{status} {lottery_type.upper()}")
print("="*70)
all_success = all(results.values())
sys.exit(0 if all_success else 1)
if __name__ == "__main__":
main()
+401
View File
@@ -0,0 +1,401 @@
#!/usr/bin/env python3
"""
Ziehungs-Verifizierer für Lotto 6aus49 und Eurojackpot
Verifiziert Ziehungen gegen offizielle Datenquellen:
1. GitHub Lotto Archive (für Lotto 6aus49)
2. Eurojackpot-zahlen.eu (für Eurojackpot)
Prüft:
- Vollständigkeit (fehlende Ziehungen)
- Korrektheit (falsche Zahlen)
- Duplikate
"""
import pandas as pd
import requests
from datetime import datetime, timedelta
from typing import Dict, List, Tuple, Optional
import sys
import os
class DrawVerifier:
"""Verifiziert Ziehungen gegen offizielle Quellen."""
def __init__(self, csv_file: str, lottery_type: str):
self.csv_file = csv_file
self.lottery_type = lottery_type
self.df_local = None
self.df_official = None
self.errors = []
self.warnings = []
self.info = []
def load_local_data(self) -> bool:
"""Lädt lokale CSV-Datei."""
try:
if not os.path.exists(self.csv_file):
self.errors.append(f"❌ Datei existiert nicht: {self.csv_file}")
return False
self.df_local = pd.read_csv(self.csv_file, sep=';')
self.df_local['datum'] = pd.to_datetime(
self.df_local['datum'],
format='%Y-%m-%d',
errors='coerce'
)
self.info.append(f"✅ Lokale Daten: {len(self.df_local)} Ziehungen")
return True
except Exception as e:
self.errors.append(f"❌ Fehler beim Laden: {e}")
return False
def fetch_official_lotto_data(self) -> bool:
"""Holt offizielle Lotto 6aus49 Daten von GitHub Archive."""
try:
url = 'https://johannesfriedrich.github.io/LottoNumberArchive/Lottonumbers_tidy_complete.json'
print("🌐 Lade offizielle Lotto-Daten von GitHub Archive...")
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
}
response = requests.get(url, headers=headers, timeout=30)
response.raise_for_status()
data = response.json()
# Parse gruppierte Daten
draws_by_id = {}
for entry in data:
draw_id = entry.get('id')
if draw_id not in draws_by_id:
draws_by_id[draw_id] = {
'date': entry.get('date'),
'numbers': [],
'superzahl': None
}
variable = entry.get('variable')
value = entry.get('value')
if variable == 'Lottozahl':
draws_by_id[draw_id]['numbers'].append(value)
elif variable == 'Superzahl':
draws_by_id[draw_id]['superzahl'] = value
# Konvertiere zu DataFrame
official_draws = []
for draw_id, draw_data in draws_by_id.items():
try:
date_str = draw_data['date']
day, month, year = date_str.split('.')
date_obj = datetime(int(year), int(month), int(day))
numbers = sorted(draw_data['numbers'])
if len(numbers) == 6:
official_draws.append({
'datum': date_obj,
'Z1': numbers[0],
'Z2': numbers[1],
'Z3': numbers[2],
'Z4': numbers[3],
'Z5': numbers[4],
'Z6': numbers[5],
'SZ': draw_data['superzahl']
})
except Exception:
continue
self.df_official = pd.DataFrame(official_draws)
self.info.append(f"✅ Offizielle Daten: {len(self.df_official)} Ziehungen")
return True
except Exception as e:
self.errors.append(f"❌ Fehler beim Laden offizieller Daten: {e}")
return False
def fetch_official_eurojackpot_data(self) -> bool:
"""
Holt offizielle Eurojackpot Daten.
Note: Da es keine vollständige öffentliche API gibt,
beschränken wir uns auf Plausibilitätsprüfungen.
"""
self.warnings.append("⚠️ Keine vollständige offizielle Eurojackpot-API verfügbar")
self.warnings.append(" Nur Plausibilitätsprüfungen möglich")
# Erstelle minimale "offizielle" Daten basierend auf erwarteten Ziehungsterminen
# Dies ist nur für Vollständigkeitsprüfung
if len(self.df_local) > 0:
start_date = self.df_local['datum'].min()
end_date = datetime.now()
expected_dates = []
current = start_date
while current <= end_date:
# Eurojackpot: Dienstag (1) und Freitag (4)
if current.weekday() in [1, 4]:
expected_dates.append(current)
current += timedelta(days=1)
self.df_official = pd.DataFrame({'datum': expected_dates})
self.info.append(f"ℹ️ Erwartete Ziehungstermine: {len(expected_dates)}")
return True
def compare_completeness(self) -> List[datetime]:
"""Prüft auf fehlende Ziehungen."""
if self.df_official is None or self.df_local is None:
return []
official_dates = set(self.df_official['datum'].dt.date)
local_dates = set(self.df_local['datum'].dt.date)
missing = official_dates - local_dates
extra = local_dates - official_dates
if missing:
self.errors.append(f"❌ {len(missing)} fehlende Ziehung(en):")
for date in sorted(missing)[:10]:
self.errors.append(f" {date.strftime('%Y-%m-%d')}")
if len(missing) > 10:
self.errors.append(f" ... und {len(missing) - 10} weitere")
if extra:
self.warnings.append(f"⚠️ {len(extra)} zusätzliche Ziehung(en) (nicht in offiziellen Daten):")
for date in sorted(extra)[:5]:
self.warnings.append(f" {date.strftime('%Y-%m-%d')}")
if len(extra) > 5:
self.warnings.append(f" ... und {len(extra) - 5} weitere")
return list(missing)
def compare_accuracy(self) -> int:
"""Vergleicht Zahlenwerte mit offiziellen Daten."""
if self.df_official is None or self.df_local is None:
return 0
if self.lottery_type == 'eurojackpot':
# Keine detaillierten offiziellen Daten verfügbar
return 0
mismatches = 0
# Merge auf Datum
merged = pd.merge(
self.df_local,
self.df_official,
on='datum',
suffixes=('_local', '_official'),
how='inner'
)
if self.lottery_type == 'lotto':
cols_to_check = ['Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'Z6', 'SZ']
else: # eurojackpot
cols_to_check = ['Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'SZ1', 'SZ2']
for idx, row in merged.iterrows():
mismatch_cols = []
for col in cols_to_check:
local_col = f"{col}_local"
official_col = f"{col}_official"
if local_col in row and official_col in row:
# Vergleiche nur wenn beide Werte vorhanden
if pd.notna(row[local_col]) and pd.notna(row[official_col]):
if row[local_col] != row[official_col]:
mismatch_cols.append(
f"{col}: {int(row[local_col])} ≠ {int(row[official_col])}"
)
if mismatch_cols:
mismatches += 1
if mismatches == 1:
self.errors.append("❌ Zahlen-Abweichungen gefunden:")
if mismatches <= 10:
date_str = row['datum'].strftime('%Y-%m-%d')
self.errors.append(f" {date_str}: {', '.join(mismatch_cols)}")
if mismatches > 10:
self.errors.append(f" ... und {mismatches - 10} weitere Abweichungen")
return mismatches
def check_recent_draws(self, days: int = 30) -> None:
"""Prüft besonders die letzten N Tage."""
if self.df_local is None:
return
cutoff = datetime.now() - timedelta(days=days)
recent = self.df_local[self.df_local['datum'] >= cutoff]
self.info.append(f"ℹ️ Letzte {days} Tage: {len(recent)} Ziehungen")
if len(recent) == 0:
self.warnings.append(f"⚠️ Keine Ziehungen in den letzten {days} Tagen!")
# Erwartete Anzahl berechnen
if self.lottery_type == 'lotto':
# 2x pro Woche
expected = (days / 7) * 2
else: # eurojackpot
# 2x pro Woche
expected = (days / 7) * 2
if len(recent) < expected * 0.8: # Toleranz 20%
self.warnings.append(
f"⚠️ Weniger Ziehungen als erwartet: {len(recent)} vs. ~{int(expected)}"
)
def verify_all(self) -> bool:
"""Führt komplette Verifikation durch."""
print(f"\n{'='*70}")
print(f" ZIEHUNGS-VERIFIZIERER")
print(f" Typ: {self.lottery_type.upper()}")
print(f"{'='*70}")
print(f"\n📁 Datei: {os.path.basename(self.csv_file)}")
# Lade lokale Daten
if not self.load_local_data():
return False
# Lade offizielle Daten
print()
if self.lottery_type == 'lotto':
if not self.fetch_official_lotto_data():
return False
else: # eurojackpot
if not self.fetch_official_eurojackpot_data():
return False
print(f"\n🔍 VERIFIKATION")
print("="*70)
# Prüfungen
print("Prüfe Vollständigkeit...")
missing = self.compare_completeness()
if self.lottery_type == 'lotto':
print("Prüfe Zahlenwerte...")
mismatches = self.compare_accuracy()
print("Prüfe aktuelle Ziehungen...")
self.check_recent_draws(30)
# Statistiken
print(f"\n📊 STATISTIKEN")
print("="*70)
for info in self.info:
print(info)
# Zusammenfassung
if self.df_local is not None and self.df_official is not None:
if self.lottery_type == 'lotto':
overlap = len(pd.merge(
self.df_local,
self.df_official,
on='datum',
how='inner'
))
if overlap > 0:
print(f"\n✅ {overlap} Ziehungen in beiden Quellen")
# Genauigkeit
if self.lottery_type == 'lotto':
accuracy = ((overlap - (mismatches if 'mismatches' in locals() else 0)) / overlap * 100)
print(f"✅ Genauigkeit: {accuracy:.1f}%")
# Fehler
if self.errors:
print(f"\n❌ FEHLER ({len(self.errors)})")
print("="*70)
for error in self.errors:
print(error)
# Warnungen
if self.warnings:
print(f"\n⚠️ WARNUNGEN ({len(self.warnings)})")
print("="*70)
for warning in self.warnings:
print(warning)
# Ergebnis
print(f"\n{'='*70}")
if not self.errors:
if self.warnings:
print("✅ VERIFIKATION OK - Nur Warnungen")
else:
print("✅ VERIFIKATION ERFOLGREICH - Alle Ziehungen korrekt!")
else:
print("❌ VERIFIKATION FEHLGESCHLAGEN - Fehler gefunden")
print("="*70)
return len(self.errors) == 0
def main():
"""Hauptfunktion."""
# Standard-Dateien
files = {
'lotto': {
'path': '/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/AlleLottozahlen.csv',
'type': 'lotto'
},
'eurojackpot': {
'path': '/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/data/AlleEurojackpotzahlen.csv',
'type': 'eurojackpot'
}
}
if len(sys.argv) > 1:
# Einzelne Datei
csv_file = sys.argv[1]
lottery_type = sys.argv[2] if len(sys.argv) > 2 else 'lotto'
verifier = DrawVerifier(csv_file, lottery_type)
success = verifier.verify_all()
sys.exit(0 if success else 1)
else:
# Beide Dateien
print("\n🎲 Verifiziere beide Lottery-Dateien...\n")
results = {}
for name, config in files.items():
if os.path.exists(config['path']):
verifier = DrawVerifier(config['path'], config['type'])
results[name] = verifier.verify_all()
else:
print(f"\n⚠️ {name.upper()}: Datei nicht gefunden")
results[name] = False
# Gesamtergebnis
print("\n" + "="*70)
print(" GESAMTERGEBNIS")
print("="*70)
for name, success in results.items():
status = "✅" if success else "❌"
print(f"{status} {name.upper()}")
print("="*70)
all_success = all(results.values())
sys.exit(0 if all_success else 1)
if __name__ == "__main__":
main()
+210
View File
@@ -0,0 +1,210 @@
#!/usr/bin/env python3
"""
Eurojackpot Zahlen-Umschlüsselung
Fügt neue Spalten z1, z2, z3, z4, z5 hinzu mit umschlüsselten Werten:
1-5 → 1, 6-10 → 2, 11-15 → 3, 16-20 → 4, 21-25 → 5,
26-30 → 6, 31-35 → 7, 36-40 → 8, 41-45 → 9, 46-50 → 10
"""
import pandas as pd
def convert_number_to_group(number):
"""Konvertiert eine Zahl (1-50) in eine Gruppe (1-10)."""
if 1 <= number <= 5:
return 1
elif 6 <= number <= 10:
return 2
elif 11 <= number <= 15:
return 3
elif 16 <= number <= 20:
return 4
elif 21 <= number <= 25:
return 5
elif 26 <= number <= 30:
return 6
elif 31 <= number <= 35:
return 7
elif 36 <= number <= 40:
return 8
elif 41 <= number <= 45:
return 9
elif 46 <= number <= 50:
return 10
else:
return 0 # Fehlerfall
def process_number_conversion():
"""Führt die Zahlenumschlüsselung durch."""
# Eingabedatei laden
input_file = "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/data/AlleEurojackpotzahlen.csv"
print("🔢 EUROJACKPOT ZAHLEN-UMSCHLÜSSELUNG")
print("="*50)
# Daten laden
print(f"📁 Lade Daten aus: AlleEurojackpotzahlen.csv")
df = pd.read_csv(input_file, sep=';')
print(f"✅ {len(df)} Ziehungen geladen")
# Umschlüsselungsschema anzeigen
print(f"\n📋 UMSCHLÜSSELUNGSSCHEMA:")
print("="*30)
ranges = [
(1, 5, 1), (6, 10, 2), (11, 15, 3), (16, 20, 4), (21, 25, 5),
(26, 30, 6), (31, 35, 7), (36, 40, 8), (41, 45, 9), (46, 50, 10)
]
for start, end, group in ranges:
print(f"Zahlen {start:2}-{end:2} → Gruppe {group:2}")
# Neue Spalten erstellen
print(f"\n🔄 Erstelle neue Spalten z1, z2, z3, z4, z5...")
source_columns = ['Z1', 'Z2', 'Z3', 'Z4', 'Z5']
target_columns = ['z1', 'z2', 'z3', 'z4', 'z5']
for source_col, target_col in zip(source_columns, target_columns):
df[target_col] = df[source_col].apply(convert_number_to_group)
print(f" {source_col} → {target_col} ✅")
# Erste 5 Beispiele anzeigen
print(f"\n📊 BEISPIEL-UMSCHLÜSSELUNGEN (erste 5 Ziehungen):")
print("="*60)
print(f"{'Datum':<12} {'Z1→z1':<8} {'Z2→z2':<8} {'Z3→z3':<8} {'Z4→z4':<8} {'Z5→z5':<8}")
print("-" * 60)
for i in range(min(5, len(df))):
row = df.iloc[i]
datum = row['datum']
conversions = []
for source_col, target_col in zip(source_columns, target_columns):
original = row[source_col]
converted = row[target_col]
conversions.append(f"{original:2}→{converted}")
print(f"{datum:<12} {conversions[0]:<8} {conversions[1]:<8} {conversions[2]:<8} {conversions[3]:<8} {conversions[4]:<8}")
# Statistiken der Umschlüsselung
print(f"\n📈 STATISTIKEN DER UMSCHLÜSSELTEN WERTE:")
print("="*45)
# Häufigkeit der Gruppen über alle Positionen
all_converted_values = []
for target_col in target_columns:
all_converted_values.extend(df[target_col].tolist())
from collections import Counter
group_counts = Counter(all_converted_values)
print(f"Verteilung der Gruppen (1-10) über alle Positionen:")
total_values = len(all_converted_values)
for group in range(1, 11):
count = group_counts.get(group, 0)
percentage = (count / total_values) * 100
original_range = f"{(group-1)*5 + 1}-{group*5}"
print(f"Gruppe {group:2} ({original_range:5}): {count:4}x ({percentage:5.1f}%)")
# Statistiken pro Position
print(f"\n📊 VERTEILUNG PRO POSITION:")
print("="*35)
for i, target_col in enumerate(target_columns, 1):
position_counts = Counter(df[target_col])
print(f"\nPosition z{i} ({target_col}):")
for group in range(1, 11):
count = position_counts.get(group, 0)
percentage = (count / len(df)) * 100
print(f" Gruppe {group:2}: {count:3}x ({percentage:4.1f}%)")
# Häufigste Kombinationen der umschlüsselten Werte
print(f"\n🎯 HÄUFIGSTE KOMBINATIONEN (umschlüsselt):")
print("="*45)
# Kombinationen als Strings erstellen
df['kombination_umschluesselt'] = df.apply(
lambda row: f"{row['z1']}-{row['z2']}-{row['z3']}-{row['z4']}-{row['z5']}", axis=1
)
combination_counts = Counter(df['kombination_umschluesselt'])
print(f"Top 15 Kombinationen (z1-z2-z3-z4-z5):")
for i, (combination, count) in enumerate(combination_counts.most_common(15), 1):
percentage = (count / len(df)) * 100
print(f"{i:2}. {combination:15} {count:3}x ({percentage:4.1f}%)")
# Muster-Analyse
print(f"\n🔍 MUSTER-ANALYSE:")
print("="*25)
# Aufsteigende Kombinationen
ascending_count = 0
descending_count = 0
for _, row in df.iterrows():
values = [row[col] for col in target_columns]
if values == sorted(values):
ascending_count += 1
elif values == sorted(values, reverse=True):
descending_count += 1
print(f"Aufsteigende Kombinationen: {ascending_count} ({(ascending_count/len(df)*100):.1f}%)")
print(f"Absteigende Kombinationen: {descending_count} ({(descending_count/len(df)*100):.1f}%)")
# Gleiche Werte
same_values_stats = {}
for num_same in range(2, 6):
count = 0
for _, row in df.iterrows():
values = [row[col] for col in target_columns]
value_counts = Counter(values)
if max(value_counts.values()) >= num_same:
count += 1
same_values_stats[num_same] = count
print(f"Mindestens {num_same} gleiche Werte: {count} ({(count/len(df)*100):.1f}%)")
# Bereiche der umschlüsselten Werte
print(f"\n📋 BEREICHSANALYSE (umschlüsselt):")
print("="*35)
# Niedrig (1-3), Mittel (4-7), Hoch (8-10)
for i, target_col in enumerate(target_columns, 1):
low_count = sum(1 for val in df[target_col] if 1 <= val <= 3)
mid_count = sum(1 for val in df[target_col] if 4 <= val <= 7)
high_count = sum(1 for val in df[target_col] if 8 <= val <= 10)
low_pct = (low_count / len(df)) * 100
mid_pct = (mid_count / len(df)) * 100
high_pct = (high_count / len(df)) * 100
print(f"z{i}: Niedrig(1-3)={low_pct:4.1f}% | Mittel(4-7)={mid_pct:4.1f}% | Hoch(8-10)={high_pct:4.1f}%")
# Ausgabedatei speichern
output_file = "/Users/sebastianfrohlich/Library/Mobile Documents/com~apple~CloudDocs/Jupyter Notebooks/Eurojackpot/AlleEurojackpotzahlen_umschluesselt.csv"
print(f"\n💾 DATEI SPEICHERN:")
print("="*25)
# Spalten neu ordnen (Original + neue Spalten)
column_order = ['tag', 'datum', 'Z1', 'Z2', 'Z3', 'Z4', 'Z5', 'z1', 'z2', 'z3', 'z4', 'z5', 'SZ1', 'SZ2', 'kombination_umschluesselt']
# Prüfen welche Spalten existieren
available_columns = [col for col in column_order if col in df.columns]
df_output = df[available_columns]
df_output.to_csv(output_file, sep=';', index=False)
print(f"✅ Umschlüsselte Daten gespeichert: AlleEurojackpotzahlen_umschluesselt.csv")
print(f"📊 Anzahl Spalten: {len(df_output.columns)}")
print(f"📈 Anzahl Zeilen: {len(df_output)}")
print(f"\n🔍 NEUE SPALTEN:")
for col in ['z1', 'z2', 'z3', 'z4', 'z5', 'kombination_umschluesselt']:
if col in df_output.columns:
print(f" ✅ {col}")
return df_output
if __name__ == "__main__":
result_df = process_number_conversion()