def run(params_config, features_path, observations, scoring, folds,
        report, processes, cv_timeout, scale_features, verbose):

    observations = list(observations)
    if scale_features:
        logger.debug("Scaling features...")
        ss = preprocessing.StandardScaler()
        feature_values, labels = (list(vect) for vect in zip(*observations))
        scaled_feature_values = ss.fit_transform(feature_values)
        observations = list(zip(scaled_feature_values, labels))

    # Prepare the worker pool
    logger.debug("Starting up multiprocessing pool (processes={0})"
                 .format(processes))
    pool = multiprocessing.Pool(processes=processes)

    # Start writing the model tuning report
    possible_labels = set(label for _, label in observations)
    report.write("# Model tuning report\n")
    report.write("- Revscoring version: {0}\n".format(__version__))
    report.write("- Features: {0}\n".format(features_path))
    report.write("- Date: {0}\n".format(datetime.datetime.now().isoformat()))
    report.write("- Observations: {0}\n".format(len(observations)))
    report.write("- Labels: {0}\n".format(json.dumps(list(possible_labels))))
    report.write("- Scoring: {0}\n".format(scoring))
    report.write("- Folds: {0}\n".format(folds))
    report.write("\n")

    # For each estimator and paramset, submit the job.
    cv_result_sets = defaultdict(lambda: [])
    for name, estimator, param_grid in _estimator_param_grid(params_config):
        logger.debug("Submitting jobs for {0}:".format(name))
        for params in param_grid:
            logger.debug("\tsubmitting {0}..."
                         .format(format_params(params)))
            result = pool.apply_async(_cross_validate,
                                      [observations, estimator, params],
                                      {'cv_timeout': cv_timeout,
                                       'scoring': scoring, 'folds': folds})
            cv_result_sets[name].append((params, result))

    # Barrier synchronization
    logger.info("Running gridsearch for {0} model/params pairs ..."
                .format(sum(len(p_r) for p_r in cv_result_sets)))
    grid_scores = []
    for name, param_results in cv_result_sets.items():
        for params, result in param_results:
            scores = result.get()  # This is a line that blocks
            grid_scores.append((name, params, scores.mean(), scores.std()))

    # Write the rest of the report!  First, print the top 10 combinations
    report.write("# Top scoring configurations\n")
    grid_scores.sort(key=lambda gs: gs[2], reverse=True)
    table = tabulate(
        ((name, round(mean_score, 3), round(std_score, 3),
          format_params(params))
         for name, params, mean_score, std_score in
         grid_scores[:10]),
        headers=["model", "mean(scores)", "std(scores)", "params"],
        tablefmt="pipe"
    )
    report.write(table + "\n")
    report.write("\n")

    # Now print out scores for each model.
    report.write("# Models\n")
    for name, param_results in cv_result_sets.items():
        report.write("## {0}\n".format(name))

        param_scores = ((p, r.get()) for p, r in param_results)
        param_stats = [(p, s.mean(), s.std()) for p, s in param_scores]
        param_stats.sort(key=lambda v: v[1], reverse=True)

        table = tabulate(
            ((round(mean_score, 3), round(std_score, 3),
              format_params(params))
             for params, mean_score, std_score in
             param_stats),
            headers=["mean(scores)", "std(scores)", "params"],
            tablefmt="pipe"
        )
        report.write(table + "\n")
        report.write("\n")

    report.close()
dataset/ETHPy150Open wiki-ai/revscoring/revscoring/utilities/tune.py/run/original