aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--parse/point.py6
-rw-r--r--parse/tuple_table.py19
-rw-r--r--plot/__init__.py0
-rw-r--r--plot/style.py62
-rwxr-xr-xplot_exps.py154
5 files changed, 137 insertions, 104 deletions
diff --git a/parse/point.py b/parse/point.py
index ce9cfb0..d577306 100644
--- a/parse/point.py
+++ b/parse/point.py
@@ -8,9 +8,9 @@ from enum import Enum
8from collections import defaultdict 8from collections import defaultdict
9 9
10Type = Enum(['Min','Max','Avg','Var']) 10Type = Enum(['Min','Max','Avg','Var'])
11default_typemap = {Type.Max : {Type.Max : 1, Type.Min : 0, Type.Avg : 1, Type.Var : 1}, 11default_typemap = {Type.Max : {Type.Max : 1, Type.Min : 1, Type.Avg : 1, Type.Var : 1},
12 Type.Min : {Type.Max : 1, Type.Min : 0, Type.Avg : 1, Type.Var : 1}, 12 Type.Min : {Type.Max : 1, Type.Min : 1, Type.Avg : 1, Type.Var : 1},
13 Type.Avg : {Type.Max : 1, Type.Min : 0, Type.Avg : 1, Type.Var : 1}} 13 Type.Avg : {Type.Max : 1, Type.Min : 1, Type.Avg : 1, Type.Var : 1}}
14 14
15def make_typemap(): 15def make_typemap():
16 return copy.deepcopy(default_typemap) 16 return copy.deepcopy(default_typemap)
diff --git a/parse/tuple_table.py b/parse/tuple_table.py
index 105b786..86baa08 100644
--- a/parse/tuple_table.py
+++ b/parse/tuple_table.py
@@ -27,6 +27,9 @@ class TupleTable(object):
27 key = self.col_map.get_key(kv) 27 key = self.col_map.get_key(kv)
28 return key in self.table 28 return key in self.table
29 29
30 def __iter__(self):
31 return self.table.iteritems()
32
30 def reduce(self): 33 def reduce(self):
31 reduced = ReducedTupleTable(self.col_map) 34 reduced = ReducedTupleTable(self.col_map)
32 for key, value in self.table.iteritems(): 35 for key, value in self.table.iteritems():
@@ -92,21 +95,27 @@ class ReducedTupleTable(TupleTable):
92 def from_dir_map(dir_map): 95 def from_dir_map(dir_map):
93 Leaf = namedtuple('Leaf', ['stat', 'variable', 'base', 96 Leaf = namedtuple('Leaf', ['stat', 'variable', 'base',
94 'summary', 'config', 'values']) 97 'summary', 'config', 'values'])
98
99 def next_type(path):
100 return path.pop() if path[-1] in Type else Type.Avg
101
95 def leafs(): 102 def leafs():
96 for path, node in dir_map.leafs(): 103 for path, node in dir_map.leafs():
97 # The path will be of at least size 1: the filename 104 # The path will be of at least size 1: the filename
98 leaf = path.pop() 105 leaf = path.pop()
99 106
100 # Set acceptable defaults for the rest of the path 107 base = path.pop() if (path and path[-1] in Type) else Type.Avg
101 path += ['?', '?', 'Avg', 'Avg'][len(path):] 108 summ = path.pop() if (path and path[-1] in Type) else Type.Avg
109
110 path += ['?', '?'][len(path):]
102 111
103 [stat, variable, base_type, summary_type] = path 112 [stat, variable] = path
104 113
105 config_str = leaf[:leaf.index('.csv')] 114 config_str = leaf[:leaf.index('.csv')]
106 config = ColMap.decode(config_str) 115 config = ColMap.decode(config_str)
107 116
108 leaf = Leaf(stat, variable, base_type, 117 leaf = Leaf(stat, variable, base, summ,
109 summary_type, config, node.values) 118 config, node.values)
110 yield leaf 119 yield leaf
111 120
112 builder = ColMapBuilder() 121 builder = ColMapBuilder()
diff --git a/plot/__init__.py b/plot/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plot/__init__.py
diff --git a/plot/style.py b/plot/style.py
new file mode 100644
index 0000000..7e964b0
--- /dev/null
+++ b/plot/style.py
@@ -0,0 +1,62 @@
1from collections import namedtuple
2import matplotlib.pyplot as plot
3
4class Style(namedtuple('SS', ['marker', 'line', 'color'])):
5 def fmt(self):
6 return self.marker + self.line + self.color
7
8class StyleMap(object):
9 '''Maps configs (dicts) to specific line styles.'''
10 DEFAULT = Style('.', '-', 'k')
11
12 def __init__(self, col_list, col_values):
13 '''Assign (some) columns in @col_list to fields in @Style to vary, and
14 assign values for these columns to specific field values.'''
15 self.value_map = {}
16 self.field_map = {}
17
18 for field, values in self.__get_all()._asdict().iteritems():
19 next_column = col_list.pop(0)
20 value_dict = {}
21
22 for value in sorted(col_values[next_column]):
23 value_dict[value] = values.pop(0)
24
25 self.value_map[next_column] = value_dict
26 self.field_map[next_column] = field
27
28 def __get_all(self):
29 '''A Style holding all possible values for each property.'''
30 return Style(list('.,ov^<>1234sp*hH+xDd|_'), # markers
31 ['-', ':', '--'], # lines
32 list('bgrcmyk')) # colors
33
34 def get_style(self, kv):
35 '''Translate column values to unique line style.'''
36 style_fields = {}
37
38 for column, values in self.value_map.iteritems():
39 if column not in kv:
40 continue
41 field = self.field_map[column]
42 style_fields[field] = values[kv[column]]
43
44 return StyleMap.DEFAULT._replace(**style_fields)
45
46 def get_key(self):
47 '''A visual description of this StyleMap.'''
48 key = []
49
50 for column, values in self.value_map.iteritems():
51 # print("***%s, %s" % column, values)
52 for v in values.keys():
53 sdict = dict([(column, v)])
54 style = self.get_style(sdict)
55
56 styled_line = plot.plot([], [], style.fmt())[0]
57 description = "%s:%s" % (column, v)
58
59 key += [(styled_line, description)]
60
61 return sorted(key, key=lambda x:x[1])
62
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
2from __future__ import print_function 2from __future__ import print_function
3 3
4import matplotlib.pyplot as plot
4import os 5import os
5import shutil as sh 6import shutil as sh
6import sys 7import sys
8from collections import namedtuple
7from optparse import OptionParser 9from optparse import OptionParser
10from parse.col_map import ColMap
8from parse.dir_map import DirMap 11from parse.dir_map import DirMap
9from parse.tuple_table import ReducedTupleTable 12from parse.tuple_table import ReducedTupleTable
10from parse.col_map import ColMap 13from plot.style import StyleMap
11from collections import namedtuple,defaultdict
12import matplotlib.pyplot as plot
13 14
14def parse_args(): 15def 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
25ExpDetails = namedtuple('ExpDetails', ['variable', 'value', 'title', 'out']) 25ExpDetails = namedtuple('ExpDetails', ['variable', 'value', 'title', 'out'])
26OUT_FORMAT = 'pdf' 26OUT_FORMAT = 'pdf'
27 27
28def get_details(path): 28def 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
42def 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
42class 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
77def 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)) 79def plot_dir(data_dir, out_dir, force):
124 sys.stderr.write('\n') 80 sys.stderr.write("Reading data...\n")
125
126def 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
140def main(): 107def 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
157if __name__ == '__main__': 119if __name__ == '__main__':
158 main() 120 main()