rock-physics-open 0.3.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.
- rock_physics_open/__init__.py +0 -0
- rock_physics_open/equinor_utilities/__init__.py +0 -0
- rock_physics_open/equinor_utilities/anisotropy.py +211 -0
- rock_physics_open/equinor_utilities/classification_functions/__init__.py +17 -0
- rock_physics_open/equinor_utilities/classification_functions/class_stats.py +68 -0
- rock_physics_open/equinor_utilities/classification_functions/lin_class.py +53 -0
- rock_physics_open/equinor_utilities/classification_functions/mahal_class.py +63 -0
- rock_physics_open/equinor_utilities/classification_functions/norm_class.py +73 -0
- rock_physics_open/equinor_utilities/classification_functions/poly_class.py +45 -0
- rock_physics_open/equinor_utilities/classification_functions/post_prob.py +27 -0
- rock_physics_open/equinor_utilities/classification_functions/two_step_classification.py +60 -0
- rock_physics_open/equinor_utilities/conversions.py +10 -0
- rock_physics_open/equinor_utilities/gen_utilities/__init__.py +11 -0
- rock_physics_open/equinor_utilities/gen_utilities/dict_to_float.py +38 -0
- rock_physics_open/equinor_utilities/gen_utilities/dim_check_vector.py +113 -0
- rock_physics_open/equinor_utilities/gen_utilities/filter_input.py +131 -0
- rock_physics_open/equinor_utilities/gen_utilities/filter_output.py +88 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/__init__.py +15 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/base_pressure_model.py +170 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/dummy_vars.py +53 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/exponential_model.py +137 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/import_ml_models.py +77 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/polynomial_model.py +132 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/run_regression.py +209 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/sigmoidal_model.py +241 -0
- rock_physics_open/equinor_utilities/optimisation_utilities/__init__.py +19 -0
- rock_physics_open/equinor_utilities/optimisation_utilities/opt_subst_utilities.py +455 -0
- rock_physics_open/equinor_utilities/snapshot_test_utilities/__init__.py +10 -0
- rock_physics_open/equinor_utilities/snapshot_test_utilities/compare_snapshots.py +184 -0
- rock_physics_open/equinor_utilities/snapshot_test_utilities/snapshots.py +97 -0
- rock_physics_open/equinor_utilities/std_functions/__init__.py +43 -0
- rock_physics_open/equinor_utilities/std_functions/backus_ave.py +68 -0
- rock_physics_open/equinor_utilities/std_functions/dvorkin_nur.py +77 -0
- rock_physics_open/equinor_utilities/std_functions/gassmann.py +165 -0
- rock_physics_open/equinor_utilities/std_functions/hashin_shtrikman.py +224 -0
- rock_physics_open/equinor_utilities/std_functions/hertz_mindlin.py +51 -0
- rock_physics_open/equinor_utilities/std_functions/moduli_velocity.py +67 -0
- rock_physics_open/equinor_utilities/std_functions/reflection_eq.py +120 -0
- rock_physics_open/equinor_utilities/std_functions/rho.py +69 -0
- rock_physics_open/equinor_utilities/std_functions/voigt_reuss_hill.py +149 -0
- rock_physics_open/equinor_utilities/std_functions/walton.py +45 -0
- rock_physics_open/equinor_utilities/std_functions/wood_brie.py +94 -0
- rock_physics_open/equinor_utilities/various_utilities/Equinor_logo.gif +0 -0
- rock_physics_open/equinor_utilities/various_utilities/Equinor_logo.ico +0 -0
- rock_physics_open/equinor_utilities/various_utilities/__init__.py +24 -0
- rock_physics_open/equinor_utilities/various_utilities/display_result_statistics.py +90 -0
- rock_physics_open/equinor_utilities/various_utilities/gassmann_dry_mod.py +56 -0
- rock_physics_open/equinor_utilities/various_utilities/gassmann_mod.py +56 -0
- rock_physics_open/equinor_utilities/various_utilities/gassmann_sub_mod.py +64 -0
- rock_physics_open/equinor_utilities/various_utilities/hs_average.py +59 -0
- rock_physics_open/equinor_utilities/various_utilities/pressure.py +96 -0
- rock_physics_open/equinor_utilities/various_utilities/reflectivity.py +101 -0
- rock_physics_open/equinor_utilities/various_utilities/timeshift.py +104 -0
- rock_physics_open/equinor_utilities/various_utilities/vp_vs_rho_set_statistics.py +170 -0
- rock_physics_open/equinor_utilities/various_utilities/vrh_3_min.py +83 -0
- rock_physics_open/fluid_models/__init__.py +9 -0
- rock_physics_open/fluid_models/brine_model/__init__.py +5 -0
- rock_physics_open/fluid_models/brine_model/brine_properties.py +178 -0
- rock_physics_open/fluid_models/gas_model/__init__.py +5 -0
- rock_physics_open/fluid_models/gas_model/gas_properties.py +319 -0
- rock_physics_open/fluid_models/oil_model/__init__.py +5 -0
- rock_physics_open/fluid_models/oil_model/dead_oil_density.py +65 -0
- rock_physics_open/fluid_models/oil_model/dead_oil_velocity.py +30 -0
- rock_physics_open/fluid_models/oil_model/live_oil_density.py +82 -0
- rock_physics_open/fluid_models/oil_model/live_oil_velocity.py +24 -0
- rock_physics_open/fluid_models/oil_model/oil_bubble_point.py +69 -0
- rock_physics_open/fluid_models/oil_model/oil_properties.py +146 -0
- rock_physics_open/sandstone_models/__init__.py +59 -0
- rock_physics_open/sandstone_models/cemented_shalysand_sandyshale_models.py +304 -0
- rock_physics_open/sandstone_models/constant_cement_models.py +204 -0
- rock_physics_open/sandstone_models/constant_cement_optimisation.py +125 -0
- rock_physics_open/sandstone_models/contact_cement_model.py +138 -0
- rock_physics_open/sandstone_models/curvefit_sandstone_models.py +143 -0
- rock_physics_open/sandstone_models/friable_models.py +177 -0
- rock_physics_open/sandstone_models/friable_optimisation.py +115 -0
- rock_physics_open/sandstone_models/friable_shalysand_sandyshale_models.py +235 -0
- rock_physics_open/sandstone_models/patchy_cement_fluid_substitution_model.py +477 -0
- rock_physics_open/sandstone_models/patchy_cement_model.py +384 -0
- rock_physics_open/sandstone_models/patchy_cement_optimisation.py +254 -0
- rock_physics_open/sandstone_models/unresolved_cemented_sandshale_models.py +134 -0
- rock_physics_open/sandstone_models/unresolved_friable_sandshale_models.py +126 -0
- rock_physics_open/shale_models/__init__.py +19 -0
- rock_physics_open/shale_models/dem.py +174 -0
- rock_physics_open/shale_models/dem_dual_por.py +61 -0
- rock_physics_open/shale_models/kus_tok.py +59 -0
- rock_physics_open/shale_models/multi_sca.py +133 -0
- rock_physics_open/shale_models/pq.py +102 -0
- rock_physics_open/shale_models/sca.py +90 -0
- rock_physics_open/shale_models/shale4_mineral.py +147 -0
- rock_physics_open/shale_models/shale4_mineral_dem_overlay.py +92 -0
- rock_physics_open/span_wagner/__init__.py +5 -0
- rock_physics_open/span_wagner/co2_properties.py +444 -0
- rock_physics_open/span_wagner/coefficients.py +165 -0
- rock_physics_open/span_wagner/equations.py +104 -0
- rock_physics_open/span_wagner/tables/__init__.py +0 -0
- rock_physics_open/span_wagner/tables/carbon_dioxide_density.npz +0 -0
- rock_physics_open/span_wagner/tables/lookup_table.py +33 -0
- rock_physics_open/t_matrix_models/Equinor_logo.ico +0 -0
- rock_physics_open/t_matrix_models/__init__.py +35 -0
- rock_physics_open/t_matrix_models/carbonate_pressure_substitution.py +124 -0
- rock_physics_open/t_matrix_models/curvefit_t_matrix_exp.py +123 -0
- rock_physics_open/t_matrix_models/curvefit_t_matrix_min.py +86 -0
- rock_physics_open/t_matrix_models/parse_t_matrix_inputs.py +297 -0
- rock_physics_open/t_matrix_models/run_t_matrix.py +243 -0
- rock_physics_open/t_matrix_models/t_matrix_C.py +210 -0
- rock_physics_open/t_matrix_models/t_matrix_opt_fluid_sub_exp.py +137 -0
- rock_physics_open/t_matrix_models/t_matrix_opt_fluid_sub_petec.py +167 -0
- rock_physics_open/t_matrix_models/t_matrix_opt_forward_model_exp.py +76 -0
- rock_physics_open/t_matrix_models/t_matrix_opt_forward_model_min.py +89 -0
- rock_physics_open/t_matrix_models/t_matrix_parameter_optimisation_exp.py +176 -0
- rock_physics_open/t_matrix_models/t_matrix_parameter_optimisation_min.py +162 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/__init__.py +12 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/array_functions.py +75 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_c_eff.py +163 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_isolated.py +95 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_kd.py +40 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_kd_eff.py +116 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_kd_uuv.py +18 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_pressure.py +140 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_t.py +71 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_td.py +42 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_theta.py +43 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_x.py +33 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_z.py +50 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/check_and_tile.py +43 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/g_tensor.py +140 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/iso_av.py +60 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/iso_ave_all.py +55 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/pressure_input.py +44 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/t_matrix_vec.py +278 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/velocity_vti_angles.py +81 -0
- rock_physics_open/t_matrix_models/tmatrix_python.dll +0 -0
- rock_physics_open/t_matrix_models/tmatrix_python.so +0 -0
- rock_physics_open/ternary_plots/__init__.py +3 -0
- rock_physics_open/ternary_plots/gen_ternary_plot.py +73 -0
- rock_physics_open/ternary_plots/shale_prop_ternary.py +337 -0
- rock_physics_open/ternary_plots/ternary_patches.py +277 -0
- rock_physics_open/ternary_plots/ternary_plot_utilities.py +197 -0
- rock_physics_open/ternary_plots/unconventionals_ternary.py +75 -0
- rock_physics_open/version.py +34 -0
- rock_physics_open-0.3.2.dist-info/METADATA +90 -0
- rock_physics_open-0.3.2.dist-info/RECORD +145 -0
- rock_physics_open-0.3.2.dist-info/WHEEL +5 -0
- rock_physics_open-0.3.2.dist-info/licenses/LICENSE +165 -0
- rock_physics_open-0.3.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from typing import Any, overload
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import numpy.typing as npt
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@overload
|
|
9
|
+
def dim_check_vector(
|
|
10
|
+
args: list[Any] | tuple[Any, ...],
|
|
11
|
+
force_type: np.dtype | None = ...,
|
|
12
|
+
) -> list[npt.NDArray[Any] | pd.DataFrame]:
|
|
13
|
+
"""Overload for when the input is a list or tuple."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@overload
|
|
17
|
+
def dim_check_vector(
|
|
18
|
+
args: pd.DataFrame,
|
|
19
|
+
force_type: np.dtype | None = ...,
|
|
20
|
+
) -> pd.DataFrame:
|
|
21
|
+
"""Overload for when the input is a pandas DataFrame."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@overload
|
|
25
|
+
def dim_check_vector(
|
|
26
|
+
args: npt.NDArray[Any],
|
|
27
|
+
force_type: np.dtype | None = ...,
|
|
28
|
+
) -> npt.NDArray[Any]:
|
|
29
|
+
"""Overload for when the input is a numpy array."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def dim_check_vector(
|
|
33
|
+
args: list[Any] | tuple[Any, ...] | npt.NDArray[Any] | pd.DataFrame,
|
|
34
|
+
force_type: np.dtype | None = None,
|
|
35
|
+
) -> npt.NDArray[Any] | pd.DataFrame | list[npt.NDArray[Any] | pd.DataFrame]:
|
|
36
|
+
"""
|
|
37
|
+
Check that all inputs are of the same (one-dimensional) size. Raise ValueError in case there are several lengths
|
|
38
|
+
present in the inputs. All inputs will be checked and possibly expanded to common length. Only the first dimension
|
|
39
|
+
is harmonised.
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
args : list or tuple
|
|
44
|
+
Input list or tuple of scalars, numpy arrays or pandas data frames of numerical or boolean type.
|
|
45
|
+
force_type : np.dtype
|
|
46
|
+
Force all outputs to be of a specific dtype.
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
output_args : list
|
|
51
|
+
List of inputs where all are of the same length.
|
|
52
|
+
"""
|
|
53
|
+
single_types = (np.ndarray, pd.DataFrame)
|
|
54
|
+
iterable_types = (list, tuple)
|
|
55
|
+
allowed_types = single_types + iterable_types
|
|
56
|
+
if not isinstance(args, allowed_types): # pyright: ignore[reportUnnecessaryIsInstance] | Kept for backward compatibility
|
|
57
|
+
raise ValueError("dim_check_vector: unknown input type: {}".format(type(args))) # pyright: ignore[reportUnreachable] | Kept for backward compatibility
|
|
58
|
+
|
|
59
|
+
# Single array or dataframe is just returned
|
|
60
|
+
if isinstance(args, single_types):
|
|
61
|
+
if force_type is not None:
|
|
62
|
+
try:
|
|
63
|
+
args = args.astype(force_type)
|
|
64
|
+
except ValueError:
|
|
65
|
+
raise ValueError(
|
|
66
|
+
"dim_check_vector: not possible to force dtype to {}".format(
|
|
67
|
+
force_type
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
return args
|
|
71
|
+
|
|
72
|
+
# If any input is a scalar, make it into an array
|
|
73
|
+
if force_type is not None:
|
|
74
|
+
try:
|
|
75
|
+
args = [
|
|
76
|
+
np.array(item, ndmin=1, dtype=force_type)
|
|
77
|
+
if np.isscalar(item)
|
|
78
|
+
else item.astype(force_type)
|
|
79
|
+
for item in args
|
|
80
|
+
]
|
|
81
|
+
except ValueError:
|
|
82
|
+
raise ValueError(
|
|
83
|
+
"dim_check_vector: not possible to force dtype to {}".format(force_type)
|
|
84
|
+
)
|
|
85
|
+
else:
|
|
86
|
+
args = [np.array(item, ndmin=1) if np.isscalar(item) else item for item in args]
|
|
87
|
+
|
|
88
|
+
# Can now test for length - must either be a scalar or have the same length
|
|
89
|
+
max_length: int = np.max([item.shape[0] for item in args])
|
|
90
|
+
if not np.all([item.shape[0] == max_length or item.shape[0] == 1 for item in args]):
|
|
91
|
+
raise ValueError(
|
|
92
|
+
"dim_check_vector: Unequal array lengths in input to dim_check_vector"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
output_arg: list[npt.NDArray[Any] | pd.DataFrame] = []
|
|
96
|
+
for item in args:
|
|
97
|
+
if item.shape[0] == max_length:
|
|
98
|
+
output_arg.append(item)
|
|
99
|
+
else:
|
|
100
|
+
item_dim = item.ndim
|
|
101
|
+
repeat_tuple = tuple([max_length] + [1] * (item_dim - 1))
|
|
102
|
+
if isinstance(item, pd.DataFrame):
|
|
103
|
+
output_arg.append(
|
|
104
|
+
pd.DataFrame(
|
|
105
|
+
np.tile(np.array(item), repeat_tuple),
|
|
106
|
+
columns=item.columns,
|
|
107
|
+
index=np.arange(max_length),
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
else:
|
|
111
|
+
output_arg.append(np.tile(item, repeat_tuple))
|
|
112
|
+
|
|
113
|
+
return output_arg
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
from sys import byteorder
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import numpy.typing as npt
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
WRONG_BYTEORDER = ">" if byteorder == "little" else "<"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def filter_input_log(
|
|
12
|
+
args: list[Any] | tuple[Any, ...] | npt.NDArray[Any] | pd.DataFrame,
|
|
13
|
+
working_int: npt.NDArray[Any] | None = None,
|
|
14
|
+
negative: bool = False,
|
|
15
|
+
no_zero: bool = False,
|
|
16
|
+
positive: bool = True,
|
|
17
|
+
) -> tuple[npt.NDArray[np.bool_], list[npt.NDArray[Any] | pd.DataFrame]]:
|
|
18
|
+
"""
|
|
19
|
+
Check for valid input values in numpy arrays or pandas data frames. Default behaviour is to
|
|
20
|
+
identify missing values - assumed to be NaN and Inf. Other conditions
|
|
21
|
+
can be stated in the key word arguments. Unknown conditions are ignored and a warning
|
|
22
|
+
is issued. Run dim_check_vector to make sure that all inputs have the same length.
|
|
23
|
+
Erroneous values in a sample in one log will remove the sample from all the logs.
|
|
24
|
+
All inputs must have the same array length (data frames the same number of indices).
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
args : list or tuple or np.ndarray or pd.DataFrame
|
|
29
|
+
Inputs to be filtered, single array or dataframe or lists of arrays or data frames.
|
|
30
|
+
working_int : np.ndarray
|
|
31
|
+
Valid positions are shown as values > 0.
|
|
32
|
+
negative : bool
|
|
33
|
+
Positive values are excluded (zero values are retained).
|
|
34
|
+
no_zero : bool
|
|
35
|
+
Zero values are excluded.
|
|
36
|
+
positive : bool
|
|
37
|
+
Negative values are excluded.
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
tuple
|
|
42
|
+
idx, output_args : (np.ndarray, list)
|
|
43
|
+
indices of valid values [bool],
|
|
44
|
+
list of input arrays at valid indices.
|
|
45
|
+
"""
|
|
46
|
+
type_error = "filter_input_log: unknown input data type: {}".format(type(args))
|
|
47
|
+
size_error = "filter_input_log: inputs of different length"
|
|
48
|
+
|
|
49
|
+
if not isinstance(args, (list, tuple, np.ndarray, pd.DataFrame)): # pyright: ignore[reportUnnecessaryIsInstance] | Kept for backward compatibility
|
|
50
|
+
raise ValueError(type_error) # pyright: ignore[reportUnreachable] | Kept for backward compatibility
|
|
51
|
+
# Make sure that 'args' is iterable
|
|
52
|
+
if isinstance(args, (np.ndarray, pd.DataFrame)):
|
|
53
|
+
args = [args]
|
|
54
|
+
|
|
55
|
+
# Input tuple
|
|
56
|
+
if isinstance(args, tuple):
|
|
57
|
+
args = list(args)
|
|
58
|
+
|
|
59
|
+
# Need to preserve original inputs
|
|
60
|
+
input_args = args.copy()
|
|
61
|
+
|
|
62
|
+
# Test that inputs are of the right types and the same length
|
|
63
|
+
if not np.all([isinstance(log, (np.ndarray, pd.DataFrame)) for log in args]):
|
|
64
|
+
raise ValueError(type_error)
|
|
65
|
+
if not np.all([log.shape[0] == args[0].shape[0] for log in args]):
|
|
66
|
+
raise ValueError(size_error)
|
|
67
|
+
|
|
68
|
+
# Generate pandas series from numpy arrays
|
|
69
|
+
args = [pd.Series(log) if isinstance(log, np.ndarray) else log for log in args]
|
|
70
|
+
# Merge into a data frame
|
|
71
|
+
logs = pd.concat(args, axis=1)
|
|
72
|
+
|
|
73
|
+
# If any of the input logs are of type boolean, False means that they should not be included,
|
|
74
|
+
# regardless of filter flags
|
|
75
|
+
# https://github.com/pandas-dev/pandas/issues/32432
|
|
76
|
+
# idx = ~logs.any(bool_only=True, axis=1)
|
|
77
|
+
# Need to do it the cumbersome way for the time being
|
|
78
|
+
bool_col = logs.dtypes == "bool"
|
|
79
|
+
if any(bool_col):
|
|
80
|
+
idx = ~logs.loc[:, logs.columns[bool_col]].any(axis=1)
|
|
81
|
+
logs.drop(columns=logs.columns[bool_col], inplace=True)
|
|
82
|
+
else:
|
|
83
|
+
idx = pd.Series(index=logs.index, data=np.zeros_like(logs.index).astype(bool))
|
|
84
|
+
|
|
85
|
+
# Standard checks: NaN and Inf
|
|
86
|
+
idx = np.logical_or(idx, logs.isna().any(axis=1))
|
|
87
|
+
idx = np.logical_or(idx, logs.isin([np.inf, -np.inf]).any(axis=1))
|
|
88
|
+
|
|
89
|
+
# Remove columns with dtype that is not numeric
|
|
90
|
+
obj_col = [dd.kind not in ["i", "u", "f", "c"] for dd in logs.dtypes]
|
|
91
|
+
logs.drop(columns=logs.columns[obj_col], inplace=True)
|
|
92
|
+
|
|
93
|
+
# Checks according to the input options input_dict
|
|
94
|
+
# Only consider working interval if it is included or set to some value
|
|
95
|
+
if working_int is not None and not np.all(working_int == 0):
|
|
96
|
+
idx = np.logical_or(idx, working_int == 0)
|
|
97
|
+
if negative:
|
|
98
|
+
# noinspection PyTypeChecker
|
|
99
|
+
idx = np.logical_or(idx, (logs >= 0.0).all(axis=1))
|
|
100
|
+
# idx = np.logical_or(idx, logs.loc[logs > 0.0]).any(axis=1)
|
|
101
|
+
if no_zero:
|
|
102
|
+
idx = np.logical_or(idx, (logs == 0.0).any(axis=1))
|
|
103
|
+
if positive:
|
|
104
|
+
# noinspection PyTypeChecker
|
|
105
|
+
idx = np.logical_or(idx, (logs < 0.0).any(axis=1))
|
|
106
|
+
|
|
107
|
+
# Negate idx to identify samples to retain
|
|
108
|
+
idx = np.logical_not(idx)
|
|
109
|
+
num_valid_samples = idx.sum()
|
|
110
|
+
if num_valid_samples == 0:
|
|
111
|
+
raise ValueError("No acceptable input values")
|
|
112
|
+
for i in range(len(input_args)):
|
|
113
|
+
if isinstance(input_args[i], np.ndarray):
|
|
114
|
+
input_args[i] = input_args[i][idx]
|
|
115
|
+
else: # data frame
|
|
116
|
+
# https://pandas.pydata.org/pandas-docs/stable/user_guide/gotchas.html#byte-ordering-issues
|
|
117
|
+
|
|
118
|
+
check_type = (
|
|
119
|
+
np.array([col_type.byteorder for col_type in input_args[i].dtypes])
|
|
120
|
+
== WRONG_BYTEORDER
|
|
121
|
+
)
|
|
122
|
+
if np.any(check_type):
|
|
123
|
+
tmp_array = (
|
|
124
|
+
input_args[i].to_numpy().byteswap().newbyteorder().astype(float)
|
|
125
|
+
)
|
|
126
|
+
cols = input_args[i].columns
|
|
127
|
+
for j in range(check_type.shape[0]):
|
|
128
|
+
if check_type[j]:
|
|
129
|
+
input_args[i][cols[j]] = tmp_array[:, j]
|
|
130
|
+
input_args[i] = input_args[i].loc[idx]
|
|
131
|
+
return np.array(idx), input_args
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import numpy.typing as npt
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def filter_output(
|
|
9
|
+
idx_inp: npt.NDArray[np.bool_],
|
|
10
|
+
inp_log: list[npt.NDArray[Any] | pd.DataFrame]
|
|
11
|
+
| tuple[npt.NDArray[Any] | pd.DataFrame, ...]
|
|
12
|
+
| npt.NDArray[Any]
|
|
13
|
+
| pd.DataFrame,
|
|
14
|
+
) -> list[npt.NDArray[Any] | pd.DataFrame]:
|
|
15
|
+
"""
|
|
16
|
+
Function to restore outputs from a plugin to original length and
|
|
17
|
+
with values at correct positions. The logs are assumed to go through
|
|
18
|
+
matching input filtering done by gen_utilities.filter_input_log earlier.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
idx_inp: np.ndarray
|
|
23
|
+
boolean array which is True at locations to be filled, length idx_inp is returned length of
|
|
24
|
+
arrays or data frames.
|
|
25
|
+
inp_log: tuple or list or np.ndarray or pd.DataFrame
|
|
26
|
+
input numpy array(s) or pandas data frame(s), in list or tuple that are to be expanded to original
|
|
27
|
+
length.
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
return_logs : list
|
|
32
|
+
Expanded inputs.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def _expand_array(
|
|
36
|
+
idx: npt.NDArray[np.bool_], inp_single_log: npt.NDArray[Any]
|
|
37
|
+
) -> npt.NDArray[Any]:
|
|
38
|
+
logs = np.ones(idx.shape, dtype=float) * np.nan
|
|
39
|
+
try:
|
|
40
|
+
logs[idx] = inp_single_log.flatten()
|
|
41
|
+
except ValueError:
|
|
42
|
+
# Assume that the dtype of the input log is not fit for casting to float, set to object and retry
|
|
43
|
+
logs = logs.astype(object)
|
|
44
|
+
logs[idx] = inp_single_log
|
|
45
|
+
return logs.reshape(idx.shape)
|
|
46
|
+
|
|
47
|
+
def _expand_df(idx: npt.NDArray[np.bool_], inp_df: pd.DataFrame) -> pd.DataFrame:
|
|
48
|
+
logs = pd.DataFrame(
|
|
49
|
+
columns=inp_df.columns, index=np.arange(idx.shape[0], dtype=np.intp)
|
|
50
|
+
)
|
|
51
|
+
logs.loc[idx] = inp_df
|
|
52
|
+
return logs
|
|
53
|
+
|
|
54
|
+
if not isinstance(inp_log, (list, tuple, np.ndarray, pd.DataFrame)): # pyright: ignore[reportUnnecessaryIsInstance] | Kept for backward compatibility
|
|
55
|
+
raise ValueError( # pyright: ignore[reportUnreachable] | Kept for backward compatibility
|
|
56
|
+
"filter_output: unknown input data type: {}".format(type(inp_log))
|
|
57
|
+
)
|
|
58
|
+
if not isinstance(idx_inp, (list, np.ndarray)): # pyright: ignore[reportUnnecessaryIsInstance] | Kept for backward compatibility
|
|
59
|
+
raise ValueError( # pyright: ignore[reportUnreachable] | Kept for backward compatibility
|
|
60
|
+
"filter_output: unknown filter array data type: {}".format(type(idx_inp))
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Make iterable in case of single input
|
|
64
|
+
if isinstance(inp_log, (np.ndarray, pd.DataFrame)):
|
|
65
|
+
inp_log = [inp_log]
|
|
66
|
+
|
|
67
|
+
if isinstance(idx_inp, np.ndarray): # pyright: ignore[reportUnnecessaryIsInstance] | Kept for backward compatibility
|
|
68
|
+
idx_inp_ = [idx_inp]
|
|
69
|
+
|
|
70
|
+
# Possible to simplify?
|
|
71
|
+
if len(idx_inp_) != len(inp_log):
|
|
72
|
+
if len(idx_inp_) == 1:
|
|
73
|
+
idx_inp_ = idx_inp_ * len(inp_log)
|
|
74
|
+
else:
|
|
75
|
+
raise ValueError(
|
|
76
|
+
"filter_output: mismatch between length of filter arrays and inputs: {} and {}".format(
|
|
77
|
+
len(idx_inp), len(inp_log)
|
|
78
|
+
)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return_logs: list[npt.NDArray[Any] | pd.DataFrame] = []
|
|
82
|
+
for this_idx, this_log in zip(idx_inp_, inp_log):
|
|
83
|
+
if isinstance(this_log, np.ndarray):
|
|
84
|
+
return_logs.append(_expand_array(this_idx, this_log))
|
|
85
|
+
elif isinstance(this_log, pd.DataFrame): # pyright: ignore[reportUnnecessaryIsInstance] | Kept for backward compatibility
|
|
86
|
+
return_logs.append(_expand_df(this_idx, this_log))
|
|
87
|
+
|
|
88
|
+
return return_logs
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .dummy_vars import generate_dummy_vars
|
|
2
|
+
from .exponential_model import ExponentialPressureModel
|
|
3
|
+
from .import_ml_models import import_model
|
|
4
|
+
from .polynomial_model import PolynomialPressureModel
|
|
5
|
+
from .run_regression import run_regression
|
|
6
|
+
from .sigmoidal_model import SigmoidalPressureModel
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"generate_dummy_vars",
|
|
10
|
+
"import_model",
|
|
11
|
+
"run_regression",
|
|
12
|
+
"ExponentialPressureModel",
|
|
13
|
+
"PolynomialPressureModel",
|
|
14
|
+
"SigmoidalPressureModel",
|
|
15
|
+
]
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import pickle
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import Any, Self
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BasePressureModel(ABC):
|
|
9
|
+
"""
|
|
10
|
+
Abstract base class for pressure sensitivity models.
|
|
11
|
+
|
|
12
|
+
All pressure models follow the convention:
|
|
13
|
+
- predict(): returns differential change (depleted - in_situ)
|
|
14
|
+
- predict_abs(): returns absolute values for specified case
|
|
15
|
+
- predict_max(): uses model_max_pressure instead of depleted pressure
|
|
16
|
+
|
|
17
|
+
Input validation is delegated to concrete implementations since
|
|
18
|
+
each model has different column requirements.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, model_max_pressure: float | None = None, description: str = ""):
|
|
22
|
+
"""
|
|
23
|
+
Initialize base pressure model.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
model_max_pressure : float | None
|
|
28
|
+
Maximum pressure for predict_max method. Required for predict_max to work.
|
|
29
|
+
description : str
|
|
30
|
+
Human-readable description of the model instance.
|
|
31
|
+
"""
|
|
32
|
+
self._model_max_pressure: float | None = model_max_pressure
|
|
33
|
+
self._description: str = description
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def max_pressure(self) -> float | None:
|
|
37
|
+
"""Maximum pressure setting for predict_max method."""
|
|
38
|
+
return self._model_max_pressure
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def description(self) -> str:
|
|
42
|
+
"""Model description."""
|
|
43
|
+
return self._description
|
|
44
|
+
|
|
45
|
+
def predict(self, inp_arr: np.ndarray) -> np.ndarray:
|
|
46
|
+
"""
|
|
47
|
+
Predict differential change: result(depleted) - result(in_situ).
|
|
48
|
+
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
inp_arr : np.ndarray
|
|
52
|
+
Input array with pressure columns and other model-specific parameters.
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
np.ndarray
|
|
57
|
+
Differential change values.
|
|
58
|
+
"""
|
|
59
|
+
arr = self.validate_input(inp_arr)
|
|
60
|
+
return self.predict_abs(arr, case="depleted") - self.predict_abs(
|
|
61
|
+
arr, case="in_situ"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def predict_max(self, inp_arr: np.ndarray) -> np.ndarray:
|
|
65
|
+
"""
|
|
66
|
+
Predict using model_max_pressure instead of depleted pressure.
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
inp_arr : np.ndarray
|
|
71
|
+
Input array where last column (depleted pressure) will be replaced.
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
np.ndarray
|
|
76
|
+
Values at model_max_pressure minus values at in_situ pressure.
|
|
77
|
+
|
|
78
|
+
Raises
|
|
79
|
+
------
|
|
80
|
+
ValueError
|
|
81
|
+
If model_max_pressure is not set.
|
|
82
|
+
"""
|
|
83
|
+
if self._model_max_pressure is None:
|
|
84
|
+
raise ValueError('Field "model_max_pressure" is not set')
|
|
85
|
+
|
|
86
|
+
arr = self.validate_input(inp_arr).copy()
|
|
87
|
+
# Replace last column (assumed to be depleted pressure) with max pressure
|
|
88
|
+
arr[:, -1] = self._model_max_pressure
|
|
89
|
+
return self.predict_abs(arr, case="depleted") - self.predict_abs(
|
|
90
|
+
arr, case="in_situ"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
@abstractmethod
|
|
94
|
+
def validate_input(self, inp_arr: np.ndarray) -> np.ndarray:
|
|
95
|
+
"""
|
|
96
|
+
Validate input array format for this specific model.
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
inp_arr : np.ndarray
|
|
101
|
+
Input array to validate.
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
np.ndarray
|
|
106
|
+
Validated input array.
|
|
107
|
+
|
|
108
|
+
Raises
|
|
109
|
+
------
|
|
110
|
+
ValueError
|
|
111
|
+
If input format is invalid for this model.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
@abstractmethod
|
|
115
|
+
def predict_abs(self, inp_arr: np.ndarray, case: str = "in_situ") -> np.ndarray:
|
|
116
|
+
"""
|
|
117
|
+
Predict absolute values for specified pressure case.
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
inp_arr : np.ndarray
|
|
122
|
+
Validated input array.
|
|
123
|
+
case : str
|
|
124
|
+
Either "in_situ" or "depleted" to specify which pressure to use.
|
|
125
|
+
|
|
126
|
+
Returns
|
|
127
|
+
-------
|
|
128
|
+
np.ndarray
|
|
129
|
+
Absolute predicted values.
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
@abstractmethod
|
|
133
|
+
def todict(self) -> dict[str, Any]:
|
|
134
|
+
"""
|
|
135
|
+
Convert model to dictionary for serialization.
|
|
136
|
+
|
|
137
|
+
Returns
|
|
138
|
+
-------
|
|
139
|
+
dict[str, Any]
|
|
140
|
+
Dictionary containing all model parameters.
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
def save(self, file: str | bytes) -> None:
|
|
144
|
+
"""
|
|
145
|
+
Save model to pickle file.
|
|
146
|
+
|
|
147
|
+
Parameters
|
|
148
|
+
----------
|
|
149
|
+
file : str | bytes
|
|
150
|
+
File path for saving.
|
|
151
|
+
"""
|
|
152
|
+
with open(file, "wb") as f_out:
|
|
153
|
+
pickle.dump(self.todict(), f_out)
|
|
154
|
+
|
|
155
|
+
@classmethod
|
|
156
|
+
@abstractmethod
|
|
157
|
+
def load(cls, file: str | bytes) -> Self:
|
|
158
|
+
"""
|
|
159
|
+
Load model from pickle file.
|
|
160
|
+
|
|
161
|
+
Parameters
|
|
162
|
+
----------
|
|
163
|
+
file : str | bytes
|
|
164
|
+
File path for loading.
|
|
165
|
+
|
|
166
|
+
Returns
|
|
167
|
+
-------
|
|
168
|
+
BasePressureModel
|
|
169
|
+
Loaded model instance.
|
|
170
|
+
"""
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from typing import cast
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import numpy.typing as npt
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from pandas.api.types import is_numeric_dtype
|
|
7
|
+
from sklearn.preprocessing import OneHotEncoder
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def generate_dummy_vars(
|
|
11
|
+
inp_frame: pd.DataFrame,
|
|
12
|
+
class_var: str,
|
|
13
|
+
ohe: OneHotEncoder | None = None,
|
|
14
|
+
) -> tuple[npt.NDArray[np.float64], int, npt.NDArray[np.str_]]:
|
|
15
|
+
"""
|
|
16
|
+
From categorical variables generate a one-hot-encoder, i.e. each value in the categorical variable becomes a binary
|
|
17
|
+
variable. See sklearn.preprocessing.OneHotEncoder.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
inp_frame : pd.DataFrame
|
|
22
|
+
Input data containing categorical variables.
|
|
23
|
+
class_var : str
|
|
24
|
+
Name of categorical variable.
|
|
25
|
+
ohe : preprocessing.OneHotEncoder
|
|
26
|
+
One-hot-encoder object.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
dum_features, no_dummy_cols, dum_var_names : (np.ndarray, int, np.ndarray)
|
|
31
|
+
dum_features: 2D array with transformed dummy variables, no_dummy_cols: number of columns in returned array,
|
|
32
|
+
dum_var_names: automatically generated feature names.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
if is_numeric_dtype(inp_frame[class_var]):
|
|
36
|
+
# Make sure that the chosen indicator variable contains discrete values
|
|
37
|
+
inp_frame = inp_frame.astype({class_var: "int32"})
|
|
38
|
+
|
|
39
|
+
features_in = np.array(inp_frame[class_var]).reshape(-1, 1)
|
|
40
|
+
|
|
41
|
+
if ohe is None:
|
|
42
|
+
classes = features_in
|
|
43
|
+
ohe = OneHotEncoder(categories="auto", sparse_output=False)
|
|
44
|
+
_ = ohe.fit(classes)
|
|
45
|
+
|
|
46
|
+
dum_features = cast( # Casting since scikit-learn is not yet fully typed. `.transform` returns sparse matrix only if `sparse_output=True`.
|
|
47
|
+
npt.NDArray[np.float64],
|
|
48
|
+
ohe.transform(features_in),
|
|
49
|
+
)
|
|
50
|
+
no_dummy_cols = dum_features.shape[1]
|
|
51
|
+
dum_var_names = ohe.get_feature_names_out()
|
|
52
|
+
|
|
53
|
+
return dum_features, no_dummy_cols, dum_var_names
|