Skip to content

ephys_report.py

activate(schema_name, ephys_schema_name, *, create_schema=True, create_tables=True)

Activate the current schema.

Parameters:

Name Type Description Default
schema_name str

schema name on the database server to activate the ephys_report schema.

required
ephys_schema_name str

schema name of the activated ephys element for which this ephys_report schema will be downstream from.

required
create_schema bool

If True (default), create schema in the database if it does not yet exist.

True
create_tables bool

If True (default), create tables in the database if they do not yet exist.

True
Source code in element_array_ephys/ephys_report.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def activate(schema_name, ephys_schema_name, *, create_schema=True, create_tables=True):
    """Activate the current schema.

    Args:
        schema_name (str): schema name on the database server to activate the `ephys_report` schema.
        ephys_schema_name (str): schema name of the activated ephys element for which
                this ephys_report schema will be downstream from.
        create_schema (bool, optional): If True (default), create schema in the database if it does not yet exist.
        create_tables (bool, optional): If True (default), create tables in the database if they do not yet exist.
    """

    global ephys
    ephys = dj.create_virtual_module("ephys", ephys_schema_name)
    schema.activate(
        schema_name,
        create_schema=create_schema,
        create_tables=create_tables,
        add_objects=ephys.__dict__,
    )

ProbeLevelReport

Bases: Computed

Table for storing probe level figures.

Attributes:

Name Type Description
ephys.CuratedClustering foreign key

ephys.CuratedClustering primary key.

shank tinyint unsigned

Shank of the probe.

drift_map_plot attach

Figure object for drift map.

Source code in element_array_ephys/ephys_report.py
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
@schema
class ProbeLevelReport(dj.Computed):
    """Table for storing probe level figures.

    Attributes:
        ephys.CuratedClustering (foreign key): ephys.CuratedClustering primary key.
        shank (tinyint unsigned): Shank of the probe.
        drift_map_plot (attach): Figure object for drift map.
    """

    definition = """
    -> ephys.CuratedClustering
    shank         : tinyint unsigned
    ---
    drift_map_plot: attach
    """

    def make(self, key):
        from .plotting.probe_level import plot_driftmap

        save_dir = _make_save_dir()

        units = ephys.CuratedClustering.Unit & key & "cluster_quality_label='good'"

        shanks = set((probe.ProbeType.Electrode & units).fetch("shank"))

        for shank_no in shanks:
            table = units * ephys.ProbeInsertion * probe.ProbeType.Electrode & {
                "shank": shank_no
            }

            spike_times, spike_depths = table.fetch(
                "spike_times", "spike_depths", order_by="unit"
            )

            # Get the figure
            fig = plot_driftmap(spike_times, spike_depths, colormap="gist_heat_r")
            fig_prefix = (
                "-".join(
                    [
                        v.strftime("%Y%m%d%H%M%S")
                        if isinstance(v, datetime.datetime)
                        else str(v)
                        for v in key.values()
                    ]
                )
                + f"-{shank_no}"
            )

            # Save fig and insert
            fig_dict = _save_figs(
                figs=(fig,),
                fig_names=("drift_map_plot",),
                save_dir=save_dir,
                fig_prefix=fig_prefix,
                extension=".png",
            )

            self.insert1({**key, **fig_dict, "shank": shank_no})

UnitLevelReport

Bases: Computed

Table for storing unit level figures.

Attributes:

Name Type Description
ephys.CuratedClustering.Unit foreign key

ephys.CuratedClustering.Unit primary key.

ephys.ClusterQualityLabel foreign key

ephys.ClusterQualityLabel primary key.

waveform_plotly longblob

Figure object for unit waveform.

autocorrelogram_plotly longblob

Figure object for an autocorrelogram.

depth_waveform_plotly longblob

Figure object for depth waveforms.

Source code in element_array_ephys/ephys_report.py
 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
@schema
class UnitLevelReport(dj.Computed):
    """Table for storing unit level figures.

    Attributes:
        ephys.CuratedClustering.Unit (foreign key): ephys.CuratedClustering.Unit primary key.
        ephys.ClusterQualityLabel (foreign key): ephys.ClusterQualityLabel primary key.
        waveform_plotly (longblob): Figure object for unit waveform.
        autocorrelogram_plotly (longblob): Figure object for an autocorrelogram.
        depth_waveform_plotly (longblob): Figure object for depth waveforms.
    """

    definition = """
    -> ephys.CuratedClustering.Unit
    ---
    -> ephys.ClusterQualityLabel
    waveform_plotly                 : longblob
    autocorrelogram_plotly          : longblob
    depth_waveform_plotly           : longblob
    """

    def make(self, key):
        from .plotting.unit_level import (
            plot_auto_correlogram,
            plot_depth_waveforms,
            plot_waveform,
        )

        sampling_rate = (ephys.EphysRecording & key).fetch1(
            "sampling_rate"
        ) / 1e3  # in kHz

        peak_electrode_waveform, spike_times, cluster_quality_label = (
            (ephys.CuratedClustering.Unit & key) * ephys.WaveformSet.PeakWaveform
        ).fetch1("peak_electrode_waveform", "spike_times", "cluster_quality_label")

        # Get the figure
        waveform_fig = plot_waveform(
            waveform=peak_electrode_waveform, sampling_rate=sampling_rate
        )

        correlogram_fig = plot_auto_correlogram(
            spike_times=spike_times, bin_size=0.001, window_size=1
        )

        depth_waveform_fig = plot_depth_waveforms(ephys, unit_key=key, y_range=60)

        self.insert1(
            {
                **key,
                "cluster_quality_label": cluster_quality_label,
                "waveform_plotly": waveform_fig.to_plotly_json(),
                "autocorrelogram_plotly": correlogram_fig.to_plotly_json(),
                "depth_waveform_plotly": depth_waveform_fig.to_plotly_json(),
            }
        )

QualityMetricCutoffs

Bases: Lookup

Cut-off values for unit quality metrics.

Attributes:

Name Type Description
cutoffs_id smallint

Unique ID for the cut-off values.

amplitude_cutoff_maximum float

Optional. Amplitude cut-off.

presence_ratio_minimum float

Optional. Presence ratio cut-off.

isi_violations_maximum float

Optional. ISI violation ratio cut-off.

cutoffs_hash uuid

uuid for the cut-off values.

Source code in element_array_ephys/ephys_report.py
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
@schema
class QualityMetricCutoffs(dj.Lookup):
    """Cut-off values for unit quality metrics.

    Attributes:
        cutoffs_id (smallint): Unique ID for the cut-off values.
        amplitude_cutoff_maximum (float): Optional. Amplitude cut-off.
        presence_ratio_minimum (float): Optional. Presence ratio cut-off.
        isi_violations_maximum (float): Optional. ISI violation ratio cut-off.
        cutoffs_hash (uuid): uuid for the cut-off values.
    """

    definition = """
    cutoffs_id                    : smallint
    ---
    amplitude_cutoff_maximum=null : float # Defaults to null, no cutoff applied
    presence_ratio_minimum=null   : float # Defaults to null, no cutoff applied
    isi_violations_maximum=null   : float # Defaults to null, no cutoff applied
    cutoffs_hash: uuid
    unique index (cutoffs_hash)
    """

    contents = [
        (0, None, None, None, UUID("5d835de1-e1af-1871-d81f-d12a9702ff5f")),
        (1, 0.1, 0.9, 0.5, UUID("f74ccd77-0b3a-2bf8-0bfd-ec9713b5dca8")),
    ]

    @classmethod
    def insert_new_cutoffs(
        cls,
        cutoffs_id: int = None,
        amplitude_cutoff_maximum: float = None,
        presence_ratio_minimum: float = None,
        isi_violations_maximum: float = None,
    ):
        if cutoffs_id is None:
            cutoffs_id = (dj.U().aggr(cls, n="max(cutoffs_id)").fetch1("n") or 0) + 1

        param_dict = {
            "amplitude_cutoff_maximum": amplitude_cutoff_maximum,
            "presence_ratio_minimum": presence_ratio_minimum,
            "isi_violations_maximum": isi_violations_maximum,
        }
        param_hash = dict_to_uuid(param_dict)
        param_query = cls & {"cutoffs_hash": param_hash}

        if param_query:  # If the specified cutoff set already exists
            existing_paramset_idx = param_query.fetch1("cutoffs_id")
            if (
                existing_paramset_idx == cutoffs_id
            ):  # If the existing set has the same id: job done
                return
            # If not same name: human err, adding the same set with different name
            else:
                raise dj.DataJointError(
                    f"The specified param-set already exists"
                    f" - with paramset_idx: {existing_paramset_idx}"
                )
        else:
            if {"cutoffs_id": cutoffs_id} in cls.proj():
                raise dj.DataJointError(
                    f"The specified cuttoffs_id {cutoffs_id} already exists,"
                    f" please pick a different one."
                )
            cls.insert1(
                {"cutoffs_id": cutoffs_id, **param_dict, "cutoffs_hash": param_hash}
            )

QualityMetricSet

Bases: Manual

Set of quality metric values for clusters and its cut-offs.

Attributes:

Name Type Description
ephys.QualityMetrics foreign key

ephys.QualityMetrics primary key.

QualityMetricCutoffs foreign key

QualityMetricCutoffs primary key.

Source code in element_array_ephys/ephys_report.py
226
227
228
229
230
231
232
233
234
235
236
237
238
@schema
class QualityMetricSet(dj.Manual):
    """Set of quality metric values for clusters and its cut-offs.

    Attributes:
        ephys.QualityMetrics (foreign key): ephys.QualityMetrics primary key.
        QualityMetricCutoffs (foreign key): QualityMetricCutoffs primary key.
    """

    definition = """
    -> ephys.QualityMetrics
    -> QualityMetricCutoffs
    """

QualityMetricReport

Bases: Computed

Table for storing quality metric figures.

Attributes:

Name Type Description
QualityMetricSet foreign key

QualityMetricSet primary key.

plot_grid longblob

Plotly figure object.

Source code in element_array_ephys/ephys_report.py
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
@schema
class QualityMetricReport(dj.Computed):
    """Table for storing quality metric figures.

    Attributes:
        QualityMetricSet (foreign key): QualityMetricSet primary key.
        plot_grid (longblob): Plotly figure object.
    """

    definition = """
    -> QualityMetricSet
    ---
    plot_grid : longblob
    """

    def make(self, key):
        from .plotting.qc import QualityMetricFigs

        cutoffs = (QualityMetricCutoffs & key).fetch1()
        qc_key = ephys.QualityMetrics & key

        self.insert1(
            key.update(
                dict(plot_grid=QualityMetricFigs(qc_key, **cutoffs).get_grid().to_json)
            )
        )