Stable Isotope Representation from Groundwater Samples with Python Pandas - Tutorial


Stable isotope analysis is a powerful tool for the evaluation of groundwater origin and groundwater dynamics, specially when the observation points and recording periods are limited. Isotope representation can be done with any spreadsheet software, but the workflow is defficient specially when plotting scatter points, solid lines, labels and legends.

This tutorial shows a the complete procedure to represent a stable isotope representation of groundwater samples from remediation site in New Mexico, USA. The script is done with Python 3, Pandas and other packages that come alread installed on the Anaconda distribution. Code for the representation of the Global Meteoric Water Line is also included as well as options for the figure storage.


Python code

This is the Python code for this tutorial:

Import required libraries

For this tutorial we will use famous visualization library Matplotlib and some packages that come already installed on the Anaconda Distribution for Windows.

%matplotlib inline
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

Read the Excel file and create a Pandas Dataframe

One powerful way to deal with tabular data is to use a Pandas Dataframe. In this part we open a Excel file and create a dataframe with an index definition.

homestakeDf = pd.read_excel('../Xls/USGS_Stable_Isotopes.xlsx','Isotopes')
Delta2H(permil) Delta18O(permil) Station_Name
0 -83.95 -10.81 CW28
1 -78.85 -10.16 CW45
2 -81.66 -10.72 CW18
3 -74.31 -9.57 CW15
4 -68.28 -8.36 CW37
homestakeDf.set_index(['Station_Name'], inplace=True)
Delta2H(permil) Delta18O(permil)
CW28 -83.95 -10.81
CW45 -78.85 -10.16
CW18 -81.66 -10.72
CW15 -74.31 -9.57
CW37 -68.28 -8.36

Create a Global Meteoric Water Line GMWL array

In order to compare the isotopic signature with the global precipitation GMWL we need to create an array that represents points over this line. But first we need some basic statistics about the distribution of deltas from 18O.

count    23.000000
mean     -9.459130
std       0.956874
min     -10.810000
25%     -10.340000
50%      -9.170000
75%      -8.775000
max      -7.370000
Name: Delta_18O_(permil), dtype: float64
#create array and equation application
delta18O = np.linspace(-11,-7,num=50)
delta2H = delta18O*8.13+10.8
array([-78.63      , -77.96632653, -77.30265306, -76.63897959,
       -75.97530612, -75.31163265, -74.64795918, -73.98428571,
       -73.32061224, -72.65693878, -71.99326531, -71.32959184,
       -70.66591837, -70.0022449 , -69.33857143, -68.67489796,
       -68.01122449, -67.34755102, -66.68387755, -66.02020408,
       -65.35653061, -64.69285714, -64.02918367, -63.3655102 ,
       -62.70183673, -62.03816327, -61.3744898 , -60.71081633,
       -60.04714286, -59.38346939, -58.71979592, -58.05612245,
       -57.39244898, -56.72877551, -56.06510204, -55.40142857,
       -54.7377551 , -54.07408163, -53.41040816, -52.74673469,
       -52.08306122, -51.41938776, -50.75571429, -50.09204082,
       -49.42836735, -48.76469388, -48.10102041, -47.43734694,
       -46.77367347, -46.11      ])

Isotope representation

We did a series of scatter representation to have an unique color for sample and entry on the legend. The GMWL was displayed as a solid line. Some labels for the

fig, ax1 = plt.subplots(figsize=(12,8))

for index, row in homestakeDf.iterrows():
    ax1.annotate(index, (row['Delta_18O_(permil)']+0.05,row['Delta_2H_(permil)']+0.05))

ax1.set_xlabel('δ18O 0/00 VSMOW')
ax1.set_ylabel('δ2H 0/00 VSMOW')

Input files

You can download the input files for this tutorial on this link.


Saul Montoya

Saul Montoya es Ingeniero Civil graduado de la Pontificia Universidad Católica del Perú en Lima con estudios de postgrado en Manejo e Ingeniería de Recursos Hídricos (Programa WAREM) de la Universidad de Stuttgart con mención en Ingeniería de Aguas Subterráneas y Hidroinformática.

Smiley face

Subscribe to our free e-newsletter for tutorials, articles, webminars, courses and more.