Aquagrams with Python and Matplotlib

Aquagrams are specific diagrams used to chart the relative strength of the NIR reflectance (or absorbance) of selected bands from water or water-based (aqueous) systems. Aquagrams were developed together with the relatively novel approach of aquaphotomics which aims at studying aqueous system through the lens of the interaction of near-infrared light with water and its solutes. As the structure of the system changes, NIR water absorption bands also change. This change is detected and analysed through aquaphotomics and aquagrams are a very suggestive way to visualise the results.

In this post, we’ll be learning how to draw an aquagram using Python and Matplotlib. We’ll use a dataset of NIR spectra of honey samples with different types of adulterants. The dataset is made available courtesy of Dr. Mercedes Bertotto, formerly at the National Service for Agrifood Health and Quality (SENASA) in Argentina.

The basics: data pre-processing and bands extraction

We won’t go much into the details of the specific methodology of aquaphotomics for this specific dataset, but just summarise them here:

  1. Load NIR reflectance data.
  2. Select the wavelength range of interest.
  3. Transform reflectance into absorbance data.
  4. Apply Standard Normal Variate (SNV).
  5. Extract the bands of interest for the aquagram.
  6. Draw the aquagram.

Let’s start with the import

Define the SNV (for more information see our post here):

We can now run through the items 1–4 of the list above. The data file is available for download at our GitHub repository.

Once again, the details of the pre-processing, may differ for your specific dataset and are not too important for the aim of this post. The point to keep in mind here is that we want to have a processed dataset of NIR absorbance data from which to extract the wavelengths of interests, which we call the water matrix coordinates (WAMACS).

In our case, these are

The WAMACS are defined as a list of strings. For the sake of creating a list of labels for the aquagrams we will be plotting, let’s generate list of ’rounded’ WAMACS:

Next, we are going to extract the indices at which these WAMACS appear in the dataset

Finally, we extract the spectral values at these WAMACS and normalise the results

OK, the data is now ready for plotting.

Aquagrams in Matplotlib: let’s start with a polar plot

An aquagram is a type of plot that can be categorised as a radar chart. It is in essence a generalised polar plot, i.e. a type of plot displaying variations on a radial and an angular scale.

A very easy first approach is therefore to build a polar plot in Matplotlib. We define an angles array containing equi-spaced angular coordinates with the same size as the number of WAMACS. We then plot the normalised absorbance bands of the last spectrum, corresponding to 100% honey sample

Which produces this plot
aquagram first attempt

Not too bad for a start, however we would like to see the plot line to form a closed polygon. The trick to do that, is to copy the first data point at the end of the array, so that the first and the last points are the same. This requires some adjustment to the code, to display the first and the last point on the same spot. The simple fix is to manually add the missing line to the chart with a simple trick (starting from line 7 below).

with this result
aquagram second attempt

That’s the result we were after. We have plotted the absorbance values of one spectra at specific wavelengths and placed these values on a equally spaced angular grid.

The difference between a polar plot and a radar plot is in the shape of the grid lines. In the polar plot the grid lines are concentric circles. In a radar chart they are polygons. The difference is minimal, however Matplotlib does not have a built-in function to change the shape of the grid lines, so we need to write one ourselves.

Aquagrams in Matplotlib: a radar chart

Fortunately, the extensive example library of Matplotlib provides us precisely with what we need in this example. All we need to do is to adapt the example to our case. Let’s copy below the main function defined in that example

This code defines a new projection type, called ‘radar’ that can be passed as an argument of the polar plot. The function support both circular and polygonal grid lines, the circular case being identical to the previous case. The other functions are required to draw the polygonal lines in the right position and with the right scale. To take advantage of this function, here’s the code we need to run

Aquagram Matplotlib Python
In addition to the previous case, we plotted two lines, one corresponding to pure honey and the other to pure glucose. As you can see, the aquagram enables a quick differentiation between the two.

As a final idea, if you want to generate a filled plot, like in the one shown in the featured image of this post, just use the command ax.fill  instead of ax.plot .

Thanks for reading and until next time,


  1. R. Tsenkova et al. Essentials of Aquaphotomics and Its Chemometrics Approaches.
  2. R. Tsenkova, Aquaphotomics: Water in the biological and aqueous world scrutinised with invisible light.
  3. G. Bázár et al. NIR detection of honey adulteration reveals differences in water spectral pattern.