Compose a pybrain.dataset from a pandas DataFrame

    Arguments:
      delays (list of int): sample delays to use for the input tapped delay line
        Positive and negative values are treated the same as sample counts into the past.
        default: [1, 2, 3], in z-transform notation: z^-1 + z^-2 + z^-3
      inputs (list of int or list of str): column indices or labels for the inputs
      outputs (list of int or list of str): column indices or labels for the outputs
      normalize (bool): whether to divide each input to be normally distributed about 0 with std 1

    Returns:
      3-tuple: tuple(dataset, list of means, list of stds)
        means and stds allow normalization of new inputs and denormalization of the outputs

    TODO:

        Detect categorical variables with low dimensionality and split into separate bits
            Vowpel Wabbit hashes strings into an int?
        Detect ordinal variables and convert to continuous int sequence
        SEE: http://www.stat.berkeley.edu/~breiman/RandomForests/cc_home.htm

def dataset_from_dataframe(df, delays=(1, 2, 3), inputs=(1, 2, -1), outputs=(-1,), normalize=False, verbosity=1):
    """Compose a pybrain.dataset from a pandas DataFrame

    Arguments:
      delays (list of int): sample delays to use for the input tapped delay line
        Positive and negative values are treated the same as sample counts into the past.
        default: [1, 2, 3], in z-transform notation: z^-1 + z^-2 + z^-3
      inputs (list of int or list of str): column indices or labels for the inputs
      outputs (list of int or list of str): column indices or labels for the outputs
      normalize (bool): whether to divide each input to be normally distributed about 0 with std 1

    Returns:
      3-tuple: tuple(dataset, list of means, list of stds)
        means and stds allow normalization of new inputs and denormalization of the outputs

    TODO:

        Detect categorical variables with low dimensionality and split into separate bits
            Vowpel Wabbit hashes strings into an int?
        Detect ordinal variables and convert to continuous int sequence
        SEE: http://www.stat.berkeley.edu/~breiman/RandomForests/cc_home.htm
    """
    if isinstance(delays, int):
        if delays:
            delays = range(1, delays + 1)
        else:
            delays = [0]
    delays = np.abs(np.array([int(i) for i in delays]))
    inputs = [df.columns[int(inp)] if isinstance(inp, (float, int)) else str(inp) for inp in inputs]
    outputs = [df.columns[int(out)] if isinstance(out, (float, int)) else str(out) for out in (outputs or [])]

    inputs = [fuzzy_get(df.columns, i) for i in inputs]
    outputs = [fuzzy_get(df.columns, o) for o in outputs]

    N_inp = len(inputs)
    N_out = len(outputs)

    inp_outs = inputs + outputs
    if verbosity > 0:
        print("inputs: {}\noutputs: {}\ndelays: {}\n".format(inputs, outputs, delays))
    means, stds = np.zeros(len(inp_outs)), np.ones(len(inp_outs))
    if normalize:
        means, stds = df[inp_outs].mean(), df[inp_outs].std()

    if normalize and verbosity > 0:
        print("Input mean values (used to normalize input biases): {}".format(means[:N_inp]))
        print("Output mean values (used to normalize output biases): {}".format(means[N_inp:]))
    ds = pb.datasets.SupervisedDataSet(N_inp * len(delays), N_out)
    if verbosity > 0:
        print("Dataset dimensions are {}x{}x{} (records x indim x outdim) for {} delays, {} inputs, {} outputs".format(
              len(df), ds.indim, ds.outdim, len(delays), len(inputs), len(outputs)))
    # FIXME: normalize the whole matrix at once and add it quickly rather than one sample at a time
    if delays == np.array([0]) and not normalize:
        if verbosity > 0:
            print("No tapped delay lines (delays) were requested, so using undelayed features for the dataset.")
        assert(df[inputs].values.shape[0] == df[outputs].values.shape[0])
        ds.setField('input', df[inputs].values)
        ds.setField('target', df[outputs].values)
        ds.linkFields(['input', 'target'])
        # for inp, outp in zip(df[inputs].values, df[outputs].values):
        #     ds.appendLinked(inp, outp)
        assert(len(ds['input']) == len(ds['target']))
    else:
        for i, out_vec in enumerate(df[outputs].values):
            if verbosity > 0 and i % 100 == 0:
                print("{}%".format(i / .01 / len(df)))
            elif verbosity > 1:
                print('sample[{i}].target={out_vec}'.format(i=i, out_vec=out_vec))
            if i < max(delays):
                continue
            inp_vec = []
            for delay in delays:
                inp_vec += list((df[inputs].values[i - delay] - means[:N_inp]) / stds[:N_inp])
            ds.addSample(inp_vec, (out_vec - means[N_inp:]) / stds[N_inp:])
    if verbosity > 0:
        print("Dataset now has {} samples".format(len(ds)))
    if normalize:
        return ds, means, stds
    else:
        return ds