ararpy 0.0.1a1__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.
ararpy/files/json.py ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ """
4
+ # ==========================================
5
+ # Copyright 2023 Yang
6
+ # ararpy - files - json
7
+ # ==========================================
8
+ #
9
+ #
10
+ #
11
+ """
12
+ from ..smp import Sample, Info, Table, Plot
13
+
14
+ import json
15
+ import numpy as np
16
+ import pandas as pd
17
+
18
+
19
+ def dumps(a):
20
+ # return json.dumps(a, default=lambda o: o.__dict__ if hasattr(0, '__dict__') else o, sort_keys=True, indent=4)
21
+ return json.dumps(a, cls=MyEncoder, indent=4)
22
+
23
+
24
+ def loads(a):
25
+ return json.loads(a)
26
+
27
+
28
+ class MyEncoder(json.JSONEncoder):
29
+ def default(self, obj):
30
+ # np.array
31
+ if isinstance(obj, np.ndarray):
32
+ return obj.tolist()
33
+ # pd.DataFrame
34
+ if isinstance(obj, pd.DataFrame):
35
+ print(obj)
36
+ raise ValueError(f"DataFrame objects found.")
37
+ # complex number
38
+ if isinstance(obj, complex):
39
+ return "complex number"
40
+ # numpy.int32
41
+ if isinstance(obj, np.int32):
42
+ return int(obj)
43
+ # sample instance
44
+ if isinstance(obj, (Sample, Info, Plot, Table, Plot.Text,
45
+ Plot.Axis, Plot.Label, Plot.Set, Plot.BasicAttr)):
46
+ return obj.__dict__
47
+ if not isinstance(obj, (int, str, list, dict, tuple, float)):
48
+ print(f"Special type, {type(obj) = }, {obj = }")
49
+ return super(MyEncoder, self).default(obj)
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ """
4
+ # ==========================================
5
+ # Copyright 2023 Yang
6
+ # webarar - new_file
7
+ # ==========================================
8
+ #
9
+ #
10
+ #
11
+ """
12
+ from .. import smp
13
+
14
+
15
+ def to_sample(file_path: str = '', sample_name: str = None):
16
+ """
17
+ Parameters
18
+ ----------
19
+ file_path
20
+ sample_name
21
+
22
+ Returns
23
+ -------
24
+
25
+ """
26
+ sample = smp.Sample()
27
+ # initial settings
28
+ smp.initial.initial(sample)
29
+ if sample_name is not None:
30
+ sample.Info.sample.name = sample_name
31
+ return sample
ararpy/files/raw.py ADDED
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ """
4
+ # ==========================================
5
+ # Copyright 2023 Yang
6
+ # ararpy - files - raw
7
+ # ==========================================
8
+ #
9
+ #
10
+ #
11
+ """
12
+ import traceback
13
+ from xlrd import open_workbook
14
+
15
+
16
+ def open_file(filepath: str, extension: str = 'xls'):
17
+ """
18
+ :param filepath: directory of file
19
+ :param extension: filter 1 for .xls files, 2 for .ahd files
20
+ :return: step_list -> [[[header of step one], [cycle one in the step], [cycle two in the step]],[[],[]]]
21
+ example:
22
+ [
23
+ [
24
+ [1, '6/8/2019 8:20:51 PM', 'BLK', 'B'], # step header
25
+ # [sequence index, date time, label, value]
26
+ ['1', 12.30, 87.73, 12.30, 1.30, 12.30, 0.40, 12.30, 0.40, 12.30, 0.44], # step/sequence 1
27
+ # [cycle index, time, Ar40 signal, time, Ar39 signal, ..., time, Ar36 signal]
28
+ ['2', 24.66, 87.70, 24.66, 1.14, 24.66, 0.36, 24.66, 0.35, 24.66, 0.43], # step/sequence 2
29
+ ...
30
+ ['10', 123.06, 22262.68, 123.06, 6.54, 123.06, 8.29, 123.06, 0.28, 123.06, 29.22],
31
+ ],
32
+ [
33
+ ...
34
+ ]
35
+ ]
36
+ """
37
+ try:
38
+ handler = {'xls': open_raw_xls, 'ahd': open_raw_ahd}[extension.lower()]
39
+ except KeyError:
40
+ print(traceback.format_exc())
41
+ return False
42
+ else:
43
+ return handler(filepath)
44
+
45
+
46
+ def open_raw_xls(filepath):
47
+ try:
48
+ wb = open_workbook(filepath)
49
+ sheets = wb.sheet_names()
50
+ sheet = wb.sheet_by_name(sheets[0])
51
+ value, step_header, step_list = [], [], []
52
+ for row in range(sheet.nrows):
53
+ row_set = []
54
+ for col in range(sheet.ncols):
55
+ if sheet.cell(row, col).value == '':
56
+ pass
57
+ else:
58
+ row_set.append(sheet.cell(row, col).value)
59
+ if row_set != [] and len(row_set) > 1:
60
+ value.append(row_set)
61
+ for each_row in value:
62
+ # if the first item of each row is float (1.0, 2.0, ...) this row is the header of a step.
63
+ if isinstance(each_row[0], float):
64
+ each_row[0] = int(each_row[0])
65
+ step_header.append(each_row)
66
+ for step_index, each_step_header in enumerate(step_header):
67
+ row_start_number = value.index(each_step_header)
68
+ try:
69
+ row_stop_number = value.index(step_header[step_index + 1])
70
+ except IndexError:
71
+ row_stop_number = len(value) + 1
72
+ step_values = [
73
+ each_step_header[0:4],
74
+ *list(map(
75
+ lambda x: [x[0], x[1], x[2], x[1], x[3], x[1], x[4], x[1], x[5], x[1], x[6]],
76
+ [value[i] for i in range(row_start_number + 2, row_stop_number - 7, 1)]))
77
+ ]
78
+ step_list.append(step_values)
79
+ except Exception as e:
80
+ print('Error in opening the original file: %s' % str(e))
81
+ return False
82
+ else:
83
+ return step_list
84
+
85
+
86
+ def open_raw_ahd(filepath):
87
+ try:
88
+ value, step_header, step_list = [], [], []
89
+ with open(filepath, 'r', encoding='utf-16-le') as f:
90
+ value = f.read()
91
+ value = [[j for j in i.split('\t') if j != ''] for i in value.split('\n')]
92
+ datetime = 'ddmmyyyy'
93
+ if datetime == 'ddmmyyyy':
94
+ dd, mm, yyyy = value[6][1].split('/')
95
+ value[6][1] = '/'.join([mm, dd, yyyy])
96
+ step_count = int(value[12][2])
97
+ step_header = [1, ' '.join(value[6][1:3]), value[0][1], value[8][1]]
98
+ step_list.append([step_header])
99
+ for step_index in range(step_count):
100
+ start_row = 15 + 5 * step_index
101
+ step_list[0].append([
102
+ str(step_index+1),
103
+ float(value[start_row+4][0]), float(value[start_row+4][1]),
104
+ float(value[start_row+3][0]), float(value[start_row+3][1]),
105
+ float(value[start_row+2][0]), float(value[start_row+2][1]),
106
+ float(value[start_row+1][0]), float(value[start_row+1][1]),
107
+ float(value[start_row+0][0]), float(value[start_row+0][1]),
108
+ ])
109
+ except Exception as e:
110
+ print('Error in opening the original file: %s' % str(e))
111
+ return False
112
+ else:
113
+ return step_list
114
+
115
+
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ """
4
+ # ==========================================
5
+ # Copyright 2023 Yang
6
+ # webarar - raw_file
7
+ # ==========================================
8
+ #
9
+ #
10
+ #
11
+ """
12
+
13
+ pass
14
+
ararpy/files/xls.py ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ """
4
+ # ==========================================
5
+ # Copyright 2023 Yang
6
+ # ararpy - files - xls
7
+ # ==========================================
8
+ #
9
+ #
10
+ #
11
+ """
12
+ from xlrd import open_workbook, biffh
13
+
14
+
15
+ def open_xls(filepath: str):
16
+ res = {}
17
+ try:
18
+ wb = open_workbook(filepath)
19
+ for sheet_name in wb.sheet_names():
20
+ sheet = wb.sheet_by_name(sheet_name)
21
+ res[sheet_name] = [[sheet.cell(row, col).value for col in range(sheet.ncols)] for row in range(sheet.nrows)]
22
+ except biffh.XLRDError as e:
23
+ print('Error in opening excel file: %s' % str(e))
24
+ return e
25
+ else:
26
+ return res
27
+
ararpy/smp/__init__.py ADDED
@@ -0,0 +1,17 @@
1
+
2
+ from . import (
3
+ sample as samples, consts, basic, corr, initial,
4
+ plots, style, table, calculation
5
+ )
6
+
7
+ Sample = samples.Sample
8
+ Info = samples.Info
9
+ Table = samples.Table
10
+ Plot = samples.Plot
11
+ Set = samples.Plot.Set
12
+ Label = samples.Plot.Label
13
+ Axis = samples.Plot.Axis
14
+ Text = samples.Plot.Text
15
+
16
+ VERSION = samples.VERSION
17
+
ararpy/smp/basic.py ADDED
@@ -0,0 +1,371 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ """
4
+ # ==========================================
5
+ # Copyright 2023 Yang
6
+ # ararpy - smp - basic
7
+ # ==========================================
8
+ #
9
+ #
10
+ #
11
+ """
12
+ # === Internal imports ===
13
+
14
+ import traceback
15
+ import pandas as pd
16
+ import numpy as np
17
+ import copy
18
+ from .. import calc
19
+ from .sample import Sample, Info, Table, Plot
20
+
21
+ Set = Plot.Set
22
+ Label = Plot.Label
23
+ Axis = Plot.Axis
24
+ Text = Plot.Text
25
+
26
+ pd.options.mode.chained_assignment = None # default='warn'
27
+
28
+
29
+ # =======================
30
+ # Calculate ages
31
+ # =======================
32
+ def calc_apparent_ages(smp: Sample):
33
+ """
34
+
35
+ Parameters
36
+ ----------
37
+ smp
38
+
39
+ Returns
40
+ -------
41
+
42
+ """
43
+ age = calc_age(smp=smp)
44
+ smp.ApparentAgeValues[2:6] = age
45
+ smp.PublishValues[5:7] = copy.deepcopy(age[0:2])
46
+
47
+
48
+ def calc_age(ar40ar39=None, params: dict = None, smp: Sample = None, index: list = None):
49
+ """
50
+ 40Ar/39Ar age calculation, two methods are supported: Min et al. (2000) or general equation.
51
+ Parameters
52
+ ----------
53
+ ar40ar39 : 2D DataFrame, Series, list
54
+ params : dict
55
+ smp : Sample instance
56
+ index:
57
+
58
+ Returns
59
+ -------
60
+
61
+ """
62
+ params_index_dict = {
63
+ 34: 'L', 35: 'sL', 36: 'Le', 37: 'sLe', 38: 'Lb', 39: 'sLb', 48: 'A', 49: 'sA',
64
+ 50: 'Ae', 51: 'sAe', 52: 'Ab', 53: 'sAb', 59: 't', 60: 'st', 67: 'J', 68: 'sJ',
65
+ 81: 'W', 82: 'sW', 83: 'No', 84: 'sNo', 85: 'Y', 86: 'sY', 87: 'f', 88: 'sf', 110: 'Min'
66
+ }
67
+
68
+ if isinstance(ar40ar39, pd.Series):
69
+ ar40ar39 = [ar40ar39.tolist(), [0] * ar40ar39.size]
70
+ if isinstance(ar40ar39, pd.DataFrame):
71
+ ar40ar39 = ar40ar39.transpose().values.tolist()
72
+ if ar40ar39 is None and smp is not None:
73
+ ar40ar39 = smp.ApparentAgeValues[0:2]
74
+ if len(np.shape(ar40ar39)) == 1:
75
+ ar40ar39 = np.reshape(ar40ar39, (2, 1))
76
+ if index is None:
77
+ index = list(range(np.shape(ar40ar39)[-1]))
78
+
79
+ if smp is None and params is None:
80
+ raise ValueError(f"Parameters are required for calculating ages, or it is empty.")
81
+ if params is not None:
82
+ for key, val in params.items():
83
+ if not isinstance(val, list):
84
+ params[key] = list(val)
85
+ if smp is not None:
86
+ try:
87
+ params_from_smp = dict(zip(
88
+ list(params_index_dict.values()),
89
+ [[smp.TotalParam[i][j] for j in index] for i in params_index_dict.keys()]
90
+ ))
91
+ except Exception:
92
+ print(traceback.format_exc())
93
+ raise ValueError(f"Parameters cannot be found in the given sample object")
94
+ if params is not None:
95
+ params_from_smp.update(params)
96
+ params = params_from_smp
97
+
98
+ # check if using Min equation
99
+ params['Min'] = [i if isinstance(i, bool) else False for i in params['Min']]
100
+
101
+ idx1 = np.flatnonzero(np.where(params['Min'], True, False)) # True, using Min euqation
102
+ idx2 = np.flatnonzero(np.where(params['Min'], False, True)) # False
103
+ k1, k2 = [], []
104
+ if np.size(idx1) > 0:
105
+ k1 = calc.age.calc_age_min(
106
+ F=[ar40ar39[0][i] for i in idx1], sF=[ar40ar39[1][i] for i in idx1],
107
+ **dict(zip(params.keys(), [[val[i] for i in idx1] for val in params.values()])))
108
+ if np.size(idx2) > 0:
109
+ k2 = calc.age.calc_age_general(
110
+ F=[ar40ar39[0][i] for i in idx2], sF=[ar40ar39[1][i] for i in idx2],
111
+ **dict(zip(params.keys(), [[val[i] for i in idx2] for val in params.values()])))
112
+
113
+ # idx1 = params[params['Min'].astype(bool)].index
114
+ # idx2 = params[~params['Min'].astype(bool)].index # The operators are: | for or, & for and, and ~ for not
115
+ # k1, k2 = [], []
116
+ # if idx1.size > 0:
117
+ # k1 = calc.age.calc_age_min(
118
+ # F=ar40ar39.take(idx1)[0], sF=ar40ar39.take(idx1)[1], **params.take(idx1).to_dict('list'))
119
+ # if idx2.size > 0:
120
+ # k2 = calc.age.calc_age_general(
121
+ # F=ar40ar39.take(idx2)[0], sF=ar40ar39.take(idx2)[1], **params.take(idx2).to_dict('list'))
122
+
123
+ columns = ['age', 's1', 's2', 's3']
124
+ ages = pd.concat([
125
+ pd.DataFrame([*k1], columns=idx1, index=columns),
126
+ pd.DataFrame([*k2], columns=idx2, index=columns)
127
+ ], axis=1).transpose().reset_index(drop=True)
128
+
129
+ if len(index) == 1:
130
+ return ages.transpose().squeeze().tolist()
131
+ else:
132
+ return ages.transpose().values.tolist()
133
+
134
+
135
+ # =======================
136
+ # Search components
137
+ # =======================
138
+ def get_content_dict(smp: Sample):
139
+ """
140
+
141
+ Parameters
142
+ ----------
143
+ smp
144
+
145
+ Returns
146
+ -------
147
+
148
+ """
149
+ return dict(zip(
150
+ ['smp', 'blk', 'cor', 'deg', 'pub', 'age', 'iso', 'pam', 'mak', 'seq'],
151
+ [smp.SampleIntercept, smp.BlankIntercept, smp.CorrectedValues, smp.DegasValues,
152
+ smp.PublishValues, smp.ApparentAgeValues, smp.IsochronValues, smp.TotalParam,
153
+ [smp.IsochronMark], [smp.SequenceName, smp.SequenceValue]]
154
+ ))
155
+
156
+
157
+ def get_dict_from_obj(obj: (Sample, Info, Plot, Table, Set, Label, Axis, Text)):
158
+ """
159
+
160
+ Parameters
161
+ ----------
162
+ obj
163
+
164
+ Returns
165
+ -------
166
+
167
+ """
168
+ res = {}
169
+ for key, attr in obj.__dict__.items():
170
+ if not isinstance(attr, (Sample, Info, Plot, Table, Set, Label, Axis, Text)):
171
+ res.update({key: attr})
172
+ else:
173
+ res.update({key: get_dict_from_obj(attr)})
174
+ return res
175
+
176
+
177
+ def get_components(smp: Sample):
178
+ """
179
+ Get updated sample.Components dict
180
+ Parameters
181
+ ----------
182
+ smp
183
+
184
+ Returns
185
+ -------
186
+
187
+ """
188
+ components_name = [
189
+ '0', '1', '2', '3', '4', '5', '6', '7', '8',
190
+ 'figure_1', 'figure_2', 'figure_3', 'figure_4', 'figure_5', 'figure_6', 'figure_7', 'figure_8', 'figure_9',
191
+ ]
192
+ components = {}
193
+ for key in components_name:
194
+ comp = get_component_byid(smp, key)
195
+ components.update({key: comp})
196
+ return components
197
+
198
+
199
+ def get_component_byid(smp: Sample, comp_id: str):
200
+ """
201
+ Get a component (Table or Plot) based on input id
202
+ Parameters
203
+ ----------
204
+ smp
205
+ comp_id
206
+
207
+ Returns
208
+ -------
209
+
210
+ """
211
+ for key, val in smp.__dict__.items():
212
+ if isinstance(val, (Plot, Table, Info)) and getattr(val, 'id') == comp_id:
213
+ return val
214
+
215
+
216
+ def get_plot_set(plot: Plot, comp_id):
217
+ """
218
+ Get a Set, Text, Axis, Label of a sample instance based on given id
219
+ """
220
+ for v in [getattr(plot, k) for k in dir(plot)]:
221
+ if isinstance(v, Plot.BasicAttr) and v.id.lower() == comp_id.lower():
222
+ return v
223
+ return None
224
+
225
+
226
+ # =======================
227
+ # Update
228
+ # =======================
229
+ def update_plot_from_dict(plot, attrs: dict):
230
+ """
231
+ plot is a Sample.Plot instance, attrs should be a dict like {'xaxis': {'show_splitline': False}, 'yaxis': {...}}
232
+ """
233
+
234
+ def _do(plot, attrs: dict):
235
+ for k1, v1 in attrs.items():
236
+ if isinstance(v1, dict):
237
+ if hasattr(plot, k1):
238
+ if isinstance(getattr(plot, k1), dict):
239
+ setattr(plot, k1, v1)
240
+ else:
241
+ _do(getattr(plot, k1), v1)
242
+ else:
243
+ setattr(plot, k1, v1)
244
+
245
+ _do(plot=plot, attrs=attrs)
246
+ return plot
247
+
248
+
249
+ # =======================
250
+ # Merge sample instances
251
+ # =======================
252
+ def get_merged_smp(a: Sample, b: (Sample, dict)):
253
+ """ Comparing two sample instances a and b
254
+ This function is used to update sample instance to make sure JS can read properties it required.
255
+ Parameters
256
+ ----------
257
+ a : sample instance that has old attributes,
258
+ b : new sample instance that has some new attributes or a has similar but different name for this attribute
259
+
260
+ Returns
261
+ -------
262
+ None
263
+ return none, but a will be updated after calling this function
264
+
265
+ for example:
266
+ A = Sample(id = 'a', set1 = Set(id = 'set1', data = [], symbolSize = 10)),
267
+ B = Sample(id = 'b', set1 = Set(id = 'set1', data = [2023], symbol_size = 5, line_type = 'solid')),
268
+ after get_merged_smp(A, B), A will be Sample(id = 'a', 'set1' = Set(id = 'set1', data = [],
269
+ symbol_size = 10, line_type = 'solid'))
270
+ """
271
+
272
+ def get_similar_name(_name: str):
273
+ res = []
274
+ for i in range(len(_name) + 1):
275
+ str_list = [i for i in _name]
276
+ str_list.insert(i, '_')
277
+ res.append(''.join(str_list))
278
+ for i in range(len(_name)):
279
+ str_list = [i for i in _name]
280
+ str_list[i] = str_list[i].capitalize()
281
+ res.append(''.join(str_list))
282
+ for i in range(len(_name)):
283
+ str_list = [i for i in _name]
284
+ if _name[i] in '-_':
285
+ str_list.pop(i)
286
+ res.append(''.join(str_list))
287
+ return res
288
+
289
+ if not isinstance(b, dict):
290
+ b = b.__dict__
291
+
292
+ for name, attr in b.items():
293
+ if hasattr(a, name):
294
+ if isinstance(attr, (Plot, Table, Info, Plot.BasicAttr)):
295
+ if not type(getattr(a, name)) == type(attr):
296
+ setattr(a, name, type(attr)())
297
+ get_merged_smp(getattr(a, name), attr)
298
+ if isinstance(attr, dict) and isinstance(getattr(a, name), dict):
299
+ setattr(a, name, calc.basic.merge_dicts(getattr(a, name), attr))
300
+ if isinstance(attr, list) and isinstance(getattr(a, name), list):
301
+ if len(attr) > len(getattr(a, name)):
302
+ setattr(a, name, getattr(a, name) + attr[len(getattr(a, name)):])
303
+ continue
304
+ else:
305
+ for xxx in get_similar_name(name):
306
+ for xx in get_similar_name(xxx):
307
+ for x in get_similar_name(xx):
308
+ if hasattr(a, x):
309
+ # print(f'Has similar {name} = {x}: {getattr(a, x)}')
310
+ setattr(a, name, getattr(a, x))
311
+ break
312
+ else:
313
+ continue
314
+ break
315
+ else:
316
+ continue
317
+ break
318
+ if not hasattr(a, name):
319
+ setattr(a, name, attr)
320
+
321
+
322
+ # =======================
323
+ # Difference between two sample instances
324
+ # =======================
325
+ def get_diff_smp(backup: (dict, Sample), smp: (dict, Sample)):
326
+ """ Comparing two sample component dicts or sample instances, and return difference between them.
327
+ Parameters
328
+ ----------
329
+ backup : backup of sample.Components or sample before changed.
330
+ smp : sample.Components or sample after changed
331
+
332
+ Returns
333
+ -------
334
+ dict
335
+ dict of keys and values that have difference, iterate to a sepcial difference,
336
+
337
+ for example:
338
+ A = Sample(id = 'a', set1 = Set(id = 'set1', data = [])),
339
+ B = Sample(id = 'b', set1 = Set(id = 'set1', data = [2023])),
340
+ res = get_diff_smp(A, B) will be {'id': 'b', 'set1': {'data': [2023]}}
341
+
342
+ """
343
+ res = {}
344
+ if isinstance(backup, Sample) and isinstance(smp, Sample):
345
+ return get_diff_smp(backup.__dict__, smp.__dict__)
346
+ for name, attr in smp.items():
347
+ if name not in backup.keys() or not isinstance(backup[name], type(attr)):
348
+ res.update({name: attr})
349
+ continue
350
+ if isinstance(attr, dict):
351
+ # if name not in backup.keys():
352
+ # res.update({name: attr})
353
+ # continue
354
+ _res = get_diff_smp(backup[name], attr)
355
+ if _res != {}:
356
+ res.update({name: _res})
357
+ continue
358
+ if isinstance(attr, np.ndarray):
359
+ if not np.array_equal(attr, backup[name]):
360
+ res.update({name: attr})
361
+ continue
362
+ if isinstance(attr, (Plot, Table, Plot.Text, Plot.Axis, Plot.Set,
363
+ Plot.Label, Plot.BasicAttr, Info)):
364
+ _res = get_diff_smp(backup[name].__dict__, attr.__dict__)
365
+ if _res != {}:
366
+ res.update({name: _res})
367
+ continue
368
+ if str(backup[name]) == str(attr) or backup[name] == attr:
369
+ continue
370
+ res.update({name: attr})
371
+ return res