def main(argv=None):
    """script main.
    parses command line options in sys.argv, unless *argv* is given.
    """

    if not argv:
        argv = sys.argv

    # setup command line parser
    parser = E.OptionParser(
        version="%prog version: $Id$",
        usage=globals()["__doc__"])

    parser.add_option("-t", "--design-file-tsv", dest="design_file",
                      type="string",
                      help="filename with design [default=%default].")

    parser.add_option("-i", "--change-bin-min", dest="min_cbin",
                      type="float",
                      help="minimum bin for change bins [default=%default].")

    parser.add_option("-x", "--change-bin-max", dest="max_cbin",
                      type="float",
                      help="maximum bin for change bins [default=%default].")

    parser.add_option("-w", "--change-bin-width", dest="width_cbin",
                      type="float",
                      help="bin width for change bins [default=%default].")

    parser.add_option("-s", "--initial-bin-min", dest="min_ibin",
                      type="float",
                      help="minimum bin for initial bins[default=%default].")

    parser.add_option("-e", "--initial-bin-max", dest="max_ibin",
                      type="float",
                      help="maximum bin for intitial bins[default=%default].")

    parser.add_option("-y", "--initial-bin-width", dest="width_ibin",
                      type="float",
                      help="bin width intitial bins[default=%default].")

    parser.add_option("-l", "--spike-minimum", dest="min_spike",
                      type="int",
                      help="minimum number of spike-ins required within each bin\
                      [default=%default].")

    parser.add_option("-u", "--spike-maximum", dest="max_spike",
                      type="int",
                      help="maximum number of spike-ins allowed within each bin\
                      [default=%default].")

    parser.add_option("-d", "--difference-method", dest="difference",
                      type="choice", choices=("relative", "logfold"),
                      help="method to use for calculating difference\
                      [default=%default].")

    parser.add_option("-r", "--iterations", dest="iterations", type="int",
                      help="number of iterations [default=%default].")

    parser.add_option("-a", "--id_columns", dest="id", action="append",  # JJ is actually id_column
                      help="name of identification column(s)\
                      [default=%default].")

    parser.add_option("-c", "--cluster-maximum-distance",
                      dest="cluster_max_distance", type="int",
                      help="maximum distance between adjacent loci in cluster\
                      [default=%default].")

    parser.add_option("-b", "--cluster-minimum-size",
                      dest="cluster_min_size", type="int",
                      help="minimum number of loci required per cluster\
                      [default=%default].")

    parser.add_option("-f", "--spike-type",
                      dest="spike_type", type="choice",
                      choices=("row", "cluster"),
                      help="spike in type [default=%default].")

    parser.add_option("-g", "--subcluster-min-size",
                      dest="min_sbin", type="int",
                      help="minimum size of subcluster\
                      [default=%default].")

    parser.add_option("-j", "--subcluster-max-size",
                      dest="max_sbin", type="int",
                      help="maximum size of subcluster\
                      [default=%default].")

    parser.add_option("-k", "--subcluster-bin-width",
                      dest="width_sbin", type="int",
                      help="bin width for subcluster size\
                      [default=%default].")

    parser.add_option("-o", "--output-method",
                      dest="output_method", type="choice",
                      choices=("append", "seperate"),
                      help="defines whether the spike-ins should be appended\
                      to the original table or seperately [default=%default].")

    parser.add_option("-p", "--shuffle-column-suffix",
                      dest="shuffle_suffix", type="string",
                      help="the suffix of the columns which are to be shuffled\
                      [default=%default].")

    parser.add_option("-q", "--keep-column-suffix",
                      dest="keep_suffix", type="string",
                      help="a list of suffixes for the columns which are to be\
                      keep along with the shuffled columns[default=%default].")

    parser.add_option("-z", "--spike-regex",
                      dest="spike_regex", type="string", action="append",
                      help="a regex to identify tracks for shuffling\
                      [default=%default].")

    parser.set_defaults(
        design_file=None,
        output_method="seperate",
        difference="relative",
        spike_type="row",
        genome_file=None,
        min_cbin=None,
        max_cbin=None,
        width_cbin=None,
        min_ibin=None,
        max_ibin=None,
        width_ibin=None,
        id_columns=None,
        max_spike=100,
        min_spike=None,
        iterations=1,
        cluster_max_distance=100,
        cluster_min_size=10,
        min_sbin=1,
        max_sbin=9,
        width_sbin=2,
        id_column=None,
        shuffle_suffix=None,
        keep_suffix=None,
        spike_regex=".*")

    # add common options (-h/--help, ...) and parse command line
    (options, args) = E.Start(parser, argv=argv, add_output_options=True)

    df = pd.read_table(sys.stdin, sep="\t")

    if options.spike_type == "cluster":
        # JJ- THIS ASSUMES THESE COLUMNS EXIST, WHICH THE SUBSEQUENT SCRIPT DOES NOT
        # THEREFORE MOVED
        df_sort = df.sort(columns=["contig", "position"])
        df_sort.set_index(df.index, inplace=True)
        # print df_sort.head()
    else:
        df_sort = df

    if not options.design_file:
        raise ValueError("a design table is required.")

    if options.spike_type == "cluster":
        if options.max_sbin > options.cluster_min_size:
            raise ValueError("maximum size of subscluster bin: %s\
            is greater than minimum size of cluster: %s" % (
                options.max_sbin, options.cluster_min_size))
        df_columns = set(df.columns.values.tolist())
        msg = "cluster analysis requires columns named 'contig' and\
        'position' in the dataframe"
        assert "contig" in df_columns and "position" in df_columns, msg

        msg2 = '''the minimum cluster size (%s) must be larger than the maximum
        size of subcluster to be shuffled (%s)''' % (options.cluster_min_size,
                                                     options.max_sbin)
        assert options.max_sbin < options.cluster_min_size, msg2

        df_sort = df.sort(columns=["contig", "position"])
        df_sort.set_index(df.index, inplace=True)

    if not options.id_column:
        if options.output_method == "append" and options.spike_type == "row":
            raise ValueError("id column must be specified to append rows")
        elif options.output_method != "append":
            options.id_column = "id"

    if not options.min_spike:
        options.min_spike = options.max_spike

    design_table = pd.read_table(options.design_file, sep="\t")

    # get dictionaries to map group members to column names

    groups,  g_to_keep_tracks, g_to_spike_tracks = groupMappers(
        design_table, options.spike_regex,
        options.shuffle_suffix, options.keep_suffix)

    change_bins = np.arange(options.min_cbin, options.max_cbin,
                            options.width_cbin)
    # JJ
    E.info("Column boundaries are: %s" % str(change_bins))

    initial_bins = np.arange(options.min_ibin, options.max_ibin,
                             options.width_ibin)

    # JJ
    E.info(str(initial_bins) % "Row boundaries are: %s")

    if options.spike_type == "cluster":
        E.info("looking for clusters...")
        clusters_dict = findClusters(df_sort, options.cluster_max_distance,
                                     options.cluster_min_size,
                                     g_to_spike_tracks, groups)
        if len(clusters_dict) == 0:
            raise Exception("no clusters were found, check parameters")

        E.info("repeatedly shuffling subcluster regions...")
        output_indices, counts = shuffleCluster(
            initial_bins, change_bins, g_to_spike_tracks, groups,
            options.difference, options.max_spike,
            options.iterations, clusters_dict,
            options.max_sbin, options.min_sbin, options.width_sbin)

    elif options.spike_type == "row":
        E.info("repeatedly shuffling rows...")
        output_indices, counts = shuffleRows(
            df, initial_bins, change_bins, g_to_spike_tracks, groups,
            options.difference, options.max_spike, options.iterations)

    filled_bins = thresholdBins(output_indices, counts, options.min_spike)
    if len(filled_bins) == 0:
        raise Exception("No bins contained a sufficient number of spike-ins")

    E.info("outputting spike-ins...")
    outputSpikes(df_sort, options.id_column, filled_bins, g_to_keep_tracks,
                 groups, method=options.output_method,
                 spike_type=options.spike_type,
                 min_cbin=options.min_cbin, width_cbin=options.width_cbin,
                 min_ibin=options.min_ibin, width_ibin=options.width_ibin,
                 min_sbin=options.min_sbin, width_sbin=options.width_sbin)

    # write footer and output benchmark information.
    E.Stop()
dataset/ETHPy150Open CGATOxford/cgat/scripts/data2spike.py/main/swapped operands of `%`