Reduce" a DataFrame by collapsing rows in grouped chunks. Returns another
    DataFrame with similar columns but fewer rows.

    Arguments:

    df
      The input :class:`pandas.DataFrame`.
    chunk_slicers
      An iterable that returns values that are used to slice *df* with its
      :meth:`pandas.DataFrame.iloc` indexer. An example value might be the
      generator returned from :func:`slice_evenly_with_gaps`.
    avg_cols
      An iterable of names of columns that are to be reduced by taking the mean.
    uavg_cols
      An iterable of names of columns that are to be reduced by taking a
      weighted mean.
    minmax_cols
      An iterable of names of columns that are to be reduced by reporting minimum
      and maximum values.
    nchunk_colname
      The name of a column to create reporting the number of rows contributing
      to each chunk.
    uncert_prefix
      The column name prefix for locating uncertainty estimates. By default, the
      uncertainty on the column ``"temp"`` is given in the column ``"utemp"``.
    min_points_per_chunk
      Require at least this many rows in each chunk. Smaller chunks are discarded.

    Returns a new :class:`pandas.DataFrame`.

def reduce_data_frame (df, chunk_slicers,
                       avg_cols=(),
                       uavg_cols=(),
                       minmax_cols=(),
                       nchunk_colname='nchunk',
                       uncert_prefix='u',
                       min_points_per_chunk=3):
    """"Reduce" a DataFrame by collapsing rows in grouped chunks. Returns another
    DataFrame with similar columns but fewer rows.

    Arguments:

    df
      The input :class:`pandas.DataFrame`.
    chunk_slicers
      An iterable that returns values that are used to slice *df* with its
      :meth:`pandas.DataFrame.iloc` indexer. An example value might be the
      generator returned from :func:`slice_evenly_with_gaps`.
    avg_cols
      An iterable of names of columns that are to be reduced by taking the mean.
    uavg_cols
      An iterable of names of columns that are to be reduced by taking a
      weighted mean.
    minmax_cols
      An iterable of names of columns that are to be reduced by reporting minimum
      and maximum values.
    nchunk_colname
      The name of a column to create reporting the number of rows contributing
      to each chunk.
    uncert_prefix
      The column name prefix for locating uncertainty estimates. By default, the
      uncertainty on the column ``"temp"`` is given in the column ``"utemp"``.
    min_points_per_chunk
      Require at least this many rows in each chunk. Smaller chunks are discarded.

    Returns a new :class:`pandas.DataFrame`.

    """
    subds = [df.iloc[idx] for idx in chunk_slicers]
    subds = [sd for sd in subds if sd.shape[0] >= min_points_per_chunk]

    chunked = df.__class__ ({nchunk_colname: np.zeros (len (subds), dtype=np.int)})

    # Some future-proofing: allow possibility of different ways of mapping
    # from a column giving a value to a column giving its uncertainty.

    uncert_col_name = lambda c: uncert_prefix + c

    for i, subd in enumerate (subds):
        label = chunked.index[i]
        chunked.loc[label,nchunk_colname] = subd.shape[0]

        for col in avg_cols:
            chunked.loc[label,col] = subd[col].mean ()

        for col in uavg_cols:
            ucol = uncert_col_name (col)
            v, u = weighted_mean (subd[col], subd[ucol])
            chunked.loc[label,col] = v
            chunked.loc[label,ucol] = u

        for col in minmax_cols:
            chunked.loc[label, 'min_'+col] = subd[col].min ()
            chunked.loc[label, 'max_'+col] = subd[col].max ()

    return chunked