chromaquant 0.3.1__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 (47) 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/{Handle → 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.3.1.dist-info → chromaquant-0.5.0.dist-info}/WHEEL +1 -1
  28. {chromaquant-0.3.1.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/handleDirectories.py +0 -89
  31. chromaquant/Manual/HydroUI.py +0 -418
  32. chromaquant/Manual/QuantUPP.py +0 -373
  33. chromaquant/Manual/Quantification.py +0 -1305
  34. chromaquant/Manual/__init__.py +0 -10
  35. chromaquant/Manual/duplicateMatch.py +0 -211
  36. chromaquant/Manual/fpm_match.py +0 -798
  37. chromaquant/Manual/label-type.py +0 -179
  38. chromaquant/Match/AutoFpmMatch.py +0 -1133
  39. chromaquant/Match/__init__.py +0 -12
  40. chromaquant/Quant/AutoQuantification.py +0 -1329
  41. chromaquant/Quant/__init__.py +0 -12
  42. chromaquant/__main__.py +0 -493
  43. chromaquant/properties.json +0 -4
  44. chromaquant-0.3.1.dist-info/METADATA +0 -189
  45. chromaquant-0.3.1.dist-info/RECORD +0 -22
  46. chromaquant-0.3.1.dist-info/entry_points.txt +0 -2
  47. chromaquant-0.3.1.dist-info/licenses/LICENSES_bundled.txt +0 -1035
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ COPYRIGHT STATEMENT:
5
+
6
+ ChromaQuant – A quantification software for complex gas chromatographic data
7
+
8
+ Copyright (c) 2026, by Julia Hancock
9
+ Affiliation: Dr. Julie Elaine Rorrer
10
+ URL: https://www.rorrerlab.com/
11
+
12
+ License: BSD 3-Clause License
13
+
14
+ ---
15
+
16
+ CLASS DEFINITION FOR CATEGORIES
17
+
18
+ Julia Hancock
19
+ Started 2-02-2026
20
+
21
+ """
22
+
23
+ """ CLASS """
24
+
25
+
26
+ # Define categories class
27
+ class Categories:
28
+
29
+ # Initialize
30
+ def __init__(self):
31
+
32
+ # Create attributes
33
+ # Category attribute
34
+ # Expected format is:
35
+ # {'category 1': ['keyword 1', 'keyword 2', ...], ...}
36
+ self._categories = {}
37
+
38
+ # Categorizer attribute
39
+ self.categorizer = self.IS_EQUAL
40
+
41
+ # Attribute denoting whether to ignore string case
42
+ self.ignore_case = True
43
+
44
+ # Define get_item to get internal categories
45
+ def __getitem__(self, key):
46
+ # Get category
47
+ return self._categories[key]
48
+
49
+ # Define set_item to set internal categories
50
+ def __setitem__(self, key, value):
51
+ # Set category
52
+ self._categories[key] = value
53
+
54
+ """ METHODS """
55
+ # Method to categorize by finding when tested value is equal to keyword,
56
+ # selecting first applicable category if more than one apply
57
+ def IS_EQUAL(self, test_value: str | int | float) -> str:
58
+
59
+ # For every category in categories...
60
+ for category in self._categories:
61
+
62
+ # For every keyword in the category...
63
+ for keyword in self._categories[category]:
64
+
65
+ # If the keyword is equal to the test value...
66
+ if keyword == test_value:
67
+
68
+ # Return the category
69
+ return category
70
+
71
+ # Otherwise, pass
72
+ else:
73
+ pass
74
+
75
+ return ''
76
+
77
+ # Method to categorize by finding when keyword is a substring of a tested
78
+ # value, selecting first applicable category if more than one apply
79
+ def IS_IN(self, test_value: str) -> str:
80
+
81
+ # Try to get the lowercase of the test_value and all keywords
82
+ try:
83
+ test_value.lower()
84
+ test_keyword_list = [keyword.lower() for keyword_list in
85
+ list(self._categories.values()) for
86
+ keyword in keyword_list]
87
+ del test_keyword_list
88
+
89
+ # Raise error if AttributeError occurs
90
+ except AttributeError as e:
91
+ raise AttributeError(('Cannot use IS_IN for non-string'
92
+ f'values and keywords: {e}'))
93
+
94
+ # For every category in categories...
95
+ for category in self._categories:
96
+
97
+ # For every keyword in the category...
98
+ for keyword in self._categories[category]:
99
+
100
+ # If ignoring case...
101
+ if self.ignore_case is True:
102
+
103
+ # If the lowercase keyword is in
104
+ # the lowercase test value...
105
+ if keyword.lower() in test_value.lower():
106
+
107
+ # Return the category
108
+ return category
109
+
110
+ # Otherwise, pass
111
+ else:
112
+ pass
113
+
114
+ # If not ignoring case...
115
+ else:
116
+
117
+ # If the keyword is in the test value...
118
+ if keyword in test_value:
119
+
120
+ # Return the category
121
+ return category
122
+
123
+ # Otherwise, pass
124
+ else:
125
+ pass
126
+
127
+ return ''
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env python
2
+ """
3
+
4
+ COPYRIGHT STATEMENT:
5
+
6
+ ChromaQuant – A quantification software for complex gas chromatographic data
7
+
8
+ Copyright (c) 2026, by Julia Hancock
9
+ Affiliation: Dr. Julie Elaine Rorrer
10
+ URL: https://www.rorrerlab.com/
11
+
12
+ License: BSD 3-Clause License
13
+
14
+ ---
15
+
16
+ TOOLS FOR FILE IMPORTING AND EXPORTING
17
+
18
+ Julia Hancock
19
+ Started 1-16-2026
20
+
21
+ """
22
+
23
+ from chemformula import ChemFormula
24
+
25
+ """ FUNCTIONS """
26
+
27
+
28
+ # Function to get the number of atoms of a given element from a formula
29
+ def get_number_element_atoms(formula: str | list[str],
30
+ element: str) -> list[int] | int:
31
+
32
+ # Try...
33
+ try:
34
+ # ...to split formula (checks if list or string)
35
+ formula.split()
36
+
37
+ # If it is a string, get a dictionary with
38
+ # key:value pairs as element:number
39
+ chemFormDict = ChemFormula(formula).element
40
+
41
+ # Get the number of atoms of the passed element
42
+ number_element_atoms: int | list[int] = chemFormDict[element]
43
+
44
+ # If an AttributeError occurs (formula is a list)...
45
+ except AttributeError:
46
+
47
+ # Create a new list to return
48
+ number_element_atoms = []
49
+
50
+ # For every formula within the list...
51
+ for single_formula in formula:
52
+
53
+ # Try...
54
+ try:
55
+
56
+ # Get a dictionary with key:value pairs as element:number
57
+ chemFormDict = ChemFormula(single_formula).element
58
+
59
+ # Get the number of atoms of the passed element from this
60
+ # dictionary and append this to the return list
61
+ number_element_atoms.append(int(chemFormDict[element]))
62
+
63
+ # If there is a ValueError (e.g., formula contains None or NaN)...
64
+ except ValueError:
65
+
66
+ # Append zero
67
+ number_element_atoms.append(0)
68
+
69
+ return number_element_atoms
70
+
71
+
72
+ # Function to get the molecular weight from a chemical formula
73
+ def get_molecular_weight(formula: str | list[str]) -> float | list[float]:
74
+
75
+ # Try...
76
+ try:
77
+ # ...to split formula (checks if list or string)
78
+ formula.split()
79
+
80
+ # If it is a string, get the weight
81
+ weight: float | list[float] = ChemFormula(formula).formula_weight
82
+
83
+ # If an AttributeError occurs (formula is a list)...
84
+ except AttributeError:
85
+
86
+ # Create a new list to return
87
+ weight = []
88
+
89
+ # For every formula within the list...
90
+ for single_formula in formula:
91
+
92
+ # Try...
93
+ try:
94
+
95
+ # Append the weight of the current formula
96
+ weight.append(ChemFormula(single_formula).formula_weight)
97
+
98
+ # If there is a ValueError (e.g., formula contains None or NaN)...
99
+ except ValueError:
100
+
101
+ # Append zero
102
+ weight.append(0)
103
+
104
+ return weight
@@ -0,0 +1,222 @@
1
+ #!/usr/bin/env python
2
+ """
3
+
4
+ COPYRIGHT STATEMENT:
5
+
6
+ ChromaQuant – A quantification software for complex gas chromatographic data
7
+
8
+ Copyright (c) 2026, by Julia Hancock
9
+ Affiliation: Dr. Julie Elaine Rorrer
10
+ URL: https://www.rorrerlab.com/
11
+
12
+ License: BSD 3-Clause License
13
+
14
+ ---
15
+
16
+ SUBPACKAGE FOR ADJUSTING AND TESTING DATAFRAMES
17
+
18
+ Julia Hancock
19
+ Started 11-12-2025
20
+
21
+ """
22
+
23
+ """ FUNCTIONS """
24
+
25
+
26
+ # Function to loop through keys in a passed dictionary and
27
+ # check them against a list of permitted keys or dictionary
28
+ # with permitted keys
29
+ def check_dict_keys(dictionary, permitted):
30
+
31
+ # For every key-value pair in the dictionary...
32
+ for key, value in dictionary.items():
33
+
34
+ # If the current key is not permitted, raise an error
35
+ if key not in permitted:
36
+ raise ValueError(f'{key} is not a valid argument')
37
+
38
+ # Otherwise, pass
39
+ else:
40
+ pass
41
+
42
+ return None
43
+
44
+
45
+ # Function to add, remove, or rename columns in a DataFrame
46
+ def column_adjust(dataframe, add_col=[], remove_col=[], rename_dict={}):
47
+ """
48
+ Function used to add, remove, or rename
49
+ columns in a passed DataFrame
50
+
51
+ Parameters
52
+ ----------
53
+ dataframe : pandas.DataFrame
54
+ DataFrame to have columns adjusted.
55
+ add_col : list, optional
56
+ List of column headers to add, by default [].
57
+ remove_col : list, optional
58
+ List of column headers to remove, by default [].
59
+ rename_dict : list, optional
60
+ Dictionary of headers to rename as keys and
61
+ new headers as values, by default {}.
62
+
63
+ Returns
64
+ -------
65
+ pandas.DataFrame
66
+ DataFrame post-adjustments.
67
+ """
68
+
69
+ # Define a new dataframe
70
+ new_dataframe = dataframe.copy()
71
+
72
+ # For each column in add_col...
73
+ if add_col:
74
+ for column in add_col:
75
+ # Create a new column and assign all values to NaN
76
+ # NOTE: if the column already exists, this will
77
+ # clear existing data
78
+ new_dataframe[column] = None
79
+
80
+ # Rename columns using key-value pairs in rename_dict
81
+ if rename_dict:
82
+ new_dataframe = new_dataframe.rename(columns=rename_dict)
83
+
84
+ # Remove dataframe columns as specified in remove_col...
85
+ if remove_col:
86
+ new_dataframe = dataframe.drop(remove_col, axis=1)
87
+
88
+ return new_dataframe
89
+
90
+
91
+ # Function to filter a DataFrame based on certain values in rows
92
+ def row_filter(dataframe, filter_dict):
93
+ """
94
+ Function used to filter a passed DataFrame
95
+ such that it only contains certain values
96
+ in specific columns
97
+ ...
98
+
99
+ Parameters
100
+ ----------
101
+ dataframe : pandas.DataFrame
102
+ DataFrame to have rows filtered.
103
+ filter_dict : _type_
104
+ Dictionary containing column names as keys and desired cell values
105
+ as values
106
+
107
+ Returns
108
+ -------
109
+ pandas.dataframe
110
+ Dataframe post-filtering.
111
+ """
112
+
113
+ # Define a new dataframe
114
+ new_dataframe = dataframe.copy()
115
+
116
+ # Filter the new dataframe to only include rows where the value
117
+ # in some column is equal to the value associated with the key
118
+ # in filter_dict that matches that column's header
119
+ for key, value in filter_dict.items():
120
+ new_dataframe = new_dataframe.loc[new_dataframe[key] == value]
121
+
122
+ return new_dataframe
123
+
124
+
125
+ # Function to check whether a column is empty
126
+ def verify_column_not_empty(dataframe, column):
127
+ """
128
+ Function used to test whether a column in a passed
129
+ DataFrame is empty
130
+
131
+ ...
132
+
133
+ Parameters
134
+ ----------
135
+ dataframe : pandas.DataFrame
136
+ DataFrame containing column to be tested
137
+ column : str
138
+ Header of a column which may or may not contain values
139
+
140
+ Returns
141
+ -------
142
+ bool
143
+ Result of the conditional test: True if the column is
144
+ not empty and False if the column is empty
145
+ """
146
+
147
+ # Define a default test result
148
+ test_result = False
149
+
150
+ # If there is any value in the passed column...
151
+ if dataframe[column].any():
152
+
153
+ # Redefine test result to True
154
+ test_result = True
155
+
156
+ # Otherwise, set to False
157
+ else:
158
+ test_result = False
159
+
160
+ return test_result
161
+
162
+
163
+ # Function to test whether certain values exist in a column
164
+ def test_for_column_values(dataframe, column, test_values):
165
+ """
166
+ Function used to test for the presence or absence of
167
+ passed values in some column of a given DataFrame
168
+
169
+ ...
170
+ Parameters
171
+ ----------
172
+ dataframe :
173
+ _description_
174
+ column : _type_
175
+ _description_
176
+ test_values : _type_
177
+ _description_
178
+
179
+ Returns
180
+ -------
181
+ _type_
182
+ _description_
183
+ """
184
+
185
+ # Define a default test result dictionary
186
+ test_result_dict = {test_value: False for test_value in test_values}
187
+
188
+ # If the dataframe is not empty...
189
+ if verify_column_not_empty(dataframe, column):
190
+
191
+ # For each test value in the test_values list...
192
+ for value in test_values:
193
+
194
+ # If the column contains the test value at at least one row...
195
+ if value in dataframe.loc[column].values.tolist():
196
+
197
+ # Set the test_result_dict entry to True
198
+ test_result_dict[value] = True
199
+
200
+ # Otherwise, pass
201
+ else:
202
+ pass
203
+
204
+ # Otherwise, pass
205
+ else:
206
+ pass
207
+
208
+ return test_result_dict
209
+
210
+
211
+ # Function that adds data from one row to another
212
+ def add_columns_from_one_row_to_another(first_row, second_row, add_columns):
213
+
214
+ # Create a copy of the passed first_row
215
+ new_first = first_row.copy()
216
+
217
+ # For every column in add_columns...
218
+ for column in add_columns:
219
+ # Set the first row's entry to the second row's entry
220
+ new_first.at[column] = second_row[column]
221
+
222
+ return new_first
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+
5
+ COPYRIGHT STATEMENT:
6
+
7
+ ChromaQuant – A quantification software for complex gas chromatographic data
8
+
9
+ Copyright (c) 2026, 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
+ TOOLS FOR FILE IMPORTING AND EXPORTING
18
+
19
+ Julia Hancock
20
+ Started 1-16-2026
21
+
22
+ """
23
+
24
+ import logging
25
+ import os
26
+ import pandas as pd
27
+ from ..logging_and_handling import setup_logger, setup_error_logging
28
+
29
+ """ LOGGING AND HANDLING """
30
+
31
+ # Create a logger
32
+ logger = logging.getLogger(__name__)
33
+
34
+ # Format the logger
35
+ logger = setup_logger(logger)
36
+
37
+ # Get an error logging decorator
38
+ error_logging = setup_error_logging(logger)
39
+
40
+ """ FUNCTIONS """
41
+
42
+
43
+ # Function to export data to a .csv file
44
+ def export_to_csv(dataframe, parent, name=''):
45
+ """export_to_csv _summary_
46
+
47
+ Parameters
48
+ ----------
49
+ dataframe : dataframe
50
+ Passed Pandas dataframe.
51
+ parent : string
52
+ Name of parent directory for exported file.
53
+ name : string
54
+ Name of file to be exported.
55
+
56
+ Returns
57
+ -------
58
+ None
59
+ """
60
+
61
+ # If the name argument is passed...
62
+ if name:
63
+ # Construct a full path from the passed
64
+ # parent directory and file name
65
+ full_path = os.path.join(parent, name, '.csv')
66
+ # Otherwise...
67
+ else:
68
+ # Set the full path to the parent
69
+ full_path = parent
70
+
71
+ # Export the dataframe to the full path
72
+ dataframe.to_csv(full_path, index=False)
73
+
74
+ return None
75
+
76
+
77
+ # Function to try to open a file using a passed function
78
+ def try_open_file(func, path, *args, **kwargs):
79
+
80
+ # Predefine the conditional Boolean result
81
+ tf = False
82
+
83
+ # Try opening the file, if it works then set tf to True
84
+ try:
85
+ data = func(path, *args, **kwargs)
86
+ tf = True
87
+
88
+ # If cannot open file, log and set tf to False
89
+ except Exception as e:
90
+ # logger.info(f'Could not open {path}, got {e}')
91
+ data = []
92
+ tf = False
93
+
94
+ return tf, data
95
+
96
+
97
+ # Function to try to open a file with pandas.read_csv
98
+ def try_open_csv(path, *args, **kwargs):
99
+ tf, data = try_open_file(pd.read_csv, path, *args, **kwargs)
100
+ return tf, data
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ COPYRIGHT STATEMENT:
5
+
6
+ ChromaQuant – A quantification software for complex gas chromatographic data
7
+
8
+ Copyright (c) 2026, by Julia Hancock
9
+ Affiliation: Dr. Julie Elaine Rorrer
10
+ URL: https://www.rorrerlab.com/
11
+
12
+ License: BSD 3-Clause License
13
+
14
+ ---
15
+
16
+ COLLECTION OF VARIOUS FORMULA TOOLS
17
+
18
+ Julia Hancock
19
+ Started 1-07-2026
20
+
21
+ """
22
+
23
+ import re
24
+ from openpyxl.utils import get_column_letter, coordinate_to_tuple
25
+
26
+ """ FUNCTIONS """
27
+
28
+
29
+ # Function for checking that passed formula starts with '='
30
+ def check_formula_starts_with_equals(formula):
31
+
32
+ # Initialize tf
33
+ tf = False
34
+
35
+ # If a match exists, set tf to True
36
+ if not re.search('^=', formula) is None:
37
+ tf = True
38
+ # Otherwise, pass
39
+ else:
40
+ pass
41
+
42
+ return tf
43
+
44
+
45
+ # Function to get a column letter based on table
46
+ # starting column and column's index within a table
47
+ def get_column_letter_from_table(
48
+ table_reference, column_name, start_cell_column):
49
+
50
+ # Find the column's index within the DataFrame
51
+ column_index = \
52
+ table_reference['columns'].index(column_name)
53
+
54
+ # Add the column's index to the starting column
55
+ # and get the column letter equivalent
56
+ column_letter = \
57
+ get_column_letter(start_cell_column + column_index)
58
+
59
+ return column_letter
60
+
61
+
62
+ # Function to construct value reference string
63
+ def get_value_reference_string(value_reference):
64
+
65
+ # Get value's sheet string
66
+ value_sheet = \
67
+ f"'{value_reference['sheet']}'!"
68
+ # Get value's cell string
69
+ value_cell = \
70
+ f"{value_reference['cell']}"
71
+
72
+ # Get the reference string
73
+ reference = value_sheet + value_cell
74
+
75
+ return reference
76
+
77
+
78
+ # Function to get the starting row and column of a table
79
+ def get_table_start_coords(table_reference):
80
+
81
+ # Get the table's starting cell
82
+ table_start_cell = table_reference['start_cell']
83
+
84
+ # Get the starting cell's row and column
85
+ start_row, start_column = \
86
+ coordinate_to_tuple(table_start_cell)
87
+
88
+ return start_row, start_column
89
+
90
+
91
+ # Function to replace formula raw insert with reference
92
+ def replace_insert(formula, raw, reference):
93
+
94
+ # Replace pointer substring with value's reference
95
+ new_formula = \
96
+ formula.replace(raw, reference)
97
+
98
+ return new_formula
99
+
100
+
101
+ # Function to get a range from passed table pointers
102
+ def table_column_to_range(table_reference, column_key):
103
+
104
+ # Get the starting coordinates
105
+ start_row, start_column = get_table_start_coords(table_reference)
106
+
107
+ # Get letter for output column
108
+ column_letter = \
109
+ get_column_letter_from_table(table_reference, column_key, start_column)
110
+
111
+ # Get the final row of the table
112
+ end_row = start_row + table_reference['length']
113
+
114
+ # Construct the range string
115
+ column_range = (
116
+ f'${column_letter}${start_row + 1}:'
117
+ f'${column_letter}${end_row + 1}')
118
+
119
+ return column_range