Home Artificial Intelligence Plotly and Python: Creating Interactive Heatmaps for Petrophysical & Geological Data Quick Recap on Using Pykrige Using Plotly to Make Our Chart Interactive Summary Dataset Used

Plotly and Python: Creating Interactive Heatmaps for Petrophysical & Geological Data Quick Recap on Using Pykrige Using Plotly to Make Our Chart Interactive Summary Dataset Used

0
Plotly and Python: Creating Interactive Heatmaps for Petrophysical & Geological Data
Quick Recap on Using Pykrige
Using Plotly to Make Our Chart Interactive
Summary
Dataset Used

Visualising Geospatial Variations in Well Log Measurements Inside the Subsurface

Towards Data Science
Plotly heatmap used to explore geospatial variation in well log measurements across the Norwegian Continental Shelf. Image by the writer.

Interpreting the subsurface requires understanding how geological and petrophysical data varies across a region. This often involves coping with well log measurements and interpreted properties scattered across the world, which results in the challenge of estimating the values between these measurements.

A method that we will estimate the values (or fill within the gaps) is through the use of a geostatistical method called kriging. This method estimates and extrapolates data between observed measurements and predicts values at unmeasured locations.

In my previous article, we focused on using pykrige and matplotlib to map and visualise geological variation across the Norwegian continental shelf. This text will take that visualisation further and make those plots interactive.

Before we use Plotly, we are going to quickly recap the code utilized in the previous article so that you simply are on top of things with the method.

Step one is to import the libraries that we require. On this case, we want pandas for loading our csv data, pykrige to perform the interpolation between the information points, and numpy to perform a number of mathematical operations.

import pandas as pd
from pykrige import OrdinaryKriging
import numpy as np

df = pd.read_csv('Data/Xeek Force 2020/Xeek_2020_Balder_DTC_AVG.csv')

Once the information has been loaded, we will perform the kriging process by calling upon pykrige’s OrdinaryKriging method.

Inside this call, we pass in our x and y data, representing our data’s latitude and longitude. We also must pass within the variable we wish to extrapolate. On this case, we’re using the common Acoustic Compressional Slowness (DTC) value for the Balder Formation.

Once the model has been generated, we will apply it to custom latitude and longitude ranges that cover the locations of the wells.

OK = OrdinaryKriging(x=df['LON'], 
y=df['LAT'],
z=df['DTC_MEAN'],
variogram_model='exponential',
verbose=True, enable_plotting=True,
coordinates_type='geographic')

grid_lat = np.arange(57.5, 62, 0.01, dtype='float64')
grid_long = np.arange(1.5, 4.5, 0.01,dtype='float64')
zstar, ss = OK.execute('grid', grid_long, grid_lat)
zstar

We then take our two positional arrays, grid_lat and grid_long and our gridded data and pass them right into a matplotlib imshow plot. This can generate a plot just like the one below.

Matplotlib imshow chart of information that has been run through Abnormal Kriging using pykrige. Image by the writer.

Though the figure we returned tells a story about trends in our data, it’s difficult to discover specific wells and any of the values between the measurement points.

One solution to immediately change this might be to make use of the Plotly library. Plotly is a fantastic library for creating highly interactive charts which are easy to place together.

Plotly comes with two important ways by which to construct plots: Plotly Express and Plotly Graph Objects.

Plotly Express provides a high-level interface for Plotly, and utilises easy syntax for creating powerful interactive charts. Nevertheless, customising certain points of the plot can take loads of work and could be difficult to do. That is where the Graph Objects a part of the library comes into play. It provides a low-level interface which provides complete control over your figures; nevertheless, it does mean that putting a figure together is barely more complex.

For this instance, we might be using Graph Objects, which could be imported as follows:

import plotly.graph_objects as go

Next, we will define our x and y arrays using numpy’s linspace function.

This can create two arrays the identical size as the information grid we created earlier.

We will even create two lists for longitude and latitude. These values extend beyond the information’s longitude and latitude values and permit us to have padding across the fringe of our data points.

longitude = [1.5, 4.5]
latitude = [57.5, 62]

x = np.linspace(longitude[0], longitude[1], zstar.shape[1])
y = np.linspace(latitude[0], latitude[1], zstar.shape[0])

When using matplotlib, we could display one of these data using imshow.

Though Plotly also has an imshow plot, we will not be (so far as I’m aware on the time of writing) in a position to control the extent of the graph. This implies we will’t specify the values for the starting points of the axes.

Subsequently, to display our data grid, we will switch to using Plotly’s heatmap.

The heatmap colors each data cell inside our grid based on its value. You will discover out more about heatmaps in my article on Seaborn.

We will use the next code to create our heatmap with Plotly Graph Objects.

fig = go.Figure()

fig.add_trace(go.Heatmap(z=zstar, x=x, y=y))

fig.update_xaxes(range=(longitude[0], longitude[1]))
fig.update_yaxes(range=(latitude[0], latitude[1]))

fig.update_layout(autosize=False, width=800, height=800)

fig.show()

First, we create a figure object after which add a trace to it. This trace incorporates our x and y location data, in addition to our grid (zstar)created by kriging.

We will even set the scale of the figure to 800 x 800, which is able to give us a big enough plot to work with inside Jupyter notebooks.

After running the above code, we get the heatmap with all our data values and the axes displayed throughout the correct range.

Plotly Graph Objects heatmap showing variations inside our Acoustic Compressional Slowness measurement across the Norwegian Continental Shelf. Image by the writer.

The beauty of this plot is that we will hover over it and consider the values at any point. Moreover, Plotly allows us to zoom in on sections for a more in-depth look.

Though the above plot is great, we’re lacking additional information which might help the reader, resembling our well locations and in addition labels for our axes.

So as to add our well locations, we want so as to add a second trace. This time using go.scatter() and passing within the latitude and longitude values from our dataframe. We can even control how these points appear by adding a dictionary for our markers. In this instance, we are going to set them to black.

fig = go.Figure()

fig.add_trace(go.Heatmap(z=zstar, x=x, y=y))

# Add Well Locations
fig.add_trace(go.Scatter(x=df['LON'], y=df['LAT'],
mode='markers', marker=dict(color='black')))

fig.update_xaxes(range=(longitude[0], longitude[1]))
fig.update_yaxes(range=(latitude[0], latitude[1]))

fig.update_layout(autosize=False, width=800, height=800)

fig.show()

Plotly Graph Objects heatmap showing measurement locations (wells) and variations inside our Acoustic Compressional Slowness measurement across the Norwegian Continental Shelf. Image by the writer.

Now, we will see where our wells are situated; nevertheless, if we hover over the markers, all we get back is the latitude and longitude values. This is beneficial to a certain extent; nevertheless, it will be nice to know what well the marker represents and what value of DTC was measured for that well.

To resolve that, we will create our hover text directly throughout the dataframe as a brand new column. This is beneficial if we wish to make use of it later for other plots.

fig = go.Figure()

fig.add_trace(go.Heatmap(z=zstar, x=x, y=y,
colorbar=dict(title='DTC (us/ft)',
title_font=dict(size=18))))

df['hover_text'] = df.apply(lambda row: f"""{row['WELL']}

Latitude: {row['LAT']}

Longitude: {row['LON']}

Log Value: {round(row['DTC_MEAN'], 2)}""",
axis=1)

fig.add_trace(go.Scatter(x=df['LON'], y=df['LAT'],
mode='markers', marker=dict(color='black'),
name='Wells', text=df['hover_text'], hoverinfo='text', showlegend=True))

fig.update_xaxes(range=(longitude[0], longitude[1]),
title='Longitude')

fig.update_yaxes(range=(latitude[0], latitude[1]),
title='Latitude')

fig.update_layout(autosize=False, width=800, height=800,
legend=dict(x=1, y=0, xanchor='auto', yanchor='auto',
bgcolor='rgba(255, 255, 255, 0.5)'))

fig.show()

Once we run the above code, we get back the next plot.

Plotly Graph Objects heatmap with added interactivity. This plot shows measurement locations (wells) and variations inside our Acoustic Compressional Slowness. Image by the writer.

Now, once we hover over any of the wells, we are going to get the well name, followed by the latitude and longitude, and the log value. On this case, we’re displaying the acoustic compressional slowness.

On this short tutorial, now we have seen how we will transcend a straightforward and static matplotlib figure of our measurement variation. The additional functionality and interactivity from Plotly makes it a fantastic selection to visualise geospatial variations in well log measurements.

The additional interactivity allows us to discover what well each of the dots represents, what the measured value was at that location, and interpret the values of the grid that shouldn’t have a direct measurement.

The dataset utilized in this text is a subset of a training dataset used as a part of a Machine Learning competition run by Xeek and FORCE 2020 (Bormann et al., 2020). It’s released under a NOLD 2.0 licence from the Norwegian Government, details of which could be found here: Norwegian Licence for Open Government Data (NLOD) 2.0. The complete dataset could be accessed here.

The complete reference for the dataset is:

Bormann, Peter, Aursand, Peder, Dilib, Fahad, Manral, Give up, & Dischington, Peter. (2020). FORCE 2020 Well well log and lithofacies dataset for machine learning competition [Data set]. Zenodo. http://doi.org/10.5281/zenodo.4351156

LEAVE A REPLY

Please enter your comment!
Please enter your name here