internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: ourownstory/neural_prophet

Issue:
add_country_holidays does replace prior holiday values in contrast to its name
Calling `add_country_holidays` does override any previously defined holidays. Meaning repeated calls of `add_country_holidays` with different countries do only result in the holidays of the final call (see example below).

Looking up the implementation/documentation of prophet their function behaves the same day, so either we should
1) add the same hint in documentation (see [reference](https://github.com/facebook/prophet/blob/10310ceb2da05837a198db6714d658a1e0a32478/python/prophet/forecaster.py#L714)), possibly throw an exception/warning in case people call it multiple times
2) differ from the behavior of prophet and actually allow for multiple countries
3) renaming the function to `set_country_holidays` to clarify its behavior.

Would be curious to hear if only I got confused and which direction forward is best.

Example from an integration test with multiple `add_country_holidays` calls: https://github.com/ourownstory/neural_prophet/blob/main/tests/test_integration.py#L429-L434


Discussion:
Adjust the documentation as discussed

Resolving patch:
diff --git a/neuralprophet/forecaster.py b/neuralprophet/forecaster.py
--- a/neuralprophet/forecaster.py
+++ b/neuralprophet/forecaster.py
@@ -537,6 +537,9 @@ def add_country_holidays(self, country_name, lower_window=0, upper_window=0, reg
         and create the corresponding configs such as lower, upper windows and the regularization
         parameters
 
+        Holidays can only be added for a single country. Calling the function
+        multiple times will override already added country holidays.
+
         Parameters
         ----------
             country_name : string
@@ -552,6 +555,10 @@ def add_country_holidays(self, country_name, lower_window=0, upper_window=0, reg
         """
         if self.fitted:
             raise Exception("Country must be specified prior to model fitting.")
+        if self.country_holidays_config:
+            log.warning(
+                "Country holidays can only be added for a single country. Previous country holidays were overridden."
+            )
 
         if regularization is not None:
             if regularization < 0:


Test patch:
diff --git a/tests/test_unit.py b/tests/test_unit.py
--- a/tests/test_unit.py
+++ b/tests/test_unit.py
@@ -896,3 +896,16 @@ def install(package):
     metadata_version_ = metadata.version("neuralprophet")
     assert metadata_version_ == init_version
     assert metadata_version_ == file_version
+
+
+def test_add_country_holiday_multiple_calls_warning(caplog):
+    error_message = (
+        "Country holidays can only be added for a single country. Previous country holidays were overridden."
+    )
+
+    m = NeuralProphet()
+    m.add_country_holidays("US")
+    assert error_message not in caplog.text
+
+    m.add_country_holidays("Germany")
+    assert error_message in caplog.text
Repository: ourownstory/neural_prophet

Issue:
Introduce automated black checks via Github Actions
As I encountered formatting issues across several files during cleanup, I'd propose we add a Github Action to ensure all pull requests comply with black.


Discussion:

Resolving patch:
diff --git a/neuralprophet/forecaster.py b/neuralprophet/forecaster.py
--- a/neuralprophet/forecaster.py
+++ b/neuralprophet/forecaster.py
@@ -2567,9 +2567,9 @@ def _make_future_dataframe(self, df, events_df, regressors_df, periods, n_histor
 
         if len(df) < self.max_lags:
             raise ValueError(
-                    "Insufficient input data for a prediction." 
-                    "Please supply historic observations (number of rows) of at least max_lags (max of number of n_lags)."
-                )
+                "Insufficient input data for a prediction."
+                "Please supply historic observations (number of rows) of at least max_lags (max of number of n_lags)."
+            )
         elif len(df) < self.max_lags + n_historic_predictions:
             log.warning(
                 f"Insufficient data for {n_historic_predictions} historic forecasts, reduced to {len(df) - self.max_lags}."
@@ -2687,7 +2687,7 @@ def _prepare_dataframe_to_predict(self, df):
             # Checks
             if len(df_i) == 0 or len(df_i) < self.max_lags:
                 raise ValueError(
-                    "Insufficient input data for a prediction." 
+                    "Insufficient input data for a prediction."
                     "Please supply historic observations (number of rows) of at least max_lags (max of number of n_lags)."
                 )
             if len(df_i.columns) == 1 and "ds" in df_i:


Test patch:
diff --git a/tests/test_regularization.py b/tests/test_regularization.py
--- a/tests/test_regularization.py
+++ b/tests/test_regularization.py
@@ -158,6 +158,6 @@ def test_regularization_lagged_regressor():
         if lagged_regressor_weight > 0.9:
             assert weight_average > 0.5
         else:
-            assert weight_average < 0.35 # Note: this should be < 0.1, but due to fitting issues, relaxed temporarily.
+            assert weight_average < 0.35  # Note: this should be < 0.1, but due to fitting issues, relaxed temporarily.
 
         print(name, weight_average, lagged_regressors_config[name])
Repository: ourownstory/neural_prophet

Issue:
Automatic deployment of docs with Github Action
> and if we can have different doc pages for different tags, we could automate the deployment via a github action, which would be ideal.



Discussion:

Resolving patch:
diff --git a/docs/source/conf.py b/docs/source/conf.py
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -82,7 +82,7 @@
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ["_static"]
+html_static_path = ["_static", "images/np_highres.svg"]
 
 # html_sidebars = { '**': [
 #     "_templates/sidebar/brand.html",
diff --git a/neuralprophet/df_utils.py b/neuralprophet/df_utils.py
--- a/neuralprophet/df_utils.py
+++ b/neuralprophet/df_utils.py
@@ -1415,6 +1415,7 @@ def handle_negative_values(df, col, handle_negatives):
 
 def drop_missing_from_df(df, drop_missing, predict_steps, n_lags):
     """Drops windows of missing values in df according to the (lagged) samples that are dropped from TimeDataset.
+
     Parameters
     ----------
         df : pd.DataFrame
@@ -1425,6 +1426,7 @@ def drop_missing_from_df(df, drop_missing, predict_steps, n_lags):
             identical to NeuralProphet
         n_lags : int
             identical to NeuralProphet
+
     Returns
     -------
         pd.DataFrame
@@ -1456,6 +1458,7 @@ def drop_missing_from_df(df, drop_missing, predict_steps, n_lags):
 
 def join_dfs_after_data_drop(predicted, df, merge=False):
     """Creates the intersection between df and predicted, removing any dates that have been imputed and dropped in NeuralProphet.predict().
+
     Parameters
     ----------
         df : pd.DataFrame
@@ -1467,6 +1470,7 @@ def join_dfs_after_data_drop(predicted, df, merge=False):
             Options
             * (default) ``False``: Returns separate dataframes
             * ``True``: Merges predicted and df into one dataframe
+
     Returns
     -------
         pd.DataFrame


Test patch:
Repository: ourownstory/neural_prophet

Issue:
[UI] add plot_last alias for renamed function (backwards compatiblitity)



Discussion:

Resolving patch:
diff --git a/neuralprophet/forecaster.py b/neuralprophet/forecaster.py
--- a/neuralprophet/forecaster.py
+++ b/neuralprophet/forecaster.py
@@ -1716,6 +1716,25 @@ def plot_latest_forecast(
                 line_per_origin=True,
             )
 
+    def plot_last_forecast(
+        self,
+        fcst,
+        df_name=None,
+        ax=None,
+        xlabel="ds",
+        ylabel="y",
+        figsize=(10, 6),
+        include_previous_forecasts=0,
+        plot_history_data=None,
+        plotting_backend="default",
+    ):
+        args = locals()
+        log.warning(
+            "plot_last_forecast() has been renamed to plot_latest_forecast() and is therefore deprecated. "
+            "Please use plot_latst_forecast() in the future"
+        )
+        return NeuralProphet.plot_latest_forecast(**args)
+
     def plot_components(
         self, fcst, df_name="__df__", figsize=None, forecast_in_focus=None, residuals=False, plotting_backend="default"
     ):


Test patch:
diff --git a/tests/test_integration.py b/tests/test_integration.py
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -485,6 +485,7 @@ def test_plot():
     forecast = m.predict(future)
     m.plot(forecast)
     m.plot_latest_forecast(forecast, include_previous_forecasts=10)
+    m.plot_last_forecast(fcst=forecast, include_previous_forecasts=10)  # test deprecated alias
     m.plot_components(forecast)
     m.plot_parameters()
     log.info("testing: Plotting with quants")
Repository: ourownstory/neural_prophet

Issue:
Plot labels of yhatx/origin-x are reversed for plotly  
*If yhats are plotted: yhat1 is the fcst predicted 1 step ago, being the most accurate --> should be darkest blue in plot

*If origin-x are plotted (plot_latest_forecast): origin-0 is the very last forecast --> should be darkest blue in plot 


Discussion:

Resolving patch:
diff --git a/neuralprophet/plot_forecast.py b/neuralprophet/plot_forecast.py
--- a/neuralprophet/plot_forecast.py
+++ b/neuralprophet/plot_forecast.py
@@ -89,14 +89,14 @@ def plot(
     ]
 
     if highlight_forecast is None or line_per_origin:
-        for i, name in enumerate(reversed(yhat_col_names_no_qts)):
+        for i, name in enumerate(yhat_col_names_no_qts):
             ax.plot(
                 ds,
-                fcst[name],
+                fcst[f"{colname}{i if line_per_origin else i + 1}"],
                 ls="-",
                 c="#0072B2",
                 alpha=0.2 + 2.0 / (i + 2.5),
-                label=f"{colname}{i if line_per_origin else i + 1}",
+                label=name,
             )
 
     if len(quantiles) > 1:
diff --git a/neuralprophet/plot_forecast_plotly.py b/neuralprophet/plot_forecast_plotly.py
--- a/neuralprophet/plot_forecast_plotly.py
+++ b/neuralprophet/plot_forecast_plotly.py
@@ -86,7 +86,7 @@ def plot(fcst, quantiles, xlabel="ds", ylabel="y", highlight_forecast=None, line
     data = []
 
     if highlight_forecast is None or line_per_origin:
-        for i, yhat_col_name in enumerate(reversed(yhat_col_names_no_qts)):
+        for i, yhat_col_name in enumerate(yhat_col_names_no_qts):
             data.append(
                 go.Scatter(
                     name=yhat_col_name,


Test patch:
Repository: ourownstory/neural_prophet

Issue:
[bug] Progress bar is given out for every batch if minimal=True, causing a significant slow down
Setting minimal=True currently causes a significant slow down of the training process, as the progress bar is given out for every batch. 


Discussion:

Resolving patch:
diff --git a/neuralprophet/utils.py b/neuralprophet/utils.py
--- a/neuralprophet/utils.py
+++ b/neuralprophet/utils.py
@@ -827,6 +827,8 @@ def configure_trainer(
     if progress_bar_enabled:
         prog_bar_callback = ProgressBar(refresh_rate=num_batches_per_epoch, epochs=config_train.epochs)
         callbacks.append(prog_bar_callback)
+    else:
+        config["enable_progress_bar"] = False
 
     # Early stopping monitor
     if early_stopping:


Test patch:
Repository: ourownstory/neural_prophet

Issue:
[bug] test_plot_conformal_prediction fails with auto-regression enabled on plot_latest_forecast
Here is the code snippet from `test_plot_conformal_prediction` in the test_plotting.py file:

```
    # With auto-regression enabled
    # TO-DO: Fix Assertion error n_train >= 1
    m = NeuralProphet(
        n_forecasts=7,
        n_lags=14,
        quantiles=[0.05, 0.95],
        epochs=EPOCHS,
        batch_size=BATCH_SIZE,
        learning_rate=LR,
    )
    train_df, test_df = m.split_df(df, freq="D", valid_p=0.2)
    train_df, cal_df = m.split_df(train_df, freq="D", valid_p=0.15)
    metrics_df = m.fit(train_df, freq="D")
    alpha = 0.1
    for method in ["naive", "cqr"]:  # Naive and CQR SCP methods
        future = m.make_future_dataframe(df, periods=m.n_forecasts, n_historic_predictions=10)
        forecast = m.conformal_predict(future, calibration_df=cal_df, alpha=alpha, method=method)
        m.highlight_nth_step_ahead_of_each_forecast(m.n_forecasts)
        fig0 = m.plot(forecast)
        fig1 = m.plot_latest_forecast(forecast, include_previous_forecasts=10, plotting_backend="matplotlib")
        fig2 = m.plot_latest_forecast(forecast, include_previous_forecasts=10, plot_history_data=True, plotting_backend="matplotlib")
        fig3 = m.plot_latest_forecast(forecast, include_previous_forecasts=10, plot_history_data=False, plotting_backend="matplotlib")
        fig4 = m.plot_components(forecast, plotting_backend="matplotlib")
        fig5 = m.plot_parameters(plotting_backend="matplotlib")
        if PLOT:
            fig0.show()
            fig1.show()
            fig2.show()
            fig3.show()
            fig4.show()
            fig5.show()
```
Running the test gets this gets the error:

```
FAILED tests/test_plotting.py::test_plot_conformal_prediction[matplotlib] - IndexError: index 5 is out of bounds for axis 1 with size 5
FAILED tests/test_plotting.py::test_plot_conformal_prediction[plotly] - IndexError: index 5 is out of bounds for axis 1 with size 5
```

This is due to the `m.plot_latest_forecast()` method, where somewhere in the utils.py or df_utils.py file, the conformal predict column (e.g., `yhat1 5.0% + qhat1`) is not correctly mapped.


Discussion:

Resolving patch:
diff --git a/neuralprophet/utils.py b/neuralprophet/utils.py
--- a/neuralprophet/utils.py
+++ b/neuralprophet/utils.py
@@ -586,7 +586,9 @@ def fcst_df_to_latest_forecast(fcst, quantiles, n_last=1):
     df = pd.concat((fcst[cols],), axis=1)
     df.reset_index(drop=True, inplace=True)
 
-    yhat_col_names = [col_name for col_name in fcst.columns if "yhat" in col_name and "%" not in col_name]
+    yhat_col_names = [
+        col_name for col_name in fcst.columns if "yhat" in col_name and "%" not in col_name and "qhat" not in col_name
+    ]
     yhat_col_names_quants = [col_name for col_name in fcst.columns if "yhat" in col_name and "%" in col_name]
     n_forecast_steps = len(yhat_col_names)
     yhats = pd.concat((fcst[yhat_col_names],), axis=1)


Test patch:
Repository: ourownstory/neural_prophet

Issue:
[bug] Adding country holidays triggers Error when using global/glocal model 
Adding country holidays with `m.add_country_holidays()` triggers an index error when the input df contains multiple time series (i.e. when glocal/global modeling is used). Resetting the index of the incoming time series in `TimeDataset.make_events_features()` resolves the problem.


Discussion:

Resolving patch:
diff --git a/neuralprophet/time_dataset.py b/neuralprophet/time_dataset.py
--- a/neuralprophet/time_dataset.py
+++ b/neuralprophet/time_dataset.py
@@ -508,7 +508,7 @@ def make_events_features(df, config_events: Optional[configure.ConfigEvents] = N
         np.array
             All multiplicative event features (both user specified and country specific)
     """
-
+    df = df.reset_index(drop=True)
     additive_events = pd.DataFrame()
     multiplicative_events = pd.DataFrame()
 


Test patch:
Repository: ourownstory/neural_prophet

Issue:
Global-local warning logged every training batch
The log in denormalize() in time_net.py is logged every batch. When training on a large dataset, this results in thousands of logs.

Could we simply remove it @alfonsogarciadecorral 

Line 1293 in `time_net.py`
```python
if not self.config_normalization.global_normalization:
     log.warning("When Global modeling with local normalization, metrics are displayed in normalized scale.")
else:
     ...
```


Discussion:
Hey Karl!

I didn't create that bit of code, so maybe not the best person to answer :( 

Resolving patch:
diff --git a/neuralprophet/forecaster.py b/neuralprophet/forecaster.py
--- a/neuralprophet/forecaster.py
+++ b/neuralprophet/forecaster.py
@@ -759,6 +759,9 @@ def fit(
             log.warning("Progress plot requires metrics to be enabled. Enabling the default metrics.")
             metrics = metrics.get_metrics(True)
 
+        if not self.config_normalization.global_normalization:
+            log.warning("When Global modeling with local normalization, metrics are displayed in normalized scale.")
+
         if minimal:
             checkpointing = False
             self.metrics = False
diff --git a/neuralprophet/time_net.py b/neuralprophet/time_net.py
--- a/neuralprophet/time_net.py
+++ b/neuralprophet/time_net.py
@@ -1290,9 +1290,7 @@ def denormalize(self, ts):
         -------
             denormalized timeseries
         """
-        if not self.config_normalization.global_normalization:
-            log.warning("When Global modeling with local normalization, metrics are displayed in normalized scale.")
-        else:
+        if self.config_normalization.global_normalization:
             shift_y = (
                 self.config_normalization.global_data_params["y"].shift
                 if self.config_normalization.global_normalization and not self.config_normalization.normalize == "off"


Test patch:
Repository: ourownstory/neural_prophet

Issue:
add model.get_params and set_params 
similar to scikit model.get_params

for scenario testing or for parameter-based save / load functions


Discussion:
see discussion #238
Hello @ourownstory, Can you assign this issue to me?
Hi @ourownstory, @anastasiia-tovstolis, with our ongoing migration to Pytorch Lightning (and I think also in plain pytorch) you can access the model parameters using `m.model.state_dict()`. This yields the trained parameters such as bias, trend etc. Further you can use `load_state_dict()` to set the parameters of the model. Not sure if this is exactly what you are looking for, but it seems very related.

The output looks roughly as follows
```python
{
    'bias': tensor([0.7926])
    'trend_k0': tensor([-0.3043])
    'trend_deltas': tensor([[1.6712, 0.7629, 1.5933, 0.4826, 1.2354, 1.0626, 1.3238, 1.1458, 1.6141, 0.1156, 1.1982]])
    'ar_net.0.weight': tensor([[1.8130, 1.6902, 0.5619], [0.3293, 0.8004, 0.9695]])
    'covar_nets.A.0.weight': tensor([[0.7283, 1.5743, 0.9238]
}
```
Hi @ourownstory, I would like to clarify if this issue is still relevant in connection with the comment @karl-richter?

Resolving patch:
diff --git a/neuralprophet/forecaster.py b/neuralprophet/forecaster.py
--- a/neuralprophet/forecaster.py
+++ b/neuralprophet/forecaster.py
@@ -340,6 +340,9 @@ def __init__(
         accelerator: Optional[str] = None,
         trainer_config: dict = {},
     ):
+        self.config = locals()
+        self.config.pop("self")
+
         # General
         self.name = "NeuralProphet"
         self.n_forecasts = n_forecasts
@@ -517,6 +520,23 @@ def add_lagged_regressor(
             )
         return self
 
+    def parameters(self):
+        return self.config
+
+    def state_dict(self):
+        return {
+            "data_freq": self.data_freq,
+            "fitted": self.fitted,
+            "data_params": self.data_params,
+            "optimizer": self.config_train.optimizer,
+            "scheduler": self.config_train.scheduler,
+            "model": self.model,
+            "future_periods": self.future_periods,
+            "predict_steps": self.predict_steps,
+            "highlight_forecast_step_n": self.highlight_forecast_step_n,
+            "true_ar_weights": self.true_ar_weights,
+        }
+
     def add_future_regressor(self, name, regularization=None, normalize="auto", mode="additive"):
         """Add a regressor as lagged covariate with order 1 (scalar) or as known in advance (also scalar).
 


Test patch:
Repository: ourownstory/neural_prophet

Issue:
Exception handling for too small dataset to detect learning rate is missleading
In case the dataset is too small to detect a learning rate the following warning is show:

`ERROR - (NP.utils.smooth_loss_and_suggest) - The number of loss values (1) is too small to estimate a learning rate. Increase the number of samples or manually set the learning rate.`

Yet the python exception thrown is very misleading with:

`UnboundLocalError: local variable 'suggestion' referenced before assignment`

Solution:

We should raise a `ValueError` or some other more suitable error message directly in `neuralprophet/utils.py:713` (inside `smooth_loss_and_suggest`) and make sure to not trigger an `UnboundLocalError`


Discussion:

Resolving patch:
diff --git a/neuralprophet/utils.py b/neuralprophet/utils.py
--- a/neuralprophet/utils.py
+++ b/neuralprophet/utils.py
@@ -730,6 +730,7 @@ def smooth_loss_and_suggest(lr_finder_results, window=10):
         log.error(
             f"The number of loss values ({len(loss)}) is too small to estimate a learning rate. Increase the number of samples or manually set the learning rate."
         )
+        raise
     return (loss, lr, suggestion)
 
 


Test patch:
Repository: ourownstory/neural_prophet

Issue:
Unclear error when doing auto-regression with too small dataset
### Discussed in https://github.com/ourownstory/neural_prophet/discussions/1257

<div type='discussions-op-text'>

<sup>Originally posted by **pzaika** April 11, 2023</sup>
When using auto-regression with a dataset that is smaller than the requested number of forecasts + lags (specifically when n_samples = len(df) - max_lags + 1 - n_forecasts is not positive) the fit method throws an IndexError. This hides the real problem, which is that the user is trying to train a model on not enough data. I think that it would be better to raise an error which tells the user to lower the number of forecasts or lags they are asking for.</div>


Discussion:

Resolving patch:
diff --git a/neuralprophet/data/process.py b/neuralprophet/data/process.py
--- a/neuralprophet/data/process.py
+++ b/neuralprophet/data/process.py
@@ -397,6 +397,11 @@ def _check_dataframe(
         pd.DataFrame
             checked dataframe
     """
+    if len(df) < (model.n_forecasts + model.n_lags) and not future:
+        raise ValueError(
+            "Dataframe has less than n_forecasts + n_lags rows. "
+            "Forecasting not possible. Please either use a larger dataset, or adjust the model parameters."
+        )
     df, _, _, _ = df_utils.prep_or_copy_df(df)
     df, regressors_to_remove, lag_regressors_to_remove = df_utils.check_dataframe(
         df=df,


Test patch:
diff --git a/tests/test_integration.py b/tests/test_integration.py
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -1355,6 +1355,12 @@ def test_get_latest_forecast():
     with pytest.raises(Exception):
         m.get_latest_forecast(forecast, include_previous_forecasts=10)
 
+    log.info("Not enough values in df")
+    df3 = df.iloc[:10].copy(deep=True)
+    df3["ID"] = "df3"
+    with pytest.raises(Exception):
+        metrics_df = m.fit(df3, freq="D")
+
 
 def test_metrics():
     log.info("testing: Plotting")