/
table.py
324 lines (273 loc) · 11.6 KB
/
table.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
""" Defines the ReportTable class """
#***************************************************************************************************
# Copyright 2015, 2019 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
# Under the terms of Contract DE-NA0003525 with NTESS, the U.S. Government retains certain rights
# in this software.
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0 or in the LICENSE file in the root pyGSTi directory.
#***************************************************************************************************
from collections import OrderedDict as _OrderedDict
from .row import Row
from .convert import convertDict as _convertDict
class ReportTable(object):
'''
Table representation, renderable in multiple formats
'''
def __init__(self, colHeadings, formatters, customHeader=None,
colHeadingLabels=None, confidenceRegionInfo=None):
'''
Create a table object
Parameters
----------
colHeadings : list or dict
default column headings if list,
dictionary of overrides if dict
formatters : list
names of default column heading formatters
customHeader : dict
dictionary of overriden headers
colHeadingLabels : list
labels for column headings (tooltips)
confidenceRegionInfo : ConfidenceRegion, optional
If not None, specifies a confidence-region
used to display error intervals.
Specifically, tells reportableqtys if
they should use non markovian error bars or not
'''
self.nonMarkovianEBs = bool(confidenceRegionInfo is not None
and confidenceRegionInfo.nonMarkRadiusSq > 0)
self._customHeadings = customHeader
self._rows = []
self._override = isinstance(colHeadings, dict)
if self._override:
self._columnNames = colHeadings['python']
else:
self._columnNames = colHeadings
if colHeadingLabels is None:
colHeadingLabels = self._columnNames
if self._override:
# Dictionary of overridden formats
self._headings = {k: Row(v, labels=colHeadingLabels, nonMarkovianEBs=self.nonMarkovianEBs)
for k, v in colHeadings.items()}
else:
self._headings = Row(colHeadings, formatters, colHeadingLabels, self.nonMarkovianEBs)
def addrow(self, data, formatters=None, labels=None, nonMarkovianEBs=None):
"""
Adds a row to the table.
Parameters
----------
data, formatters, labels : list
Parallel lists of the data, formatter names, and labels for each
cell within the new row.
nonMarkovianEBs : bool, optional
Whether non-Markovian error bars should be indicated.
Returns
-------
None
"""
if nonMarkovianEBs is None:
nonMarkovianEBs = self.nonMarkovianEBs
self._rows.append(Row(data, formatters, labels, nonMarkovianEBs))
def finish(self):
""" Finish table creation. Indicates no more rows will be added."""
pass # nothing to do currently
def _get_col_headings(self, fmt, spec):
if self._override:
# _headings is a dictionary of overridden formats
return self._headings[fmt].render(fmt, spec)
else:
# _headings is a row object
return self._headings.render(fmt, spec)
def render(self, fmt, longtables=False, tableID=None, tableclass=None,
output_dir=None, precision=6, polarprecision=3, sciprecision=0,
resizable=False, autosize=False, fontsize=None, complexAsPolar=True,
brackets=False, click_to_display=False, link_to=None, render_includes=True):
'''
Render a table object
Parameters
----------
fmt : string
name of format to be used
output_dir : string
directory for latex figures to be rendered in
precision : int
number of digits to render
polarprecision : int
number of digits to render for polars
sciprecision : int
number of digits to render for scientific notation
resizable : bool
allow a table to be resized
autosize : bool
allow a table to be automatically sized
click_to_display : bool
table plots must be clicked to prompt creation
fontsize : int
override fontsize of a tabel
complexAsPolar : bool
render complex numbers as polars
brackets : bool
render matrix like types w/ brackets
longtables : bool
latex table option
tableID : string
id tag for HTML tables
tableclass : string
class tag for HTML tables
link_to : list or {'tex', 'pdf', 'pkl'}
whether to create links to TEX, PDF, and/or PKL files
render_includes: bool
whether files included in rendered latex should also be rendered
(usually as PDFs for use with the 'includegraphics' latex statement)
Returns
-------
string
'''
spec = {
'output_dir': output_dir,
'precision': precision,
'polarprecision': polarprecision,
'sciprecision': sciprecision,
'resizable': resizable,
'autosize': autosize,
'click_to_display': click_to_display,
'fontsize': fontsize,
'complexAsPolar': complexAsPolar,
'brackets': brackets,
'longtables': longtables,
'tableID': tableID,
'tableclass': tableclass,
'link_to': link_to,
'render_includes': render_includes}
if fmt not in _convertDict:
raise NotImplementedError('%s format option is not currently supported' % fmt)
table = _convertDict[fmt]['table'] # Function for rendering a table in the format "fmt"
rows = [row.render(fmt, spec) for row in self._rows]
colHeadingsFormatted = self._get_col_headings(fmt, spec)
return table(self._customHeadings, colHeadingsFormatted, rows, spec)
def __str__(self):
def _strlen(x):
return max([len(p) for p in str(x).split('\n')])
def _nlines(x):
return len(str(x).split('\n'))
def _getline(x, i):
lines = str(x).split('\n')
return lines[i] if i < len(lines) else ""
#self.render('text')
col_widths = [0] * len(self._columnNames)
row_lines = [0] * len(self._rows)
header_lines = 0
for i, nm in enumerate(self._columnNames):
col_widths[i] = max(_strlen(nm), col_widths[i])
header_lines = max(header_lines, _nlines(nm))
for k, row in enumerate(self._rows):
for i, el in enumerate(row.cells):
el = el.data.get_value()
col_widths[i] = max(_strlen(el), col_widths[i])
row_lines[k] = max(row_lines[k], _nlines(el))
row_separator = "|" + '-' * (sum([w + 5 for w in col_widths]) - 1) + "|\n"
# +5 for pipe & spaces, -1 b/c don't count first pipe
s = "*** ReportTable object ***\n"
s += row_separator
for k in range(header_lines):
for i, nm in enumerate(self._columnNames):
s += "| %*s " % (col_widths[i], _getline(nm, k))
s += "|\n"
s += row_separator
for rowIndex, row in enumerate(self._rows):
for k in range(row_lines[rowIndex]):
for i, el in enumerate(row.cells):
el = el.data.get_value()
s += "| %*s " % (col_widths[i], _getline(el, k))
s += "|\n"
s += row_separator
s += "\n"
s += "Access row and column data by indexing into this object\n"
s += " as a dictionary using the column header followed by the\n"
s += " value of the first element of each row, i.e.,\n"
s += " tableObj[<column header>][<first row element>].\n"
return s
def __getitem__(self, key):
"""Indexes the first column rowdata"""
for row in self._rows:
row_data = row.cells
if len(row_data) > 0 and row_data[0].data.get_value() == key:
return _OrderedDict(zip(self._columnNames, row_data))
raise KeyError("%s not found as a first-column value" % key)
def __len__(self):
return len(self._rows)
def __contains__(self, key):
return key in list(self.keys())
def __getstate__(self):
state_dict = self.__dict__.copy()
return state_dict
def __setstate__(self, d):
self.__dict__.update(d)
def keys(self):
"""
Return a list of the first element of each row, which can be
used for indexing.
"""
return [row.cells[0].data.get_value() for row in self._rows if len(row.cells) > 0]
def row(self, key=None, index=None):
"""
Retrieve a row's cell data. A row is identified by either its `key`
(its first cell's value) OR its `index` (0-based).
Returns
-------
list
"""
if key is not None:
if index is not None:
raise ValueError("Cannot specify *both* key and index")
for row in self._rows:
row_data = row.cells
if len(row_data) > 0 and row_data[0].data.get_value() == key:
return row_data
raise KeyError("%s not found as a first-column value" % key)
elif index is not None:
if 0 <= index < len(self):
return self._rows[index].cells
else:
raise ValueError("Index %d is out of bounds" % index)
else:
raise ValueError("Must specify either key or index")
def col(self, key=None, index=None):
"""
Retrieve a column's cell data. A column is identified by either its
`key` (its column header's value) OR its `index` (0-based).
Returns
-------
list
"""
if key is not None:
if index is not None:
raise ValueError("Cannot specify *both* key and index")
if key in self._columnNames:
iCol = self._columnNames.index(key)
return [row.cells[iCol] for row in self._rows] # if len(d)>iCol
raise KeyError("%s is not a column name." % key)
elif index is not None:
if 0 <= index < len(self._columnNames):
return [row.cells[index] for row in self._rows] # if len(d)>iCol
else:
raise ValueError("Index %d is out of bounds" % index)
else:
raise ValueError("Must specify either key or index")
@property
def num_rows(self):
""" The number of rows in this table """
return len(self._rows)
@property
def num_cols(self):
""" The number of columns in this table """
return len(self._columnNames)
@property
def row_names(self):
""" The names (keys) of the rows in this table """
return list(self.keys())
@property
def col_names(self):
""" The names (keys) of the columns in this table """
return self._columnNames