'''
A Python module for displaying log, lithofacies and zonation plots
'''
from __future__ import annotations
import numpy as np
import lasio
import csv
import pandas as pd
import contextily as ctx
import geopandas as gpd
import os
from itertools import cycle
from pathlib import Path
from random import choice
from matplotlib import pyplot as plt
from typing import *
from random import choice
from matplotlib.patches import Patch
[docs]def plotLoc(data:list[lasio.las.LASFile], shape_file:Path=None, area:str=None, figsize:tuple=(7, 7),
label:list=None, withmap:bool=False, return_table:bool=False) -> pd.DataFrame|None:
'''
Plots location of wells
The longitude and latitude must be available in the LAS files
Parameters
----------
data : list
List of lasio.las.LASFile objects
shape_file : str, default None
A .shp, .shx or .dbf file containing the shape file of the area where the well is located
If not supplied, `area` must be passed
area : str default, None
Well location. Must inlcude area from `gpd.datasets.get_path('naturalearth_lowres')`
figsize : tuple
Size of plot
label : list of str
Well name(s). Must be a list of equal length with `data`
withmap : bool default False
Plots location of wells on a map if True
Plots the location on a scatter plot if False
return_table : bool default False
Return well information such as the long, lat and name
Example
-------
>>> plotLoc(data=[las], area='Norway', label=['15_9-F-1A'], withmap=True, figsize=(30, 10))
'''
#getting well names
if label!=None:
assert len(label) == len(data), 'label and data not of same length'
well_name = label
#getting longitude and latitude from LAS
latitude = list()
longitude = list()
try:
for idx, d in enumerate(data):
assert type(d) == lasio.las.LASFile, f'Expected a lasio data object'
# well_name.append(str(idx))
longitude.append(d.well.LONG.value)
latitude.append(d.well.LATI.value)
except:
raise AttributeError('Latitude and Longitude not in LAS file')
#save coordinate location in dataframe
latitude = [float(i[:3]) for i in latitude]
longitude = [float(i[:3]) for i in longitude]
well_df = pd.DataFrame({'WELL_NAME':well_name, 'LAT':latitude,'LONG':longitude})
a, b = np.array(longitude), np.array(latitude)
cycol = cycle('bgrcmk')
color = [choice(next(cycol)) for i in range(len(well_name))]
#plot well coordinates on map
if withmap == True:
if shape_file == None:
assert area != None, f'Must supply area if shape_file is None'
shape = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
shape = shape[shape['name'] == area]
#plotting
fig, ax = plt.subplots(figsize=figsize)
plt.title(f'WELL LOCATION - {area}')
shape.geometry.plot(ax=ax, figsize=figsize, color='#e3bccf', edgecolor='k', alpha=0.5, zorder=1)
a, b = np.array(longitude), np.array(latitude)
plt.plot(a, b, 'b*', markersize=15)
for i in range(0,well_df.shape[0]):
plt.text(a[i], b[i], well_name[i], ha='right', size='large', color=color[i], va='bottom')
ctx.add_basemap(ax, source=ctx.providers.CartoDB.Positron)
elif shape_file != None:
assert area == None, f'Area must be set to None'
assert shape_file.endswidth('.shx') or shape_file.endswidth('.shp') or shape_file.endswidth('.dbf') or shape_file.endswidth('.prj'), f'Supply a valid shape file'
shape = gpd.read_file(shape_file)
#Plotting locations
fig, ax = plt.subplots(1, figsize=figsize)
plt.title('WELL LOCATION')
shape.geometry.plot(ax=ax, color='#e3bccf', edgecolor='k', alpha=0.5, zorder=1)
plt.plot(a, b, 'b*', markersize=15)
for i in range(0,well_df.shape[0]):
plt.text(a[i], b[i], well_name[i], ha='right', size='large', color=color[i], va='bottom')
ctx.add_basemap(ax, source=ctx.providers.CartoDB.Positron)
else:
assert area == None, 'Set area to None'
fig=plt.figure(figsize=figsize)
plt.title('WELL LOCATION')
plt.grid(which='major', linestyle=':')
plt.plot(a, b, 'b*', markersize=15)
for i in range(0,well_df.shape[0]):
plt.text(a[i], b[i], well_name[i], ha='right', size='large', color=color[i], va='bottom')
if return_table:
return well_df
else:
pass
[docs]def tripleCombo(data:pd.DataFrame, depth:str, gr:str, res:str, nphi:str, rhob:str, ztop:float, zbot:float,
res_thres:float=10.0, fill:str=None, palette_op:str=None,
limit:str=None, figsize:tuple=(9, 10), title:str='Three Combo Log Plot') -> None:
r'''
Plots a three combo log of well data
Parameters
----------
data : pd.DataFrame
Dataframe of data
depth : str
Depth column
gr : str
Gamma ray column
res : str
Resistivity column
nphi : str
Neutron porosity column
rhob : str
Bulk density column
ztop : list
Top or minimum depth to zoom on.
zbot : list
Bottom or maximum depth to zoom on.
res_thres : float
Resistivity threshold to use in the identification on hydrocarbon bearing zone
fill : str default None
To show either of the porous and/or non porous zones in the neutron-density crossover.
Can either be ['left', 'right', 'both']
* default None - show neither of the porous nor non-porous zones
* 'left' - shows only porous zones
* 'right' - shows only non-porous zones
* 'both' - shows both porous and non-porous zones
palette_op : str optional
Palette option to fill gamma ray log
limit : str default None
Tells which side to fill the gamma ray track, ['left', 'right']
lf None, it's filled in both sides delineating shale-sand region
figsize : tuple
Size of plot
title : str
Title of plot
Example
-------
>>> import matplotlib inline
>>> from petrolib.plots import tripleCombo
>>> # %matplotlib inline
>>> tripleCombo(df, 'DEPTH', 'GR', 'RT', 'NPHI', 'RHOB', ztop=3300,
>>> zbot=3450, res_thres=10, fill='right', palette_op='rainbow', limit='left')
'''
#getting logs from dataframe
depth_log = data[depth]
gr_log = data[gr]
res_log = data[res]
nphi_log = data[nphi]
rhob_log = data[rhob]
# color-fill options
span = abs(gr_log.min()-gr_log.max())
if palette_op != None:
cmap=plt.get_cmap(palette_op)
else:
pass
color_index = np.arange(gr_log.min(), gr_log.max(), span/100)
# create the subplots; ncols equals the number of logs
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=figsize,sharey=True)
fig.suptitle(f'{title}', size=15, y=1.)
#for GR track
ax[0].minorticks_on()
ax[0].grid(which='major', linestyle='-', linewidth=1, color='darkgrey')
ax[0].yaxis.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
ax[0].plot(gr_log, depth_log, color='black', linewidth=1.0)
ax[0].set_xlim(gr_log.min(), gr_log.max())
ax[0].set_ylim(ztop, zbot)
ax[0].invert_yaxis()
ax[0].xaxis.label.set_color('black')
ax[0].tick_params(axis='x', colors='black')
ax[0].spines['top'].set_edgecolor('black')
ax[0].set_xlabel('Gamma ray\nGR (gAPI)', color='black', labelpad=15)
ax[0].spines["top"].set_position(("axes", 1.02))
ax[0].xaxis.set_ticks_position("top")
ax[0].xaxis.set_label_position("top")
if palette_op == None:
assert limit == None, 'Set limit to None'
gr_base = (gr_log.max() - gr_log.min())/2
ax[0].fill_betweenx(depth_log, gr_base, gr_log, where=gr_log<=gr_base, facecolor='yellow', linewidth=0)
ax[0].fill_betweenx(depth_log, gr_log, gr_base, where=gr_log>=gr_base, facecolor='brown', linewidth=0)
elif palette_op != None:
assert limit != None, 'Set limit value. Can\'t be None'
if limit == 'left':
for index in sorted(color_index):
index_value = (index-gr_log.min())/span
palette = cmap(index_value)
ax[0].fill_betweenx(depth_log, gr_log.min(), gr_log, where=gr_log>=index, color=palette)
elif limit == 'right':
for index in sorted(color_index):
index_value = (index-gr_log.min())/span
palette = cmap(index_value)
ax[0].fill_betweenx(depth_log, gr_log.max(), gr_log, where=gr_log>=index, color=palette)
#for resitivity
ax[1].minorticks_on()
ax[1].grid(which='major', linestyle='-', linewidth=1.0, color='darkgrey')
ax[1].grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
ax[1].yaxis.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
ax[1].semilogx(res_log, depth_log, color='red', linewidth=1.0, linestyle='--')
ax[1].set_xlim(res_log.min(), res_log.max())
ax[1].set_ylim(ztop, zbot)
ax[1].invert_yaxis()
ax[1].xaxis.label.set_color('red')
ax[1].tick_params(axis='x', colors='red')
ax[1].spines['top'].set_edgecolor('red')
ax[1].set_xlabel('Resistivity\nILD (ohm.m)', labelpad=15)
ax[1].spines["top"].set_position(("axes", 1.02))
ax[1].xaxis.set_ticks_position("top")
ax[1].xaxis.set_label_position("top")
ax[1].fill_betweenx(depth_log, res_thres, res_log, where=res_log >= res_thres, interpolate=True, color='red', linewidth=0)
#for nphi
ax[2].minorticks_on()
ax[2].yaxis.grid(which='major', linestyle='-', linewidth=1, color='darkgrey')
ax[2].yaxis.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
ax[2].set_xticklabels([]);ax[2].set_xticks([])
rhob_ = ax[2].twiny()
rhob_.plot(rhob_log, depth_log, color='red', linewidth=1.)
rhob_.set_xlim(rhob_log.min(), rhob_log.max())
rhob_.set_ylim(ztop, zbot)
rhob_.invert_yaxis()
rhob_.xaxis.label.set_color('red')
rhob_.tick_params(axis='x', colors='red')
rhob_.spines['top'].set_edgecolor('red')
rhob_.set_xlabel('Bulk Density\nRHOB (g/cm3)', color='red')
rhob_.spines["top"].set_position(("axes", 1.02))
rhob_.xaxis.set_ticks_position("top")
rhob_.xaxis.set_label_position("top")
rhob_.set_xticks(list(np.linspace(rhob_log.min(), rhob_log.max(), num=5, dtype='float32')))
nphi_ = ax[2].twiny()
nphi_.grid(which='major', linestyle='-', linewidth=0.5, color='darkgrey')
nphi_.plot(nphi_log, depth_log, color='blue', linewidth=1.0, linestyle='--')
nphi_.set_xlim(nphi_log.max(), nphi_log.min())
nphi_.set_ylim(ztop, zbot)
nphi_.invert_yaxis()
# nphi_.invert_xaxis()
nphi_.xaxis.label.set_color('blue')
nphi_.tick_params(axis='x', colors='blue')
nphi_.spines['top'].set_edgecolor('blue')
nphi_.set_xlabel('Neutron Porosity\nNPHI (m3/m3)', color='blue')
nphi_.spines["top"].set_position(("axes", 1.05))
nphi_.xaxis.set_ticks_position("top")
nphi_.xaxis.set_label_position("top")
nphi_.set_xticks(list(np.linspace(nphi_log.min(), nphi_log.max(), num=5, dtype='float32')))
#setting up the nphi and rhob fill
#inspired from
x1=rhob_log
x2=nphi_log
x = np.array(rhob_.get_xlim())
z = np.array(nphi_.get_xlim())
nz=((x2-np.max(z))/(np.min(z)-np.max(z)))*(np.max(x)-np.min(x))+np.min(x)
if fill == 'left':
#shows only porous zones
rhob_.fill_betweenx(depth_log, x1, nz, where=x1<=nz, interpolate=True, hatch='..', facecolor='yellow', linewidth=0)
elif fill == 'right':
#shows only non-porous zones
rhob_.fill_betweenx(depth_log, x1, nz, where=x1>=nz, interpolate=True, hatch='---', facecolor='slategray', linewidth=0)
elif fill == 'both':
#shows both porous and non-porous zones
rhob_.fill_betweenx(depth_log, x1, nz, where=x1<=nz, interpolate=True, hatch='..', facecolor='yellow', linewidth=0)
rhob_.fill_betweenx(depth_log, x1, nz, where=x1>=nz, interpolate=True, hatch='---', facecolor='slategray', linewidth=0)
plt.tight_layout(h_pad=1.2)
fig.subplots_adjust(wspace = 0.0)
plt.show()
[docs]class Zonation:
r'''
This is a zonation class that extract zone/reservoir information from the file.
This information may include top, bottom and zone/reservoir name. This information
can be accessed when an instance of `Zonation object` is called.
Attributes
----------
df : pd.DataFrame
Dataframe of well data
zones : list of dictionaries, default None
A optional attribute containing list of dictionaries that holds zone information
If None, `path` must be supplied
path : Path or str, default None
A csv file containing reservoir info.
To be able to use this, the information in the file should be entered in the following format
top, bottom, zonename
100, 200, RES_A
210, 220, RES_B
If None, `zone` must be passed
Example
-------
>>> from petrolib.plots import Zonation
>>> zones = Zonation(df, path='./well tops.csv')
>>> zones = Zonation(df, zones = [{'RES_A':[3000, 3100]}, {'RES_B':[3300, 3400]}])
>>> #get reservoir information by calling the Zonation object
>>> #ztop = top ; zbot = base; zn = zonename ; fm = formation mids to place zone name in plot
>>> ztop, zbot, zn, fm = zones()
'''
def __init__(self, df:pd.DataFrame, zones:List[Dict[str, List[float]]]=None, path:Path=None):
self._df = df
self._zones = zones
self._path = path
self._ztop = []
self._zbot = []
self._zonename = []
if self._path != None and self._zones==None:
with open(self._path, mode='r') as tops:
items = list(csv.DictReader(tops)) #reads csv content as list of dictionaries
for item in items:
self._ztop.append(float(item.get('top')))
self._zbot.append(float(item.get('bottom')))
self._zonename.append(str(item.get('zonename')))
elif self._path==None and self._zones!=None:
for zone in self._zones:
self._ztop.append(float(list(zone.values())[0][0]))
self._zbot.append(float(list(zone.values())[0][1]))
self._zonename.append(list(zone.keys())[0])
# elif path!=None and zones!=None:
# pass
self._fm_mid = []
for t, b in zip(self._ztop, self._zbot):
self._fm_mid.append((t+(b-t)/2))
def __call__(self):
'''
When the zonation object is called, this method is returned
Returns
-------
In the following sequence, Tops, Bottoms, Zone name and Formation mids
Example
-------
>>> ztop, zbot, zn, fm = zones()
'''
return self._ztop, self._zbot, self._zonename, self._fm_mid
[docs] def plotZone(self, depth:str, logs:List[str], top:float, bottom:float, title:str='Log Plot', figsize:tuple=(8, 12)):
r'''
Plots log curves with zonation track.
Parameters
----------
depth : str
Depth column
logs : list of str
A list of logs curves to include in plot
top : float
Minimum depth to zoom on
bottom : float
Maximum depth to zoom on
title : str
Plot title
figsize : tuple
Size of plot
Example
-------
>>> Zonation.plotZone('DEPTH', ['GR', 'RT', 'RHOB', 'NPHI', 'CALI'], 3300, 3600, 'Volve')
'''
# create the subplots; ncols equals the number of logs
fig, ax = plt.subplots(nrows=1, ncols=len(logs)+1, figsize=figsize, sharey=True)
fig.suptitle(f'{title}', size=15, y=1.)
cycol = cycle('bgrcmyk')
color = [choice(next(cycol)) for i in range(len(logs))]
np.random.shuffle(color)
for i in range(len(logs)):
if logs[i] == 'RT' or logs[i] == 'ILD':
# for resistivity, semilog plot
ax[i].semilogx(self._df[logs[i]], self._df[depth], color=color[i], linewidth=1.)
else:
# for non-resistivity, normal plot
ax[i].plot(self._df[logs[i]], self._df[depth], color=color[i], linewidth=1.)
if logs[i] == 'NPHI':
ax[i].invert_xaxis()
ax[i].set_title(logs[i], pad=15)
ax[i].minorticks_on()
ax[i].set_ylim(top, bottom); ax[i].invert_yaxis()
ax[i].grid(which='major', linestyle='-', linewidth=1.0, color='darkgrey')
ax[i].grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
ax[i].xaxis.label.set_color(color[i])
ax[i].tick_params(axis='x', colors=color[i])
ax[i].spines['top'].set_edgecolor(color[i])
ax[i].spines["top"].set_position(("axes", 1.01)); ax[i].xaxis.set_ticks_position("top")
ax[i].xaxis.set_label_position("top")
ax[i].hlines([t for t in self._ztop], xmin=self._df[logs[i]].min(), xmax=self._df[logs[i]].max(), colors='black', linestyles='solid', linewidth=1.)
ax[i].hlines([b for b in self._zbot], xmin=self._df[logs[i]].min(), xmax=self._df[logs[i]].max(), colors='black', linestyles='solid', linewidth=1.)
#formation subplot
ax[-1].set_ylim(top, bottom); ax[-1].invert_yaxis()
ax[-1].set_title('Zones', pad=45)
ax[-1].set_xticks([])
# ax[-1].set_yticklabels([])
ax[-1].set_xticklabels([])
ax[-1].hlines([t for t in self._ztop], xmin=0, xmax=1, colors='black', linestyles='solid', linewidth=1.)
ax[-1].hlines([b for b in self._zbot], xmin=0, xmax=1, colors='black', linestyles='solid', linewidth=1.)
formations = ax[-1]
#delineating zones
colors = [choice(next(cycol)) for i in range(len(self._zonename))]
np.random.shuffle(colors)
for i in ax:
for t,b, c in zip(self._ztop, self._zbot, colors):
i.axhspan(t, b, color=c, alpha=0.3)
#adding zone names
for label, fm_mids in zip(self._zonename, self._fm_mid):
formations.text(0.5, fm_mids, label, rotation=0,
verticalalignment='center', fontweight='bold',
fontsize='large')
#
plt.tight_layout(h_pad=1)
fig.subplots_adjust(wspace = 0.04)
plt.show()
[docs]def plotLog(df:pd.DataFrame, depth:str, logs:List[str], top:float, bottom:float, title:str='Log Plot', figsize:tuple=(8, 12)):
r'''
Plots log curves singly. To plot overlay plots use `plots.plotLogs` or `plots.plotLogFacies`
Parameters
----------
df : pd.DataFrame
Dataframe
depth : str
Depth column
logs : list of str
A list of logs curves to include in plot
top : float
Minimum depth to zoom on
bottom : float
Maximum depth to zoom on
title : str
Plot title
figsize : tuple
Size of plot
Example
-------
>>> from petrolib.plots import plotLog
>>> plotLog('DEPTH', ['GR', 'RT', 'RHOB', 'NPHI', 'CALI'], 3300, 3600, 'Volve')
>>> plotLog(df,'DEPTH', ['NPHI'], 2751, 2834.58, 'Fre-1')
'''
# create the subplots; ncols equals the number of logs
fig, ax = plt.subplots(nrows=1, ncols=len(logs), figsize=figsize, sharey=True)
fig.suptitle(f'{title}', size=15, y=1.)
cycol = cycle('bgrcmyk')
color = [choice(next(cycol)) for i in range(len(logs))]
np.random.shuffle(color)
if len(logs) > 1:
for i in range(len(logs)):
np.random.shuffle(color)
if logs[i] == 'RT' or logs[i] == 'ILD':
# for resistivity, semilog plot
ax[i].semilogx(df[logs[i]], df[depth], color=color[i], linewidth=1.)
else:
# for non-resistivity, normal plot
ax[i].plot(df[logs[i]], df[depth], color=color[i], linewidth=1.)
if logs[i] == 'NPHI' or logs[i] == 'PHIE':
ax[i].invert_xaxis()
ax[i].set_title(logs[i], pad=15)
ax[i].minorticks_on()
ax[i].set_ylim(top, bottom); ax[i].invert_yaxis()
ax[i].grid(which='major', linestyle='-', linewidth=1.0, color='darkgrey')
ax[i].grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
ax[i].xaxis.label.set_color(color[i])
ax[i].tick_params(axis='x', colors=color[i])
ax[i].spines['top'].set_edgecolor(color[i])
ax[i].spines["top"].set_position(("axes", 1.02)); ax[i].xaxis.set_ticks_position("top")
ax[i].xaxis.set_label_position("top")
elif len(logs) == 1:
for i in logs:
np.random.shuffle(color)
if i == 'RT' or i == 'ILD':
# for resistivity, semilog plot
plt.semilogx(df[i], df[depth], color=next(cycol), linewidth=1.)
else:
# for non-resistivity, normal plot
plt.plot(df[i], df[depth], color=next(cycol), linewidth=1.)
if i == 'NPHI' or i == 'PHIE':
plt.xlim(df[i].max(), df[i].min())
plt.title(i, pad=15)
plt.minorticks_on()
plt.ylim(bottom, top);
plt.grid(which='major', linestyle='-', linewidth=1.0, color='darkgrey')
plt.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
plt.tick_params(axis='x', colors=next(cycol))
plt.tight_layout(h_pad=1)
fig.subplots_adjust(wspace = 0.02)
plt.show()
[docs]def plotZoneCombo(data:pd.DataFrame, depth:str, gr:str, res:str, nphi:str, rhob:str, ztop:float, zbot:float,
ztops:List[float], zbots:List[float], zonename:List[str], limit:str, res_thres:float=10.,
palette_op:str=None, fill:str=None, title:str='Log plot', figsize:tuple=(10, 20)) -> None:
r'''
Function for plotting three combo logs alongside the zonation/reservoir track
Parameters
----------
df : pd.DataFrame
Dataframe of data
depth : str
Depth column
gr : str
Gamma ray column
res : str
Resistivity column
nphi : str
Neutron porosity column
rhob : str
Bulk density column
ztop : float
Top or minimum depth value
zbot : float
Bottom or maximum depth value
ztops : list of float
Tops (depth) of each reservoir/zones
zbots : list of float
Bottom (depth) of each reservoir/zones
zonename : list of str
Name of each zones
limit : str default None
Tells which side to fill the gamma ray track, ['left', 'right'].
lf None, it's filled in both sides delineating shale-sand region
res_thres : float
Resistivity threshold to use in the identification on hydrocarbon bearing zone
palette_op : str optional
Palette option to fill gamma ray log. If None, `fill must be provided
fill : str default None
To show either of the porous and/or non porous zones in the neutron-density crossover.
Can either be ['left', 'right', 'both']
* default None - show neither of the porous nor non-porous zones
* 'left' - shows only porous zones
* 'right' - shows only non-porous zones
* 'both' - shows both porous and non-porous zones
figsize : tuple
Size of plot
Example
-------
>>> from petrolib.plots import plotZoneCombo
>>> plotZoneCombo(well11, 'DEPTH', 'GR', 'RT', 'NPHI', 'RHOB', min(ztop), max(zbot),
>>> ztop, zbot, zn, fill='both', limit=None, figsize=(13, 30), title='ATAGA-11')
'''
#getting logs from dataframe
depth_log = data[depth]
gr_log = data[gr]
res_log = data[res]
nphi_log = data[nphi]
rhob_log = data[rhob]
# color-fill options
span = abs(gr_log.min()-gr_log.max())
if palette_op != None:
cmap=plt.get_cmap(palette_op)
else:
pass
color_index = np.arange(gr_log.min(), gr_log.max(), span/100)
# create the subplots; ncols equals the number of logs
fig, ax = plt.subplots(nrows=1, ncols=4, figsize=figsize, sharey=True)
fig.suptitle(f'{title}', size=15, y=1.)
#for GR track
ax[0].minorticks_on()
ax[0].grid(which='major', linestyle='-', linewidth=1, color='darkgrey')
ax[0].yaxis.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
ax[0].plot(gr_log, depth_log, color='black', linewidth=1.0)
ax[0].set_xlim(gr_log.min(), gr_log.max())
ax[0].set_ylim(ztop, zbot)
ax[0].invert_yaxis()
ax[0].xaxis.label.set_color('black')
ax[0].tick_params(axis='x', colors='black')
ax[0].spines['top'].set_edgecolor('black')
ax[0].set_xlabel('Gamma ray\nGR (gAPI)', color='black', labelpad=15)
ax[0].spines["top"].set_position(("axes", 1.01))
ax[0].xaxis.set_ticks_position("top")
ax[0].xaxis.set_label_position("top")
ax[0].hlines([t for t in ztops], xmin=gr_log.min(), xmax=gr_log.max(), colors='black', linestyles='solid',linewidth=1.)
ax[0].hlines([b for b in zbots], xmin=gr_log.min(), xmax=gr_log.max(), colors='black', linestyles='solid', linewidth=1.)
if palette_op == None:
assert limit == None, 'Set limit to None'
gr_base = (gr_log.max() - gr_log.min())/2
ax[0].fill_betweenx(depth_log, gr_base, gr_log, where=gr_log<=gr_base, facecolor='yellow', linewidth=0)
ax[0].fill_betweenx(depth_log, gr_log, gr_base, where=gr_log>=gr_base, facecolor='brown', linewidth=0)
elif palette_op != None:
assert limit != None, 'Set limit value. Can\'t be None'
if limit == 'left':
for index in sorted(color_index):
index_value = (index-gr_log.min())/span
palette = cmap(index_value)
ax[0].fill_betweenx(depth_log, gr_log.min(), gr_log, where=gr_log>=index, color=palette)
elif limit == 'right':
for index in sorted(color_index):
index_value = (index-gr_log.min())/span
palette = cmap(index_value)
ax[0].fill_betweenx(depth_log, gr_log.max(), gr_log, where=gr_log>=index, color=palette)
#for resitivity
ax[1].minorticks_on()
ax[1].grid(which='major', linestyle='-', linewidth=1.0, color='darkgrey')
ax[1].grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
ax[1].yaxis.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
ax[1].semilogx(res_log, depth_log, color='red', linewidth=1.0, linestyle='--')
ax[1].set_xlim(res_log.min(), res_log.max())
ax[1].set_ylim(ztop, zbot)
ax[1].invert_yaxis()
ax[1].xaxis.label.set_color('red')
ax[1].tick_params(axis='x', colors='red')
ax[1].spines['top'].set_edgecolor('red')
ax[1].set_xlabel('Resistivity\nILD (ohm.m)', labelpad=15)
ax[1].spines["top"].set_position(("axes", 1.01))
ax[1].xaxis.set_ticks_position("top")
ax[1].xaxis.set_label_position("top")
ax[1].fill_betweenx(depth_log, res_thres, res_log, where=res_log >= res_thres, interpolate=True, color='red', linewidth=0)
ax[1].hlines([t for t in ztops], xmin=res_log.min(), xmax=res_log.max(), colors='black', linestyles='solid',linewidth=1.)
ax[1].hlines([b for b in zbots], xmin=res_log.min(), xmax=res_log.max(), colors='black', linestyles='solid', linewidth=1.)
#for nphi
ax[2].minorticks_on()
ax[2].yaxis.grid(which='major', linestyle='-', linewidth=1, color='darkgrey')
ax[2].yaxis.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
ax[2].set_xticklabels([]);ax[2].set_xticks([])
rhob_ = ax[2].twiny()
rhob_.plot(rhob_log, depth_log, color='red', linewidth=1.)
rhob_.set_xlim(rhob_log.min(), rhob_log.max())
rhob_.set_ylim(ztop, zbot)
rhob_.invert_yaxis()
rhob_.xaxis.label.set_color('red')
rhob_.tick_params(axis='x', colors='red')
rhob_.spines['top'].set_edgecolor('red')
rhob_.set_xlabel('Bulk Density\nRHOB (g/cm3)', color='red')
rhob_.spines["top"].set_position(("axes", 1.01))
rhob_.xaxis.set_ticks_position("top")
rhob_.xaxis.set_label_position("top")
rhob_.set_xticks(list(np.linspace(rhob_log.min(), rhob_log.max(), num=4)))
rhob_.hlines([t for t in ztops], xmin=rhob_log.min(), xmax=rhob_log.max(), colors='black', linestyles='solid',linewidth=1.)
rhob_.hlines([b for b in zbots], xmin=rhob_log.min(), xmax=rhob_log.max(), colors='black', linestyles='solid', linewidth=1.)
nphi_ = ax[2].twiny()
nphi_.grid(which='major', linestyle='-', linewidth=0.5, color='darkgrey')
nphi_.plot(nphi_log, depth_log, color='blue', linewidth=1.0, linestyle='--')
nphi_.set_xlim(nphi_log.max(), nphi_log.min())
nphi_.set_ylim(ztop, zbot)
nphi_.invert_yaxis()
# nphi_.invert_xaxis()
nphi_.xaxis.label.set_color('blue')
nphi_.tick_params(axis='x', colors='blue')
nphi_.spines['top'].set_edgecolor('blue')
nphi_.set_xlabel('Neutron Porosity\nNPHI (m3/m3)', color='blue')
nphi_.spines["top"].set_position(("axes", 1.05))
nphi_.xaxis.set_ticks_position("top")
nphi_.xaxis.set_label_position("top")
nphi_.set_xticks(list(np.linspace(nphi_log.min(), nphi_log.max(), num=4)))
nphi_.hlines([t for t in ztops], xmin=nphi_log.min(), xmax=nphi_log.max(), colors='black', linestyles='solid',linewidth=1.)
nphi_.hlines([b for b in zbots], xmin=nphi_log.min(), xmax=nphi_log.max(), colors='black', linestyles='solid', linewidth=1.)
#setting up the nphi and rhob fill
#inspired from
x1=rhob_log
x2=nphi_log
x = np.array(rhob_.get_xlim())
z = np.array(nphi_.get_xlim())
nz=((x2-np.max(z))/(np.min(z)-np.max(z)))*(np.max(x)-np.min(x))+np.min(x)
if fill == 'left':
#shows only porous zones
rhob_.fill_betweenx(depth_log, x1, nz, where=x1<=nz, interpolate=True, hatch='..', facecolor='yellow', linewidth=0)
elif fill == 'right':
#shows only non-porous zones
rhob_.fill_betweenx(depth_log, x1, nz, where=x1>=nz, interpolate=True, hatch='---', facecolor='slategray', linewidth=0)
elif fill == 'both':
#shows both porous and non-porous zones
rhob_.fill_betweenx(depth_log, x1, nz, where=x1<=nz, interpolate=True, hatch='..', facecolor='yellow', linewidth=0)
rhob_.fill_betweenx(depth_log, x1, nz, where=x1>=nz, interpolate=True, hatch='---', facecolor='slategray', linewidth=0)
#formation subplot
ax[-1].set_ylim(ztop, zbot); ax[-1].invert_yaxis()
ax[-1].set_title('Zones', pad=45)
ax[-1].set_xticks([])
# ax[-1].set_yticklabels([])
ax[-1].set_xticklabels([])
ax[-1].hlines([t for t in ztops], xmin=0, xmax=1, colors='black', linestyles='solid',linewidth=1.)
ax[-1].hlines([b for b in zbots], xmin=0, xmax=1, colors='black', linestyles='solid', linewidth=1.)
formations = ax[-1]
#delineating zones
# np.random.seed(2)
cycol = cycle('bgrcymk')
color = [choice(next(cycol)) for i in range(len(ztops))]
np.random.shuffle(color)
for i in ax:
for t,b, c in zip(ztops, zbots, color):
i.axhspan(t, b, color=c, alpha=.3)
#adding zone names
fm_mid = []
for t, b in zip(ztops, zbots):
fm_mid.append((t+(b-t)/2))
for label, fm_mids in zip(zonename, fm_mid):
formations.text(0.5, fm_mids, label, rotation=0,
verticalalignment='center', fontweight='bold',
fontsize='large')
plt.tight_layout(h_pad=1.2)
fig.subplots_adjust(wspace = 0.0)
[docs]def plotLogFacies(df:pd.DataFrame, depth:str, logs:List[list], top:float, bottom:float, facies:str=None,
title:str='Log Plot', figsize:tuple=(8, 12)):
r'''
Plots overlayed/super-imposed log curves along with the facies.
Parameters
----------
df : pd.DataFrame
Dataframe
depth : str
Depth column
logs : list of lists/str
A list of logs curves to include in plot
top : float
Minimum depth to zoom on
bottom : float
Maximum depth to zoom on
facies : str
Facies columns. Supported facies include :
['Sandstone', 'Sandstone/Shale', 'Shale', 'Marl', 'Dolomite', 'Limestone',
'Chalk', 'Halite', 'Anhydrite', 'Tuff', 'Coal', 'Basement']
title : str
Plot title
figsize : tuple
Size of plot
Example
-------
>>> from petrolib.plots import *
>>> plotLogFacies(well11, 'DEPTH', ['GR', 'RT', ['RHOB', 'NPHI']], facies='litho', top=well11.DEPTH.min(),
bottom=well11.DEPTH.max(), figsize=(9, 12), title='15-9-F1A')
'''
df = df.copy()
# file_dir, file_name = os.path.split(__file__)
df_litho = pd.read_csv(os.path.join(os.path.dirname(__file__), 'data', 'litho_info.csv'))
# create the subplots; ncols equals the number of logs
if facies is None:
fig, ax = plt.subplots(nrows=1, ncols=len(logs), figsize=figsize, sharey=True)
else:
fig, ax = plt.subplots(nrows=1, ncols=len(logs)+1, figsize=figsize, sharey=True)
fig.suptitle(f'{title}', size=15, y=1.)
#generating random number of colors equal to total number of logs
cycol = cycle('bgyrcmk')
tot_logs = 0
for i in logs:
if isinstance(i, list):
tot_logs += len(i)
else:
tot_logs += 1
color = [choice(next(cycol)) for i in range(tot_logs)]
np.random.shuffle(color)
#filtering facies in lithofacies info dataframe that the number of identified lithology in `df`
if facies != None:
facies_label = df[facies].unique()
df_litho = df_litho[df_litho.lith.isin(facies_label)]
try:
if df[facies].dtype == 'object':
lithology = {}
for li, li_num in zip(df_litho.lith, df_litho.lith_num):
lithology[li] = li_num
df[facies] = [lithology[i] for i in df[facies]]
elif df[facies].dtype == 'int':
pass
except:
raise ValueError(f'Unidentified facies in \'{facies}\'')
#generating log tracks
if len(logs) > 1:
for i, j in enumerate(logs):
if isinstance(j, list):
assert len(j) > 1, 'Error. list of lists of curves must be greater than 1.'
np.random.shuffle(color)
for ii in range(len(j)):
if logs[i][ii] == 'RT' or logs[i][ii] == 'ILD':
# for resistivity, semilog plot
ax[i].semilogx(df[logs[i][ii]], df[depth], color=color[-i], linewidth=1.)
if ii == 0:# and logs[i][ii] != 'RT':
# for non-resistivity, normal plot
ax[i].plot(df[logs[i][ii]], df[depth], color=color[ii], linewidth=1.)
# ax[ii].set_xticklabels([]); ax[ii].set_xticks([])
ax[i].set_xticklabels([]); ax[i].set_xticks([])
if ii >= 1:# or logs[i][ii] != 'RT':
ax[i].twiny().plot(df[logs[i][ii]], df[depth], color=color[ii], linewidth=1.)
ax[i].set_xticklabels([]); ax[i].set_xticks([])
ax[i].yaxis.grid(which='major', linestyle='-', linewidth=1, color='darkgrey')
ax[i].yaxis.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
tracks = []
for _ in range(len(j)):
tracks.append(ax[i].twiny())
additive = 1.02
for _, axes in enumerate(tracks):
axes.set_xlabel(logs[i][_])
# axes.set_ylim(top, bottom)
if logs[i][_] == 'NPHI' or logs[i][_] == 'PHIE':
axes.set_xlim(df[logs[i][_]].max(), df[logs[i][_]].min())
axes.set_xticks(list(np.linspace(df[logs[i][_]].max(), df[logs[i][_]].min(), num=4)))
else:
axes.set_xlim(df[logs[i][_]].min(), df[logs[i][_]].max())
axes.set_xticks(list(np.linspace(df[logs[i][_]].min(), df[logs[i][_]].max(), num=4)))
axes.grid(which='major', linestyle='-', linewidth=1.0, color='darkgrey')
axes.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
axes.xaxis.label.set_color(color[_])
axes.tick_params(axis='x', colors=color[_])
axes.spines['top'].set_edgecolor(color[_])
axes.spines["top"].set_position(("axes", additive))
axes.xaxis.set_ticks_position("top")
axes.xaxis.set_label_position("top")
axes.set_frame_on(True)
axes.patch.set_visible(False)
axes.invert_yaxis()
additive += 0.06
else:
np.random.shuffle(color)
if logs[i] == 'RT' or logs[i] == 'ILD':
# for resistivity, semilog plot
ax[i].semilogx(df[logs[i]], df[depth], color=color[i], linewidth=1.)
else:
# for non-resistivity, normal plot
ax[i].plot(df[logs[i]], df[depth], color=color[i], linewidth=1.)
if logs[i] == 'NPHI' or logs[i] == 'PHIE':
ax[i].invert_xaxis()
ax[i].set_title(logs[i], pad=15)
ax[i].minorticks_on()
ax[i].set_ylim(top, bottom); ax[i].invert_yaxis()
ax[i].grid(which='major', linestyle='-', linewidth=1.0, color='darkgrey')
ax[i].grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
ax[i].xaxis.label.set_color(color[i])
ax[i].tick_params(axis='x', colors=color[i])
ax[i].spines['top'].set_edgecolor(color[i])
ax[i].spines["top"].set_position(("axes", 1.02)); ax[i].xaxis.set_ticks_position("top")
ax[i].xaxis.set_label_position("top")
ax[i].set_frame_on(True)
ax[i].patch.set_visible(False)
elif len(logs) == 1 and facies == None:
for idxi, i in enumerate(logs):
if not isinstance(i, list):
np.random.shuffle(color)
if i == 'RT' or i == 'ILD':
# for resistivity, semilog plot
plt.semilogx(df[i], df[depth], color=color[idxi], linewidth=1.)
else:
# for non-resistivity, normal plot
plt.plot(df[i], df[depth], color=color[idxi], linewidth=1.)
if i == 'NPHI' or i == 'PHIE':
plt.xlim(df[i].max(), df[i].min())
plt.title(i, pad=15)
plt.minorticks_on()
plt.ylim(bottom, top);
plt.grid(which='major', linestyle='-', linewidth=1.0, color='darkgrey')
plt.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
plt.gca().tick_params(axis='x', colors=color[idxi])
plt.gca().spines['top'].set_edgecolor(color[idxi])
plt.gca().spines["top"].set_position(("axes", 1.02));
plt.gca().xaxis.set_ticks_position("top")
plt.gca().xaxis.set_label_position("top")
else:
np.random.shuffle(color)
assert len(i) > 1, 'List of list of curves must be greater than one.'
for idxii, ii in enumerate(range(len(i))):
if logs[idxi][idxii] == 'RT' or logs[idxi][idxii] == 'ILD':
# for resistivity, semilog plot
ax.semilogx(df[logs[idxi][idxii]], df[depth], color=color[idxii], linewidth=1.)
# ax.set_xticklabels([]);ax.set_xticks([])
if idxii == 0:
# for non-resistivity, normal plot
ax.plot(df[logs[idxi][idxii]], df[depth], color=color[idxii], linewidth=1.)
ax.set_xticklabels([]); ax.set_xticks([])
if idxii >= 1:
ax.twiny().plot(df[logs[idxi][idxii]], df[depth], color=color[idxii], linewidth=1.)
ax.set_xticklabels([]); ax.set_xticks([])
ax.yaxis.grid(which='major', linestyle='-', linewidth=1, color='darkgrey')
ax.yaxis.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
tracks = []
np.random.shuffle(color)
for x in range(len(i)):
tracks.append(ax.twiny())
additive = 1.02
for _, axes in enumerate(tracks):
axes.set_xlabel(logs[idxi][_])
# axes.minorticks_on()
axes.set_ylim(top, bottom)
if logs[idxi][_] == 'NPHI' or logs[idxi][_] == 'PHIE':
axes.set_xlim(df[logs[idxi][_]].max(), df[logs[idxi][_]].min())
axes.set_xticks(list(np.linspace(df[logs[idxi][_]].max(), df[logs[idxi][_]].min(), num=4)))
else:
axes.set_xlim(df[logs[idxi][_]].min(), df[logs[idxi][_]].max())
axes.set_xticks(list(np.linspace(df[logs[idxi][_]].min(), df[logs[idxi][_]].max(), num=4)))
axes.grid(which='major', linestyle='-', linewidth=1.0, color='darkgrey')
axes.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
axes.xaxis.label.set_color(color[_])
axes.tick_params(axis='x', colors=color[_])
axes.spines['top'].set_edgecolor(color[_])
axes.spines["top"].set_position(("axes", additive))
axes.xaxis.set_ticks_position("top")
axes.xaxis.set_label_position("top")
axes.set_frame_on(True)
axes.patch.set_visible(False)
axes.invert_yaxis()
additive += 0.06
elif len(logs) == 1 and facies != None:
for idxi, i in enumerate(logs):
if not isinstance(i, list):
np.random.shuffle(color)
if i == 'RT' or i == 'ILD':
# for resistivity, semilog plot
ax[idxi].semilogx(df[i], df[depth], color=color[idxi], linewidth=1.)
else:
# for non-resistivity, normal plot
ax[idxi].plot(df[i], df[depth], color=color[idxi], linewidth=1.)
if i == 'NPHI' or i == 'PHIE':
ax[idxi].xlim(df[i].max(), df[i].min())
ax[idxi].set_title(i, pad=15)
ax[idxi].minorticks_on()
ax[idxi].set_ylim(bottom, top);
ax[idxi].grid(which='major', linestyle='-', linewidth=1.0, color='darkgrey')
ax[idxi].grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
ax[idxi].tick_params(axis='x', colors=color[idxi])
ax[idxi].spines['top'].set_edgecolor(color[idxi])
ax[idxi].spines["top"].set_position(("axes", 1.02)); ax[idxi].xaxis.set_ticks_position("top")
ax[idxi].xaxis.set_label_position("top")
else:
assert len(i) > 1, 'List of list of curves must be greater than one.'
for idxii, ii in enumerate(range(len(i))):
if logs[idxi][idxii] == 'RT' or logs[idxi][idxii] == 'ILD':
# for resistivity, semilog plot
ax[idxi].semilogx(df[logs[idxi][idxii]], df[depth], color=color[idxii], linewidth=1.)
# ax[idxi].set_xticklabels([]);ax[idxi].set_xticks([])
if idxii == 0:
# for non-resistivity, normal plot
ax[idxi].plot(df[logs[idxi][idxii]], df[depth], color=color[idxii], linewidth=1.)
ax[idxi].set_xticklabels([]); ax[idxi].set_xticks([])
if idxii >= 1:
ax[idxi].twiny().plot(df[logs[idxi][idxii]], df[depth], color=color[idxii], linewidth=1.)
ax[idxii].twiny().set_xticklabels([]); ax[idxii].twiny().set_xticks([])
ax[idxi].yaxis.grid(which='major', linestyle='-', linewidth=1, color='darkgrey')
ax[idxi].yaxis.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
tracks = []
for x in range(len(i)):
tracks.append(ax[idxi].twiny())
# np.random.shuffle(color)
additive = 1.02
for _, axes in enumerate(tracks):
axes.set_xlabel(logs[idxi][_])
# axes.minorticks_on()
axes.set_ylim(top, bottom)
if logs[idxi][_] == 'NPHI' or logs[idxi][_] == 'PHIE':
axes.set_xlim(df[logs[idxi][_]].max(), df[logs[idxi][_]].min())
axes.set_xticks(list(np.linspace(df[logs[idxi][_]].max(), df[logs[idxi][_]].min(), num=4)))
else:
axes.set_xlim(df[logs[idxi][_]].min(), df[logs[idxi][_]].max())
axes.set_xticks(list(np.linspace(df[logs[idxi][_]].min(), df[logs[idxi][_]].max(), num=4)))
axes.grid(which='major', linestyle='-', linewidth=1.0, color='darkgrey')
axes.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
axes.xaxis.label.set_color(color[_])
axes.tick_params(axis='x', colors=color[_])
axes.spines['top'].set_edgecolor(color[_])
axes.spines["top"].set_position(("axes", additive))
axes.xaxis.set_ticks_position("top")
axes.xaxis.set_label_position("top")
axes.set_frame_on(True)
axes.patch.set_visible(False)
axes.invert_yaxis()
additive += 0.06
#Plot lithology track
if facies != None:
ax[-1].plot(df[facies], df[depth], color='black', linewidth=0.5)
ax[-1].set_xlabel("Lithology")
ax[-1].set_xlim(0, 1)
ax[-1].xaxis.label.set_color("black")
ax[-1].tick_params(axis='x', colors="black")
ax[-1].spines["top"].set_edgecolor("black")
ax[-1].xaxis.set_ticks_position("top")
ax[-1].xaxis.set_label_position("top")
ax[-1].spines["top"].set_position(("axes", 1.02))
#adding legend of lithofacies
legend = []
for key, name, color, hatch in zip(df_litho.lith_num, df_litho.lith, df_litho.color, df_litho.hatch):
ax[-1].fill_betweenx(df[depth], 0, df[facies], where=(df[facies]==key),
facecolor=color, hatch=hatch)
legend.append(Patch(facecolor=color, hatch=hatch, label=name))
ax[-1].set_xticks([0, 1])
ax[-1].legend(handles=legend, bbox_to_anchor=(1.05, 1),
loc='upper left', borderaxespad=0.5)
else:
pass
plt.tight_layout(h_pad=1)
fig.subplots_adjust(wspace = 0.04)
plt.show()
[docs]def plotLogs(df:pd.DataFrame, depth:str, logs:List[List[str]], top:float, bottom:float,
title:str='Log Plot', figsize:tuple=(8, 12)):
r'''
Plots overlayed/super-imposed log curves.
Parameters
----------
df : pd.DataFrame
Dataframe
depth : str
Depth column
logs : list of lists/str
A list of logs curves to include in plot
top : float
Minimum depth to zoom on
bottom : float
Maximum depth to zoom on
title : str
Plot title
figsize : tuple
Size of plot
Example
-------
>>> plotLogs(well11, 'DEPTH', ['GR', 'RT', ['RHOB', 'NPHI']], top=well11.DEPTH.min(),
>>> bottom=well11.DEPTH.max(), figsize=(9, 12), title='15-9-F1A')
'''
df = df.copy()
# create the subplots; ncols equals the number of logs
fig, ax = plt.subplots(nrows=1, ncols=len(logs), figsize=figsize, sharey=True)
fig.suptitle(f'{title}', size=15, y=1.)
#generating random number of colors equal to total number of logs
cycol = cycle('bgyrcmk')
tot_logs = 0
for i in logs:
if isinstance(i, list):
tot_logs += len(i)
else:
tot_logs += 1
color = [choice(next(cycol)) for i in range(tot_logs)]
np.random.shuffle(color)
#generating log tracks
if len(logs) > 1:
for i, j in enumerate(logs):
if isinstance(j, list):
assert len(j) > 1, 'Error. list of lists of curves must be greater than 1.'
np.random.shuffle(color)
for ii in range(len(j)):
if logs[i][ii] == 'RT' or logs[i][ii] == 'ILD':
# for resistivity, semilog plot
ax[i].semilogx(df[logs[i][ii]], df[depth], color=color[-i], linewidth=1.)
if ii == 0:# and logs[i][ii] != 'RT':
# for non-resistivity, normal plot
ax[i].plot(df[logs[i][ii]], df[depth], color=color[ii], linewidth=1.)
# ax[ii].set_xticklabels([]); ax[ii].set_xticks([])
ax[i].set_xticklabels([]); ax[i].set_xticks([])
if ii >= 1:# or logs[i][ii] != 'RT':
ax[i].twiny().plot(df[logs[i][ii]], df[depth], color=color[ii], linewidth=1.)
ax[i].set_xticklabels([]); ax[i].set_xticks([])
ax[i].yaxis.grid(which='major', linestyle='-', linewidth=1, color='darkgrey')
ax[i].yaxis.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
tracks = []
for _ in range(len(j)):
tracks.append(ax[i].twiny())
additive = 1.02
for _, axes in enumerate(tracks):
axes.set_xlabel(logs[i][_])
# axes.set_ylim(top, bottom)
if logs[i][_] == 'NPHI' or logs[i][_] == 'PHIE':
axes.set_xlim(df[logs[i][_]].max(), df[logs[i][_]].min())
axes.set_xticks(list(np.linspace(df[logs[i][_]].min(), df[logs[i][_]].max(), num=4)))
else:
axes.set_xlim(df[logs[i][_]].min(), df[logs[i][_]].max())
axes.set_xticks(list(np.linspace(df[logs[i][_]].min(), df[logs[i][_]].max(), num=4)))
axes.grid(which='major', linestyle='-', linewidth=1.0, color='darkgrey')
axes.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
axes.xaxis.label.set_color(color[_])
axes.tick_params(axis='x', colors=color[_])
axes.spines['top'].set_edgecolor(color[_])
axes.spines["top"].set_position(("axes", additive))
axes.xaxis.set_ticks_position("top")
axes.xaxis.set_label_position("top")
axes.set_frame_on(True)
axes.patch.set_visible(False)
axes.invert_yaxis()
additive += 0.06
else:
np.random.shuffle(color)
if logs[i] == 'RT' or logs[i] == 'ILD':
# for resistivity, semilog plot
ax[i].semilogx(df[logs[i]], df[depth], color=color[i], linewidth=1.)
else:
# for non-resistivity, normal plot
ax[i].plot(df[logs[i]], df[depth], color=color[i], linewidth=1.)
if logs[i] == 'NPHI' or logs[i] == 'PHIE':
ax[i].invert_xaxis()
ax[i].set_title(logs[i], pad=15)
ax[i].minorticks_on()
ax[i].set_ylim(top, bottom); ax[i].invert_yaxis()
ax[i].grid(which='major', linestyle='-', linewidth=1.0, color='darkgrey')
ax[i].grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
ax[i].xaxis.label.set_color(color[i])
ax[i].tick_params(axis='x', colors=color[i])
ax[i].spines['top'].set_edgecolor(color[i])
ax[i].spines["top"].set_position(("axes", 1.02)); ax[i].xaxis.set_ticks_position("top")
ax[i].xaxis.set_label_position("top")
ax[i].set_frame_on(True)
ax[i].patch.set_visible(False)
elif len(logs) == 1:
for idxi, i in enumerate(logs):
if not isinstance(i, list):
np.random.shuffle(color)
if i == 'RT' or i == 'ILD':
# for resistivity, semilog plot
plt.semilogx(df[i], df[depth], color=color[idxi], linewidth=1.)
else:
# for non-resistivity, normal plot
plt.plot(df[i], df[depth], color=color[idxi], linewidth=1.)
if i == 'NPHI' or i == 'PHIE':
plt.xlim(df[i].max(), df[i].min())
plt.title(i, pad=15)
plt.minorticks_on()
plt.ylim(bottom, top);
plt.grid(which='major', linestyle='-', linewidth=1.0, color='darkgrey')
plt.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
plt.gca().tick_params(axis='x', colors=color[idxi])
plt.gca().spines['top'].set_edgecolor(color[idxi])
plt.gca().spines["top"].set_position(("axes", 1.02));
plt.gca().xaxis.set_ticks_position("top")
plt.gca().xaxis.set_label_position("top")
plt.gca().xaxis.label.set_color(color[idxi])
else:
np.random.shuffle(color)
assert len(i) > 1, 'List of list of curves must be greater than one.'
for idxii, ii in enumerate(range(len(i))):
if logs[idxi][idxii] == 'RT' or logs[idxi][idxii] == 'ILD':
# for resistivity, semilog plot
ax.semilogx(df[logs[idxi][idxii]], df[depth], color=color[idxii], linewidth=1.)
# ax.set_xticklabels([]);ax.set_xticks([])
if idxii == 0:
# for non-resistivity, normal plot
ax.plot(df[logs[idxi][idxii]], df[depth], color=color[idxii], linewidth=1.)
ax.set_xticklabels([]); ax.set_xticks([])
if idxii >= 1:
ax.twiny().plot(df[logs[idxi][idxii]], df[depth], color=color[idxii], linewidth=1.)
ax.set_xticklabels([]); ax.set_xticks([])
ax.yaxis.grid(which='major', linestyle='-', linewidth=1, color='darkgrey')
ax.yaxis.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
tracks = []
np.random.shuffle(color)
for x in range(len(i)):
tracks.append(ax.twiny())
additive = 1.02
for _, axes in enumerate(tracks):
axes.set_xlabel(logs[idxi][_])
# axes.minorticks_on()
axes.set_ylim(top, bottom)
if logs[idxi][_] == 'NPHI' or logs[idxi][_] == 'PHIE':
axes.set_xlim(df[logs[idxi][_]].max(), df[logs[idxi][_]].min())
axes.set_xticks(list(np.linspace(df[logs[idxi][_]].max(), df[logs[idxi][_]].min(), num=4)))
else:
axes.set_xlim(df[logs[idxi][_]].min(), df[logs[idxi][_]].max())
axes.set_xticks(list(np.linspace(df[logs[idxi][_]].min(), df[logs[idxi][_]].max(), num=4)))
axes.grid(which='major', linestyle='-', linewidth=1.0, color='darkgrey')
axes.grid(which='minor', linestyle='-', linewidth=0.5, color='lightgrey')
axes.xaxis.label.set_color(color[_])
axes.tick_params(axis='x', colors=color[_])
axes.spines['top'].set_edgecolor(color[_])
axes.spines["top"].set_position(("axes", additive))
axes.xaxis.set_ticks_position("top")
axes.xaxis.set_label_position("top")
axes.set_frame_on(True)
axes.patch.set_visible(False)
axes.invert_yaxis()
additive += 0.06
plt.tight_layout(h_pad=1)
fig.subplots_adjust(wspace = 0.04)
plt.show()