Files
utilities/personal/tools/powerball_reader.py
T
2026-04-23 08:26:38 -04:00

377 lines
14 KiB
Python

#!/usr/bin/env python3
"""
Script to read and parse Powerball numbers from a CSV file.
"""
import csv
from datetime import datetime
from pathlib import Path
from typing import List, Dict, Tuple, Optional
from collections import Counter
def read_powerball_csv(filepath: str) -> List[Dict]:
"""
Read a CSV file containing Powerball numbers.
Expected CSV format:
Draw Date,Winning Numbers,Multiplier
09/26/2020,11 21 27 36 62 24,3
Args:
filepath: Path to the CSV file
Returns:
List of dictionaries containing parsed powerball data
"""
powerball_data = []
try:
with open(filepath, 'r', encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile)
for row_num, row in enumerate(reader, start=2): # start=2 because row 1 is header
try:
# Skip empty rows
if not any(row.values()):
continue
# Parse the row
draw_date = datetime.strptime(row['Draw Date'].strip(), '%m/%d/%Y')
# Parse winning numbers - filter out empty strings
numbers_str = row['Winning Numbers'].strip()
winning_numbers = [int(n) for n in numbers_str.split() if n.strip()]
multiplier = int(row['Multiplier'].strip())
powerball_data.append({
'draw_date': draw_date,
'date_str': draw_date.strftime('%Y-%m-%d'),
'winning_numbers': winning_numbers,
'multiplier': multiplier
})
except ValueError as e:
print(f"Warning: Skipping row {row_num} due to error: {e}")
print(f" Row data: {row}")
continue
except FileNotFoundError:
print(f"Error: File '{filepath}' not found.")
return []
except Exception as e:
print(f"Error: Failed to read CSV file - {e}")
return []
return powerball_data
def display_powerball_data(data: List[Dict]) -> None:
"""
Display powerball data in a formatted table.
Args:
data: List of powerball data dictionaries
"""
if not data:
print("No data to display.")
return
print(f"{'Draw Date':<12} {'Winning Numbers':<30} {'Multiplier':<10}")
print("-" * 52)
for entry in data:
numbers_str = ' '.join(map(str, entry['winning_numbers']))
print(f"{entry['date_str']:<12} {numbers_str:<30} {entry['multiplier']:<10}")
def _get_unweighted_top_numbers(draws: List[Dict]) -> Tuple[List[int], Optional[int]]:
"""Return top 5 white balls and top Powerball from a draw list."""
if not draws:
return [], None
white_counter = Counter()
powerball_counter = Counter()
for entry in draws:
numbers = entry['winning_numbers']
for number in numbers[:5]:
white_counter[number] += 1
if len(numbers) >= 6:
powerball_counter[numbers[5]] += 1
top_white = [number for number, _ in white_counter.most_common(5)]
top_powerball = powerball_counter.most_common(1)[0][0] if powerball_counter else None
return top_white, top_powerball
def _get_recency_weighted_top_numbers(draws: List[Dict]) -> Tuple[List[int], Optional[int]]:
"""Return top numbers weighted so newer draws contribute more."""
if not draws:
return [], None
sorted_draws = sorted(draws, key=lambda x: x['draw_date'])
total_draws = len(sorted_draws)
white_scores = Counter()
powerball_scores = Counter()
for index, entry in enumerate(sorted_draws, start=1):
weight = index / total_draws
numbers = entry['winning_numbers']
for number in numbers[:5]:
white_scores[number] += weight
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
return top_white, top_powerball
def _get_aggressive_recency_weighted_top_numbers(draws: List[Dict]) -> Tuple[List[int], Optional[int]]:
"""Return top numbers with an aggressive recency curve (cubic weighting)."""
if not draws:
return [], None
sorted_draws = sorted(draws, key=lambda x: x['draw_date'])
total_draws = len(sorted_draws)
white_scores = Counter()
powerball_scores = Counter()
for index, entry in enumerate(sorted_draws, start=1):
normalized_position = index / total_draws
weight = normalized_position ** 3
numbers = entry['winning_numbers']
for number in numbers[:5]:
white_scores[number] += weight
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
return top_white, top_powerball
def analyze_most_frequent_numbers(data: List[Dict]) -> Dict:
"""
Analyze and display the most frequent number in each slot position.
Args:
data: List of powerball data dictionaries
"""
if not data:
print("No data to analyze.")
return
# Initialize lists to hold numbers for each position
num_positions = len(data[0]['winning_numbers'])
position_numbers = [[] for _ in range(num_positions)]
# Collect all numbers for each position
for entry in data:
for position, number in enumerate(entry['winning_numbers']):
position_numbers[position].append(number)
# Find most common number in each position
print("\n" + "=" * 60)
print("Most Frequent Numbers by Slot Position")
print("=" * 60)
most_frequent = []
for position, numbers in enumerate(position_numbers, start=1):
counter = Counter(numbers)
most_common_num, frequency = counter.most_common(1)[0]
most_frequent.append(most_common_num)
percentage = (frequency / len(numbers)) * 100
print(f"Position {position}: {most_common_num:<3} (appears {frequency} times, {percentage:.1f}%)")
print("-" * 60)
print(f"Most frequent numbers combined: {' '.join(map(str, most_frequent))}")
# Analyze most frequent number across first 5 positions combined
print("\n" + "=" * 60)
print("Additional Analysis")
print("=" * 60)
# Combine all numbers from positions 1-5
first_five_combined = []
for i in range(min(5, num_positions)):
first_five_combined.extend(position_numbers[i])
top_five_numbers = []
top_ten_numbers = []
if first_five_combined:
counter_first_five = Counter(first_five_combined)
top_five_numbers = counter_first_five.most_common(5)
top_ten_numbers = counter_first_five.most_common(10)
print("Top 5 most drawn numbers in first 5 positions combined:")
for rank, (number, frequency) in enumerate(top_five_numbers, start=1):
percentage = (frequency / len(first_five_combined)) * 100
print(f" {rank}. {number:<3} (appears {frequency} times, {percentage:.1f}%)")
# Analyze the sixth position (Powerball)
powerball_ranked = []
most_common_sixth = None
if num_positions >= 6:
sixth_position_numbers = position_numbers[5]
counter_sixth = Counter(sixth_position_numbers)
powerball_ranked = counter_sixth.most_common(5)
most_common_sixth, freq_sixth = counter_sixth.most_common(1)[0]
percentage_sixth = (freq_sixth / len(sixth_position_numbers)) * 100
print(f"\nMost drawn number in position 6 (Powerball): {most_common_sixth} (appears {freq_sixth} times, {percentage_sixth:.1f}%)")
# Third strategy: most frequent numbers from the most recent 104 draws
sorted_by_date = sorted(data, key=lambda x: x['draw_date'])
recent_104_draws = sorted_by_date[-104:]
recent_104_top_five, recent_104_powerball = _get_unweighted_top_numbers(recent_104_draws)
# Fourth strategy: weighted-by-recency across all draws
weighted_top_five, weighted_powerball = _get_recency_weighted_top_numbers(data)
# Fifth strategy: aggressive weighted-by-recency across all draws
aggressive_weighted_top_five, aggressive_weighted_powerball = _get_aggressive_recency_weighted_top_numbers(data)
# Return statistics for prediction
return {
'top_five_combined': [num for num, freq in top_five_numbers] if first_five_combined else [],
'top_ten_combined': [num for num, freq in top_ten_numbers],
'most_common_powerball': most_common_sixth,
'powerball_ranked': [num for num, freq in powerball_ranked],
'recent_104_top_five': recent_104_top_five,
'recent_104_powerball': recent_104_powerball,
'weighted_top_five': weighted_top_five,
'weighted_powerball': weighted_powerball,
'aggressive_weighted_top_five': aggressive_weighted_top_five,
'aggressive_weighted_powerball': aggressive_weighted_powerball
}
def generate_suggested_combination(stats: dict) -> None:
"""
Generate a suggested combination based on statistical analysis.
Args:
stats: Dictionary containing statistical analysis results
"""
print("\n" + "=" * 60)
print("Suggested Combination Based on Statistical Analysis")
print("=" * 60)
print("\n⚠️ DISCLAIMER: This is purely statistical analysis.")
print("Past results do NOT predict future outcomes.")
print("Each draw is random and independent.\n")
top_five = stats['top_five_combined']
top_ten = stats.get('top_ten_combined', [])
powerball = stats['most_common_powerball']
ranked_powerballs = stats.get('powerball_ranked', [])
recent_104_top_five = stats.get('recent_104_top_five', [])
recent_104_powerball = stats.get('recent_104_powerball')
weighted_top_five = stats.get('weighted_top_five', [])
weighted_powerball = stats.get('weighted_powerball')
aggressive_weighted_top_five = stats.get('aggressive_weighted_top_five', [])
aggressive_weighted_powerball = stats.get('aggressive_weighted_powerball')
if len(top_five) >= 5 and powerball:
# Use the top 5 most frequent numbers from positions 1-5
primary_numbers = sorted(top_five[:5])
primary_powerball = powerball
# Build a secondary set from the next-most-frequent historical numbers.
secondary_pool = top_ten[5:10]
if len(secondary_pool) < 5:
secondary_pool = top_ten[:5]
secondary_numbers = sorted(secondary_pool[:5])
secondary_powerball = ranked_powerballs[1] if len(ranked_powerballs) > 1 else primary_powerball
third_numbers = sorted(recent_104_top_five[:5]) if len(recent_104_top_five) >= 5 else []
third_powerball = recent_104_powerball
fourth_numbers = sorted(weighted_top_five[:5]) if len(weighted_top_five) >= 5 else []
fourth_powerball = weighted_powerball
fifth_numbers = sorted(aggressive_weighted_top_five[:5]) if len(aggressive_weighted_top_five) >= 5 else []
fifth_powerball = aggressive_weighted_powerball
print("Primary set (most frequent):")
print(f" White balls: {' '.join(map(str, primary_numbers))}")
print(f" Powerball: {primary_powerball}")
print("\nSecondary set (next-most frequent):")
print(f" White balls: {' '.join(map(str, secondary_numbers))}")
print(f" Powerball: {secondary_powerball}")
if third_numbers and third_powerball:
print("\nThird set (most frequent in recent 104 draws):")
print(f" White balls: {' '.join(map(str, third_numbers))}")
print(f" Powerball: {third_powerball}")
if fourth_numbers and fourth_powerball:
print("\nFourth set (weighted by recency):")
print(f" White balls: {' '.join(map(str, fourth_numbers))}")
print(f" Powerball: {fourth_powerball}")
if fifth_numbers and fifth_powerball:
print("\nFifth set (aggressive recency weighting):")
print(f" White balls: {' '.join(map(str, fifth_numbers))}")
print(f" Powerball: {fifth_powerball}")
print()
print(f"Primary combination: {' '.join(map(str, primary_numbers))} + {primary_powerball}")
print(f"Secondary combination: {' '.join(map(str, secondary_numbers))} + {secondary_powerball}")
if third_numbers and third_powerball:
print(f"Third combination: {' '.join(map(str, third_numbers))} + {third_powerball}")
if fourth_numbers and fourth_powerball:
print(f"Fourth combination: {' '.join(map(str, fourth_numbers))} + {fourth_powerball}")
if fifth_numbers and fifth_powerball:
print(f"Fifth combination: {' '.join(map(str, fifth_numbers))} + {fifth_powerball}")
print()
print("These combinations are based on:")
print(" • The 5 most frequently drawn numbers across all white ball positions")
print(f" • The most frequently drawn Powerball number ({primary_powerball})")
print(" • A secondary set from the next-most-frequent white ball and Powerball values")
print(" • Recent-window frequency (last 104 draws)")
print(" • Recency-weighted scoring where newer draws count more")
print(" • Aggressive recency weighting using a cubic curve")
else:
print("Insufficient data to generate a suggestion.")
def main():
"""Main function."""
# Default to 'powerball_numbers.csv' in the same directory as the script
script_dir = Path(__file__).parent
csv_file = script_dir / 'powerball_numbers.csv'
# You can also specify a different file path here
# csv_file = Path('path/to/your/file.csv')
print(f"Reading Powerball numbers from: {csv_file}\n")
powerball_data = read_powerball_csv(str(csv_file))
if powerball_data:
print(f"Successfully loaded {len(powerball_data)} records.\n")
display_powerball_data(powerball_data)
stats = analyze_most_frequent_numbers(powerball_data)
generate_suggested_combination(stats)
else:
print("Failed to load powerball data.")
if __name__ == '__main__':
main()