예제 #1
0
def analyse_2d_density(
        exp_list='experiments_completed.txt',
        res_dir='',
        out_dir='',
        source_filter='',
        min_values='3',
        xmetric='throughput',
        ymetric='tcprtt',
        variables='',
        out_name='',
        xmin='0',
        xmax='0',
        ymin='0',
        ymax='0',
        lnames='',
        group_by='aqm',
        replot_only='0',
        pdf_dir='',
        stime='0.0',
        etime='0.0',
        ts_correct='1',
        smoothed='1',
        link_len='0',
        plot_params='',
        plot_script='',
        xstat_index='',
        ystat_index='',
        dupacks='0',
        cum_ackseq='1',
        merge_data='0',
        #sburst='1', eburst='0', test_id_prefix='[0-9]{8}\-[0-9]{6}_experiment_',
        sburst='1',
        eburst='0',
        test_id_prefix='exp_[0-9]{8}\-[0-9]{6}_',
        slowest_only='0',
        query_host=''):
    "2d density / ellipse plot for different experiments"

    test_id_pfx = ''

    check = get_metric_params(xmetric, smoothed, ts_correct)
    if check == None:
        abort('Unknown metric %s specified with xmetric' % xmetric)
    check = get_metric_params(ymetric, smoothed, ts_correct)
    if check == None:
        abort('Unknown metric %s specified with ymetric' % ymetric)

    #if source_filter == '':
    #    abort('Must specify at least one source filter')

    if len(source_filter.split(';')) > 12:
        abort('Cannot have more than 12 filters')

    # XXX more param checking

    # make sure res_dir has valid form (out_dir is handled by extract methods)
    res_dir = valid_dir(res_dir)

    # Initialise source filter data structure
    sfil = SourceFilter(source_filter)

    # read test ids
    experiments = read_experiment_ids(exp_list)

    # get path based on first experiment id
    dir_name = get_first_experiment_path(experiments)

    # if we haven' got the extracted data run extract method(s) first
    if res_dir == '':
        for experiment in experiments:

            (ex_function,
             kwargs) = get_extract_function(xmetric,
                                            link_len,
                                            xstat_index,
                                            sburst=sburst,
                                            eburst=eburst,
                                            slowest_only=slowest_only,
                                            query_host=query_host)

            (dummy, out_files,
             out_groups) = ex_function(test_id=experiment,
                                       out_dir=out_dir,
                                       source_filter=source_filter,
                                       replot_only=replot_only,
                                       ts_correct=ts_correct,
                                       **kwargs)

            (ex_function,
             kwargs) = get_extract_function(ymetric,
                                            link_len,
                                            ystat_index,
                                            sburst=sburst,
                                            eburst=eburst,
                                            slowest_only=slowest_only,
                                            query_host=query_host)

            (dummy, out_files,
             out_groups) = ex_function(test_id=experiment,
                                       out_dir=out_dir,
                                       source_filter=source_filter,
                                       replot_only=replot_only,
                                       ts_correct=ts_correct,
                                       **kwargs)

        if out_dir == '' or out_dir[0] != '/':
            res_dir = dir_name + '/' + out_dir
        else:
            res_dir = out_dir

    else:
        if res_dir[0] != '/':
            res_dir = dir_name + '/' + res_dir

    # make sure we have trailing slash
    res_dir = valid_dir(res_dir)

    if pdf_dir == '':
        pdf_dir = res_dir
    else:
        if pdf_dir[0] != '/':
            pdf_dir = dir_name + '/' + pdf_dir
        pdf_dir = valid_dir(pdf_dir)
        # if pdf_dir specified create if it doesn't exist
        mkdir_p(pdf_dir)

    #
    # build match string from variables
    #

    (match_str, match_str2) = build_match_strings(experiments[0], variables,
                                                  test_id_prefix)

    #
    # filter out the experiments to plot, generate x-axis labels, get test id prefix
    #

    (fil_experiments, test_id_pfx,
     dummy) = filter_experiments(experiments, match_str, match_str2)

    #
    # get groups based on group_by variable
    #

    group_idx = 1
    levels = {}
    groups = []
    leg_names = []
    _experiments = []
    for experiment in fil_experiments:
        level = ''
        add_exp = True
        for g in group_by.split(';'):
            p = experiment.find(g)
            if p > -1:
                s = experiment.find('_', p)
                s += 1
                e = experiment.find('_', s)
                level += g + ':' + experiment[s:e] + ' '
            else:
                add_exp = False
                break

        # remove the final space from the string
        level = level[:-1]

        if add_exp == True:
            _experiments.append(experiment)
            #print('level: ' + level)

            if level not in levels:
                levels[level] = group_idx
                group_idx += 1
                leg_names.append(level)

            if merge_data == '1':
                groups.append(levels[level])
            else:
                for i in range(len(source_filter.split(';'))):
                    groups.append(levels[level])

    fil_experiments = _experiments

    #
    # get metric parameters and list of data files
    #

    # get the metric parameter for both x and y
    x_axis_params = get_metric_params(xmetric, smoothed, ts_correct,
                                      xstat_index, dupacks, cum_ackseq,
                                      slowest_only)
    y_axis_params = get_metric_params(ymetric, smoothed, ts_correct,
                                      ystat_index, dupacks, cum_ackseq,
                                      slowest_only)

    x_ext = x_axis_params[0]
    y_ext = y_axis_params[0]

    # if we merge responders make sure we only use the merged files
    if merge_data == '1':
        # reset source filter so we match the merged file
        sfil.clear()
        sfil = SourceFilter('S_0.0.0.0_0')

    x_files = []
    y_files = []
    for experiment in fil_experiments:
        _x_files = []
        _y_files = []
        _x_ext = x_ext
        _y_ext = y_ext

        _files = get_testid_file_list('', experiment, _x_ext, 'LC_ALL=C sort',
                                      res_dir)
        if merge_data == '1':
            _x_ext += '.all'
            _files = merge_data_files(_files)
        _x_files += _files

        _files = get_testid_file_list('', experiment, _y_ext, 'LC_ALL=C sort',
                                      res_dir)
        if merge_data == '1':
            _y_ext += '.all'
            _files = merge_data_files(_files)
        _y_files += _files

        match_str = '.*_([0-9\.]*_[0-9]*_[0-9\.]*_[0-9]*)[0-9a-z_.]*' + _x_ext
        for f in _x_files:
            #print(f)
            res = re.search(match_str, f)
            #print(res.group(1))
            if res and sfil.is_in(res.group(1)):
                # only add file if enough data points
                rows = int(
                    local('wc -l %s | awk \'{ print $1 }\'' % f, capture=True))
                if rows > int(min_values):
                    x_files.append(f)

        match_str = '.*_([0-9\.]*_[0-9]*_[0-9\.]*_[0-9]*)[0-9a-z_.]*' + _y_ext
        for f in _y_files:
            # print(f)
            res = re.search(match_str, f)
            if res and sfil.is_in(res.group(1)):
                # only add file if enough data points
                rows = int(
                    local('wc -l %s | awk \'{ print $1 }\'' % f, capture=True))
                if rows > int(min_values):
                    y_files.append(f)

    yindexes = [str(x_axis_params[2]), str(y_axis_params[2])]
    yscalers = [str(x_axis_params[3]), str(y_axis_params[3])]
    aggr_flags = [x_axis_params[5], y_axis_params[5]]
    diff_flags = [x_axis_params[6], y_axis_params[6]]

    if lnames != '':
        lnames_arr = lnames.split(';')
        if len(lnames_arr) != len(leg_names):
            abort(
                'Number of legend names must be qual to the number of source filters'
            )
        leg_names = lnames_arr

    print(x_files)
    print(y_files)
    print(groups)
    print(leg_names)

    #
    # pass the data files and auxilary info to plot function
    #

    if out_name != '':
        oprefix = out_name + '_' + test_id_pfx + '_' + xmetric + '_' + ymetric
    else:
        oprefix = test_id_pfx + '_' + xmetric + '_' + ymetric
    title = oprefix

    plot_2d_density(title, x_files, y_files, x_axis_params[1],
                    y_axis_params[1], yindexes, yscalers, 'pdf', oprefix,
                    pdf_dir, x_axis_params[4], y_axis_params[4], aggr_flags,
                    diff_flags, xmin, xmax, ymin, ymax, stime, etime, groups,
                    leg_names, plot_params, plot_script)

    # done
    puts('\n[MAIN] COMPLETED analyse_2d_density %s \n' % test_id_pfx)
예제 #2
0
def analyse_cmpexp(
        exp_list='experiments_completed.txt',
        res_dir='',
        out_dir='',
        source_filter='',
        min_values='3',
        omit_const='0',
        metric='throughput',
        ptype='box',
        variables='',
        out_name='',
        ymin='0',
        ymax='0',
        lnames='',
        group_by_prefix='0',
        omit_const_xlab_vars='0',
        replot_only='0',
        pdf_dir='',
        stime='0.0',
        etime='0.0',
        ts_correct='1',
        smoothed='1',
        link_len='0',
        plot_params='',
        plot_script='',
        stat_index='',
        dupacks='0',
        cum_ackseq='1',
        merge_data='0',
        sburst='1',
        #eburst='0', test_id_prefix='[0-9]{8}\-[0-9]{6}_experiment_',
        eburst='0',
        test_id_prefix='exp_[0-9]{8}\-[0-9]{6}_',
        slowest_only='0',
        res_time_mode='0',
        query_host=''):
    "Compare metrics for different experiments"

    if ptype != 'box' and ptype != 'mean' and ptype != 'median':
        abort('ptype must be either box, mean or median')

    check = get_metric_params(metric, smoothed, ts_correct)
    if check == None:
        abort('Unknown metric %s specified' % metric)

    if source_filter == '':
        abort('Must specify at least one source filter')

    if len(source_filter.split(';')) > 12:
        abort('Cannot have more than 12 filters')

    # prevent wrong use of res_time_mode
    if metric != 'restime' and res_time_mode != '0':
        res_time_mode = '0'
    if ptype == 'box' and res_time_mode == '2':
        res_time_mode = '0'

    # XXX more param checking

    # Initialise source filter data structure
    sfil = SourceFilter(source_filter)

    # read test ids
    experiments = read_experiment_ids(exp_list)

    # get path based on first experiment id
    dir_name = get_first_experiment_path(experiments)

    # if we haven' got the extracted data run extract method(s) first
    if res_dir == '':
        for experiment in experiments:

            (ex_function,
             kwargs) = get_extract_function(metric,
                                            link_len,
                                            stat_index,
                                            sburst=sburst,
                                            eburst=eburst,
                                            slowest_only=slowest_only,
                                            query_host=query_host)

            (dummy, out_files,
             out_groups) = ex_function(test_id=experiment,
                                       out_dir=out_dir,
                                       source_filter=source_filter,
                                       replot_only=replot_only,
                                       ts_correct=ts_correct,
                                       **kwargs)

        if out_dir == '' or out_dir[0] != '/':
            res_dir = dir_name + '/' + out_dir
        else:
            res_dir = out_dir
    else:
        if res_dir[0] != '/':
            res_dir = dir_name + '/' + res_dir

    # make sure we have trailing slash
    res_dir = valid_dir(res_dir)

    if pdf_dir == '':
        pdf_dir = res_dir
    else:
        if pdf_dir[0] != '/':
            pdf_dir = dir_name + '/' + pdf_dir
        pdf_dir = valid_dir(pdf_dir)
        # if pdf_dir specified create if it doesn't exist
        mkdir_p(pdf_dir)

    #
    # build match string from variables
    #

    (match_str, match_str2) = build_match_strings(experiments[0], variables,
                                                  test_id_prefix)

    #
    # filter out the experiments to plot, generate x-axis labels, get test id prefix
    #

    (fil_experiments, test_id_pfx,
     xlabs) = filter_experiments(experiments, match_str, match_str2)

    #
    # get out data files based on filtered experiment list and source_filter
    #

    (ext, ylab, yindex, yscaler, sep, aggr,
     diff) = get_metric_params(metric, smoothed, ts_correct, stat_index,
                               dupacks, cum_ackseq, slowest_only)

    if res_time_mode == '1':
        plot_params += ' NOMINAL_RES_TIME="1"'
    if res_time_mode == '2':
        if ptype == 'median':
            ylab = 'Median resp time / nominal resp time'
        elif ptype == 'mean':
            ylab = 'Mean resp time / nominal resp time'
        plot_params += ' RATIO_RES_TIME="1"'

    leg_names = source_filter.split(';')

    # if we merge responders make sure we only use the merged files
    if merge_data == '1':
        # set label to indicate merged data
        leg_names = ['Merged data']
        # reset source filter so we match the merged file
        sfil.clear()
        source_filter = 'S_0.0.0.0_0'
        sfil = SourceFilter(source_filter)

    file_names = []
    for experiment in fil_experiments:
        out_files = {}
        _ext = ext

        files = get_testid_file_list('', experiment, '%s' % _ext,
                                     'LC_ALL=C sort', res_dir)
        if merge_data == '1':
            # change extension
            _ext += '.all'
            files = merge_data_files(files)

        #print(files)
        match_str = '.*_([0-9\.]*_[0-9]*_[0-9\.]*_[0-9]*)[0-9a-z_.]*' + _ext
        for f in files:
            # print(f)
            res = re.search(match_str, f)
            #print(res.group(1))
            if res and sfil.is_in(res.group(1)):
                # only add file if enough data points
                rows = int(
                    local('wc -l %s | awk \'{ print $1 }\'' % f, capture=True))
                if rows > int(min_values):
                    out_files[res.group(1)] = f

        #print(out_files)
        #print(leg_names)
        if len(out_files) < len(leg_names):
            abort(
                'No data files for some of the source filters for experiment %s'
                % experiment)

        sorted_files = sort_by_flowkeys(out_files, source_filter)

        for name, file_name in sorted_files:
            file_names.append(file_name)

    if group_by_prefix == '1':
        # group by test prefix (and flow)

        # first, get all test id prefixes
        test_id_pfxs = {}
        for experiment in fil_experiments:
            res = re.search(match_str2, experiment)
            if res:
                test_id_pfxs[res.group(1)] = 1

        # second, sort files so that same parameter combinations for different
        # prefixes are together
        # if we have multiple prefixes, create legend entry for each
        # prefix+flow combination
        _file_names = [''] * len(file_names)
        _leg_names = []
        pfx_cnt = len(test_id_pfxs)
        i = 0
        j = -1
        last_pfx = ''
        for name in file_names:
            for p in test_id_pfxs:
                if name.find(p) > -1:
                    curr_pfx = p
                    break

            if curr_pfx != last_pfx:
                i = 0
                j += 1
                for l in leg_names:
                    _leg_names.append(curr_pfx + '-' + l)

            _file_names[i * pfx_cnt + j] = name

            i += 1
            last_pfx = curr_pfx

        file_names = _file_names
        leg_names = _leg_names

        # remove duplicates in the x-axis labels
        xlabs = list(set(xlabs))

    if lnames != '':
        lnames_arr = lnames.split(';')
        if len(lnames_arr) != len(leg_names):
            abort(
                'Number of legend names must be qual to the number of source filters'
            )
        leg_names = lnames_arr

    # filter out unchanged variables in the x labels (need at least 2 labels)
    if omit_const_xlab_vars == '1' and len(xlabs) > 1:

        xlabs_arrs = {}
        xlabs_changed = {}

        for i in range(len(xlabs)):
            xlabs_arrs[i] = xlabs[i].split('\n')

        for i in range(len(xlabs_arrs[0])):
            changed = False
            xlab_var = xlabs_arrs[0][i]
            for j in range(1, len(xlabs)):
                if xlabs_arrs[j][i] != xlab_var:
                    changed = True
                    break

            xlabs_changed[i] = changed

        for i in range(len(xlabs)):
            tmp = []
            for j in range(len(xlabs_arrs[i])):
                if xlabs_changed[j]:
                    tmp.append(xlabs_arrs[i][j].replace('_', ' ', 1))

            xlabs[i] = '\n'.join(tmp)

    print(leg_names)
    print(file_names)

    #
    # pass the data files and auxilary info to plot function
    #

    if out_name != '':
        oprefix = out_name + '_' + test_id_pfx + '_' + metric + '_' + ptype
    else:
        oprefix = test_id_pfx + '_' + metric + '_' + ptype
    title = oprefix

    plot_cmpexp(title, file_names, xlabs, ylab, yindex, yscaler, 'pdf',
                oprefix, pdf_dir, sep, aggr, diff, omit_const, ptype, ymin,
                ymax, leg_names, stime, etime, plot_params, plot_script)

    # done
    puts('\n[MAIN] COMPLETED analyse_cmpexp %s \n' % test_id_pfx)
예제 #3
0
def plot_dash_goodput(title='',
                      files={},
                      groups={},
                      ylab='',
                      otype='',
                      oprefix='',
                      pdf_dir='',
                      sep=' ',
                      ymin=0,
                      ymax=0,
                      lnames='',
                      stime='0.0',
                      etime='0.0',
                      plot_params='',
                      plot_script=''):

    file_names = []
    leg_names = []

    sorted_files = sorted(files.items())
    sorted_files = sort_by_group_id(sorted_files, groups)
    #print(sorted_files)

    for name, file_name in sorted_files:
        leg_names.append(name)
        file_names.append(file_name)

    if lnames != '':
        lname_arr = lnames.split(';')
        if len(lname_arr) != len(leg_names):
            abort(
                'Number of legend names must be the same as the number of flows'
            )
        else:
            leg_names = lname_arr

    # get the directory name here if not specified
    if pdf_dir == '':
        pdf_dir = os.path.dirname(file_names[0]) + '/'
    else:
        pdf_dir = valid_dir(pdf_dir)
        # if not absolute dir, make it relative to experiment_dir
        # assume experiment dir is part before first slash
        if pdf_dir != '/':
            pdf_dir = file_names[0].split('/')[0] + '/' + pdf_dir
        # if pdf_dir specified create if it doesn't exist
        mkdir_p(pdf_dir)

    if plot_script == '':
        plot_script = 'R CMD BATCH --vanilla %s/plot_dash_goodput.R' % \
                      config.TPCONF_script_path

    # interface between this code and the plot function are environment variables
    # the following variables are passed to plot function:
    # TC_TITLE:  character string that is plotted over the graph
    # TC_FNAMES: comma-separated list of file names (each file contains one date series,
    #         e.g. data for one flow). The format of each file is CSV-style, but the
    #         separator does not have to be a comma (can be set with SEP). The first
    #         column contains the timestamps. The second, third etc. columns contain
    #         data, but only one of these columns will be plotted (set with YINDEX).
    # TC_LNAMES: comma-separated list of legend names. this list has the same length
    #         as FNAMES and each entry corresponds to data in file name with the
    #         same index in FNAMES
    # TC_YLAB:   y-axis label character string
    # TC_SEP:    column separator used in data file
    # TC_OTYPE:  type of output graph (default is 'pdf')
    # TC_OPREFIX: the prefix (first part) of the graph file name
    # TC_ODIR:   directory where output files, e.g. pdfs are placed
    # TC_YMIN:   minimum value on y-axis (for zooming in), default is 0
    # TC_YMAX:   maximum value on y-axis (for zooming in), default is 0 meaning the
    #         maximum value is determined from the data
    # TC_STIME:  start time on x-axis (for zooming in), default is 0.0 meaning the start
    #         of an experiment
    # TC_ETIME:  end time on x-axis (for zooming in), default is 0.0 meaning the end of an
    #         experiment a determined from the data

    #local('which R')
    local(
        'TC_TITLE="%s" TC_FNAMES="%s" TC_LNAMES="%s" TC_YLAB="%s" TC_SEP="%s" TC_OTYPE="%s" '
        'TC_OPREFIX="%s" TC_ODIR="%s" TC_YMIN="%s" TC_YMAX="%s" TC_STIME="%s" TC_ETIME="%s" %s '
        '%s %s%s_plot_dash_goodput.Rout' %
        (title, ','.join(file_names), ','.join(leg_names), ylab, sep, otype,
         oprefix, pdf_dir, ymin, ymax, stime, etime, plot_params, plot_script,
         pdf_dir, oprefix))

    if config.TPCONF_debug_level == 0:
        local('rm -f %s%s_plot_dash_goodput.Rout' % (pdf_dir, oprefix))
예제 #4
0
def plot_incast_ACK_series(title='',
                           files={},
                           ylab='',
                           yindex=2,
                           yscaler=1.0,
                           otype='',
                           oprefix='',
                           pdf_dir='',
                           sep=' ',
                           aggr='',
                           omit_const='0',
                           ymin=0,
                           ymax=0,
                           lnames='',
                           stime='0.0',
                           etime='0.0',
                           groups={},
                           sort_flowkey='1',
                           burst_sep='1.0',
                           sburst=1,
                           plot_params='',
                           plot_script='',
                           source_filter=''):

    file_names = []
    leg_names = []
    _groups = []

    # Pick up case where the user has supplied a number of legend names
    # that doesn't match the number of distinct trials (as opposed to the
    # number of bursts detected within each trial)
    if lnames != '':
        if len(lnames.split(";")) != len(files.keys()):
            abort(
                'Number of legend names must be the same as the number of flows'
            )

    if sort_flowkey == '1':
        sorted_files = sort_by_flowkeys(files, source_filter)
    else:
        sorted_files = files.items()

    #print("MAIN: sorted_files: %s" % sorted_files)

    # sort by group id
    sorted_files = sort_by_group_id2(sorted_files, groups)

    for name, file_name in sorted_files:
        # Create a sequence of burst-specific legend names,
        # derived from the flowID-based legend name.
        # Keep the .R code happy by creating a groups entry
        # for each burst-specific file.
        for burst_index in range(len(file_name)):
            leg_names.append(name + "%" + str(burst_index + sburst))
            file_names.append(file_name[burst_index])
            _groups.append(groups[file_name[burst_index]])

    if lnames != '':
        # Create a sequence of burst-specific legend names,
        # derived from the per-trial legend names provided by user.
        lname_arr_orig = lnames.split(';')
        lname_arr = []
        i = 0
        for name, file_name in sorted_files:
            for burst_index in range(len(file_name)):
                lname_arr.append(lname_arr_orig[i] + "%" +
                                 str(burst_index + sburst))
            i += 1

        if len(lname_arr) != len(leg_names):
            abort(
                'Number of legend names must be the same as the number of flows'
            )
        else:
            leg_names = lname_arr

    # get the directory name here if not specified
    if pdf_dir == '':
        pdf_dir = os.path.dirname(file_names[0]) + '/'
    else:
        pdf_dir = valid_dir(pdf_dir)
        # if no absolute path make it relative to experiment_dir
        # assume experiment dir is part before first slash
        if pdf_dir[0] != '/':
            pdf_dir = file_names[0].split('/')[0] + '/' + pdf_dir
        # if pdf_dir specified create if it doesn't exist
        mkdir_p(pdf_dir)

    if plot_script == '':
        plot_script = 'R CMD BATCH --vanilla %s/plot_bursts.R' % \
                       config.TPCONF_script_path

    # for a description of parameters see plot_time_series above
    #local('which R')
    local(
        'TC_TITLE="%s" TC_FNAMES="%s" TC_LNAMES="%s" TC_YLAB="%s" TC_YINDEX="%d" TC_YSCALER="%f" '
        'TC_SEP="%s" TC_OTYPE="%s" TC_OPREFIX="%s" TC_ODIR="%s" TC_AGGR="%s" TC_OMIT_CONST="%s" '
        'TC_YMIN="%s" TC_YMAX="%s" TC_STIME="%s" TC_ETIME="%s" TC_GROUPS="%s" %s '
        'TC_BURST_SEP=1 '
        '%s %s%s_plot_bursts.Rout' %
        (title, ','.join(file_names), ','.join(leg_names), ylab, yindex,
         yscaler, sep, otype, oprefix, pdf_dir,
         aggr, omit_const, ymin, ymax, stime, etime, ','.join(map(
             str, _groups)), plot_params, plot_script, pdf_dir, oprefix))

    if config.TPCONF_debug_level == 0:
        local('rm -f %s%s_plot_bursts.Rout' % (pdf_dir, oprefix))
예제 #5
0
def plot_time_series(title='',
                     files={},
                     ylab='',
                     yindex=2,
                     yscaler=1.0,
                     otype='',
                     oprefix='',
                     pdf_dir='',
                     sep=' ',
                     aggr='',
                     omit_const='0',
                     ymin=0,
                     ymax=0,
                     lnames='',
                     stime='0.0',
                     etime='0.0',
                     groups={},
                     sort_flowkey='1',
                     boxplot='',
                     plot_params='',
                     plot_script='',
                     source_filter=''):

    file_names = []
    leg_names = []
    _groups = []

    if sort_flowkey == '1':
        sorted_files = sort_by_flowkeys(files, source_filter)
    else:
        sorted_files = files.items()

    sorted_files = sort_by_group_id(sorted_files, groups)

    for name, file_name in sorted_files:
        leg_names.append(name)
        file_names.append(file_name)
        _groups.append(groups[file_name])

    if lnames != '':
        lname_arr = lnames.split(';')
        if boxplot == '0' and len(lname_arr) != len(leg_names):
            abort(
                'Number of legend names must be the same as the number of flows'
            )
        else:
            leg_names = lname_arr

    # get the directory name here if not specified
    if pdf_dir == '':
        pdf_dir = os.path.dirname(file_names[0]) + '/'
    else:
        pdf_dir = valid_dir(pdf_dir)
        # if not absolute dir, make it relative to experiment_dir
        # assume experiment dir is part before first slash
        if pdf_dir[0] != '/':
            pdf_dir = file_names[0].split('/')[0] + '/' + pdf_dir
        # if pdf_dir specified create if it doesn't exist
        mkdir_p(pdf_dir)

    if plot_script == '':
        plot_script = 'R CMD BATCH --vanilla %s/plot_time_series.R' % \
                      config.TPCONF_script_path

    # interface between this code and the plot function are environment variables
    # the following variables are passed to plot function:
    # TC_TITLE:  character string that is plotted over the graph
    # TC_FNAMES: comma-separated list of file names (each file contains one date series,
    #         e.g. data for one flow). The format of each file is CSV-style, but the
    #         separator does not have to be a comma (can be set with SEP). The first
    #         column contains the timestamps. The second, third etc. columns contain
    #         data, but only one of these columns will be plotted (set with YINDEX).
    # TC_LNAMES: comma-separated list of legend names. this list has the same length
    #         as FNAMES and each entry corresponds to data in file name with the
    #         same index in FNAMES
    # TC_YLAB:   y-axis label character string
    # TC_YINDEX: index of data column in file to plot on y-axis (file can have more than
    #         one data column)
    # TC_YSCALER: factor which is multiplied with each data value before plotting
    # TC_SEP:    column separator used in data file
    # TC_OTYPE:  type of output graph (default is 'pdf')
    # TC_OPREFIX: the prefix (first part) of the graph file name
    # TC_ODIR:   directory where output files, e.g. pdfs are placed
    # TC_AGGR:   set to '1' means data is aggregated over time intervals, more specifically
    #         the data is summed over the time intervals (used to determine throughput
    #         over time windows based on packet lengths)
    #         set to '0' means plot data as is
    # TC_OMIT_CONST: '0' don't omit anything,
    #             '1' omit any data series from plot that are 100% constant
    # TC_YMIN:   minimum value on y-axis (for zooming in), default is 0
    # TC_YMAX:   maximum value on y-axis (for zooming in), default is 0 meaning the
    #         maximum value is determined from the data
    # TC_STIME:  start time on x-axis (for zooming in), default is 0.0 meaning the start
    #         of an experiment
    # TC_ETIME:  end time on x-axis (for zooming in), default is 0.0 meaning the end of an
    #         experiment a determined from the data
    # TC_GROUPS: comma-separated list of group IDs (integer numbers). This list has
    #         the same length as FNAMES. If data from different experiments is plotted,
    #         each experiment will be assigned a different number and these are passed
    #         via GROUPS. This allows the plotting function to determine which data
    #         series are (or are not) from the same experiment, so that results
    #         from different experiments, that started at different times, can be
    #         plotted in the same graph.
    # TC_BOXPL:  '0' plot each point on time axis
    #         '1' plot a boxplot over all data points from all data seres for each
    #         distinct timestamp (instead of a point for each a data series)

    #local('which R')
    local(
        'TC_TITLE="%s" TC_FNAMES="%s" TC_LNAMES="%s" TC_YLAB="%s" TC_YINDEX="%d" TC_YSCALER="%f" '
        'TC_SEP="%s" TC_OTYPE="%s" TC_OPREFIX="%s" TC_ODIR="%s" TC_AGGR="%s" TC_OMIT_CONST="%s" '
        'TC_YMIN="%s" TC_YMAX="%s" TC_STIME="%s" TC_ETIME="%s" TC_GROUPS="%s" TC_BOXPL="%s" %s '
        '%s %s%s_plot_time_series.Rout' %
        (title, ','.join(file_names), ','.join(leg_names), ylab, yindex,
         yscaler, sep, otype, oprefix, pdf_dir, aggr, omit_const, ymin, ymax,
         stime, etime, ','.join(map(str, _groups)), boxplot, plot_params,
         plot_script, pdf_dir, oprefix))

    if config.TPCONF_debug_level == 0:
        local('rm -f %s%s_plot_time_series.Rout' % (pdf_dir, oprefix))