from datetime import datetime, timedelta

# Function to extract actuals for a given lag and cutoff date
def extract_actuals_for_lag(df, lag, cutoff_date):
    start_date = cutoff_date - timedelta(weeks=lag+4)
    end_date = cutoff_date - timedelta(weeks=lag)
    mask = (df['ds'] >= pl.lit(start_date)) & (df['ds'] < pl.lit(end_date))
    mask_df = df.filter(mask)  # Use df.filter() instead of boolean indexing
    return mask_df.select(['unique_id', 'y']).groupby('unique_id').agg([
        pl.col('y').list().alias('actuals')
    ])

# Function to calculate group accuracy and bias for a given lag
def calculate_lag_accuracy_bias(actuals, forecasts, cutoff_date):
    lag_accuracy = {}
    lag_bias = {}

    forecasts_filtered = forecasts[(forecasts['WeekDate'] >= cutoff_date) & (forecasts['WeekDate'] < cutoff_date + timedelta(weeks=12))]
    forecasts_filtered = forecasts_filtered.with_columns([
        pl.concat_str([
            pl.col('MaterialID').cast(pl.Utf8),
            "_",
            pl.col('SalesOrg').cast(pl.Utf8),
            "_",
            pl.col('DistrChan').cast(pl.Utf8),
            "_",
            pl.col('CL4').cast(pl.Utf8)
        ]).alias('unique_id')
    ])

    for record in actuals.to_dicts():
        key = record['unique_id']
        actual_values = record['actuals']
        forecasts = forecasts_filtered[forecasts_filtered['unique_id'] == key, 'AutoARIMA'].to_lists()[0]
        abs_errors = [abs(forecast - actual) for forecast, actual in zip(forecasts, actual_values)]
        sum_abs_errors = sum(abs_errors)
        sum_actuals = sum(actual_values)
        sum_forecasts = sum(forecasts)
        if sum_actuals > 0:
            lag_accuracy[key] = 1 - (sum_abs_errors / sum_actuals)
            lag_bias[key] = (sum_forecasts / sum_actuals) - 1
        else:
            lag_accuracy[key] = 0  # Set accuracy to 0 if sum_actuals is 0
            lag_bias[key] = 0  # Set bias to 0 if sum_actuals is 0

    return lag_accuracy, lag_bias

# Set the cutoff date for lag 0 (e.g., first week of 2023)
cutoff_date = datetime(2023, 1, 2)

# Calculate accuracy and bias for each lag
lag_results = {}
for lag in range(12):
    actuals = extract_actuals_for_lag(crossvalidation_df, lag, cutoff_date)
    lag_accuracy, lag_bias = calculate_lag_accuracy_bias(actuals, forecasts_df, cutoff_date)
    lag_results[f'lag{lag}'] = {'accuracy': lag_accuracy, 'bias': lag_bias}
    cutoff_date += timedelta(weeks=1)

# Print the results along with the 12-week out forecasts
for lag, result in lag_results.items():
    print(f"{lag}:")
    for key, accuracy in result['accuracy'].items():
        forecasts = forecasts_df[forecasts_df['unique_id'] == key, 'AutoARIMA'].to_lists()[0]
        print(f"  {key}: Accuracy={accuracy:.4f}, Bias={result['bias'][key]:.4f}, 12-week out forecasts={forecasts}") ---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/Users/tungnguyen/Downloads/Nestle/Nestle CL4 Low Volume ARIMAX Ensemble.ipynb Cell 26 line 5
     52 lag_results = {}
     53 for lag in range(12):
---> 54     actuals = extract_actuals_for_lag(crossvalidation_df, lag, cutoff_date)
     55     lag_accuracy, lag_bias = calculate_lag_accuracy_bias(actuals, forecasts_df, cutoff_date)
     56     lag_results[f'lag{lag}'] = {'accuracy': lag_accuracy, 'bias': lag_bias}

/Users/tungnguyen/Downloads/Nestle/Nestle CL4 Low Volume ARIMAX Ensemble.ipynb Cell 26 line 1
      7 mask = (df['ds'] >= pl.lit(start_date)) & (df['ds'] < pl.lit(end_date))
      8 mask_df = df.filter(mask)  # Use df.filter() instead of boolean indexing
      9 return mask_df.select(['unique_id', 'y']).groupby('unique_id').agg([
---> 10     pl.col('y').list().alias('actuals')
     11 ])

TypeError: 'ExprListNameSpace' object is not callable. My model already has output for cross validation with accuracy and bias and forecast. I wwanted to include a lag in here with lag accuracy and bias and 12 weeks out forecast from cut off week. basically, we just need to pull the ouputs from my model in order to do this. is there an easier way to do it than his block of code. I used polars on my model