code: def _bottom_hier_datagen(
      no_levels=3,
      no_bottom_nodes=6,
      intercept_max=20,
      coef_1_max=20,
      coef_2_max=0.1,
      random_seed=None,
      length=144,
  ):
      """Hierarchical data generator using the airline dataset.
  
      This function generates bottom level, i.e. not aggregated, time-series
      from the airline dataset (sktime.datasets.load_airline).
  
      Each series is generated from the airline dataset using a linear model,
      y = c0 + c1x + c2x^(c3), where the coefficients, intercept, and exponent
      are randomly sampled for each series. The coefficients and intercept are
      sampled between np.arange(0, *_max, 0.01) to keep the values positive. The
      exponent is sampled from [0.5, 1, 1.5, 2].
  
      Parameters
      ----------
      no_levels : int, optional
          The number of levels not considering the time-index, by default 3
      no_bottom_nodes : int, optional
         Number of time series, i.e. bottom nodes, to generate, by default 6.
      *_max : int, optional
          Maximum possible value of the coefficient or intercept value.
      random_seed : int, optional
          Random seed for reproducibility.
      length : int between 1 and 144, optional, default = 144
          length of base time series. If lowe than 144,
          the airline dataset is truncated to the specified length, cutting from the end.
  
      Returns
      -------
      pd.DataFrame with multiindex
      """
      if no_levels > no_bottom_nodes:
          raise ValueError(
              "no_levels should be less than no_bottom_nodes"
          )
  
      rng = np.random.default_rng(random_seed)
  
      base_ts = load_airline()[:length]
      df = pd.DataFrame(base_ts, index=base_ts.index)
      df.index.rename(None, inplace=True)
  
      if no_levels == 0:
          df.columns = ["passengers"]
          df.index.rename("timepoints", inplace=True)
          return df
      else:
          df.columns = ["l1_node01"]
  
          intercept = np.arange(0, intercept_max, 0.01)
          coef_1 = np.arange(0, coef_1_max, 0.01)
          coef_2 = np.arange(0, coef_2_max, 0.01)
          power_2 = [0.5, 1, 1.5, 2]
  
          # create structure of hierarchy
          node_lookup = pd.DataFrame(
              [
                  "l1_node" + f"{x:02d}"
                  for x in range(1, no_bottom_nodes + 1)
              ]
          )
          node_lookup.columns = ["l1_agg"]
  
          if no_levels >= 2:
              # create index from bottom up, sampling node names
              for i in range(2, no_levels + 1):
                  node_lookup[
                      "l" + str(i) + "_agg"
                  ] = node_lookup.groupby(["l" + str(i - 1) + "_agg"])[
                      "l1_agg"
                  ].transform(
                      lambda x: "l"
                      + str(i)  # noqa B023
                      + "_node"
                      + "{:02d}".format(
                          _sample_node(
                              node_lookup.index, i, rng
                          )  # noqa B023
                      )
                  )
  
          node_lookup = node_lookup.set_index("l1_agg", drop=True)
  
          # now define the series for each level by sampling coefficients etc.
          for i in range(2, no_bottom_nodes + 1):
              df["l1_node" + f"{i:02d}"] = (
                  rng.choice(intercept, size=1)
                  + rng.choice(coef_1, size=1) * df["l1_node01"]
                  + (
                      rng.choice(coef_2, size=1)
                      * (df["l1_node01"] ** rng.choice(power_2, size=1))
                  )
              )
  
          df = (
              df.melt(ignore_index=False)
              .reset_index(drop=False)
              .rename(
                  columns={
                      "variable": "l1_agg",
                      "index": "timepoints",
                      "value": "passengers",
                  }
              )
          )
  
          df = pd.merge(
              left=df, right=node_lookup.reset_index(), on="l1_agg"
          )
          df = df[df.columns.sort_values(ascending=True)]
  
          df_newindex = [
              "l" + str(x) + "_agg" for x in range(1, no_levels + 1)
          ][::-1]
          df_newindex.append("timepoints")
  
          df = df.set_index(df_newindex)
          df.sort_index(inplace=True)
  
          return df
  
file_path: sktime/sktime/utils/_testing/hierarchical.py
imports: from itertools import product
  from typing import Tuple, Union
  import numpy
  import pandas
  from sklearn.utils import check_random_state
  from sktime.datasets import load_airline
  from sktime.utils._testing.series import _make_index
label_desc: Use image node_id 2 for calling a global function with example usage: _bottom_hier_datagen(no_levels, no_bottom_nodes, intercept_max, coef_1_max, coef_2_max, random_seed, length) and returns: df, df