-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathbase.py
305 lines (242 loc) · 10.7 KB
/
base.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
from nwbwidgets import view
import matplotlib.pyplot as plt
from ipywidgets import widgets
from collections.abc import Iterable
from pynwb import ProcessingModule
from pynwb.core import NWBDataInterface
from matplotlib.pyplot import Figure
from datetime import datetime
from typing import Union
import pandas as pd
from IPython import display
import ipysheet
import h5py
GroupingWidget = Union[widgets.Accordion, widgets.Tab]
def show_fields(node, **kwargs) -> widgets.Widget:
field_lay = widgets.Layout(max_height='40px', max_width='600px',
min_height='30px', min_width='130px')
info = []
for key, val in node.fields.items():
lbl_key = widgets.Label(key+':', layout=field_lay)
lbl_val = widgets.Label(str(val), layout=field_lay)
info.append(widgets.HBox(children=[lbl_key, lbl_val]))
vbox = widgets.VBox(info)
return vbox
def render_dataframe(df):
out1 = widgets.Output()
with out1:
display.display(df.to_dataframe())
return out1
def show_neurodata_base(node: NWBDataInterface, neurodata_vis_spec: dict) -> widgets.Widget:
"""
Gets a pynwb object and returns a Vertical Box containing textual info and
an expandable Accordion with it's children.
"""
field_lay = widgets.Layout(max_height='40px', max_width='500px',
min_height='30px', min_width='180px')
info = [] # string data type, exposed as a Text widget
neuro_data = [] # more complex data types, also with children
labels = []
for key, value in node.fields.items():
if isinstance(value, (str, datetime)):
lbl_key = widgets.Label(key+':', layout=field_lay)
lbl_val = widgets.Label(str(value), layout=field_lay)
info.append(widgets.HBox(children=[lbl_key, lbl_val]))
elif key == 'related_publications':
pub_list = []
for pub in value:
pub_list.append(widgets.HTML(value="<a href=http://dx.doi.org/"+pub[4:]+">"+pub+"</a>"))
lbl_key = widgets.Label(key+':', layout=field_lay)
pub_list.insert(0, lbl_key)
info.append(widgets.HBox(children=pub_list))
elif key == 'experimenter':
lbl_experimenter = widgets.Label('Experimenter:', layout=field_lay)
if isinstance(value, (list, tuple)):
lbl_names = widgets.Label(', '.join(value), layout=field_lay)
else:
lbl_names = widgets.Label(value, layout=field_lay)
hbox_exp = widgets.HBox(children=[lbl_experimenter, lbl_names])
info.append(hbox_exp)
elif (isinstance(value, Iterable) and len(value)) or value:
neuro_data.append(view.nwb2widget(value, neurodata_vis_spec=neurodata_vis_spec))
labels.append(key)
accordion = widgets.Accordion(children=neuro_data, selected_index=None)
for i, label in enumerate(labels):
if hasattr(node.fields[label], 'description') and node.fields[label].description:
accordion.set_title(i, label + ': ' + node.fields[label].description)
else:
accordion.set_title(i, label)
return widgets.VBox(info + [accordion])
def dict2accordion(d: dict, neurodata_vis_spec: dict, **pass_kwargs) -> widgets.Accordion:
children = [widgets.HTML('Rendering...') for _ in d]
accordion = widgets.Accordion(children=children, selected_index=None)
for i, label in enumerate(d):
if hasattr(d[label], 'description') and d[label].description:
accordion.set_title(i, label + ': ' + d[label].description)
else:
accordion.set_title(i, label)
accordion.set_title(i, label)
def on_selected_index(change):
if change.new is not None and isinstance(change.owner.children[change.new], widgets.HTML):
children[change.new] = nwb2widget(list(d.values())[change.new], neurodata_vis_spec=neurodata_vis_spec,
**pass_kwargs)
change.owner.children = children
accordion.observe(on_selected_index, names='selected_index')
return accordion
def lazy_tabs(in_dict: dict, node, style: GroupingWidget = widgets.Tab) -> GroupingWidget:
"""Creates a lazy tab object where multiple visualizations can be used for a single node and are generated on the
fly
Parameters
----------
in_dict: dict
keys are labels for tabs and values are functions
node: NWBDataInterface
instance of neurodata type to visualize
style: ipywidgets.Tab or ipywidgets.Accordion, optional
which way to present the data
Returns
-------
tab: widget
"""
tabs_spec = list(in_dict.items())
children = [tabs_spec[0][1](node)] + [widgets.HTML('Rendering...')
for _ in range(len(tabs_spec) - 1)]
tab = style(children=children)
[tab.set_title(i, label) for i, (label, _) in enumerate(tabs_spec)]
def on_selected_index(change):
if isinstance(change.owner.children[change.new], widgets.HTML):
children[change.new] = vis2widget(tabs_spec[change.new][1](node))
change.owner.children = children
tab.observe(on_selected_index, names='selected_index')
return tab
class LazyTab(widgets.Tab):
"""A lazy tab object where multiple visualizations can be used for a single node and are generated on the fly"""
def __init__(self, func_dict, data):
"""
Parameters
----------
func_dict: dict
keys are labels for tabs and values are functions
data: NWBDataInterface
instance of neurodata type to visualize
"""
tabs_spec = list(func_dict.items())
children = [tabs_spec[0][1](data)] + [widgets.HTML('Rendering...') for _ in range(len(tabs_spec) - 1)]
super().__init__(children=children)
[self.set_title(i, label) for i, (label, _) in enumerate(tabs_spec)]
def on_selected_index(change):
if isinstance(change.owner.children[change.new], widgets.HTML):
children[change.new] = vis2widget(tabs_spec[change.new][1](data))
change.owner.children = children
self.observe(on_selected_index, names='selected_index')
def lazy_show_over_data(list_, func_, labels=None, style: GroupingWidget = widgets.Tab) -> GroupingWidget:
"""
Apply same function to list of data in lazy tabs or lazy accordion
Parameters
----------
list_
func_
labels: list of str
style: widgets.Tab or widgets.Accordion
Returns
-------
ipywidgets.Tab or ipywidgets.Accordion
subtype Tab or Accordion
"""
children = [vis2widget(func_(list_[0]))] + [widgets.HTML('Rendering...') for _ in range(len(list_) - 1)]
out = style(children=children)
if labels is not None:
[out.set_title(i, label) for i, label in enumerate(labels)]
def on_selected_index(change):
if change.new is not None and isinstance(change.owner.children[change.new], widgets.HTML):
children[change.new] = vis2widget(func_(list_[change.new]))
change.owner.children = children
out.observe(on_selected_index, names='selected_index')
return out
def nwb2widget(node, neurodata_vis_spec: dict, **pass_kwargs) -> widgets.Widget:
for ndtype in type(node).__mro__:
if ndtype in neurodata_vis_spec:
spec = neurodata_vis_spec[ndtype]
if isinstance(spec, dict):
return lazy_tabs(spec, node)
elif callable(spec):
return vis2widget(spec(node, neurodata_vis_spec=neurodata_vis_spec, **pass_kwargs))
out1 = widgets.Output()
with out1:
print(node)
return out1
def vis2widget(vis) -> widgets.Widget:
if isinstance(vis, widgets.Widget):
out = vis
elif isinstance(vis, plt.Figure):
out = fig2widget(vis)
elif isinstance(vis, plt.Axes):
out = fig2widget(vis.get_figure())
else:
raise ValueError('unsupported vis type {}'.format(type(vis)))
out.add_class("custom_theme")
return out
def fig2widget(fig: Figure, **kwargs) -> widgets.Widget:
out = widgets.Output()
with out:
plt.show(fig)
return out
def processing_module(node: ProcessingModule, neurodata_vis_spec: dict) -> widgets.Widget:
return nwb2widget(node.data_interfaces, neurodata_vis_spec=neurodata_vis_spec)
def show_text_fields(node, exclude=('comments', 'interval'), **kwargs) -> widgets.Widget:
info = []
for key in node.fields:
if key not in exclude and isinstance(key, (str, float, int)):
info.append(widgets.Text(value=repr(getattr(node, key)), description=key, disabled=True))
return widgets.VBox(info)
def df2accordion(df: pd.DataFrame, by, func, style: GroupingWidget = widgets.Accordion, detect_single=True) \
-> GroupingWidget:
"""
Visualize pandas.DataFrame with an ipywidgets.Accordion
Parameters
----------
df: pandas.DataFrame
by: str
func: visualization function
style: ipywidgets.Tab or ipywidgets.Accordion, optional
detect_single: bool
If True, test if the dimension you are grouping by only has 1 unique value. If so, do not form an Accordion.
Returns
-------
ipywigets.Accordion or ipywidgets.Tab
"""
if detect_single and df[by].nunique() == 1:
return func(df)
else:
labels, idfs = zip(*df.groupby(by))
return lazy_show_over_data(idfs, func, labels=labels, style=style)
def show_dset(dset: h5py.Dataset, **kwargs):
return widgets.VBox(children=[
show_dict(dict(dset.attrs)),
dataset_to_sheet(dset)
])
def dataset_to_sheet(dset:h5py.Dataset):
if dset.ndim == 1:
nrows = len(dset)
sheet = ipysheet.easy.sheet(rows=nrows, columns=1, column_headers=False)
for row in range(nrows):
ipysheet.easy.cell(row, 0, dset[row], read_only=True)
elif dset.ndim == 2:
nrows, ncols = dset.shape
sheet = ipysheet.easy.sheet(rows=nrows, columns=ncols, column_headers=False)
for row, col in zip(range(nrows), range(ncols)):
ipysheet.easy.cell(row, col, dset[row, col], read_only=True)
else:
# do not know how to render datasets that have 3 or more dimensions
return widgets.HTML(print(dset))
return sheet
def show_dict(in_dict) -> widgets.Widget:
field_lay = widgets.Layout(max_height='40px', max_width='600px',
min_height='30px', min_width='130px')
info = []
for key, val in in_dict.items():
lbl_key = widgets.Label(key+':', layout=field_lay)
lbl_val = widgets.Label(str(val), layout=field_lay)
info.append(widgets.HBox(children=[lbl_key, lbl_val]))
vbox = widgets.VBox(info)
return vbox