well-log-toolkit 0.1.116__tar.gz → 0.1.117__tar.gz
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.
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/PKG-INFO +1 -1
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/pyproject.toml +1 -1
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit/manager.py +12 -0
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit/visualization.py +151 -25
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit.egg-info/PKG-INFO +1 -1
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/README.md +0 -0
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/setup.cfg +0 -0
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit/__init__.py +0 -0
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit/exceptions.py +0 -0
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit/las_file.py +0 -0
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit/operations.py +0 -0
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit/property.py +0 -0
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit/regression.py +0 -0
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit/statistics.py +0 -0
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit/utils.py +0 -0
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit/well.py +0 -0
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit.egg-info/SOURCES.txt +0 -0
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit.egg-info/dependency_links.txt +0 -0
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit.egg-info/requires.txt +0 -0
- {well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "well-log-toolkit"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.117"
|
|
8
8
|
description = "Fast LAS file processing with lazy loading and filtering for well log analysis"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -2466,6 +2466,9 @@ class WellDataManager:
|
|
|
2466
2466
|
depth_range: Optional[tuple[float, float]] = None,
|
|
2467
2467
|
show_colorbar: bool = True,
|
|
2468
2468
|
show_legend: bool = True,
|
|
2469
|
+
show_regression_legend: bool = True,
|
|
2470
|
+
show_regression_equation: bool = True,
|
|
2471
|
+
show_regression_r2: bool = True,
|
|
2469
2472
|
regression: Optional[Union[str, dict]] = None,
|
|
2470
2473
|
regression_by_color: Optional[Union[str, dict]] = None,
|
|
2471
2474
|
regression_by_group: Optional[Union[str, dict]] = None,
|
|
@@ -2534,6 +2537,12 @@ class WellDataManager:
|
|
|
2534
2537
|
Show colorbar when using color mapping. Default: True
|
|
2535
2538
|
show_legend : bool, optional
|
|
2536
2539
|
Show legend. Default: True
|
|
2540
|
+
show_regression_legend : bool, optional
|
|
2541
|
+
Show separate legend for regression lines in lower right corner. Default: True
|
|
2542
|
+
show_regression_equation : bool, optional
|
|
2543
|
+
Include regression equation in regression legend labels. Default: True
|
|
2544
|
+
show_regression_r2 : bool, optional
|
|
2545
|
+
Include R² value in regression legend labels. Default: True
|
|
2537
2546
|
regression : str or dict, optional
|
|
2538
2547
|
Regression type to apply to all data points. Can be a string (e.g., "linear") or
|
|
2539
2548
|
dict with keys: type, line_color, line_width, line_style, line_alpha, x_range.
|
|
@@ -2624,6 +2633,9 @@ class WellDataManager:
|
|
|
2624
2633
|
depth_range=depth_range,
|
|
2625
2634
|
show_colorbar=show_colorbar,
|
|
2626
2635
|
show_legend=show_legend,
|
|
2636
|
+
show_regression_legend=show_regression_legend,
|
|
2637
|
+
show_regression_equation=show_regression_equation,
|
|
2638
|
+
show_regression_r2=show_regression_r2,
|
|
2627
2639
|
regression=regression,
|
|
2628
2640
|
regression_by_color=regression_by_color,
|
|
2629
2641
|
regression_by_group=regression_by_group,
|
|
@@ -2822,6 +2822,12 @@ class Crossplot:
|
|
|
2822
2822
|
Show colorbar when using color mapping. Default: True
|
|
2823
2823
|
show_legend : bool, optional
|
|
2824
2824
|
Show legend when using shape/well mapping. Default: True
|
|
2825
|
+
show_regression_legend : bool, optional
|
|
2826
|
+
Show separate legend for regression lines in lower right. Default: True
|
|
2827
|
+
show_regression_equation : bool, optional
|
|
2828
|
+
Show equations in regression legend. Default: True
|
|
2829
|
+
show_regression_r2 : bool, optional
|
|
2830
|
+
Show R² values in regression legend. Default: True
|
|
2825
2831
|
regression : str or dict, optional
|
|
2826
2832
|
Regression type to apply to all data points. Can be a string (e.g., "linear") or
|
|
2827
2833
|
dict with keys: type, line_color, line_width, line_style, line_alpha, x_range.
|
|
@@ -2912,9 +2918,12 @@ class Crossplot:
|
|
|
2912
2918
|
depth_range: Optional[tuple[float, float]] = None,
|
|
2913
2919
|
show_colorbar: bool = True,
|
|
2914
2920
|
show_legend: bool = True,
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2921
|
+
show_regression_legend: bool = True,
|
|
2922
|
+
show_regression_equation: bool = True,
|
|
2923
|
+
show_regression_r2: bool = True,
|
|
2924
|
+
regression: Optional[Union[str, dict]] = None,
|
|
2925
|
+
regression_by_color: Optional[Union[str, dict]] = None,
|
|
2926
|
+
regression_by_group: Optional[Union[str, dict]] = None,
|
|
2918
2927
|
):
|
|
2919
2928
|
# Store wells as list
|
|
2920
2929
|
if not isinstance(wells, list):
|
|
@@ -2948,6 +2957,9 @@ class Crossplot:
|
|
|
2948
2957
|
self.depth_range = depth_range
|
|
2949
2958
|
self.show_colorbar = show_colorbar
|
|
2950
2959
|
self.show_legend = show_legend
|
|
2960
|
+
self.show_regression_legend = show_regression_legend
|
|
2961
|
+
self.show_regression_equation = show_regression_equation
|
|
2962
|
+
self.show_regression_r2 = show_regression_r2
|
|
2951
2963
|
self.regression = regression
|
|
2952
2964
|
self.regression_by_color = regression_by_color
|
|
2953
2965
|
self.regression_by_group = regression_by_group
|
|
@@ -2961,6 +2973,7 @@ class Crossplot:
|
|
|
2961
2973
|
# Regression storage - nested structure: {type: {identifier: regression_obj}}
|
|
2962
2974
|
self._regressions = {}
|
|
2963
2975
|
self.regression_lines = {}
|
|
2976
|
+
self.regression_legend = None # Separate legend for regressions
|
|
2964
2977
|
|
|
2965
2978
|
# Pending regressions (added before plot() is called)
|
|
2966
2979
|
self._pending_regressions = []
|
|
@@ -3126,6 +3139,121 @@ class Crossplot:
|
|
|
3126
3139
|
self._regressions[reg_type] = {}
|
|
3127
3140
|
self._regressions[reg_type][identifier] = regression_obj
|
|
3128
3141
|
|
|
3142
|
+
def _format_regression_label(self, name: str, reg, include_equation: bool = None, include_r2: bool = None) -> str:
|
|
3143
|
+
"""Format a modern, compact regression label.
|
|
3144
|
+
|
|
3145
|
+
Args:
|
|
3146
|
+
name: Name of the regression
|
|
3147
|
+
reg: Regression object
|
|
3148
|
+
include_equation: Whether to include equation (uses self.show_regression_equation if None)
|
|
3149
|
+
include_r2: Whether to include R² (uses self.show_regression_r2 if None)
|
|
3150
|
+
|
|
3151
|
+
Returns:
|
|
3152
|
+
Formatted label string
|
|
3153
|
+
"""
|
|
3154
|
+
if include_equation is None:
|
|
3155
|
+
include_equation = self.show_regression_equation
|
|
3156
|
+
if include_r2 is None:
|
|
3157
|
+
include_r2 = self.show_regression_r2
|
|
3158
|
+
|
|
3159
|
+
# Start with name
|
|
3160
|
+
parts = [name]
|
|
3161
|
+
|
|
3162
|
+
# Add equation and R² on same line if both shown, more compact
|
|
3163
|
+
metrics = []
|
|
3164
|
+
if include_equation:
|
|
3165
|
+
eq = reg.equation()
|
|
3166
|
+
# Shorten equation format for compactness
|
|
3167
|
+
eq = eq.replace(' ', '') # Remove spaces
|
|
3168
|
+
metrics.append(eq)
|
|
3169
|
+
|
|
3170
|
+
if include_r2:
|
|
3171
|
+
# Use superscript 2 for R²
|
|
3172
|
+
metrics.append(f"R²={reg.r_squared:.3f}")
|
|
3173
|
+
|
|
3174
|
+
if metrics:
|
|
3175
|
+
# Join equation and R² with pipe separator for clarity
|
|
3176
|
+
parts.append(" | ".join(metrics))
|
|
3177
|
+
|
|
3178
|
+
return "\n".join(parts)
|
|
3179
|
+
|
|
3180
|
+
def _update_regression_legend(self) -> None:
|
|
3181
|
+
"""Create or update the separate regression legend with smart placement."""
|
|
3182
|
+
if not self.show_regression_legend or not self.regression_lines:
|
|
3183
|
+
return
|
|
3184
|
+
|
|
3185
|
+
if self.ax is None:
|
|
3186
|
+
return
|
|
3187
|
+
|
|
3188
|
+
# Remove old regression legend if it exists
|
|
3189
|
+
if self.regression_legend is not None:
|
|
3190
|
+
self.regression_legend.remove()
|
|
3191
|
+
self.regression_legend = None
|
|
3192
|
+
|
|
3193
|
+
# Create new regression legend with only regression lines
|
|
3194
|
+
regression_handles = []
|
|
3195
|
+
regression_labels = []
|
|
3196
|
+
|
|
3197
|
+
for line in self.regression_lines.values():
|
|
3198
|
+
regression_handles.append(line)
|
|
3199
|
+
regression_labels.append(line.get_label())
|
|
3200
|
+
|
|
3201
|
+
if regression_handles:
|
|
3202
|
+
# Smart placement: try these locations in priority order
|
|
3203
|
+
# Prefer corners away from main legend and colorbar
|
|
3204
|
+
locations = [
|
|
3205
|
+
'lower right', # Primary choice
|
|
3206
|
+
'upper right', # If lower right conflicts with data
|
|
3207
|
+
'lower left', # If right side has colorbar/main legend
|
|
3208
|
+
'center right', # Fallback
|
|
3209
|
+
]
|
|
3210
|
+
|
|
3211
|
+
# If main legend is shown, it's likely in upper left
|
|
3212
|
+
# If colorbar is shown, it's on the right side
|
|
3213
|
+
# Adjust preferences based on what's visible
|
|
3214
|
+
if self.show_legend and self.show_colorbar:
|
|
3215
|
+
# Both legend and colorbar present - lower left might be better
|
|
3216
|
+
locations = ['lower left', 'lower right', 'upper right', 'center left']
|
|
3217
|
+
elif self.show_colorbar:
|
|
3218
|
+
# Colorbar on right - prefer left side
|
|
3219
|
+
locations = ['lower left', 'upper left', 'lower right', 'center left']
|
|
3220
|
+
|
|
3221
|
+
# Try to create legend with best location
|
|
3222
|
+
# Use 'best' as fallback - matplotlib will find optimal position
|
|
3223
|
+
try:
|
|
3224
|
+
self.regression_legend = self.ax.legend(
|
|
3225
|
+
regression_handles,
|
|
3226
|
+
regression_labels,
|
|
3227
|
+
loc=locations[0], # Try first preference
|
|
3228
|
+
frameon=True,
|
|
3229
|
+
framealpha=0.95,
|
|
3230
|
+
edgecolor='#cccccc',
|
|
3231
|
+
fancybox=False,
|
|
3232
|
+
shadow=False,
|
|
3233
|
+
fontsize=9,
|
|
3234
|
+
title='Regressions',
|
|
3235
|
+
title_fontsize=10
|
|
3236
|
+
)
|
|
3237
|
+
except Exception:
|
|
3238
|
+
# Fallback to 'best' if specific location fails
|
|
3239
|
+
self.regression_legend = self.ax.legend(
|
|
3240
|
+
regression_handles,
|
|
3241
|
+
regression_labels,
|
|
3242
|
+
loc='best',
|
|
3243
|
+
frameon=True,
|
|
3244
|
+
framealpha=0.95,
|
|
3245
|
+
edgecolor='#cccccc',
|
|
3246
|
+
fancybox=False,
|
|
3247
|
+
shadow=False,
|
|
3248
|
+
fontsize=9,
|
|
3249
|
+
title='Regressions',
|
|
3250
|
+
title_fontsize=10
|
|
3251
|
+
)
|
|
3252
|
+
|
|
3253
|
+
# Modern styling
|
|
3254
|
+
self.regression_legend.get_frame().set_linewidth(0.8)
|
|
3255
|
+
self.regression_legend.get_title().set_fontweight('600')
|
|
3256
|
+
|
|
3129
3257
|
def _add_automatic_regressions(self, data: pd.DataFrame) -> None:
|
|
3130
3258
|
"""Add automatic regressions based on initialization parameters."""
|
|
3131
3259
|
if not any([self.regression, self.regression_by_color, self.regression_by_group]):
|
|
@@ -3313,8 +3441,8 @@ class Crossplot:
|
|
|
3313
3441
|
warnings.warn(f"Could not generate plot data for {name} regression: {e}")
|
|
3314
3442
|
return
|
|
3315
3443
|
|
|
3316
|
-
# Create label
|
|
3317
|
-
label =
|
|
3444
|
+
# Create label using formatter
|
|
3445
|
+
label = self._format_regression_label(name, reg)
|
|
3318
3446
|
|
|
3319
3447
|
# Plot line with config parameters
|
|
3320
3448
|
line = self.ax.plot(
|
|
@@ -3328,9 +3456,9 @@ class Crossplot:
|
|
|
3328
3456
|
|
|
3329
3457
|
self.regression_lines[name] = line
|
|
3330
3458
|
|
|
3331
|
-
# Update legend if requested (skipped during batch operations for performance)
|
|
3459
|
+
# Update regression legend if requested (skipped during batch operations for performance)
|
|
3332
3460
|
if update_legend and self.ax is not None:
|
|
3333
|
-
self.
|
|
3461
|
+
self._update_regression_legend()
|
|
3334
3462
|
|
|
3335
3463
|
def plot(self) -> 'Crossplot':
|
|
3336
3464
|
"""Generate the crossplot figure."""
|
|
@@ -3394,13 +3522,12 @@ class Crossplot:
|
|
|
3394
3522
|
warnings.warn(f"Could not generate plot data for {reg_type} regression: {e}")
|
|
3395
3523
|
continue
|
|
3396
3524
|
|
|
3397
|
-
# Create label
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
label = "\n".join(label_parts)
|
|
3525
|
+
# Create label using formatter
|
|
3526
|
+
label = self._format_regression_label(
|
|
3527
|
+
reg_name, reg,
|
|
3528
|
+
include_equation=pending['show_equation'],
|
|
3529
|
+
include_r2=pending['show_r2']
|
|
3530
|
+
)
|
|
3404
3531
|
|
|
3405
3532
|
# Plot line
|
|
3406
3533
|
line = self.ax.plot(
|
|
@@ -3414,9 +3541,9 @@ class Crossplot:
|
|
|
3414
3541
|
|
|
3415
3542
|
self.regression_lines[reg_name] = line
|
|
3416
3543
|
|
|
3417
|
-
# Update legend once after all pending regressions
|
|
3544
|
+
# Update regression legend once after all pending regressions
|
|
3418
3545
|
if self.ax is not None:
|
|
3419
|
-
self.
|
|
3546
|
+
self._update_regression_legend()
|
|
3420
3547
|
|
|
3421
3548
|
# Clear pending list
|
|
3422
3549
|
self._pending_regressions = []
|
|
@@ -3650,13 +3777,12 @@ class Crossplot:
|
|
|
3650
3777
|
warnings.warn(f"Could not generate plot data for {regression_type} regression: {e}")
|
|
3651
3778
|
return self
|
|
3652
3779
|
|
|
3653
|
-
# Create label
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
label = "\n".join(label_parts)
|
|
3780
|
+
# Create label using formatter
|
|
3781
|
+
label = self._format_regression_label(
|
|
3782
|
+
reg_name, reg,
|
|
3783
|
+
include_equation=show_equation,
|
|
3784
|
+
include_r2=show_r2
|
|
3785
|
+
)
|
|
3660
3786
|
|
|
3661
3787
|
# Plot line
|
|
3662
3788
|
line = self.ax.plot(
|
|
@@ -3670,8 +3796,8 @@ class Crossplot:
|
|
|
3670
3796
|
|
|
3671
3797
|
self.regression_lines[reg_name] = line
|
|
3672
3798
|
|
|
3673
|
-
# Update legend
|
|
3674
|
-
self.
|
|
3799
|
+
# Update regression legend
|
|
3800
|
+
self._update_regression_legend()
|
|
3675
3801
|
else:
|
|
3676
3802
|
# Store for later when plot() is called
|
|
3677
3803
|
self._pending_regressions.append({
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit.egg-info/requires.txt
RENAMED
|
File without changes
|
{well_log_toolkit-0.1.116 → well_log_toolkit-0.1.117}/well_log_toolkit.egg-info/top_level.txt
RENAMED
|
File without changes
|