/
Objects.py
356 lines (329 loc) · 13.1 KB
/
Objects.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
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
import numpy
import Operations
class Experiment(object):
"""Experiment containing all data
=== Attributes ===
@type directory: str
Folder where our data is/analysis will output
@type analyses: list[Analysis]
Individual CATE run objects (includes current analysis implemented)
"""
def __init__(self, directory, analyses):
"""Experiment object with all relevant data
@type self: Experiment
@type directory: str
Folder where our data is/analysis will output
@type analyses: list[Analysis]
Individual CATE run objects (includes current analysis implemented)
@rtype: None
"""
self.directory = directory
self.analyses = analyses # List of Analysis objects
class Analysis(object):
"""Analysis of a single CATE run/replicate
=== Attributes ===
@type kind: None | 'obj' | 'subj'
The kind of analysis currently implemented. Default in None
@type obj_num_pts: int
Number of points that objective analysis is to be done with.
Set to 8 for the default objective analysis.
@type run: Run
Run object containing basic run data
@type xs_p1: ('', '') | (float, float)
x-values of boundaries of phase 1. Default are empty strings.
@type xs_p2: ('', '') | (float, float)
x-values of boundaries of phase 2. Default are empty strings.
@type xs_p3: ('', '') | (float, float)
x-values of boundaries of phase 3. Default are empty strings.
"""
def __init__(
self, kind, obj_num_pts, run, xs_p1=('', ''),
xs_p2=('', ''), xs_p3=('', '')):
""" Constructor of Analysis object
@type kind: None | 'obj' | 'subj'
The kind of analysis currently implemented. Default in None
@type obj_num_pts: int | None
Number of points that objective analysis is to be done with.
Set to 8 for the default objective analysis.
@type run: Run
Run object containing basic run data
@type xs_p1: ('', '') | (float, float)
x-values of boundaries of phase 1. Default are empty strings.
@type xs_p2: ('', '') | (float, float)
x-values of boundaries of phase 2. Default are empty strings.
@type xs_p3: ('', '') | (float, float)
x-values of boundaries of phase 3. Default are empty strings.
@rtype: None
"""
self.kind = kind # None, 'obj', or 'subj'
self.obj_num_pts = obj_num_pts # None if not obj regression
self.run = run
self.xs_p3, self.xs_p2, self.xs_p1 = xs_p3, xs_p2, xs_p1
# Default values are None unless assigned
self.phase3 = Phase(
('', ''), ('', ''), ('', ''), '', '', '', [], [], '', '', '', '')
self.phase2 = Phase(
('', ''), ('', ''), ('', ''), '', '', '', [], [], '', '', '', '')
self.phase1 = Phase(
('', ''), ('', ''), ('', ''), '', '', '', [], [], '', '', '', '')
self.r2s = None
self.obj_x_start, self.obj_y_start = None, None
# Attributes for testing
self.r2s = None # Lists from obj analysis, y=mx+b
self.p12_r2_max = None
self.x_p12, self.y_p12 = None, None
self.x_p12_curvestrip_p3, self.y_p12_curvestrip_p3 = None, None
self.x_p1_curvestrip_p3, self.y_p1_curvestrip_p3 = None, None
self.x_p1_curvestrip_p23, self.y_p1_curvestrip_p23 = None, None
self.elut_period, self.tracer_retained, self.poolsize = None, None, None
self.influx, self.netflux, self.ratio = None, None, None
def analyze(self):
"""Implement analysis based on settings from attributes.
Parameters are defined based on the phase limits that have been provided
The 'engine' of the analysis if you will.
@type self: Analysis
@rtype: None
"""
# Implement objective analysis. Note that objective analysis just uses a
# set algorithm to set phase limits. After this if block the process
# is the same of both objective and subjective analyses. A subjective
# just allows the user to directly set the phase limits.
if self.kind == 'obj':
self.obj_x_start = self.run.x[-self.obj_num_pts:]
self.obj_y_start = self.run.y[-self.obj_num_pts:]
self.xs_p3, self.r2s, self.ms, self.bs = Operations.get_obj_phase3(
obj_num_pts=self.obj_num_pts,
elut_ends_parsed=self.run.elut_ends_parsed,
elut_cpms_log=self.run.elut_cpms_log)
self.xs_p2, self.xs_p1, self.p12_r2_max = Operations.get_obj_phase12(
xs_p3=self.xs_p3,
elut_ends_parsed=self.run.elut_ends_parsed,
elut_cpms_log=self.run.elut_cpms_log,
elut_ends=self.run.elut_ends)
# From here analysis is same for both objective and subjective analyses
if self.xs_p3 != ('', ''):
self.phase3 = Operations.extract_phase(
xs=self.xs_p3,
x_series=self.run.elut_ends_parsed,
y_series=self.run.elut_cpms_log,
elut_ends=self.run.elut_ends,
SA=self.run.SA, load_time=self.run.load_time)
Operations.advanced_run_calcs(analysis=self)
if self.xs_p2 != ('', '') and self.phase3.xs != ('', ''):
# Set series' to be curve-stripped
end_p12_index = Operations.x_to_index(
x_value=self.xs_p2[1], boundary_type='end',
x_series=self.run.elut_ends_parsed,
larger_x=self.run.elut_ends)
self.x_p12 = self.run.x[: end_p12_index+1]
self.y_p12 = self.run.y[: end_p12_index+1]
# Curve strip phase 1 + 2 data of phase 3
# From here on data series potentially have 'holes' from
# omitting negative log operations during curvestripping
self.x_p12_curvestrip_p3, self.y_p12_curvestrip_p3 = \
Operations.curvestrip(
x_series=self.x_p12, y_series=self.y_p12,
slope=self.phase3.slope,
intercept=self.phase3.intercept)
self.phase2 = Operations.extract_phase(
xs=self.xs_p2,
x_series=self.x_p12_curvestrip_p3,
y_series=self.y_p12_curvestrip_p3,
elut_ends=self.run.elut_ends,
SA=self.run.SA, load_time=self.run.load_time)
if self.xs_p1 != ('', '') and self.phase2.xs != ('', ''):
start_p1_index = Operations.x_to_index(
x_value=self.xs_p1[0], boundary_type='start',
x_series=self.run.elut_ends_parsed,
larger_x=self.run.elut_ends)
end_p1_index = Operations.x_to_index(
x_value=self.xs_p1[1], boundary_type='end',
x_series=self.run.elut_ends,
larger_x=self.run.elut_ends)
self.x_p1 = self.run.x[start_p1_index : end_p1_index+1]
self.y_p1 = self.run.y[start_p1_index : end_p1_index+1]
# Getting phase 1 data that has been already stripped of
# phase 3 data
self.x_p1_curvestrip_p3 =\
self.x_p12_curvestrip_p3[start_p1_index: end_p1_index+1]
self.y_p1_curvestrip_p3 =\
self.y_p12_curvestrip_p3[start_p1_index: end_p1_index+1]
# Curve-strip phase 2 data from phase 1
self.x_p1_curvestrip_p23, self.y_p1_curvestrip_p23 = \
Operations.curvestrip(
x_series=self.x_p1_curvestrip_p3,
y_series=self.y_p1_curvestrip_p3,
slope=self.phase2.slope,
intercept=self.phase2.intercept)
self.phase1 = Operations.extract_phase(
xs=self.xs_p1,
x_series=self.x_p1_curvestrip_p23,
y_series=self.y_p1_curvestrip_p23,
elut_ends=self.run.elut_ends,
SA=self.run.SA, load_time=self.run.load_time)
class Run(object):
"""Basic data form a single CATE run/replicate
=== Attributes ===
@type name: str
Name of run, used for identification purposes
@type SA: float
The specific activity of the loading solution used for the CATE
run/replicate; radioactivity(counts per min)/volume (mL).
@type rt_cnts: float
Radioactivity of the roots of the plant used (cpm).
@type sht_cnts: float
Radioactivity of the shoots of the plant used (cpm).
@type rt_wght: float
Weight of the roots of the plant used (grams).
@type gfact: float
Instrument-specific correction factor. Used to account of idiosyncrasies
of the detecting equipment.
@type load_time: float
Amount of time plant used was placed in loading solution for (minutes).
@type elut_ends: list[float]
Time points that eluates were removed from plants (vs added to plants)
@type raw_cpms: list[float]
Eluate radioactivities as measured by detecting equipment.
@type elut_cpms_gfact: list[float]
elut_cpms corrected for (multiplied by) g_fact
@type elut_cpms_gRFW: list[float]
elut_cpms_gfact corrected for (divided by) g_fact
@type elut_cpms_log: list[float]
Logarithmic conversion of elut_cpms_gRFW
@type elut_ends_parsed: list[float]
elut_ends with time points corresponding to eluates with no
radioactivity removed.
@type x: ndarry
elut_ends_parsed converted to a numpy compatible array
@type y: ndarry
elut_cpms_log converted to a numpy compatible array
"""
def __init__(
self, name, SA, rt_cnts, sht_cnts, rt_wght, gfact,
load_time, elut_ends, raw_cpms, elut_cpms):
"""Constructor of Run objects
@type self: Run
@type name: str
Name of run, used for identification purposes
@type SA: float
The specific activity of the loading solution used for the CATE
run/replicate; radioactivity(counts per min)/volume (mL).
@type rt_cnts: float
Radioactivity of the roots of the plant used (cpm).
@type sht_cnts: float
Radioactivity of the shoots of the plant used (cpm).
@type rt_wght: float
Weight of the roots of the plant used (grams).
@type gfact: float
Instrument-specific correction factor. Used to account of idiosyncrasies
of the detecting equipment.
@type load_time: float
Amount of time plant used was placed in loading solution for (minutes).
@type elut_ends: list[float]
Time points that eluates were removed from plants (vs added to plants)
@type raw_cpms: list[float]
Eluate radioactivities as measured by detecting equipment.
@type elut_cpms: list[float]
raw_cpms with blank values ('') replaced with 0s
@rtype: None
"""
self.name = name # Text identifier extracted from col header in excel
self.SA = SA
self.rt_cnts = rt_cnts
self.sht_cnts = sht_cnts
self.rt_wght = rt_wght
self.gfact = gfact
self.load_time = load_time
self.elut_ends = elut_ends
self.raw_cpms = raw_cpms
self.elut_cpms = elut_cpms # = raw_cpms with blanks('') replaced w/0
self.elut_starts = [0.0] + elut_ends[:-1]
self.elut_cpms_gfact, self.elut_cpms_gRFW, \
self.elut_cpms_log, self.elut_ends_parsed = \
Operations.basic_run_calcs(
rt_wght, gfact, self.elut_starts, elut_ends, elut_cpms)
# x and y data for graphing ('numpy-fied')
self.x = numpy.array(self.elut_ends_parsed)
self.y = numpy.array(self.elut_cpms_log)
class Phase(object):
""" Data for a particular phase in our Analysis
=== Attributes ===
@type xs: (float, float) | ('', '')
x-values (from elut_ends_parsed) of boundaries of phase.
@type xy1: (float, float) | ('', '')
x, y coordinates for one end of regression line.
Used to plot phase in GUI.
@type xy2: (float, float) | ('', '')
x, y coordinates for other end of regression line.
Used to plot phase in GUI.
@type r2: float | ''
Coefficient of correlation (R^2) between <x_series> and <y_series>.
@type slope: float | ''
Slope of regression line between <x_series> and <y_series>.
@type intercept: float | ''
Intercept of regression line between <x_series> and <y_series>.
@type x_series: [float]
x_series of data. Generally elut_ends_parsed.
@type x_series: [float]
y_series of data. elut_cpms_log data is curve-stripped forms.
@type k: float | ''
Rate constant of the phase (slope *2.303).
@type t05: float | ''
Half-life of exchange of the phase (0.693/k).
@type r0: float | ''
Rate of radioisotope release from compartment at time = 0 (antilog of
intercept).
@type efflux: float | ''
Efflux from compartment (r0/SA).
=== Representation Invariants ===
- Default values of attributes in blank phase are empty strings
"""
def __init__(
self, xs, xy1, xy2, r2, slope, intercept, x_series, y_series,
k, t05, r0, efflux):
""" Constructor of Phase object.
@type self: Phase
@type xs: (float, float) | ('', '')
x-values (from elut_ends_parsed) of boundaries of phase.
@type xy1: (float, float) | ('', '')
x, y coordinates for one end of regression line.
Used to plot phase in GUI.
@type xy2: (float, float) | ('', '')
x, y coordinates for other end of regression line.
Used to plot phase in GUI.
@type r2: float | ''
Coefficient of correlation (R^2) between <x_series> and <y_series>.
@type slope: float | ''
Slope of regression line between <x_series> and <y_series>.
@type intercept: float | ''
Intercept of regression line between <x_series> and <y_series>.
@type x_series: [float]
x_series of data. Generally elut_ends_parsed.
@type x_series: [float]
y_series of data. elut_cpms_log data is curve-stripped forms.
@type k: float
Rate constant of the phase (slope *2.303).
@type t05: float
Half-life of exchange of the phase (0.693/k).
@type r0: float
Rate of radioisotope release from compartment at time = 0 (antilog
of intercept).
@type efflux: float
Efflux from compartment (r0/SA).
@rtype: None
"""
self.xs = xs # paired tuple (x, y)
self.xy1, self.xy2 = xy1, xy2 # Each is a paired tuple
self.r2, self.slope, self.intercept = r2, slope, intercept
self.x_series, self.y_series = x_series, y_series
self.k, self.t05, self.r0, self.efflux = k, t05, r0, efflux
if __name__ == "__main__":
import Excel
import os
directory = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(directory, "Tests/1/Test_SingleRun1.xlsx")
temp_data = Excel.grab_data(file_path)
temp_analysis = temp_data.analyses[0]
temp_analysis.kind = 'obj'
temp_analysis.obj_num_pts = 4
temp_analysis.analyze()