Compare commits
5
Commits
f8fbe265c0
...
5cc34d95b9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5cc34d95b9 | ||
|
|
ab42a13048 | ||
|
|
0285a1c5fa | ||
|
|
f23922e05e | ||
|
|
5703e6a12c |
@@ -33,3 +33,6 @@ data/backups/
|
||||
data/*.backup*
|
||||
data/generated_tips/weekly_tips_*.csv
|
||||
data/performance_reports/
|
||||
|
||||
# graphify Code-Graph (regenerierbar via /graphify --update)
|
||||
graphify-out/
|
||||
|
||||
@@ -1,120 +1,156 @@
|
||||
# Eurojackpot Analyse & Generator
|
||||
|
||||
Sammlung von Python-Skripten zur Analyse von Eurojackpot-Daten und Generierung von Tipps basierend auf historischen Daten.
|
||||
Automatisiertes System zur Analyse von Eurojackpot-Daten und Generierung von KI-gestützten Tipps mit Telegram-Benachrichtigungen. Schwesterprojekt von [Lotto 6aus49](../Lotto) — gleiche Architektur, angepasst an das Eurojackpot-Format (5 Hauptzahlen aus 50 + 2 Eurozahlen aus 12).
|
||||
|
||||
## Verzeichnisstruktur
|
||||
Eine detaillierte Architektur- und Datenfluss-Beschreibung steht in [documentation/ARCHITECTURE.md](documentation/ARCHITECTURE.md).
|
||||
|
||||
```
|
||||
.
|
||||
├── README.md # Diese Datei
|
||||
├── data/ # Alle CSV-Dateien und Rohdaten
|
||||
## 🎲 Features
|
||||
|
||||
- **🧠 AI/ML-basierte Tipp-Generierung**: Random Forest + LSTM (PyTorch) Hybrid-Predictor
|
||||
- **🎨 Pattern-Analyse**: Historische Verteilungsmuster
|
||||
- **⚡ 4-Strategien-System**: HYBRID-OPT, BALANCED-SPREAD, HIGH-EV, SOFT-CONTRARIAN mit adaptiver Gewichtung
|
||||
- **💰 EV-Optimierung**: Quality-Score gewichtet gezielt auf unpopuläre (aber gleich wahrscheinliche) Zahlenkombinationen, um den Gewinnanteil im Trefferfall zu erhöhen
|
||||
- **📚 Real-Time Learning**: Kontinuierliches Retraining bei neuen Ziehungen
|
||||
- **🤖 Automatisierung**: Tipp-Generierung & Datenupdate via launchd
|
||||
- **📲 Telegram-Benachrichtigungen**: Automatische Tipps direkt aufs Smartphone
|
||||
- **🔄 Data-Updates**: API-Kette (lottoAPI → Lottoland → Sazka.cz) mit Web-Scraper-Fallback
|
||||
|
||||
## 📁 Verzeichnisstruktur
|
||||
|
||||
```text
|
||||
Eurojackpot/
|
||||
├── README.md
|
||||
├── config/
|
||||
│ └── notifications.json # Telegram/Email Config
|
||||
├── data/
|
||||
│ ├── AlleEurojackpotzahlen.csv # Ziehungshistorie
|
||||
│ ├── eurojackpot_ml_models/ # Trainierte Modelle (RF + LSTM)
|
||||
│ └── generated_tips/ # Generierte Tipps
|
||||
├── scripts/
|
||||
│ ├── analysis/ # Analyse-Skripte
|
||||
│ │ ├── bereichskombinationen_analyse.py
|
||||
│ │ ├── eurojackpot_bereichsanalyse.py
|
||||
│ │ ├── positionsanalyse.py
|
||||
│ │ ├── simple_bereichsanalyse.py
|
||||
│ │ └── treffer_analyse_umschluesselt.py
|
||||
│ ├── generators/ # Tipp-Generatoren
|
||||
│ │ ├── eurojackpot_generator.py
|
||||
│ │ ├── optimized_eurojackpot_generator.py
|
||||
│ │ ├── tipp_generator_nmmhh.py
|
||||
│ │ ├── tipp_generator_nmmhh_v2.py
|
||||
│ │ └── ultimate_ai_ml_eurojackpot_generator.py
|
||||
│ └── utils/ # Hilfsskripte
|
||||
│ ├── create_example_files.py
|
||||
│ ├── eurojackpot_processor.py
|
||||
│ ├── eurojackpot_processor_fixed.py
|
||||
│ ├── eurojackpot_simple.py
|
||||
│ └── zahlen_umschluesseln.py
|
||||
├── results/ # Ausgabedateien und Grafiken
|
||||
└── documentation/ # Alte Dokumentation
|
||||
|
||||
│ ├── generators/
|
||||
│ │ └── ultimate_ai_ml_eurojackpot_generator.py # aktiver Generator
|
||||
│ ├── automation/
|
||||
│ │ ├── auto_update_and_learn.py # Update + Retraining
|
||||
│ │ ├── weekly_tip_generator.py # Tipp-Generierung
|
||||
│ │ ├── com.eurojackpot.update.plist
|
||||
│ │ └── com.eurojackpot.weekly.plist
|
||||
│ └── utils/
|
||||
│ ├── notifier.py # Telegram/Email
|
||||
│ ├── update_from_api.py # API-Updater (aktiv)
|
||||
│ ├── update_from_eurojackpot_zahlen_eu.py # Web-Scraper-Fallback
|
||||
│ ├── deep_learning_engine_pytorch.py # LSTM Engine
|
||||
│ ├── simple_update.py # Manuelle Eingabe
|
||||
│ ├── validate_csv.py # manuelles Utility
|
||||
│ ├── verify_draws.py # manuelles Utility
|
||||
│ └── health_check.py # manuelles Utility
|
||||
├── documentation/
|
||||
│ └── ARCHITECTURE.md
|
||||
├── run_tip_generator.sh
|
||||
├── run_update_and_learn.sh
|
||||
└── logs/
|
||||
```
|
||||
|
||||
## Voraussetzungen
|
||||
## 🚀 Quick Start
|
||||
|
||||
### 1. Installation
|
||||
|
||||
```bash
|
||||
pip install pandas numpy matplotlib scikit-learn
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install pandas numpy scikit-learn requests torch
|
||||
```
|
||||
|
||||
## Verwendung
|
||||
### 2. Telegram-Bot einrichten
|
||||
|
||||
### Analyse ausführen
|
||||
```bash
|
||||
cd scripts/analysis
|
||||
python eurojackpot_bereichsanalyse.py
|
||||
1. Erstelle einen Bot mit [@BotFather](https://t.me/botfather)
|
||||
2. Kopiere den Bot-Token, hole deine Chat-ID (z.B. mit [@userinfobot](https://t.me/userinfobot))
|
||||
3. Konfiguriere `config/notifications.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"telegram": {
|
||||
"enabled": true,
|
||||
"bot_token": "YOUR_BOT_TOKEN",
|
||||
"chat_id": "YOUR_CHAT_ID"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Tipps generieren
|
||||
```bash
|
||||
cd scripts/generators
|
||||
python ultimate_ai_ml_eurojackpot_generator.py
|
||||
```
|
||||
|
||||
### Daten verarbeiten
|
||||
```bash
|
||||
cd scripts/utils
|
||||
python eurojackpot_processor.py
|
||||
```
|
||||
|
||||
## Hauptkomponenten
|
||||
|
||||
### Analyse-Skripte
|
||||
- **bereichskombinationen_analyse.py**: Analysiert Zahlenbereiche in Kombinationen
|
||||
- **eurojackpot_bereichsanalyse.py**: Detaillierte Bereichsanalyse mit Visualisierung
|
||||
- **positionsanalyse.py**: Analysiert Zahlen nach Position
|
||||
- **treffer_analyse_umschluesselt.py**: Trefferanalyse mit Umschlüsselung
|
||||
|
||||
### Generatoren
|
||||
|
||||
- **ultimate_ai_ml_eurojackpot_generator.py**: ML-basierter Generator mit intelligentem Retraining
|
||||
- **optimized_eurojackpot_generator.py**: Optimierter Generator mit mehreren Strategien
|
||||
- **tipp_generator_nmmhh.py**: Generator basierend auf NMMHH-Prinzip
|
||||
|
||||
### 🤖 Automatisches ML-Retraining
|
||||
|
||||
Das System verwendet **intelligentes Retraining**:
|
||||
|
||||
- ✅ **Automatische Erkennung**: Prüft ob CSV neuer als ML-Cache
|
||||
- ✅ **Smart Caching**: Nutzt Cache wenn Daten unverändert
|
||||
- ✅ **Auto-Retrain**: Trainiert neu wenn neue Daten verfügbar
|
||||
|
||||
**So funktioniert es:**
|
||||
|
||||
1. Nach `update_from_eurojackpot_zahlen_eu.py` wird CSV aktualisiert
|
||||
2. Beim nächsten Generator-Start: CSV-Timestamp > Cache-Timestamp
|
||||
3. ✅ Automatisches Retraining mit neuen Daten
|
||||
4. Neue ML-Modelle berücksichtigen aktuelle Ziehungen
|
||||
|
||||
**Manuelles Cache-Löschen (falls nötig):**
|
||||
Test-Benachrichtigung senden:
|
||||
|
||||
```bash
|
||||
cd Eurojackpot
|
||||
rm -rf cache/
|
||||
python scripts/generators/ultimate_ai_ml_eurojackpot_generator.py
|
||||
python scripts/utils/notifier.py --test
|
||||
```
|
||||
|
||||
### Utilities
|
||||
- **eurojackpot_processor.py**: Verarbeitet und markiert Kombinationen
|
||||
- **zahlen_umschluesseln.py**: Konvertiert Zahlenkombinationen
|
||||
- **create_example_files.py**: Erstellt Testdaten
|
||||
### 3. Tipps manuell generieren
|
||||
|
||||
## Datenformat
|
||||
```bash
|
||||
python scripts/automation/weekly_tip_generator.py --force --num-tips 10
|
||||
|
||||
# Nur Historie anzeigen
|
||||
python scripts/automation/weekly_tip_generator.py --history
|
||||
```
|
||||
|
||||
### 4. Daten manuell aktualisieren + Modelle neu trainieren
|
||||
|
||||
```bash
|
||||
./run_update_and_learn.sh
|
||||
```
|
||||
|
||||
## 🧠 Generator-Strategien
|
||||
|
||||
Der Ultimate AI/ML Hybrid Generator (`scripts/generators/ultimate_ai_ml_eurojackpot_generator.py`) verteilt pro Lauf 10 Tipps über 4 Strategien, deren Gewichtung sich anhand der bisherigen Trefferleistung selbst anpasst (Startwerte in Klammern):
|
||||
|
||||
1. **HYBRID-OPT** (40%): AI-Score + historisches Pattern-Gewicht kombiniert
|
||||
2. **BALANCED-SPREAD** (30%): erzwingt Verteilung über Zonen, kontrollierter Summenbereich
|
||||
3. **HIGH-EV** (20%): 2 Hauptzahlen >31, meidet empirisch belegte populäre Einzelzahlen — auf EV statt Trefferwahrscheinlichkeit optimiert
|
||||
4. **SOFT-CONTRARIAN** (10%): bevorzugt in den letzten 30 Ziehungen unterrepräsentierte Zahlen
|
||||
|
||||
### Warum EV statt Trefferwahrscheinlichkeit?
|
||||
|
||||
Eurojackpot-Ziehungen sind unabhängige Zufallsereignisse — kein Modell kann
|
||||
die Trefferwahrscheinlichkeit über den statistischen Erwartungswert heben.
|
||||
Der Quality-Score optimiert deshalb bewusst auf **Expected Value**: unpopuläre
|
||||
Zahlenkombinationen haben bei einem Treffer weniger Mitgewinner und damit eine
|
||||
höhere Auszahlung. Details und Quellen dazu in
|
||||
[documentation/ARCHITECTURE.md](documentation/ARCHITECTURE.md#3-design-entscheidung-quality-score--ev-optimierung).
|
||||
|
||||
## 🤖 Automatisierung
|
||||
|
||||
Läuft über **launchd** (`~/Library/LaunchAgents/`), nicht crontab:
|
||||
|
||||
| Job | Plist | Zeitplan |
|
||||
| --- | --- | --- |
|
||||
| Tipp-Generierung | `com.eurojackpot.weekly.plist` | Mo + Do, 21:00 Uhr (Abend vor Di/Fr-Ziehung) |
|
||||
| Update & Learning | `com.eurojackpot.update.plist` | Mi + Sa, 08:00 Uhr (Morgen nach Di/Fr-Ziehung) |
|
||||
|
||||
Beide Plists liegen unter `scripts/automation/` und müssen für die Aktivierung
|
||||
nach `~/Library/LaunchAgents/` verlinkt/kopiert und mit `launchctl load`
|
||||
geladen werden.
|
||||
|
||||
## 📲 Telegram-Benachrichtigungen
|
||||
|
||||
Bei erfolgreicher Tipp-Generierung bzw. nach jeder ausgewerteten Ziehung
|
||||
verschickt `EurojackpotNotifier` automatisch eine Zusammenfassung (bester
|
||||
Tipp, Ø Confidence/Quality, Trefferauswertung).
|
||||
|
||||
## 📈 Performance Tracking
|
||||
|
||||
Alle Generierungen werden in `data/generated_tips/generation_history.json`
|
||||
protokolliert, alle Ziehungsauswertungen in `data/learning_log.json`.
|
||||
|
||||
## 🗂️ Datenformat
|
||||
|
||||
`data/AlleEurojackpotzahlen.csv` (Semikolon-getrennt):
|
||||
|
||||
Die CSV-Dateien im [data](data/)-Ordner folgen dem Format:
|
||||
```csv
|
||||
Zahl1,Zahl2,Zahl3,Zahl4,Zahl5,bereits_gezogen
|
||||
1,2,3,4,5,0
|
||||
5,12,23,34,45,1
|
||||
datum;Z1;Z2;Z3;Z4;Z5;SZ1;SZ2
|
||||
2012-03-23;5;8;21;37;46;6;8
|
||||
```
|
||||
|
||||
## Hinweise
|
||||
|
||||
- Eurojackpot: 5 aus 50 Zahlen + 2 aus 12 Eurozahlen
|
||||
- Alle Kombinationen: ~139 Millionen Möglichkeiten
|
||||
- Die Skripte benötigen ausreichend RAM für große Datenmengen
|
||||
|
||||
## Weitere Dokumentation
|
||||
|
||||
Siehe [documentation/README.md](documentation/README.md) für die ursprüngliche Dokumentation.
|
||||
- Eurojackpot: 5 aus 50 Hauptzahlen + 2 aus 12 Eurozahlen
|
||||
- `setup_cron.sh` beschreibt einen älteren, crontab-basierten Zeitplan, der
|
||||
nicht mehr dem tatsächlich aktiven launchd-Setup entspricht — nicht als
|
||||
aktuelle Referenz verwenden.
|
||||
|
||||
@@ -962,3 +962,16 @@ datum;Z1;Z2;Z3;Z4;Z5;SZ1;SZ2
|
||||
2026-06-16;9;26;29;37;42;1;7
|
||||
2026-06-19;16;27;37;42;45;5;12
|
||||
2026-06-23;24;27;43;48;50;4;12
|
||||
2026-06-26;17;25;35;39;41;5;9
|
||||
2026-06-30;12;19;34;44;50;3;8
|
||||
2026-07-03;4;9;20;25;28;1;3
|
||||
2026-07-07;6;16;24;41;46;2;3
|
||||
2026-07-10;13;25;28;42;45;5;12
|
||||
2026-07-14;5;6;17;34;36;3;11
|
||||
2026-07-17;6;21;31;47;48;2;9
|
||||
2026-07-21;4;8;10;17;37;5;7
|
||||
2026-07-24;4;6;8;17;22;7;10
|
||||
2026-07-28;11;25;40;41;45;1;5
|
||||
2026-07-31;4;10;20;40;41;4;6
|
||||
2026-08-04;3;17;30;35;43;1;12
|
||||
2026-08-07;1;3;6;13;23;5;7
|
||||
|
||||
|
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"trained_at": "2026-06-26T14:12:09.271071",
|
||||
"num_samples": 943,
|
||||
"trained_at": "2026-08-08T08:00:38.014984",
|
||||
"num_samples": 956,
|
||||
"num_features": 20,
|
||||
"final_loss": 0.3350204216937224,
|
||||
"final_val_loss": 0.3224504888057709,
|
||||
"best_val_loss": 0.32012996077537537,
|
||||
"epochs_trained": 22
|
||||
"final_loss": 0.3359047199289004,
|
||||
"final_val_loss": 0.32727712392807007,
|
||||
"best_val_loss": 0.32497593263785046,
|
||||
"epochs_trained": 21
|
||||
}
|
||||
Binary file not shown.
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"trained_at": "2026-06-26T14:12:02.493679",
|
||||
"num_samples": 943,
|
||||
"trained_at": "2026-08-08T08:00:31.473921",
|
||||
"num_samples": 956,
|
||||
"num_features": 20,
|
||||
"final_loss": 0.32751141612728435,
|
||||
"final_val_loss": 0.3300037036339442,
|
||||
"best_val_loss": 0.3280275712410609,
|
||||
"epochs_trained": 30
|
||||
"final_loss": 0.33306921894351643,
|
||||
"final_val_loss": 0.32881902654965717,
|
||||
"best_val_loss": 0.3284312884012858,
|
||||
"epochs_trained": 20
|
||||
}
|
||||
Binary file not shown.
@@ -1,78 +1,78 @@
|
||||
{
|
||||
"adjustments_main": {
|
||||
"1": -0.12619836608746318,
|
||||
"2": -0.15221250479165757,
|
||||
"3": -0.16725576971060133,
|
||||
"4": -0.17991956083653718,
|
||||
"5": -0.16628347544855948,
|
||||
"6": -0.1938443906543706,
|
||||
"7": -0.18109772210361866,
|
||||
"8": -0.15931078508418037,
|
||||
"9": -0.1667508747810567,
|
||||
"10": -0.1557650408354652,
|
||||
"11": -0.16650629495977046,
|
||||
"12": -0.19539844102264076,
|
||||
"13": -0.15613737904077563,
|
||||
"14": -0.16645817071995633,
|
||||
"15": -0.14482271091270074,
|
||||
"16": -0.1281450473660133,
|
||||
"17": -0.10479049324048999,
|
||||
"18": -0.11507907310647222,
|
||||
"19": -0.1425584638001938,
|
||||
"20": -0.16707436524451125,
|
||||
"21": -0.12810585677132857,
|
||||
"22": -0.1790876250355349,
|
||||
"23": -0.10261991330687453,
|
||||
"24": -0.20565400571670125,
|
||||
"25": -0.18120728566028785,
|
||||
"26": -0.16865521208798265,
|
||||
"27": -0.15137349832884742,
|
||||
"28": -0.16645968576456524,
|
||||
"29": -0.1571303945901751,
|
||||
"30": -0.1819025385106489,
|
||||
"31": -0.16653919395700817,
|
||||
"32": -0.15523736200969857,
|
||||
"33": -0.1664821604084597,
|
||||
"34": -0.15664428220600507,
|
||||
"35": -0.15273288355762948,
|
||||
"36": -0.1396388962359922,
|
||||
"37": -0.07551254965229758,
|
||||
"38": -0.1330577090551517,
|
||||
"39": -0.11639308425633971,
|
||||
"40": -0.16813563743232532,
|
||||
"41": -0.16730757643637156,
|
||||
"42": -0.14793711630571973,
|
||||
"43": -0.151292585302194,
|
||||
"44": -0.12935107126420056,
|
||||
"45": -0.16951003107554743,
|
||||
"46": -0.1520774638732381,
|
||||
"47": -0.11406441743860245,
|
||||
"48": -0.1279729118208299,
|
||||
"49": -0.19351903302230727,
|
||||
"50": -0.1790542892546445
|
||||
"1": -0.16608198398936372,
|
||||
"2": -0.20538001276393666,
|
||||
"3": -0.18969889369537835,
|
||||
"4": -0.17318339990945472,
|
||||
"5": -0.2041529162618371,
|
||||
"6": -0.17167194971141891,
|
||||
"7": -0.23244299727053913,
|
||||
"8": -0.1828461769270083,
|
||||
"9": -0.20480590652032585,
|
||||
"10": -0.17937672521502113,
|
||||
"11": -0.20406983228000747,
|
||||
"12": -0.23171720884079933,
|
||||
"13": -0.1797939414402776,
|
||||
"14": -0.218726986482017,
|
||||
"15": -0.19845640630138875,
|
||||
"16": -0.1685642063246048,
|
||||
"17": -0.08845082339173124,
|
||||
"18": -0.17058915502103805,
|
||||
"19": -0.18221065780965467,
|
||||
"20": -0.1903328663635429,
|
||||
"21": -0.1683113303354663,
|
||||
"22": -0.21593097649650866,
|
||||
"23": -0.14399098514385758,
|
||||
"24": -0.2411834882331731,
|
||||
"25": -0.17525602370144946,
|
||||
"26": -0.2207854268205856,
|
||||
"27": -0.20459393528150874,
|
||||
"28": -0.19019474357700644,
|
||||
"29": -0.20998765597413135,
|
||||
"30": -0.2183466665873564,
|
||||
"31": -0.20432008864113324,
|
||||
"32": -0.2082140459353828,
|
||||
"33": -0.2187494627736534,
|
||||
"34": -0.18099747238166455,
|
||||
"35": -0.17696346788244138,
|
||||
"36": -0.179189218511983,
|
||||
"37": -0.11896311021030447,
|
||||
"38": -0.18743360319929292,
|
||||
"39": -0.15776655098075973,
|
||||
"40": -0.19082026405727728,
|
||||
"41": -0.16172406919338378,
|
||||
"42": -0.18703599479832891,
|
||||
"43": -0.18966775167799174,
|
||||
"44": -0.16983645768805158,
|
||||
"45": -0.19254573310504927,
|
||||
"46": -0.1909868389863429,
|
||||
"47": -0.15515569951545483,
|
||||
"48": -0.1681867722579999,
|
||||
"49": -0.2440807059750868,
|
||||
"50": -0.21640413290670632
|
||||
},
|
||||
"adjustments_euro": {
|
||||
"1": -0.1274952805859072,
|
||||
"2": -0.08356248943314629,
|
||||
"3": -0.15514841761166354,
|
||||
"4": -0.16769641312863537,
|
||||
"5": -0.04496226390910391,
|
||||
"6": -0.07576564462854152,
|
||||
"7": -0.031991927667788016,
|
||||
"8": -0.12741033551216618,
|
||||
"9": -0.1323058522512846,
|
||||
"10": -0.15347090287457343,
|
||||
"11": -0.11382875262351197,
|
||||
"12": -0.10983575407388531
|
||||
"1": -0.13847414603178812,
|
||||
"2": -0.11231134304899443,
|
||||
"3": -0.15113400421047893,
|
||||
"4": -0.20511099086305523,
|
||||
"5": -0.03232079204789439,
|
||||
"6": -0.11897970312758545,
|
||||
"7": -0.048634268732921454,
|
||||
"8": -0.16801815384697347,
|
||||
"9": -0.15819264741746886,
|
||||
"10": -0.19193029312048415,
|
||||
"11": -0.15500731573611248,
|
||||
"12": -0.13648789370458764
|
||||
},
|
||||
"stats": {
|
||||
"incorrect_main": 2250,
|
||||
"correct_main": 250,
|
||||
"incorrect_euro": 500,
|
||||
"correct_euro": 100,
|
||||
"cycles": 50,
|
||||
"last_update": "2026-06-24T08:00:34.777510"
|
||||
"incorrect_main": 2835,
|
||||
"correct_main": 315,
|
||||
"incorrect_euro": 630,
|
||||
"correct_euro": 126,
|
||||
"cycles": 63,
|
||||
"last_update": "2026-08-08T08:00:38.020167"
|
||||
},
|
||||
"learning_rate": 0.1,
|
||||
"last_saved": "2026-06-24T08:00:34.777550"
|
||||
"last_saved": "2026-08-08T08:00:38.020225"
|
||||
}
|
||||
Binary file not shown.
@@ -517,6 +517,83 @@
|
||||
"file": "weekly_tips_20260704_165830.csv",
|
||||
"avg_confidence": 0.5226028457900052,
|
||||
"avg_quality": 0.5218641480233097
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-06T21:00:15.960297",
|
||||
"num_tips": 10,
|
||||
"file": "weekly_tips_20260706_210015.csv",
|
||||
"avg_confidence": 0.5226028457900052,
|
||||
"avg_quality": 0.5218641480233097
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-09T21:00:10.431328",
|
||||
"num_tips": 10,
|
||||
"file": "weekly_tips_20260709_210010.csv",
|
||||
"avg_confidence": 0.4576759604396684,
|
||||
"avg_quality": 0.4899123425520503
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-13T21:00:12.010153",
|
||||
"num_tips": 10,
|
||||
"file": "weekly_tips_20260713_210012.csv",
|
||||
"avg_confidence": 0.5001572202836366,
|
||||
"avg_quality": 0.5060097206485906
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-16T21:00:19.490697",
|
||||
"num_tips": 10,
|
||||
"file": "weekly_tips_20260716_210019.csv",
|
||||
"avg_confidence": 0.5238483846177105,
|
||||
"avg_quality": 0.5258664812725949
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-20T21:00:16.518596",
|
||||
"num_tips": 10,
|
||||
"file": "weekly_tips_20260720_210016.csv",
|
||||
"avg_confidence": 0.4854339474855488,
|
||||
"avg_quality": 0.5006247522288646
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-23T21:00:16.708518",
|
||||
"num_tips": 10,
|
||||
"file": "weekly_tips_20260723_210016.csv",
|
||||
"avg_confidence": 0.5152142706004199,
|
||||
"avg_quality": 0.5145964865224885
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-27T21:08:59.062626",
|
||||
"num_tips": 10,
|
||||
"file": "weekly_tips_20260727_210859.csv",
|
||||
"avg_confidence": 0.47424985238563194,
|
||||
"avg_quality": 0.49870618646900783
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-30T21:00:21.594441",
|
||||
"num_tips": 10,
|
||||
"file": "weekly_tips_20260730_210021.csv",
|
||||
"avg_confidence": 0.5101750540191647,
|
||||
"avg_quality": 0.5123943650959577
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-08-03T21:00:13.073096",
|
||||
"num_tips": 10,
|
||||
"file": "weekly_tips_20260803_210013.csv",
|
||||
"avg_confidence": 0.5409446644482934,
|
||||
"avg_quality": 0.5203607130907963
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-08-06T21:00:19.537139",
|
||||
"num_tips": 10,
|
||||
"file": "weekly_tips_20260806_210019.csv",
|
||||
"avg_confidence": 0.5060792548620471,
|
||||
"avg_quality": 0.5070598024768673
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-08-10T19:03:55.303406",
|
||||
"num_tips": 10,
|
||||
"file": "weekly_tips_20260810_190355.csv",
|
||||
"avg_confidence": 0.4962393376563816,
|
||||
"avg_quality": 0.5413046258517413
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -676,6 +676,175 @@
|
||||
"main": 1.0,
|
||||
"euro": 0.1
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-06-27T08:00:35.750434",
|
||||
"draw_date": "2026-06-26",
|
||||
"evaluation": {
|
||||
"main": 1,
|
||||
"euro": 1,
|
||||
"tip": 2
|
||||
},
|
||||
"avg_matches": {
|
||||
"main": 0.6,
|
||||
"euro": 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-01T08:01:25.937379",
|
||||
"draw_date": "2026-06-30",
|
||||
"evaluation": {
|
||||
"main": 1,
|
||||
"euro": 1,
|
||||
"tip": 10
|
||||
},
|
||||
"avg_matches": {
|
||||
"main": 0.1,
|
||||
"euro": 0.4
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-04T08:01:20.071498",
|
||||
"draw_date": "2026-07-03",
|
||||
"evaluation": {
|
||||
"main": 2,
|
||||
"euro": 0,
|
||||
"tip": 1
|
||||
},
|
||||
"avg_matches": {
|
||||
"main": 1.0,
|
||||
"euro": 0.1
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-08T08:00:38.507652",
|
||||
"draw_date": "2026-07-07",
|
||||
"evaluation": {
|
||||
"main": 1,
|
||||
"euro": 1,
|
||||
"tip": 7
|
||||
},
|
||||
"avg_matches": {
|
||||
"main": 0.4,
|
||||
"euro": 0.4
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-11T08:00:36.708330",
|
||||
"draw_date": "2026-07-10",
|
||||
"evaluation": {
|
||||
"main": 2,
|
||||
"euro": 1,
|
||||
"tip": 3
|
||||
},
|
||||
"avg_matches": {
|
||||
"main": 1.0,
|
||||
"euro": 0.2
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-15T08:00:36.445887",
|
||||
"draw_date": "2026-07-14",
|
||||
"evaluation": {
|
||||
"main": 0,
|
||||
"euro": 1,
|
||||
"tip": 2
|
||||
},
|
||||
"avg_matches": {
|
||||
"main": 0.3,
|
||||
"euro": 0.1
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-18T08:09:22.468950",
|
||||
"draw_date": "2026-07-17",
|
||||
"evaluation": {
|
||||
"main": 2,
|
||||
"euro": 1,
|
||||
"tip": 4
|
||||
},
|
||||
"avg_matches": {
|
||||
"main": 0.4,
|
||||
"euro": 0.3
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-22T08:15:54.062417",
|
||||
"draw_date": "2026-07-21",
|
||||
"evaluation": {
|
||||
"main": 1,
|
||||
"euro": 0,
|
||||
"tip": 4
|
||||
},
|
||||
"avg_matches": {
|
||||
"main": 0.4,
|
||||
"euro": 0.2
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-25T08:10:35.829667",
|
||||
"draw_date": "2026-07-24",
|
||||
"evaluation": {
|
||||
"main": 1,
|
||||
"euro": 0,
|
||||
"tip": 2
|
||||
},
|
||||
"avg_matches": {
|
||||
"main": 0.4,
|
||||
"euro": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-07-29T08:00:37.275495",
|
||||
"draw_date": "2026-07-28",
|
||||
"evaluation": {
|
||||
"main": 1,
|
||||
"euro": 0,
|
||||
"tip": 3
|
||||
},
|
||||
"avg_matches": {
|
||||
"main": 0.3,
|
||||
"euro": 0.2
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-08-01T08:00:38.526101",
|
||||
"draw_date": "2026-07-31",
|
||||
"evaluation": {
|
||||
"main": 1,
|
||||
"euro": 1,
|
||||
"tip": 2
|
||||
},
|
||||
"avg_matches": {
|
||||
"main": 0.3,
|
||||
"euro": 0.3
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-08-05T08:00:36.980662",
|
||||
"draw_date": "2026-08-04",
|
||||
"evaluation": {
|
||||
"main": 1,
|
||||
"euro": 0,
|
||||
"tip": 1
|
||||
},
|
||||
"avg_matches": {
|
||||
"main": 0.3,
|
||||
"euro": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2026-08-08T08:00:38.021759",
|
||||
"draw_date": "2026-08-07",
|
||||
"evaluation": {
|
||||
"main": 1,
|
||||
"euro": 1,
|
||||
"tip": 4
|
||||
},
|
||||
"avg_matches": {
|
||||
"main": 0.1,
|
||||
"euro": 1.0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
# Architektur
|
||||
|
||||
Dieses Dokument beschreibt den aktiven Datenfluss, die Kernkomponenten und
|
||||
bekannte Schwachstellen des Eurojackpot-Systems. Erstellt mit Hilfe einer
|
||||
Code-Graph-Analyse (`graphify-out/`) analog zum Schwesterprojekt Lotto 6aus49
|
||||
— siehe dort `documentation/ARCHITECTURE.md` für den Vergleich. Format: 5
|
||||
Hauptzahlen (1–50) + 2 Eurozahlen.
|
||||
|
||||
## 1. Aktiver Datenfluss
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Ingestion["1. Daten-Ingestion"]
|
||||
A1[lottoAPI / Lottoland / Sazka.cz] -->|EurojackpotAPIUpdater| B[AlleEurojackpotzahlen.csv]
|
||||
A2[eurojackpot-zahlen.eu<br/>Web-Scraper Fallback] -.->|nur falls API leer| B
|
||||
end
|
||||
|
||||
subgraph Training["2. Training"]
|
||||
B --> C[EurojackpotFeatureEngineer]
|
||||
C --> D[EurojackpotAIMLEngine<br/>RandomForest]
|
||||
C --> E[DeepLearningEngine<br/>LSTM, PyTorch]
|
||||
D --> F[Hybrid Predictor]
|
||||
E --> F
|
||||
end
|
||||
|
||||
subgraph Generation["3. Tipp-Generierung"]
|
||||
F --> G[UltimateAIMLEurojackpotGenerator]
|
||||
G --> H1[HYBRID-OPT]
|
||||
G --> H2[BALANCED-SPREAD]
|
||||
G --> H3[HIGH-EV]
|
||||
G --> H4[SOFT-CONTRARIAN]
|
||||
H1 & H2 & H3 & H4 --> I[Quality/Popularity Score<br/>5 Hauptzahlen + 2 Eurozahlen]
|
||||
I --> J[10 Tipps als CSV]
|
||||
end
|
||||
|
||||
subgraph Learning["4. Learning-Loop"]
|
||||
B -->|neue Ziehung| K[AutoUpdateAndLearn]
|
||||
K -->|evaluiert| J
|
||||
K -->|retrained| D
|
||||
K -->|retrained| E
|
||||
K --> L[EurojackpotRealTimeLearner /<br/>strategy_weights]
|
||||
end
|
||||
|
||||
J --> M[EurojackpotNotifier<br/>Telegram]
|
||||
K --> M
|
||||
|
||||
L -.->|beeinflusst nächsten Lauf| G
|
||||
```
|
||||
|
||||
**Zwei launchd-Jobs** (nicht crontab — siehe Abschnitt 4):
|
||||
|
||||
| Schritt | Entrypoint | Zeitplan (tatsächlich) |
|
||||
|---|---|---|
|
||||
| Ingestion + Training + Learning | `run_update_and_learn.sh` → `scripts/automation/auto_update_and_learn.py` | Mi + Sa, 08:00 Uhr (Morgen nach Di/Fr-Ziehung) |
|
||||
| Tipp-Generierung | `run_tip_generator.sh` → `scripts/automation/weekly_tip_generator.py` | Mo + Do, 21:00 Uhr (Abend vor Di/Fr-Ziehung) |
|
||||
|
||||
Anders als beim Lotto-Projekt prüft der Update-Job hier erst am **nächsten
|
||||
Morgen**, nicht noch am Ziehungsabend — das gibt den Datenquellen mehr Zeit,
|
||||
die Ziehung zu veröffentlichen.
|
||||
|
||||
## 2. Kernkomponenten (aktive Pipeline)
|
||||
|
||||
Alle Pfade relativ zum Projekt-Root.
|
||||
|
||||
| Komponente | Datei | Rolle |
|
||||
|---|---|---|
|
||||
| `EurojackpotAPIUpdater` | `scripts/utils/update_from_api.py` | Holt neue Ziehungen über `fetch_from_all_apis()`: lottoAPI, Lottoland, Sazka.cz. `AutoUpdateAndLearn.update_data()` nutzt bereits `api_name='all'` **und** fällt bei leerem API-Ergebnis zusätzlich auf einen Web-Scraper zurück — robuster als die Lotto-Pipeline vor deren Fix. |
|
||||
| `WebScraper` (`EurojackpotUpdater`) | `scripts/utils/update_from_eurojackpot_zahlen_eu.py` | Zweiter Fallback, wird nur aktiv wenn die API-Kette keine neuen Daten liefert. |
|
||||
| `AutoUpdateAndLearn` | `scripts/automation/auto_update_and_learn.py` | Orchestriert: Daten aktualisieren → neue Ziehung prüfen → letzte Tipps evaluieren → Learning-Update (Retraining). |
|
||||
| `EurojackpotFeatureEngineer` | `scripts/generators/ultimate_ai_ml_eurojackpot_generator.py` | Feature-Engineering (Frequenzen, Gaps, Momentum) für Haupt- und Eurozahlen. |
|
||||
| `EurojackpotAIMLEngine` | `scripts/generators/ultimate_ai_ml_eurojackpot_generator.py` | RandomForest-Modelle. **Achtung:** eine Klasse mit identischem Namen existiert nochmal in der Backup-Datei (siehe Abschnitt 5) — beim Editieren die richtige Datei prüfen. |
|
||||
| `DeepLearningEngine` | `scripts/utils/deep_learning_engine_pytorch.py` | LSTM (PyTorch), analog zu Lotto. |
|
||||
| `UltimateAIMLEurojackpotGenerator` | `scripts/generators/ultimate_ai_ml_eurojackpot_generator.py` | **God Node der Pipeline** (40 Kanten im Code-Graph) — zentraler Einstiegspunkt `generate_ultimate_tips()`. |
|
||||
| `EurojackpotPatternEngine`, `EurojackpotHybridOptimizer` | `scripts/generators/ultimate_ai_ml_eurojackpot_generator.py` | Wie bei Lotto: historische Muster bzw. Kandidaten-Generierung für HYBRID-OPT. |
|
||||
| `EurojackpotRealTimeLearner` | `scripts/generators/ultimate_ai_ml_eurojackpot_generator.py` | Persistiert Learning-State, passt `strategy_weights` an. |
|
||||
| `EurojackpotPerformanceTracker` | `scripts/generators/ultimate_ai_ml_eurojackpot_generator.py` | Loggt Trefferauswertungen. |
|
||||
| `EurojackpotNotifier` | `scripts/utils/notifier.py` | Telegram-Benachrichtigungen. |
|
||||
|
||||
### Die 4 Tipp-Strategien
|
||||
|
||||
Analog zu Lotto: HYBRID-OPT, BALANCED-SPREAD, HIGH-EV, SOFT-CONTRARIAN —
|
||||
gleiche Namen, gleiches Grundprinzip, aber Zahlenraum 1–50 (5 Hauptzahlen)
|
||||
statt 1–49 (6 Zahlen) plus zusätzlich 2 Eurozahlen (`_get_smart_euro_numbers`).
|
||||
|
||||
## 3. Design-Entscheidung: Quality-Score = EV-Optimierung — hier noch nicht nachgezogen
|
||||
|
||||
Wie beim Lotto-Projekt gilt: Eurojackpot-Ziehungen sind unabhängige
|
||||
Zufallsereignisse, kein Modell kann die Trefferwahrscheinlichkeit über den
|
||||
Zufalls-Erwartungswert heben. `_calculate_quality_score()` sollte deshalb auf
|
||||
Expected Value (unpopuläre Kombinationen → höhere Auszahlung bei Treffer)
|
||||
optimieren.
|
||||
|
||||
**Der Code hier entspricht noch dem alten, unrevidierten Lotto-Stand
|
||||
(vor der Überarbeitung dort):**
|
||||
|
||||
- `_calculate_popularity_score()` (Zeile 688) nutzt weiterhin die ad-hoc
|
||||
`lucky_numbers = {3, 7, 9, 11, 13, 17, 19, 21, 23}`-Liste ohne empirische
|
||||
Grundlage, statt der bei Lotto inzwischen verwendeten
|
||||
`POPULAR_PLAYER_PICKS = {5, 7, 9, 11, 12, 13}`.
|
||||
- Nur direkte ±1-Nachbarn werden als Muster erkannt, keine allgemeinen
|
||||
arithmetischen Folgen (z.B. 5-10-15-20-25).
|
||||
- Kein Odd/Even-Split-Kriterium.
|
||||
- Popularity zählt nur 20% der Quality-Formel (`main_quality` 25%,
|
||||
`euro_quality` 15%, `pattern_quality` 15%, `diversity` 10%, `popularity`
|
||||
20%, `recency` 15%) — Recency/Pattern-Anteile folgen Heuristiken, die auch
|
||||
andere Systemspieler nutzen und damit das EV-Ziel eher unterlaufen.
|
||||
- **`_get_smart_euro_numbers()`** (Zeile 673) hat **gar keine**
|
||||
EV-Betrachtung — die 2 Eurozahlen werden rein nach AI-Score gewählt, analog
|
||||
zur alten (inzwischen bei Lotto durch eine EV-Heuristik ersetzten)
|
||||
Superzahl-Logik.
|
||||
|
||||
Eine Übertragung der Lotto-Fixes (siehe dortiges `ARCHITECTURE.md` Abschnitt 3)
|
||||
auf dieses Projekt steht noch aus.
|
||||
|
||||
## 4. Automatisierung
|
||||
|
||||
Läuft über **launchd** (`~/Library/LaunchAgents/`), nicht über `crontab`:
|
||||
|
||||
| launchd Job | Plist | Zeitplan |
|
||||
|---|---|---|
|
||||
| `com.eurojackpot.update` | `scripts/automation/com.eurojackpot.update.plist` | Mi + Sa, 08:00 Uhr |
|
||||
| `com.eurojackpot.weekly` | `scripts/automation/com.eurojackpot.weekly.plist` | Mo + Do, 21:00 Uhr |
|
||||
|
||||
## 5. Bekannte Schwachstellen / offene Punkte
|
||||
|
||||
- **Echtes Klassen-Duplikat:** `UltimateAIMLEurojackpotGenerator` (inkl.
|
||||
`EurojackpotAIMLEngine`, `EurojackpotPatternEngine`,
|
||||
`EurojackpotHybridOptimizer`) ist **wortgleich** sowohl in der aktiven
|
||||
`scripts/generators/ultimate_ai_ml_eurojackpot_generator.py` als auch in
|
||||
`scripts/generators/ultimate_ai_ml_eurojackpot_generator_v2.1_backup.py`
|
||||
definiert. Nur die erste Datei wird importiert
|
||||
(`auto_update_and_learn.py`, `weekly_tip_generator.py`).
|
||||
- **Dritter, eigenständiger Generator:** `optimized_eurojackpot_generator.py`
|
||||
(Klasse `UltimativerEurojackpotGenerator`) ist im README als Feature
|
||||
dokumentiert, aber nirgends in der Automatisierung importiert.
|
||||
- **Ungenutzter zweiter Updater:** `scripts/utils/update_historical_data.py`
|
||||
(`EurojackpotDataUpdater`, eigene Quellen euro-jackpot.net/eurojackpot.de)
|
||||
wird von keinem aktiven Skript referenziert.
|
||||
- **~12 Standalone-Analyse-/Utility-Skripte** ohne Anbindung an die
|
||||
Automatisierung: `eurojackpot_processor.py`, `eurojackpot_processor_fixed.py`,
|
||||
`eurojackpot_simple.py`, `bereichskombinationen_analyse.py`,
|
||||
`positionsanalyse.py`, `zahlen_umschluesseln.py`,
|
||||
`treffer_analyse_umschluesselt.py`, `simple_bereichsanalyse.py`,
|
||||
`create_example_files.py`, `tipp_generator_nmmhh.py`,
|
||||
`tipp_generator_nmmhh_v2.py`, `eurojackpot_bereichsanalyse.py`,
|
||||
`eurojackpot_generator.py`.
|
||||
- **Popularity/Quality-Score veraltet** — siehe Abschnitt 3.
|
||||
- **`documentation/`-Ordner** enthält laut README selbst "Alte Dokumentation"
|
||||
(`README 2.md`, `NOTIFICATIONS_SETUP.md`, `ULTIMATE_GENERATOR_GUIDE.md`) —
|
||||
Aktualität gegenüber dem laufenden Code nicht verifiziert.
|
||||
|
||||
## 6. Code-Graph
|
||||
|
||||
Navigierbare Graph-Ansicht unter `graphify-out/`:
|
||||
|
||||
- `graphify-out/graph.html` — interaktive Visualisierung
|
||||
- `graphify-out/GRAPH_REPORT.md` — God Nodes, Communities, auffällige Verbindungen
|
||||
- `graphify-out/graph.json` — Rohdaten
|
||||
|
||||
Bei strukturellen Änderungen: `/graphify --update` zum inkrementellen
|
||||
Neuaufbau.
|
||||
@@ -1,188 +0,0 @@
|
||||
#!/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()
|
||||
@@ -1,217 +0,0 @@
|
||||
#!/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()
|
||||
@@ -1,208 +0,0 @@
|
||||
#!/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()
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/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()
|
||||
@@ -1,270 +0,0 @@
|
||||
#!/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()
|
||||
@@ -1,141 +0,0 @@
|
||||
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("...")
|
||||
@@ -1,806 +0,0 @@
|
||||
#!/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)
|
||||
@@ -1,234 +0,0 @@
|
||||
#!/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)
|
||||
@@ -1,234 +0,0 @@
|
||||
#!/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)
|
||||
@@ -93,6 +93,11 @@ class UltimateAIMLEurojackpotGenerator:
|
||||
Ultimate Eurojackpot Generator - ALLE Strategien kombiniert
|
||||
"""
|
||||
|
||||
# Empirisch belegte "griffige" Einzelzahlen, die Spieler überproportional oft frei
|
||||
# wählen (Quelle: Analysen realer Tippscheine, z.B. frz. 6aus49 über 25 Jahre:
|
||||
# populärste Zahlen 5,7,9,11,12,13). Unabhängig vom Geburtstags-Bias (<=31).
|
||||
POPULAR_PLAYER_PICKS = {5, 7, 9, 11, 12, 13}
|
||||
|
||||
def __init__(self, data_path, fast_mode=True):
|
||||
self.data_path = data_path
|
||||
self.df = None
|
||||
@@ -441,7 +446,7 @@ class UltimateAIMLEurojackpotGenerator:
|
||||
"""High-EV: 2 Zahlen >31, max. 1 Lucky Number, soft Consecutive-Vermeidung."""
|
||||
random.seed(42 + tip_number * 41)
|
||||
|
||||
lucky_numbers = {3, 7, 9, 11, 13, 17, 19, 21, 23}
|
||||
lucky_numbers = UltimateAIMLEurojackpotGenerator.POPULAR_PLAYER_PICKS
|
||||
above_31_target = 2 # für 5 Zahlen: 2 above-31 = solide EV-Basis
|
||||
|
||||
selected_main = []
|
||||
@@ -671,41 +676,91 @@ class UltimateAIMLEurojackpotGenerator:
|
||||
return (distance_score * 0.6 + range_score * 0.4)
|
||||
|
||||
def _get_smart_euro_numbers(self, tip_number, euro_preds):
|
||||
"""Intelligente Euro-Zahlen Auswahl."""
|
||||
sorted_euro = sorted(euro_preds.items(), key=lambda x: x[1], reverse=True)
|
||||
"""
|
||||
EV-bewusste Euro-Zahlen-Auswahl: gewichtete Zufallswahl aus den
|
||||
AI-Predictions statt starrer Top-2 (vorher: Tipps 1-3 bekamen IMMER
|
||||
exakt dasselbe Euro-Paar), mit Abwertung klassischer Glückszahlen
|
||||
(3, 7) - analog zur Hauptzahlen-Logik in _generate_high_ev_tip().
|
||||
|
||||
# Strategy based on tip number
|
||||
if tip_number <= 3:
|
||||
# Top predictions
|
||||
return sorted([num for num, _ in sorted_euro[:2]])
|
||||
else:
|
||||
# Mix of top and diversity
|
||||
top_euros = [num for num, _ in sorted_euro[:6]]
|
||||
random.seed(42 + tip_number * 7)
|
||||
return sorted(random.sample(top_euros, 2))
|
||||
Schwächer empirisch belegt als die Hauptzahlen-Heuristik: 1-12
|
||||
überschneidet sich mit Kalendermonaten, es gibt keine klare
|
||||
Geburtstags-Grenze wie bei den Hauptzahlen (>31). Deshalb bewusst
|
||||
konservativ: nur die zwei bekanntesten Glückszahlen abgewertet,
|
||||
keine erfundene Rangfolge aller 12 Ziffern.
|
||||
"""
|
||||
random.seed(100 + tip_number * 13)
|
||||
lucky_euro = {3, 7}
|
||||
|
||||
pool = list(euro_preds.keys())
|
||||
weights = []
|
||||
for n in pool:
|
||||
w = euro_preds.get(n, 0.1)
|
||||
if n in lucky_euro:
|
||||
w *= 0.5
|
||||
weights.append(max(0.001, w))
|
||||
|
||||
chosen = []
|
||||
for _ in range(2):
|
||||
pick = random.choices(pool, weights=weights)[0]
|
||||
idx = pool.index(pick)
|
||||
pool.pop(idx)
|
||||
weights.pop(idx)
|
||||
chosen.append(pick)
|
||||
|
||||
return sorted(chosen)
|
||||
|
||||
@staticmethod
|
||||
def _calculate_popularity_score(main_numbers):
|
||||
"""
|
||||
Schätzt den Erwartungswert-Vorteil durch Vermeidung populärer Zahlenkombinationen.
|
||||
Höher = unpopulärer = höherer Gewinnanteil bei einem Treffer.
|
||||
Basis: Spieler bevorzugen Geburtstagszahlen (1-31), Glückszahlen und Zahlenfolgen.
|
||||
Höher = unpopulärer bei Mitspielern = höherer Gewinnanteil bei einem Treffer
|
||||
(Hit-Wahrscheinlichkeit selbst ist bei i.i.d. Ziehungen nicht beeinflussbar).
|
||||
|
||||
Basis: dokumentierte Spielerverhalten-Biases (Henze/Riedwyl-artige Analysen
|
||||
realer Tippscheine): Geburtstagszahlen (<=31), einzelne "griffige" Zahlen,
|
||||
Zahlenfolgen/arithmetische Muster (z.B. 5-10-15-20-25) und "zufällig
|
||||
aussehende" ausgeglichene Odd/Even-Splits werden von Menschen
|
||||
überproportional oft gewählt - jede einzelne Kombination ist aber exakt
|
||||
gleich wahrscheinlich, unabhängig von diesen Eigenschaften.
|
||||
|
||||
Hinweis: eine "Summe nahe am Mittel"-Komponente wurde bewusst NICHT
|
||||
aufgenommen - sie widerspricht sich mit der Muster-Erkennung, da gerade
|
||||
die meistgespielten sequenziellen Kombinationen (z.B. 1-2-3-4-5) durch
|
||||
ihre enge Zahlen-Clusterung zugleich eine extreme Summe erzeugen.
|
||||
"""
|
||||
n = len(main_numbers)
|
||||
sorted_nums = sorted(main_numbers)
|
||||
|
||||
# Anteil Zahlen > 31 (Geburtstags-Range vermeiden)
|
||||
# 1) Geburtstags-Range (1-31) meiden
|
||||
above_31_ratio = sum(1 for x in main_numbers if x > 31) / n
|
||||
|
||||
# Aufeinanderfolgende Zahlen vermeiden (visuelle Muster)
|
||||
consecutive_pairs = sum(1 for i in range(n - 1) if sorted_nums[i + 1] - sorted_nums[i] == 1)
|
||||
consecutive_ratio = consecutive_pairs / (n - 1) if n > 1 else 0
|
||||
# 2) Empirisch belegte populäre Einzelzahlen meiden
|
||||
popular_ratio = sum(
|
||||
1 for x in main_numbers
|
||||
if x in UltimateAIMLEurojackpotGenerator.POPULAR_PLAYER_PICKS
|
||||
) / n
|
||||
|
||||
# Häufig gespielte "Glückszahlen" vermeiden
|
||||
lucky_numbers = {3, 7, 9, 11, 13, 17, 19, 21, 23}
|
||||
lucky_ratio = sum(1 for x in main_numbers if x in lucky_numbers) / n
|
||||
# 3) Muster/Zahlenfolgen meiden: direkte Nachbarn UND allgemeine
|
||||
# arithmetische Folgen (konstante Schrittweite, z.B. 5-10-15-20-25
|
||||
# zählt zu den meistgespielten Kombinationen überhaupt)
|
||||
diffs = [sorted_nums[i + 1] - sorted_nums[i] for i in range(n - 1)]
|
||||
small_step_ratio = sum(1 for d in diffs if d <= 5) / (n - 1) if diffs else 0
|
||||
is_perfect_progression = len(diffs) > 1 and len(set(diffs)) == 1
|
||||
pattern_penalty = small_step_ratio * (0.6 if not is_perfect_progression else 1.0)
|
||||
|
||||
score = above_31_ratio * 0.5 + (1 - consecutive_ratio) * 0.3 + (1 - lucky_ratio) * 0.2
|
||||
# 4) Odd/Even-Split: Menschen bevorzugen "ausgeglichen aussehende" Splits,
|
||||
# obwohl jede einzelne Kombination gleich wahrscheinlich ist. Bei 5
|
||||
# Zahlen ist der ausgeglichenste Split 2-3/3-2 (kein exaktes Halbieren
|
||||
# möglich), daher Distanz zum Mittelpunkt 2.5 statt n/2.
|
||||
even_count = sum(1 for x in main_numbers if x % 2 == 0)
|
||||
split_extremity = abs(even_count - (n / 2)) / (n / 2)
|
||||
|
||||
score = (
|
||||
above_31_ratio * 0.30
|
||||
+ (1 - popular_ratio) * 0.20
|
||||
+ (1 - pattern_penalty) * 0.30
|
||||
+ split_extremity * 0.20
|
||||
)
|
||||
return min(max(score, 0.0), 1.0)
|
||||
|
||||
def _calculate_quality_score(self, main_numbers, euro_numbers, main_preds, euro_preds, pattern_weight):
|
||||
@@ -741,13 +796,17 @@ class UltimateAIMLEurojackpotGenerator:
|
||||
else:
|
||||
recency_quality = 0.5
|
||||
|
||||
# Popularity/EV dominiert bewusst: AI-Score, Pattern und Recency sagen nichts
|
||||
# über die (unbeeinflussbare) Trefferwahrscheinlichkeit aus - Recency/Pattern
|
||||
# folgen zudem Heuristiken, die auch andere Systemspieler nutzen und damit die
|
||||
# Popularity eher untergraben statt sie zu unterstützen.
|
||||
quality = (
|
||||
main_quality * 0.25
|
||||
+ euro_quality * 0.15
|
||||
+ pattern_quality * 0.15
|
||||
main_quality * 0.15
|
||||
+ euro_quality * 0.10
|
||||
+ pattern_quality * 0.10
|
||||
+ diversity_quality * 0.10
|
||||
+ popularity_quality * 0.20
|
||||
+ recency_quality * 0.15
|
||||
+ popularity_quality * 0.45
|
||||
+ recency_quality * 0.10
|
||||
)
|
||||
return min(quality, 1.0)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,77 +0,0 @@
|
||||
#!/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:")
|
||||
@@ -1,165 +0,0 @@
|
||||
#!/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()
|
||||
@@ -1,220 +0,0 @@
|
||||
#!/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()
|
||||
@@ -1,62 +0,0 @@
|
||||
#!/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"
|
||||
)
|
||||
@@ -1,681 +0,0 @@
|
||||
#!/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()
|
||||
@@ -1,210 +0,0 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user