diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-02-12 18:32:19 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-02-12 18:32:19 -0500 |
commit | 4a1c47705868ce2c48f1c57a7190d435e16c3ce8 (patch) | |
tree | 9d3540dc0203d1f5508ee2919c8d68bfff97a861 /plot_exps.py | |
parent | b2fa65ecfe14bb9377fbd8afa5f457a07472b6fb (diff) |
Optimized plot script to handle different directory structures.
Diffstat (limited to 'plot_exps.py')
-rwxr-xr-x | plot_exps.py | 154 |
1 files changed, 58 insertions, 96 deletions
diff --git a/plot_exps.py b/plot_exps.py index 39529bd..bb6a707 100755 --- a/plot_exps.py +++ b/plot_exps.py | |||
@@ -1,15 +1,16 @@ | |||
1 | #!/usr/bin/env python | 1 | #!/usr/bin/env python |
2 | from __future__ import print_function | 2 | from __future__ import print_function |
3 | 3 | ||
4 | import matplotlib.pyplot as plot | ||
4 | import os | 5 | import os |
5 | import shutil as sh | 6 | import shutil as sh |
6 | import sys | 7 | import sys |
8 | from collections import namedtuple | ||
7 | from optparse import OptionParser | 9 | from optparse import OptionParser |
10 | from parse.col_map import ColMap | ||
8 | from parse.dir_map import DirMap | 11 | from parse.dir_map import DirMap |
9 | from parse.tuple_table import ReducedTupleTable | 12 | from parse.tuple_table import ReducedTupleTable |
10 | from parse.col_map import ColMap | 13 | from plot.style import StyleMap |
11 | from collections import namedtuple,defaultdict | ||
12 | import matplotlib.pyplot as plot | ||
13 | 14 | ||
14 | def parse_args(): | 15 | def parse_args(): |
15 | parser = OptionParser("usage: %prog [options] [csv_dir]...") | 16 | parser = OptionParser("usage: %prog [options] [csv_dir]...") |
@@ -21,15 +22,16 @@ def parse_args(): | |||
21 | 22 | ||
22 | return parser.parse_args() | 23 | return parser.parse_args() |
23 | 24 | ||
24 | |||
25 | ExpDetails = namedtuple('ExpDetails', ['variable', 'value', 'title', 'out']) | 25 | ExpDetails = namedtuple('ExpDetails', ['variable', 'value', 'title', 'out']) |
26 | OUT_FORMAT = 'pdf' | 26 | OUT_FORMAT = 'pdf' |
27 | 27 | ||
28 | def get_details(path): | 28 | def get_details(path, out_dir): |
29 | '''Decode a @path into details about a single experiment.''' | ||
29 | out = "_".join(path) if path else "plot" | 30 | out = "_".join(path) if path else "plot" |
31 | out = "%s/%s.%s" % (out_dir, out, OUT_FORMAT) | ||
30 | 32 | ||
31 | value = path.pop() if path else None | 33 | value = path.pop(0) if path else None |
32 | variable = path.pop() if path else None | 34 | variable = path.pop(0) if path else None |
33 | 35 | ||
34 | title = value.capitalize() if value else "" | 36 | title = value.capitalize() if value else "" |
35 | title += " by %s" % variable if variable else "" | 37 | title += " by %s" % variable if variable else "" |
@@ -37,105 +39,70 @@ def get_details(path): | |||
37 | 39 | ||
38 | return ExpDetails(variable, value, title, out) | 40 | return ExpDetails(variable, value, title, out) |
39 | 41 | ||
42 | def plot_by_variable(plot_node, col_map, details): | ||
43 | '''Plot each .csv files under @plot_node as a line on a shared plot.''' | ||
40 | 44 | ||
45 | # Generate mapping of (column)=>(line property to vary) for consistently | ||
46 | # formatted plots | ||
47 | columns = list(col_map.columns()) | ||
48 | if details.variable and details.variable in columns: | ||
49 | columns.remove(details.variable) | ||
50 | style_map = StyleMap(columns, col_map.get_values()) | ||
41 | 51 | ||
42 | class StyleMap(object): | 52 | figure = plot.figure() |
43 | COLORS = list('bgrcmyk') | 53 | axes = figure.add_subplot(111) |
44 | LINES = ['-', ':', '--'] | ||
45 | MARKERS = list('.,ov^<>1234sp*hH+xDd|_') | ||
46 | ORDER = [MARKERS, COLORS, LINES] | ||
47 | DEFAULT = ["k", "-", "k"] | ||
48 | |||
49 | def __init__(self, col_list, col_values): | ||
50 | self.prop_map = dict(zip(col_list, StyleMap.ORDER)) | ||
51 | |||
52 | # Store 1 style per value | ||
53 | self.value_map = defaultdict(dict) | ||
54 | for column, styles in self.prop_map.iteritems(): | ||
55 | value_styles = self.value_map[column] | ||
56 | for value in sorted(col_values[column]): | ||
57 | value_styles[value] = styles.pop(0) | ||
58 | styles += [value_styles[value]] | ||
59 | |||
60 | def get_style(self, kv): | ||
61 | style = '' | ||
62 | for k,v in kv.iteritems(): | ||
63 | if k in self.value_map: | ||
64 | style += self.value_map[k][v] | ||
65 | return style | ||
66 | |||
67 | def get_key(self): | ||
68 | key = [] | ||
69 | for column, properties in self.prop_map.iteritems(): | ||
70 | idx = StyleMap.ORDER.index(properties) | ||
71 | prop_string = StyleMap.DEFAULT[idx] + "%s" | ||
72 | for value, prop in self.value_map[column].iteritems(): | ||
73 | style = plot.plot([],[], prop_string%prop)[0] | ||
74 | key += [(style, "%s:%s" % (column, value))] | ||
75 | return sorted(key, key=lambda x:x[1]) | ||
76 | |||
77 | def plot_by_variable(dir_map, col_map, out_dir, force): | ||
78 | num_plots = 0 | ||
79 | id = 0 | ||
80 | for _,_ in dir_map.leafs(1): | ||
81 | num_plots += 1 | ||
82 | sys.stderr.write("Plotting by variable...") | ||
83 | |||
84 | for plot_path, plot_node in dir_map.leafs(1): | ||
85 | id += 1 | ||
86 | details = get_details(plot_path) | ||
87 | out_fname = "%s/%s.%s" % (out_dir, details.out, OUT_FORMAT) | ||
88 | if os.path.exists(out_fname) and not force: | ||
89 | continue | ||
90 | |||
91 | # Kinda bad... | ||
92 | first_csv = plot_node.children.keys()[0] | ||
93 | first_config = ColMap.decode(first_csv[:first_csv.index('.csv')]) | ||
94 | columns = filter(lambda c: c in first_config, col_map.columns()) | ||
95 | |||
96 | style_map = StyleMap(columns, col_map.get_values()) | ||
97 | |||
98 | figure = plot.figure() | ||
99 | axes = figure.add_subplot(111) | ||
100 | 54 | ||
101 | for line_path, line_node in plot_node.children.iteritems(): | 55 | # Create a line for each file node |
102 | encoded = line_path[:line_path.index(".csv")] | 56 | for line_path, line_node in plot_node.children.iteritems(): |
103 | config = ColMap.decode(encoded) | 57 | # Create line style to match this configuration |
104 | style = style_map.get_style(config) | 58 | encoded = line_path[:line_path.index(".csv")] |
59 | config = ColMap.decode(encoded) | ||
60 | style = style_map.get_style(config) | ||
105 | 61 | ||
106 | values = sorted(line_node.values, key=lambda tup: tup[0]) | 62 | values = sorted(line_node.values, key=lambda tup: tup[0]) |
107 | xvalues, yvalues = zip(*values) | 63 | xvalues, yvalues = zip(*values) |
108 | 64 | ||
109 | plot.plot(xvalues, yvalues, style) | 65 | plot.plot(xvalues, yvalues, style.fmt()) |
110 | 66 | ||
111 | lines, labels = zip(*style_map.get_key()) | 67 | axes.set_title(details.title) |
112 | 68 | ||
113 | axes.legend(tuple(lines), tuple(labels), prop={'size':10}) | 69 | lines, labels = zip(*style_map.get_key()) |
114 | axes.set_ylabel(details.value) | 70 | axes.legend(tuple(lines), tuple(labels), prop={'size':10}) |
115 | axes.set_xlabel(details.variable) | ||
116 | axes.set_xlim(0, axes.get_xlim()[1] + 1) | ||
117 | axes.set_ylim(0, axes.get_ylim()[1] + 1) | ||
118 | 71 | ||
119 | axes.set_title(details.title) | 72 | axes.set_ylabel(details.value) |
73 | axes.set_xlabel(details.variable) | ||
74 | axes.set_xlim(0, axes.get_xlim()[1] + 1) | ||
75 | axes.set_ylim(0, axes.get_ylim()[1] + 1) | ||
120 | 76 | ||
121 | plot.savefig(out_fname, format=OUT_FORMAT) | 77 | plot.savefig(details.out, format=OUT_FORMAT) |
122 | 78 | ||
123 | sys.stderr.write('\r {0:.2%}'.format(float(id)/num_plots)) | 79 | def plot_dir(data_dir, out_dir, force): |
124 | sys.stderr.write('\n') | 80 | sys.stderr.write("Reading data...\n") |
125 | |||
126 | def plot_exp(data_dir, out_dir, force): | ||
127 | print("Reading data...") | ||
128 | dir_map = DirMap.read(data_dir) | 81 | dir_map = DirMap.read(data_dir) |
129 | print("Sorting configs...") | 82 | |
83 | sys.stderr.write("Creating column map...\n") | ||
130 | tuple_table = ReducedTupleTable.from_dir_map(dir_map) | 84 | tuple_table = ReducedTupleTable.from_dir_map(dir_map) |
131 | col_map = tuple_table.get_col_map() | 85 | col_map = tuple_table.get_col_map() |
132 | 86 | ||
133 | if not os.path.exists(out_dir): | 87 | if not os.path.exists(out_dir): |
134 | os.mkdir(out_dir) | 88 | os.mkdir(out_dir) |
135 | 89 | ||
136 | print("Plotting data...") | 90 | sys.stderr.write("Plotting...\n") |
137 | plot_by_variable(dir_map, col_map, out_dir, force) | 91 | |
138 | # plot_by_config(tuple_table, out_dir) | 92 | # Count total plots for % counter |
93 | num_plots = len([x for x in dir_map.leafs(1)]) | ||
94 | plot_num = 0 | ||
95 | |||
96 | for plot_path, plot_node in dir_map.leafs(1): | ||
97 | details = get_details(plot_path, out_dir) | ||
98 | |||
99 | if force or not os.path.exists(details.out): | ||
100 | plot_by_variable(plot_node, col_map, details) | ||
101 | |||
102 | plot_num += 1 | ||
103 | |||
104 | sys.stderr.write('\r {0:.2%}'.format(float(plot_num)/num_plots)) | ||
105 | sys.stderr.write('\n') | ||
139 | 106 | ||
140 | def main(): | 107 | def main(): |
141 | opts, args = parse_args() | 108 | opts, args = parse_args() |
@@ -146,13 +113,8 @@ def main(): | |||
146 | if not os.path.exists(opts.out_dir): | 113 | if not os.path.exists(opts.out_dir): |
147 | os.mkdir(opts.out_dir) | 114 | os.mkdir(opts.out_dir) |
148 | 115 | ||
149 | for exp in args: | 116 | for dir in args: |
150 | name = os.path.split(exp)[1] | 117 | plot_dir(dir, opts.out_dir, opts.force) |
151 | if exp != os.getcwd(): | ||
152 | out_dir = "%s/%s" % (opts.out_dir, name) | ||
153 | else: | ||
154 | out_dir = os.getcwd() | ||
155 | plot_exp(exp, out_dir, opts.force) | ||
156 | 118 | ||
157 | if __name__ == '__main__': | 119 | if __name__ == '__main__': |
158 | main() | 120 | main() |