proadv 2.0.2__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.
- proadv/__init__.py +0 -0
- proadv/filtration/__init__.py +0 -0
- proadv/filtration/detection/__init__.py +0 -0
- proadv/filtration/detection/acceleration.py +65 -0
- proadv/filtration/detection/bivariatekernel.py +53 -0
- proadv/filtration/detection/correlation.py +78 -0
- proadv/filtration/detection/phasespace.py +173 -0
- proadv/filtration/detection/poincare.py +46 -0
- proadv/filtration/detection/pollution.py +32 -0
- proadv/filtration/detection/spherical.py +181 -0
- proadv/filtration/detection/trivariatekernel.py +521 -0
- proadv/filtration/replacement/__init__.py +0 -0
- proadv/filtration/replacement/replacements.py +187 -0
- proadv/kernel/__init__.py +0 -0
- proadv/kernel/bivariate.py +437 -0
- proadv/statistics/__init__.py +0 -0
- proadv/statistics/descriptive.py +328 -0
- proadv/statistics/distributions/__init__.py +1 -0
- proadv/statistics/distributions/normal.py +126 -0
- proadv/statistics/moment.py +122 -0
- proadv/statistics/series.py +337 -0
- proadv/statistics/spread.py +119 -0
- proadv/tests/__init__.py +0 -0
- proadv/tests/statistics/__init__.py +0 -0
- proadv/tests/statistics/descriptive/__init__.py +0 -0
- proadv/tests/statistics/descriptive/test_max.py +36 -0
- proadv/tests/statistics/descriptive/test_mean.py +37 -0
- proadv/tests/statistics/descriptive/test_median.py +26 -0
- proadv/tests/statistics/descriptive/test_min.py +36 -0
- proadv/tests/statistics/descriptive/test_mode.py +34 -0
- proadv-2.0.2.dist-info/METADATA +167 -0
- proadv-2.0.2.dist-info/RECORD +34 -0
- proadv-2.0.2.dist-info/WHEEL +5 -0
- proadv-2.0.2.dist-info/top_level.txt +1 -0
proadv/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from proadv.statistics.spread import std
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def acceleration_thresholding(velocities, frequency, tag, gravity=980, k_gravity=1.5, k_sigma=1):
|
|
6
|
+
"""
|
|
7
|
+
Detects acceleration events based on velocity data.
|
|
8
|
+
|
|
9
|
+
This function calculates acceleration events based on velocity data, considering thresholds
|
|
10
|
+
for acceleration magnitude and velocity deviation from mean.
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
------
|
|
14
|
+
velocities (array_like): Array of velocity data.
|
|
15
|
+
An array-like object containing velocity values.
|
|
16
|
+
frequency (float): Sampling frequency.
|
|
17
|
+
The frequency at which the velocity data is sampled,
|
|
18
|
+
used to calculate acceleration from velocity differences.
|
|
19
|
+
tag (int): Tag for acceleration direction.
|
|
20
|
+
An integer indicating the direction of acceleration to detect:
|
|
21
|
+
- 1 for positive acceleration (increasing velocity)
|
|
22
|
+
- 2 for negative acceleration (decreasing velocity).
|
|
23
|
+
gravity (float): Value of gravity.
|
|
24
|
+
The acceleration due to gravity, used as a reference for acceleration thresholds.
|
|
25
|
+
Default is 980 (m/s^2).
|
|
26
|
+
k_gravity (float): Threshold multiplier for gravity.
|
|
27
|
+
A multiplier applied to the gravity value to determine the acceleration threshold.
|
|
28
|
+
Default is 1.5.
|
|
29
|
+
k_sigma (float, optional): Threshold multiplier for standard deviation.
|
|
30
|
+
A multiplier applied to the standard deviation of velocities to determine velocity deviation threshold.
|
|
31
|
+
Default is 1.
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
------
|
|
35
|
+
accel_indices (array_like): Indices of acceleration events.
|
|
36
|
+
An array containing the indices of the detected acceleration events.
|
|
37
|
+
|
|
38
|
+
Raises
|
|
39
|
+
------
|
|
40
|
+
ValueError: If invalid tag value is provided.
|
|
41
|
+
Raised when the tag value is not 1 or 2.
|
|
42
|
+
|
|
43
|
+
References
|
|
44
|
+
------
|
|
45
|
+
Goring, Derek G., and Vladimir I. Nikora.
|
|
46
|
+
"Despiking acoustic Doppler velocimeter data."
|
|
47
|
+
Journal of hydraulic engineering 128.1 (2002): 117-126.
|
|
48
|
+
"""
|
|
49
|
+
velocities = np.asarray(velocities)
|
|
50
|
+
|
|
51
|
+
# Calculate velocity differences and multiply by frequency to obtain acceleration
|
|
52
|
+
differences = np.concatenate((np.array([0]), np.diff(velocities) * frequency))
|
|
53
|
+
|
|
54
|
+
if tag == 1:
|
|
55
|
+
# Detect positive acceleration events
|
|
56
|
+
accel_indices = np.intersect1d(np.where(differences > k_gravity * gravity)[0],
|
|
57
|
+
np.where(velocities > np.mean(velocities) + k_sigma * std(velocities))[0])
|
|
58
|
+
elif tag == 2:
|
|
59
|
+
# Detect negative acceleration events
|
|
60
|
+
accel_indices = np.intersect1d(np.where(differences < -k_gravity * gravity)[0],
|
|
61
|
+
np.where(velocities < np.mean(velocities) - k_sigma * std(velocities))[0])
|
|
62
|
+
else:
|
|
63
|
+
raise ValueError("Invalid tag value. Expected 1 or 2.")
|
|
64
|
+
|
|
65
|
+
return accel_indices
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _cutoff(density_profile, velocity_profile, c1_threshold, c2_threshold, force_profile, peak_index, grid):
|
|
5
|
+
"""
|
|
6
|
+
Find the lower and upper cutoff velocities based on specified criteria.
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
------
|
|
10
|
+
density_profile (array_like): Density profile of the system.
|
|
11
|
+
velocity_profile (array_like): Velocity profile of the system.
|
|
12
|
+
c1_threshold (float): Threshold ratio for force compared to peak force.
|
|
13
|
+
c2_threshold (float): Threshold for absolute change in force.
|
|
14
|
+
force_profile (array_like): Force profile of the system.
|
|
15
|
+
peak_index (int): Index of the peak force in the force profile.
|
|
16
|
+
grid (int): Grid spacing or resolution.
|
|
17
|
+
|
|
18
|
+
Returns
|
|
19
|
+
------
|
|
20
|
+
lower_cutoff_velocity, upper_cutoff_velocity: A tuple containing the lower and upper cutoff velocities.
|
|
21
|
+
|
|
22
|
+
Note
|
|
23
|
+
------
|
|
24
|
+
This function assumes that the input arrays (density_profile, velocity_profile, and force_profile)
|
|
25
|
+
are of the same length.
|
|
26
|
+
length.
|
|
27
|
+
The density profile, velocity profile, and force profile should be consistent and correspond to
|
|
28
|
+
each other at each index.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
profile_length = force_profile.size
|
|
32
|
+
delta_force = np.append([0], np.diff(force_profile)) * grid / density_profile
|
|
33
|
+
|
|
34
|
+
# Find lower cutoff index
|
|
35
|
+
for i in range(peak_index - 1, 0, -1):
|
|
36
|
+
if force_profile[i] / force_profile[peak_index] <= c1_threshold and abs(delta_force[i]) <= c2_threshold:
|
|
37
|
+
lower_cutoff_index = i
|
|
38
|
+
break
|
|
39
|
+
else:
|
|
40
|
+
lower_cutoff_index = 1
|
|
41
|
+
|
|
42
|
+
# Find upper cutoff index
|
|
43
|
+
for i in range(peak_index + 1, profile_length - 1):
|
|
44
|
+
if force_profile[i] / force_profile[peak_index] <= c1_threshold and abs(delta_force[i]) <= c2_threshold:
|
|
45
|
+
upper_cutoff_index = i
|
|
46
|
+
break
|
|
47
|
+
else:
|
|
48
|
+
upper_cutoff_index = profile_length - 1
|
|
49
|
+
|
|
50
|
+
lower_cutoff_velocity = velocity_profile[lower_cutoff_index]
|
|
51
|
+
upper_cutoff_velocity = velocity_profile[upper_cutoff_index]
|
|
52
|
+
|
|
53
|
+
return lower_cutoff_velocity, upper_cutoff_velocity
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from proadv.filtration.detection.poincare import calculate_ab, calculate_rho
|
|
3
|
+
from proadv.statistics.spread import std
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def calculate_parameters(up, vp, wp):
|
|
7
|
+
"""
|
|
8
|
+
Calculate parameters required for velocity correlation.
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
------
|
|
12
|
+
up (array_like): Array of the first velocity component.
|
|
13
|
+
vp (array_like): Array of the second velocity component.
|
|
14
|
+
wp (array_like): Array of the third velocity component.
|
|
15
|
+
|
|
16
|
+
Returns
|
|
17
|
+
------
|
|
18
|
+
lambda_ (float): Lambda value used in velocity correlation detection.
|
|
19
|
+
std_u (float): Standard deviation of the longitudinal velocity component.
|
|
20
|
+
std_v (float): Standard deviation of the transverse velocity component.
|
|
21
|
+
std_w (float): Standard deviation of the vertical velocity component.
|
|
22
|
+
"""
|
|
23
|
+
data_size = up.size
|
|
24
|
+
std_u = std(up)
|
|
25
|
+
std_v = std(vp)
|
|
26
|
+
std_w = std(wp)
|
|
27
|
+
lambda_ = np.sqrt(2 * np.log(data_size))
|
|
28
|
+
return lambda_, std_u, std_v, std_w
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def velocity_correlation(ui, vi, wi):
|
|
32
|
+
"""
|
|
33
|
+
Detect spikes using velocity correlation filter, based on three velocity components.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
------
|
|
37
|
+
ui (array_like): Array of the longitudinal velocity component.
|
|
38
|
+
vi (array_like): Array of the transverse velocity component.
|
|
39
|
+
wi (array_like): Array of the vertical velocity component.
|
|
40
|
+
|
|
41
|
+
Returns
|
|
42
|
+
------
|
|
43
|
+
correl_indices (array_like): Indices of spikes detected by velocity correlation.
|
|
44
|
+
|
|
45
|
+
References
|
|
46
|
+
------
|
|
47
|
+
Cea, L., J. Puertas, and L. Pena.
|
|
48
|
+
"Velocity measurements on highly turbulent free surface flow using ADV."
|
|
49
|
+
Experiments in fluids 42 (2007): 333-348.
|
|
50
|
+
"""
|
|
51
|
+
from proadv.statistics.descriptive import mean
|
|
52
|
+
ui, vi, wi = ui - mean(ui), vi - mean(vi), wi - mean(wi)
|
|
53
|
+
lambda_, std_u, std_v, std_w = calculate_parameters(ui, vi, wi)
|
|
54
|
+
|
|
55
|
+
# Calculate angles between velocity components
|
|
56
|
+
theta1 = np.arctan(np.sum(ui * vi) / np.sum(ui ** 2))
|
|
57
|
+
theta2 = np.arctan(np.sum(ui * wi) / np.sum(ui ** 2))
|
|
58
|
+
theta3 = np.arctan(np.sum(vi * wi) / np.sum(vi ** 2))
|
|
59
|
+
|
|
60
|
+
# Calculate 'a' and 'b' coefficients for each angle
|
|
61
|
+
a1, b1 = calculate_ab(std_u, std_v, theta1, lambda_)
|
|
62
|
+
a2, b2 = calculate_ab(std_u, std_w, theta2, lambda_)
|
|
63
|
+
a3, b3 = calculate_ab(std_v, std_w, theta3, lambda_)
|
|
64
|
+
|
|
65
|
+
# Calculate rho values for each component pair
|
|
66
|
+
rho1 = calculate_rho(ui, vi, theta1, a1, b1)
|
|
67
|
+
rho2 = calculate_rho(ui, wi, theta2, a2, b2)
|
|
68
|
+
rho3 = calculate_rho(vi, wi, theta3, a3, b3)
|
|
69
|
+
|
|
70
|
+
# Find indices where rho values exceed 1 (indicating correlation)
|
|
71
|
+
x1 = np.nonzero(rho1 > 1)[0]
|
|
72
|
+
x2 = np.nonzero(rho2 > 1)[0]
|
|
73
|
+
x3 = np.nonzero(rho3 > 1)[0]
|
|
74
|
+
|
|
75
|
+
# Combine all detected indices and remove duplicates
|
|
76
|
+
correl_indices = np.sort(np.unique(np.concatenate((x1, x2, x3))))
|
|
77
|
+
|
|
78
|
+
return correl_indices
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from proadv.filtration.detection.poincare import calculate_rho
|
|
3
|
+
from proadv.statistics.spread import std
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def calculate_derivatives(c):
|
|
7
|
+
"""
|
|
8
|
+
Calculate time-independent first and second order derivatives of the input data.
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
------
|
|
12
|
+
c (array_like): Input data.
|
|
13
|
+
|
|
14
|
+
Returns
|
|
15
|
+
------
|
|
16
|
+
dc (array_like): First derivative of the input data.
|
|
17
|
+
dc2 (array_like): Second derivative of the input data.
|
|
18
|
+
"""
|
|
19
|
+
# Initialize arrays for first and second derivatives
|
|
20
|
+
dc = np.zeros_like(c)
|
|
21
|
+
dc2 = np.zeros_like(c)
|
|
22
|
+
|
|
23
|
+
# Calculate first derivative
|
|
24
|
+
for i in range(1, len(c) - 1):
|
|
25
|
+
dc[i] = np.around((c[i + 1] - c[i - 1]) / 2, 4)
|
|
26
|
+
|
|
27
|
+
# Calculate second derivative
|
|
28
|
+
for i in range(1, len(c) - 1):
|
|
29
|
+
dc2[i] = np.around((dc[i + 1] - dc[i - 1]) / 2, 4)
|
|
30
|
+
|
|
31
|
+
return dc, dc2
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def calculate_parameters(c, dc, dc2):
|
|
35
|
+
"""
|
|
36
|
+
Calculate parameters for phase-space thresholding.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
------
|
|
40
|
+
c (array_like): Array of the velocity component.
|
|
41
|
+
dc (array_like): First derivative of the input data.
|
|
42
|
+
dc2 (array_like): Second derivative of the input data.
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
------
|
|
46
|
+
std_c (float): Standard deviation of the input data.
|
|
47
|
+
std_dc (float): Standard deviation of the first derivative.
|
|
48
|
+
std_dc2 (float): Standard deviation of the second derivative.
|
|
49
|
+
lambda_ (float): Lambda value.
|
|
50
|
+
theta (float | rad): Angle between components.
|
|
51
|
+
a1 (float): Coefficient 'a1'.
|
|
52
|
+
b1 (float): Coefficient 'b1'.
|
|
53
|
+
a2 (float): Coefficient 'a2'.
|
|
54
|
+
b2 (float): Coefficient 'b2'.
|
|
55
|
+
a3 (float): Coefficient 'a3'.
|
|
56
|
+
b3 (float): Coefficient 'b3'.
|
|
57
|
+
"""
|
|
58
|
+
# Calculate standard deviations
|
|
59
|
+
std_c = std(c)
|
|
60
|
+
std_dc = std(dc)
|
|
61
|
+
std_dc2 = std(dc2)
|
|
62
|
+
|
|
63
|
+
# Calculate lambda value
|
|
64
|
+
lambda_ = np.sqrt(2 * np.log(len(c)))
|
|
65
|
+
|
|
66
|
+
# Calculate theta value
|
|
67
|
+
theta = np.arctan(np.sum(c * dc2) / np.sum(c ** 2))
|
|
68
|
+
|
|
69
|
+
# Calculate coefficients
|
|
70
|
+
a1 = lambda_ * std_c
|
|
71
|
+
b1 = lambda_ * std_dc
|
|
72
|
+
a2 = lambda_ * std_dc
|
|
73
|
+
b2 = lambda_ * std_dc2
|
|
74
|
+
fact = np.cos(theta) ** 4 - np.sin(theta) ** 4
|
|
75
|
+
a3 = np.sqrt(a1 ** 2 * np.cos(theta) ** 2 - b2 ** 2 * np.sin(theta) ** 2) / fact
|
|
76
|
+
b3 = np.sqrt(b2 ** 2 * np.cos(theta) ** 2 - a1 ** 2 * np.sin(theta) ** 2) / fact
|
|
77
|
+
return std_c, std_dc, std_dc2, lambda_, theta, a1, b1, a2, b2, a3, b3
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def phasespace_thresholding(c):
|
|
81
|
+
"""
|
|
82
|
+
Detect spikes using phase-space thresholding, based on each velocity component and
|
|
83
|
+
their first-order and second-order derivatives.
|
|
84
|
+
|
|
85
|
+
Phase-space thresholding is a method for detecting spikes or abrupt changes in time series data
|
|
86
|
+
by analyzing the behavior of the data in a multidimensional space defined by the data and its derivatives.
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
Parameters
|
|
90
|
+
------
|
|
91
|
+
c (array_like): Velocity component
|
|
92
|
+
|
|
93
|
+
Returns
|
|
94
|
+
------
|
|
95
|
+
phase_indices (array_like): Array containing the indices of detected spikes.
|
|
96
|
+
|
|
97
|
+
References
|
|
98
|
+
------
|
|
99
|
+
Goring, Derek G., and Vladimir I. Nikora.
|
|
100
|
+
"Despiking acoustic Doppler velocimeter data."
|
|
101
|
+
Journal of hydraulic engineering 128.1 (2002): 117-126.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
# Calculate first and second order derivatives of the input data
|
|
105
|
+
dc, dc2 = calculate_derivatives(c)
|
|
106
|
+
|
|
107
|
+
# Calculate parameters used in phase-space thresholding
|
|
108
|
+
std_c, std_dc, std_dc2, lambda_, theta, a1, b1, a2, b2, a3, b3 = calculate_parameters(c, dc, dc2)
|
|
109
|
+
|
|
110
|
+
# Calculate poincare map rho values for each dimension
|
|
111
|
+
rho1 = calculate_rho(c, dc, 0, a1, b1)
|
|
112
|
+
rho2 = calculate_rho(dc, dc2, 0, a2, b2)
|
|
113
|
+
rho3 = calculate_rho(c, dc2, theta, a3, b3)
|
|
114
|
+
|
|
115
|
+
# Find indices where rho values exceed the threshold
|
|
116
|
+
x1 = np.nonzero(rho1 > 1)[0]
|
|
117
|
+
x2 = np.nonzero(rho2 > 1)[0]
|
|
118
|
+
x3 = np.nonzero(rho3 > 1)[0]
|
|
119
|
+
|
|
120
|
+
# Concatenate and sort indices to get unique spike indices
|
|
121
|
+
phase_indices = np.sort(np.unique(np.concatenate((x1, x2, x3))))
|
|
122
|
+
|
|
123
|
+
return phase_indices
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def phasespace_thresholding_premodification(data, c1=1.5, c2=1.35):
|
|
127
|
+
"""
|
|
128
|
+
Perform phase space thresholding premodification on a given data array.
|
|
129
|
+
|
|
130
|
+
Parameters
|
|
131
|
+
------
|
|
132
|
+
data (array_like): Array of data to be processed.
|
|
133
|
+
c1 (float, optional): Threshold parameter for unremovable data. Default is 1.5.
|
|
134
|
+
c2 (float, optional): Threshold parameter for removable data. Default is 1.35.
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
------
|
|
138
|
+
data (array_like): Processed data array with potential modifications.
|
|
139
|
+
unremovable_indices (array_like): Indices of unremovable data points.
|
|
140
|
+
|
|
141
|
+
Notes
|
|
142
|
+
------
|
|
143
|
+
This function applies phase space thresholding premodification to the input data array.
|
|
144
|
+
Threshold parameters c1 and c2 determine which data points are considered unremovable and removable, respectively.
|
|
145
|
+
Unremovable data points are those within the range of -c1*theta to c1*theta, where theta is the median absolute deviation.
|
|
146
|
+
Removable data points are those exceeding c2*theta*sqrt(2*log(ndata)), where theta is the median absolute deviation
|
|
147
|
+
and ndata is the length of the data array.
|
|
148
|
+
Removable data points are replaced with the value of the preceding data point.
|
|
149
|
+
|
|
150
|
+
Example
|
|
151
|
+
------
|
|
152
|
+
>>> data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
|
153
|
+
>>> phasespace_thresholding_premodification(data)
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
data_size = data.size
|
|
157
|
+
theta = np.median(np.absolute(data - np.median(data)))
|
|
158
|
+
|
|
159
|
+
# Calculate indices of unremovable data
|
|
160
|
+
unremovable_indices = np.intersect1d(np.nonzero(data >= -c1 * theta)[0], np.nonzero(data <= c1 * theta)[0])
|
|
161
|
+
|
|
162
|
+
# Calculate threshold for removable data
|
|
163
|
+
removable_threshold = c2 * theta * np.sqrt(2 * np.log(data_size))
|
|
164
|
+
|
|
165
|
+
# Calculate indices of removable data
|
|
166
|
+
removable_indices = np.intersect1d(np.nonzero(data > removable_threshold)[0],
|
|
167
|
+
np.nonzero(data < -removable_threshold)[0])
|
|
168
|
+
|
|
169
|
+
# Replace removable data points with the preceding data point
|
|
170
|
+
for i in removable_indices:
|
|
171
|
+
data[i] = data[i - 1]
|
|
172
|
+
|
|
173
|
+
return data, unremovable_indices
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def calculate_ab(std1, std2, theta, lambda_):
|
|
5
|
+
"""
|
|
6
|
+
Calculate 'a' and 'b' coefficients for poincare mapping base detection algorithms.
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
------
|
|
10
|
+
std1 (float): Standard deviation of the first component.
|
|
11
|
+
std2 (float): Standard deviation of the second component.
|
|
12
|
+
theta (float | rad): Angle between components.
|
|
13
|
+
lambda_ (float): Lambda value used in algorithm.
|
|
14
|
+
|
|
15
|
+
Returns
|
|
16
|
+
------
|
|
17
|
+
float: Coefficient 'a' used in poincare map.
|
|
18
|
+
float: Coefficient 'b' used in poincare map.
|
|
19
|
+
"""
|
|
20
|
+
r1 = lambda_ * std1
|
|
21
|
+
r2 = lambda_ * std2
|
|
22
|
+
fact = np.cos(theta) ** 4 - np.sin(theta) ** 4
|
|
23
|
+
fa = (r1 ** 2 * np.cos(theta) ** 2 - r2 ** 2 * np.sin(theta) ** 2) / fact
|
|
24
|
+
fb = (r2 ** 2 * np.cos(theta) ** 2 - r1 ** 2 * np.sin(theta) ** 2) / fact
|
|
25
|
+
return np.sqrt(fa), np.sqrt(fb) if fa > 0 and fb > 0 else (1e10, 1e10)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def calculate_rho(x, y, theta, a, b):
|
|
29
|
+
"""
|
|
30
|
+
Calculate rho value for poincare mapping base detection algorithms.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
------
|
|
34
|
+
x (array_like): First component.
|
|
35
|
+
y (array_like): Second component.
|
|
36
|
+
theta (float | rad): Angle between components.
|
|
37
|
+
a (float): Coefficient 'a' used in poincare map.
|
|
38
|
+
b (float): Coefficient 'b' used in poincare map.
|
|
39
|
+
|
|
40
|
+
Returns
|
|
41
|
+
------
|
|
42
|
+
rho (array_like): Rho value calculated for poincare map.
|
|
43
|
+
"""
|
|
44
|
+
xp = x * np.cos(theta) + y * np.sin(theta)
|
|
45
|
+
yp = y * np.cos(theta) - x * np.sin(theta)
|
|
46
|
+
return (xp / a) ** 2 + (yp / b) ** 2
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from proadv.statistics.spread import std
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def pollution_rate(x, xfil, k):
|
|
6
|
+
"""
|
|
7
|
+
Compute the pollution rate based on the given data and threshold.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
------
|
|
11
|
+
x (array_like): Input data array.
|
|
12
|
+
xfil (array_like): SSA analyzed data of x.
|
|
13
|
+
k (float): Threshold factor.
|
|
14
|
+
|
|
15
|
+
Returns
|
|
16
|
+
------
|
|
17
|
+
pollution (float): Pollution rate as a percentage.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# Find positions where x exceeds the threshold
|
|
21
|
+
pos = np.nonzero(x > xfil + k * std(x))
|
|
22
|
+
|
|
23
|
+
# Find positions where x is below the negative threshold
|
|
24
|
+
neg = np.nonzero(x < -xfil - k * std(x))
|
|
25
|
+
|
|
26
|
+
# Concatenate positive and negative noises
|
|
27
|
+
noises = np.concatenate((pos, neg), axis=None)
|
|
28
|
+
|
|
29
|
+
# Compute pollution rate
|
|
30
|
+
pollution = len(noises) / len(x) * 100
|
|
31
|
+
|
|
32
|
+
return pollution
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _descript(c, iteration, c_mean):
|
|
5
|
+
"""
|
|
6
|
+
Calculate the mean value and center the input data.
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
------
|
|
10
|
+
c (array_like): Input data.
|
|
11
|
+
iteration (int): Loop counter.
|
|
12
|
+
c_mean (float): Mean value of input data.
|
|
13
|
+
|
|
14
|
+
Returns
|
|
15
|
+
------
|
|
16
|
+
f (array_like): Centered input data.
|
|
17
|
+
f_mean (float): Updated mean value.
|
|
18
|
+
"""
|
|
19
|
+
if iteration == 1:
|
|
20
|
+
f_mean = np.around(np.nanmean(c), 4)
|
|
21
|
+
else:
|
|
22
|
+
f_mean = np.around(c_mean + np.nanmean(c), 4)
|
|
23
|
+
f = np.around(c - np.nanmean(c), 4)
|
|
24
|
+
return f, f_mean
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _gradients(c):
|
|
28
|
+
"""
|
|
29
|
+
Calculate the first and second order derivatives of the input data.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
------
|
|
33
|
+
c (array_like): Input data.
|
|
34
|
+
|
|
35
|
+
Returns
|
|
36
|
+
------
|
|
37
|
+
dc (array_like): First order derivative of the input data.
|
|
38
|
+
dc2 (array_like): Second order derivative of the input data.
|
|
39
|
+
"""
|
|
40
|
+
dc = np.gradient(c)
|
|
41
|
+
dc2 = np.gradient(dc)
|
|
42
|
+
return dc, dc2
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _rotation(c, dc, dc2, theta):
|
|
46
|
+
"""
|
|
47
|
+
Rotate the input data based on the specified angle theta.
|
|
48
|
+
|
|
49
|
+
Parameters
|
|
50
|
+
------
|
|
51
|
+
c (array_like): Input data.
|
|
52
|
+
dc (array_like): First order derivative of the input data.
|
|
53
|
+
dc2 (array_like): Second order derivative of the input data.
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
------
|
|
57
|
+
x (array_like): Rotated X-axis data.
|
|
58
|
+
y (array_like): Rotated Y-axis data.
|
|
59
|
+
z (array_like): Rotated Z-axis data.
|
|
60
|
+
"""
|
|
61
|
+
if theta == 0:
|
|
62
|
+
x = c.copy()
|
|
63
|
+
y = dc.copy()
|
|
64
|
+
z = dc2.copy()
|
|
65
|
+
else:
|
|
66
|
+
rotation_matrix = np.around(np.array([
|
|
67
|
+
[np.cos(theta), 0, np.sin(theta)],
|
|
68
|
+
[0, 1, 0],
|
|
69
|
+
[-np.sin(theta), 0, np.cos(theta)]
|
|
70
|
+
]), 4)
|
|
71
|
+
x = np.around(c * rotation_matrix[0, 0] + dc * rotation_matrix[0, 1] + dc2 * rotation_matrix[0, 2], 4)
|
|
72
|
+
y = np.around(c * rotation_matrix[1, 0] + dc * rotation_matrix[1, 1] + dc2 * rotation_matrix[1, 2], 4)
|
|
73
|
+
z = np.around(c * rotation_matrix[2, 0] + dc * rotation_matrix[2, 1] + dc2 * rotation_matrix[2, 2], 4)
|
|
74
|
+
return x, y, z
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _parameters(x, y, z):
|
|
78
|
+
"""
|
|
79
|
+
Calculate parameters for phase-space thresholding in spherical coordinates.
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
------
|
|
83
|
+
x (array_like): Rotated X-axis data.
|
|
84
|
+
y (array_like): Rotated Y-axis data.
|
|
85
|
+
z (array_like): Rotated Z-axis data.
|
|
86
|
+
|
|
87
|
+
Returns
|
|
88
|
+
------
|
|
89
|
+
a (float): Coefficient 'a'.
|
|
90
|
+
b (float): Coefficient 'b'.
|
|
91
|
+
c (float): Coefficient 'c'.
|
|
92
|
+
"""
|
|
93
|
+
lambda_ = np.around(np.sqrt(2 * np.log(x.size)), 4)
|
|
94
|
+
a = np.around(lambda_ * np.nanstd(x), 4)
|
|
95
|
+
b = np.around(lambda_ * np.nanstd(y), 4)
|
|
96
|
+
c = np.around(lambda_ * np.nanstd(z), 4)
|
|
97
|
+
return a, b, c
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _spike_indices(x, y, z, a, b, c):
|
|
101
|
+
"""
|
|
102
|
+
Calculate spike indices based on the input data and coefficients.
|
|
103
|
+
|
|
104
|
+
Parameters
|
|
105
|
+
------
|
|
106
|
+
x (array_like): Rotated X-axis data.
|
|
107
|
+
y (array_like): Rotated Y-axis data.
|
|
108
|
+
z (array_like): Rotated Z-axis data.
|
|
109
|
+
a (float): Coefficient 'a'.
|
|
110
|
+
b (float): Coefficient 'a'.
|
|
111
|
+
c (float): Coefficient 'a'.
|
|
112
|
+
|
|
113
|
+
Returns
|
|
114
|
+
------
|
|
115
|
+
spike_indices (array_like): Indices of detected spikes.
|
|
116
|
+
"""
|
|
117
|
+
xp, yp, zp, ip = [], [], [], []
|
|
118
|
+
for i in range(x.size):
|
|
119
|
+
x1 = x[i]
|
|
120
|
+
y1 = y[i]
|
|
121
|
+
z1 = z[i]
|
|
122
|
+
x2 = np.around(a * b * c * x1 / np.sqrt((a * c * y1) ** 2 + b ** 2 * (c ** 2 * x1 ** 2 + a ** 2 * z1 ** 2)), 4)
|
|
123
|
+
y2 = np.around(a * b * c * y1 / np.sqrt((a * c * y1) ** 2 + b ** 2 * (c ** 2 * x1 ** 2 + a ** 2 * z1 ** 2)), 4)
|
|
124
|
+
zt = np.around(c ** 2 * (1 - (x2 / a) ** 2 - (y2 / b) ** 2), 4)
|
|
125
|
+
if z1 < 0:
|
|
126
|
+
z2 = -np.around(np.sqrt(zt), 4)
|
|
127
|
+
elif z1 > 0:
|
|
128
|
+
z2 = np.around(np.sqrt(zt), 4)
|
|
129
|
+
else:
|
|
130
|
+
z2 = 0
|
|
131
|
+
dis = (x2 ** 2 + y2 ** 2 + z2 ** 2) - (x1 ** 2 + y1 ** 2 + z1 ** 2)
|
|
132
|
+
if dis < 0:
|
|
133
|
+
ip.append(i)
|
|
134
|
+
xp.append(x[i])
|
|
135
|
+
yp.append(y[i])
|
|
136
|
+
zp.append(z[i])
|
|
137
|
+
spike_indices = np.array(ip, dtype=np.int64)
|
|
138
|
+
return spike_indices
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def spherical_phasespace_thresholding(c, iteration, c_mean):
|
|
142
|
+
"""
|
|
143
|
+
Detect spikes using three-dimensional phase-space thresholding, based on each velocity component and
|
|
144
|
+
their first-order and second-order derivatives.
|
|
145
|
+
|
|
146
|
+
Parameters
|
|
147
|
+
------
|
|
148
|
+
c (array_like): Velocity component
|
|
149
|
+
iteration (int): Loop counter.
|
|
150
|
+
c_mean (float): Mean of the velocity component
|
|
151
|
+
|
|
152
|
+
Returns
|
|
153
|
+
------
|
|
154
|
+
spherical_indices (array_like): Array containing the indices of detected spikes.
|
|
155
|
+
|
|
156
|
+
References
|
|
157
|
+
------
|
|
158
|
+
Wahl, Tony L.
|
|
159
|
+
"Discussion of “Despiking acoustic doppler velocimeter data” by Derek G. Goring and Vladimir I. Nikora."
|
|
160
|
+
Journal of Hydraulic Engineering 129, no. 6 (2003): 484-487.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
# Calculate mean and center the data
|
|
164
|
+
c, c_mean = _descript(c, iteration, c_mean)
|
|
165
|
+
|
|
166
|
+
# Calculate gradients
|
|
167
|
+
dc, dc2 = _gradients(c)
|
|
168
|
+
|
|
169
|
+
# Calculaterotation angle
|
|
170
|
+
theta = np.around(np.arctan2(np.sum(c * dc), np.sum(dc2 ** 2)), 4)
|
|
171
|
+
|
|
172
|
+
# Rotate data
|
|
173
|
+
x, y, z = _rotation(c, dc, dc2, theta)
|
|
174
|
+
|
|
175
|
+
# Calculate parameters
|
|
176
|
+
a, b, c = _parameters(x, y, z)
|
|
177
|
+
|
|
178
|
+
# Calculate spike indices
|
|
179
|
+
spherical_indices = _spike_indices(x, y, z, a, b, c)
|
|
180
|
+
|
|
181
|
+
return spherical_indices
|