All about curves

Some preliminaries...


In [1]:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

import welly
welly.__version__


Out[1]:
'0.4.2'

Load a well from LAS

Use the from_las() method to load a well by passing a filename as a str.

This is really just a wrapper for lasio but instantiates a Header, Curves, etc.


In [2]:
from welly import Well

In [3]:
w = Well.from_las('P-129_out.LAS')

In [4]:
w.data['GR']


Out[4]:
GR [gAPI]
1.0668 : 1939.1376 : 0.1524
run1
null-999.25
service_companySchlumberger
date10-Oct-2007
code
descriptionGamma-Ray
Stats
samples (NaNs)12718 (0)
min mean max3.89 78.986 267.94
DepthValue
1.066846.6987
1.219246.6987
1.371646.6987
1938.832892.2462
1938.985292.2462
1939.137692.2462

In [5]:
w.plot()


Curves

Just a list of objects.


In [6]:
w.data  # Just a dict of data objects.


Out[6]:
{'CALI': Curve([2.44381547, 2.44381547, 2.44381547, ..., 2.42026806, 2.42026806,
        2.42026806]),
 'HCAL': Curve([4.39128494, 4.39128494, 4.39128494, ...,        nan,        nan,
               nan]),
 'PEF': Curve([3.58640003, 3.58640003, 3.58640003, ..., 2.23697996, 2.23697996,
        2.23697996]),
 'DT': Curve([nan, nan, nan, ..., nan, nan, nan]),
 'DTS': Curve([nan, nan, nan, ..., nan, nan, nan]),
 'DPHI_SAN': Curve([0.15748   , 0.15748   , 0.15748   , ..., 0.54641998, 0.54641998,
        0.54641998]),
 'DPHI_LIM': Curve([1.98440000e-01, 1.98440000e-01, 1.98440000e-01, ...,
        5.85941528e+02, 5.85941528e+02, 5.85941528e+02]),
 'DPHI_DOL': Curve([2.5909999e-01, 2.5909999e-01, 2.5909999e-01, ..., 5.4167572e+02,
        5.4167572e+02, 5.4167572e+02]),
 'NPHI_SAN': Curve([0.46509999, 0.46509999, 0.46509999, ..., 0.12834001, 0.12834001,
        0.12834001]),
 'NPHI_LIM': Curve([0.33647001, 0.33647001, 0.33647001, ..., 0.08417   , 0.08417   ,
        0.08417   ]),
 'NPHI_DOL': Curve([0.30186, 0.30186, 0.30186, ..., 0.05256, 0.05256, 0.05256]),
 'RLA5': Curve([2.55800001e-02, 2.55800001e-02, 2.55800001e-02, ...,
        7.82533508e+02, 7.82533508e+02, 7.82533508e+02]),
 'RLA3': Curve([2.67500002e-02, 2.67500002e-02, 2.67500002e-02, ...,
        7.13433655e+02, 7.13433655e+02, 7.13433655e+02]),
 'RLA4': Curve([2.56200004e-02, 2.56200004e-02, 2.56200004e-02, ...,
        7.52805481e+02, 7.52805481e+02, 7.52805481e+02]),
 'RLA1': Curve([3.20999995e-02, 3.20999995e-02, 3.20999995e-02, ...,
        2.74026489e+02, 2.74026489e+02, 2.74026489e+02]),
 'RLA2': Curve([2.79399995e-02, 2.79399995e-02, 2.79399995e-02, ...,
        6.63104065e+02, 6.63104065e+02, 6.63104065e+02]),
 'RXOZ': Curve([0.05761   , 0.05761   , 0.05761   , ..., 7.09686995, 7.03910017,
        7.00194979]),
 'RXO_HRLT': Curve([0.02558, 0.02558, 0.02558, ...,     nan,     nan,     nan]),
 'RT_HRLT': Curve([0.02558   , 0.02558   , 0.02558   , ..., 7.38065004, 7.32116985,
        7.1535902 ]),
 'RM_HRLT': Curve([0.05501, 0.05501, 0.05501, ..., 0.02729, 0.02729, 0.02729]),
 'DRHO': Curve([0.19423294, 0.19423294, 0.19423294, ..., 0.06139515, 0.06139515,
        0.06139515]),
 'RHOB': Curve([2.39014983, 2.39014983, 2.39014983, ...,        nan,        nan,
               nan]),
 'GR': Curve([46.69865036, 46.69865036, 46.69865036, ..., 92.24622345,
        92.24622345, 92.24622345]),
 'SP': Curve([120.125 , 120.125 , 120.125 , ...,  73.    ,  73.9375,  74.25  ])}

Instantiating a new curve


In [7]:
from welly import Curve

In [8]:
p = {'mnemonic': 'FOO', 'run':0, }
data = [20, 30, 40, 20, 10, 0, 10]
c = Curve(data, basis=[2,3,4,5,6,7,8], params=p)

In [9]:
c.plot()


Curve info

In Jupyter Notebook, the __repr__() is a little table summarizing the curve data...


In [10]:
gr = w.data['GR']
gr


Out[10]:
GR [gAPI]
1.0668 : 1939.1376 : 0.1524
run1
null-999.25
service_companySchlumberger
date10-Oct-2007
code
descriptionGamma-Ray
Stats
samples (NaNs)12718 (0)
min mean max3.89 78.986 267.94
DepthValue
1.066846.6987
1.219246.6987
1.371646.6987
1938.832892.2462
1938.985292.2462
1939.137692.2462

In [11]:
gr.read_at(168.7068)


Out[11]:
72.29363250700216

In [14]:
gr.basis


Out[14]:
array([1.0668000e+00, 1.2192000e+00, 1.3716000e+00, ..., 1.9388328e+03,
       1.9389852e+03, 1.9391376e+03])

In [15]:
gr[1000:1010]


Out[15]:
GR [gAPI]
153.4668 : 154.8384 : 0.1524
run1
null-999.25
service_companySchlumberger
date10-Oct-2007
code
Stats
samples (NaNs)10 (0)
min mean max36.19 47.949 53.62
DepthValue
153.466849.6221
153.619252.2858
153.771653.6213
154.533642.8825
154.686038.5150
154.838436.1877

Curve objects are just ndarrays, so we get lots of things for free...


In [12]:
gr.describe()  # Equivalent to get_stats()


Out[12]:
{'samples': 12718,
 'nulls': 0,
 'mean': 78.9863535887685,
 'min': 3.89406991,
 'max': 267.94042969}

In [18]:
gr.get_stats()


Out[18]:
{'samples': 12718,
 'nulls': 0,
 'mean': 78.9863535887685,
 'min': 3.89406991,
 'max': 267.94042969}

In [13]:
m = np.mean(gr)

In [15]:
m  # Not really sure why this is a Curve


Out[15]:
Curve(78.98635359)

In [16]:
gr.mnemonic


Out[16]:
'GR'

In [17]:
w.data['GR_DESP'] = gr.despike(window_length=50, z=2)
w.plot(tracks = [['GR', 'GR_DESP']])


Slicing

It's usually better to get a subset of the data with to_basis() (see below, Getting a segment of the data). But you can slice with indices too:


In [18]:
gr.start, gr.stop


Out[18]:
(1.0668, 1939.1376000000012)

In [19]:
g = gr[1000:1020]

In [20]:
g.basis


Out[20]:
array([153.4668, 153.6192, 153.7716, 153.924 , 154.0764, 154.2288,
       154.3812, 154.5336, 154.686 , 154.8384, 154.9908, 155.1432,
       155.2956, 155.448 , 155.6004, 155.7528, 155.9052, 156.0576,
       156.21  , 156.3624])

Notice that the basis is updated to match the data retrieved by the slice.

Plotting and reading


In [21]:
gr.plot(lw=0.5)


There's also an experimental 'imshow'-style 2D plot.


In [22]:
gr.plot_2d(cmap='viridis_r')



In [23]:
(200-gr).plot_2d(cmap='viridis', curve=True, lw=0.3, edgecolor='k')
plt.xlim(0,200)


Out[23]:
(0, 200)

Interpolated values

You can get values from anywhere on the log:


In [24]:
gr.step


Out[24]:
0.1524000000000001

In [25]:
gr.read_at(1001)


Out[25]:
71.05545630648585

In [26]:
gr.read_at([1001, 1003, 1004])


Out[26]:
array([71.05545631, 43.21758001, 59.86768433])

Despike

You can despike with a window length for the trend and a Z-score to clip at — the curve is compared to the median in the window using the standard deviation from the entire curve. Here's the difference:


In [27]:
w.data['DESP'] = gr.despike(z=1)
w.data['DIFF'] = gr - w.data['DESP']
w.plot(tracks=['GR', 'DESP', 'DIFF'])


Changing basis

You can easily upscale a bit by changing the step:


In [28]:
gr.to_basis(step=5).plot()


Or take out a segment of the log:


In [45]:
newb = gr.to_basis(start=1207, stop=1215)
newb.plot()



In [47]:
silly = newb.to_basis(start=1205, step=2)
silly.plot(marker='o')



In [48]:
dt = w.data['DT']
dt.to_basis_like(newb.basis).plot()


Getting a segment of the data


In [49]:
segment = gr.to_basis(start=600, stop=680)

In [50]:
segment.basis[-1]


Out[50]:
679.8576

In [64]:
fig, axs = plt.subplots(ncols=2)

segment.plot(ax=axs[0], c='r')
segment.block(cutoffs=120, values=(0, 1)).plot(ax=axs[1])


Out[64]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fb031b27eb8>

In [67]:
fig, ax = plt.subplots()

segment.plot(ax=ax)
segment.block(values=(80, 120)).plot(ax=ax)


Out[67]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fb0343f31d0>

You can use a cutoff of, say, 120 API, then reassign the output values to whatever you like:


In [69]:
segment.block(cutoffs=120, values=(2.718, 3.142)).plot()


You can send a function in to determine replacement values from the original log. E.g., to replace the values with the block means:


In [77]:
fig, ax = plt.subplots()

segment.plot(ax=ax)
segment.block(cutoffs=120, function=np.mean).plot(ax=ax)
plt.axvline(120, color='c', lw=0.75)


Out[77]:
<matplotlib.lines.Line2D at 0x7fb03386df28>

In [ ]: