import pandas as pd
import swisseph as swe
import numpy as np
from datetime import datetime, timedelta
import os
# Configuration
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
WEATHER_FILE = os.path.join(BASE_DIR, 'weather_data_full_checkpoint.csv')
OUTPUT_FILE = os.path.join(BASE_DIR, 'weather_with_astro_full.csv')
# Initialize Ephemeris
swe.set_ephe_path('/usr/share/swisseph') # Default or None often works if env var set
def get_julian_day(date_str):
dt = datetime.strptime(date_str, '%Y-%m-%d')
return swe.julday(dt.year, dt.month, dt.day, 12.0) # Noon
def get_zodiac_sign(lon):
return int(lon / 30)
def get_element(sign_idx):
# 0=Fire, 1=Earth, 2=Air, 3=Water
# Aries(0)=Fire, Taurus(1)=Earth, Gemini(2)=Air, Cancer(3)=Water...
elements = ['Fire', 'Earth', 'Air', 'Water']
return elements[sign_idx % 4]
def normalize_angle(angle):
return angle % 360
def calculate_astro_features(row):
jd = get_julian_day(row['date'])
# Planets to track
planets = {
'Sun': swe.SUN,
'Moon': swe.MOON,
'Mars': swe.MARS,
'Venus': swe.VENUS,
'Jupiter': swe.JUPITER,
'Saturn': swe.SATURN,
'Uranus': swe.URANUS,
'Neptune': swe.NEPTUNE,
'Pluto': swe.PLUTO,
'Node': swe.MEAN_NODE
}
results = {}
# --- TROPICAL ---
swe.set_sid_mode(swe.SIDM_FAGAN_BRADLEY) # Reset/Standard Tropical? Actually standard is default.
# To be safe for tropical, we don't set sid mode or we set 0?
# Standard swe_calc returns tropical by default unless specific flag used
tropical_pos = {}
for name, pid in planets.items():
res = swe.calc_ut(jd, pid)
tropical_pos[name] = res[0][0] # Longitude
results[f'{name}_Tro_Lon'] = tropical_pos[name]
sign_idx = get_zodiac_sign(tropical_pos[name])
results[f'{name}_Tro_Sign'] = sign_idx
results[f'{name}_Tro_Elem'] = get_element(sign_idx)
# --- VEDIC (LAHIRI) ---
swe.set_sid_mode(swe.SIDM_LAHIRI)
vedic_pos = {}
for name, pid in planets.items():
res = swe.calc_ut(jd, pid, swe.FLG_SIDEREAL)
vedic_pos[name] = res[0][0]
results[f'{name}_Ved_Lon'] = vedic_pos[name]
sign_idx = get_zodiac_sign(vedic_pos[name])
results[f'{name}_Ved_Sign'] = sign_idx
results[f'{name}_Ved_Elem'] = get_element(sign_idx)
# --- ANGLES (0-360) ---
# Sun-Moon Angle (Lunar Phase/Day)
# Tithi Calculation: (Moon - Sun) / 12
# Tropical Lunar Day
diff_tro = normalize_angle(tropical_pos['Moon'] - tropical_pos['Sun'])
results['Sun_Moon_Angle_Tro'] = diff_tro
results['Lunar_Day_Tro'] = int(diff_tro / 12) + 1
# Vedic Lunar Day
diff_ved = normalize_angle(vedic_pos['Moon'] - vedic_pos['Sun'])
results['Sun_Moon_Angle_Ved'] = diff_ved
results['Lunar_Day_Ved'] = int(diff_ved / 12) + 1
# Aspect Angles (0-360) for major pairs (requested "Angles from 1 to 360")
# Doing primary pairs
pairs = [('Sun', 'Mars'), ('Sun', 'Saturn'), ('Sun', 'Jupiter'),
('Moon', 'Mars'), ('Moon', 'Saturn'), ('Venus', 'Mars')]
for p1, p2 in pairs:
# Tropical
angle_tro = normalize_angle(tropical_pos[p2] - tropical_pos[p1])
results[f'{p1}_{p2}_Angle_Tro'] = angle_tro
# Vedic
angle_ved = normalize_angle(vedic_pos[p2] - vedic_pos[p1])
results[f'{p1}_{p2}_Angle_Ved'] = angle_ved
return pd.Series(results)
def main():
print("Loading weather data...")
df = pd.read_csv(WEATHER_FILE)
print(f"Loaded {len(df)} rows.")
print("Calculating astrological features (this may take a moment)...")
# Apply calculation (Pandas apply can be slow, but for 45k rows it's acceptable ~10-20s)
astro_df = df.apply(calculate_astro_features, axis=1)
# Merge back
full_df = pd.concat([df, astro_df], axis=1)
print(f"Saving to {OUTPUT_FILE}...")
full_df.to_csv(OUTPUT_FILE, index=False)
print("Done.")
if __name__ == "__main__":
main()