anchor.diff:    /// This operation is only supported on ordered series. The method throws
     /// `InvalidOperationException` when the series is not ordered. 
     /// 
  -  /// [category:Lookup, resampling and scaling]
  +  /// [category:Sampling, resampling and advanced lookup]
     let inline sampleTimeAt start interval dir series = sampleTimeAtInto start interval dir id series
   
     /// Finds values at, or near, the specified times in a given series. The operation generates
  
positive.code:   /// [category:Data structure manipulation]
    let sortByKey series =
      series |> orderByKey
    // ----------------------------------------------------------------------------------------------
    // Resampling and similar stuff
    // ----------------------------------------------------------------------------------------------
  
negative.code:       | _              -> raise <| new ArgumentException("Unexpected code path in interpolation")
      series |> interpolate keys linearF   
  
    // ----------------------------------------------------------------------------------------------
    // Hierarchical index operations
    // ----------------------------------------------------------------------------------------------
  
    /// [omit]
    /// Applies `fastAggregation` to each group produced using the specified `keySelector`
    [<CompilerMessageAttribute("This is an internal function and should not be used directly", 10002, IsHidden=true)>]
    [<CompiledName("InternalApplyLevel")>]
    let inline fastApplyLevel keySelector flist foptlist fseq (series:Series<_, _>) : Series<_, _> = 
      series.GroupBy(fun kvp -> keySelector kvp.Key)
      |> mapValues (fastAggregation flist foptlist fseq)
  
    /// Groups the elements of the input series in groups based on the keys
    /// produced by `level` and then aggregates elements in each group
    /// using the specified function `op`. The result is a new series containing
    /// the aggregates of each group. 
    ///
    /// This operation is designed to be used with [hierarchical indexing](../features.html#indexing).
    ///
    /// ## Parameters
    ///  - `series` - An input series to be aggregated
    ///  - `op` - A function that takes a sequence and produces an aggregated result
    ///  - `level` - A delegate that returns a new group key, based on the key in the input series
    ///
    /// [category:Hierarchical index operations]
    [<CompiledName("StatisticsLevel")>]
    let inline statLevel (level:'K1 -> 'K2) op (series:Series<_, 'V>) : Series<_, 'R> = 
      series.GroupBy(fun kvp -> level kvp.Key) |> mapValues (stat op)
  
    /// Groups the elements of the input series in groups based on the keys
    /// produced by `level` and then aggregates series representing each group
    /// using the specified function `op`. The result is a new series containing
    /// the aggregates of each group. 
    ///
    /// This operation is designed to be used with [hierarchical indexing](../features.html#indexing).
    ///
    /// ## Parameters
    ///  - `series` - An input series to be aggregated
    ///  - `op` - A function that takes a series and produces an aggregated result
    ///  - `level` - A delegate that returns a new group key, based on the key in the input series
    ///
    /// [category:Hierarchical index operations]
    [<CompiledName("ApplyLevel")>]
    let inline applyLevel (level:'K1 -> 'K2) op (series:Series<_, 'V>) : Series<_, 'R> = 
      series.GroupBy(fun kvp -> level kvp.Key) |> mapValues op
  
    /// Groups the elements of the input series in groups based on the keys
    /// produced by `level` and then returns a new series containing
    /// the mean of each group. 
    ///
    /// This operation is designed to be used with [hierarchical indexing](../features.html#indexing).
    ///
    /// ## Parameters
    ///  - `series` - A series of values that are used to calculate the means
    ///  - `level` - A delegate that returns a new group key, based on the key in the input series
    ///
    /// [category:Hierarchical index operations]
    [<CompiledName("MeanLevel")>]
    let inline meanLevel (level:'K1 -> 'K2) (series:Series<_, 'V>) = 
      series |> fastApplyLevel level ReadOnlyCollection.average ReadOnlyCollection.averageOptional Seq.average
  
    /// Groups the elements of the input series in groups based on the keys
    /// produced by `level` and then returns a new series containing
    /// the standard deviation of each group. 
    ///
    /// This operation is designed to be used with [hierarchical indexing](../features.html#indexing).
    ///
    /// ## Parameters
    ///  - `series` - A series of values that are used to calculate the standard deviations
    ///  - `level` - A delegate that returns a new group key, based on the key in the input series
    ///
    /// [category:Hierarchical index operations]
    [<CompiledName("StandardDeviationLevel")>]
    let inline sdvLevel (level:'K1 -> 'K2) (series:Series<_, float>) = 
      series |> statLevel level Statistics.StandardDeviation 
  
    /// Groups the elements of the input series in groups based on the keys
    /// produced by `level` and then returns a new series containing
    /// the median of each group. 
    ///
    /// This operation is designed to be used with [hierarchical indexing](../features.html#indexing).
    ///
    /// ## Parameters
    ///  - `series` - A series of values that are used to calculate the medians
    ///  - `level` - A delegate that returns a new group key, based on the key in the input series
    ///
    /// [category:Hierarchical index operations]
    [<CompiledName("MedianLevel")>]
    let inline medianLevel level (series:Series<_, float>) = 
      series |> statLevel level Statistics.Median
  
    /// Groups the elements of the input series in groups based on the keys
    /// produced by `level` and then returns a new series containing
    /// the sum of each group. 
    ///
    /// This operation is designed to be used with [hierarchical indexing](../features.html#indexing).
    ///
    /// ## Parameters
    ///  - `series` - A series of values that are used to calculate the sums
    ///  - `level` - A delegate that returns a new group key, based on the key in the input series
    ///
    /// [category:Hierarchical index operations]
    [<CompiledName("SumLevel")>]
    let inline sumLevel (level:'K1 -> 'K2) (series:Series<_, 'V>) = 
      series |> fastApplyLevel level ReadOnlyCollection.sum ReadOnlyCollection.sumOptional Seq.sum
  
    /// Groups the elements of the input series in groups based on the keys
    /// produced by `level` and then returns a new series containing
    /// the greatest element of each group. 
    ///
    /// This operation is designed to be used with [hierarchical indexing](../features.html#indexing).
    ///
    /// ## Parameters
    ///  - `series` - A series of values that are used to calculate the greatest elements
    ///  - `level` - A delegate that returns a new group key, based on the key in the input series
    ///
    /// [category:Hierarchical index operations]
    [<CompiledName("MinLevel")>]
    let inline minLevel (level:'K1 -> 'K2) (series:Series<_, 'V>) = 
      series |> fastApplyLevel level ReadOnlyCollection.min (ReadOnlyCollection.minOptional >> OptionalValue.get) Seq.min
  
    /// Groups the elements of the input series in groups based on the keys
    /// produced by `level` and then returns a new series containing
    /// the greatest element of each group. 
    ///
    /// This operation is designed to be used with [hierarchical indexing](../features.html#indexing).
    ///
    /// ## Parameters
    ///  - `series` - A series of values that are used to calculate the greatest elements
    ///  - `level` - A delegate that returns a new group key, based on the key in the input series
    ///
    /// [category:Hierarchical index operations]
    [<CompiledName("MaxLevel")>]
    let inline maxLevel (level:'K1 -> 'K2) (series:Series<_, 'V>) = 
      series |> fastApplyLevel level ReadOnlyCollection.max (ReadOnlyCollection.maxOptional >> OptionalValue.get) Seq.max
  
    /// Groups the elements of the input series in groups based on the keys
    /// produced by `level` and then returns a new series containing
    /// the counts of elements in each group. 
    ///
    /// This operation is designed to be used with [hierarchical indexing](../features.html#indexing).
    ///
    /// ## Parameters
    ///  - `series` - A series of values that are used to calculate the counts
    ///  - `level` - A delegate that returns a new group key, based on the key in the input series
    ///
    /// [category:Hierarchical index operations]
    [<CompiledName("CountLevel")>]
    let inline countLevel (level:'K1 -> 'K2) (series:Series<_, 'V>) = 
      series |> fastApplyLevel level ReadOnlyCollection.length ReadOnlyCollection.lengthOptional Seq.length
  
    /// Groups the elements of the input series in groups based on the keys
    /// produced by `level` and then aggregates elements in each group
    /// using the specified function `op`. The result is a new series containing
    /// the aggregates of each group. 
    ///
    /// This operation is designed to be used with [hierarchical indexing](../features.html#indexing).
    ///
    /// ## Parameters
    ///  - `series` - An input series to be aggregated
    ///  - `op` - A function that is used to aggregate elements of each group
    ///  - `level` - A delegate that returns a new group key, based on the key in the input series
    ///
    /// [category:Hierarchical index operations]
    [<CompiledName("ReduceLevel")>]
    let reduceLevel (level:'K1 -> 'K2) op (series:Series<_, 'T>) = 
      series.GroupBy(fun (KeyValue(key, _)) -> level key) |> mapValues (reduce op)
  
    /// Given a series with (potentially) multi-level index, group the elements
    /// of the series according to one part of the key (specified by a key selector)
    /// and then apply the specified transformation on each group.
    ///
    /// ## Parameters
    ///  - `level` - Specifies the level projection (from source key to target key)
    ///  - `op` - Reduction operation to be applied to each group
    ///  - `series` - Input series to be flattened
    ///
    /// [category:Hierarchical index operations]
    [<CompiledName("FlattenLevel")>]
    let inline flattenLevel (level:'K1 -> 'K2) op (series:Series<_, 'S>) : Series<_, 'V> = 
      series.GroupBy(fun kvp -> level kvp.Key) |> mapValues op
  
    // ----------------------------------------------------------------------------------------------
    // Windowing, chunking and grouping
    // ----------------------------------------------------------------------------------------------
  
    /// Aggregates an ordered series using the method specified by `Aggregation<K>` and 
    /// returns the windows or chunks as nested series. A key for each window or chunk is
    /// selected using the specified `keySelector`.
    ///
    /// ## Parameters
    ///  - `aggregation` - Specifies the aggregation method using `Aggregation<K>`. This is
    ///    a discriminated union listing various chunking and windowing conditions.
    ///  - `keySelector` - A function that is called on each chunk to obtain a key.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("Aggregate")>]
    let aggregate aggregation keySelector (series:Series<'K, 'T>) : Series<'TNewKey, _> =
      series.Aggregate
        ( aggregation, System.Func<_, _>(keySelector), System.Func<_, _>(fun v -> OptionalValue(v)))
  
    /// Aggregates an ordered series using the method specified by `Aggregation<K>` and then
    /// applies the provided value selector `f` on each window or chunk to produce the result
    /// which is returned as a new series. A key for each window or chunk is
    /// selected using the specified `keySelector`.
    ///
    /// ## Parameters
    ///  - `aggregation` - Specifies the aggregation method using `Aggregation<K>`. This is
    ///    a discriminated union listing various chunking and windowing conditions.
    ///  - `keySelector` - A function that is called on each chunk to obtain a key.
    ///  - `f` - A value selector function that is called to aggregate each chunk or window.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("AggregateInto")>]
    let aggregateInto aggregation keySelector f (series:Series<'K, 'T>) : Series<'TNewKey, 'R> =
      series.Aggregate
        ( aggregation, System.Func<_, _>(keySelector), System.Func<_, _>(f))
  
    /// Creates a sliding window using the specified size and boundary behavior and then
    /// applies the provided value selector `f` on each window to produce the result
    /// which is returned as a new series. The key is the last key of the window, unless
    /// boundary behavior is `Boundary.AtEnding` (in which case it is the first key).
    ///
    /// ## Parameters
    ///  - `bounds` - Specifies the window size and bounary behavior. The boundary behavior
    ///    can be `Boundary.Skip` (meaning that no incomplete windows are produced), 
    ///    `Boundary.AtBeginning` (meaning that incomplete windows are produced at the beginning)
    ///    or `Boundary.AtEnding` (to produce incomplete windows at the end of series)
    ///  - `f` - A value selector that is called to aggregate each window.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("WindowSizeInto")>]
    let windowSizeInto bounds f (series:Series<'K, 'T>) : Series<'K, 'R> =
      let dir = if snd bounds = Boundary.AtEnding then Direction.Forward else Direction.Backward
      let keySel = System.Func<DataSegment<Series<_, _>>, _>(fun data -> 
        if dir = Direction.Backward then data.Data.Index.KeyRange |> snd
        else data.Data.Index.KeyRange |> fst )
      series.Aggregate(WindowSize(bounds), keySel, (fun ds -> OptionalValue(f ds)))
  
    /// Creates a sliding window using the specified size and boundary behavior and returns
    /// the produced windows as a nested series. The key is the last key of the window, unless
    /// boundary behavior is `Boundary.AtEnding` (in which case it is the first key).
    ///
    /// ## Parameters
    ///  - `bounds` - Specifies the window size and bounary behavior. The boundary behavior
    ///    can be `Boundary.Skip` (meaning that no incomplete windows are produced), 
    ///    `Boundary.AtBeginning` (meaning that incomplete windows are produced at the beginning)
    ///    or `Boundary.AtEnding` (to produce incomplete windows at the end of series)
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("WindowSize")>]
    let inline windowSize bounds (series:Series<'K, 'T>) = 
      windowSizeInto bounds DataSegment.data series 
  
    // Based on distance
  
    /// Creates a sliding window based on distance between keys. A window is started at each
    /// input element and ends once the distance between the first and the last key is greater
    /// than the specified `distance`. Each window is then aggregated into a value using the
    /// specified function `f`. The key of each window is the key of the first element in the window.
    ///
    /// ## Parameters
    ///  - `distance` - The maximal allowed distance between keys of a window. Note that this
    ///    is an inline function - there must be `-` operator defined between `distance` and the
    ///    keys of the series.
    ///  - `f` - A function that is used to aggregate each window into a single value.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("WindowDistanceInto")>]
    let inline windowDistInto distance f (series:Series<'K, 'T>) =
      series.Aggregate
        ( WindowWhile(fun skey ekey -> (ekey - skey) < distance), 
          (fun d -> d.Data.Keys |> Seq.head), fun ds -> OptionalValue(f ds.Data))
  
    /// Creates a sliding window based on distance between keys. A window is started at each
    /// input element and ends once the distance between the first and the last key is greater
    /// than the specified `distance`. The windows are then returned as a nested series.
    /// The key of each window is the key of the first element in the window.
    ///
    /// ## Parameters
    ///  - `distance` - The maximal allowed distance between keys of a window. Note that this
    ///    is an inline function - there must be `-` operator defined between `distance` and the
    ///    keys of the series.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("WindowDistance")>]
    let inline windowDist (distance:'D) (series:Series<'K, 'T>) = 
      windowDistInto distance id series 
  
    // Window using while
  
    /// Creates a sliding window based on a condition on keys. A window is started at each
    /// input element and ends once the specified `cond` function returns `false` when called on 
    /// the first and the last key of the window. Each window is then aggregated into a value using the
    /// specified function `f`. The key of each window is the key of the first element in the window.
    ///
    /// ## Parameters
    ///  - `cond` - A function that is called on the first and the last key of a window
    ///    to determine when a window should end.
    ///  - `f` - A function that is used to aggregate each window into a single value.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("WindowWhileInto")>]
    let inline windowWhileInto cond f (series:Series<'K, 'T>) =
      series.Aggregate(WindowWhile(cond), (fun d -> d.Data.Keys |> Seq.head), fun ds -> OptionalValue(f ds.Data))
  
    /// Creates a sliding window based on a condition on keys. A window is started at each
    /// input element and ends once the specified `cond` function returns `false` when called on 
    /// the first and the last key of the window. The windows are then returned as a nested series.
    /// The key of each window is the key of the first element in the window.
    ///
    /// ## Parameters
    ///  - `cond` - A function that is called on the first and the last key of a window
    ///    to determine when a window should end.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("WindowWhile")>]
    let inline windowWhile cond (series:Series<'K, 'T>) = 
      windowWhileInto cond id series 
  
    // Chunk based on size
  
    /// Aggregates the input into a series of adacent chunks using the specified size and boundary behavior and then
    /// applies the provided value selector `f` on each chunk to produce the result
    /// which is returned as a new series. The key is the last key of the chunk, unless
    /// boundary behavior is `Boundary.AtEnding` (in which case it is the first key).
    ///
    /// ## Parameters
    ///  - `bounds` - Specifies the chunk size and bounary behavior. The boundary behavior
    ///    can be `Boundary.Skip` (meaning that no incomplete chunks are produced), 
    ///    `Boundary.AtBeginning` (meaning that incomplete chunks are produced at the beginning)
    ///    or `Boundary.AtEnding` (to produce incomplete chunks at the end of series)
    ///  - `f` - A value selector that is called to aggregate each chunk.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("ChunkSizeInto")>]
    let inline chunkSizeInto bounds f (series:Series<'K, 'T>) : Series<'K, 'R> =
      series.Aggregate(ChunkSize(bounds), (fun d -> d.Data.Keys |> Seq.head), fun ds -> OptionalValue(f ds))
  
    /// Aggregates the input into a series of adacent chunks using the specified size and boundary behavior and returns
    /// the produced chunks as a nested series. The key is the last key of the chunk, unless
    /// boundary behavior is `Boundary.AtEnding` (in which case it is the first key).
    ///
    /// ## Parameters
    ///  - `bounds` - Specifies the chunk size and bounary behavior. The boundary behavior
    ///    can be `Boundary.Skip` (meaning that no incomplete chunks are produced), 
    ///    `Boundary.AtBeginning` (meaning that incomplete chunks are produced at the beginning)
    ///    or `Boundary.AtEnding` (to produce incomplete chunks at the end of series)
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("ChunkSize")>]
    let inline chunkSize bounds (series:Series<'K, 'T>) = 
      chunkSizeInto bounds DataSegment.data series 
  
    // Chunk based on distance
  
    /// Aggregates the input into a series of adacent chunks. A chunk is started once
    /// the distance between the first and the last key of a previous chunk is greater
    /// than the specified `distance`. Each chunk is then aggregated into a value using the
    /// specified function `f`. The key of each chunk is the key of the first element in the chunk.
    ///
    /// ## Parameters
    ///  - `distance` - The maximal allowed distance between keys of a chunk. Note that this
    ///    is an inline function - there must be `-` operator defined between `distance` and the
    ///    keys of the series.
    ///  - `f` - A value selector that is called to aggregate each chunk.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("ChunkDistanceInto")>]
    let inline chunkDistInto (distance:^D) f (series:Series<'K, 'T>) : Series<'K, 'R> =
      series.Aggregate(ChunkWhile(fun skey ekey -> (ekey - skey) < distance), (fun d -> d.Data.Keys |> Seq.head), fun ds -> OptionalValue(f ds.Data))
  
    /// Aggregates the input into a series of adacent chunks. A chunk is started once
    /// the distance between the first and the last key of a previous chunk is greater
    /// than the specified `distance`. The chunks are then returned as a nested series.
    /// The key of each chunk is the key of the first element in the chunk.
    ///
    /// ## Parameters
    ///  - `distance` - The maximal allowed distance between keys of a chunk. Note that this
    ///    is an inline function - there must be `-` operator defined between `distance` and the
    ///    keys of the series.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("ChunkDistance")>]
    let inline chunkDist (distance:^D) (series:Series<'K, 'T>) = 
      chunkDistInto distance id series 
  
    // Chunk while
  
    /// Aggregates the input into a series of adacent chunks based on a condition on keys. A chunk is started 
    /// once the specified `cond` function returns `false` when called on  the first and the last key of the 
    /// previous chunk. Each chunk is then aggregated into a value using the
    /// specified function `f`. The key of each chunk is the key of the first element in the chunk.
    ///
    /// ## Parameters
    ///  - `cond` - A function that is called on the first and the last key of a chunk
    ///    to determine when a window should end.
    ///  - `f` - A value selector that is called to aggregate each chunk.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("ChunkWhileInto")>]
    let inline chunkWhileInto cond f (series:Series<'K, 'T>) =
      series.Aggregate(ChunkWhile(cond), (fun d -> d.Data.Keys |> Seq.head), fun ds -> OptionalValue(f ds.Data))
  
    /// Aggregates the input into a series of adacent chunks based on a condition on keys. A chunk is started 
    /// once the specified `cond` function returns `false` when called on  the first and the last key of the 
    /// previous chunk. The chunks are then returned as a nested series.
    /// The key of each chunk is the key of the first element in the chunk.
    ///
    /// ## Parameters
    ///  - `cond` - A function that is called on the first and the last key of a chunk
    ///    to determine when a window should end.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("ChunkWhile")>]
    let inline chunkWhile cond (series:Series<'K, 'T>) = 
      chunkWhileInto cond id series 
  
    // Most common-case functions
  
    /// Creates a sliding window using the specified size and then applies the provided 
    /// value selector `f` on each window to produce the result which is returned as a new series. 
    /// This function skips incomplete chunks - you can use `Series.windowSizeInto` for more options.
    ///
    /// ## Parameters
    ///  - `size` - The size of the sliding window.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("WindowInto")>]
    let inline windowInto size f (series:Series<'K, 'T>) : Series<'K, 'R> =
      windowSizeInto (size, Boundary.Skip) (DataSegment.data >> f) series
  
    /// Creates a sliding window using the specified size and returns the produced windows as 
    /// a nested series. The key in the new series is the last key of the window. This function
    /// skips incomplete chunks - you can use `Series.windowSize` for more options.
    ///
    /// ## Parameters
    ///  - `size` - The size of the sliding window.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("Window")>]
    let inline window size (series:Series<'K, 'T>) =
      windowSize (size, Boundary.Skip) series
  
    /// Aggregates the input into a series of adacent chunks and then applies the provided 
    /// value selector `f` on each chunk to produce the result which is returned as a new series. 
    /// The key in the new series is the last key of the chunk. This function
    /// skips incomplete chunks - you can use `Series.chunkSizeInto` for more options.
    ///
    /// ## Parameters
    ///  - `size` - The size of the chunk.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("ChunkInto")>]
    let inline chunkInto size f (series:Series<'K, 'T>) : Series<'K, 'R> =
      chunkSizeInto (size, Boundary.Skip) (DataSegment.data >> f) series
  
    /// Aggregates the input into a series of adacent chunks and returns the produced chunks as
    /// a nested series. The key in the new series is the last key of the chunk. This function
    /// skips incomplete chunks - you can use `Series.chunkSize` for more options.
    ///
    /// ## Parameters
    ///  - `size` - The size of the chunk.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("Chunk")>]
    let inline chunk size (series:Series<'K, 'T>) =
      chunkSize (size, Boundary.Skip) series
  
    // Pairwise
  
    /// Returns a series containing the predecessor and an element for each input, except
    /// for the first one. The returned series is one key shorter (it does not contain a 
    /// value for the first key).
    ///
    /// ## Parameters
    ///  - `series` - The input series to be aggregated.
    ///
    /// ## Example
    ///
    ///     let input = series [ 1 => 'a'; 2 => 'b'; 3 => 'c']
    ///     let res = input |> Series.pairwise
    ///     res = series [2 => ('a', 'b'); 3 => ('b', 'c') ]
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("Pairwise")>]
    let pairwise (series:Series<'K, 'T>) = 
      series.Pairwise()
  
    /// Aggregates the input into pairs containing the predecessor and an element for each input, except
    /// for the first one. Then calls the specified aggregation function `f` with a tuple and a key.
    /// The returned series is one key shorter (it does not contain a  value for the first key).
    ///
    /// ## Parameters
    ///  - `f` - A function that is called for each pair to produce result in the final series.
    ///  - `series` - The input series to be aggregated.
    ///
    /// [category:Windowing, chunking and grouping]
    [<CompiledName("PairwiseWith")>]
    let pairwiseWith f (series:Series<'K, 'T>) = 
      series.Pairwise() |> map (fun k v -> f k v)
  
    // Grouping
  
    /// Groups a series (ordered or unordered) using the specified key selector (`keySelector`) 
    /// and then aggregates each group into a single value, returned in the resulting series,
    /// using the provided `f` function.
    ///
    /// ## Parameters
    ///  - `keySelector` - Generates a new key that is used for aggregation, based on the original 
    ///    key and value. The new key must support equality testing.
    ///  - `f` - A function to aggregate each group of collected elements.
    ///  - `series` - An input series to be grouped. 
    ///
    /// [category:Windowing, chunking and grouping]
    let groupInto (keySelector:'K -> 'T -> 'TNewKey) f (series:Series<'K, 'T>) : Series<'TNewKey, 'TNewValue> =
      series.GroupBy(fun (KeyValue(k,v)) -> keySelector k v) |> map (fun k s -> f k s)
  
    /// Groups a series (ordered or unordered) using the specified key selector (`keySelector`) 
    /// and then returns a series of (nested) series as the result. The outer series is indexed by
    /// the newly produced keys, the nested series are indexed with the original keys.
    ///
    /// ## Parameters
    ///  - `keySelector` - Generates a new key that is used for aggregation, based on the original 
    ///    key and value. The new key must support equality testing.
    ///  - `series` - An input series to be grouped. 
    ///
    /// [category:Windowing, chunking and grouping]
    let groupBy (keySelector:'K -> 'T -> 'TNewKey) (series:Series<'K, 'T>) =
      groupInto keySelector (fun k s -> s) series
  
  
    // ----------------------------------------------------------------------------------------------
    // Handling of missing values
  