Compare commits

...

2 Commits

Author SHA1 Message Date
wjones e50a5c2e05 Merge branch 'main' of https://gitea.wpjones.com/wjones/utilities 2026-06-24 14:36:07 -04:00
wjones 41942eb9c9 Add advanced pattern detection features to Powerball reader
- Enhanced CSV parsing script to include advanced statistical analyses.
- Implemented functions to detect consecutive numbers, odd/even distributions, high/low distributions, sum patterns, gap patterns, hot/cold numbers, overdue numbers, number pairs, and decade distributions.
- Integrated pattern analysis results into combination generation for more strategic number selection.
- Updated main function to run advanced analyses and generate pattern-based combinations.
2026-06-24 14:35:29 -04:00
6 changed files with 4569 additions and 15 deletions
@@ -22,7 +22,7 @@ IPMIPW = None
IPMIEK = DEFAULT_EK
# Fan / temperature settings
FANSPEED = 20 # percent
FANSPEED = 15 # percent
MAXTEMP = 37 # Celsius
def build_ipmi_base():
+10
View File
@@ -77,6 +77,16 @@ clean_walfiles() {
fi
done < <(find "$WAL_ARCHIVE_DIR" -maxdepth 1 -type f ! -name '*.backup' -print0)
# Also delete old .backup files (except the newest one) that exceed retention period
for backup_file in "${backup_files[@]}"; do
if [[ "$backup_file" != "$newest_backup" ]]; then
backup_ts=$(stat -c %Y "$backup_file")
if [[ "$backup_ts" -lt "$age_cutoff_ts" ]]; then
files_to_delete+=("$backup_file")
fi
fi
done
if [[ ${#files_to_delete[@]} -eq 0 ]]; then
log "No WAL files matched cleanup criteria."
return 0
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env python3
"""Fix the powerball CSV file format."""
import shutil
# Read the original file
with open('powerball_numbers.csv', 'r') as f:
lines = f.readlines()
# Create backup
shutil.copy('powerball_numbers.csv', 'powerball_numbers.csv.backup')
# Open output file
with open('powerball_numbers.csv', 'w') as f:
# Write header
f.write('Draw Date,Winning Numbers,Multiplier\n')
# Process each line
for line in lines:
parts = line.strip().split(',')
# Extract components
month = parts[0]
day_year = parts[1].split('/')
day = day_year[0]
year = day_year[1]
# Format date as MM/DD/YYYY
date = f"{int(month):02d}/{int(day):02d}/{year}"
# Get the 6 winning numbers (5 white balls + 1 powerball)
numbers = ' '.join(parts[2:8])
# Get the multiplier
multiplier = parts[8]
# Write the formatted line
f.write(f'{date},{numbers},{multiplier}\n')
print("✓ File fixed! Created backup at powerball_numbers.csv.backup")
print("\nFirst few lines of fixed file:")
with open('powerball_numbers.csv', 'r') as f:
for i, line in enumerate(f):
if i < 5:
print(line.strip())
else:
break
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+584 -14
View File
@@ -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.")