docs: def compute_route_time_series_base(
      trip_stats_subset: DataFrame,
      date_label: str = "20010101",
      freq: str = "5Min",
      *,
      split_directions: bool = False,
  ) -> DataFrame:
      """
      Compute stats in a 24-hour time series form for the given subset of trips.
  
      Parameters
      ----------
      trip_stats_subset : DataFrame
          A subset of the output of :func:`.trips.compute_trip_stats`
      split_directions : boolean
          If ``True``, then separate each routes's stats by trip direction;
          otherwise aggregate trips in both directions
      freq : Pandas frequency string
          Specifices the frequency with which to resample the time series;
          max frequency is one minute ('Min')
      date_label : string
          YYYYMMDD date string used as the date in the time series index
  
      Returns
      -------
      DataFrame
          A time series version of the following route stats for each
          route.
  
          - ``num_trips``: number of trips in service on the route
            at any time within the time bin
          - ``num_trip_starts``: number of trips that start within
            the time bin
          - ``num_trip_ends``: number of trips that end within the
            time bin, ignoring trips that end past midnight
          - ``service_distance``: sum of the service duration accrued
            during the time bin across all trips on the route;
            measured in hours
          - ``service_distance``: sum of the service distance accrued
            during the time bin across all trips on the route; measured
            in kilometers
          - ``service_speed``: ``service_distance/service_duration``
            for the route
  
          The columns are hierarchical (multi-indexed) with
  
          - top level: name is ``'indicator'``; values are
            ``'num_trip_starts'``, ``'num_trip_ends'``, ``'num_trips'``,
            ``'service_distance'``, ``'service_duration'``, and
            ``'service_speed'``
          - middle level: name is ``'route_id'``;
            values are the active routes
          - bottom level: name is ``'direction_id'``; values are 0s and 1s
  
          If not ``split_directions``, then don't include the bottom level.
  
          The time series has a timestamp index for a 24-hour period
          sampled at the given frequency.
          The maximum allowable frequency is 1 minute.
          If ``trip_stats_subset`` is empty, then return an empty
          DataFrame with the columns ``'num_trip_starts'``,
          ``'num_trip_ends'``, ``'num_trips'``, ``'service_distance'``,
          ``'service_duration'``, and ``'service_speed'``.
  
      Notes
      -----
      - The time series is computed at a one-minute frequency, then
        resampled at the end to the given frequency
      - Trips that lack start or end times are ignored, so the the
        aggregate ``num_trips`` across the day could be less than the
        ``num_trips`` column of :func:`compute_route_stats_base`
      - All trip departure times are taken modulo 24 hours.
        So routes with trips that end past 23:59:59 will have all
        their stats wrap around to the early morning of the time series,
        except for their ``num_trip_ends`` indicator.
        Trip endings past 23:59:59 not binned so that resampling the
        ``num_trips`` indicator works efficiently.
      - Note that the total number of trips for two consecutive time bins
        t1 < t2 is the sum of the number of trips in bin t2 plus the
        number of trip endings in bin t1.
        Thus we can downsample the ``num_trips`` indicator by keeping
        track of only one extra count, ``num_trip_ends``, and can avoid
        recording individual trip IDs.
      - All other indicators are downsampled by summing.
      - Raise a ValueError if ``split_directions`` and no non-NaN
        direction ID values present
  
      """
      if trip_stats_subset.empty:
          return pd.DataFrame()
  
      tss = trip_stats_subset.copy()
      if split_directions:
          tss = tss.loc[lambda x: x.direction_id.notnull()].assign(
              direction_id=lambda x: x.direction_id.astype(int)
          )
          if tss.empty:
              raise ValueError(
                  "At least one trip stats direction ID value "
                  "must be non-NaN."
              )
  
          # Alter route IDs to encode direction:
          # <route ID>-0 and <route ID>-1 or <route ID>-NA
          tss["route_id"] = (
              tss["route_id"]
              + "-"
              + tss["direction_id"].map(lambda x: str(int(x)))
          )
  
      routes = tss["route_id"].unique()
      # Build a dictionary of time series and then merge them all
      # at the end.
      # Assign a uniform generic date for the index
      date_str = date_label
      day_start = pd.to_datetime(date_str + " 00:00:00")
      day_end = pd.to_datetime(date_str + " 23:59:00")
      rng = pd.period_range(day_start, day_end, freq="Min")
      indicators = [
          "num_trip_starts",
          "num_trip_ends",
          "num_trips",
          "service_duration",
          "service_distance",
      ]
  
      bins = [i for i in range(24 * 60)]  # One bin for each minute
      num_bins = len(bins)
  
      # Bin start and end times
      def F(x):
          return (hp.timestr_to_seconds(x) // 60) % (24 * 60)
  
      tss[["start_index", "end_index"]] = tss[
          ["start_time", "end_time"]
      ].applymap(F)
      routes = sorted(set(tss["route_id"].values))
  
      # Bin each trip according to its start and end time and weight
      series_by_route_by_indicator = {
          indicator: {route: [0 for i in range(num_bins)] for route in routes}
          for indicator in indicators
      }
      for index, row in tss.iterrows():
          route = row["route_id"]
          start = row["start_index"]
          end = row["end_index"]
          distance = row["distance"]
  
          if start is None or np.isnan(start) or start == end:
              continue
  
          # Get bins to fill
          if start <= end:
              bins_to_fill = bins[start:end]
          else:
              bins_to_fill = bins[start:] + bins[:end]
  
          # Bin trip
          # Do num trip starts
          series_by_route_by_indicator["num_trip_starts"][route][start] += 1
          # Don't mark trip ends for trips that run past midnight;
          # allows for easy resampling of num_trips later
          if start <= end:
              series_by_route_by_indicator["num_trip_ends"][route][end] += 1
          # Do rest of indicators
          for indicator in indicators[2:]:
              if indicator == "num_trips":
                  weight = 1
              elif indicator == "service_duration":
                  weight = 1 / 60
              else:
                  weight = distance / len(bins_to_fill)
              for bin in bins_to_fill:
                  series_by_route_by_indicator[indicator][route][bin] += weight
  
      # Create one time series per indicator
      rng = pd.date_range(date_str, periods=24 * 60, freq="Min")
      series_by_indicator = {
          indicator: pd.DataFrame(
              series_by_route_by_indicator[indicator], index=rng
          ).fillna(0)
          for indicator in indicators
      }
  
      # Combine all time series into one time series
      g = hp.combine_time_series(
          series_by_indicator, kind="route", split_directions=split_directions
      )
  
      return hp.downsample(g, freq=freq)