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/__init__.py +178 -0
- ararpy/calc/__init__.py +11 -0
- ararpy/calc/age.py +161 -0
- ararpy/calc/arr.py +490 -0
- ararpy/calc/basic.py +57 -0
- ararpy/calc/corr.py +240 -0
- ararpy/calc/err.py +117 -0
- ararpy/calc/histogram.py +166 -0
- ararpy/calc/isochron.py +194 -0
- ararpy/calc/jvalue.py +38 -0
- ararpy/calc/plot.py +68 -0
- ararpy/calc/raw_funcs.py +118 -0
- ararpy/calc/regression.py +961 -0
- ararpy/calc/spectra.py +63 -0
- ararpy/files/__init__.py +2 -0
- ararpy/files/arr_file.py +86 -0
- ararpy/files/basic.py +100 -0
- ararpy/files/calc_file.py +683 -0
- ararpy/files/export.py +1181 -0
- ararpy/files/json.py +49 -0
- ararpy/files/new_file.py +31 -0
- ararpy/files/raw.py +115 -0
- ararpy/files/raw_file.py +14 -0
- ararpy/files/xls.py +27 -0
- ararpy/smp/__init__.py +17 -0
- ararpy/smp/basic.py +371 -0
- ararpy/smp/calculation.py +94 -0
- ararpy/smp/consts.py +20 -0
- ararpy/smp/corr.py +376 -0
- ararpy/smp/initial.py +232 -0
- ararpy/smp/plots.py +636 -0
- ararpy/smp/sample.py +911 -0
- ararpy/smp/style.py +191 -0
- ararpy/smp/table.py +131 -0
- ararpy-0.0.1a1.dist-info/LICENSE +21 -0
- ararpy-0.0.1a1.dist-info/METADATA +269 -0
- ararpy-0.0.1a1.dist-info/RECORD +39 -0
- ararpy-0.0.1a1.dist-info/WHEEL +5 -0
- ararpy-0.0.1a1.dist-info/top_level.txt +1 -0
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)
|
ararpy/files/new_file.py
ADDED
|
@@ -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
|
+
|
ararpy/files/raw_file.py
ADDED
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
|