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.
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ """
4
+ # ==========================================
5
+ # Copyright 2023 Yang
6
+ # ararpy - calc - data
7
+ # ==========================================
8
+ #
9
+ # Get plot data, including isochron diagram data points, age spectra data
10
+ #
11
+ """
12
+
13
+ from math import atan, cos, sin
14
+ from ..calc import arr
15
+ import numpy as np
16
+
17
+
18
+ def get_data(x: list, sx: list, y: list, sy: list, z: list, sz: list, f: int = 1):
19
+ """
20
+ Get isochron data based on isotopic values. Values of two x and y axes of isochron
21
+ plots are x / z and y / z.
22
+
23
+ Parameters
24
+ ----------
25
+ x :
26
+ sx :
27
+ y :
28
+ sy :
29
+ z :
30
+ sz :
31
+ f :
32
+
33
+ Returns
34
+ -------
35
+ 2D lists [X, sX, Y, sY, Ri]
36
+ X and Y are coordinate values of data points in x and y axes respectively.
37
+ sX and sY are corresponding errors. Ri is a list of error correlation factors.
38
+ """
39
+ if np.issubdtype(type(f), np.integer) and f > 1:
40
+ sx, sy, sz = np.divide([sx, sy, sz], f)
41
+ # x / z
42
+ k0, k1 = arr.div((x, sx), (z, sz))
43
+ # y / z
44
+ k2, k3 = arr.div((y, sy), (z, sz))
45
+ # ri
46
+ k4 = arr.cor(np.divide(sx, x), np.divide(sy, y), np.divide(sz, z))
47
+ return [k0, k1, k2, k3, k4]
48
+
49
+
50
+ def get_3d_data(x1: list, sx1: list, x2: list, sx2: list,
51
+ x3: list, sx3: list, z: list, sz: list, f: int = 1):
52
+ """ Get values of three axes, x, y, z, based on the given isotopic values
53
+ x = x1 / z, y = x2 / z, z = x3 / z
54
+ Parameters
55
+ ----------
56
+ x1
57
+ sx1
58
+ x2
59
+ sx2
60
+ x3
61
+ sx3
62
+ z
63
+ sz
64
+ f
65
+
66
+ Returns
67
+ -------
68
+ list.
69
+ """
70
+ if np.issubdtype(type(f), np.integer) and f > 1:
71
+ sx1, sx2, sx3 = np.divide([sx1, sx2, sx3], f)
72
+ # x1 / z
73
+ k0, k1 = arr.div((x1, sx1), (z, sz))
74
+ # x2 / z
75
+ k2, k3 = arr.div((x2, sx2), (z, sz))
76
+ # x3 / z
77
+ k4, k5 = arr.div((x3, sx3), (z, sz))
78
+ # r1 between x and y
79
+ r1 = arr.cor(np.divide(sx1, x1), np.divide(sx2, x2), np.divide(sz, z))
80
+ # r2 between x and z
81
+ r2 = arr.cor(np.divide(sx1, x1), np.divide(sx3, x3), np.divide(sz, z))
82
+ # r3 between y and z
83
+ r3 = arr.cor(np.divide(sx2, x2), np.divide(sx3, x3), np.divide(sz, z))
84
+ return [k0, k1, k2, k3, k4, k5, r1, r2, r3]
85
+
86
+
87
+ def get_ellipse(x: float, sx: float, y: float, sy: float, r: float):
88
+ """ Error ellipses of data points in isochrons. Get 24 points of a ellispse
89
+
90
+ Parameters
91
+ ----------
92
+ x
93
+ sx
94
+ y
95
+ sy
96
+ r
97
+
98
+ Returns
99
+ -------
100
+ list of 24 points. [[x1, y1], ..., [x24, y24]]
101
+ """
102
+ x, sx, y, sy, r = float(x), float(sx), float(y), float(sy), float(r)
103
+ Qxx = sx ** 2
104
+ Qxy = sx * sy * r
105
+ Qyy = sy ** 2
106
+ # Calculate the ellipse's short semi-axial and long semi-axial
107
+ k = pow((Qxx - Qyy) ** 2 + 4 * Qxy ** 2, 0.5)
108
+ Qee = (Qxx + Qyy + k) / 2
109
+ Qff = (Qxx + Qyy - k) / 2
110
+ e = pow(Qee, 0.5) # long semi-axial
111
+ f = pow(Qff, 0.5) # short semi-axial
112
+ phi_e = atan((Qee - Qxx) / Qxy) if Qxy != 0 else 0 if Qxx >= Qyy else np.pi / 2 # radian
113
+
114
+ # adjust
115
+ plt_sfactor = 1
116
+ if plt_sfactor == 1:
117
+ v = 2.279 # 68% confidence limit, 1 sigma
118
+ elif plt_sfactor == 2:
119
+ v = 5.991 # 95% confidence limit, 2 sigma, Isoplot R always gives ellipse with 95% confidence
120
+ else:
121
+ v = 1
122
+ e = e * pow(v, 0.5)
123
+ f = f * pow(v, 0.5)
124
+
125
+ ellipse_points = []
126
+ for i in range(24):
127
+ theta = i * 15 / 180 * np.pi
128
+ ellipse_points.append([
129
+ e * cos(theta) * cos(phi_e) - f * sin(theta) * sin(phi_e) + x,
130
+ e * cos(theta) * sin(phi_e) + f * sin(theta) * cos(phi_e) + y
131
+ ])
132
+
133
+ return ellipse_points
134
+
135
+
136
+ def get_line_points(xscale, yscale, coeffs=None):
137
+ """
138
+
139
+ Parameters
140
+ ----------
141
+ xscale : x boundary, [min, max]
142
+ yscale : y boundary, [min, max]
143
+ coeffs : y = coeffs[0] + coeffs[1:] * [x]
144
+
145
+ Returns
146
+ -------
147
+ List of data points
148
+ """
149
+
150
+ if not isinstance(coeffs, list) or len(coeffs) < 2:
151
+ raise ValueError(f"Coeffs should be a list with length with 2.")
152
+ get_y = lambda x: coeffs[0] + coeffs[1] * x
153
+ get_x = lambda y: (y - coeffs[0]) / coeffs[1] if coeffs[1] != 0 else None
154
+ res = []
155
+ for point in [
156
+ [xscale[0], get_y(xscale[0])], [xscale[1], get_y(xscale[1])],
157
+ [get_x(yscale[0]), yscale[0]], [get_x(yscale[1]), yscale[1]],
158
+ ]:
159
+ if xscale[0] <= point[0] <= xscale[1] and yscale[0] <= point[1] <= yscale[1]:
160
+ res.append(point)
161
+ return res
162
+
163
+
164
+ def get_set_data(total_data: list, set1_index: list, set2_index: list, unselected_index: list):
165
+ """
166
+
167
+ Parameters
168
+ ----------
169
+ total_data
170
+ set1_index
171
+ set2_index
172
+ unselected_index
173
+
174
+ Returns
175
+ -------
176
+
177
+ """
178
+ set_1, set_2, unslected = [], [], []
179
+ # Remove string and None type in data
180
+ none_index = []
181
+ for each in total_data:
182
+ none_index = none_index + [key for key, val in enumerate(each) if isinstance(val, (str, type(None)))]
183
+ none_index = list(set(none_index))
184
+ none_index.sort(reverse=True)
185
+ for each in total_data:
186
+ for i in none_index:
187
+ each.pop(i)
188
+ set1_index.sort()
189
+ set2_index.sort()
190
+ for col in total_data:
191
+ unslected.append([col[i] for i in unselected_index if i < len(col)])
192
+ set_1.append([col[i] for i in set1_index if i < len(col)])
193
+ set_2.append([col[i] for i in set2_index if i < len(col)])
194
+ return set_1, set_2, unslected
ararpy/calc/jvalue.py ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ """
4
+ # ==========================================
5
+ # Copyright 2023 Yang
6
+ # ararpy - calc - jvalue
7
+ # ==========================================
8
+ #
9
+ #
10
+ #
11
+ """
12
+ import numpy as np
13
+
14
+
15
+ def j_value(age: float, sage: float, r: float, sr: float, f: float, rsf: float):
16
+ """ Calculate J value according to the given age and the ratio
17
+
18
+ Parameters
19
+ ----------
20
+ age : age of the reference standard sample, in Ma
21
+ sage : 1 sigma error of age
22
+ r : 40/39 ratio of the reference standard standard sample
23
+ sr : 1 sigma error of 40/39 ratio
24
+ f : decay constant(lambda) of K
25
+ rsf : relative error of decay constant
26
+
27
+ Returns
28
+ -------
29
+ tuple of J value and error
30
+ """
31
+ f = f * 1000000 # exchange to unit of Ma
32
+ rsf = f * rsf / 100 # exchange to absolute error
33
+ k0 = (np.exp(f * age) - 1) / r
34
+ v1 = rsf ** 2 * (age * np.exp(f * age) / r) ** 2
35
+ v2 = sage ** 2 * (f * np.exp(f * age) / r) ** 2
36
+ v3 = sr ** 2 * ((1 - np.exp(f * age)) / r ** 2) ** 2
37
+ k1 = pow(v1 + v2 + v3, 0.5)
38
+ return k0, k1
ararpy/calc/plot.py ADDED
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ """
4
+ # ==========================================
5
+ # Copyright 2023 Yang
6
+ # ararpy - calc - plot
7
+ # ==========================================
8
+ #
9
+ #
10
+ #
11
+ """
12
+ import traceback
13
+
14
+ import decimal
15
+ from math import exp, log, cos, acos, ceil, sqrt, atan, sin, gamma
16
+ from typing import List, Any
17
+ from scipy.optimize import fsolve
18
+ from scipy.stats import distributions
19
+ import numpy as np
20
+
21
+ math_e = 2.718281828459045
22
+ math_pi = 3.141592653589793
23
+
24
+
25
+ def get_axis_scale(data: list, count=6, increment=None, extra_count=0, min_interval=1):
26
+ """
27
+
28
+ Parameters
29
+ ----------
30
+ data
31
+ count
32
+ increment
33
+ extra_count
34
+ min_interval
35
+
36
+ Returns
37
+ -------
38
+
39
+ """
40
+ if len(data) == 0:
41
+ return 0, 1, 5, 0.2 # Default return
42
+ _max = float(np.ma.masked_invalid(data).max())
43
+ _min = float(np.ma.masked_invalid(data).min())
44
+ interval = (_max - _min) / count
45
+ if interval == 0:
46
+ interval = 10
47
+ mag = 10 ** int(log(interval, 10)) if interval >= 1 else 10 ** (int(log(interval, 10)) - 1)
48
+ if not increment:
49
+ increment = decimal.Decimal(
50
+ str(decimal.Decimal(int(interval / mag // min_interval) + min_interval) * decimal.Decimal(str(mag))))
51
+ else:
52
+ increment = decimal.Decimal(increment)
53
+ start = decimal.Decimal(0)
54
+ if _min < 0:
55
+ start = -increment
56
+ while start > _min:
57
+ start -= increment
58
+ else:
59
+ while start + increment < _min:
60
+ start += increment
61
+ count = 0
62
+ while count * increment + start < _max:
63
+ count += 1
64
+ end = decimal.Decimal(str(count + extra_count)) * decimal.Decimal(str(increment)) + start
65
+ start -= decimal.Decimal(extra_count) * decimal.Decimal(str(increment))
66
+ start = 0 if start < 0 <= _min else start
67
+ count = (end - start) / increment
68
+ return float(start), float(end), int(count), float(increment)
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ """
4
+ # ==========================================
5
+ # Copyright 2023 Yang
6
+ # ararpy - calc - raw_funcs
7
+ # ==========================================
8
+ #
9
+ #
10
+ #
11
+ """
12
+ import traceback
13
+ import numpy as np
14
+ from .arr import transpose, is_twoD
15
+ from . import regression
16
+
17
+
18
+ """get regression results for raw data points"""
19
+
20
+
21
+ def get_raw_data_regression_results(points_data, unselected: list = None):
22
+ """
23
+ Parameters
24
+ ----------
25
+ points_data : two dimensional list. like [[x1, y1], [x2, y2], ..., [xn, yn]]
26
+ unselected :
27
+
28
+ Returns
29
+ -------
30
+
31
+ """
32
+ if unselected is None:
33
+ unselected = []
34
+ linesData, linesResults = [], []
35
+ x, y = transpose(points_data)
36
+ un_x = transpose(unselected)[0] if is_twoD(unselected) else []
37
+ reg_handler = [
38
+ regression.linest, regression.quadratic, regression.exponential,
39
+ regression.power, regression.average]
40
+ size = 50
41
+ lines_x = [(max(x + un_x) - 0) / size * i for i in range(size + 1)]
42
+ for i in range(len(reg_handler)):
43
+ try:
44
+ res = reg_handler[i](a0=y, a1=x)
45
+ line_data = transpose([lines_x, res[7](lines_x)])
46
+ line_results = res[0:4]
47
+ if np.isin(np.inf, line_data) or np.isin(np.nan, line_data):
48
+ raise ZeroDivisionError(f"Infinite value or nan value.")
49
+ if abs(res[0] - min(y)) > 5 * (max(y) - min(y)):
50
+ raise ValueError
51
+ except RuntimeError:
52
+ line_data, line_results = [], ['RuntimeError', np.nan, np.nan, np.nan]
53
+ except np.linalg.LinAlgError:
54
+ line_data, line_results = [], ['MatrixError', np.nan, np.nan, np.nan]
55
+ except TypeError or IndexError:
56
+ line_data, line_results = [], ['NotEnoughPoints', np.nan, np.nan, np.nan]
57
+ except ZeroDivisionError:
58
+ line_data, line_results = [], [np.inf, np.nan, np.nan, np.nan]
59
+ except ValueError:
60
+ line_data, line_results = [], ['BadFitting', np.nan, np.nan, np.nan]
61
+ except:
62
+ line_data, line_results = [], [np.nan, np.nan, np.nan, np.nan]
63
+ linesData.append(line_data)
64
+ linesResults.append(line_results)
65
+ return linesData, linesResults
66
+
67
+ #
68
+ # def get_lines_data(sequenceData, only_isotope=None):
69
+ # def _get_scatter_x(a: list, nope=50):
70
+ # interval = (max(a) - 0) / nope
71
+ # return [interval * (i + 1) for i in range(nope)]
72
+ #
73
+ # linesData, linesResults = [], []
74
+ # for i in range(5):
75
+ # try:
76
+ # if isinstance(only_isotope, int) and i != only_isotope:
77
+ # raise IndexError
78
+ # _ = transpose(sequenceData[i])
79
+ # x, y = _[0], _[1]
80
+ # try:
81
+ # line_res = regression.linest(a0=y, a1=x)
82
+ # except:
83
+ # line_res = ['BadFitting', 'None', 'None', 'None', 'None', 'None', 'None', 'None', 'None']
84
+ # try:
85
+ # quad_res = regression.quadratic(a0=y, a1=x)
86
+ # except:
87
+ # quad_res = ['BadFitting', 'None', 'None', 'None', 'None', 'None', 'None', 'None', 'None']
88
+ # try:
89
+ # exp_res = regression.exponential(a0=y, a1=x)
90
+ # except:
91
+ # exp_res = ['BadFitting', 'None', 'None', 'None', 'None', 'None', 'None', 'None', 'None']
92
+ # try:
93
+ # pow_res = regression.power(a0=y, a1=x)
94
+ # except:
95
+ # pow_res = ['BadFitting', 'None', 'None', 'None', 'None', 'None', 'None', 'None', 'None']
96
+ # else:
97
+ # if abs(pow_res[0]) > 10 * max(y):
98
+ # pow_res = ['BadFitting', 'None', 'None', 'None', 'None', 'None', 'None', 'None', 'None']
99
+ # elif pow_res[0] < 0:
100
+ # pow_res = ['Negative', 'None', 'None', 'None', 'None', 'None', 'None', 'None', 'None']
101
+ # try:
102
+ # ave_res = regression.average(a0=y)
103
+ # except:
104
+ # ave_res = ['BadFitting', 'None', 'None', 'None', 'None', 'None', 'None', 'None', 'None']
105
+ #
106
+ # lines_x = [_x for _x in [0] + _get_scatter_x(x)]
107
+ # linesData.append([])
108
+ # for index, res in enumerate([line_res, quad_res, exp_res, pow_res, ave_res]):
109
+ # try:
110
+ # linesData[i].append(transpose([lines_x, res[7](lines_x)]))
111
+ # except Exception as e:
112
+ # linesData[i].append([])
113
+ # linesResults.append([line_res[0:4], quad_res[0:4], exp_res[0:4], pow_res[0:4], ave_res[0:4]])
114
+ # except IndexError or ValueError:
115
+ # linesData.append([[], [], [], [], []])
116
+ # linesResults.append([[], [], [], [], []])
117
+ #
118
+ # return linesData, linesResults