docs: def create_agg_ctable(self, groupby_cols, agg_list, expectedlen, rootdir):
          '''Create a container for the output table, a dictionary describing it's
              columns and a list of tuples describing aggregation
              operations to perform.
  
          Args:
              groupby_cols (list): a list of columns to groupby over
              agg_list (list): the aggregation operations (see groupby for more info)
              expectedlen (int): expected length of output table
              rootdir (string): the directory to write the table to
  
          Returns:
              ctable: A table in the correct format for containing the output of
                      the specified aggregation operations.
              dict: (dtype_dict) dictionary describing columns to create
              list: (agg_ops) list of tuples of the form:
                      (input_col_name, output_col_name, agg_op)
                      input_col_name (string): name of the column to act on
                      output_col_name (string): name of the column to output to
                      agg_op (int): aggregation operation to perform
          '''
          dtype_dict = {}
  
          # include all the groupby columns
          for col in groupby_cols:
              dtype_dict[col] = self[col].dtype
  
          agg_ops_list = ['sum', 'count', 'count_distinct', 'sorted_count_distinct', 'mean', 'std']
          agg_ops = []
  
          for agg_info in agg_list:
  
              if not isinstance(agg_info, list):
                  # example: ['m1', 'm2', ...]
                  # default operation (sum) and default output column name (same is input)
                  output_col_name = agg_info
                  input_col_name = agg_info
                  agg_op = 'sum'
              else:
                  input_col_name = agg_info[0]
                  agg_op = agg_info[1]
  
                  if len(agg_info) == 2:
                      # example: [['m1', 'sum'], ['m2', 'mean], ...]
                      # default output column name
                      output_col_name = input_col_name
                  else:
                      # example: [['m1', 'sum', 'mnew1'], ['m1, 'mean','mnew2'], ...]
                      # fully specified
                      output_col_name = agg_info[2]
  
              if agg_op not in agg_ops_list:
                  raise NotImplementedError(
                      'Unknown Aggregation Type: ' + str(agg_op))
  
              # choose output column dtype based on aggregation operation and
              # input column dtype
              # TODO: check if the aggregation columns is numeric
              # NB: we could build a concatenation for strings like pandas, but I would really prefer to see that as a
              # separate operation
              if agg_op in ('count', 'count_distinct', 'sorted_count_distinct'):
                  output_col_dtype = np.dtype(np.int64)
              elif agg_op in ('mean', 'std'):
                  output_col_dtype = np.dtype(np.float64)
              else:
                  output_col_dtype = self[input_col_name].dtype
  
              dtype_dict[output_col_name] = output_col_dtype
  
              # save output
              agg_ops.append((input_col_name, output_col_name, agg_op))
  
          # create aggregation table
          ct_agg = bcolz.ctable(
              np.zeros(expectedlen, [('tmp_col_bquery__', np.bool)]),
              expectedlen=expectedlen,
              rootdir=rootdir)
  
          return ct_agg, dtype_dict, agg_ops