|
|
|
@@ -1,13 +1,14 @@
|
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
"""
|
|
|
|
|
Script to read and parse Powerball numbers from a CSV file.
|
|
|
|
|
Script to read and parse Powerball numbers from a CSV file with advanced pattern detection.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import csv
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
from typing import List, Dict, Tuple, Optional
|
|
|
|
|
from collections import Counter
|
|
|
|
|
from typing import List, Dict, Tuple, Optional, Set
|
|
|
|
|
from collections import Counter, defaultdict
|
|
|
|
|
from itertools import combinations
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def read_powerball_csv(filepath: str) -> List[Dict]:
|
|
|
|
@@ -115,8 +116,8 @@ def _get_recency_weighted_top_numbers(draws: List[Dict]) -> Tuple[List[int], Opt
|
|
|
|
|
sorted_draws = sorted(draws, key=lambda x: x['draw_date'])
|
|
|
|
|
total_draws = len(sorted_draws)
|
|
|
|
|
|
|
|
|
|
white_scores = Counter()
|
|
|
|
|
powerball_scores = Counter()
|
|
|
|
|
white_scores: Dict[int, float] = defaultdict(float)
|
|
|
|
|
powerball_scores: Dict[int, float] = defaultdict(float)
|
|
|
|
|
|
|
|
|
|
for index, entry in enumerate(sorted_draws, start=1):
|
|
|
|
|
weight = index / total_draws
|
|
|
|
@@ -128,8 +129,8 @@ def _get_recency_weighted_top_numbers(draws: List[Dict]) -> Tuple[List[int], Opt
|
|
|
|
|
if len(numbers) >= 6:
|
|
|
|
|
powerball_scores[numbers[5]] += weight
|
|
|
|
|
|
|
|
|
|
top_white = [number for number, _ in white_scores.most_common(5)]
|
|
|
|
|
top_powerball = powerball_scores.most_common(1)[0][0] if powerball_scores else None
|
|
|
|
|
top_white = [number for number, _ in sorted(white_scores.items(), key=lambda x: x[1], reverse=True)[:5]]
|
|
|
|
|
top_powerball = max(powerball_scores.items(), key=lambda x: x[1])[0] if powerball_scores else None
|
|
|
|
|
|
|
|
|
|
return top_white, top_powerball
|
|
|
|
|
|
|
|
|
@@ -142,8 +143,8 @@ def _get_aggressive_recency_weighted_top_numbers(draws: List[Dict]) -> Tuple[Lis
|
|
|
|
|
sorted_draws = sorted(draws, key=lambda x: x['draw_date'])
|
|
|
|
|
total_draws = len(sorted_draws)
|
|
|
|
|
|
|
|
|
|
white_scores = Counter()
|
|
|
|
|
powerball_scores = Counter()
|
|
|
|
|
white_scores: Dict[int, float] = defaultdict(float)
|
|
|
|
|
powerball_scores: Dict[int, float] = defaultdict(float)
|
|
|
|
|
|
|
|
|
|
for index, entry in enumerate(sorted_draws, start=1):
|
|
|
|
|
normalized_position = index / total_draws
|
|
|
|
@@ -156,8 +157,8 @@ def _get_aggressive_recency_weighted_top_numbers(draws: List[Dict]) -> Tuple[Lis
|
|
|
|
|
if len(numbers) >= 6:
|
|
|
|
|
powerball_scores[numbers[5]] += weight
|
|
|
|
|
|
|
|
|
|
top_white = [number for number, _ in white_scores.most_common(5)]
|
|
|
|
|
top_powerball = powerball_scores.most_common(1)[0][0] if powerball_scores else None
|
|
|
|
|
top_white = [number for number, _ in sorted(white_scores.items(), key=lambda x: x[1], reverse=True)[:5]]
|
|
|
|
|
top_powerball = max(powerball_scores.items(), key=lambda x: x[1])[0] if powerball_scores else None
|
|
|
|
|
|
|
|
|
|
return top_white, top_powerball
|
|
|
|
|
|
|
|
|
@@ -171,7 +172,7 @@ def analyze_most_frequent_numbers(data: List[Dict]) -> Dict:
|
|
|
|
|
"""
|
|
|
|
|
if not data:
|
|
|
|
|
print("No data to analyze.")
|
|
|
|
|
return
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
# Initialize lists to hold numbers for each position
|
|
|
|
|
num_positions = len(data[0]['winning_numbers'])
|
|
|
|
@@ -267,7 +268,7 @@ def generate_suggested_combination(stats: dict) -> None:
|
|
|
|
|
print("Suggested Combination Based on Statistical Analysis")
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
print("\n⚠️ DISCLAIMER: This is purely statistical analysis.")
|
|
|
|
|
print("\n*** DISCLAIMER: This is purely statistical analysis.")
|
|
|
|
|
print("Past results do NOT predict future outcomes.")
|
|
|
|
|
print("Each draw is random and independent.\n")
|
|
|
|
|
|
|
|
|
@@ -350,6 +351,569 @@ def generate_suggested_combination(stats: dict) -> None:
|
|
|
|
|
print("Insufficient data to generate a suggestion.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def detect_consecutive_patterns(data: List[Dict]) -> Dict:
|
|
|
|
|
"""
|
|
|
|
|
Analyze frequency of consecutive numbers in draws.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
data: List of powerball data dictionaries
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dictionary with consecutive number statistics
|
|
|
|
|
"""
|
|
|
|
|
consecutive_counts = Counter()
|
|
|
|
|
total_draws = len(data)
|
|
|
|
|
draws_with_consecutives = 0
|
|
|
|
|
consecutive_pairs = []
|
|
|
|
|
|
|
|
|
|
for entry in data:
|
|
|
|
|
white_balls = sorted(entry['winning_numbers'][:5])
|
|
|
|
|
has_consecutive = False
|
|
|
|
|
|
|
|
|
|
for i in range(len(white_balls) - 1):
|
|
|
|
|
if white_balls[i+1] - white_balls[i] == 1:
|
|
|
|
|
consecutive_counts[f"{white_balls[i]}-{white_balls[i+1]}"] += 1
|
|
|
|
|
consecutive_pairs.append((white_balls[i], white_balls[i+1]))
|
|
|
|
|
has_consecutive = True
|
|
|
|
|
|
|
|
|
|
if has_consecutive:
|
|
|
|
|
draws_with_consecutives += 1
|
|
|
|
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
print("PATTERN: Consecutive Numbers Analysis")
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
percentage = (draws_with_consecutives / total_draws) * 100 if total_draws > 0 else 0
|
|
|
|
|
print(f"Draws with consecutive numbers: {draws_with_consecutives}/{total_draws} ({percentage:.1f}%)")
|
|
|
|
|
|
|
|
|
|
if consecutive_counts:
|
|
|
|
|
print("\nMost common consecutive pairs:")
|
|
|
|
|
for pair, count in consecutive_counts.most_common(10):
|
|
|
|
|
pair_pct = (count / total_draws) * 100
|
|
|
|
|
print(f" {pair}: {count} times ({pair_pct:.1f}%)")
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
'draws_with_consecutives': draws_with_consecutives,
|
|
|
|
|
'percentage': percentage,
|
|
|
|
|
'common_pairs': dict(consecutive_counts.most_common(10))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def detect_odd_even_patterns(data: List[Dict]) -> Dict:
|
|
|
|
|
"""
|
|
|
|
|
Analyze odd/even distribution patterns.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
data: List of powerball data dictionaries
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dictionary with odd/even distribution statistics
|
|
|
|
|
"""
|
|
|
|
|
distribution = Counter()
|
|
|
|
|
|
|
|
|
|
for entry in data:
|
|
|
|
|
white_balls = entry['winning_numbers'][:5]
|
|
|
|
|
odd_count = sum(1 for num in white_balls if num % 2 == 1)
|
|
|
|
|
even_count = 5 - odd_count
|
|
|
|
|
distribution[f"{odd_count}odd-{even_count}even"] += 1
|
|
|
|
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
print("PATTERN: Odd/Even Distribution")
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
print("Distribution of odd vs even numbers:")
|
|
|
|
|
|
|
|
|
|
total = sum(distribution.values())
|
|
|
|
|
for pattern, count in sorted(distribution.items(), key=lambda x: x[1], reverse=True):
|
|
|
|
|
percentage = (count / total) * 100
|
|
|
|
|
print(f" {pattern}: {count} times ({percentage:.1f}%)")
|
|
|
|
|
|
|
|
|
|
return {'distribution': dict(distribution)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def detect_high_low_patterns(data: List[Dict]) -> Dict:
|
|
|
|
|
"""
|
|
|
|
|
Analyze high/low number distribution (1-35 = low, 36-69 = high).
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
data: List of powerball data dictionaries
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dictionary with high/low distribution statistics
|
|
|
|
|
"""
|
|
|
|
|
distribution = Counter()
|
|
|
|
|
SPLIT_POINT = 35
|
|
|
|
|
|
|
|
|
|
for entry in data:
|
|
|
|
|
white_balls = entry['winning_numbers'][:5]
|
|
|
|
|
low_count = sum(1 for num in white_balls if num <= SPLIT_POINT)
|
|
|
|
|
high_count = 5 - low_count
|
|
|
|
|
distribution[f"{low_count}low-{high_count}high"] += 1
|
|
|
|
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
print(f"PATTERN: High/Low Distribution (Low: 1-{SPLIT_POINT}, High: {SPLIT_POINT+1}-69)")
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
print("Distribution of low vs high numbers:")
|
|
|
|
|
|
|
|
|
|
total = sum(distribution.values())
|
|
|
|
|
for pattern, count in sorted(distribution.items(), key=lambda x: x[1], reverse=True):
|
|
|
|
|
percentage = (count / total) * 100
|
|
|
|
|
print(f" {pattern}: {count} times ({percentage:.1f}%)")
|
|
|
|
|
|
|
|
|
|
return {'distribution': dict(distribution)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def detect_sum_patterns(data: List[Dict]) -> Dict:
|
|
|
|
|
"""
|
|
|
|
|
Analyze the sum of winning numbers.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
data: List of powerball data dictionaries
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dictionary with sum statistics
|
|
|
|
|
"""
|
|
|
|
|
sums = []
|
|
|
|
|
|
|
|
|
|
for entry in data:
|
|
|
|
|
white_balls = entry['winning_numbers'][:5]
|
|
|
|
|
total = sum(white_balls)
|
|
|
|
|
sums.append(total)
|
|
|
|
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
print("PATTERN: Sum of Winning Numbers")
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
avg_sum = sum(sums) / len(sums) if sums else 0
|
|
|
|
|
min_sum = min(sums) if sums else 0
|
|
|
|
|
max_sum = max(sums) if sums else 0
|
|
|
|
|
|
|
|
|
|
print(f"Average sum: {avg_sum:.1f}")
|
|
|
|
|
print(f"Range: {min_sum} - {max_sum}")
|
|
|
|
|
|
|
|
|
|
# Create buckets for sum ranges
|
|
|
|
|
buckets = Counter()
|
|
|
|
|
for s in sums:
|
|
|
|
|
bucket = (s // 25) * 25 # Group into 25-number ranges
|
|
|
|
|
buckets[f"{bucket}-{bucket+24}"] += 1
|
|
|
|
|
|
|
|
|
|
print("\nSum distribution by range:")
|
|
|
|
|
for range_str, count in sorted(buckets.items(), key=lambda x: int(x[0].split('-')[0])):
|
|
|
|
|
percentage = (count / len(sums)) * 100
|
|
|
|
|
print(f" {range_str}: {count} times ({percentage:.1f}%)")
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
'average': avg_sum,
|
|
|
|
|
'min': min_sum,
|
|
|
|
|
'max': max_sum,
|
|
|
|
|
'buckets': dict(buckets)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def detect_gap_patterns(data: List[Dict]) -> Dict:
|
|
|
|
|
"""
|
|
|
|
|
Analyze gaps (spacing) between consecutive numbers when sorted.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
data: List of powerball data dictionaries
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dictionary with gap statistics
|
|
|
|
|
"""
|
|
|
|
|
all_gaps = []
|
|
|
|
|
|
|
|
|
|
for entry in data:
|
|
|
|
|
white_balls = sorted(entry['winning_numbers'][:5])
|
|
|
|
|
gaps = [white_balls[i+1] - white_balls[i] for i in range(len(white_balls) - 1)]
|
|
|
|
|
all_gaps.extend(gaps)
|
|
|
|
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
print("PATTERN: Gap Analysis (spacing between sorted numbers)")
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
avg_gap = sum(all_gaps) / len(all_gaps) if all_gaps else 0
|
|
|
|
|
gap_counter = Counter(all_gaps)
|
|
|
|
|
|
|
|
|
|
print(f"Average gap: {avg_gap:.1f}")
|
|
|
|
|
print("\nMost common gap sizes:")
|
|
|
|
|
for gap, count in gap_counter.most_common(10):
|
|
|
|
|
percentage = (count / len(all_gaps)) * 100
|
|
|
|
|
print(f" Gap of {gap}: {count} times ({percentage:.1f}%)")
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
'average_gap': avg_gap,
|
|
|
|
|
'gap_distribution': dict(gap_counter.most_common(15))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def detect_hot_cold_numbers(data: List[Dict], recent_draws: int = 20) -> Dict:
|
|
|
|
|
"""
|
|
|
|
|
Identify hot (frequently appearing) and cold (rarely appearing) numbers in recent draws.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
data: List of powerball data dictionaries
|
|
|
|
|
recent_draws: Number of recent draws to analyze
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dictionary with hot/cold number analysis
|
|
|
|
|
"""
|
|
|
|
|
sorted_data = sorted(data, key=lambda x: x['draw_date'])
|
|
|
|
|
recent = sorted_data[-recent_draws:] if len(sorted_data) >= recent_draws else sorted_data
|
|
|
|
|
|
|
|
|
|
white_counter = Counter()
|
|
|
|
|
powerball_counter = Counter()
|
|
|
|
|
|
|
|
|
|
for entry in recent:
|
|
|
|
|
for num in entry['winning_numbers'][:5]:
|
|
|
|
|
white_counter[num] += 1
|
|
|
|
|
if len(entry['winning_numbers']) >= 6:
|
|
|
|
|
powerball_counter[entry['winning_numbers'][5]] += 1
|
|
|
|
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
print(f"PATTERN: Hot & Cold Numbers (Last {len(recent)} draws)")
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
print(f"\nHottest white balls (appeared most in last {len(recent)} draws):")
|
|
|
|
|
for num, count in white_counter.most_common(10):
|
|
|
|
|
print(f" {num}: {count} times")
|
|
|
|
|
|
|
|
|
|
print(f"\nHottest Powerballs:")
|
|
|
|
|
for num, count in powerball_counter.most_common(5):
|
|
|
|
|
print(f" {num}: {count} times")
|
|
|
|
|
|
|
|
|
|
# Cold numbers - all possible white ball numbers (1-69) that appeared least
|
|
|
|
|
all_white_numbers = set(range(1, 70))
|
|
|
|
|
appeared = set(white_counter.keys())
|
|
|
|
|
not_appeared = all_white_numbers - appeared
|
|
|
|
|
|
|
|
|
|
print(f"\nColdest white balls (appeared least or not at all):")
|
|
|
|
|
cold_numbers = sorted(
|
|
|
|
|
[(num, white_counter.get(num, 0)) for num in all_white_numbers],
|
|
|
|
|
key=lambda x: (x[1], x[0])
|
|
|
|
|
)[:10]
|
|
|
|
|
|
|
|
|
|
for num, count in cold_numbers:
|
|
|
|
|
if count == 0:
|
|
|
|
|
print(f" {num}: 0 times (not appeared)")
|
|
|
|
|
else:
|
|
|
|
|
print(f" {num}: {count} times")
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
'hot_white': dict(white_counter.most_common(10)),
|
|
|
|
|
'hot_powerball': dict(powerball_counter.most_common(5)),
|
|
|
|
|
'cold_white': dict(cold_numbers[:10])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def detect_overdue_numbers(data: List[Dict]) -> Dict:
|
|
|
|
|
"""
|
|
|
|
|
Identify numbers that haven't appeared in the longest time.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
data: List of powerball data dictionaries
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dictionary with overdue number analysis
|
|
|
|
|
"""
|
|
|
|
|
sorted_data = sorted(data, key=lambda x: x['draw_date'])
|
|
|
|
|
|
|
|
|
|
# Track last appearance of each number
|
|
|
|
|
last_seen_white = {}
|
|
|
|
|
last_seen_powerball = {}
|
|
|
|
|
|
|
|
|
|
for entry in sorted_data:
|
|
|
|
|
draw_date = entry['draw_date']
|
|
|
|
|
for num in entry['winning_numbers'][:5]:
|
|
|
|
|
last_seen_white[num] = draw_date
|
|
|
|
|
if len(entry['winning_numbers']) >= 6:
|
|
|
|
|
last_seen_powerball[entry['winning_numbers'][5]] = draw_date
|
|
|
|
|
|
|
|
|
|
most_recent_draw = sorted_data[-1]['draw_date']
|
|
|
|
|
|
|
|
|
|
# Calculate days since last appearance
|
|
|
|
|
overdue_white = {}
|
|
|
|
|
for num in range(1, 70): # White balls are 1-69
|
|
|
|
|
if num in last_seen_white:
|
|
|
|
|
days_overdue = (most_recent_draw - last_seen_white[num]).days
|
|
|
|
|
overdue_white[num] = days_overdue
|
|
|
|
|
else:
|
|
|
|
|
overdue_white[num] = float('inf') # Never appeared
|
|
|
|
|
|
|
|
|
|
overdue_powerball = {}
|
|
|
|
|
for num in range(1, 27): # Powerballs are 1-26
|
|
|
|
|
if num in last_seen_powerball:
|
|
|
|
|
days_overdue = (most_recent_draw - last_seen_powerball[num]).days
|
|
|
|
|
overdue_powerball[num] = days_overdue
|
|
|
|
|
else:
|
|
|
|
|
overdue_powerball[num] = float('inf')
|
|
|
|
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
print("PATTERN: Overdue Numbers (longest time since last appearance)")
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
print("\nMost overdue white balls:")
|
|
|
|
|
sorted_overdue_white = sorted(overdue_white.items(), key=lambda x: x[1], reverse=True)[:10]
|
|
|
|
|
for num, days in sorted_overdue_white:
|
|
|
|
|
if days == float('inf'):
|
|
|
|
|
print(f" {num}: Never appeared")
|
|
|
|
|
else:
|
|
|
|
|
print(f" {num}: {days} days ago")
|
|
|
|
|
|
|
|
|
|
print("\nMost overdue Powerballs:")
|
|
|
|
|
sorted_overdue_powerball = sorted(overdue_powerball.items(), key=lambda x: x[1], reverse=True)[:5]
|
|
|
|
|
for num, days in sorted_overdue_powerball:
|
|
|
|
|
if days == float('inf'):
|
|
|
|
|
print(f" {num}: Never appeared")
|
|
|
|
|
else:
|
|
|
|
|
print(f" {num}: {days} days ago")
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
'overdue_white': dict(sorted_overdue_white),
|
|
|
|
|
'overdue_powerball': dict(sorted_overdue_powerball)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def detect_number_pairs(data: List[Dict], top_n: int = 10) -> Dict:
|
|
|
|
|
"""
|
|
|
|
|
Identify which number pairs appear together most frequently.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
data: List of powerball data dictionaries
|
|
|
|
|
top_n: Number of top pairs to display
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dictionary with pair frequency analysis
|
|
|
|
|
"""
|
|
|
|
|
pair_counter = Counter()
|
|
|
|
|
|
|
|
|
|
for entry in data:
|
|
|
|
|
white_balls = entry['winning_numbers'][:5]
|
|
|
|
|
# Get all pairs from this draw
|
|
|
|
|
for pair in combinations(sorted(white_balls), 2):
|
|
|
|
|
pair_counter[pair] += 1
|
|
|
|
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
print("PATTERN: Number Pair Analysis")
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
print(f"\nTop {top_n} most common number pairs:")
|
|
|
|
|
|
|
|
|
|
for (num1, num2), count in pair_counter.most_common(top_n):
|
|
|
|
|
percentage = (count / len(data)) * 100
|
|
|
|
|
print(f" ({num1}, {num2}): appeared together {count} times ({percentage:.1f}%)")
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
'top_pairs': dict(pair_counter.most_common(top_n))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def detect_decade_distribution(data: List[Dict]) -> Dict:
|
|
|
|
|
"""
|
|
|
|
|
Analyze how numbers are distributed across decades (1-9, 10-19, 20-29, etc.).
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
data: List of powerball data dictionaries
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dictionary with decade distribution statistics
|
|
|
|
|
"""
|
|
|
|
|
decade_counts = Counter()
|
|
|
|
|
|
|
|
|
|
for entry in data:
|
|
|
|
|
white_balls = entry['winning_numbers'][:5]
|
|
|
|
|
for num in white_balls:
|
|
|
|
|
decade = (num // 10) * 10
|
|
|
|
|
decade_label = f"{decade:02d}-{decade+9:02d}" if decade < 60 else "60-69"
|
|
|
|
|
decade_counts[decade_label] += 1
|
|
|
|
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
print("PATTERN: Decade Distribution")
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
print("How often numbers from each range appear:")
|
|
|
|
|
|
|
|
|
|
total = sum(decade_counts.values())
|
|
|
|
|
for decade in ["00-09", "10-19", "20-29", "30-39", "40-49", "50-59", "60-69"]:
|
|
|
|
|
count = decade_counts.get(decade, 0)
|
|
|
|
|
percentage = (count / total) * 100 if total > 0 else 0
|
|
|
|
|
print(f" {decade}: {count} times ({percentage:.1f}%)")
|
|
|
|
|
|
|
|
|
|
return {'decade_distribution': dict(decade_counts)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def run_all_pattern_analyses(data: List[Dict]) -> Dict:
|
|
|
|
|
"""
|
|
|
|
|
Run all pattern detection analyses.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
data: List of powerball data dictionaries
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
Dictionary containing all pattern analysis results
|
|
|
|
|
"""
|
|
|
|
|
print("\n" + "#" * 60)
|
|
|
|
|
print("# ADVANCED PATTERN DETECTION ANALYSIS")
|
|
|
|
|
print("#" * 60)
|
|
|
|
|
|
|
|
|
|
results = {}
|
|
|
|
|
|
|
|
|
|
results['consecutive'] = detect_consecutive_patterns(data)
|
|
|
|
|
results['odd_even'] = detect_odd_even_patterns(data)
|
|
|
|
|
results['high_low'] = detect_high_low_patterns(data)
|
|
|
|
|
results['sum'] = detect_sum_patterns(data)
|
|
|
|
|
results['gaps'] = detect_gap_patterns(data)
|
|
|
|
|
results['hot_cold'] = detect_hot_cold_numbers(data, recent_draws=20)
|
|
|
|
|
results['overdue'] = detect_overdue_numbers(data)
|
|
|
|
|
results['pairs'] = detect_number_pairs(data, top_n=15)
|
|
|
|
|
results['decades'] = detect_decade_distribution(data)
|
|
|
|
|
|
|
|
|
|
return results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_pattern_based_combinations(data: List[Dict], pattern_results: Dict, num_combinations: int = 5) -> List[Dict]:
|
|
|
|
|
"""
|
|
|
|
|
Generate number combinations based on pattern analysis.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
data: List of powerball data dictionaries
|
|
|
|
|
pattern_results: Results from run_all_pattern_analyses
|
|
|
|
|
num_combinations: Number of combinations to generate
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
List of dictionaries containing combination details
|
|
|
|
|
"""
|
|
|
|
|
import random
|
|
|
|
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
print("PATTERN-BASED NUMBER COMBINATIONS")
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
print("\n*** DISCLAIMER: These combinations are based on pattern analysis.")
|
|
|
|
|
print("Lottery draws are random. Past patterns do NOT predict future results.")
|
|
|
|
|
print("Play responsibly.\n")
|
|
|
|
|
|
|
|
|
|
combinations = []
|
|
|
|
|
|
|
|
|
|
# Get pattern insights
|
|
|
|
|
hot_white = list(pattern_results['hot_cold']['hot_white'].keys())[:15]
|
|
|
|
|
cold_white = [num for num, _ in pattern_results['hot_cold']['cold_white'].items() if _ < 3][:10]
|
|
|
|
|
overdue_white = [num for num, days in pattern_results['overdue']['overdue_white'].items() if days != float('inf')][:15]
|
|
|
|
|
hot_powerball = list(pattern_results['hot_cold']['hot_powerball'].keys())[:3]
|
|
|
|
|
overdue_powerball = [num for num, days in pattern_results['overdue']['overdue_powerball'].items() if days != float('inf')][:5]
|
|
|
|
|
|
|
|
|
|
# Get most common distributions
|
|
|
|
|
odd_even_dist = pattern_results['odd_even']['distribution']
|
|
|
|
|
most_common_oe = max(odd_even_dist.items(), key=lambda x: x[1])[0]
|
|
|
|
|
target_odds = int(most_common_oe.split('odd')[0])
|
|
|
|
|
|
|
|
|
|
high_low_dist = pattern_results['high_low']['distribution']
|
|
|
|
|
most_common_hl = max(high_low_dist.items(), key=lambda x: x[1])[0]
|
|
|
|
|
target_lows = int(most_common_hl.split('low')[0])
|
|
|
|
|
|
|
|
|
|
# Get average sum
|
|
|
|
|
avg_sum = pattern_results['sum']['average']
|
|
|
|
|
sum_tolerance = 30
|
|
|
|
|
|
|
|
|
|
# Get top pairs
|
|
|
|
|
top_pairs = list(pattern_results['pairs']['top_pairs'].keys())[:20]
|
|
|
|
|
|
|
|
|
|
# Strategy descriptions
|
|
|
|
|
strategies = [
|
|
|
|
|
("Hot Numbers Focus", hot_white, "Uses frequently drawn recent numbers"),
|
|
|
|
|
("Balanced Hot + Overdue", hot_white[:8] + overdue_white[:12], "Mixes hot numbers with overdue picks"),
|
|
|
|
|
("Overdue Numbers Focus", overdue_white, "Focuses on numbers that haven't appeared recently"),
|
|
|
|
|
("Cold Numbers Gamble", cold_white + hot_white[:10], "Includes rarely drawn numbers"),
|
|
|
|
|
("Pair-Based Selection", [n for pair in top_pairs for n in pair], "Uses numbers from common pairs")
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
for strategy_idx in range(min(num_combinations, len(strategies))):
|
|
|
|
|
strategy_name, number_pool, strategy_desc = strategies[strategy_idx]
|
|
|
|
|
|
|
|
|
|
# Ensure we have enough numbers in pool
|
|
|
|
|
if len(number_pool) < 20:
|
|
|
|
|
number_pool = list(set(number_pool + hot_white + list(range(1, 70))))
|
|
|
|
|
|
|
|
|
|
# Try to generate a combination that fits the patterns
|
|
|
|
|
attempts = 0
|
|
|
|
|
max_attempts = 1000
|
|
|
|
|
selected = [] # Initialize to avoid unbound variable
|
|
|
|
|
|
|
|
|
|
while attempts < max_attempts:
|
|
|
|
|
# Select 5 random numbers from pool
|
|
|
|
|
selected = random.sample(number_pool[:30], min(5, len(number_pool[:30])))
|
|
|
|
|
selected = sorted(selected)
|
|
|
|
|
|
|
|
|
|
# Check odd/even distribution
|
|
|
|
|
odds_count = sum(1 for n in selected if n % 2 == 1)
|
|
|
|
|
if abs(odds_count - target_odds) > 1:
|
|
|
|
|
attempts += 1
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# Check high/low distribution
|
|
|
|
|
lows_count = sum(1 for n in selected if n <= 35)
|
|
|
|
|
if abs(lows_count - target_lows) > 1:
|
|
|
|
|
attempts += 1
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# Check sum is reasonable
|
|
|
|
|
total = sum(selected)
|
|
|
|
|
if abs(total - avg_sum) > sum_tolerance:
|
|
|
|
|
attempts += 1
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# Check gaps are reasonable (not too clustered or too spread)
|
|
|
|
|
gaps = [selected[i+1] - selected[i] for i in range(4)]
|
|
|
|
|
if min(gaps) < 2 or max(gaps) > 25:
|
|
|
|
|
attempts += 1
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# Good combination found
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
# If we didn't find a perfect match, selected still has the last attempt
|
|
|
|
|
if not selected:
|
|
|
|
|
selected = sorted(random.sample(range(1, 70), 5))
|
|
|
|
|
|
|
|
|
|
# Select powerball
|
|
|
|
|
if strategy_idx % 2 == 0 and hot_powerball:
|
|
|
|
|
powerball = random.choice(hot_powerball)
|
|
|
|
|
elif overdue_powerball:
|
|
|
|
|
powerball = random.choice(overdue_powerball[:5])
|
|
|
|
|
else:
|
|
|
|
|
powerball = random.randint(1, 26)
|
|
|
|
|
|
|
|
|
|
# Calculate combination stats
|
|
|
|
|
odds = sum(1 for n in selected if n % 2 == 1)
|
|
|
|
|
evens = 5 - odds
|
|
|
|
|
lows = sum(1 for n in selected if n <= 35)
|
|
|
|
|
highs = 5 - lows
|
|
|
|
|
total = sum(selected)
|
|
|
|
|
|
|
|
|
|
combinations.append({
|
|
|
|
|
'strategy': strategy_name,
|
|
|
|
|
'description': strategy_desc,
|
|
|
|
|
'numbers': selected,
|
|
|
|
|
'powerball': powerball,
|
|
|
|
|
'odds': odds,
|
|
|
|
|
'evens': evens,
|
|
|
|
|
'lows': lows,
|
|
|
|
|
'highs': highs,
|
|
|
|
|
'sum': total,
|
|
|
|
|
'attempts': attempts
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
# Display combinations
|
|
|
|
|
for i, combo in enumerate(combinations, 1):
|
|
|
|
|
print(f"\nCombination #{i}: {combo['strategy']}")
|
|
|
|
|
print(f" Strategy: {combo['description']}")
|
|
|
|
|
print(f" Numbers: {' '.join(map(str, combo['numbers']))} + Powerball: {combo['powerball']}")
|
|
|
|
|
print(f" Stats: {combo['odds']}odd-{combo['evens']}even, {combo['lows']}low-{combo['highs']}high, Sum: {combo['sum']}")
|
|
|
|
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
print("Quick Pick Lines:")
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
for i, combo in enumerate(combinations, 1):
|
|
|
|
|
numbers_str = ' - '.join(f"{n:2d}" for n in combo['numbers'])
|
|
|
|
|
print(f" Line {i}: {numbers_str} | PB: {combo['powerball']:2d}")
|
|
|
|
|
|
|
|
|
|
return combinations
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
"""Main function."""
|
|
|
|
|
# Default to 'powerball_numbers.csv' in the same directory as the script
|
|
|
|
@@ -368,6 +932,12 @@ def main():
|
|
|
|
|
display_powerball_data(powerball_data)
|
|
|
|
|
stats = analyze_most_frequent_numbers(powerball_data)
|
|
|
|
|
generate_suggested_combination(stats)
|
|
|
|
|
|
|
|
|
|
# Run advanced pattern detection
|
|
|
|
|
pattern_results = run_all_pattern_analyses(powerball_data)
|
|
|
|
|
|
|
|
|
|
# Generate pattern-based combinations
|
|
|
|
|
pattern_combos = generate_pattern_based_combinations(powerball_data, pattern_results, num_combinations=5)
|
|
|
|
|
else:
|
|
|
|
|
print("Failed to load powerball data.")
|
|
|
|
|
|
|
|
|
|