code: def split_df(
      df: pd.DataFrame,
      n_lags: int,
      n_forecasts: int,
      valid_p: float = 0.2,
      inputs_overbleed: bool = True,
      local_split: bool = False,
  ):
      """Splits timeseries df into train and validation sets.
  
      Prevents overbleed of targets. Overbleed of inputs can be configured.
      In case of global modeling the split could be either local or global.
  
      Parameters
      ----------
          df : pd.DataFrame
              dataframe containing column ``ds``, ``y``, and optionally``ID`` with all data
          n_lags : int
              identical to NeuralProphet
          n_forecasts : int
              identical to NeuralProphet
          valid_p : float, int
              fraction (0,1) of data to use for holdout validation set, or number of validation samples >1
          inputs_overbleed : bool
              Whether to allow last training targets to be first validation inputs (never targets)
          local_split : bool
              when set to true, each episode from a dict of dataframes will be split locally
  
      Returns
      -------
          pd.DataFrame, dict
              training data
          pd.DataFrame, dict
              validation data
      """
      df, _, _, _ = prep_or_copy_df(df)
      df_train = pd.DataFrame()
      df_val = pd.DataFrame()
      if local_split:
          n_samples = df.groupby("ID").size()
          n_samples = n_samples - n_lags + 2 - (2 * n_forecasts)
          n_samples = (
              n_samples if inputs_overbleed else n_samples - n_lags
          )
  
          if 0.0 < valid_p < 1.0:
              n_valid = n_samples.apply(
                  lambda x: max(1, int(x * valid_p))
              )
          else:
              assert valid_p >= 1
              assert isinstance(valid_p, int)
              n_valid = valid_p
          n_train = n_samples - n_valid
  
          log.debug(f"{n_train} n_train, {n_samples - n_train} n_eval")
  
      else:
          # Split data according to time threshold defined by the valid_p
          threshold_time_stamp = find_time_threshold(
              df, n_lags, n_forecasts, valid_p, inputs_overbleed
          )
          n_train = (
              df["ds"]
              .groupby(df["ID"])
              .apply(lambda x: x[x < threshold_time_stamp].count())
          )
  
      assert n_train.min() > 1
      split_idx_train = n_train + n_lags + n_forecasts - 1
      split_idx_val = (
          split_idx_train - n_lags
          if inputs_overbleed
          else split_idx_train
      )
      df_train = df.groupby("ID", group_keys=False).apply(
          lambda x: x.iloc[: split_idx_train[x.name]]
      )
      df_val = df.groupby("ID", group_keys=False).apply(
          lambda x: x.iloc[split_idx_val[x.name] :]
      )
  
      return df_train, df_val
  
imports: from __future__ import annotations
  import logging
  import math
  from collections import OrderedDict
  from dataclasses import dataclass
  from typing import TYPE_CHECKING, List, Optional, Tuple
  import numpy
  import pandas
label_desc: Use image node_id 19 for calling a global function with example usage: split_df(df, n_lags, n_forecasts, valid_p, inputs_overbleed, local_split) and returns: df_train,  df_val