#!/usr/bin/env python3 description = """ Plot the number of tasks for each depth level and each type of task. Usage: ./plot_task_level.py task_level_0.txt or ./plot_task_level.py task_level_*_0.txt """ import pandas as pd import numpy as np import matplotlib.pyplot as plt import sys import argparse from os import path def parse_args(): """ Parses command line arguments. Returns ------- args: Namespace Namespace returned by argparse.ArgumentParser.parse_args() containing all arguments files: List of files parsed from the command line. Raises ------ FileNotFoundError If the required filename provided on the command line doesn't exist """ # description is string at the top of this file. parser = argparse.ArgumentParser(description=description) parser.add_argument( "-c", "--count", dest="count_levels", help="count on how many different levels the tasks can be found" " and add it to the label", action="store_true", ) parser.add_argument( "-d", "--displace", dest="displace", help="attempt to displace overlapping point on the plot a bit" " and try to make them visible", action="store_true", ) parser.add_argument( "file", type=str, nargs="+", help="Required file name(s) of .txt file(s) of the task levels " "generated by swift.", ) args = parser.parse_args() files = args.file for f in files: if not path.exists(f): raise FileNotFoundError("File not found:'" + f + "'") return args, files def read_data(files): """ Reads in data from the .txt file. Parameters ---------- files: list list of filenames to be read from Returns ------- data: pandas dataframe dataframe containing read in data """ # Column names names = ["type", "subtype", "depth", "count"] alldata = None for f in files: # read file data = pd.read_csv(f, sep=" ", comment="#", names=names) if alldata is None: alldata = data else: concat = pd.concat([alldata, data]) alldata = concat.groupby(["type", "subtype", "depth"], as_index=False).sum() return data def get_discrete_cmap(nentries): """ Returns a discrete colormap. Parameters ---------- nentries: int how many entries you want for your colormap Returns ------- cmap: list list of colors Raises ------ IndexError: When you want more entries than there are available. Current maximum is 21. """ fullcolorlist = [ "red", "green", "blue", "gold", "magenta", "cyan", "lime", "saddlebrown", "darkolivegreen", "cornflowerblue", "orange", "dimgrey", "navajowhite", "darkslategray", "mediumpurple", "lightpink", "mediumseagreen", "maroon", "midnightblue", "silver", "black", ] if nentries >= len(fullcolorlist) - 1: raise IndexError( "I can't handle more than 21 different colors." "Add more manually in get_discrete_cmap() function" ) return fullcolorlist[: nentries + 1] def add_levelcounts(data): """ Adds a column to the data with the counts on how many levels a given task is executed on. Parameters ---------- data: pandas dataframe The dataframe to use Returns ------- data: pandas dataframe the modified dataframe """ # add new column data["nlevels"] = ["1" for _ in range(data.shape[0])] # count on how many levels each task exists for i, (ttype, tsubtype) in enumerate(zip(data["type"], data["subtype"])): istype = data["type"] == ttype issubtype = data["subtype"] == tsubtype isthis = np.logical_and(istype, issubtype) count = np.count_nonzero(isthis) data.at[i, "nlevels"] = str(count) return data def add_displacement(data): """ Add small displacements to the task number counts and try to make them better visible Parameters ---------- data: pandas dataframe the data to be modified Returns ------- data: pandas dataframe the modified data """ # add new column data["yvals"] = data["count"] * 1.0 data["yval_modified"] = False inds = np.arange(0, data.shape[0]) # count on how many levels each task exists for i, (ttype, tsubtype) in enumerate(zip(data["type"], data["subtype"])): if data["yval_modified"][i]: continue istype = data["type"] == ttype issubtype = data["subtype"] == tsubtype isthis = np.logical_and(istype, issubtype) uniques = np.unique(data["count"][isthis]) for u in uniques: occurances = np.count_nonzero(data["count"][isthis] == u) for o in range(occurances): thisind = inds[isthis][o] step = 0.05 * max(int(np.log10(u) + 0.5), 1) * o * (-1) ** o data.at[thisind, "yvals"] += step data.at[thisind, "yval_modified"] = True return data if __name__ == "__main__": args, files = parse_args() data = read_data(files) cmap = get_discrete_cmap(data["depth"].max()) # are we counting the levels? if args.count_levels: data = add_levelcounts(data) # are we displacing the particles on the y axis? if args.displace: data = add_displacement(data) # plot data for i in range(data["depth"].max() + 1): ind = data["depth"] == i label = "depth = {0:d}".format(i) if args.count_levels: xvals = ( data["type"][ind] + "_" + data["subtype"][ind] + "[" + data["nlevels"][ind] + "]" ) else: xvals = data["type"][ind] + "_" + data["subtype"][ind] if args.displace: yvals = data["yvals"][ind] else: yvals = data["count"][ind] c = cmap[i] plt.plot(xvals, yvals, "o", label=label, color=c, alpha=0.7) # modify figure parameters and show it plt.gca().set_yscale("log") plt.xticks(rotation=90) plt.ylabel("Number of Tasks") plt.gcf().subplots_adjust(bottom=0.225) plt.legend() plt.grid() plt.show()