#!/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()