@@ -42,32 +42,32 @@ def __init__(
4242 self ._ephys = ephys
4343 self ._key = key
4444 self ._scale = scale
45- self ._plots = {}
45+ self ._plots = {} # Empty default to defer set to dict property below
4646 self ._fig_width = fig_width
4747 self ._amplitude_cutoff_max = amplitude_cutoff_maximum
4848 self ._presence_ratio_min = presence_ratio_minimum
4949 self ._isi_violations_max = isi_violations_maximum
5050 self ._dark_mode = dark_mode
51- self ._units = pd .DataFrame ()
51+ self ._units = pd .DataFrame () # Empty default
5252 self ._x_fmt = dict (showgrid = False , zeroline = False , linewidth = 2 , ticks = "outside" )
5353 self ._y_fmt = dict (showgrid = False , linewidth = 0 , zeroline = True , visible = False )
54- self ._no_data_text = "No data available"
55- self ._null_series = pd .Series (np .nan )
54+ self ._no_data_text = "No data available" # What to show when no data in table
55+ self ._null_series = pd .Series (np .nan ) # What to substitute when no data
5656
5757 @property
5858 def key (self ) -> dict :
5959 """Key in ephys.QualityMetrics table"""
6060 return self ._key
6161
62- @key .setter
62+ @key .setter # Allows `cls.property = new_item` notation
6363 def key (self , key : dict ):
6464 """Use class_instance.key = your_key to reset key"""
6565 if key not in self ._ephys .QualityMetrics .fetch ("KEY" ):
66- # if not already key, check if unquely identifies entry
66+ # If not already full key, check if unquely identifies entry
6767 key = (self ._ephys .QualityMetrics & key ).fetch1 ("KEY" )
6868 self ._key = key
6969
70- @key .deleter
70+ @key .deleter # Allows `del cls.property` to clear key
7171 def key (self ):
7272 """Use del class_instance.key to clear key"""
7373 logger .info ("Cleared key" )
@@ -103,15 +103,17 @@ def cutoffs(self, add_to_tables: bool = False, **cutoff_kwargs):
103103 "isi_violations_maximum" , self ._isi_violations_max
104104 )
105105 _ = self .units
106+
106107 if add_to_tables :
107108 ephys_report .QualityMetricCutoffs .insert_new_cutoffs (** cutoff_kwargs )
109+ logger .info ("Added cutoffs to QualityMetricCutoffs table" )
108110
109111 @property
110112 def units (self ) -> pd .DataFrame :
111113 """Pandas dataframe of QC metrics"""
112114 if not self ._key :
113- logger .info ("No key set" )
114115 return self ._null_series
116+
115117 if self ._units .empty :
116118 restrictions = ["TRUE" ]
117119 if self ._amplitude_cutoff_max :
@@ -120,14 +122,15 @@ def units(self) -> pd.DataFrame:
120122 restrictions .append (f"presence_ratio > { self ._presence_ratio_min } " )
121123 if self ._isi_violations_max :
122124 restrictions .append (f"isi_violation < { self ._isi_violations_max } " )
123- " AND " .join (restrictions )
125+ " AND " .join (restrictions ) # Build restriction from cutoffs
124126 return (
125127 self ._ephys .QualityMetrics
126128 * self ._ephys .QualityMetrics .Cluster
127129 * self ._ephys .QualityMetrics .Waveform
128130 & self ._key
129131 & restrictions
130132 ).fetch (format = "frame" )
133+
131134 return self ._units
132135
133136 def _format_fig (
@@ -145,12 +148,13 @@ class init, 1.
145148 Returns:
146149 go.Figure: Formatted figure
147150 """
148-
149151 if not fig :
150152 fig = go .Figure ()
151153 if not scale :
152154 scale = self ._scale
155+
153156 width = self ._fig_width * scale
157+
154158 return fig .update_layout (
155159 template = "plotly_dark" if self ._dark_mode else "simple_white" ,
156160 width = width ,
@@ -160,14 +164,15 @@ class init, 1.
160164 )
161165
162166 def _empty_fig (
163- self , annotation = "Select a key to visualize QC metrics" , scale = None
167+ self , text = "Select a key to visualize QC metrics" , scale = None
164168 ) -> go .Figure :
165169 """Return figure object for when no key is provided"""
166170 if not scale :
167171 scale = self ._scale
172+
168173 return (
169174 self ._format_fig (scale = scale )
170- .add_annotation (text = annotation , showarrow = False )
175+ .add_annotation (text = text , showarrow = False )
171176 .update_layout (xaxis = self ._y_fmt , yaxis = self ._y_fmt )
172177 )
173178
@@ -196,10 +201,14 @@ class initialization.
196201 scale = self ._scale
197202 if not fig :
198203 fig = self ._format_fig (scale = scale )
199- # if data.isnull().all():
200- histogram , histogram_bins = np .histogram (data , bins = bins , density = True )
201204
202- fig .add_trace (
205+ if not data .isnull ().all ():
206+ histogram , histogram_bins = np .histogram (data , bins = bins , density = True )
207+ else :
208+ # To quiet divide by zero error when no data
209+ histogram , histogram_bins = np .ndarray (0 ), np .ndarray (0 )
210+
211+ return fig .add_trace (
203212 go .Scatter (
204213 x = histogram_bins [:- 1 ],
205214 y = gaussian_filter1d (histogram , 1 ), # TODO: remove smoothing
@@ -209,7 +218,6 @@ class initialization.
209218 ),
210219 ** trace_kwargs ,
211220 )
212- return fig
213221
214222 def get_single_fig (self , fig_name : str , scale : float = None ) -> go .Figure :
215223 """Return a single figure of the plots listed in the plot_list property
@@ -224,7 +232,6 @@ def get_single_fig(self, fig_name: str, scale: float = None) -> go.Figure:
224232 """
225233 if not self ._key :
226234 return self ._empty_fig ()
227-
228235 if not scale :
229236 scale = self ._scale
230237
@@ -234,7 +241,7 @@ def get_single_fig(self, fig_name: str, scale: float = None) -> go.Figure:
234241 vline = fig_dict .get ("vline" , None )
235242
236243 if data .isnull ().all ():
237- return self ._empty_fig (annotation = self ._no_data_text )
244+ return self ._empty_fig (text = self ._no_data_text )
238245
239246 fig = (
240247 self ._plot_metric (data = data , bins = bins , scale = scale )
@@ -265,11 +272,11 @@ def get_grid(self, n_columns: int = 4, scale: float = 1.0) -> go.Figure:
265272
266273 if not self ._key :
267274 return self ._empty_fig ()
268-
269- n_rows = int (np .ceil (len (self .plots ) / n_columns ))
270275 if not scale :
271276 scale = self ._scale
272277
278+ n_rows = int (np .ceil (len (self .plots ) / n_columns ))
279+
273280 fig = self ._format_fig (
274281 fig = make_subplots (
275282 rows = n_rows ,
@@ -280,12 +287,12 @@ def get_grid(self, n_columns: int = 4, scale: float = 1.0) -> go.Figure:
280287 ),
281288 scale = scale ,
282289 ratio = (n_columns / n_rows ),
283- ).update_layout (
290+ ).update_layout ( # Global title
284291 title = dict (text = "Histograms of Quality Metrics" , xanchor = "center" , x = 0.5 ),
285292 font = dict (size = 12 * scale ),
286293 )
287294
288- for idx , plot in enumerate (self ._plots .values ()):
295+ for idx , plot in enumerate (self ._plots .values ()): # Each subplot
289296 this_row = int (np .floor (idx / n_columns ) + 1 )
290297 this_col = idx % n_columns + 1
291298 data = plot .get ("data" , self ._null_series )
@@ -302,7 +309,7 @@ def get_grid(self, n_columns: int = 4, scale: float = 1.0) -> go.Figure:
302309 ),
303310 ]
304311 )
305- fig = self ._plot_metric ( # still need to plot so vlines y_value works right
312+ fig = self ._plot_metric ( # still need to plot empty to cal y_vals min/max
306313 data = data ,
307314 bins = plot ["bins" ],
308315 fig = fig ,
@@ -317,12 +324,11 @@ def get_grid(self, n_columns: int = 4, scale: float = 1.0) -> go.Figure:
317324 )
318325 if vline :
319326 y_vals = fig .to_dict ()["data" ][idx ]["y" ]
320- # y_vals = plot["data"]
321- fig .add_shape (
327+ fig .add_shape ( # Add overlay WRT whole fig
322328 go .layout .Shape (
323329 type = "line" ,
324330 yref = "paper" ,
325- xref = "x" ,
331+ xref = "x" , # relative to subplot x
326332 x0 = vline ,
327333 y0 = min (y_vals ),
328334 x1 = vline ,
@@ -332,14 +338,13 @@ def get_grid(self, n_columns: int = 4, scale: float = 1.0) -> go.Figure:
332338 row = this_row ,
333339 col = this_col ,
334340 )
335- fig .update_xaxes (** self ._x_fmt )
336- fig .update_yaxes (** self ._y_fmt )
337- return fig
341+
342+ return fig .update_xaxes (** self ._x_fmt ).update_yaxes (** self ._y_fmt )
338343
339344 @property
340345 def plot_list (self ):
341346 """List of plots that can be rendered inidividually by name or as grid"""
342- if not self .plots :
347+ if not self ._plots :
343348 _ = self .plots
344349 return [plot for plot in self ._plots ]
345350
0 commit comments