1 import gobject
2 import gtk
3 import numpy
4 import traceback
5 from itertools import izip
6
7 import qubx.accept
8 import qubx.global_namespace
9 import qubx.GTK
10 import qubx.task
11 import qubx.util_types
12 from qubx.GTK import pack_item, pack_label, pack_check, pack_scrolled, pack_space
13 from qubx.settings import Property, Propertied
14 import qubxidlmdl
15
16
17 @Propertied(Property('Lmin', 3, 'Width of the shortest dwell, in samples'),
18 Property('split3', False, 'True to also try splitting an interval three ways before giving up'),
19 Property('combine_close', True, 'True to combine adjacent events with similar Amp'),
20 Property('combine_delta', 0.5, 'If combine_close, adjacent events nearer than this are merged'),
21 Property('make_list', True, 'whether to output a List of each event'),
22 Property('list_name', "MDL events", "name of output List"))
24 __explore_featured = ['global_name', 'robot', 'txtStatus', 'outStatus', 'shared_controls', 'idealize', 'ctx', 'out_list']
26 gtk.HBox.__init__(self)
27 self.global_name = 'QubX.Modeling.Idealize.methods["MDL"]'
28 self.__ref = qubx.util_types.Reffer()
29 self.propertied_connect_settings('Idealize_MDL')
30 self.robot = qubx.task.Robot('Idl:MDL', self.__ref(lambda: qubx.task.Tasks.add_task(self.robot)),
31 self.__ref(lambda: qubx.task.Tasks.remove_task(self.robot)))
32 self.robot.OnException += self.__ref(self.__onException)
33 self.robot.OnInterrupt += self.__ref(self.__onInterrupt)
34
35 self.set_size_request(200, 50)
36 self.txtStatus = gtk.TextView()
37 self.txtStatus.set_wrap_mode(gtk.WRAP_WORD)
38 self.outStatus = qubx.GTK.TextViewAppender(self.txtStatus)
39 self.txtStatus.set_editable(False)
40 qubx.GTK.SetFixedWidth(self.txtStatus)
41 pack_scrolled(self.txtStatus, self, expand=True)
42 self.outStatus.write("""Unsupervised Idealization of Ion Channel Recordings by Minimum Description Length: Application to Human PIEZO1-Channels
43
44 Radhakrishnan Gnanasambandam, Morten S. Nielsen, Christopher Nicolai, Frederick Sachs, Johannes P. Hofgaard and Jakob K. Dreyer
45
46 https://doi.org/10.3389/fninf.2017.00031
47
48 Abstract:
49 Researchers can investigate the mechanistic and molecular basis of many physiological phenomena in cells by analyzing the fundamental properties of single ion channels. These analyses entail recording single channel currents and measuring current amplitudes and transition rates between conductance states. Since most electrophysiological recordings contain noise, the data analysis can proceed by idealizing the recordings to isolate the true currents from the noise. This de-noising can be accomplished with threshold crossing algorithms and Hidden Markov Models, but such procedures generally depend on inputs and supervision by the user, thus requiring some prior knowledge of underlying processes. Channels with unknown gating and/or functional sub-states and the presence in the recording of currents from uncorrelated background channels present substantial challenges to such analyses. Here we describe and characterize an idealization algorithm based on Rissanen's Minimum Description Length (MDL) Principle. This method uses minimal assumptions and idealizes ion channel recordings without requiring a detailed user input or a priori assumptions about channel conductance and kinetics. Furthermore, we demonstrate that correlation analysis of conductance steps can resolve properties of single ion channels in recordings contaminated by signals from multiple channels. We first validated our methods on simulated data defined with a range of different signal-to-noise levels, and then showed that our algorithm can recover channel currents and their substates from recordings with multiple channels, even under conditions of high noise. We then tested the MDL algorithm on real experimental data from human PIEZO1 channels and found that our method revealed the presence of substates with alternate conductances.
50 """)
51
52 column = pack_item(gtk.VBox(), self)
53 h = pack_item(gtk.HBox(), column)
54 pack_label('Lmin:', h)
55 txt = pack_item(qubx.GTK.NumEntry(self.Lmin, qubx.accept.acceptIntGreaterThan(0), width_chars=6), h, at_end=True)
56 self.propertied_connect_NumEntry('Lmin', txt)
57 h.set_tooltip_text("Width of shortest dwells, in number of samples")
58 h = pack_item(gtk.HBox(), column)
59 chk = pack_check('Try 3-way splits (slow)', h)
60 self.propertied_connect_check('split3', chk)
61 h = pack_item(gtk.HBox(), column)
62 chk = pack_check('Merge adjacent events', h)
63 self.propertied_connect_check('combine_close', chk)
64 h = pack_item(gtk.HBox(), column)
65 pack_space(h, x=20)
66 pack_label('if amps closer than:', h)
67 txt = pack_item(qubx.GTK.NumEntry(self.combine_delta, qubx.accept.acceptFloatGreaterThan(0.0), '%.3g'), h)
68 self.propertied_connect_NumEntry('combine_delta', txt)
69 h = pack_item(gtk.HBox(), column)
70 chk = pack_check('make List of events', h)
71 self.propertied_connect_check('make_list', chk)
72 h = pack_item(gtk.HBox(), column)
73 pack_label("List name:", h)
74 txt = pack_item(qubx.GTK.NumEntry(self.list_name, qubx.accept.acceptStringNonempty), h, expand=True)
75 self.propertied_connect_NumEntry('list_name', txt)
76 self.shared_controls = pack_item(gtk.VBox(), column, at_end=True)
77
78 self.ctx = qubxidlmdl.MDLIdealizeContext()
79 self.out_list = None
80 self.idealizer = None
81
84 - def idealize(self, segments, model, on_finish):
97 - def robot_idealize(self, segments, Lmin, split3, combine_close, combine_delta, on_finish):
98 idlseg = [ [] ]
99 seg_amps, seg_stds = [], []
100 last_seg_index = [-1]
101 c = [0]
102 def emit_idl():
103 if len(seg_amps) > 1:
104 seg_amps[:] = [numpy.hstack(seg_amps)]
105 seg_stds[:] = [numpy.hstack(seg_stds)]
106 if len(seg_amps):
107 gobject.idle_add(self.__set_idl, segments[0].file, segments[0].signal, last_seg_index[0], seg_amps[0], seg_stds[0], idlseg[0])
108 idlseg[:] = [ [] ]
109 seg_amps[:] = []
110 seg_stds[:] = []
111 def add_idl(seg_index, amps, stds, ff, ll):
112 if seg_index != last_seg_index[0]:
113 emit_idl()
114 last_seg_index[0] = seg_index
115 c[0] = 0
116 n = len(ff)
117 cc = numpy.arange(c[0], c[0]+n, dtype="int32")
118 c[0] += n
119 idlseg[0].append((ff, ll, cc))
120 seg_amps.append(amps)
121 seg_stds.append(stds)
122 try:
123 if segments:
124 self.idealizer = self.ctx.new_idealizer()
125 for seg in segments:
126 for chunk in seg.chunks:
127 if chunk.included:
128 samples = chunk.get_samples().samples
129 ff, ll, amps, stds = self.idealizer.idealize(samples, chunk.f, Lmin, split3, combine_close, combine_delta)
130
131 add_idl(seg.index, amps, stds, ff, ll)
132 emit_idl()
133 finally:
134 self.idealizer = None
135 if segments:
136 gobject.idle_add(self.ideal_updater.done)
137 gobject.idle_add(on_finish)
138 - def __set_idl(self, datafile, out_ix, seg_index, amps, stds, idlseg):
139 for ff, ll, cc in idlseg:
140 self.ideal_updater.set_dwells(len(ff), ff, ll, cc, amps, stds, event=True)
141 if not (self.out_list is None):
142 lst = self.out_list
143 sampling_ms = datafile.sampling * 1e3
144 for ff, ll, cc in idlseg:
145 last_amp = amps[cc[0]] if len(cc) else 0.0
146 for f, l, c in izip(ff, ll, cc):
147 lst.insert_selection(f, l, Duration=sampling_ms*(l-f+1), Amp=amps[c], Std=stds[c], Delta=amps[c]-last_amp)
148 last_amp = amps[c]
149
150
151
152
153
154
156 traceback.print_exception(typ, val, trace, file=self.outStatus)
161
162
163
164
165
166