code: def spatial_learning_curve_splitter(
      train_data: pd.DataFrame,
      space_column: str,
      time_column: str,
      training_limit: DateType,
      holdout_gap: timedelta = timedelta(days=0),
      train_percentages: Iterable[float] = (0.25, 0.5, 0.75, 1.0),
      random_state: int = None,
  ) -> SplitterReturnType:
      """
      Splits the data for a spatial learning curve. Progressively adds more and
      more examples to the training in order to verify the impact of having more
      data available on a validation set.
  
      The validation set starts after the training set, with an optional time gap.
  
      Similar to the temporal learning curves, but with spatial increases in the training set.
  
      Parameters
      ----------
  
      train_data : pandas.DataFrame
          A Pandas' DataFrame that will be split for learning curve estimation.
  
      space_column : str
          The name of the ID column of `train_data`.
  
      time_column : str
          The name of the temporal column of `train_data`.
  
      training_limit: datetime or str
          The date limiting the training (after which the holdout begins).
  
      holdout_gap: timedelta
          The gap between the end of training and the start of the holdout.
          If you have censored data, use a gap similar to the censor time.
  
      train_percentages: list or tuple of floats
          A list containing the percentages of IDs to use in the training.
          Defaults to (0.25, 0.5, 0.75, 1.0). For example: For the default value,
          there would be four model trainings, containing respectively 25%, 50%,
          75%, and 100% of the IDs that are not part of the held out set.
  
      random_state : int
          A seed for the random number generator that shuffles the IDs.
      """
      if (
          np.min(np.array(train_percentages)) < 0
          or np.max(np.array(train_percentages)) > 1
      ):
          raise ValueError("Train percentages must be between 0 and 1")
  
      if isinstance(training_limit, str):
          training_limit = datetime.strptime(training_limit, "%Y-%m-%d")
  
      if (
          training_limit < train_data[time_column].min()
          or training_limit > train_data[time_column].max()
      ):
          raise ValueError(
              "Temporal training limit should be within datasets temporal bounds (min and max times)"
          )
      if timedelta(days=0) > holdout_gap:
          raise ValueError("Holdout gap cannot be negative")
      if holdout_gap >= (
          train_data[time_column].max() - training_limit
      ):
          raise ValueError(
              "After taking the gap into account, there should be enough time for the holdout set"
          )
  
      train_data = train_data.reset_index()
  
      # We need to sample the space column before getting its unique values so their order in the DF won't matter here
      spatial_ids = (
          train_data[space_column]
          .sample(frac=1, random_state=random_state)
          .unique()
      )
  
      cumulative_ids = pipe(
          spatial_ids,
          lambda ids: (np.array(train_percentages) * len(ids)).astype(
              int
          ),  # Get the corresponding indices for each %
          lambda idx: np.split(spatial_ids, idx)[
              :-1
          ],  # Split spatial ids by the indices
          lambda to_list: map(
              lambda x: x.tolist(), to_list
          ),  # Transform sub-arrays into sub-lists
          lambda drop_empty: filter(
              None, drop_empty
          ),  # Drop empty sub-lists
          accumulate(operator.add),  # Cumulative sum of lists
      )
  
      validation_set = train_data[
          train_data[time_column] > (training_limit + holdout_gap)
      ]
      train_data = train_data[train_data[time_column] <= training_limit]
  
      folds = [
          (
              train_data[train_data[space_column].isin(ids)][
                  time_column
              ],
              validation_set[time_column],
          )
          for ids in cumulative_ids
      ]
  
      folds_indices = _lc_fold_to_indexes(
          folds
      )  # final formatting with idx
  
      logs = [
          assoc(learner, "percentage", p)
          for learner, p in zip(
              map(_log_time_fold, folds), train_percentages
          )
      ]
  
      return folds_indices, logs
  
imports: import operator
  from datetime import datetime, timedelta
  from itertools import chain, repeat, starmap
  from typing import Callable, Iterable, List, Tuple, Union
  import numpy
  import pandas
  from sklearn.model_selection import GroupKFold, KFold, StratifiedKFold
  from sklearn.utils import check_random_state
  from toolz.curried import curry, partial, pipe, assoc, accumulate, map, filter
  from fklearn.common_docstrings import splitter_return_docstring
  from fklearn.types import DateType, LogType, SplitterReturnType
label_desc: Use image node_id 11 for calling a global function with example usage: spatial_learning_curve_splitter(train_data, space_column, time_column, training_limit, holdout_gap, train_percentages, random_state) and returns: folds_indices,  logs