Visualization¶
Template¶
- class logsuite.Template(name='default', tracks=None)[source]¶
Bases:
objectTemplate for well log display configuration.
A template defines the layout and styling of tracks in a well log display. Each track can contain multiple logs, fills, and tops markers.
- Parameters:
Examples
>>> # Create empty template >>> template = Template("reservoir") >>> >>> # Add a GR track >>> template.add_track( ... track_type="continuous", ... logs=[{"name": "GR", "x_range": [0, 150], "color": "green"}] ... ) >>> >>> # Add a porosity/saturation track with fill >>> template.add_track( ... track_type="continuous", ... logs=[ ... {"name": "PHIE", "x_range": [0.45, 0], "color": "blue"}, ... {"name": "SW", "x_range": [0, 1], "color": "red"} ... ], ... fill={ ... "left": {"curve": "PHIE"}, ... "right": {"value": 0}, ... "color": "lightblue", ... "alpha": 0.5 ... } ... ) >>> >>> # Add depth track >>> template.add_track(track_type="depth") >>> >>> # Save to file >>> template.save("reservoir_template.json") >>> >>> # Load from file >>> template2 = Template.load("reservoir_template.json")
- add_track(track_type='continuous', logs=None, fill=None, tops=None, width=1.0, title=None, log_scale=False)[source]¶
Add a track to the template.
- Parameters:
track_type ({"continuous", "discrete", "depth"}, default "continuous") – Type of track: - “continuous”: Continuous log curves (GR, RHOB, etc.) - “discrete”: Discrete/categorical logs (facies, zones) - “depth”: Depth axis track
List of log configurations. Each dict can contain:
name (str): Property name
x_range (list[float, float]): Min and max x-axis values [left, right]
scale (str): Optional override for this log’s scale (“log” or “linear”). If not specified, uses the track’s log_scale setting.
color (str): Line color
style (str): Line style - supports both matplotlib codes and friendly names. Matplotlib: “-” (solid), “–” (dashed), “-.” (dashdot), “:” (dotted), “none” (no line). Friendly: “solid”, “dashed”, “dashdot”, “dotted”, “none”. Use “none” to show only markers without a connecting line.
thickness (float): Line width
alpha (float): Transparency (0-1)
marker (str): Marker style for data points (disabled by default). Supports Matplotlib codes: “o”, “s”, “D”, “^”, “v”, “<”, “>”, “+”, “x”, “*”, “p”, “h”, “.”, “,”, “|”, “_”. Friendly names: “circle”, “square”, “diamond”, “triangle_up”, “triangle_down”, “triangle_left”, “triangle_right”, “plus”, “cross”, “star”, “pentagon”, “hexagon”, “point”, “pixel”, “vline”, “hline”.
marker_size (float): Size of markers (default: 6)
marker_outline_color (str): Marker edge color (defaults to ‘color’)
marker_fill (str): Marker fill color (optional). If not specified, markers are unfilled.
marker_interval (int): Show every nth marker (default: 1, shows all markers)
fill (Union[dict, list[dict]], optional) –
Fill configuration or list of fill configurations. Each fill dict can contain:
left: Left boundary (string/number or dict). Simple: “track_edge”, “CurveName”, or numeric value. Dict:
{"curve": name},{"value": float}, or{"track_edge": "left"}.right: Right boundary (same format as left)
color (str): Fill color name (for solid fills)
colormap (str): Matplotlib colormap name (e.g., “viridis”, “inferno”). Creates horizontal bands where depth intervals are colored based on curve values.
colormap_curve (str): Curve name to use for colormap values (defaults to left boundary curve)
color_range (list): [min, max] values for colormap normalization
alpha (float): Transparency (0-1)
Multiple fills are drawn in order (first fill is drawn first, then subsequent fills on top).
tops (dict, optional) –
Formation tops configuration with keys:
name (str): Property name containing tops
line_style (str): Line style for markers
line_width (float): Line thickness
dotted (bool): Use dotted lines
title_size (int): Font size for labels
title_weight (str): Font weight (“normal”, “bold”)
title_orientation (str): Text alignment (“left”, “center”, “right”)
line_offset (float): Horizontal offset for line position
width (float, default 1.0) – Relative width of track (used for layout proportions)
title (str, optional) – Track title to display at top
log_scale (bool, default False) – Use logarithmic scale for the entire track. Individual logs can override this with the “scale” parameter (“log” or “linear”)
- Returns:
Self for method chaining
- Return type:
Examples
>>> template = Template("my_template") >>> >>> # Add GR track >>> template.add_track( ... track_type="continuous", ... logs=[{ ... "name": "GR", ... "x_range": [0, 150], ... "color": "green", ... "style": "solid", # or "-", both work ... "thickness": 1.0 ... }], ... title="Gamma Ray" ... ) >>> >>> # Add resistivity track with log scale >>> template.add_track( ... track_type="continuous", ... logs=[{ ... "name": "RES", ... "x_range": [0.2, 2000], ... "color": "red" ... }], ... title="Resistivity", ... log_scale=True # Apply log scale to entire track ... ) >>> >>> # Add track with mixed scales (one log overrides track setting) >>> template.add_track( ... track_type="continuous", ... logs=[ ... {"name": "ILD", "x_range": [0.2, 2000], "color": "red"}, # Uses track log_scale ... {"name": "GR", "x_range": [0, 150], "scale": "linear", "color": "green"} # Override to linear ... ], ... log_scale=True # Default for track is log scale ... ) >>> >>> # Add track with different line styles >>> template.add_track( ... track_type="continuous", ... logs=[ ... {"name": "RHOB", "x_range": [1.95, 2.95], "color": "red", "style": "solid", "thickness": 1.5}, ... {"name": "NPHI", "x_range": [0.45, -0.15], "color": "blue", "style": "dashed", "thickness": 2.0} ... ], ... title="Density & Neutron" ... ) >>> >>> # Add track with markers (line + markers) >>> template.add_track( ... track_type="continuous", ... logs=[{ ... "name": "PERM", ... "x_range": [0.1, 1000], ... "color": "green", ... "style": "solid", ... "marker": "circle", ... "marker_size": 4, ... "marker_fill": "lightgreen" ... }], ... title="Permeability", ... log_scale=True ... ) >>> >>> # Add track with markers only (no line) >>> template.add_track( ... track_type="continuous", ... logs=[{ ... "name": "SAMPLE_POINTS", ... "x_range": [0, 100], ... "color": "red", ... "style": "none", ... "marker": "diamond", ... "marker_size": 8, ... "marker_outline_color": "darkred", ... "marker_fill": "yellow" ... }], ... title="Sample Locations" ... ) >>> >>> # Add porosity track with fill (simplified API) >>> template.add_track( ... track_type="continuous", ... logs=[{ ... "name": "PHIE", ... "x_range": [0.45, 0], ... "color": "blue" ... }], ... fill={ ... "left": "PHIE", # Simple: curve name ... "right": 0, # Simple: numeric value ... "color": "lightblue", ... "alpha": 0.5 ... }, ... title="Porosity" ... ) >>> >>> # Add GR track with colormap fill (horizontal bands colored by GR value) >>> template.add_track( ... track_type="continuous", ... logs=[{"name": "GR", "x_range": [0, 150], "color": "black"}], ... fill={ ... "left": "track_edge", # Simple: track edge ... "right": "GR", # Simple: curve name ... "colormap": "viridis", ... "color_range": [20, 150], ... "alpha": 0.7 ... }, ... title="Gamma Ray" ... ) >>> >>> # Add porosity/saturation track with multiple fills >>> template.add_track( ... track_type="continuous", ... logs=[ ... {"name": "PHIE", "x_range": [0.45, 0], "color": "blue"}, ... {"name": "SW", "x_range": [0, 1], "color": "red"} ... ], ... fill=[ ... # Fill 1: PHIE to zero with light blue ... { ... "left": "PHIE", ... "right": 0, ... "color": "lightblue", ... "alpha": 0.3 ... }, ... # Fill 2: SW to one with light red ... { ... "left": "SW", ... "right": 1, ... "color": "lightcoral", ... "alpha": 0.3 ... } ... ], ... title="PHIE & SW" ... ) >>> >>> # Add discrete facies track >>> template.add_track( ... track_type="discrete", ... logs=[{"name": "Facies"}], ... title="Facies" ... ) >>> >>> # Add depth track >>> template.add_track(track_type="depth", width=0.3)
- remove_track(index)[source]¶
Remove track at specified index.
- Parameters:
index (int) – Track index to remove
- Returns:
Self for method chaining
- Return type:
Examples
>>> template.remove_track(0) # Remove first track
- add_tops(property_name=None, tops_dict=None, colors=None, styles=None, thicknesses=None)[source]¶
Add well tops configuration to the template.
Tops added to the template will be displayed in all WellViews created from this template. They span across all tracks (except depth track).
- Parameters:
property_name (str, optional) – Name of discrete property in well containing tops data. The property name will be resolved when the template is used with a well.
tops_dict (dict[float, str], optional) – Dictionary mapping depth values to formation names. Example: {2850.0: ‘Formation A’, 2920.5: ‘Formation B’}
colors (dict[float, str], optional) – Optional color mapping for each depth or discrete value. - For tops_dict: keys are depths matching tops_dict keys - For property_name: keys are discrete values (integers) If not provided and using a discrete property with color mapping, those colors will be used.
styles (dict[float, str], optional) – Optional line style mapping for each depth or discrete value. Valid styles: ‘solid’, ‘dashed’, ‘dotted’, ‘dashdot’ - For tops_dict: keys are depths matching tops_dict keys - For property_name: keys are discrete values (integers) If not provided and using a discrete property with style mapping, those styles will be used.
thicknesses (dict[float, float], optional) – Optional line thickness mapping for each depth or discrete value. - For tops_dict: keys are depths matching tops_dict keys - For property_name: keys are discrete values (integers) If not provided and using a discrete property with thickness mapping, those thicknesses will be used.
- Returns:
Self for method chaining
- Return type:
Examples
>>> # Add tops from discrete property (resolved when used with well) >>> template = Template("my_template") >>> template.add_track(...) >>> template.add_tops(property_name="Formations") >>> >>> # Add manual tops with colors >>> template.add_tops( ... tops_dict={2850.0: 'Reservoir', 2920.5: 'Seal'}, ... colors={2850.0: 'yellow', 2920.5: 'gray'} ... ) >>> >>> # Add tops from discrete property with color overrides >>> template.add_tops( ... property_name='Zone', ... colors={0: 'red', 1: 'green', 2: 'blue'} # Map discrete values ... )
Notes
Tops are drawn as horizontal lines spanning all tracks (except depth track). Formation names are displayed at the right end of each line, floating above it.
- edit_track(index, **kwargs)[source]¶
Edit track at specified index.
- Parameters:
index (int) – Track index to edit
**kwargs – Track parameters to update (same as add_track)
- Returns:
Self for method chaining
- Return type:
Examples
>>> # Change track title >>> template.edit_track(0, title="New Title") >>> >>> # Update log styling >>> template.edit_track(1, logs=[{"name": "PHIE", "color": "red"}])
- get_track(index)[source]¶
Get track configuration at specified index.
Examples
>>> track_config = template.get_track(0) >>> print(track_config["type"]) 'continuous'
- list_tracks()[source]¶
List all tracks with summary information.
- Returns:
DataFrame with columns: Index, Type, Logs, Title, Width
- Return type:
pd.DataFrame
Examples
>>> template.list_tracks() Index Type Logs Title Width 0 0 continuous [GR] Gamma Ray 1.0 1 1 continuous [PHIE, SW] Porosity 1.0 2 2 depth [] Depth 0.3
- save(filepath)[source]¶
Save template to JSON file.
- Parameters:
filepath (Union[str, Path]) – Path to save the JSON file.
- Return type:
None
Examples
>>> template.save("reservoir_template.json")
- classmethod load(filepath)[source]¶
Load template from JSON file.
- Parameters:
filepath (Union[str, Path]) – Path to JSON file
- Returns:
Loaded template
- Return type:
Examples
>>> template = Template.load("reservoir_template.json")
WellView¶
- class logsuite.WellView(well, depth_range=None, tops=None, template=None, figsize=None, dpi=100, header_config=None)[source]¶
Bases:
objectInteractive well log display for Jupyter Lab.
Creates matplotlib-based well log plots with multiple tracks showing continuous logs, discrete properties, fills, and formation tops.
- Parameters:
well (Well) – Well object containing log data
depth_range (tuple[float, float], optional) – Depth interval to display [start_depth, end_depth]. If None, shows full depth range.
template (Union[Template, dict, str], optional) – Display template. Can be: - Template object - Dictionary with template configuration - String name of template stored in well’s parent manager If None, creates a simple default view.
figsize (tuple[float, float], optional) – Figure size (width, height) in inches. If None, calculated from number of tracks.
dpi (int, default 100) – Figure resolution
Attributes (Class)
----------------
HEADER_BOX_TOP (float) – Fixed top position of header boxes for alignment across tracks
HEADER_TITLE_SPACING (float) – Vertical space between log name and its scale line in continuous tracks
HEADER_LOG_SPACING (float) – Vertical space allocated per log in continuous tracks
HEADER_TOP_PADDING (float) – Padding inside header box above content
HEADER_BOTTOM_PADDING (float) – Padding inside header box below content
header_config (dict | None)
- well¶
Source well object
- Type:
Well
- fig¶
Matplotlib figure object
- Type:
- axes¶
List of axes for each track
- Type:
Examples
>>> from logsuite import WellDataManager >>> from logsuite.visualization import WellView, Template >>> >>> # Load data >>> manager = WellDataManager() >>> manager.load_las("well.las") >>> well = manager.well_36_7_5_A >>> >>> # Create template >>> template = Template("basic") >>> template.add_track( ... track_type="continuous", ... logs=[{"name": "GR", "x_range": [0, 150], "color": "green"}], ... title="Gamma Ray" ... ) >>> template.add_track(track_type="depth") >>> >>> # Display well log with depth range >>> view = WellView(well, depth_range=[2800, 3000], template=template) >>> view.show() >>> >>> # Or auto-calculate from formation tops >>> template.add_tops(property_name='Zone') >>> view2 = WellView(well, tops=['Top_Brent', 'Top_Statfjord'], template=template) >>> view2.show() >>> >>> # Or use template from manager >>> manager.set_template("reservoir", template) >>> view3 = WellView(well, depth_range=[3000, 3200], template="reservoir") >>> view3.show() >>> >>> # Save figure >>> view.save("well_log.png", dpi=300)
- HEADER_BOX_TOP = 1.1¶
- HEADER_TITLE_SPACING = 0.0015¶
- HEADER_LOG_SPACING = 0.025¶
- HEADER_TOP_PADDING = 0.01¶
- HEADER_BOTTOM_PADDING = 0.01¶
- add_track(track_type='continuous', logs=None, fill=None, width=1.0, title=None, log_scale=False)[source]¶
Add a temporary track to this view (not saved to template).
This allows adding tracks to a specific view without modifying the underlying template. Temporary tracks are appended after template tracks.
- Parameters:
track_type ({"continuous", "discrete", "depth"}, default "continuous") – Type of track
logs (list[dict], optional) – List of log configurations (same format as Template.add_track)
fill (Union[dict, list[dict]], optional) – Fill configuration or list of fills
width (float, default 1.0) – Relative width of track
title (str, optional) – Track title
log_scale (bool, default False) – Use logarithmic scale for the track
- Returns:
Self for method chaining
- Return type:
Examples
>>> # Create view with template, then add temporary track >>> view = WellView(well, template=template) >>> view.add_track( ... track_type="continuous", ... logs=[{"name": "TEMP_LOG", "x_range": [0, 100], "color": "orange"}], ... title="Temporary" ... ) >>> view.show()
Notes
Temporary tracks are not saved to the template and only exist for this view. If you want to reuse tracks across multiple views, add them to the template instead.
- add_tops(property_name=None, tops_dict=None, colors=None, styles=None, thicknesses=None, source=None)[source]¶
Add temporary well tops to this view (not saved to template).
Tops can be specified either from a discrete property in the well or as a dictionary mapping depths to formation names.
- Parameters:
property_name (str, optional) – Name of discrete property in well containing tops data. The property should have depth values where formations start.
tops_dict (dict[float, str], optional) – Dictionary mapping depth values to formation names. Example: {2850.0: ‘Formation A’, 2920.5: ‘Formation B’}
colors (dict[float, str], optional) – Optional color mapping for each depth. If provided, must have same keys as tops_dict or match values in the discrete property. Colors can be matplotlib color names, hex codes, or RGB tuples. If not provided and using a discrete property with color mapping, those colors will be used.
styles (dict[float, str], optional) – Optional line style mapping for each depth or discrete value. Valid styles: ‘solid’, ‘dashed’, ‘dotted’, ‘dashdot’ If not provided and using a discrete property with style mapping, those styles will be used.
thicknesses (dict[float, float], optional) – Optional line thickness mapping for each depth or discrete value. If not provided and using a discrete property with thickness mapping, those thicknesses will be used.
source (str, optional) – Source name to get property from (if property_name is specified). Only needed if property exists in multiple sources.
- Returns:
Self for method chaining
- Return type:
Examples
>>> # Add tops from discrete property >>> view = WellView(well, template=template) >>> view.add_tops(property_name='Zone') >>> view.show() >>> >>> # Add tops manually with custom colors >>> view.add_tops( ... tops_dict={2850.0: 'Reservoir', 2920.5: 'Seal'}, ... colors={2850.0: 'yellow', 2920.5: 'gray'} ... ) >>> >>> # Add tops from discrete property, overriding colors >>> view.add_tops( ... property_name='Formation', ... colors={0: 'red', 1: 'green', 2: 'blue'} # Map discrete values to colors ... )
Notes
Tops are drawn as horizontal lines spanning all tracks (except depth track). Formation names are displayed at the right end of each line, floating above it. Temporary tops are not saved to the template.
- plot()[source]¶
Create the well log plot.
This method generates the matplotlib figure with all configured tracks. Call show() or save() after this to display or save the figure.
Examples
>>> view = WellView(well, template=template) >>> view.plot() >>> view.show()
- Return type:
None
- show()[source]¶
Display the well log plot in Jupyter notebook.
This will render the plot inline in Jupyter Lab/Notebook.
Examples
>>> view = WellView(well, template=template) >>> view.show()
- Return type:
None
Crossplot¶
- class logsuite.Crossplot(wells, x=None, y=None, layers=None, shape=None, color=None, size=None, colortemplate='viridis', color_range=None, size_range=(20, 200), title='Cross Plot', xlabel=None, ylabel=None, figsize=(10, 8), dpi=100, marker='o', marker_size=50, marker_alpha=0.7, edge_color='black', edge_width=0.5, x_log=False, y_log=False, grid=True, grid_alpha=0.7, depth_range=None, show_colorbar=True, show_legend=True, show_regression_legend=True, show_regression_equation=True, show_regression_r2=True, equation_format='natural', decimals=4, regression=None, regression_by_color=None, regression_by_group=None, regression_by_color_and_shape=None, regression_by_shape_and_color=None)[source]¶
Bases:
objectCreate beautiful, modern crossplots for well log analysis.
Supports single and multi-well crossplots with extensive customization options including color mapping, size mapping, shape mapping, regression analysis, and multi-layer plotting for combining different data types (e.g., Core vs Sidewall).
- Parameters:
wells (Well or list of Well) – Single well or list of wells to plot
x (str, optional) – Name of property for x-axis. Required if layers is not provided.
y (str, optional) – Name of property for y-axis. Required if layers is not provided.
layers (dict[str, list[str]], optional) – Dictionary mapping layer labels to [x_property, y_property] lists. Use this to combine multiple property pairs in a single plot. Example: {“Core”: [“CorePor”, “CorePerm”], “Sidewall”: [“SWPor”, “SWPerm”]} When using layers, shape defaults to “label” and color defaults to “well” for easy visualization of both layer types and wells. Default: None
shape (str, optional) – Property name for shape mapping. Use “well” to map shapes by well name, or “label” (when using layers) to map shapes by layer type. Default: “well” for multi-well plots, “label” when layers provided, None otherwise
color (str, optional) – Property name for color mapping. Use “depth” to color by depth, “well” to color by well, or “label” (when using layers) to color by layer type. Default: “well” when layers provided, None otherwise
size (str, optional) – Property name for size mapping, or “label” (when using layers) to size by layer type. Default: None (constant size)
colortemplate (str, optional) – Matplotlib colormap name (e.g., “viridis”, “plasma”, “coolwarm”) Default: “viridis”
color_range (tuple[float, float], optional) – Min and max values for color mapping. If None, uses data range. Default: None
size_range (tuple[float, float], optional) – Min and max marker sizes for size mapping. Default: (20, 200)
title (str, optional) – Plot title. Default: “Cross Plot”
xlabel (str, optional) – X-axis label. If None, uses property name.
ylabel (str, optional) – Y-axis label. If None, uses property name.
figsize (tuple[float, float], optional) – Figure size (width, height) in inches. Default: (10, 8)
dpi (int, optional) – Figure resolution. Default: 100
marker (str, optional) – Marker style. Default: “o”
marker_size (float, optional) – Base marker size. Default: 50
marker_alpha (float, optional) – Marker transparency (0-1). Default: 0.7
edge_color (str, optional) – Marker edge color. Default: “black”
edge_width (float, optional) – Marker edge width. Default: 0.5
x_log (bool, optional) – Use logarithmic scale for x-axis. Default: False
y_log (bool, optional) – Use logarithmic scale for y-axis. Default: False
grid (bool, optional) – Show grid. Default: True
grid_alpha (float, optional) – Grid transparency. Default: 0.3
depth_range (tuple[float, float], optional) – Depth range to filter data. Default: None (all depths)
show_colorbar (bool, optional) – Show colorbar when using color mapping. Default: True
show_legend (bool, optional) – Show legend when using shape/well mapping. Default: True
show_regression_legend (bool, optional) – Show separate legend for regression lines in lower right. Default: True
show_regression_equation (bool, optional) – Show equations in regression legend. Default: True
show_regression_r2 (bool, optional) – Show R-squared values in regression legend. Default: True
regression (str or dict, optional) – Regression type to apply to all data points. Can be a string (e.g., “linear”) or dict with keys: type, line_color, line_width, line_style, line_alpha, x_range. Default: None
regression_by_color (str or dict, optional) – Regression type to apply separately for each color group in the plot. Creates separate regression lines based on what determines colors in the visualization: explicit color mapping if specified, otherwise shape groups (e.g., wells when shape=’well’). Accepts string or dict format. Default: None
regression_by_group (str or dict, optional) – Regression type to apply separately for each group (well or shape). Creates separate regression lines for each well or shape category. Accepts string or dict. Default: None
regression_by_color_and_shape (str or dict, optional) – Regression type to apply separately for each combination of color AND shape groups. Creates separate regression lines for each (color, shape) combination. This is useful for analyzing how the relationship changes across both dimensions simultaneously (e.g., each well in each formation, each layer in each zone). Accepts string or dict. Default: None
regression_by_shape_and_color (str or dict, optional) – Alias for
regression_by_color_and_shape. Provided for convenience — both parameters do exactly the same thing. Use whichever order feels more natural. Defaults toNone.equation_format (str)
decimals (int)
Examples
Basic crossplot from a single well:
>>> plot = well.Crossplot(x="RHOB", y="NPHI") >>> plot.show()
Multi-well crossplot with color and size mapping:
>>> plot = manager.Crossplot( ... x="PHIE", ... y="SW", ... color="depth", ... size="PERM", ... shape="well", ... colortemplate="viridis" ... ) >>> plot.show()
With regression analysis (string format):
>>> plot = well.Crossplot(x="RHOB", y="NPHI", regression="linear") >>> plot.show()
With regression analysis (dict format for custom styling):
>>> plot = well.Crossplot( ... x="RHOB", y="NPHI", ... regression={"type": "linear", "line_color": "red", "line_width": 3} ... ) >>> plot.show()
Multi-well with group-specific regressions:
>>> plot = manager.Crossplot( ... x="PHIE", y="SW", ... shape="well", ... regression_by_group={"type": "linear", "line_style": "--"} ... ) >>> plot.show()
Combining multiple data types with layers (Core + Sidewall):
>>> plot = manager.Crossplot( ... layers={ ... "Core": ["CorePor_obds", "CorePerm_obds"], ... "Sidewall": ["SidewallPor_ob", "SidewallPerm_ob"] ... }, ... y_log=True ... # shape defaults to "label" - different shapes for Core vs Sidewall ... # color defaults to "well" - different colors for each well ... ) >>> plot.show()
Using add_layer method with method chaining:
>>> manager.Crossplot(y_log=True) \ ... .add_layer("CorePor_obds", "CorePerm_obds", label="Core") \ ... .add_layer("SidewallPor_ob", "SidewallPerm_ob", label="Sidewall") \ ... .show() ... # Automatically uses shape="label" and color="well"
Layers with regression by color (single trend per well):
>>> plot = manager.Crossplot( ... layers={ ... "Core": ["CorePor_obds", "CorePerm_obds"], ... "Sidewall": ["SidewallPor_ob", "SidewallPerm_ob"] ... }, ... regression_by_color="linear" # One trend per well (combining both data types) ... # Defaults: shape="label" (different shapes), color="well" (different colors) ... ) >>> plot.show()
Access regression objects:
>>> linear_regs = plot.regression("linear") >>> for name, reg in linear_regs.items(): ... print(f"{name}: {reg.equation()}, R²={reg.r_squared:.3f}")
- add_layer(x, y, label)[source]¶
Add a new data layer to the crossplot.
This allows combining multiple property pairs in a single plot, useful for comparing different data types (e.g., Core vs Sidewall data).
- Parameters:
- Returns:
Returns self to allow method chaining
- Return type:
self
Examples
>>> plot = manager.Crossplot(y_log=True) >>> plot.add_layer('CorePor_obds', 'CorePerm_obds', label='Core') >>> plot.add_layer('SidewallPor_ob', 'SidewallPerm_ob', label='Sidewall') >>> plot.show()
With method chaining: >>> manager.Crossplot(y_log=True) … .add_layer(‘CorePor_obds’, ‘CorePerm_obds’, label=’Core’) … .add_layer(‘SidewallPor_ob’, ‘SidewallPerm_ob’, label=’Sidewall’) … .show()
- regression(regression_type=None)[source]¶
Access regression objects.
- Args:
regression_type: Optional regression type to filter by (e.g., “linear”, “polynomial”)
- Returns:
- If regression_type is None: Returns all regressions organized by type:
{“linear”: {“red”: RegObj, …}, “polynomial”: {“blue”: RegObj, …}}
- If regression_type specified: Returns regressions of that type:
{“red”: RegObj, …}
- Examples:
>>> plot.regression() # Get all regressions >>> plot.regression("linear") # Get only linear regressions
- add_regression_per(group_property, regression_type, min_samples=5, **kwargs)[source]¶
Fit one regression per unique value of a group property.
Convenience wrapper around
add_regression()that enumerates the unique values ofgroup_property(from the prepared crossplot data) and callsadd_regressiononce per value with the appropriatewhere=andname=set automatically. Each resulting line picks up its color fromProperty.colorsvia_resolve_line_color_from_where()when the palette is set, otherwise defaults apply.- Parameters:
group_property (str) – Public column name (e.g. the property bound to
colororshapeon this Crossplot). Must resolve to a column on the prepared data.regression_type (str) – Forwarded to
add_regression()("linear","exponential", …).min_samples (int, default 5) – Forwarded to
add_regression(). Subsets smaller than this are skipped with a warning.**kwargs – Other arguments forwarded to
add_regression().where=andname=are filled in by this method; passing them explicitly raisesTypeError.
- Returns:
Self for method chaining.
- Return type:
Examples
>>> xplot = Crossplot(manager, x="PHIE", y="PERM", color="Facies") >>> xplot.add_regression_per("Facies", "exponential", equation_format="petrel") # Three regressions added, one per facies, each colored from # manager.Facies.colors when set.
- column_for(public_name)[source]¶
Return the prepared-data column name for a public property reference.
Useful when writing a callable for
add_regression(where=callable)that needs to inspect the prepared crossplot DataFrame: instead of hardcoding"color_val", you can writedf[xplot.column_for("Facies")] == 2.0and stay independent of which property is bound to which role.- Parameters:
public_name (str) – A property name bound to
x,y,color,shape, orsizeon this Crossplot, or one of the literal internal column names ("x","y","color_val","shape_val","size_val","well").- Returns:
The internal column name, or
Noneifpublic_namedoes not match any binding.- Return type:
str or None
Examples
>>> xplot = Crossplot(manager, x="PHIE", y="PERM", color="Facies") >>> xplot.column_for("Facies") 'color_val' >>> xplot.column_for("PHIE") 'x'
- add_regression(regression_type, name=None, line_color=None, line_width=2, line_style='-', line_alpha=0.8, show_equation=True, show_r2=True, x_range=None, where=None, min_samples=5, decimals=None, legend_decimals=None, equation_format=None, legend_loc=None, **kwargs)[source]¶
Add a regression line to the crossplot.
- Parameters:
regression_type (str) – Type of regression: “linear”, “logarithmic”, “exponential”, “polynomial”, or “power”
name (str, optional) – Name for this regression. If None, uses regression_type.
line_color (str, optional) – Color of regression line. Default: ‘red’
line_width (float, optional) – Width of regression line. Default: 2
line_style (str, optional) – Style of regression line. Default: ‘-’
line_alpha (float, optional) – Transparency of regression line. Default: 0.8
show_equation (bool, optional) – Show equation in legend. Default: True
show_r2 (bool, optional) – Show R-squared value in legend. Default: True
x_range (tuple[float, float], optional) – Custom x-axis range for plotting the regression line. If None, uses the data range from fitting.
where (dict or callable, optional) –
Restrict the fit to a subset of points. Two forms:
dict:
{column: allowed_value(s)}. Keys may be public property names (the same asx,y,color,shape,sizeon the Crossplot) or the literal column name"well". Values are scalars or lists. Example:where={"Facies": [5, 6]}.callable: a function
f(df) -> boolean maskover the prepared crossplot data. Example:where=lambda df: df["color_val"].isin([5, 6]).
min_samples (int, default 5) – Minimum number of points required after filtering. If the filtered subset has fewer rows, a warning is emitted and the regression is skipped (no exception).
decimals (int, optional) – Decimal precision for the equation in the legend.
Nonefalls back to the Crossplot constructor’sdecimals=(4 by default).legend_decimals (int, optional) – Alias for
decimals. If both are supplied,legend_decimalswins. Provided for API symmetry withlegend_loc.equation_format ({"natural", "log10", "petrel"}, optional) – Equation form rendered in the legend.
None(default) falls back to the Crossplot constructor’sequation_format=.log10andpetrelare meaningful for exponential fits — Petrel form yieldspow(10, c1*x + c0), suitable for direct paste into Petrel calculators. Other models fall back to natural.legend_loc (str or tuple, optional) – Matplotlib
locfor the regression legend (e.g."upper left",(0.65, 0.05)). When supplied, overrides the auto-placement algorithm. The latestlegend_loc=value across all add_regression calls wins, since the regression legend is rebuilt as one block.**kwargs – Additional arguments for regression (e.g., degree for polynomial)
- Returns:
Self for method chaining
- Return type:
Examples
>>> plot = well.Crossplot(x="RHOB", y="NPHI") >>> plot.add_regression("linear") >>> plot.add_regression("polynomial", degree=2, line_color="blue") >>> plot.add_regression("linear", x_range=(0, 10)) # Custom range >>> plot.show()
- add(artifact)[source]¶
Add an
Artifactto the crossplot.The artifact’s
_render_in_crossplot(ax)method is called with this crossplot’s matplotlib axis. Artifacts that do not support Crossplot rendering raiseTypeError. The plot is rendered first if it has not been already.- Parameters:
artifact (Artifact) – Any object implementing
_render_in_crossplot(ax).- Returns:
Self for method chaining.
- Return type:
Examples
>>> from logsuite import ExponentialRegression, RegressionFit >>> reg = ExponentialRegression().fit(df["PHIE"], df["PERM"]) >>> fit = RegressionFit(reg, name="all wells", equation_format="petrel") >>> xplot = Crossplot(...).add(fit)
- add_table_panel(df, position='bottom', title=None, formatters=None, table_fraction=0.3)[source]¶
Attach a DataFrame as a rendered table panel to this Crossplot.
Grows the figure along the panel’s axis so the scatter is not squished, then renders the DataFrame using
logsuite.visualization.table_panel.render_table_panel(). After this call,self.save("file.svg")produces the combined scatter+table figure as a single deliverable.- Parameters:
df (pandas.DataFrame) – Rows × columns of values to render.
NaNvalues become"N/A";MultiIndexcolumns flatten via" | ";MultiIndexrows visually merge repeated outer levels.position ({"bottom", "right"}, default "bottom") – Where the panel sits relative to the scatter axes.
title (str, optional) – Heading rendered above the table.
formatters (dict, optional) – Per-column formatter spec. Each value may be a callable
f(value) -> stror a Python format spec like".4f". Example:{"PHIE": ".4f", "PERM": ".2f"}.table_fraction (float, default 0.30) – Fraction of the figure dimension reserved for the panel.
- Returns:
Self for method chaining.
- Return type:
Examples
>>> stats = manager.PHIE.filter("Facies").stats( ... return_df=True, flat_columns=True ... ) >>> xplot = Crossplot(manager, x="PHIE", y="PERM", color="Facies") >>> xplot.add_regression_per("Facies", "exponential") >>> xplot.add_table_panel(stats, title="Per-facies summary", ... formatters={"mean": ".4f", "p50": ".4f"}) >>> xplot.save("deliverable.svg")