Skip to content

Commit b97e0bf

Browse files
committed
Custoim weights included in point cloud
1 parent dab3d5a commit b97e0bf

File tree

3 files changed

+380
-18
lines changed

3 files changed

+380
-18
lines changed

src/pyinterpolate/semivariogram/experimental/experimental_semivariogram.py

Lines changed: 102 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
from pyinterpolate.semivariogram.experimental.functions.semivariance import \
1515
semivariance_fn
1616
from pyinterpolate.semivariogram.lags.lags import get_lags
17+
from semivariogram.experimental.functions.directional import \
18+
from_ellipse_weighted_cloud
19+
from semivariogram.experimental.functions.general import \
20+
omnidirectional_weighted_semivariogram_cloud
1721

1822

1923
def calculate_semivariance(ds: Union[np.ndarray, VariogramPoints],
@@ -296,6 +300,77 @@ def directional_semivariance_cloud(points: np.ndarray,
296300
return output_semivariances
297301

298302

303+
def directional_weighted_semivariance_cloud(points: np.ndarray,
304+
lags: Union[List, np.ndarray],
305+
custom_weights: np.ndarray,
306+
direction: float,
307+
tolerance: float,
308+
method: str) -> Dict:
309+
"""
310+
Function calculates directional semivariances.
311+
312+
Parameters
313+
----------
314+
points : numpy array
315+
``[x, y, value]``
316+
317+
lags : numpy array
318+
319+
custom_weights : numpy array
320+
321+
direction : float, optional
322+
Direction of semivariogram, values from 0 to 360 degrees:
323+
324+
- 0 or 180: is E-W,
325+
- 90 or 270 is N-S,
326+
- 45 or 225 is NE-SW,
327+
- 135 or 315 is NW-SE.
328+
329+
tolerance : float, optional
330+
If ``tolerance`` is 0 then points must be placed at a single line with
331+
the beginning in the origin of the coordinate system and the
332+
direction given by y axis and direction parameter.
333+
If ``tolerance`` is ``> 0`` then the bin is selected as an elliptical
334+
area with major axis pointed in the same direction as the line for
335+
``0`` tolerance.
336+
337+
* The major axis size == ``step_size``.
338+
* The minor axis size is ``tolerance * step_size``
339+
* The baseline point is at a center of the ellipse.
340+
* The ``tolerance == 1`` creates an omnidirectional semivariogram.
341+
342+
method : str
343+
Neighbors selection in a given direction. Available methods:
344+
345+
* "triangle" or "t", default method where point neighbors are
346+
selected from a triangular area,
347+
* "ellipse" or "e", more accurate method but also slower.
348+
349+
Returns
350+
-------
351+
: Dict
352+
``{lag: [semivariances]}``
353+
"""
354+
output_semivariances = dict()
355+
356+
if method == "e" or method == "ellipse":
357+
output_semivariances = from_ellipse_weighted_cloud(
358+
points,
359+
lags,
360+
custom_weights,
361+
direction,
362+
tolerance)
363+
elif method == "t" or method == "triangle":
364+
output_semivariances = from_triangle_cloud(
365+
points=points,
366+
lags=lags,
367+
direction=direction,
368+
tolerance=tolerance
369+
)
370+
371+
return output_semivariances
372+
373+
299374
def directional_semivariance(points: np.ndarray,
300375
lags: Union[List, np.ndarray],
301376
direction: float,
@@ -352,7 +427,7 @@ def directional_semivariance(points: np.ndarray,
352427

353428
if custom_weights is None:
354429
if (dir_neighbor_selection_method == "e" or
355-
dir_neighbor_selection_method == "ellipse"):
430+
dir_neighbor_selection_method == "ellipse"):
356431
output_semivariances = from_ellipse(
357432
semivariance_fn,
358433
points,
@@ -408,13 +483,16 @@ def omnidirectional_semivariance(points: np.ndarray,
408483
else:
409484

410485
if custom_weights is not None:
411-
# todo warning message - custom_weights not included
412-
pass
413-
414-
sorted_semivariances = omnidirectional_semivariogram_cloud(
415-
points=points,
416-
lags=lags
417-
)
486+
sorted_semivariances = omnidirectional_weighted_semivariogram_cloud(
487+
points=points,
488+
lags=lags,
489+
custom_weights=custom_weights
490+
)
491+
else:
492+
sorted_semivariances = omnidirectional_semivariogram_cloud(
493+
points=points,
494+
lags=lags
495+
)
418496

419497
return sorted_semivariances
420498

@@ -592,16 +670,22 @@ def point_cloud_semivariance(ds: Union[np.ndarray, VariogramPoints],
592670
if direction is not None and tolerance is not None:
593671

594672
if custom_weights is not None:
595-
# todo warning message - custom_weights not included
596-
pass
597-
598-
experimental_semivariances = directional_semivariance_cloud(
599-
ds.points,
600-
lags,
601-
direction,
602-
tolerance,
603-
dir_neighbors_selection_method
604-
)
673+
experimental_semivariances = directional_weighted_semivariance_cloud(
674+
ds.points,
675+
lags,
676+
custom_weights,
677+
direction,
678+
tolerance,
679+
dir_neighbors_selection_method
680+
)
681+
else:
682+
experimental_semivariances = directional_semivariance_cloud(
683+
ds.points,
684+
lags,
685+
direction,
686+
tolerance,
687+
dir_neighbors_selection_method
688+
)
605689
else:
606690
experimental_semivariances = omnidirectional_semivariance(
607691
ds.points, lags, custom_weights, as_point_cloud=True

src/pyinterpolate/semivariogram/experimental/functions/directional.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,65 @@ def from_ellipse_cloud(
134134
return point_cloud
135135

136136

137+
def from_ellipse_weighted_cloud(
138+
points: np.ndarray,
139+
lags: Union[List, np.ndarray],
140+
custom_weights: np.ndarray,
141+
direction: float,
142+
tolerance: float):
143+
"""
144+
Function calculates weighted point cloud semivariances from the elliptical
145+
neighborhood.
146+
147+
Parameters
148+
----------
149+
points : numpy array
150+
Coordinates and their values: ``[x, y, value]``.
151+
152+
lags : numpy array
153+
Bins array.
154+
155+
custom_weights : numpy array
156+
Custom weights applied to points.
157+
158+
direction : float
159+
Direction of semivariogram, values from 0 to 360 degrees:
160+
161+
- 0 or 180: is E-W,
162+
- 90 or 270 is N-S,
163+
- 45 or 225 is NE-SW,
164+
- 135 or 315 is NW-SE.
165+
166+
tolerance : float, default=1
167+
Value in range (0-1] to calculate semi-minor axis length of the
168+
search area. If tolerance is close to 0 then points must be placed
169+
in a single line with beginning in the origin of coordinate system
170+
and direction given by y axis and direction parameter.
171+
* The major axis length == step_size,
172+
* The minor axis size == tolerance * step_size.
173+
* Tolerance == 1 creates the omnidirectional semivariogram.
174+
175+
Returns
176+
-------
177+
output : Dict
178+
``{lag: [semivariances]}``
179+
"""
180+
point_cloud = dict()
181+
w_matrix = define_whitening_matrix(theta=direction,
182+
minor_axis_size=tolerance)
183+
for idx in range(len(lags)):
184+
values = _get_weighted_values_ellipse_cloud(
185+
points=points,
186+
lags=lags,
187+
custom_weights=custom_weights,
188+
lag_idx=idx,
189+
w_matrix=w_matrix
190+
)
191+
point_cloud[lags[idx]] = values
192+
193+
return point_cloud
194+
195+
137196
def from_triangle(
138197
fn: Callable,
139198
points: np.ndarray,
@@ -533,6 +592,79 @@ def _get_values_ellipse_cloud(
533592
return np.array([])
534593

535594

595+
def _get_weighted_values_ellipse_cloud(
596+
points: np.ndarray,
597+
lags: Union[List, np.ndarray],
598+
custom_weights: np.ndarray,
599+
lag_idx: int,
600+
w_matrix: np.ndarray) -> np.ndarray:
601+
"""
602+
Function selects semivariances or covariances per lag in the ellipse.
603+
604+
Parameters
605+
----------
606+
points : numpy array
607+
``[x, y, value]``
608+
609+
lags : list or numpy array
610+
The list of lags.
611+
612+
custom_weights : numpy array
613+
Custom weights applied to points.
614+
615+
lag_idx : int
616+
Current lag index.
617+
618+
w_matrix : numpy array
619+
Matrix used for masking values in ellipse.
620+
621+
Returns
622+
-------
623+
values : numpy array
624+
Semivariances for a given lag.
625+
"""
626+
coords = points[:, :-1]
627+
628+
current_lag, previous_lag = get_current_and_previous_lag(
629+
lag_idx=lag_idx, lags=lags
630+
)
631+
632+
step_size = current_lag - previous_lag
633+
semivariances = []
634+
635+
for idx, point in enumerate(points):
636+
637+
coordinates = point[:-1]
638+
639+
mask = select_points_within_ellipse(
640+
ellipse_center=coordinates,
641+
other_points=coords,
642+
lag=current_lag,
643+
step_size=step_size,
644+
w_matrix=w_matrix
645+
)
646+
647+
points_in_range = points[mask, -1]
648+
649+
# Extend list for calculations
650+
if len(points_in_range) > 0:
651+
# Calculate semivariances
652+
w_vals = custom_weights[mask]
653+
w_of_single_point = custom_weights[idx]
654+
w_mult = w_of_single_point * w_vals
655+
w_sum = w_of_single_point + w_vals
656+
w_h = w_mult / w_sum
657+
z_h = (points_in_range - point[-1]) ** 2
658+
nominator_z = z_h - np.average(points_in_range,
659+
weights=w_vals)
660+
nominator_z = np.sum(nominator_z * w_h)
661+
gamma = nominator_z / np.sum(w_h)
662+
average_semivariance = 0.5 * gamma
663+
semivariances.append(average_semivariance)
664+
665+
return np.array(semivariances)
666+
667+
536668
def _get_values_triangle(
537669
fn: Callable,
538670
points: np.ndarray,

0 commit comments

Comments
 (0)