Aggregates molecular data for model training

    Parameters
    ----------
    data: list
        density, cpt, and/or viscosity
    T: array
        desired min and max of temperature distribution
    P: array
        desired min and max of pressure distribution
    data_ranges: array
        desired min and max of property distribution(s)
    merge: str
        overlap or union, defaults to overlap. Merge type of property sets
    feature_type: str
        desired feature set, defaults to RDKit's 2D descriptor set

    Returns
    -----------
    devmodel: dev_model obj
        returns dev_model object containing scale/center information,
        data summary, and the data frame

def aggregate_data(data, T=[0, inf], P=[0, inf], data_ranges=None,
                   merge="overlap", feature_type=None, impute=False,
                   scale_center=True):
    """
    Aggregates molecular data for model training

    Parameters
    ----------
    data: list
        density, cpt, and/or viscosity
    T: array
        desired min and max of temperature distribution
    P: array
        desired min and max of pressure distribution
    data_ranges: array
        desired min and max of property distribution(s)
    merge: str
        overlap or union, defaults to overlap. Merge type of property sets
    feature_type: str
        desired feature set, defaults to RDKit's 2D descriptor set

    Returns
    -----------
    devmodel: dev_model obj
        returns dev_model object containing scale/center information,
        data summary, and the data frame
    """
    data_files = []
    for i, string in enumerate(data):
        data_files.append(load_data("%s_premodel.csv" % string))
        if i == 0:
            merged = data_files[0]
        if i == 1:
            merged = pd.merge(data_files[0], data_files[1], sort=False,
                              how='outer')
        elif i > 1:
            merged = pd.merge(merged, data_files[-1], sort=False, how='outer')
    if merge == "overlap":
        merged.dropna(inplace=True)
    # select state variable and data ranges
    merged = merged.loc[merged["Temperature, K"] < T[1]]
    merged = merged.loc[merged["Temperature, K"] > T[0]]
    merged = merged.loc[merged["Pressure, kPa"] < P[1]]
    merged = merged.loc[merged["Pressure, kPa"] > P[0]]
    for i in range(1, len(data) + 1):
        merged = merged[merged.iloc[:, -i] != 0]  # avoid log(0) error
        if data_ranges:
            merged = merged[merged.iloc[:, -i] < data_ranges[::-1][i - 1][1]]
            merged = merged[merged.iloc[:, -i] > data_ranges[::-1][i - 1][0]]
    merged.reset_index(drop=True, inplace=True)
    # Create summary of dataset
    unique_salts = merged["smiles-cation"] + merged["smiles-anion"]
    unique_cations = repr(merged["smiles-cation"].unique())
    unique_anions = repr(merged["smiles-anion"].unique())
    actual_data_ranges = []
    for i in range(1, len(data) + 3):
        actual_data_ranges.append("{} - {}".format(
            str(merged.iloc[:, -i].min()), str(merged.iloc[:, -i].max())))
    a = np.array([len(unique_salts.unique()), unique_cations, unique_anions,
                 len(unique_salts)])
    a = np.concatenate((a, actual_data_ranges))
    cols1 = ["Unique salts", "Cations", "Anions", "Total datapoints"]
    cols2 = ["Temperature range (K)", "Pressure range (kPa)"]
    cols = cols1 + data[::-1] + cols2
    data_summary = pd.DataFrame(a, cols)
    # scale and center
    metaDf = merged.select_dtypes(include=["object"])
    dataDf = merged.select_dtypes(include=[np.number])
    cols = dataDf.columns.tolist()
    if impute:
        imp = Imputer(missing_values='NaN', strategy="median", axis=0)
        X = imp.fit_transform(dataDf)
        dataDf = pd.DataFrame(X, columns=cols)
    instance = StandardScaler()
    if scale_center:
        for i in range(1, len(data) + 1):
            dataDf.is_copy = False
            dataDf.iloc[:, -i] = dataDf.iloc[:, -i].apply(lambda x:
                                                          log(float(x)))
        scaled_data = pd.DataFrame(instance.
                                   fit_transform(dataDf.iloc[:, :-len(data)]),
                                   columns=cols[:-len(data)])
        df = pd.concat([scaled_data, dataDf.iloc[:, -len(data):], metaDf],
                       axis=1)
        mean_std_of_coeffs = pd.DataFrame([instance.mean_, instance.scale_],
                                          columns=cols[:-len(data)])
    else:
        instance.fit(dataDf.iloc[:, :-len(data)])
        df = pd.concat([dataDf, metaDf], axis=1)
        mean_std_of_coeffs = pd.DataFrame([instance.mean_, instance.scale_],
                                          columns=cols[:-len(data)])
    devmodel = dev_model(mean_std_of_coeffs, data_summary, df)
    return devmodel