<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 x lines of Python
Reading and writing LAS files
This notebook goes with the Agile blog post of 23 October.
Set up a conda environment with
Step1:  1. Load the LAS file with lasio
Step2:  That's it! But the object itself doesn't tell us much — it's really just a container
Step3:  2. Look at the WELL section of the header
Step4:  You can go in and find the KB if you know what to look for
Step5:  3. Look at the curve data
The curves are all present one big NumPy array
Step6:  Or we can go after a single curve object
Step7:  And there's a shortcut to its data
Step8:  ...so it's easy to make a plot against depth
Step9:  4. Inspect the curves as a pandas dataframe
Step10:  5. Load the LAS file with welly
Step11:  welly Wells know how to display some basics
Step12:  And the Well object also has lasio's access to a pandas DataFrame
Step13:  6. Look at welly's Curve object
Like the Well, a Curve object can report a bit about itself
Step14:  One important thing about Curves is that each one knows its own depths — they are stored as a property called basis. (It's not actually stored, but computed on demand from the start depth, the sample interval (which must be constant for the whole curve) and the number of samples in the object.)
Step15:  7. Plot part of a curve
We'll grab the interval from 300 m to 1000 m and plot it.
Step16:  8. Smooth a curve
Curve objects are, fundamentally, NumPy arrays. But they have some extra tricks. We've already seen Curve.plot(). 
Using the Curve.smooth() method, we can easily smooth a curve, eg by 15 m (passing samples=True would smooth by 15 samples)
Step17:  9. Export a set of curves as a matrix
You can get at all the data through the lasio l.data object
Step18:  But we might want to do some other things, such as specify which curves you want (optionally using aliases like GR1, GRC, NGC, etc for GR), resample the data, or specify a start and stop depth — welly can do all this stuff. This method is also wrapped by Project.data_as_matrix() which is nice because it ensures that all the wells are exported at the same sample interval.
Here are the curves in this well
Step19:  So CALI had 12,718 points in it... since we downsampled to 0.5 m and removed the top and tail, we should have substantially fewer points
Step20:  10+. BONUS
Step23:  Let's look at some of the header
Step25:  Make sure that works!
<ASSISTANT_TASK:>
Python Code:
import welly
ls ../data/*.LAS
Explanation: x lines of Python
Reading and writing LAS files
This notebook goes with the Agile blog post of 23 October.
Set up a conda environment with:
conda create -n welly python=3.6 matplotlib=2.0 scipy pandas
You'll need welly in your environment:
conda install tqdm  # Should happen automatically but doesn't
pip install welly
This will also install the latest versions of striplog and lasio.
End of explanation
import lasio
l = lasio.read('../data/P-129.LAS')  # Line 1.
Explanation: 1. Load the LAS file with lasio
End of explanation
l
Explanation: That's it! But the object itself doesn't tell us much — it's really just a container:
End of explanation
l.header['Well']  # Line 2.
Explanation: 2. Look at the WELL section of the header
End of explanation
l.header['Parameter']['EKB']
Explanation: You can go in and find the KB if you know what to look for:
End of explanation
l.data
Explanation: 3. Look at the curve data
The curves are all present one big NumPy array:
End of explanation
l.curves.GR  # Line 3.
Explanation: Or we can go after a single curve object:
End of explanation
l['GR']  # Line 4.
Explanation: And there's a shortcut to its data:
End of explanation
import matplotlib.pyplot as plt
plt.figure(figsize=(15,3))
plt.plot(l['DEPT'], l['GR'])
plt.show()
Explanation: ...so it's easy to make a plot against depth:
End of explanation
l.df().head()  # Line 5.
Explanation: 4. Inspect the curves as a pandas dataframe
End of explanation
from welly import Well
w = Well.from_las('../data/P-129.LAS')  # Line 6.
Explanation: 5. Load the LAS file with welly
End of explanation
w
Explanation: welly Wells know how to display some basics:
End of explanation
w.df().head()
Explanation: And the Well object also has lasio's access to a pandas DataFrame:
End of explanation
gr = w.data['GR']  # Line 7.
gr
Explanation: 6. Look at welly's Curve object
Like the Well, a Curve object can report a bit about itself:
End of explanation
gr.basis
Explanation: One important thing about Curves is that each one knows its own depths — they are stored as a property called basis. (It's not actually stored, but computed on demand from the start depth, the sample interval (which must be constant for the whole curve) and the number of samples in the object.)
End of explanation
gr.to_basis(start=300, stop=1000).plot()  # Line 8.
Explanation: 7. Plot part of a curve
We'll grab the interval from 300 m to 1000 m and plot it.
End of explanation
sm = gr.smooth(window_length=15, samples=False)  # Line 9.
sm.plot()
Explanation: 8. Smooth a curve
Curve objects are, fundamentally, NumPy arrays. But they have some extra tricks. We've already seen Curve.plot(). 
Using the Curve.smooth() method, we can easily smooth a curve, eg by 15 m (passing samples=True would smooth by 15 samples):
End of explanation
print("Data shape: {}".format(w.las.data.shape))
w.las.data
Explanation: 9. Export a set of curves as a matrix
You can get at all the data through the lasio l.data object:
End of explanation
w.data.keys()
keys=['CALI', 'DT', 'DTS', 'RHOB', 'SP']
w.plot(tracks=['TVD']+keys)
X, basis = w.data_as_matrix(keys=keys, start=275, stop=1850, step=0.5, return_basis=True)
w.data['CALI'].shape
Explanation: But we might want to do some other things, such as specify which curves you want (optionally using aliases like GR1, GRC, NGC, etc for GR), resample the data, or specify a start and stop depth — welly can do all this stuff. This method is also wrapped by Project.data_as_matrix() which is nice because it ensures that all the wells are exported at the same sample interval.
Here are the curves in this well:
End of explanation
X.shape
plt.figure(figsize=(15,3))
plt.plot(X.T[0])
plt.show()
Explanation: So CALI had 12,718 points in it... since we downsampled to 0.5 m and removed the top and tail, we should have substantially fewer points:
End of explanation
w.location
Explanation: 10+. BONUS: fix the lat, lon
OK, we're definitely going to go over our budget on this one.
Did you notice that the location of the well did not get loaded properly?
End of explanation
import re
def transform_ll(text):
    
    Parses malformed lat and lon so they load properly.
    
    def callback(match):
        d = match.group(1).strip()
        m = match.group(2).strip()
        s = match.group(3).strip()
        c = match.group(4).strip()
        if c.lower() in ('w', 's') and d[0] != '-':
            d = '-' + d
        return ' '.join([d, m, s])
    pattern = re.compile(r.+?([-0-9]+?).? ?([0-9]+?).? ?([\.0-9]+?).? +?([NESW]), re.I)
    text = pattern.sub(callback, text)
    return welly.utils.dms2dd([float(i) for i in text.split()])
Explanation: Let's look at some of the header:
# LAS format log file from PETREL
# Project units are specified as depth units
#==================================================================
~Version information
VERS.   2.0:
WRAP.   YES:
#==================================================================
~WELL INFORMATION
#MNEM.UNIT      DATA             DESCRIPTION
#---- ------ --------------   -----------------------------
STRT .M      1.0668          :START DEPTH     
STOP .M      1939.13760      :STOP DEPTH     
STEP .M       0.15240        :STEP        
NULL .          -999.25      :NULL VALUE
COMP .        Elmworth Energy Corporation              :COMPANY
WELL .        Kennetcook #2                            :WELL
FLD  .        Windsor Block                            :FIELD
LOC  .        Lat = 45* 12' 34.237" N                  :LOCATION
PROV .        Nova Scotia                              :PROVINCE
  UWI.        Long = 63* 45'24.460  W                  :UNIQUE WELL ID
LIC  .        P-129                                    :LICENSE NUMBER
CTRY .        CA                                       :COUNTRY (WWW code)
 DATE.        10-Oct-2007                              :LOG DATE {DD-MMM-YYYY}
SRVC .        Schlumberger                             :SERVICE COMPANY
LATI .DEG                                              :LATITUDE
LONG .DEG                                              :LONGITUDE
GDAT .                                                 :GeoDetic Datum
SECT .        45.20 Deg N                              :Section
RANG .        PD 176                                   :Range
TOWN .        63.75 Deg W                              :Township
Look at LOC and UWI. There are two problems:
These items are in the wrong place. (Notice LATI and LONG are empty.)
The items are malformed, with lots of extraneous characters.
We can fix this in two steps:
Remap the header items to fix the first problem.
Parse the items to fix the second one.
We'll define these in reverse because the remapping uses the transforming function.
End of explanation
print(transform_ll(Lat = 45* 12' 34.237" N))
remap = {
    'LATI': 'LOC',  # Use LOC for the parameter LATI.
    'LONG': 'UWI',  # Use UWI for the parameter LONG.
    'LOC':  None,   # Use nothing for the parameter SECT.
    'SECT': None,   # Use nothing for the parameter SECT.
    'RANG': None,   # Use nothing for the parameter RANG.
    'TOWN': None,   # Use nothing for the parameter TOWN.
}
funcs = {
    'LATI': transform_ll,  # Pass LATI through this function before loading.
    'LONG': transform_ll,  # Pass LONG through it too.
    'UWI': lambda x: "No UWI, fix this!"
}
w = Well.from_las('../data/P-129.LAS', remap=remap, funcs=funcs)
w.location.latitude, w.location.longitude
w.uwi
Explanation: Make sure that works!
End of explanation
<END_TASK>