chromaquant 0.4.0__py3-none-any.whl → 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. chromaquant/__init__.py +9 -2
  2. chromaquant/data/__init__.py +14 -0
  3. chromaquant/data/breakdown.py +430 -0
  4. chromaquant/data/dataset.py +195 -0
  5. chromaquant/data/table.py +412 -0
  6. chromaquant/data/value.py +215 -0
  7. chromaquant/formula/__init__.py +13 -0
  8. chromaquant/formula/base_formulas.py +168 -0
  9. chromaquant/formula/formula.py +507 -0
  10. chromaquant/import_local_packages.py +55 -0
  11. chromaquant/logging_and_handling.py +76 -0
  12. chromaquant/match/__init__.py +13 -0
  13. chromaquant/match/match.py +184 -0
  14. chromaquant/match/match_config.py +296 -0
  15. chromaquant/match/match_tools.py +154 -0
  16. chromaquant/{Quant → results}/__init__.py +2 -2
  17. chromaquant/results/reporting_tools.py +190 -0
  18. chromaquant/results/results.py +250 -0
  19. chromaquant/utils/__init__.py +14 -0
  20. chromaquant/utils/categories.py +127 -0
  21. chromaquant/utils/chemical_formulas.py +104 -0
  22. chromaquant/utils/dataframe_processing.py +222 -0
  23. chromaquant/utils/file_tools.py +100 -0
  24. chromaquant/utils/formula_tools.py +119 -0
  25. chromaquant-0.5.0.dist-info/METADATA +61 -0
  26. chromaquant-0.5.0.dist-info/RECORD +29 -0
  27. {chromaquant-0.4.0.dist-info → chromaquant-0.5.0.dist-info}/WHEEL +1 -1
  28. {chromaquant-0.4.0.dist-info → chromaquant-0.5.0.dist-info}/licenses/LICENSE.txt +1 -1
  29. chromaquant-0.5.0.dist-info/licenses/LICENSES_bundled.txt +251 -0
  30. chromaquant/Handle/__init__.py +0 -13
  31. chromaquant/Handle/fileChecks.py +0 -172
  32. chromaquant/Handle/handleDirectories.py +0 -89
  33. chromaquant/Hydro/__init__.py +0 -12
  34. chromaquant/Hydro/hydroMain.py +0 -496
  35. chromaquant/Manual/HydroUI.py +0 -418
  36. chromaquant/Manual/QuantUPP.py +0 -373
  37. chromaquant/Manual/Quantification.py +0 -1305
  38. chromaquant/Manual/__init__.py +0 -10
  39. chromaquant/Manual/duplicateMatch.py +0 -211
  40. chromaquant/Manual/fpm_match.py +0 -798
  41. chromaquant/Manual/label-type.py +0 -179
  42. chromaquant/Match/AutoFpmMatch.py +0 -1133
  43. chromaquant/Match/MatchSub/__init__.py +0 -13
  44. chromaquant/Match/MatchSub/matchTools.py +0 -282
  45. chromaquant/Match/MatchSub/peakTools.py +0 -259
  46. chromaquant/Match/__init__.py +0 -13
  47. chromaquant/Match/matchMain.py +0 -233
  48. chromaquant/Quant/AutoQuantification.py +0 -1329
  49. chromaquant/Quant/QuantSub/__init__.py +0 -15
  50. chromaquant/Quant/QuantSub/gasFID.py +0 -241
  51. chromaquant/Quant/QuantSub/gasTCD.py +0 -425
  52. chromaquant/Quant/QuantSub/liquidFID.py +0 -310
  53. chromaquant/Quant/QuantSub/parseTools.py +0 -162
  54. chromaquant/Quant/quantMain.py +0 -417
  55. chromaquant/UAPP/__init__.py +0 -12
  56. chromaquant/UAPP/uappMain.py +0 -427
  57. chromaquant/__main__.py +0 -526
  58. chromaquant/oldui.py +0 -492
  59. chromaquant/properties.json +0 -4
  60. chromaquant-0.4.0.dist-info/METADATA +0 -189
  61. chromaquant-0.4.0.dist-info/RECORD +0 -38
  62. chromaquant-0.4.0.dist-info/entry_points.txt +0 -2
  63. chromaquant-0.4.0.dist-info/licenses/LICENSES_bundled.txt +0 -1035
@@ -1,496 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
-
5
- COPYRIGHT STATEMENT:
6
-
7
- ChromaQuant – A quantification software for complex gas chromatographic data
8
-
9
- Copyright (c) 2024, by Julia Hancock
10
- Affiliation: Dr. Julie Elaine Rorrer
11
- URL: https://www.rorrerlab.com/
12
-
13
- License: BSD 3-Clause License
14
-
15
- ---
16
-
17
- SCRIPT WHICH RUNS A DESKTOP APPLICATION FOR FUTURE USE IN SPECTRA VISUALIZATION
18
-
19
- Julia Hancock
20
- Started 11/21/2023
21
-
22
- """
23
-
24
- """ PACKAGES """
25
- import sys
26
- import pandas as pd
27
- #Dash and plotly – dcc = "Dash Core Components", px = "Plotly express" with visualization tools
28
- from dash import Dash, html, dash_table, dcc, callback, Output, Input
29
- from plotly.subplots import make_subplots
30
- import plotly.express as px
31
- import plotly.graph_objects as go
32
- from pubchempy import Compound, get_compounds
33
- from rdkit import Chem
34
- from rdkit.Chem.Draw import MolsToGridImage, MolDrawing, rdMolDraw2D
35
- import base64
36
- from io import BytesIO
37
- import os
38
- from molmass import Formula
39
- import importlib.util
40
- import json
41
-
42
- """ PARAMETERS """
43
- #Write sample name
44
- #sname = "MBPR048_01"
45
-
46
- #Write whether sample is liquid ("L") or gas ("G")
47
- #stype = "G"
48
-
49
- #Write whether there exists a file containing matched FID and MS peaks (T/F)
50
- #smatchtf = 'F'
51
-
52
- """ LOCAL PACKAGES """
53
-
54
- #Get package directory
55
- app_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
56
-
57
- #Get absolute directories for subpackages
58
- subpack_dir = {'Handle':os.path.join(app_dir,'Handle','__init__.py'),
59
- 'Manual':os.path.join(app_dir,'Manual','__init__.py'),
60
- 'MatchSub':os.path.join(app_dir,'Match','MatchSub','__init__.py')}
61
-
62
- #Define function to import from path
63
- def import_from_path(module_name,path):
64
- #Define spec
65
- spec = importlib.util.spec_from_file_location(module_name,path)
66
- #Define modules
67
- module = importlib.util.module_from_spec(spec)
68
- #Expand sys.modules dict
69
- sys.modules[module_name] = module
70
- #Load module
71
- spec.loader.exec_module(module)
72
- return module
73
-
74
- #Import all local packages
75
- hd = import_from_path("hd",subpack_dir['Handle'])
76
- mn = import_from_path("mn",subpack_dir['Manual'])
77
- mtsb = import_from_path("mtsb",subpack_dir['MatchSub'])
78
-
79
- """ FUNCTION """
80
-
81
- def mainHydro(sname,stype,smatchtf):
82
-
83
- #Define file names using user parameters
84
- if stype == "G":
85
- fn_FID_SPEC = sname+"_GS2_FID_SPEC.csv"
86
- fn_MS_SPEC = sname+"_GS1_MS_SPEC.csv"
87
-
88
- if smatchtf == 'T':
89
- fn_FPM = sname+"_GS2_FIDpMS.csv"
90
- else:
91
- fn_FPM = "default_FIDpMS.csv"
92
-
93
- if stype == "L":
94
- fn_FID_SPEC = sname+"_LQ1_FID_SPEC.csv"
95
- fn_MS_SPEC = sname+"_LQ1_MS_SPEC.csv"
96
-
97
- if smatchtf == 'T':
98
- fn_FPM = sname+"_LQ1_FIDpMS.csv"
99
- else:
100
- fn_FPM = "default_FIDpMS.csv"
101
-
102
- """ DIRECTORIES """
103
- print("[hydroMain] Getting directories...")
104
- #Get directories from handling script
105
- directories = hd.handle(app_dir)
106
-
107
- #Data file log directory
108
- directories['log'] = os.path.join(directories['data'],sname,'log')
109
-
110
- #Data file breakdowns directory
111
- directories['break'] = os.path.join(directories['data'],sname,'breakdowns')
112
-
113
- #Raw data file directory
114
- directories['raw'] = os.path.join(directories['data'],sname,'raw data')
115
-
116
- """ ANALYSIS CONFIGURATION """
117
- print("[hydroMain] Interpreting analysis configuration...")
118
- #Read analysis configuration file
119
- with open(os.path.join(directories['resources'],'analysis-config.json')) as f:
120
- analysis_config = json.load(f)
121
-
122
- #Extract analysis configuration info
123
- #This dictionary contain lists of substrings to be checked against compound name strings to
124
- #assign a compound type
125
-
126
- #Six compound types exist: linear alkanes (L), branched alkanes (B), aromatics (A), cycloalkanes (C),
127
- #alkenes/alkynes (E), and other (O)
128
-
129
- #Each compound type abbreviation will have an entry in the dictionary corresponding to a list of
130
- #substrings to be checked against a compound name string
131
-
132
- contains = analysis_config["CT-assign-contains"]
133
-
134
- #Tuple of contains keys in order of priority
135
- keyloop = analysis_config["CT-assign-keyloop"]
136
-
137
- #Tuple of elements to be excluded and automatically labelled as 'O'
138
- element_exclude = analysis_config["CT-assign-element-exclude"]
139
-
140
- #File suffixes to add to form data filenames
141
- file_suffix = analysis_config["file-suffix"]
142
-
143
- #Fit parameters for matching FID and MS accortding to polynomial fit
144
- match_fit_parameters = analysis_config["match-fit-parameters"]
145
-
146
- #Acceptable peak errors for matching
147
- peak_errors = analysis_config["peak-errors"]
148
-
149
- #Define directories for desired dataframes
150
- #DIR_LQ_FIDpMS_PeaksLB = os.path.join(directories['raw'],+fn_FPM)
151
- DIR_LQ_FID_SPEC = os.path.join(directories['raw'],fn_FID_SPEC)
152
- DIR_LQ_MS_SPEC = os.path.join(directories['raw'],fn_MS_SPEC)
153
-
154
- #Column names to add to imported spectral data
155
- col_names_SPEC = ['RT','Signal']
156
-
157
- #Read matched peak data between FID and MS
158
- #LQ_FIDpMS_PeaksLB = pd.read_csv(DIR_LQ_FIDpMS_PeaksLB)
159
-
160
- #Read spectra data for FID
161
- LQ_FID_SPEC = pd.read_csv(DIR_LQ_FID_SPEC, names=col_names_SPEC, header=None)
162
- #Add column labeling FID data
163
- LQ_FID_SPEC['SignalType'] = 'FID'
164
-
165
- #Read spectra data for MS
166
- LQ_MS_SPEC = pd.read_csv(DIR_LQ_MS_SPEC, names=col_names_SPEC, header=None)
167
- #Add column labeling MS data
168
- LQ_MS_SPEC['SignalType'] = 'MS'
169
-
170
- #Get dictionary of checklist entries to SignalType entries
171
- dictSPEC = {'FID':'FID','MS':'MS'}
172
- #Get list of checklist keys
173
- dictSPEC_keys = list(dictSPEC.keys())
174
-
175
- #Append MS dataframe to FID dataframe
176
- LQ_FIDpMS_SPEC = pd.concat([LQ_FID_SPEC,LQ_MS_SPEC],ignore_index=True)
177
-
178
- """ ACCESSING PUBCHEM """
179
-
180
- #If there exists FIDpMS files, proceed
181
- if smatchtf == 'T':
182
-
183
- """ COMMENTING OUT SECTION, PUBCHEM FUNCTIONALITY OUTDATED"""
184
- """
185
- #Create a copy of the PeaksLB dataframe called PeaksLB_SMILES
186
- PeaksLB_Smiles = LQ_FIDpMS_PeaksLB.copy()
187
- #Read the existing smilePairs.csv
188
- smilePairs = pd.read_csv(os.path.join(script_directory,'resources','smilePairs.csv'))
189
- #Add a column to the PeaksLB dataframe with the smiles of matched compounds, ignoring 'No match'
190
- PeaksLB_Smiles['isoSMILES'] = ''
191
- #List of indices with compound names
192
- compindex = []
193
- #Set up temporary smile string for use in for loop
194
- tempSmile = smilePairs.loc[smilePairs['Compound Name']==PeaksLB_Smiles.at[4,'Compound Name']]
195
-
196
- #Loop through all rows in PeaksLB_Smiles
197
- print('[MAIN] Assessing compound structures through SMILES...')
198
- for i, row in PeaksLB_Smiles.iterrows():
199
-
200
- #Only add peaks with matched compounds
201
- if PeaksLB_Smiles.at[i,'Compound Name'] != 'No match' and PeaksLB_Smiles.at[i,'Compound Name'] != 'No Match':
202
-
203
- #Try/except to catch error in accessing dataframe
204
- try:
205
- #If the compound name can be found in the compound-SMILES .csv file, add to dataframe
206
- if any(smilePairs['Compound Name']==row['Compound Name']):
207
- tempSmile = smilePairs.loc[smilePairs['Compound Name']==row['Compound Name'],'isoSMILES']
208
- #Choose the first SMILES in the series
209
- tempSmile = tempSmile[tempSmile.index[0]]
210
- #Save SMILES to PeaksLB_Smiles dataframe
211
- PeaksLB_Smiles.at[i,'isoSMILES'] = tempSmile
212
- #If the compound isn't in the list, get SMILES from PubCHEM
213
- else:
214
- PeaksLB_Smiles.at[i,'isoSMILES'] = get_compounds(PeaksLB_Smiles.at[i,'Compound Name'],\
215
- 'name')[0].isomeric_smiles
216
- print('[MAIN] Downloading SMILES for {0}...'.format(PeaksLB_Smiles.at[i,'Compound Name']))
217
- compindex.append(i)
218
-
219
- except:
220
- print('[ERROR] An error occurred in accessing SMILES for {0}'.format(row['Compound Name']))
221
-
222
- else:
223
- pass
224
-
225
- #If any compounds were previously unlabelled, save the compound name and SMILES pairs as a .csv
226
- smilePairsOut = pd.concat([smilePairs,PeaksLB_Smiles.loc[compindex,['Compound Name','isoSMILES']]],ignore_index=True)
227
- if smilePairsOut.size > smilePairs.size:
228
- print("[MAIN] Saving updated compound-SMILES pairs...")
229
- smilePairsOut.to_csv(os.path.join(script_directory,'resources','smilePairs.csv'),index=False)
230
-
231
- else:
232
- pass
233
-
234
- """
235
- """ LABELLING SPECTRA """
236
- """
237
- print("[MAIN] Labelling the FID spectrum using the peak-compound dataframe...")
238
- #Find only the PeaksLB_Smiles rows which have compound matches
239
- PeaksLB_Smiles = PeaksLB_Smiles[~PeaksLB_Smiles['Compound Name'].isin(['No Match','No match'])]
240
- #For every entry in this new PeaksLB_Smiles dataframe, find the nearest FID Spectra RT to the labelled peak RT's
241
- for i, row in PeaksLB_Smiles.iterrows():
242
- #Find the closest row index in LQ_FID_SPEC
243
- df_closestIN = LQ_FID_SPEC.iloc[(LQ_FID_SPEC['RT']-row['FID RT']).abs().argsort()[:1]].index[0]
244
- #Add four columns to the spectral data: compound name, formula, match factor, and isosmiles
245
- LQ_FID_SPEC.at[df_closestIN,'Compound Name'] = row['Compound Name']
246
- LQ_FID_SPEC.at[df_closestIN,'Formula'] = row['Formula']
247
- LQ_FID_SPEC.at[df_closestIN,'Match Factor'] = row['Match Factor']
248
- LQ_FID_SPEC.at[df_closestIN,'isoSMILES'] = row['isoSMILES']
249
- #Find the molecular weight of the given compound and assign to a new column in the spectral data
250
- LQ_FID_SPEC.at[df_closestIN,'MW'] = Formula(row['Formula']).mass
251
- """
252
-
253
- #Otherwise, pass
254
- else:
255
- pass
256
-
257
- """ WEB APPLICATION """
258
-
259
- #Initialize the application with the Dash constructor
260
- app = Dash(__name__)
261
-
262
- #App layout
263
- app.layout = html.Div([
264
-
265
- #First row
266
- html.Div(children=[
267
- #First row first column
268
- html.Div(children=[
269
- #Scatter plot inside of a dcc interactive figure module
270
- dcc.Graph(id='controls_and_graph')],\
271
- style={'display':'inline-block','vertical-align':'top','margin-left': 5,'margin-top': '3vw','width':'70%'}),
272
-
273
- #First row second column
274
- html.Div(children=[
275
- #Checklist for selecting viewed data
276
- html.Div(children=[dcc.Checklist(dictSPEC_keys,[dictSPEC_keys[0]],id='controls_and_checklist')],style={'margin-top':100},className='row'),
277
- #Axis limit entries
278
- #x limit lower
279
- html.Div(children=[dcc.Input(id='xlim_l',type='number',placeholder='Lower x-limit')],style={'margin-top':20},className='row'),
280
- #x limit upper
281
- html.Div(children=[dcc.Input(id='xlim_u',type='number',placeholder='Upper x-limit')],className='row'),
282
- #y limit lower
283
- html.Div(children=[dcc.Input(id='ylim_l',type='number',placeholder='Lower y-limit')],className='row'),
284
- #y limit upper
285
- html.Div(children=[dcc.Input(id='ylim_u',type='number',placeholder='Upper y-limit')],className='row'),
286
- #y2 limit lower
287
- html.Div(children=[dcc.Input(id='y2lim_l',type='number',placeholder='Secondary Lower y-limit')],className='row'),
288
- #y2 limit upper
289
- html.Div(children=[dcc.Input(id='y2lim_u',type='number',placeholder='Secondary Upper y-limit')],className='row')],
290
-
291
- #Set display style to inline-block
292
- style={'display':'inline-block','vertical-align':'middle','margin-left': 0,'margin-top': '3vw','width':'25%'})],
293
- className='row'),
294
-
295
- #Second row
296
- html.Div(children=[
297
- #Compound structure image
298
- html.Img(id='structure-image')],className='row')
299
-
300
- ])
301
-
302
- """ CALLBACK CONTROLS """
303
- #FID and MS spectra selection and axis limits callback
304
- @callback(
305
- Output(component_id='controls_and_graph',component_property='figure'),
306
- Input(component_id='controls_and_checklist',component_property='value'),
307
- Input('xlim_l','value'),
308
- Input('xlim_u','value'),
309
- Input('ylim_l','value'),
310
- Input('ylim_u','value'),
311
- Input('y2lim_l','value'),
312
- Input('y2lim_u','value')
313
- )
314
- #Callback graph updating function
315
- def update_graph(fig_chosen,xlim_lval,xlim_uval,ylim_lval,ylim_uval,y2lim_lval,y2lim_uval):
316
- #Format axis limits into a list
317
- axLim = [xlim_lval,xlim_uval,ylim_lval,ylim_uval,y2lim_lval,y2lim_uval]
318
- #Dictionary of colors for specific use for MS and FID spectra
319
- colorDict = {'FID':px.colors.qualitative.Safe[4],'MS':px.colors.qualitative.Safe[1]}
320
- #List of applicable dataframe titles based on fig_chosen as keys in dictSPEC
321
- fig_chosen_dictSPEC = [dictSPEC[i] for i in fig_chosen]
322
- #Filter DataFrame based on fig_chosen
323
- #filDF = LQ_FIDpMS_SPEC[LQ_FIDpMS_SPEC['SignalType'].isin(fig_chosen_dictSPEC)]
324
-
325
- #Initialize figure
326
- fig = make_subplots(specs=[[{'secondary_y': True}]])
327
- #If there are any spectra chosen, plot them
328
- if len(fig_chosen) != 0:
329
- #Add a trace to the figure for each entry in fig_chosen
330
- for i in range(len(fig_chosen_dictSPEC)):
331
- #If the entry is past the first, set secondary_y axis value to True, otherwise set to False
332
- if i > 0:
333
- yTF = True
334
- else:
335
- yTF = False
336
- #Filter DataFrame based on ith entry in fig_chosen_dictSPEC
337
- filDF = LQ_FIDpMS_SPEC[LQ_FIDpMS_SPEC['SignalType'].str.match(fig_chosen_dictSPEC[i])]
338
-
339
- #Add a trace
340
- fig.add_trace(go.Scattergl(x=filDF['RT'],y=filDF['Signal'],\
341
- name=fig_chosen_dictSPEC[i],marker=dict(color=colorDict[fig_chosen[i]]),mode='lines+markers'),\
342
- secondary_y=yTF)
343
-
344
- #Update figure layout by adding titles
345
- fig.update_layout(title={'text':sname+' Spectra','font':{'color':'rgb(76, 46, 132)','size':20,'family':'Arial'}},\
346
- legend_title='Displayed Spectra')
347
- fig.update_xaxes(title_text='Retention time (min)')
348
- fig.update_yaxes(title_text=fig_chosen_dictSPEC[0]+' Signal (a.u.)',secondary_y=False)
349
- #Add a secondary y-axis title if necessary
350
- if len(fig_chosen_dictSPEC) > 1:
351
- fig.update_yaxes(title_text=fig_chosen_dictSPEC[1]+' Signal (a.u.)',secondary_y=True)
352
- #Check if user-inputted axis limits are viable
353
- #If lower ylim is less than upper ylim, update y-axis
354
- #If any NoneType exist in the axis limits list, do not update limits
355
- if None not in axLim[4:] and axLim[4] < axLim[5]:
356
- fig.update_yaxes(range=[axLim[4],axLim[5]],secondary_y=True)
357
- else:
358
- pass
359
- else:
360
- pass
361
-
362
- #Check if user-inputted axis limits are viable
363
- #If lower xlim is less than upper xlim, update x-axis
364
- #If any NoneType exist in the axis limits list, do not update limits
365
- if None not in axLim[:1] and axLim[0] < axLim[1]:
366
- fig.update_xaxes(range=[axLim[0],axLim[1]])
367
- else:
368
- pass
369
- #If lower ylim is less than upper ylim, update y-axis
370
- #If any NoneType exist in the axis limits list, do not update limits
371
- if None not in axLim[2:4] and axLim[2] < axLim[3]:
372
- fig.update_yaxes(range=[axLim[2],axLim[3]],secondary_y=False)
373
- else:
374
- pass
375
-
376
- #If no spectra are chosen, don't plot anything
377
- else:
378
- pass
379
-
380
- return fig
381
-
382
-
383
- #Compound structure drawing function
384
- @callback(
385
- Output('structure-image','src'),
386
- Input('controls_and_graph','selectedData')
387
- )
388
- def draw_structure(selectedData):
389
- max_structs = 6
390
- empty_plot = "data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA="
391
-
392
- if selectedData:
393
- #Take first point in selectedData, creating nested list
394
- checkPoint = [selectedData['points'][0]['curveNumber'],\
395
- selectedData['points'][0]['x'],\
396
- selectedData['points'][0]['y']]
397
-
398
- #Dictionary assigning each spectra to a curve number
399
- specKeys = {'FID':'','MS':''}
400
-
401
- #Assign spectra to curve numbers according to whether or not the first point matches any points in the FID signal
402
- try:
403
- if checkPoint[2] == LQ_FID_SPEC.at[LQ_FID_SPEC[LQ_FID_SPEC['RT']==checkPoint[1]].index[0],'Signal']:
404
- specKeys['FID'] = int(checkPoint[0])
405
- specKeys['MS'] = int(not checkPoint[0])
406
- else:
407
- pass
408
- except:
409
- specKeys['FID'] = int(not checkPoint[0])
410
- specKeys['MS'] = int(checkPoint[0])
411
-
412
- #Create list of selected points filtered so only FID results show
413
- selectedFIDData = []
414
- for i in selectedData['points']:
415
- if i['curveNumber'] == specKeys['FID']:
416
- selectedFIDData.append({'x':i['x'],'y':i['y'],'pointIndex':i['pointIndex']})
417
-
418
- if len(selectedFIDData) == 0:
419
- return empty_plot
420
- #List of selected point indices on FID spectrum
421
- match_idx = [x['pointIndex'] for x in selectedFIDData]
422
- #Dataframe with smiles and related data for selected points
423
- smilesDF = LQ_FID_SPEC.loc[match_idx,['RT','Compound Name','Formula','Match Factor','isoSMILES']]
424
- #Set FID spectra index to its own column and reset the indices
425
- smilesDF.reset_index(inplace=True)
426
- smilesDF = smilesDF.rename(columns={'index':'Spectra Index'})
427
- #Filter dataframe to only include non-NaN smiles entries
428
- smilesDF_F = smilesDF[smilesDF['isoSMILES'].notnull()].reset_index(drop=True)
429
- #Get lists of smiles rdkit objects and corresponding compound names
430
- list_smiles = [Chem.MolFromSmiles(x) for x in smilesDF_F['isoSMILES'].tolist()][:max_structs]
431
- #Create a list of legend entries using data from smilesDF_F
432
- list_legends = [smilesDF_F.loc[smilesDF_F.index[i],'Compound Name']+'\n'+\
433
- smilesDF_F.loc[smilesDF_F.index[i],'Formula']+' - '+\
434
- '{:.4}% Match'.format(smilesDF_F.loc[smilesDF_F.index[i],'Match Factor'])+'\n'+\
435
- '{:.3} min'.format(smilesDF_F.loc[smilesDF_F.index[i],'RT']) for i in range(len(smilesDF_F))][:max_structs]
436
- #Initialize MolDraw2DCairo drawer
437
- drawer = rdMolDraw2D.MolDraw2DCairo(1200,180,200,180)
438
- #Set drawing options
439
- drawer.drawOptions().useBWAtomPalette()
440
- drawer.drawOptions().legendFontSize = 16
441
- drawer.drawOptions().legendFraction = 0.3
442
- drawer.drawOptions().maxFontSize = 16
443
- drawer.drawOptions().padding = 0.05
444
- drawer.drawOptions().centreMoleculesBeforeDrawing = True
445
- #Draw the molecules
446
- drawer.DrawMolecules(list_smiles,legends=list_legends)
447
- drawer.FinishDrawing()
448
- #Set buffered image using BytesIO encoding
449
- buffered = BytesIO(drawer.GetDrawingText())
450
-
451
- #OLD METHOD FOR DRAWING GRID OF MOLECULE IMAGES
452
- #Create a grid of molecule images using rdkit
453
- #img = MolsToGridImage(list_smiles[0:max_structs], molsPerRow=structs_per_row, legends=list_legends, subImgSize=(300,300))
454
- #img = SVG(drawer.GetDrawingText())
455
- #Encode and save the image
456
- #buffered = BytesIO()
457
- #img.save(buffered, format="JPEG")
458
-
459
- #Encode the image
460
- encoded_image = base64.b64encode(buffered.getvalue())
461
- #Define and return the image string
462
- src_str = 'data:image/png;base64,{}'.format(encoded_image.decode())
463
- return src_str
464
- else:
465
- #Return a blank string
466
- return ''
467
-
468
- """ RETURN APPLICATION """
469
- return app
470
-
471
- """
472
- #Callback controls
473
- @callback(
474
- Output(component_id='controls_and_graph',component_property='figure'),
475
- Input(component_id='controls_and_checklist',component_property='value')
476
- )
477
-
478
- def update_graph(fig_chosen):
479
- #List of applicable dataframe titles based on fig_chosen as keys in dictSPEC
480
- fig_chosen_dictSPEC = [dictSPEC[i] for i in fig_chosen]
481
- #Filter DataFrame based on fig_chosen
482
- filDF = LQ_FIDpMS_SPEC[LQ_FIDpMS_SPEC['SignalType'].isin(fig_chosen_dictSPEC)]
483
- fig = px.scatter(filDF,x='RT',y='Signal',color='SignalType',\
484
- color_discrete_sequence=[px.colors.qualitative.Safe[4],\
485
- px.colors.qualitative.Safe[1]])
486
- #Update figure layout
487
- fig.update_layout(title='MBPR031 Spectra',xaxis_title='Retention time (min)',yaxis_title='Signal (a.u.)',legend_title='Displayed Spectra')
488
- return fig
489
- """
490
-
491
-
492
-
493
-
494
-
495
-
496
-