Skip to content

Commit ab385e9

Browse files
author
Jonas Köppeler
committed
module: plot: initial draft for dynamic plot
1 parent 40f7763 commit ab385e9

File tree

1 file changed

+197
-0
lines changed

1 file changed

+197
-0
lines changed

module/plot/Plot_dynamic.py

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
# TIPSY: Telco pIPeline benchmarking SYstem
2+
#
3+
# Copyright (C) 2018 by its authors (See AUTHORS)
4+
#
5+
# This program is free software: you can redistribute it and/or modify
6+
# it under the terms of the GNU General Public License as published by
7+
# the Free Software Foundation, either version 3 of the License, or
8+
# (at your option) any later version.
9+
#
10+
# This program is distributed in the hope that it will be useful, but
11+
# WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
# General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU General Public License
16+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
18+
import collections
19+
import inspect
20+
import math
21+
22+
from plot_base import Plot as Base
23+
from plot import eval_expr
24+
25+
26+
def ensure_list(object_or_list):
27+
if type(object_or_list) == list:
28+
return object_or_list
29+
else:
30+
return [object_or_list]
31+
32+
def str2tex(s):
33+
return s.replace('_', '$\_$')
34+
35+
36+
class Plot(Base):
37+
def __init__(self, conf):
38+
super().__init__(conf)
39+
self.ylabel = None
40+
41+
def format_matplotlib(self, series, title):
42+
import matplotlib as mpl # "Generating graphs w/o a running X server"
43+
mpl.use('Agg') # https://stackoverflow.com/a/4935945
44+
import matplotlib.pyplot as plt
45+
plot_fv = getattr(plt, self.conf.axis_type, plt.plot)
46+
for name, points in series.items():
47+
print(f"{name} {points}")
48+
x = [float(p[0]) for p in points]
49+
y = [float(p[1]) for p in points]
50+
print(x)
51+
print(y)
52+
plot_fv(x, y, '-o', label=name)
53+
plt.title(title)
54+
plt.xlabel(self.conf.x_axis)
55+
if self.ylabel:
56+
plt.ylabel(self.ylabel)
57+
plt.legend()
58+
plt.savefig('fig.png')
59+
60+
def get_latex_axis_type(self):
61+
if self.conf.axis_type == 'normal':
62+
return 'axis'
63+
return '%saxis' % self.conf.axis_type
64+
65+
def latex_extra_plot(self, name, points):
66+
return ""
67+
68+
def format_latex(self, series, title):
69+
def is_empty(curve):
70+
return all([math.isnan(p[1]) for p in curve[1]])
71+
72+
# Put empty series to the end, otherwise markers and lables
73+
# are misaligned in the legend.
74+
ordered_series = sorted(series.items(), key=is_empty)
75+
76+
error_bar = ""
77+
if self.conf.get("error_bar", None):
78+
error_bar = r'+ [error bars/.cd,y dir=both,y explicit]'
79+
80+
addplot = ""
81+
symbols = list()
82+
for name, points in ordered_series:
83+
addplot += "\n"
84+
addplot += f" \\addplot{error_bar} coordinates {{\n"
85+
for p in points:
86+
addplot += f" ({p[0]}, {p[1]})"
87+
if error_bar and len(p) > 2:
88+
addplot += f" +- ({p[2]}, {p[2]})"
89+
addplot += "\n"
90+
if not isinstance(p[0], (int, float)) and p[0] not in symbols:
91+
symbols.append(p[0])
92+
addplot += " };\n"
93+
addplot += r' \addlegendentry{%s}' % str2tex(name)
94+
addplot += "\n"
95+
addplot += self.latex_extra_plot(name, points)
96+
97+
98+
symbolic_str = "{"
99+
for x in symbols:
100+
symbolic_str +=f"{x},"
101+
symbolic_str = symbolic_str[:-1]+"}"
102+
f = {
103+
'title': title,
104+
'xlabel': str2tex(self.conf.x_axis),
105+
'axis_type': self.get_latex_axis_type(),
106+
'addplot': addplot,
107+
}
108+
if self.ylabel:
109+
f['ylabel_opt'] = 'ylabel=%s,' % str2tex(self.ylabel)
110+
else:
111+
f['ylabel_opt'] = ""
112+
f['other_opts'] = ''
113+
if len(symbolic_str) > 1:
114+
f['symbolic_x_coords'] = "symbolic x coords="+symbolic_str+","
115+
else:
116+
f['symbolic_x_coords'] = ""
117+
sep = "\n "
118+
for prop in ['xmin', 'xmax', 'ymin', 'ymax']:
119+
if self.conf.get(prop, None) is not None:
120+
f['other_opts'] += f"{sep}{prop}={self.conf.get(prop, 0)}"
121+
sep = ",\n "
122+
text = inspect.cleandoc(r"""
123+
\begin{{figure}}
124+
\centering
125+
\begin{{tikzpicture}}
126+
\begin{{{axis_type}}}[
127+
xlabel={xlabel}, {ylabel_opt}, {symbolic_x_coords}
128+
legend pos=outer north east,
129+
legend cell align=left, {other_opts},
130+
]
131+
{addplot}
132+
\end{{{axis_type}}}
133+
\end{{tikzpicture}}
134+
\caption{{{title}}}
135+
\end{{figure}}
136+
"""
137+
).format(**f)
138+
with open('fig.tex', 'w') as f:
139+
f.write(text)
140+
f.write("\n")
141+
142+
def format_latex_empty(self, title):
143+
text = inspect.cleandoc(f"""
144+
\\begin{{figure}}
145+
\\centering
146+
(empty)
147+
\\caption{{{title}}}
148+
\\end{{figure}}
149+
""")
150+
with open('fig.tex', 'w') as f:
151+
f.write(text)
152+
153+
def plot(self, raw_data):
154+
y_axis = ensure_list(self.conf.y_axis)
155+
156+
if len(y_axis) == 1:
157+
self.ylabel = y_axis[0]
158+
159+
160+
series = collections.defaultdict(list)
161+
for row in raw_data:
162+
if self.conf.get('y_pipeline', None):
163+
row = eval_expr(self.conf.y_pipeline, row)
164+
x = row[self.conf.x_axis]
165+
groups = ensure_list(self.conf.group_by)
166+
for idx, var_name in enumerate(y_axis):
167+
y = row.get(var_name, None)
168+
if y is None:
169+
y = "nan"
170+
171+
if type(y) != list:
172+
raise
173+
174+
if len(y_axis) == 1 and len(groups) > 0:
175+
key = []
176+
else:
177+
key = [var_name]
178+
for group in groups:
179+
group_val = row.get(group, 'na')
180+
key.append('%s' % group_val)
181+
key = '/'.join(key)
182+
183+
series[key] = list(zip(x,y))
184+
print(f"{key}: {series[key]}")
185+
title = self.conf.title.format(**row)
186+
187+
188+
series = collections.OrderedDict(series.items())
189+
print(series)
190+
191+
if series:
192+
self.format_matplotlib(series, title)
193+
# self.format_latex(series, title)
194+
else:
195+
self.format_latex_empty(self.conf.title)
196+
197+
return series

0 commit comments

Comments
 (0)