<script data-pm-proxy="intercept"></script><?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Subsurface Syntax]]></title><description><![CDATA[Where geoscience meets Python, one plot at a time]]></description><link>https://andymcdonaldgeo.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!JLsK!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8f57252-4306-43f0-97f3-c979dce93b0b_790x790.png</url><title>Subsurface Syntax</title><link>https://andymcdonaldgeo.substack.com</link></image><generator>Substack</generator><lastBuildDate>Sat, 05 Sep 2026 03:08:22 GMT</lastBuildDate><atom:link href="/__u/andymcdonaldgeo.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Andy McDonald]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[andymcdonaldgeo@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[andymcdonaldgeo@substack.com]]></itunes:email><itunes:name><![CDATA[Andy McDonald]]></itunes:name></itunes:owner><itunes:author><![CDATA[Andy McDonald]]></itunes:author><googleplay:owner><![CDATA[andymcdonaldgeo@substack.com]]></googleplay:owner><googleplay:email><![CDATA[andymcdonaldgeo@substack.com]]></googleplay:email><googleplay:author><![CDATA[Andy McDonald]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[When a Line Chart Becomes Spaghetti]]></title><description><![CDATA[Past a handful of series, a line chart stops letting you follow any of them. Here is what breaks and what to plot instead]]></description><link>https://andymcdonaldgeo.substack.com/p/when-a-line-chart-becomes-spaghetti</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/when-a-line-chart-becomes-spaghetti</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Sat, 08 Aug 2026 12:25:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!WmX0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5efb35d-722f-4302-a6c4-3471ba417a4e_700x342.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!WmX0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5efb35d-722f-4302-a6c4-3471ba417a4e_700x342.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!WmX0!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5efb35d-722f-4302-a6c4-3471ba417a4e_700x342.png 424w, /__u/substackcdn.com/image/fetch/$s_!WmX0!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5efb35d-722f-4302-a6c4-3471ba417a4e_700x342.png 848w, /__u/substackcdn.com/image/fetch/$s_!WmX0!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5efb35d-722f-4302-a6c4-3471ba417a4e_700x342.png 1272w, /__u/substackcdn.com/image/fetch/$s_!WmX0!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5efb35d-722f-4302-a6c4-3471ba417a4e_700x342.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!WmX0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5efb35d-722f-4302-a6c4-3471ba417a4e_700x342.png" width="700" height="342" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c5efb35d-722f-4302-a6c4-3471ba417a4e_700x342.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:342,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!WmX0!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5efb35d-722f-4302-a6c4-3471ba417a4e_700x342.png 424w, /__u/substackcdn.com/image/fetch/$s_!WmX0!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5efb35d-722f-4302-a6c4-3471ba417a4e_700x342.png 848w, /__u/substackcdn.com/image/fetch/$s_!WmX0!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5efb35d-722f-4302-a6c4-3471ba417a4e_700x342.png 1272w, /__u/substackcdn.com/image/fetch/$s_!WmX0!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5efb35d-722f-4302-a6c4-3471ba417a4e_700x342.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Line charts are common data visualisation tools. They are usually the first chart we reach for when we want to visualise something that changes with time, depth (e.g well logs) or any other ordered axis. They are great when we want to display a handful of lines, however, when we start adding more than several onto that plot, it can become hard to read and starts to resemble spaghetti.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>This is the equivalent of the overplotting problem we previously saw when looking at scatter plots / crossplots. Where a scatter plot fails is when the points on the chart start to lie on top of the same position resulting in a dense indistinguishable blob, but a line chart on the other hand fails when the series cross over each other and it can become difficult to follow them. Even though we can make the chart look good through styling, it now longer supports what the line chart was supposed to do.</p><p>To illustrate the problem with line charts, we will work with a set of twenty synthetic daily temperature records throughout a single year.</p><pre><code><span>import numpy as np
import matplotlib.pyplot as plt

rng = np.random.default_rng(11)
days = np.arange(365)
n_stations = 20

def temperature_series(mean_c, amplitude_c, noise_sd=1.8, peak_day=200):
    seasonal = mean_c + amplitude_c * np.sin(2 * np.pi * (days - peak_day + 91.25) / 365)
    raw = rng.normal(0, noise_sd, days.size + 10)
    weather = np.convolve(raw, np.ones(11) / 11, mode=&#8221;valid&#8221;) * 3.0
    return seasonal + weather

stations = {}

for i in range(n_stations):
    stations[f&#8221;Station {i+1:02d}&#8221;] = temperature_series(
        mean_c=rng.uniform(4, 19),
        amplitude_c=rng.uniform(5, 15),
    )

fig, ax = plt.subplots(figsize=(8, 5))
cmap = plt.get_cmap(&#8221;tab20&#8221;)
for j, k in enumerate(stations):
    ax.plot(days, stations[k], color=cmap(j % 20), lw=1.2, label=k)
ax.legend(ncol=2, fontsize=7)
plt.show(</span></code></pre><p>When we run the above, we get the following chart.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!l_Xd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd3a94e0-88ba-4366-a141-ae63ce5f7bd4_700x469.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!l_Xd!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd3a94e0-88ba-4366-a141-ae63ce5f7bd4_700x469.png 424w, /__u/substackcdn.com/image/fetch/$s_!l_Xd!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd3a94e0-88ba-4366-a141-ae63ce5f7bd4_700x469.png 848w, /__u/substackcdn.com/image/fetch/$s_!l_Xd!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd3a94e0-88ba-4366-a141-ae63ce5f7bd4_700x469.png 1272w, /__u/substackcdn.com/image/fetch/$s_!l_Xd!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd3a94e0-88ba-4366-a141-ae63ce5f7bd4_700x469.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!l_Xd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd3a94e0-88ba-4366-a141-ae63ce5f7bd4_700x469.png" width="700" height="469" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cd3a94e0-88ba-4366-a141-ae63ce5f7bd4_700x469.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:469,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!l_Xd!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd3a94e0-88ba-4366-a141-ae63ce5f7bd4_700x469.png 424w, /__u/substackcdn.com/image/fetch/$s_!l_Xd!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd3a94e0-88ba-4366-a141-ae63ce5f7bd4_700x469.png 848w, /__u/substackcdn.com/image/fetch/$s_!l_Xd!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd3a94e0-88ba-4366-a141-ae63ce5f7bd4_700x469.png 1272w, /__u/substackcdn.com/image/fetch/$s_!l_Xd!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd3a94e0-88ba-4366-a141-ae63ce5f7bd4_700x469.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>You can still read a few things from this, such as the overall trend of being cold at both ends of the year and warm in the middle, and you could probably identify the single warmest or coldest line at a given moment.</p><p>Beyond that, the data separates from the intended purpose of the line chart. You can&#8217;t properly follow Station 12 from January (day 0) to December (day 365), and you constantly have to refer to the legend to remember what line you are tracing, especially if colours are similar.</p><p>By having a chart like this, it makes the reader do a lot of hard work to try and extract anything meaningful from it.</p><h2><strong>Why the Line Chart Fails</strong></h2><p>The failure in this example has a few separate causes that compound on top of each other:</p><ul><li><p>Lines cross, so a colour you were tracking disappears behind others and re-emerges somewhere you weren&#8217;t looking</p></li><li><p>There are more series than distinct colours the eye can hold apart, so the palette recycles and two stations end up near-identical shades</p></li><li><p>And the legend sits away from the data, which forces a constant back-and-forth between the key and the plot that gets harder the more entries there are.</p></li></ul><p>None of this can be fixed by looking harder at the plot, and it becomes a case of trying to ask too much of the chart at one time, such as:</p><ul><li><p>follow one specific station</p></li><li><p>compare two stations</p></li><li><p>judge the whole population</p></li><li><p>spot the outlier(s)</p></li></ul><p>It can&#8217;t serve all of these purposes on a shared axes once the series count is high. As with overplotting on a crossplot, the plot doesn&#8217;t warn you that there is a problem. That is down to you when you are creating it.</p><h2><strong>More Colours Won&#8217;t Save It</strong></h2><p>The first instinct you may have is to start looking at the colours of the lines and selecting a bigger palette in the hopes that if every station has a unique colour it may help resolve the issue. However, the human eye can only reliably separate a handful of categorical colours at once. Pushing past roughly ten or twelve colours produces shades so close that neighbours start to become indistinguishable from one another.</p><p>A twenty-colour or thirty-colour spaghetti plot is essentially a more colourful spaghetti plot, rather than a clearer one. The same goes for changing line styles or markers. These can help a little when we have a handful of series, but they clutter the chart as we add more.</p><p>What we need to try and do is to stop demanding that a single chart answer every question we have, but instead we need to pick a plot that answers the questions you have. This may mean several different smaller plots, but each should answer a question you have.</p><h2><strong>If You Need to See Each Station: Small Multiples</strong></h2><p>When the task is to inspect the individual behaviour of each series then it is best to give each one its own small panel and keep the axes common across all of them.</p><p>This results in small multiples or subplots appearing on the figure (also known as faceting).</p><pre><code><span>fig, axes = plt.subplots(4, 5, figsize=(15, 9), sharex=True, sharey=True)
for ax, k in zip(axes.ravel(), stations):
    ax.plot(days, stations[k], color=&#8221;#1f3b57&#8221;, lw=1.0)
    ax.set_title(k, fontsize=9)
plt.tight_layout()
plt.show()</span></code></pre><p>With the small multiples, you can still compare across them, which will make identifying a flat, mild year looks from a swinging one at a glance, and an odd station stands out from its neighbours. However, what you may end up losing, is the ability to see everything superimposed, and it comes with a size limit, which can be restrictive with small screen space.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!wkIK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf0d3a11-93de-4a52-bf2a-a33aea486609_700x416.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!wkIK!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf0d3a11-93de-4a52-bf2a-a33aea486609_700x416.png 424w, /__u/substackcdn.com/image/fetch/$s_!wkIK!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf0d3a11-93de-4a52-bf2a-a33aea486609_700x416.png 848w, /__u/substackcdn.com/image/fetch/$s_!wkIK!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf0d3a11-93de-4a52-bf2a-a33aea486609_700x416.png 1272w, /__u/substackcdn.com/image/fetch/$s_!wkIK!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf0d3a11-93de-4a52-bf2a-a33aea486609_700x416.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!wkIK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf0d3a11-93de-4a52-bf2a-a33aea486609_700x416.png" width="700" height="416" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bf0d3a11-93de-4a52-bf2a-a33aea486609_700x416.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:416,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!wkIK!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf0d3a11-93de-4a52-bf2a-a33aea486609_700x416.png 424w, /__u/substackcdn.com/image/fetch/$s_!wkIK!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf0d3a11-93de-4a52-bf2a-a33aea486609_700x416.png 848w, /__u/substackcdn.com/image/fetch/$s_!wkIK!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf0d3a11-93de-4a52-bf2a-a33aea486609_700x416.png 1272w, /__u/substackcdn.com/image/fetch/$s_!wkIK!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf0d3a11-93de-4a52-bf2a-a33aea486609_700x416.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>If You Care About One or Two Series: Highlight and Context</strong></h2><p>Often you do not need to see all of your data series equally, but rather you may want to see how one or two specific stations behave against the rest.</p><p>In this case, you would draw the series you care about in strong colour and push everything else into a light grey background, which de-emphasises</p><pre><code><span>fig, ax = plt.subplots(figsize=(8, 5))
for k in stations:
    ax.plot(days, stations[k], color=&#8221;0.8&#8221;, lw=0.9, zorder=1)
ax.plot(days, stations[&#8221;Station 07&#8221;], color=&#8221;#C44E52&#8221;, lw=2.4, zorder=3, label=&#8221;Station 07&#8221;)
ax.plot(days, stations[&#8221;Station 14&#8221;], color=&#8221;#4C72B0&#8221;, lw=2.4, zorder=3, label=&#8221;Station 14&#8221;)
ax.legend()
plt.show()</span></code></pre><p>In this example, we will make two distinctive stations show on the plot.</p><p>Both of the series we have selected have annual means are almost the same, 10.6 &#176;C and 11.4 &#176;C, which is close enough that they may be missed in a summary table, however, when we look at where the stations are they are nothing alike. Station 07 is maritime: it moves through a range of about 11 &#176;C across the whole year, roughly 7 &#176;C in January and 14 &#176;C in July. Station 14 is continental, swinging through 37 &#176;C, from about &#8722;3 &#176;C in January to 28 &#176;C in July.</p><p>In January, Station 14 is the 17th warmest of the twenty and Station 07 sits 6th. By July they have switched places almost exactly: Station 14 is 3rd warmest, Station 07 has fallen to 18th. Two stations with the same annual average, ranked at opposite ends of the group depending on when you look. That is the kind of thing that looking an annual mean can hide, but was also completely invisible in the earlier spaghetti plot. The grey context still shows the population that these two series sit within, so you can still have a sense of scale without trying to untangle the lines.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!ICZ3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc3b512b-4945-4ed1-b047-6971f2c1164c_700x469.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!ICZ3!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc3b512b-4945-4ed1-b047-6971f2c1164c_700x469.png 424w, /__u/substackcdn.com/image/fetch/$s_!ICZ3!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc3b512b-4945-4ed1-b047-6971f2c1164c_700x469.png 848w, /__u/substackcdn.com/image/fetch/$s_!ICZ3!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc3b512b-4945-4ed1-b047-6971f2c1164c_700x469.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ICZ3!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc3b512b-4945-4ed1-b047-6971f2c1164c_700x469.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!ICZ3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc3b512b-4945-4ed1-b047-6971f2c1164c_700x469.png" width="700" height="469" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fc3b512b-4945-4ed1-b047-6971f2c1164c_700x469.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:469,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!ICZ3!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc3b512b-4945-4ed1-b047-6971f2c1164c_700x469.png 424w, /__u/substackcdn.com/image/fetch/$s_!ICZ3!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc3b512b-4945-4ed1-b047-6971f2c1164c_700x469.png 848w, /__u/substackcdn.com/image/fetch/$s_!ICZ3!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc3b512b-4945-4ed1-b047-6971f2c1164c_700x469.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ICZ3!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc3b512b-4945-4ed1-b047-6971f2c1164c_700x469.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This technique is common in climate charts, where a single year is highlighted against decades of others in grey. It works whenever the question is &#8220;how does this one compare to the rest.&#8221; It can also easily be applied to any other chart where you want to compare one against a large number of samples.</p><h2><strong>If You Only Care About the Population: Aggregate</strong></h2><p>Sometimes the individual identities don&#8217;t matter at all and you only want the overall behaviour and its spread. In that case, one option is to summarise by plotting a median line and shade a band between the full dataset&#8217;s percentiles.</p><pre><code><span>Y = np.vstack(list(stations.values()))

p10 = np.percentile(Y, 10, axis=0)
p50 = np.percentile(Y, 50, axis=0)
p90 = np.percentile(Y, 90, axis=0)

fig, ax = plt.subplots(figsize=(8, 5))
ax.fill_between(days, p10, p90, alpha=0.25, label=&#8221;10th-90th percentile&#8221;)
ax.plot(days, p50, lw=2.2, label=&#8221;Median&#8221;)
ax.legend()
plt.show()</span></code></pre><p>This throws away all of the individual station data, and what you get instead is a clean view on the typical seasonal cycle and how much stations vary around it. The band will widen or narrow to show where the population agrees or has minimal spread or where is has maximum spread.</p><p>This can be the right plot when you&#8217;re characterising a group of measurements rather than any particular member of it. However, it becomes the wrong plot when someone wants to ask something about a specific station, because that information is gone.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!7lFI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64bdbe6d-0aed-457d-85b0-5cdc5b6526b8_700x475.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!7lFI!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64bdbe6d-0aed-457d-85b0-5cdc5b6526b8_700x475.png 424w, /__u/substackcdn.com/image/fetch/$s_!7lFI!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64bdbe6d-0aed-457d-85b0-5cdc5b6526b8_700x475.png 848w, /__u/substackcdn.com/image/fetch/$s_!7lFI!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64bdbe6d-0aed-457d-85b0-5cdc5b6526b8_700x475.png 1272w, /__u/substackcdn.com/image/fetch/$s_!7lFI!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64bdbe6d-0aed-457d-85b0-5cdc5b6526b8_700x475.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!7lFI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64bdbe6d-0aed-457d-85b0-5cdc5b6526b8_700x475.png" width="700" height="475" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/64bdbe6d-0aed-457d-85b0-5cdc5b6526b8_700x475.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:475,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!7lFI!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64bdbe6d-0aed-457d-85b0-5cdc5b6526b8_700x475.png 424w, /__u/substackcdn.com/image/fetch/$s_!7lFI!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64bdbe6d-0aed-457d-85b0-5cdc5b6526b8_700x475.png 848w, /__u/substackcdn.com/image/fetch/$s_!7lFI!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64bdbe6d-0aed-457d-85b0-5cdc5b6526b8_700x475.png 1272w, /__u/substackcdn.com/image/fetch/$s_!7lFI!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64bdbe6d-0aed-457d-85b0-5cdc5b6526b8_700x475.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>The Other Spaghetti: Overlaid Distributions</strong></h2><p>There is also a second problem where we may have too many distribution lines on a plot that overlap each other.</p><p>With three or four groups that reads fine, however, when we start to have ten or twelve, the smooth curves cross and stack into their own kind of spaghetti. This results in you losing the ability to see any single distribution&#8217;s shape.</p><p>The plot to help alleviate this is the <a href="/__u/andymcdonaldgeo.substack.com/p/ridgeline-plots-in-matplotlib-an">ridgeline plot</a>, which I have spoken about a few times on here. On the ridgeline plot each distribution gets its own baseline, stacked vertically with a little overlap, so the whole set reads like a landscape of ridges (and it also is pretty to look at!).</p><p>In this example, we have thirty years of daily temperature at one continental station, split by month, January at the bottom to December at the top.</p><pre><code><span>from scipy.stats import gaussian_kde

month_len = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
edges = np.concatenate([[0], np.cumsum(month_len)])
years = [temperature_series(mean_c=11.0, amplitude_c=15.5) for _ in range(30)]
monthly = [np.concatenate([y[edges[m]:edges[m + 1]] for y in years]) for m in range(12)]
t_grid = np.linspace(-20, 36, 400)
overlap = 1.8

fig, ax = plt.subplots(figsize=(8, 7))
colours = plt.get_cmap(&#8221;coolwarm&#8221;)(np.linspace(0.05, 0.95, 12))

for i, samples in enumerate(monthly):
    dens = gaussian_kde(samples)(t_grid)
    dens = dens / dens.max()
    base = i * (1.0 / overlap)
    ax.fill_between(t_grid, base, base + dens, color=colours[i], alpha=0.85,
                    zorder=12 - i)
    ax.plot(t_grid, base + dens, color=&#8221;white&#8221;, lw=0.8, zorder=12 - i)

ax.set_yticks([])
ax.set_xlabel(&#8221;Temperature (&#176;C)&#8221;)
plt.show()</span></code></pre><p>The staggered baselines are what make this one work. Each month&#8217;s distribution is readable on its own, and because they share a horizontal axis you can still track how the whole seasonal cycle moves, the peaks sliding right through spring into summer and back left again through autumn.</p><p>You also can get something from this chart that was not easily identifiable on the line chart and that is that the summer months are visibly narrower than the winter ones, so this station&#8217;s winters are not just colder, but are far more variable. If we overlaid each of these on a single axis we would end up with an overlapping mess.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!KFLh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d35dff2-a47f-4b08-ab38-73d9d6ca857e_700x680.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!KFLh!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d35dff2-a47f-4b08-ab38-73d9d6ca857e_700x680.png 424w, /__u/substackcdn.com/image/fetch/$s_!KFLh!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d35dff2-a47f-4b08-ab38-73d9d6ca857e_700x680.png 848w, /__u/substackcdn.com/image/fetch/$s_!KFLh!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d35dff2-a47f-4b08-ab38-73d9d6ca857e_700x680.png 1272w, /__u/substackcdn.com/image/fetch/$s_!KFLh!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d35dff2-a47f-4b08-ab38-73d9d6ca857e_700x680.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!KFLh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d35dff2-a47f-4b08-ab38-73d9d6ca857e_700x680.png" width="700" height="680" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5d35dff2-a47f-4b08-ab38-73d9d6ca857e_700x680.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:680,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!KFLh!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d35dff2-a47f-4b08-ab38-73d9d6ca857e_700x680.png 424w, /__u/substackcdn.com/image/fetch/$s_!KFLh!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d35dff2-a47f-4b08-ab38-73d9d6ca857e_700x680.png 848w, /__u/substackcdn.com/image/fetch/$s_!KFLh!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d35dff2-a47f-4b08-ab38-73d9d6ca857e_700x680.png 1272w, /__u/substackcdn.com/image/fetch/$s_!KFLh!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d35dff2-a47f-4b08-ab38-73d9d6ca857e_700x680.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The trade-off with this chart is vertical space and a small amount of occlusion where a tall ridge hides the foot of the one behind it, but for comparing many distributions in an order that means something, it&#8217;s far more legible than the overlaid version.</p><h2><strong>Which One to Reach For</strong></h2><p>There&#8217;s no single replacement for a spaghetti plot, because the spaghetti plot was failing to answer several different questions at once, and each of those questions has its own better chart. Decide what you actually need to see, then pick accordingly: small multiples when you need to read each series, highlight-and-context when one or two matter against the rest, an aggregate band when only the population does, and a ridgeline plot when you&#8217;re comparing many distributions rather than many time series. Reaching for more colours is the one move that never helps.</p><p>The line chart isn&#8217;t wrong, any more than the scatter plot or the histogram was. Line charts are designed to answer the question &#8220;how does this series change&#8221;, however, when you ask it to answer that same question for twenty series and more at once on the same axes, it becomes painful. It is at this point that you need to stop adding lines and start choosing the plot that fits the question you&#8217;re really asking.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><div><hr></div><p><em>The ridgeline above relies on two settings that quietly decide whether the comparison is honest: every curve is normalised to the same peak height, and every KDE is evaluated over one fixed x-range. There is a third setting I left on default, and it is the one most likely to mislead you. The default </em><code>gaussian_kde</code><em> bandwidth over-smooths, and it will flatten a genuine right-skew into something that looks symmetric. Over on Substack I&#8217;ve written the full build of this chart, from scratch and with joypy, covering bandwidth choice, overlap tuning, and why 4 to 20 groups is the range where a ridgeline actually earns its place.</em></p><p><em><a href="/__u/andymcdonaldgeo.substack.com/p/ridgeline-plots-in-matplotlib-an">&#8594; Ridgeline Plots in Matplotlib: An Underused Way to Compare Distributions</a></em></p>]]></content:encoded></item><item><title><![CDATA[Why a Histogram Can Show You the Wrong Shape]]></title><description><![CDATA[How the number and position of bins can change what you think the distribution is telling you]]></description><link>https://andymcdonaldgeo.substack.com/p/why-a-histogram-can-show-you-the</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/why-a-histogram-can-show-you-the</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Sat, 01 Aug 2026 11:49:52 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!uoER!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb678ecd-71e8-463c-acc5-46930176512e_630x354.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!uoER!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb678ecd-71e8-463c-acc5-46930176512e_630x354.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!uoER!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb678ecd-71e8-463c-acc5-46930176512e_630x354.png 424w, /__u/substackcdn.com/image/fetch/$s_!uoER!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb678ecd-71e8-463c-acc5-46930176512e_630x354.png 848w, /__u/substackcdn.com/image/fetch/$s_!uoER!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb678ecd-71e8-463c-acc5-46930176512e_630x354.png 1272w, /__u/substackcdn.com/image/fetch/$s_!uoER!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb678ecd-71e8-463c-acc5-46930176512e_630x354.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!uoER!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb678ecd-71e8-463c-acc5-46930176512e_630x354.png" width="630" height="354" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/db678ecd-71e8-463c-acc5-46930176512e_630x354.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:354,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!uoER!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb678ecd-71e8-463c-acc5-46930176512e_630x354.png 424w, /__u/substackcdn.com/image/fetch/$s_!uoER!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb678ecd-71e8-463c-acc5-46930176512e_630x354.png 848w, /__u/substackcdn.com/image/fetch/$s_!uoER!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb678ecd-71e8-463c-acc5-46930176512e_630x354.png 1272w, /__u/substackcdn.com/image/fetch/$s_!uoER!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb678ecd-71e8-463c-acc5-46930176512e_630x354.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The histogram is on of the default ways to look at a dataset&#8217;s distribution. We essentially hand it a column of numbers and it shows where the values are concentrated, how widely they are spread out, and whether the distribution appears skewed. It&#8217;s simple, yet a familiar and useful data plot.</p><p>However, a histogram is not always a direct picture of the data. Before the chart appears on the screen, the values from our curve are divided into bins. The number of bins, their width and the position of their boundaries all affect the resulting shape of the histogram.</p><p>If we change any of those parameter, then the same dataset can appear smooth, bimodal or unexpectedly noisy.</p><p>This does not make the histogram a wrong choice, but it does mean that the histogram shows the data in a particular way, and how we view and set those parameters can influence the conclusions we draw.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>Creating a Distribution With Two Modes</strong></h2><p>To see how this happens, we can create a synthetic porosity dataset representing two geological formations. Each formation contains 300 observations, with mean porosities of 0.15 and 0.20, and a standard deviation of 0.014.</p><pre><code><span>import numpy as np
import matplotlib.pyplot as plt

rng = np.random.default_rng(3)

n = 600

pop_a = rng.normal(0.150, 0.014, n // 2)
pop_b = rng.normal(0.200, 0.014, n // 2)

por = np.clip(
    np.concatenate([pop_a, pop_b]),
    0.05,
    0.30
)

plot_range = (0.05, 0.30)</span></code></pre><p>The two populations overlap, but they are sufficiently separated for the combined distribution itself to contain two modes.</p><p>Two geological groups do not always produce two peaks in a histogram. If their distributions overlap strongly enough, the combined data may be genuinely unimodal. In this example, we already know that two modes are present, so we can see how well the histogram reveals them.</p><h2><strong>The Bin Count Decides What You See</strong></h2><p>The number of bins controls the resolution of the histogram. With too few bins, the observations from different parts of the distribution are combined into the same broad intervals. This can make the real structure can disappear.</p><p>With too many bins, all of the small variations caused by random sampling can start to look like meaningful features. To illustrate this we can hold the data and plotting range constant whilst changing only the number of bins.</p><pre><code><span>fig, axes = plt.subplots(
    1,
    4,
    figsize=(16, 4),
    sharey=True
)

for ax, bins in zip(axes, [6, 12, 18, 40]):
    ax.hist(
        por,
        bins=bins,
        range=plot_range
    )

    ax.set_xlim(plot_range)
    ax.set_xlabel(&#8221;Porosity (fraction)&#8221;)
    ax.set_title(f&#8221;{bins} bins&#8221;)

axes[0].set_ylabel(&#8221;Count&#8221;)

plt.tight_layout()
plt.show()</span></code></pre><p>This gives us the following figure:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!3xv_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa390017c-192c-4418-956b-32e74ebacf95_630x136.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!3xv_!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa390017c-192c-4418-956b-32e74ebacf95_630x136.png 424w, /__u/substackcdn.com/image/fetch/$s_!3xv_!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa390017c-192c-4418-956b-32e74ebacf95_630x136.png 848w, /__u/substackcdn.com/image/fetch/$s_!3xv_!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa390017c-192c-4418-956b-32e74ebacf95_630x136.png 1272w, /__u/substackcdn.com/image/fetch/$s_!3xv_!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa390017c-192c-4418-956b-32e74ebacf95_630x136.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!3xv_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa390017c-192c-4418-956b-32e74ebacf95_630x136.png" width="630" height="136" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a390017c-192c-4418-956b-32e74ebacf95_630x136.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:136,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!3xv_!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa390017c-192c-4418-956b-32e74ebacf95_630x136.png 424w, /__u/substackcdn.com/image/fetch/$s_!3xv_!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa390017c-192c-4418-956b-32e74ebacf95_630x136.png 848w, /__u/substackcdn.com/image/fetch/$s_!3xv_!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa390017c-192c-4418-956b-32e74ebacf95_630x136.png 1272w, /__u/substackcdn.com/image/fetch/$s_!3xv_!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa390017c-192c-4418-956b-32e74ebacf95_630x136.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>With six bins, the centre of the distribution becomes a broad block. There is no clear valley separating the two populations, and it would be difficult to justify a claim that the data contains two modes.</p><p>At twelve bins, some structure begins to appear.</p><p>At eighteen bins, the two concentrations and the lower-density region between them are much clearer.</p><p>At forty bins, the broader pattern remains visible, but the bars also begin to jump up and down in response to relatively small differences in sample count. Some of those fluctuations could easily be mistaken for additional peaks.</p><p>Each histogram is a small summary of the same data, and can support different visual interpretations.</p><p>A low-resolution histogram can hide genuine structure, whilst a high-resolution histogram can give random sampling variation more visual importance than it deserves.</p><h2><strong>There Is No Universally Correct Number of Bins</strong></h2><p>It is tempting to select the 18 bin histogram as the correct one, but there is no single bin count that is best for every dataset and every question.</p><p>A histogram designed to show the broad range of a variable may need fewer bins than one intended to investigate subtle modes. Sample size, variability, skewness and outliers can all affect what works well.</p><p>This is why several rules have been developed for selecting bin widths automatically.</p><p>One commonly used option is the Freedman&#8211;Diaconis rule, which uses the interquartile range and sample size</p><pre><code><span>fig, ax = plt.subplots(figsize=(6, 4.5))

ax.hist(
    por,
    bins=&#8221;fd&#8221;,
    range=plot_range
)

ax.set_xlim(plot_range)
ax.set_xlabel(&#8221;Porosity (fraction)&#8221;)
ax.set_ylabel(&#8221;Count&#8221;)

plt.show()</span></code></pre><p>Other options include <code>"scott"</code>, <code>"sturges"</code> and <code>"auto"</code>.</p><p>These rules are useful because they respond to the data rather than applying the same arbitrary bin count to every variable.</p><p>However, they should be treated as starting points and worked on from there.</p><p>A bin-width rule attempts to balance resolution against sampling noise, however, it does not know whether twe are trying to investigate skewness, tails, outliers or multiple geological populations.</p><p>It can still produce a reasonable histogram whilst obscuring the particular feature you care about.</p><h2><strong>Bin Position Matters Too</strong></h2><p>Bin count is often the choice most will go to when changing parameters on a histogram, however, the less obvious choice is where the bin boundaries begin.</p><p>Two histograms can use exactly the same bin width and still look different because their bin boundaries begin in different positions. Shifting the first boundary shifts every subsequent boundary with it. Values near an edge may then move from one bar to another, changing the heights of neighbouring bins.</p><p>In the following example, the bin width remains fixed at 0.018. The second histogram shifts the boundaries by half a bin width.</p><pre><code><span>width = 0.018
offsets = [0.0, width / 2]

fig, axes = plt.subplots(
    1,
    2,
    figsize=(10, 4.2),
    sharey=True
)

for ax, offset in zip(axes, offsets):

    edges = np.arange(
        plot_range[0] + offset - width,
        plot_range[1] + width,
        width
    )

    ax.hist(por, bins=edges)
    ax.set_xlim(plot_range)
    ax.set_xlabel(&#8221;Porosity (fraction)&#8221;)

axes[0].set_title(&#8221;Original bin alignment&#8221;)
axes[1].set_title(&#8221;Shifted by half a bin&#8221;)
axes[0].set_ylabel(&#8221;Count&#8221;)

plt.tight_layout()
plt.show()</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!n_Ke!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb79c2197-c2d1-4c43-8132-08408f6c90ef_630x222.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!n_Ke!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb79c2197-c2d1-4c43-8132-08408f6c90ef_630x222.png 424w, /__u/substackcdn.com/image/fetch/$s_!n_Ke!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb79c2197-c2d1-4c43-8132-08408f6c90ef_630x222.png 848w, /__u/substackcdn.com/image/fetch/$s_!n_Ke!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb79c2197-c2d1-4c43-8132-08408f6c90ef_630x222.png 1272w, /__u/substackcdn.com/image/fetch/$s_!n_Ke!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb79c2197-c2d1-4c43-8132-08408f6c90ef_630x222.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!n_Ke!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb79c2197-c2d1-4c43-8132-08408f6c90ef_630x222.png" width="630" height="222" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b79c2197-c2d1-4c43-8132-08408f6c90ef_630x222.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:222,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!n_Ke!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb79c2197-c2d1-4c43-8132-08408f6c90ef_630x222.png 424w, /__u/substackcdn.com/image/fetch/$s_!n_Ke!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb79c2197-c2d1-4c43-8132-08408f6c90ef_630x222.png 848w, /__u/substackcdn.com/image/fetch/$s_!n_Ke!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb79c2197-c2d1-4c43-8132-08408f6c90ef_630x222.png 1272w, /__u/substackcdn.com/image/fetch/$s_!n_Ke!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb79c2197-c2d1-4c43-8132-08408f6c90ef_630x222.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Both histograms still contain evidence of two modes, but the apparent shape changes.</p><p>The relative heights of the peaks differ, as does the depth and position of the valley between them. One alignment produces sharper individual bars, whilst the other gives the populations a broader appearance.</p><p>Nothing about the observations has changed. Only the location of the bin boundaries has moved.</p><p>For a clearly separated distribution, this may alter only the visual emphasis. For a borderline case with more strongly overlapping populations, the shift may be enough to determine whether a small valley appears at all.</p><p>Bin position is particularly easy to overlook because it is rarely stated on the finished figure. The reader sees the bars, but not the arbitrary grid that produced them.</p><h2><strong>Can We Avoid Binning?</strong></h2><p>One way to check the distribution is to compare the histogram with a plot that does not use bins.</p><p>The empirical cumulative distribution function, or ECDF, plots each observed value against the proportion of observations at or below it.</p><pre><code><span>xs = np.sort(por)
ys = np.arange(1, len(xs) + 1) / len(xs)

fig, ax = plt.subplots(figsize=(6, 4.5))

ax.plot(
    xs,
    ys,
    drawstyle=&#8221;steps-post&#8221;
)

ax.set_xlim(plot_range)
ax.set_ylim(0, 1)

ax.set_xlabel(&#8221;Porosity (fraction)&#8221;)
ax.set_ylabel(&#8221;Cumulative proportion&#8221;)

plt.show()</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!GJGS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd46f89f-27d7-4148-94cf-2da829267ca9_630x224.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!GJGS!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd46f89f-27d7-4148-94cf-2da829267ca9_630x224.png 424w, /__u/substackcdn.com/image/fetch/$s_!GJGS!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd46f89f-27d7-4148-94cf-2da829267ca9_630x224.png 848w, /__u/substackcdn.com/image/fetch/$s_!GJGS!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd46f89f-27d7-4148-94cf-2da829267ca9_630x224.png 1272w, /__u/substackcdn.com/image/fetch/$s_!GJGS!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd46f89f-27d7-4148-94cf-2da829267ca9_630x224.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!GJGS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd46f89f-27d7-4148-94cf-2da829267ca9_630x224.png" width="630" height="224" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dd46f89f-27d7-4148-94cf-2da829267ca9_630x224.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:224,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!GJGS!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd46f89f-27d7-4148-94cf-2da829267ca9_630x224.png 424w, /__u/substackcdn.com/image/fetch/$s_!GJGS!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd46f89f-27d7-4148-94cf-2da829267ca9_630x224.png 848w, /__u/substackcdn.com/image/fetch/$s_!GJGS!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd46f89f-27d7-4148-94cf-2da829267ca9_630x224.png 1272w, /__u/substackcdn.com/image/fetch/$s_!GJGS!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdd46f89f-27d7-4148-94cf-2da829267ca9_630x224.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The ECDF has no bin count, bin width or bin origin. Every observation contributes directly to the curve.</p><p>It is particularly useful for reading quantiles, comparing distributions and understanding the proportion of values above or below a threshold.</p><p>Changes in concentration can also appear as changes in slope. Steeper sections indicate that many values occupy a relatively narrow interval, whilst shallower sections indicate that the observations are more dispersed.</p><p>However, an ECDF is not necessarily the best plot for identifying modes.</p><p>The two concentrations are visible through changes in the slope of the curve, but they are less immediate than the peaks in a well-chosen histogram. Someone unfamiliar with ECDFs may find the histogram much easier to interpret.</p><p>It is therefore better viewed as a complementary plot rather than a complete alternative.</p><p>Kernel density estimates offer another smooth view of a distribution without discrete bins, but they introduce a bandwidth parameter. Too much smoothing can merge populations, whilst too little can produce artificial bumps. Kernel density plots avoid discrete bins, but they still require us to choose how much smoothing to apply.</p><h2><strong>A More Reliable Way to Use Histograms</strong></h2><p>When working with histograms, there are a few things we can do to settle on the most optimal bin parameters for the figure we are working on:</p><ol><li><p>Plot several reasonable bin counts whilst keeping the plotting range fixed.</p></li><li><p>Try shifting the bin origin, particularly when a possible mode depends on one or two bars.</p></li><li><p>Use an automatic rule such as Freedman&#8211;Diaconis as a starting point, and work from there</p></li><li><p>Compare the result with an ECDF, density estimate, rug plot or the known geological groupings.</p></li></ol><p>Known categories are especially important.</p><p>If formation labels are available, plotting each formation separately may reveal that two overlapping geological populations produce only one broad peak in the combined data.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/p/why-a-histogram-can-show-you-the?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/p/why-a-histogram-can-show-you-the?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share</span></a></p><h2><strong>The Histogram Is Still Useful</strong></h2><p>A histogram remains one of the fastest ways to obtain a first impression of a continuous variable. It can reveal skewness, unusual tails, gaps and possible modes in a form that most readers understand immediately.</p><p>However, the danger begins when we forget that its shape depends partly on decisions that we make with the parameters. Bin count controls how much structure is resolved. Bin position determines which observations are grouped together.</p><p>Neither the bin count nor the bin position comes from the underlying data, but both can change how we interpret the final plot.</p><p><em>The histogram&#8217;s blind spot isn&#8217;t unique to histograms. Every standard chart compresses the data somehow, and each has a point where the thing it compresses away is the thing you needed to see. Over on Substack I walk through nine common scientific plots and the single blind spot that trips each one up, from scatter plots and boxplots to correlation matrices and PCA.</em></p><p><em><a href="/__u/andymcdonaldgeo.substack.com/p/9-scientific-data-plots-and-when">&#8594; 9 Scientific Data Plots and When They Fail</a></em></p>]]></content:encoded></item><item><title><![CDATA[9 Scientific Data Plots and When They Fail ]]></title><description><![CDATA[Every chart has a blind spot. Learn where common scientific visualisations start to hide the patterns that matter.]]></description><link>https://andymcdonaldgeo.substack.com/p/9-scientific-data-plots-and-when</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/9-scientific-data-plots-and-when</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Wed, 29 Jul 2026 20:16:47 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!CxlK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea862d-d302-429f-ba31-647d9cd1f9c7_630x420.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!CxlK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea862d-d302-429f-ba31-647d9cd1f9c7_630x420.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!CxlK!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea862d-d302-429f-ba31-647d9cd1f9c7_630x420.png 424w, /__u/substackcdn.com/image/fetch/$s_!CxlK!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea862d-d302-429f-ba31-647d9cd1f9c7_630x420.png 848w, /__u/substackcdn.com/image/fetch/$s_!CxlK!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea862d-d302-429f-ba31-647d9cd1f9c7_630x420.png 1272w, /__u/substackcdn.com/image/fetch/$s_!CxlK!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea862d-d302-429f-ba31-647d9cd1f9c7_630x420.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!CxlK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea862d-d302-429f-ba31-647d9cd1f9c7_630x420.png" width="630" height="420" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/79ea862d-d302-429f-ba31-647d9cd1f9c7_630x420.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:420,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!CxlK!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea862d-d302-429f-ba31-647d9cd1f9c7_630x420.png 424w, /__u/substackcdn.com/image/fetch/$s_!CxlK!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea862d-d302-429f-ba31-647d9cd1f9c7_630x420.png 848w, /__u/substackcdn.com/image/fetch/$s_!CxlK!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea862d-d302-429f-ba31-647d9cd1f9c7_630x420.png 1272w, /__u/substackcdn.com/image/fetch/$s_!CxlK!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F79ea862d-d302-429f-ba31-647d9cd1f9c7_630x420.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Many articles and guides on data visualisation focus on when to use certain charts and how to get the best answers from your data. A histogram for understanding data distributions, a scatter plot (crossplot in petrophysics) for analysing relationships, a heatmap for matrices. There is nothing wrong with that advice, however many of those articles overlook the situations where those charts are not the best choice.</p><p>All data visualisations and chart types make trade-offs when it comes to showing data. Some charts compress your data down to bins or several key numbers. For example, boxplots compress a data distribution into five numbers so you can easily compare twenty groups at a glance. The trouble begins when the thing your data needs to say is exactly the thing the chart compresses away.</p><p>Here, we will look at nine common data plots and one aspect where each one starts to fail. The examples have a geological and petrophysical flavour, but the same issues apply across engineering, data science and any discipline where data needs to be visualised.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><h2><strong>1. Scatter Plots and Overplotting</strong></h2><p>Scatter plots (also known as crossplots in geoscience) are one of the most common charts in science and data analysis. They allow us to easily compare two variables without needing to calculate a summary statistic first.</p><p>For small or moderately sized datasets, they work extremely well. We can identify trends, groups of data, data gaps, outliers and changes in variance.</p><p>However, the problem begins when the number of points being displayed on these plots increases.</p><p>Once points start landing on top of one another, the scatter plot no longer shows every observation clearly. Instead, it starts to turn into a structureless blob with points around the edges only being easily identifiable.</p><p>A region containing 50 points may look almost identical to one containing 5,000 points. Dense areas become solid blocks, while clusters and secondary trends can become indistinguishable due tooverlapping markers.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!x0EV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff551b21f-9944-4050-8c68-834611843b36_630x330.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!x0EV!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff551b21f-9944-4050-8c68-834611843b36_630x330.png 424w, /__u/substackcdn.com/image/fetch/$s_!x0EV!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff551b21f-9944-4050-8c68-834611843b36_630x330.png 848w, /__u/substackcdn.com/image/fetch/$s_!x0EV!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff551b21f-9944-4050-8c68-834611843b36_630x330.png 1272w, /__u/substackcdn.com/image/fetch/$s_!x0EV!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff551b21f-9944-4050-8c68-834611843b36_630x330.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!x0EV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff551b21f-9944-4050-8c68-834611843b36_630x330.png" width="630" height="330" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f551b21f-9944-4050-8c68-834611843b36_630x330.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:330,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!x0EV!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff551b21f-9944-4050-8c68-834611843b36_630x330.png 424w, /__u/substackcdn.com/image/fetch/$s_!x0EV!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff551b21f-9944-4050-8c68-834611843b36_630x330.png 848w, /__u/substackcdn.com/image/fetch/$s_!x0EV!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff551b21f-9944-4050-8c68-834611843b36_630x330.png 1272w, /__u/substackcdn.com/image/fetch/$s_!x0EV!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff551b21f-9944-4050-8c68-834611843b36_630x330.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Two scatter plots showing synthetic porosity and permeability data. The first contains 800 points and shows the underlying structure clearly. The second contains 60,000 points and has collapsed into a dense cloud.</p><p>This can be particularly dangerous because the resulting chart still looks valid. It has labelled axes, sensible markers and maybe even a fitted trend line. There is nothing obvious to tell the reader that a large amount of information is hidden.</p><p>Transparency and reducing point size can help, especially when the overlap is moderate. However, there comes a point where adjusting marker properties is no longer enough.</p><p>At that stage, alternatives such as hexbin plots, two-dimensional histograms, density contours or careful sampling may give a more honest view of the data.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!uVXd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98572b7a-ef78-4257-b7f1-565c6e19bb03_630x523.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!uVXd!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98572b7a-ef78-4257-b7f1-565c6e19bb03_630x523.png 424w, /__u/substackcdn.com/image/fetch/$s_!uVXd!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98572b7a-ef78-4257-b7f1-565c6e19bb03_630x523.png 848w, /__u/substackcdn.com/image/fetch/$s_!uVXd!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98572b7a-ef78-4257-b7f1-565c6e19bb03_630x523.png 1272w, /__u/substackcdn.com/image/fetch/$s_!uVXd!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98572b7a-ef78-4257-b7f1-565c6e19bb03_630x523.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!uVXd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98572b7a-ef78-4257-b7f1-565c6e19bb03_630x523.png" width="630" height="523" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/98572b7a-ef78-4257-b7f1-565c6e19bb03_630x523.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:523,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!uVXd!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98572b7a-ef78-4257-b7f1-565c6e19bb03_630x523.png 424w, /__u/substackcdn.com/image/fetch/$s_!uVXd!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98572b7a-ef78-4257-b7f1-565c6e19bb03_630x523.png 848w, /__u/substackcdn.com/image/fetch/$s_!uVXd!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98572b7a-ef78-4257-b7f1-565c6e19bb03_630x523.png 1272w, /__u/substackcdn.com/image/fetch/$s_!uVXd!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98572b7a-ef78-4257-b7f1-565c6e19bb03_630x523.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>20,000 points as a hexbin plot, which allows a secondary group of points to show through.</p><p>The important question here is not simply whether all the observations have been plotted, but rather whether the reader can still see where those observations are concentrated.</p><p><em>The way this deteriorates at scale, and the alternatives worth reaching for, are covered in more detail here -&gt; <a href="/__u/andymcdonaldgeo.substack.com/p/what-your-scatter-plot-stops-showing">What Your Scatter Plot Stops Showing You Once It Gets Crowded</a></em></p><h2><strong>2. Histograms and Bin Sensitivity</strong></h2><p>A histogram divides a continuous variable into multiple bins and counts how many observations fall into each one. It may seem like a straightforward process, but the resulting shape of the histogram will heavily depend on how those bins are chosen.</p><p>If you use too few bins then genuine features may be smoothed away. This could lead to the loss of information. For example, a bimodal distribution, which may represent two different rock types or experimental populations may appear as one broad peak.</p><p>If you use too many bins, the random variation starts to look like meaningful structure and the distribution becomes a noisy group of peaks and gaps.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!StRF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24db116a-5aa6-43a3-a69f-829b8d255421_630x272.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!StRF!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24db116a-5aa6-43a3-a69f-829b8d255421_630x272.png 424w, /__u/substackcdn.com/image/fetch/$s_!StRF!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24db116a-5aa6-43a3-a69f-829b8d255421_630x272.png 848w, /__u/substackcdn.com/image/fetch/$s_!StRF!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24db116a-5aa6-43a3-a69f-829b8d255421_630x272.png 1272w, /__u/substackcdn.com/image/fetch/$s_!StRF!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24db116a-5aa6-43a3-a69f-829b8d255421_630x272.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!StRF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24db116a-5aa6-43a3-a69f-829b8d255421_630x272.png" width="630" height="272" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/24db116a-5aa6-43a3-a69f-829b8d255421_630x272.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:272,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!StRF!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24db116a-5aa6-43a3-a69f-829b8d255421_630x272.png 424w, /__u/substackcdn.com/image/fetch/$s_!StRF!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24db116a-5aa6-43a3-a69f-829b8d255421_630x272.png 848w, /__u/substackcdn.com/image/fetch/$s_!StRF!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24db116a-5aa6-43a3-a69f-829b8d255421_630x272.png 1272w, /__u/substackcdn.com/image/fetch/$s_!StRF!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24db116a-5aa6-43a3-a69f-829b8d255421_630x272.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The same synthetic bimodal porosity dataset shown using 6, 20 and 80 bins. The first appears to contain one broad population, the second reveals two populations, and the third produces a noisy comb-like pattern.</p><p>There is no universally correct number of bins. However, there are rules such as Sturges, Scott and Freedman-Diaconis that can provide useful starting points to decide an appropriate number of bins. But, they don&#8217;t remove the need to inspect the result. Different rules are based on different assumptions and may produce very different charts.</p><p>This means that bin choice should not be treated as a minor plotting setting as it can influence the conclusion we draw from the data.</p><p>A useful habit that I have got into is to inspect the histogram using several different bin widths.</p><p>Kernel density estimates can provide another view of the data distribution, although they can introduce their own decisions around bandwidth and smoothing parameters. The example below shows a combination of a KDE, histogram bins and key statistics.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!ok5N!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49ae6af2-951c-4013-bf4c-bd025872f2bf_567x355.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!ok5N!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49ae6af2-951c-4013-bf4c-bd025872f2bf_567x355.png 424w, /__u/substackcdn.com/image/fetch/$s_!ok5N!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49ae6af2-951c-4013-bf4c-bd025872f2bf_567x355.png 848w, /__u/substackcdn.com/image/fetch/$s_!ok5N!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49ae6af2-951c-4013-bf4c-bd025872f2bf_567x355.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ok5N!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49ae6af2-951c-4013-bf4c-bd025872f2bf_567x355.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!ok5N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49ae6af2-951c-4013-bf4c-bd025872f2bf_567x355.png" width="567" height="355" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/49ae6af2-951c-4013-bf4c-bd025872f2bf_567x355.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:355,&quot;width&quot;:567,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!ok5N!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49ae6af2-951c-4013-bf4c-bd025872f2bf_567x355.png 424w, /__u/substackcdn.com/image/fetch/$s_!ok5N!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49ae6af2-951c-4013-bf4c-bd025872f2bf_567x355.png 848w, /__u/substackcdn.com/image/fetch/$s_!ok5N!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49ae6af2-951c-4013-bf4c-bd025872f2bf_567x355.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ok5N!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F49ae6af2-951c-4013-bf4c-bd025872f2bf_567x355.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>A step-by-step walkthrough of taking a matplotlib histogram from its defaults to something more informative is here: <a href="https://medium.com/data-science-collective/from-default-to-publication-ready-transforming-matplotlib-histograms-e654c87aa50f">From Default to Publication-Ready: Transforming Matplotlib Histograms | by Andy McDonald | Data Science Collective | Medium</a></em></p><h2><strong>3. Boxplots and Hidden Distributions</strong></h2><p>Boxplots are excellent for comparing distributions across several groups. I have covered these and their alternatives several times recently, and some of the issues that may be encountered whilst using them.</p><p>Boxplots display the median, the upper and lower quartiles, whiskers and any values identified as potential outliers. This compact summary makes it possible to compare ten or twenty groups without filling the figure with individual observations.</p><p>However, the compression of that data into a succinct summary comes at a cost.</p><p>Very different distributions can produce similar boxplots. A normal distribution, a uniform distribution and a bimodal distribution can all share approximately the same median and interquartile range.</p><p>When shown as boxplots, they look very similar to each other.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!48-t!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6298d75e-e5b1-44c5-b828-6d535ff908e6_630x270.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!48-t!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6298d75e-e5b1-44c5-b828-6d535ff908e6_630x270.png 424w, /__u/substackcdn.com/image/fetch/$s_!48-t!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6298d75e-e5b1-44c5-b828-6d535ff908e6_630x270.png 848w, /__u/substackcdn.com/image/fetch/$s_!48-t!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6298d75e-e5b1-44c5-b828-6d535ff908e6_630x270.png 1272w, /__u/substackcdn.com/image/fetch/$s_!48-t!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6298d75e-e5b1-44c5-b828-6d535ff908e6_630x270.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!48-t!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6298d75e-e5b1-44c5-b828-6d535ff908e6_630x270.png" width="630" height="270" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6298d75e-e5b1-44c5-b828-6d535ff908e6_630x270.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:270,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!48-t!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6298d75e-e5b1-44c5-b828-6d535ff908e6_630x270.png 424w, /__u/substackcdn.com/image/fetch/$s_!48-t!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6298d75e-e5b1-44c5-b828-6d535ff908e6_630x270.png 848w, /__u/substackcdn.com/image/fetch/$s_!48-t!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6298d75e-e5b1-44c5-b828-6d535ff908e6_630x270.png 1272w, /__u/substackcdn.com/image/fetch/$s_!48-t!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6298d75e-e5b1-44c5-b828-6d535ff908e6_630x270.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Three similar boxplots shown beside their underlying observations. The raw data reveals normal, bimodal and uniform distributions.</p><p>This matters because the missing structure may be scientifically important.</p><p>A bimodal distribution often suggests that two populations have been combined. In a petrophysical dataset, this could mean that two facies or depositional settings are being treated as one group. In another discipline, it might indicate two experimental regimes or two different responses within a population.</p><p>The boxplot can&#8217;t show this because it wasn&#8217;t originally designed to describe the detailed shape of a distribution. This doesn&#8217;t mean that we should ignore them or drop them entirely from our toolkit. They remain useful when our primary goal is to compare medians, spreads and potential outliers across many groups.</p><p><em>The problem, and some practical alternatives, are covered in more detail here: <strong><a href="/__u/andymcdonaldgeo.substack.com/p/boxplots-are-hiding-your-data-heres">Boxplots Are Hiding Your Data. Here&#8217;s a Simple Fix</a></strong></em></p><p>However, problems can come up when we interpret them as complete descriptions of the underlying data.</p><p>There are a number of alternatives that can be used to reveal more about the data distribution including violin plots, strip plots and raincloud plots Another simple option is to overlay the individual data points on top of the boxplot, particularly when the sample size is manageable. However, my favourite recently is the raincloud plot that can be seen below.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!1c3I!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ae4b11-c4e5-48e3-85fa-ffa6495095c8_567x425.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!1c3I!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ae4b11-c4e5-48e3-85fa-ffa6495095c8_567x425.png 424w, /__u/substackcdn.com/image/fetch/$s_!1c3I!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ae4b11-c4e5-48e3-85fa-ffa6495095c8_567x425.png 848w, /__u/substackcdn.com/image/fetch/$s_!1c3I!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ae4b11-c4e5-48e3-85fa-ffa6495095c8_567x425.png 1272w, /__u/substackcdn.com/image/fetch/$s_!1c3I!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ae4b11-c4e5-48e3-85fa-ffa6495095c8_567x425.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!1c3I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ae4b11-c4e5-48e3-85fa-ffa6495095c8_567x425.png" width="567" height="425" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/94ae4b11-c4e5-48e3-85fa-ffa6495095c8_567x425.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:425,&quot;width&quot;:567,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!1c3I!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ae4b11-c4e5-48e3-85fa-ffa6495095c8_567x425.png 424w, /__u/substackcdn.com/image/fetch/$s_!1c3I!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ae4b11-c4e5-48e3-85fa-ffa6495095c8_567x425.png 848w, /__u/substackcdn.com/image/fetch/$s_!1c3I!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ae4b11-c4e5-48e3-85fa-ffa6495095c8_567x425.png 1272w, /__u/substackcdn.com/image/fetch/$s_!1c3I!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ae4b11-c4e5-48e3-85fa-ffa6495095c8_567x425.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>You can find out how to create raincloud plots in several easy steps here: <strong><a href="/__u/andymcdonaldgeo.substack.com/p/from-boxplot-to-raincloud-building">From Boxplot to Raincloud: Building a Better Distribution Plot in Python</a></strong></p><h2><strong>4. Heatmaps and Information Overload</strong></h2><p>Heatmaps are often used to display a large number of values within a compact space and be visually appealing to look at. Essentially, they work well when the grid is reasonably small and the colour scale has a clear meaning. A correlation matrix containing eight variables, for example, can be easy to inspect, however, if that matrix were to grow to 100 variables, then readability breaks down.</p><p>A heatmap containing dozens of rows and columns may technically display every value, but that does not mean the information can be interpreted. Individual cells become too small, labels can become crowded and difficult to read, and subtle differences in colour can become difficult to distinguish.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!E7HV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ab1a3cf-c6c6-41de-8959-bfebdbeba04d_630x429.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!E7HV!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ab1a3cf-c6c6-41de-8959-bfebdbeba04d_630x429.png 424w, /__u/substackcdn.com/image/fetch/$s_!E7HV!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ab1a3cf-c6c6-41de-8959-bfebdbeba04d_630x429.png 848w, /__u/substackcdn.com/image/fetch/$s_!E7HV!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ab1a3cf-c6c6-41de-8959-bfebdbeba04d_630x429.png 1272w, /__u/substackcdn.com/image/fetch/$s_!E7HV!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ab1a3cf-c6c6-41de-8959-bfebdbeba04d_630x429.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!E7HV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ab1a3cf-c6c6-41de-8959-bfebdbeba04d_630x429.png" width="630" height="429" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9ab1a3cf-c6c6-41de-8959-bfebdbeba04d_630x429.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:429,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!E7HV!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ab1a3cf-c6c6-41de-8959-bfebdbeba04d_630x429.png 424w, /__u/substackcdn.com/image/fetch/$s_!E7HV!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ab1a3cf-c6c6-41de-8959-bfebdbeba04d_630x429.png 848w, /__u/substackcdn.com/image/fetch/$s_!E7HV!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ab1a3cf-c6c6-41de-8959-bfebdbeba04d_630x429.png 1272w, /__u/substackcdn.com/image/fetch/$s_!E7HV!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ab1a3cf-c6c6-41de-8959-bfebdbeba04d_630x429.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A 40 by 28 annotated heatmap where the labels overlap and individual values are difficult to interpret.</p><p>At this point, the heatmap becomes a block of patchwork coloured boxes rather than a useful chart. The deeper problem is often that the heatmap has been created without a specific question in mind. Displaying every sample against every feature may feel like it is serving a purpose, but it is left to the reader to work out what they are supposed to notice.</p><p>Sorting or clustering the rows and columns can reveal structure and reducing the number of variables may also help. In some cases, a focused heatmap showing only the most important features communicates far more than the complete matrix.</p><p>The colour scale also matters. If we choose a poorly suited colourscale , the we could inadvertently exaggerate small differences or hide important ones. Diverging colour maps should normally be centred on a meaningful reference value, such as zero in a correlation matrix.</p><p>A heatmap should also help answer a question. In the example below, we are trying to answer the questions &#8220;What wells have complete logging sets and what curves are missing and where&#8221;. When the plot tries to display everything at once, it often answers nothing particularly well.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!4Yqp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedb850b8-f9a9-4269-88fb-4352cfd6bfa9_567x429.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!4Yqp!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedb850b8-f9a9-4269-88fb-4352cfd6bfa9_567x429.png 424w, /__u/substackcdn.com/image/fetch/$s_!4Yqp!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedb850b8-f9a9-4269-88fb-4352cfd6bfa9_567x429.png 848w, /__u/substackcdn.com/image/fetch/$s_!4Yqp!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedb850b8-f9a9-4269-88fb-4352cfd6bfa9_567x429.png 1272w, /__u/substackcdn.com/image/fetch/$s_!4Yqp!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedb850b8-f9a9-4269-88fb-4352cfd6bfa9_567x429.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!4Yqp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedb850b8-f9a9-4269-88fb-4352cfd6bfa9_567x429.png" width="567" height="429" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/edb850b8-f9a9-4269-88fb-4352cfd6bfa9_567x429.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:429,&quot;width&quot;:567,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!4Yqp!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedb850b8-f9a9-4269-88fb-4352cfd6bfa9_567x429.png 424w, /__u/substackcdn.com/image/fetch/$s_!4Yqp!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedb850b8-f9a9-4269-88fb-4352cfd6bfa9_567x429.png 848w, /__u/substackcdn.com/image/fetch/$s_!4Yqp!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedb850b8-f9a9-4269-88fb-4352cfd6bfa9_567x429.png 1272w, /__u/substackcdn.com/image/fetch/$s_!4Yqp!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fedb850b8-f9a9-4269-88fb-4352cfd6bfa9_567x429.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Example of a heatmap that answers the question of &#8220;What wells have complete logging sets and what curves are missing and where.&#8221;</figcaption></figure></div><p>Interested in creating your own interactive heatmap with Plotly? If so, check out <strong><a href="/__u/andymcdonaldgeo.substack.com/p/how-to-visualise-missing-data-with">How to Visualise Missing Data with an Interactive Plotly Heatmap</a></strong></p><h2><strong>5. Pie Charts and Too Many Categories</strong></h2><p>Pie charts show how a total is divided between categories. With three or four clearly different categories, they can provide a quick impression of the overall composition. Problems begin when the number of categories increases or when several slices are similar in size.</p><p>They are are a love it or hate it type of plot. If you search up pie chart problems on Google, you will find hundreds of articles pointing to the same problems. Some with very strong views on when not to use the plot.</p><p>Humans are not particularly good at comparing angles or areas, and it becomes harder when trying to compare multiple copies of the pie chart to see what has changed. It is easy to identify the largest slice in a simple pie chart, but becomes much harder to decide whether one category represents 11 per cent and another represents 13 per cent.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!6isI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dfbdae5-4102-4a62-b5bd-4efcd749bf01_630x316.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!6isI!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dfbdae5-4102-4a62-b5bd-4efcd749bf01_630x316.png 424w, /__u/substackcdn.com/image/fetch/$s_!6isI!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dfbdae5-4102-4a62-b5bd-4efcd749bf01_630x316.png 848w, /__u/substackcdn.com/image/fetch/$s_!6isI!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dfbdae5-4102-4a62-b5bd-4efcd749bf01_630x316.png 1272w, /__u/substackcdn.com/image/fetch/$s_!6isI!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dfbdae5-4102-4a62-b5bd-4efcd749bf01_630x316.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!6isI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dfbdae5-4102-4a62-b5bd-4efcd749bf01_630x316.png" width="630" height="316" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4dfbdae5-4102-4a62-b5bd-4efcd749bf01_630x316.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:316,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!6isI!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dfbdae5-4102-4a62-b5bd-4efcd749bf01_630x316.png 424w, /__u/substackcdn.com/image/fetch/$s_!6isI!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dfbdae5-4102-4a62-b5bd-4efcd749bf01_630x316.png 848w, /__u/substackcdn.com/image/fetch/$s_!6isI!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dfbdae5-4102-4a62-b5bd-4efcd749bf01_630x316.png 1272w, /__u/substackcdn.com/image/fetch/$s_!6isI!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4dfbdae5-4102-4a62-b5bd-4efcd749bf01_630x316.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Twelve similarly sized lithology categories shown as a pie chart and as a sorted horizontal bar chart.</p><p>The reader is then forced to move repeatedly between the chart and its legend, and similar colours become difficult to distinguish. If the pie slices are not ordered by size, then ranking the categories requires more effort than it should.</p><p>A sorted bar chart can be a better way to communicate the same values more clearly. The categories share a common baseline, making small differences much easier to compare. These can be further enhanced by including data labels to make it easier for the reader.</p><p>The pie chart does have one useful property in that it makes it visually clear that the categories form parts of a whole. However, that advantage is quickly lost when there are too many slices or when precise comparison between slices or multiple charts matters.</p><p>For a small number of distinct categories, a pie chart may be suitable. For most scientific or technical comparisons, particularly those involving several categories, a bar chart is usually easier to read.</p><h2><strong>6. Line Charts and Spaghetti Plots</strong></h2><p>A line chart is one of the clearest ways to show how a value changes across time, depth or another ordered axis.</p><p>One line is easy to follow and five lines may still be manageable. However, by the time eighteen lines are shown on the same axes, the chart has usually become a spaghetti plot.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!WLVy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1b997b1-5804-4087-b7aa-02297d5f9f51_630x352.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!WLVy!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1b997b1-5804-4087-b7aa-02297d5f9f51_630x352.png 424w, /__u/substackcdn.com/image/fetch/$s_!WLVy!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1b997b1-5804-4087-b7aa-02297d5f9f51_630x352.png 848w, /__u/substackcdn.com/image/fetch/$s_!WLVy!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1b997b1-5804-4087-b7aa-02297d5f9f51_630x352.png 1272w, /__u/substackcdn.com/image/fetch/$s_!WLVy!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1b997b1-5804-4087-b7aa-02297d5f9f51_630x352.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!WLVy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1b997b1-5804-4087-b7aa-02297d5f9f51_630x352.png" width="630" height="352" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f1b997b1-5804-4087-b7aa-02297d5f9f51_630x352.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:352,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!WLVy!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1b997b1-5804-4087-b7aa-02297d5f9f51_630x352.png 424w, /__u/substackcdn.com/image/fetch/$s_!WLVy!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1b997b1-5804-4087-b7aa-02297d5f9f51_630x352.png 848w, /__u/substackcdn.com/image/fetch/$s_!WLVy!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1b997b1-5804-4087-b7aa-02297d5f9f51_630x352.png 1272w, /__u/substackcdn.com/image/fetch/$s_!WLVy!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff1b997b1-5804-4087-b7aa-02297d5f9f51_630x352.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Eighteen synthetic well production decline curves overlaid on the same axes, with a large legend and several intersecting lines.</p><p>The problem isn&#8217;t simply that the figure looks messy, but rather that the chart can no longer support many of the tasks we expect it to perform.</p><p>We may still be able to see the overall range of values or identify a major outlier. However, following an individual series becomes difficult. Lines cross, colours repeat and the legend becomes detached from the data.</p><p>Increasing the number of colours rarely solves the problem. It often produces a more colourful version of the same confusion.</p><p>The appropriate alternative to the mess of spaghetti depends on the question being asked of the data.</p><p>We can use small multiples to show each series in a separate panel while retaining a common scale. This makes it easier to inspect individual behaviour without losing comparability.</p><p>Another option is to highlight one or two important lines and display the remaining series in the background. This works well when the aim is to compare a selected well, experiment or model against the wider population. It is often used when comparing yearly temperatures to previous years.</p><p>Grouping and summarising the series may also help. A median line with an uncertainty band can communicate the overall behaviour when individual identities are not important.</p><p>A line chart containing many series is not always wrong and it may still be useful for showing an envelope or identifying extreme cases. It begins to fail when we expect the reader to follow every line individually.</p><h2><strong>7. Correlation Matrices and Non-Linear Relationships</strong></h2><p>Correlation matrices provide a neat and compact summary of the relationships between several variables.</p><p>In most cases, the values shown on the correlation matrix are Pearson correlation coefficients. Pearson&#8217;s correlation measures the strength of a linear relationship between two variables and ranges between -1.0 and +1.0, with a value close to 0 indicating no linear relationship.</p><p>Two variables can have a strong and predictable non-linear relationship while producing a Pearson correlation close to zero.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!zpJw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ec0596f-5f2e-4541-ad3a-23c72327e4f8_630x298.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!zpJw!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ec0596f-5f2e-4541-ad3a-23c72327e4f8_630x298.png 424w, /__u/substackcdn.com/image/fetch/$s_!zpJw!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ec0596f-5f2e-4541-ad3a-23c72327e4f8_630x298.png 848w, /__u/substackcdn.com/image/fetch/$s_!zpJw!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ec0596f-5f2e-4541-ad3a-23c72327e4f8_630x298.png 1272w, /__u/substackcdn.com/image/fetch/$s_!zpJw!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ec0596f-5f2e-4541-ad3a-23c72327e4f8_630x298.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!zpJw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ec0596f-5f2e-4541-ad3a-23c72327e4f8_630x298.png" width="630" height="298" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5ec0596f-5f2e-4541-ad3a-23c72327e4f8_630x298.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:298,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!zpJw!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ec0596f-5f2e-4541-ad3a-23c72327e4f8_630x298.png 424w, /__u/substackcdn.com/image/fetch/$s_!zpJw!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ec0596f-5f2e-4541-ad3a-23c72327e4f8_630x298.png 848w, /__u/substackcdn.com/image/fetch/$s_!zpJw!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ec0596f-5f2e-4541-ad3a-23c72327e4f8_630x298.png 1272w, /__u/substackcdn.com/image/fetch/$s_!zpJw!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ec0596f-5f2e-4541-ad3a-23c72327e4f8_630x298.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A correlation matrix showing a correlation near zero between X and Y, shown beside a scatter plot that reveals a clear quadratic relationship.</p><p>Looking at the matrix above alone, we might conclude that some of the variables are unrelated. The scatter plot on the other hand shows that Y is strongly determined by X, but the relationship is curved rather than linear.</p><p>This can cause problems during exploratory analysis and feature selection. A variable may be removed because its correlation with the target is weak, even though a non-linear model could make good use of it.</p><p>Spearman correlation can detect monotonic relationships, including some curved relationships, because it works with ranks rather than raw values. However, neither Pearson nor Spearman reliably captures a relationship such as a parabola.</p><p>Other approaches, including mutual information and distance-based measures, can identify broader forms of dependence. These methods also come with assumptions and limitations, so they should not be treated as automatic replacements.</p><p>The correlation matrix is useful for screening a set of variables, however it should be supported by other plots such as scatter plots.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/p/9-scientific-data-plots-and-when?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/p/9-scientific-data-plots-and-when?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share</span></a></p><h2><strong>8. PCA Plots and Interpretation</strong></h2><p>Principal Component Analysis (PCA) is often used to reduce a dataset containing many variables into two or three dimensions.</p><p>The resulting scatter plot of the Principal Components can reveal broad clusters, outliers and changes in structure, however, these plots are also easy to over-interpret.</p><p>The first issue that you may encounter is scaling. PCA identifies directions that explain the largest amount of variance within a given set of features. If the input features use very different numerical scales, the result may be dominated by the variables with the greatest variance.</p><p>For example, a gamma ray curve measured over a range of hundreds of API units may dominate a density curve varying between approximately 2 and 3 g/cc.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!ePtK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26abe1fe-dc21-4eab-b2d2-86fa13bc3d55_630x306.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!ePtK!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26abe1fe-dc21-4eab-b2d2-86fa13bc3d55_630x306.png 424w, /__u/substackcdn.com/image/fetch/$s_!ePtK!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26abe1fe-dc21-4eab-b2d2-86fa13bc3d55_630x306.png 848w, /__u/substackcdn.com/image/fetch/$s_!ePtK!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26abe1fe-dc21-4eab-b2d2-86fa13bc3d55_630x306.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ePtK!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26abe1fe-dc21-4eab-b2d2-86fa13bc3d55_630x306.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!ePtK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26abe1fe-dc21-4eab-b2d2-86fa13bc3d55_630x306.png" width="630" height="306" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/26abe1fe-dc21-4eab-b2d2-86fa13bc3d55_630x306.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:306,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!ePtK!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26abe1fe-dc21-4eab-b2d2-86fa13bc3d55_630x306.png 424w, /__u/substackcdn.com/image/fetch/$s_!ePtK!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26abe1fe-dc21-4eab-b2d2-86fa13bc3d55_630x306.png 848w, /__u/substackcdn.com/image/fetch/$s_!ePtK!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26abe1fe-dc21-4eab-b2d2-86fa13bc3d55_630x306.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ePtK!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F26abe1fe-dc21-4eab-b2d2-86fa13bc3d55_630x306.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The same four synthetic well log curves projected using PCA before and after standardisation. The unscaled version is dominated by one feature, while the standardised version produces a different structure.</p><p>Applying standardisation to the features gives each variable a more comparable influence, but this is also a modelling decision. In some cases, the original scale may contain meaningful information, whereas in others, it simply reflects the units used for measurement.</p><p>The second issue is interpretation.</p><p>Principal Components are essentially weighted combinations of the original variables. They don&#8217;t have an inherent physical meaning, even when the resulting axes appear to separate familiar groups.</p><p>It may be tempting to assign a label of &#8220;Lithology&#8221; to PC1 and &#8220;Porosity influence&#8221; to PC2, unless it is supported by evidence and domain knowledge</p><p>Variance is also not the same as importance. A PC can explain a large proportion of the variation in the dataset whilst being unrelated to the prediction or scientific question we care about.</p><p>Finally, if we only look t a two-dimensional PCA plot we are only displaying part of the original feature space. Which may mean what appear to be nice data clusters or data overlaps may change when additional PCs are considered.</p><p>PCA plots are useful for orientation and gaining insight into the overall structure, as well as help identify unusual observations. They can become unreliable when we start treating the projected axes as measured physical properties.</p><h2><strong>9. Violin Plots and Sample Size</strong></h2><p>Violin plots combine a summary of the distribution with a kernel density estimate. Unlike boxplots, they show an estimated shape, which can reveal skewness, multiple peaks and differences in spread that would otherwise remain hidden.</p><p>However, the smooth shape of a violin plot can create a false sense of confidence in the data.</p><p>A Kernel Density Estimate (KDE) will produce a continuous curve even when it is based on only a small number of observations. A group containing nine samples can therefore produce a violin that looks just as smooth and complete as one containing five hundred.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!NUKs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08570901-46b5-4478-8b98-7628a3b2f9bf_630x286.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!NUKs!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08570901-46b5-4478-8b98-7628a3b2f9bf_630x286.png 424w, /__u/substackcdn.com/image/fetch/$s_!NUKs!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08570901-46b5-4478-8b98-7628a3b2f9bf_630x286.png 848w, /__u/substackcdn.com/image/fetch/$s_!NUKs!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08570901-46b5-4478-8b98-7628a3b2f9bf_630x286.png 1272w, /__u/substackcdn.com/image/fetch/$s_!NUKs!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08570901-46b5-4478-8b98-7628a3b2f9bf_630x286.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!NUKs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08570901-46b5-4478-8b98-7628a3b2f9bf_630x286.png" width="630" height="286" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/08570901-46b5-4478-8b98-7628a3b2f9bf_630x286.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:286,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!NUKs!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08570901-46b5-4478-8b98-7628a3b2f9bf_630x286.png 424w, /__u/substackcdn.com/image/fetch/$s_!NUKs!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08570901-46b5-4478-8b98-7628a3b2f9bf_630x286.png 848w, /__u/substackcdn.com/image/fetch/$s_!NUKs!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08570901-46b5-4478-8b98-7628a3b2f9bf_630x286.png 1272w, /__u/substackcdn.com/image/fetch/$s_!NUKs!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F08570901-46b5-4478-8b98-7628a3b2f9bf_630x286.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Two violin plots with similar smooth shapes. One is based on 9 observations and the other on 500. Strip plots beside them reveal the difference in sample size.</p><p>With a small sample size much of the apparent structure is controlled by the bandwidth used by the density estimator. A narrow bandwidth may create several peaks, whilst a wider bandwidth may merge them into one broad shape.</p><p>The reader may also have no way to tell how much data sits behind the violin unless sample sizes are reported. Much in a similar way to boxplots.</p><p>This is where raw points become particularly useful. By overlying a strip plot or by using a raincloud plot the reader can see both the estimated distribution and the observations that produced it.</p><p>For large samples, showing every point may create overplotting, so a sampling strategy or alternative density representation may be needed. For small groups, however, there is usually little reason to hide the individual observations.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/p/9-scientific-data-plots-and-when/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/p/9-scientific-data-plots-and-when/comments"><span>Leave a comment</span></a></p><h2><strong>The Pattern Underneath</strong></h2><p>These nine plots fail in different ways, but the underlying problem is similar.</p><p>Each chart relies on assumptions about what matters.</p><p>A scatter plot assumes the points remain individually visible. A histogram assumes the selected bins provide a useful representation of the distribution. A correlation matrix assumes that a coefficient can describe the relationship we care about. A PCA plot assumes that variance is a useful direction in which to view the data.</p><p>The data plot continues to render even when those assumptions are broken.</p><p>That is what makes these failures easy to miss. A crowded scatter plot and an overfilled heatmap can all look clean and publication-ready. Nothing in the plotting library warns us that the chart is hiding an important part of the story. It is down to us, as the interpreter, to understand what each chart compresses in the data and how we can get the best from them.</p><div><hr></div><p>Knowing what each chart compresses is the starting point. Getting the figure itself right before those trade-offs start to matter is the next step. Over on Substack I&#8217;ve covered six specific matplotlib changes that take a figure from functional defaults to something you&#8217;d put in a report: spine cleanup, intentional colour choices, appropriate axis scaling, gridlines that support rather than clutter, annotation, and export settings that don&#8217;t clip your labels.</p><p><a href="/__u/andymcdonaldgeo.substack.com/p/six-matplotlib-changes-that-improve">Six Matplotlib Changes That Improve Your Figures for Reporting and Publication</a></p>]]></content:encoded></item><item><title><![CDATA[What Your Scatter Plot Stops Showing You Once It Gets Crowded]]></title><description><![CDATA[Overplotting is easy to miss and easy to fix, once you stop asking your chart to show every point.]]></description><link>https://andymcdonaldgeo.substack.com/p/what-your-scatter-plot-stops-showing</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/what-your-scatter-plot-stops-showing</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Tue, 21 Jul 2026 20:50:42 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!gHjl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfd4b62-7523-4fc0-8e94-57feb3039fc7_630x358.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!gHjl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfd4b62-7523-4fc0-8e94-57feb3039fc7_630x358.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!gHjl!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfd4b62-7523-4fc0-8e94-57feb3039fc7_630x358.png 424w, /__u/substackcdn.com/image/fetch/$s_!gHjl!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfd4b62-7523-4fc0-8e94-57feb3039fc7_630x358.png 848w, /__u/substackcdn.com/image/fetch/$s_!gHjl!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfd4b62-7523-4fc0-8e94-57feb3039fc7_630x358.png 1272w, /__u/substackcdn.com/image/fetch/$s_!gHjl!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfd4b62-7523-4fc0-8e94-57feb3039fc7_630x358.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!gHjl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfd4b62-7523-4fc0-8e94-57feb3039fc7_630x358.png" width="630" height="358" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1cfd4b62-7523-4fc0-8e94-57feb3039fc7_630x358.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:358,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!gHjl!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfd4b62-7523-4fc0-8e94-57feb3039fc7_630x358.png 424w, /__u/substackcdn.com/image/fetch/$s_!gHjl!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfd4b62-7523-4fc0-8e94-57feb3039fc7_630x358.png 848w, /__u/substackcdn.com/image/fetch/$s_!gHjl!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfd4b62-7523-4fc0-8e94-57feb3039fc7_630x358.png 1272w, /__u/substackcdn.com/image/fetch/$s_!gHjl!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfd4b62-7523-4fc0-8e94-57feb3039fc7_630x358.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Imagine that you have just finished building a regression model to predict missing well log data You then check how well it has performed on an actual vs predicted scatter plot (also known as a crossplot in petrophysics).</p><p>The result may look convincing, with a dense cloud of data points following the trend of the one-to-one line and hugging close to it. However, a large portion of those points are likely sitting directly on top of each other. Therefore, what may look like a reassuring cloud to begin with, may be systematically wrong across a whole range of values. From this chart alone and without further tweaks to the plot, it can become difficult to tell.</p><p>This is a case of overplotting, and it&#8217;s one of the common ways that a simple scatter plot can become an unreadable mess.</p><p>This does not mean that the data and the plot are entirely usless. There are a few fixes we can make to the scatter plot to help clear things up, such as switching to other styles of visualisation within the bounds of the scatter plot in order to understand the underlying relationship between the actual and predicted data.</p><p>We will see some of those fixes in this article. Also, one thing to note, even though this article focuses on actual vs predicted data, the reuls and ideas can equally apply to any scatter plot.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>Setting Up a Stand-In Dataset</strong></h2><p>To illustrate overplotting we will create some synthetic well data that will allow us to display a predicted versus measured scatter plot (also known as a crossplot in petrophysics, hence the name <code>make_crossplot_data</code>). Here most of the points will fall in a broad cloud along the one-to-one line, with a tighter secondary concentration of data points that we will deliberately bury inside it.</p><pre><code><span>import numpy as np
import matplotlib.pyplot as plt

rng = np.random.default_rng(42)
def make_crossplot_data(n):
    n_main = int(n * 0.7)
    n_knot = n - n_main
    measured_main = rng.normal(0.15, 0.05, n_main)
    measured_knot = rng.normal(0.22, 0.012, n_knot)   # the hidden concentration
    measured = np.concatenate([measured_main, measured_knot])
    predicted = measured + rng.normal(0, 0.02, n)      # model error around 1:1
    measured = np.clip(measured, 0, 0.35)
    predicted = np.clip(predicted, 0, 0.35)
    return measured, predicted

LIMS = (0, 0.35)

def style_axes(ax):
    ax.set_xlim(*LIMS)
    ax.set_ylim(*LIMS)
    ax.set_xlabel(&#8221;Measured porosity&#8221;)
    ax.set_ylabel(&#8221;Predicted porosity&#8221;)
    ax.plot(LIMS, LIMS, color=&#8221;0.4&#8221;, lw=1, ls=&#8221;--&#8221;, zorder=0)  # 1:1 line
    ax.set_aspect(&#8221;equal&#8221;)</span></code></pre><p>With the data generated, and some plotting defaults set up, we can create our first scatter plot, one that many of us will jump to after carrying out a continuous variable prediction.</p><pre><code><span>measured, predicted = make_crossplot_data(20000)

fig, ax = plt.subplots(figsize=(6, 6))
ax.scatter(measured, predicted, s=10, color=&#8221;#1f3b57&#8221;)
style_axes(ax)
plt.show()</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!id5P!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04be7884-ba84-4cc2-b454-c9aef38e524d_630x616.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!id5P!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04be7884-ba84-4cc2-b454-c9aef38e524d_630x616.png 424w, /__u/substackcdn.com/image/fetch/$s_!id5P!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04be7884-ba84-4cc2-b454-c9aef38e524d_630x616.png 848w, /__u/substackcdn.com/image/fetch/$s_!id5P!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04be7884-ba84-4cc2-b454-c9aef38e524d_630x616.png 1272w, /__u/substackcdn.com/image/fetch/$s_!id5P!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04be7884-ba84-4cc2-b454-c9aef38e524d_630x616.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!id5P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04be7884-ba84-4cc2-b454-c9aef38e524d_630x616.png" width="630" height="616" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/04be7884-ba84-4cc2-b454-c9aef38e524d_630x616.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:616,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Setting Up a Stand-In Dataset. A predicted vs measured crossplot with 20,000 points forming a solid dark cloud along the one-to-one line, with no visible internal structure&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Setting Up a Stand-In Dataset. A predicted vs measured crossplot with 20,000 points forming a solid dark cloud along the one-to-one line, with no visible internal structure" title="Setting Up a Stand-In Dataset. A predicted vs measured crossplot with 20,000 points forming a solid dark cloud along the one-to-one line, with no visible internal structure" srcset="/__u/substackcdn.com/image/fetch/$s_!id5P!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04be7884-ba84-4cc2-b454-c9aef38e524d_630x616.png 424w, /__u/substackcdn.com/image/fetch/$s_!id5P!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04be7884-ba84-4cc2-b454-c9aef38e524d_630x616.png 848w, /__u/substackcdn.com/image/fetch/$s_!id5P!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04be7884-ba84-4cc2-b454-c9aef38e524d_630x616.png 1272w, /__u/substackcdn.com/image/fetch/$s_!id5P!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04be7884-ba84-4cc2-b454-c9aef38e524d_630x616.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Setting Up a Stand-In Dataset. A predicted vs measured crossplot with 20,000 points forming a solid dark cloud along the one-to-one line, with no visible internal strucure</figcaption></figure></div><p>On first looking at this chart we may think this looks like a decent prediction for porosity. However, most of the points closer to the 1-to-1 line are actually sitting on top of each other.</p><h2><strong>Watching a Scatter Plot Fail</strong></h2><p>The easiest way to visualise overplotting is to watch it happen.</p><p>To do this, we will draw the same distribution at increasing sample sizes and see where the scatter plot starts to break down.</p><p>First, we start small with a hundred points. At this level, we can see that the scatter plot is doing it&#8217;s job well. We are able to see individual observations, judge the spread, and pick out anything unusual.</p><pre><code><span>m100, p100 = make_crossplot_data(100)

fig, ax = plt.subplots(figsize=(6, 6))
ax.scatter(m100, p100, s=25, color=&#8221;#1f3b57&#8221;)
style_axes(ax)
plt.show()</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!lPHy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a704156-1de2-4b28-8ccf-5e783bbd94bd_630x616.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!lPHy!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a704156-1de2-4b28-8ccf-5e783bbd94bd_630x616.png 424w, /__u/substackcdn.com/image/fetch/$s_!lPHy!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a704156-1de2-4b28-8ccf-5e783bbd94bd_630x616.png 848w, /__u/substackcdn.com/image/fetch/$s_!lPHy!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a704156-1de2-4b28-8ccf-5e783bbd94bd_630x616.png 1272w, /__u/substackcdn.com/image/fetch/$s_!lPHy!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a704156-1de2-4b28-8ccf-5e783bbd94bd_630x616.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!lPHy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a704156-1de2-4b28-8ccf-5e783bbd94bd_630x616.png" width="630" height="616" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5a704156-1de2-4b28-8ccf-5e783bbd94bd_630x616.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:616,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!lPHy!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a704156-1de2-4b28-8ccf-5e783bbd94bd_630x616.png 424w, /__u/substackcdn.com/image/fetch/$s_!lPHy!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a704156-1de2-4b28-8ccf-5e783bbd94bd_630x616.png 848w, /__u/substackcdn.com/image/fetch/$s_!lPHy!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a704156-1de2-4b28-8ccf-5e783bbd94bd_630x616.png 1272w, /__u/substackcdn.com/image/fetch/$s_!lPHy!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a704156-1de2-4b28-8ccf-5e783bbd94bd_630x616.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">A scatter plot working as intended at around 100 points, each observation clearly distinguishable with visible spread.</figcaption></figure></div><p>If we push it to a thousand points, the chart still holds up, but the densest region is starting to merge together, which makes it difficult to count the individual observations in there. However, the overall shape of the data remains readable, for the most part.</p><pre><code><span>m1k, p1k = make_crossplot_data(1000)

fig, ax = plt.subplots(figsize=(6, 6))
ax.scatter(m1k, p1k, s=15, color=&#8221;#1f3b57&#8221;)
style_axes(ax)
plt.show()</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!IHhQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff449a4af-4bcb-411a-872d-fe8723f77d3b_630x616.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!IHhQ!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff449a4af-4bcb-411a-872d-fe8723f77d3b_630x616.png 424w, /__u/substackcdn.com/image/fetch/$s_!IHhQ!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff449a4af-4bcb-411a-872d-fe8723f77d3b_630x616.png 848w, /__u/substackcdn.com/image/fetch/$s_!IHhQ!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff449a4af-4bcb-411a-872d-fe8723f77d3b_630x616.png 1272w, /__u/substackcdn.com/image/fetch/$s_!IHhQ!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff449a4af-4bcb-411a-872d-fe8723f77d3b_630x616.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!IHhQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff449a4af-4bcb-411a-872d-fe8723f77d3b_630x616.png" width="630" height="616" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f449a4af-4bcb-411a-872d-fe8723f77d3b_630x616.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:616,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!IHhQ!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff449a4af-4bcb-411a-872d-fe8723f77d3b_630x616.png 424w, /__u/substackcdn.com/image/fetch/$s_!IHhQ!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff449a4af-4bcb-411a-872d-fe8723f77d3b_630x616.png 848w, /__u/substackcdn.com/image/fetch/$s_!IHhQ!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff449a4af-4bcb-411a-872d-fe8723f77d3b_630x616.png 1272w, /__u/substackcdn.com/image/fetch/$s_!IHhQ!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff449a4af-4bcb-411a-872d-fe8723f77d3b_630x616.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The same distribution at 1,000 points, with the central region beginning to merge into a solid mass</figcaption></figure></div><p>Now, if we increase it to twenty thousand points, the chart fails.</p><p>The centre becomes a uniform dark blue blob with many overlapping observations. And as a result, any structure that existed within that blob has now become invisible.</p><pre><code><span>fig, ax = plt.subplots(figsize=(6, 6))
ax.scatter(measured, predicted, s=10, color=&#8221;#1f3b57&#8221;)
style_axes(ax)
plt.show()</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!P3LO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba97be1b-2833-4092-a49d-35428cffb4eb_630x616.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!P3LO!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba97be1b-2833-4092-a49d-35428cffb4eb_630x616.png 424w, /__u/substackcdn.com/image/fetch/$s_!P3LO!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba97be1b-2833-4092-a49d-35428cffb4eb_630x616.png 848w, /__u/substackcdn.com/image/fetch/$s_!P3LO!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba97be1b-2833-4092-a49d-35428cffb4eb_630x616.png 1272w, /__u/substackcdn.com/image/fetch/$s_!P3LO!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba97be1b-2833-4092-a49d-35428cffb4eb_630x616.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!P3LO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba97be1b-2833-4092-a49d-35428cffb4eb_630x616.png" width="630" height="616" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ba97be1b-2833-4092-a49d-35428cffb4eb_630x616.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:616,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!P3LO!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba97be1b-2833-4092-a49d-35428cffb4eb_630x616.png 424w, /__u/substackcdn.com/image/fetch/$s_!P3LO!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba97be1b-2833-4092-a49d-35428cffb4eb_630x616.png 848w, /__u/substackcdn.com/image/fetch/$s_!P3LO!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba97be1b-2833-4092-a49d-35428cffb4eb_630x616.png 1272w, /__u/substackcdn.com/image/fetch/$s_!P3LO!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba97be1b-2833-4092-a49d-35428cffb4eb_630x616.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">20,000 points rendered as a solid dark mass, with only the sparse edges of the distribution still showing individual observations</figcaption></figure></div><h2><strong>Why This Happens</strong></h2><p>Each observation gets its own visible mark on the scatter plot. Once two markers occupy the same pixels, the second one adds no information and sits on top of it. The hundredth point on the same position adds none either. Dense regions start to saturate early and then hide everything that arrives afterwards, which means the regions where most of your data lives are precisely the regions you can see least clearly. Meanwhile, if we have a single stray outlier at the edge of the plot, it remains perfectly visible and can draw far more attention from the reader than its importance deserves.</p><p>There is no universal point count where this issue kicks in and it will depend on the size of the markers, figure dimensions, screen resolution and, most of all, how many data points there are concentrated close together.</p><p>A tight bivariate relationship can saturate with a few thousand points, whilst a more diffuse one might last far longer before it becomes saturated and we lose any meaningful pattern or structure.</p><p>What we need to watch for is: if you can no longer distinguish individual observations in the densest region,the chart can no longer answer the question we initially set out to answer.</p><h2><strong>Temporary Fix: Transparency and Marker Size</strong></h2><p>The usual first response to improving this is to make the markers semi-transparent. Overlapping points then stack visually, so denser regions render darker and you get a crude signal for data density.</p><pre><code><span>fig, ax = plt.subplots(figsize=(6, 6))
ax.scatter(measured, predicted, s=10, color=&#8221;#1f3b57&#8221;, alpha=0.1)
style_axes(ax)
plt.show()</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!pIIO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feedf8db6-3851-44a0-8c63-8f12cf91fee8_630x616.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!pIIO!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feedf8db6-3851-44a0-8c63-8f12cf91fee8_630x616.png 424w, /__u/substackcdn.com/image/fetch/$s_!pIIO!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feedf8db6-3851-44a0-8c63-8f12cf91fee8_630x616.png 848w, /__u/substackcdn.com/image/fetch/$s_!pIIO!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feedf8db6-3851-44a0-8c63-8f12cf91fee8_630x616.png 1272w, /__u/substackcdn.com/image/fetch/$s_!pIIO!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feedf8db6-3851-44a0-8c63-8f12cf91fee8_630x616.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!pIIO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feedf8db6-3851-44a0-8c63-8f12cf91fee8_630x616.png" width="630" height="616" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eedf8db6-3851-44a0-8c63-8f12cf91fee8_630x616.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:616,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!pIIO!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feedf8db6-3851-44a0-8c63-8f12cf91fee8_630x616.png 424w, /__u/substackcdn.com/image/fetch/$s_!pIIO!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feedf8db6-3851-44a0-8c63-8f12cf91fee8_630x616.png 848w, /__u/substackcdn.com/image/fetch/$s_!pIIO!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feedf8db6-3851-44a0-8c63-8f12cf91fee8_630x616.png 1272w, /__u/substackcdn.com/image/fetch/$s_!pIIO!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feedf8db6-3851-44a0-8c63-8f12cf91fee8_630x616.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The 20,000-point dataset with alpha set to 0.1, revealing gradations of darkness where the solid blob used to be.</figcaption></figure></div><p>This can help for a while. Ten overlapping points at ten per cent opacity already render as effectively solid, so the darkness scale saturates quickly. You can, however, distinguish sparse data from dense data, but not dense from very dense.</p><p>To buy us more headroom, we can shrink the markers and reduce the impact of overlapping data points. This may work for datasets in the low thousands. However, beyond that, both tricks run into the same issue: a finite number of pixels and far more observations than pixels to put them in.</p><h2><strong>Sub-Sampling: Honest but Lossy</strong></h2><p>Another option is to plot a random sample of the data rather than the full dataset. Draw a couple of thousand points from a couple of hundred thousand and the scatter plot becomes readable again. This might mean we are only displaying 10% of the full data volume.</p><p>For quick exploration this may be a reasonable move, however, the cost is that you&#8217;ve thrown away most of your data to make the chart legible.</p><p>Rare behaviour, small clusters and sparse outliers may easily be removed and become less prominent when the chart is drawn. Sampling is a workaround for the display problem, rather than a solution to it.</p><h2><strong>The Real Fix: Ask About Density Instead</strong></h2><p>Everything so far tries to preserve the original question, &#8220;where are my individual observations&#8221;, in a situation where it can no longer be answered. The better move is to change the question to &#8220;where is my data concentrated&#8221;.</p><p>This is what a hexbin plot does.</p><p>The hexbin plot divides the plotting area into hexagonal cells, counts the observations falling into each one, and maps the count to colour. No overlap is possible, because nothing is drawn per observation. Dense regions are no longer hidden, instead they become the brightest cells on the chart.</p><pre><code><span>fig, ax = plt.subplots(figsize=(6, 6))
hb = ax.hexbin(measured, predicted, gridsize=40, cmap=&#8221;viridis&#8221;,
               mincnt=1, extent=(*LIMS, *LIMS))
ax.plot(LIMS, LIMS, color=&#8221;0.7&#8221;, lw=1, ls=&#8221;--&#8221;, zorder=3)
ax.set_xlim(*LIMS); ax.set_ylim(*LIMS)
ax.set_xlabel(&#8221;Measured porosity&#8221;); ax.set_ylabel(&#8221;Predicted porosity&#8221;)
ax.set_aspect(&#8221;equal&#8221;)
fig.colorbar(hb, ax=ax, label=&#8221;count&#8221;, shrink=0.8)
plt.show()</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!NvE7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c4496d-aea5-4774-ab2d-cf2e886d93f5_630x523.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!NvE7!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c4496d-aea5-4774-ab2d-cf2e886d93f5_630x523.png 424w, /__u/substackcdn.com/image/fetch/$s_!NvE7!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c4496d-aea5-4774-ab2d-cf2e886d93f5_630x523.png 848w, /__u/substackcdn.com/image/fetch/$s_!NvE7!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c4496d-aea5-4774-ab2d-cf2e886d93f5_630x523.png 1272w, /__u/substackcdn.com/image/fetch/$s_!NvE7!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c4496d-aea5-4774-ab2d-cf2e886d93f5_630x523.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!NvE7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c4496d-aea5-4774-ab2d-cf2e886d93f5_630x523.png" width="630" height="523" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/05c4496d-aea5-4774-ab2d-cf2e886d93f5_630x523.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:523,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!NvE7!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c4496d-aea5-4774-ab2d-cf2e886d93f5_630x523.png 424w, /__u/substackcdn.com/image/fetch/$s_!NvE7!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c4496d-aea5-4774-ab2d-cf2e886d93f5_630x523.png 848w, /__u/substackcdn.com/image/fetch/$s_!NvE7!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c4496d-aea5-4774-ab2d-cf2e886d93f5_630x523.png 1272w, /__u/substackcdn.com/image/fetch/$s_!NvE7!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05c4496d-aea5-4774-ab2d-cf2e886d93f5_630x523.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The same 20,000 points as a hexbin plot, with the formerly solid blob resolved into graded cells, including the secondary concentration that the scatter plot hid entirely</figcaption></figure></div><p>If we look at the bright cell just above the middle of the one-to-one line, that&#8217;s the secondary concentration we built into the data at the start. This was what we lost when we displayed the vast number of points earlier. Here, the hexbin surfaces it without any special effort.</p><p>Using a hexbin plot does come with a trade-off, in that you lose individual observations, and you can no longer point at a specific well or sample. Outliers can become single faint cells that&#8217;s easy to miss.</p><p>Choosing the right bin size also matters. If the cells are too large smear then the underlying structure is not there any more, whereas cells that are too small reintroduce noise. For the core task of understanding where a large dataset really lives on the plot, then the hexbin chart can help with that.</p><h2><strong>Density Contours</strong></h2><p>Where the underlying data is continuous, kernel density estimation offers a related view.</p><p>Instead of counting points in cells, it estimates a smooth density surface and draws it as contour lines or filled bands, much like elevation contours on a map.</p><pre><code><span>from scipy.stats import gaussian_kde

xy = np.vstack([measured, predicted])
kde = gaussian_kde(xy)
xx, yy = np.mgrid[LIMS[0]:LIMS[1]:200j, LIMS[0]:LIMS[1]:200j]
density = kde(np.vstack([xx.ravel(), yy.ravel()])).reshape(xx.shape)
fig, ax = plt.subplots(figsize=(6, 6))
cf = ax.contourf(xx, yy, density, levels=10, cmap=&#8221;viridis&#8221;)
ax.set_xlim(*LIMS); ax.set_ylim(*LIMS)
ax.set_xlabel(&#8221;Measured porosity&#8221;); ax.set_ylabel(&#8221;Predicted porosity&#8221;)
ax.set_aspect(&#8221;equal&#8221;)
fig.colorbar(cf, ax=ax, label=&#8221;density&#8221;, shrink=0.8)
plt.show()</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!RYfI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2734aa10-c36a-405c-821e-5fbf0ab03449_630x523.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!RYfI!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2734aa10-c36a-405c-821e-5fbf0ab03449_630x523.png 424w, /__u/substackcdn.com/image/fetch/$s_!RYfI!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2734aa10-c36a-405c-821e-5fbf0ab03449_630x523.png 848w, /__u/substackcdn.com/image/fetch/$s_!RYfI!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2734aa10-c36a-405c-821e-5fbf0ab03449_630x523.png 1272w, /__u/substackcdn.com/image/fetch/$s_!RYfI!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2734aa10-c36a-405c-821e-5fbf0ab03449_630x523.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!RYfI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2734aa10-c36a-405c-821e-5fbf0ab03449_630x523.png" width="630" height="523" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2734aa10-c36a-405c-821e-5fbf0ab03449_630x523.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:523,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Filled KDE contours over the 20,000-point dataset, showing nested regions of increasing density&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Filled KDE contours over the 20,000-point dataset, showing nested regions of increasing density" title="Filled KDE contours over the 20,000-point dataset, showing nested regions of increasing density" srcset="/__u/substackcdn.com/image/fetch/$s_!RYfI!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2734aa10-c36a-405c-821e-5fbf0ab03449_630x523.png 424w, /__u/substackcdn.com/image/fetch/$s_!RYfI!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2734aa10-c36a-405c-821e-5fbf0ab03449_630x523.png 848w, /__u/substackcdn.com/image/fetch/$s_!RYfI!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2734aa10-c36a-405c-821e-5fbf0ab03449_630x523.png 1272w, /__u/substackcdn.com/image/fetch/$s_!RYfI!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2734aa10-c36a-405c-821e-5fbf0ab03449_630x523.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Filled KDE contours over the 20,000-point dataset, showing nested regions of increasing density</figcaption></figure></div><p>Contours are great for describing the overall shape of a distribution and for overlaying two groups on the same axes, something that can become impossible when using saturated scatter plots.</p><p>However, one thing to bear in mind is the smoothing is also the weakness. KDE can paint density into regions where no data exists, which matters if your variables have hard physical limits, such as a porosity that can&#8217;t go below zero. Treat contours as a description of shape, and check the edges against the raw data.</p><h2><strong>When You Have Millions of Points</strong></h2><p>If you truly have a large dataset, for example in the millions and beyond, tools like Datashader can be useful to explore. Rather than drawing markers at all, they aggregate the data into an image directly, computing a value per pixel. Every observation contributes to the overall picture with nothing</p><h2><strong>You Don&#8217;t Need Big Data to Have This Problem</strong></h2><p>Most discussions of overplotting treat it as a big data problem. It can also appear, and often more severely, when your values are discrete.</p><p>Well log data is a good example. A gamma ray curve recorded to the nearest whole API unit could contain the same value thousands of times across a field. On a crossplot, every one of those observations lands on exactly the same coordinates. Not nearly the same pixel, but actually the same pixel. A chart with only three thousand points can be badly overplotted if those points take a few hundred distinct value pairs, and you&#8217;ll see the same effect with any rounded measurement, integer counts, categorical codes plotted numerically, and sensor data with limited resolution.</p><pre><code><span>n = 3000
base = rng.normal(0, 1, n)
gr1 = np.round(np.clip(70 + 12 * base + rng.normal(0, 6, n), 40, 110)).astype(int)
gr2 = np.round(np.clip(70 + 12 * base + rng.normal(0, 6, n), 40, 110)).astype(int)

fig, axes = plt.subplots(1, 2, figsize=(12, 6))
axes[0].scatter(gr1, gr2, s=12, color=&#8221;#1f3b57&#8221;)
axes[0].set_title(&#8221;Scatter: 3,000 points on a grid&#8221;)
axes[0].set_xlabel(&#8221;GR curve A (API)&#8221;); axes[0].set_ylabel(&#8221;GR curve B (API)&#8221;)
axes[0].set_aspect(&#8221;equal&#8221;)
hb = axes[1].hexbin(gr1, gr2, gridsize=30, cmap=&#8221;viridis&#8221;, mincnt=1)
axes[1].set_title(&#8221;Hexbin: where the weight actually is&#8221;)
axes[1].set_xlabel(&#8221;GR curve A (API)&#8221;); axes[1].set_ylabel(&#8221;GR curve B (API)&#8221;)
axes[1].set_aspect(&#8221;equal&#8221;)
fig.colorbar(hb, ax=axes[1], label=&#8221;count&#8221;, shrink=0.8)
plt.show()</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Tk8m!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98f35c04-4953-4c42-a773-c6ecb1fa00dd_630x316.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Tk8m!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98f35c04-4953-4c42-a773-c6ecb1fa00dd_630x316.png 424w, /__u/substackcdn.com/image/fetch/$s_!Tk8m!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98f35c04-4953-4c42-a773-c6ecb1fa00dd_630x316.png 848w, /__u/substackcdn.com/image/fetch/$s_!Tk8m!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98f35c04-4953-4c42-a773-c6ecb1fa00dd_630x316.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Tk8m!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98f35c04-4953-4c42-a773-c6ecb1fa00dd_630x316.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Tk8m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98f35c04-4953-4c42-a773-c6ecb1fa00dd_630x316.png" width="630" height="316" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/98f35c04-4953-4c42-a773-c6ecb1fa00dd_630x316.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:316,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!Tk8m!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98f35c04-4953-4c42-a773-c6ecb1fa00dd_630x316.png 424w, /__u/substackcdn.com/image/fetch/$s_!Tk8m!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98f35c04-4953-4c42-a773-c6ecb1fa00dd_630x316.png 848w, /__u/substackcdn.com/image/fetch/$s_!Tk8m!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98f35c04-4953-4c42-a773-c6ecb1fa00dd_630x316.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Tk8m!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98f35c04-4953-4c42-a773-c6ecb1fa00dd_630x316.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">A crossplot of two discretised gamma ray curves collapsing onto a visible grid of repeated coordinate pairs, shown alongside a hexbin of the same data revealing which grid positions carry the weight</figcaption></figure></div><p>This case here is sneakier than the large data point version, because the number of points being plotted gives you no warning.</p><p>The chart could look sparse enough to trust, and adjusting the transparency of the points can help a little or even some point jittering (adding small random offsets to separate identical values, although it means plotting data that doesn&#8217;t quite exist).</p><p>Using a density view can handle that more cleanly.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/subscribe"><span>Subscribe now</span></a></p><h2><strong>Summary</strong></h2><p>A scatter plot answers the question: &#8220;where are my individual observations?&#8221; That&#8217;s a good question to have when every observation is visible.</p><p>However, once observations start disappearing underneath each other, whether from the sheer volume of data or from repeated discrete values, the chart is no longer answering that question. You may try changing the styling, however, that may not be enough.</p><p>At that point, you should stop asking where the data points are and start asking where the data is concentrated. This is where hexbin plots, density contours, or a separate aggregation tool, can show you what the blob was hiding.</p><div><hr></div><p><em>Overplotting is one way a default chart hides your data, but it isn&#8217;t the only one. A boxplot does the same job on a distribution, reducing it to five numbers so a bimodal formation with a gap in the middle can look like an ordinary spread. Over on Substack I build a raincloud plot one layer at a time, stacking the density, the raw points and the boxplot summary so you can see the shape and check it against the actual observations.</em></p><p><em><a href="/__u/andymcdonaldgeo.substack.com/p/from-boxplot-to-raincloud-building">&#8594; From Boxplot to Raincloud: Building a Better Distribution Plot in Python</a></em></p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/p/what-your-scatter-plot-stops-showing?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading Subsurface Syntax! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/p/what-your-scatter-plot-stops-showing?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/p/what-your-scatter-plot-stops-showing?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share</span></a></p></div><p></p>]]></content:encoded></item><item><title><![CDATA[Handling Categorical Geological Data in Machine Learning Models]]></title><description><![CDATA[What label, ordinal, and one hot encoding each tell a model about your rocks]]></description><link>https://andymcdonaldgeo.substack.com/p/handling-categorical-geological-data</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/handling-categorical-geological-data</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Sun, 19 Jul 2026 09:43:30 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!One6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff526d346-63cb-46d7-89b3-1029d66ea5d2_938x527.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!One6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff526d346-63cb-46d7-89b3-1029d66ea5d2_938x527.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!One6!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff526d346-63cb-46d7-89b3-1029d66ea5d2_938x527.png 424w, /__u/substackcdn.com/image/fetch/$s_!One6!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff526d346-63cb-46d7-89b3-1029d66ea5d2_938x527.png 848w, /__u/substackcdn.com/image/fetch/$s_!One6!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff526d346-63cb-46d7-89b3-1029d66ea5d2_938x527.png 1272w, /__u/substackcdn.com/image/fetch/$s_!One6!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff526d346-63cb-46d7-89b3-1029d66ea5d2_938x527.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!One6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff526d346-63cb-46d7-89b3-1029d66ea5d2_938x527.png" width="938" height="527" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f526d346-63cb-46d7-89b3-1029d66ea5d2_938x527.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:527,&quot;width&quot;:938,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:836252,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://andymcdonaldgeo.substack.com/i/207640499?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff526d346-63cb-46d7-89b3-1029d66ea5d2_938x527.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!One6!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff526d346-63cb-46d7-89b3-1029d66ea5d2_938x527.png 424w, /__u/substackcdn.com/image/fetch/$s_!One6!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff526d346-63cb-46d7-89b3-1029d66ea5d2_938x527.png 848w, /__u/substackcdn.com/image/fetch/$s_!One6!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff526d346-63cb-46d7-89b3-1029d66ea5d2_938x527.png 1272w, /__u/substackcdn.com/image/fetch/$s_!One6!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff526d346-63cb-46d7-89b3-1029d66ea5d2_938x527.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Machine learning has become a common part of petrophysical workflows, from predicting missing curves to classifying facies. Most of the data we feed these models is continuous, eg. gamma ray, density, neutron porosity, sonic compressional. But, alongside the curves, almost every project carries categorical data too. This includes formation names, facies schemes, zonations, and lithology descriptions.</p><p>The problem is that machine learning models can&#8217;t use them directly. A linear regression, a neural network, or a support vector machine works entirely in numbers. It can multiply a gamma ray value by a coefficient, but it cannot multiply text strings by anything. So before a formation column can go into a machine learning model, it has to be converted into numeric values. There are different ways to do this, and choosing which method can impact how the model(s) treat those values and information.</p><p>In this article, we will look at what encoding is, the common options, and why one hot encoding is usually a safe choice. We will then test it on a simple, but real dataset from the Volve field to see how much difference the choice of encoding method makes.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>What is encoding?</strong></h2><p>First, we need to understand what encoding is.</p><p>Encoding is simply the process of converting categorical values into numerical values that a model can work with. That sounds like a mechanical step, and it is often treated as one, a line of preprocessing code to get through before the interesting work starts. But, every encoding scheme carries information into the model that in turn tells the model something about those categories.</p><p>Numbers have an order, and as a result they have distances between them. Whatever numbers we choose, the model will take them seriously, whether we intended them or not.</p><p>Here are a few common encoding techniques.</p><p><strong>Label encoding</strong> (also called integer encoding) assigns each category a whole number: 0, 1, 2, 3 and so on. It&#8217;s simple and compact, one column in, one column out, and often the first thing we think of doing when converting lithologies or formations to numeric values.</p><p>In some cases, the label encoding of lithology values is already done as part of data deliverables or converted as part of a company standard. For example a coarse grained arkosic sandstone is always encoded as 12345, and a fine grained laminated shale is always encoded as 54321. Even though the numbers are very different, they still carry information and distances.</p><p>When using tools like scikit-learn&#8217;s <code>LabelEncoder</code> each category is assigned an integer based on alphabetical order. This is illustrated below:</p><p>The encoder has placed the formations on a number line, at equal spacing, in alphabetical order. To us that looks like a lookup table, however when a model looks it this it considers it a measurement scale. A linear model given this column has to fit a single coefficient across it, which means treating the step from Draupne Fm to Heather Fm as the same size as the step from Hugin Fm to Tor Fm, and treating the Draupne Fm as two times as far from Heimdal Fm as the Tor Fm is.</p><p><strong>Ordinal encoding</strong> is the same idea, but with the integers assigned in an order you choose deliberately, because the categories have a real ranking. Coarse to fine grain size across facies would be a candidate, as would geological age.</p><p><strong>One hot encoding</strong> creates a new binary column for each category, which we will come to shortly.</p><p>There are more exotic options too, such as target encoding and learned embeddings, which are useful when a categorical has hundreds of levels, but for the handful of formations or facies in a typical petrophysical dataset, the three above cover almost every situation.</p><h2><strong>What is one hot encoding?</strong></h2><p>One hot encoding takes a different approach. Instead of one column of integers, it creates one binary column per category. Each sample gets a 1 in the column belonging to its category and a 0 everywhere else.</p><p>Which prediction algorithms this matters for comes down to a single question: does the algorithm treat the numbers you feed it as positions on a scale? The ones that do are exactly the ones that get misled by label encoding, and exactly the ones one hot encoding can work well with.</p><p>Distance-based methods are the clearest case. For example, K-nearest neighbours decides what a sample is by measuring how far it sits from others, and self-organising maps and clustering methods like K-means work the same way. Under label encoding, the Skagerrak at code 6 is literally six units from a formation at code 0 and one unit from its alphabetical neighbour, so the algorithm treats alphabetical adjacency as geological similarity. One hot encoding fixes this because every category sits the same distance from every other, so no false neighbours are created.</p><p>Linear and additive models are the next case. Multiple linear regression fits one coefficient per feature, so a label encoded column forces a single straight-line relationship across categories that have no order. With one hot encoding, each formation gets its own coefficient and can take whatever value the data supports, independently of the others. The same reasoning covers logistic regression and, to a degree, support vector machines.</p><p>Neural networks sit here too. A network multiplies each input by a weight, so a label encoded formation enters as a single input whose magnitude the network reads as meaningful, and larger codes push harder on the first layer for no good reason. One hot encoding gives the network one input per category, each with its own weight, which is why it is the usual starting point for categorical inputs that are not being handled by a dedicated embedding layer.</p><p>The common theme between these models is that all of them treat input magnitude as information. The main family that does not are tree-based models. A decision tree, random forest, or gradient boosting model splits a feature into groups rather than scaling it, so it can carve a label encoded column into sensible partitions on its own. Encoding still matters there for other reasons, but the false-ordering problem is much weaker.</p><p>So one hot encoding is generally the safe default whenever the model reads numbers as positions on a scale, and the categories have no natural order. It makes no ordering claim, so it cannot make a wrong one.</p><p>One issue that can arise from using One Hot Encoding is that one column can suddenly become many. This can start to become a handful when we have 100s of categories being converted into 100s of input curves. However, for most petrophysical work, this is unlikely to become a huge issue due to the smaller amount of categories and formation names we work with.</p><h2><strong>One column too many: dropping the first category</strong></h2><p>There is one wrinkle worth understanding before we use One Hot Encoding, because it appears as an argument in most encoders. When you one hot encode a formation with, say, four categories, you get four binary columns. But, those four columns carry a hidden redundancy: because every sample belongs to exactly one formation, the four columns always sum to one across any row. Knowing three of them tells you the fourth for free. If it is not Heather, Hod, or Hugin, it must be Draupne.</p><p>For many models that redundancy is harmless, but for linear regression it is a real problem. A regression usually includes an intercept, which is effectively a column of all ones. Since the full set of dummy columns also sums to one on every row, the dummies and the intercept become perfectly collinear, and the regression can no longer find a unique solution. This is sometimes called the <strong>dummy variable trap.</strong></p><p>The fix is to drop one category, which is what <code>drop="first"</code> does in scikit-learn (<code>pd.get_dummies</code> offers the same through <code>drop_first=True</code>). The dropped category does not disappear from the model. It becomes the baseline, and every remaining coefficient is then read as the effect relative to that baseline.</p><p>For tree-based models and regularised models the collinearity is not fatal, so you will often see the full set of columns kept there, but for a plain linear regression, dropping one is the safe habit.</p><h2><strong>A real example from Volve</strong></h2><p>To see how much the choice actually matters, we can test both encodings on the same task with real data: well 15/9&#8211;19 A from Equinor&#8217;s openly released Volve dataset, with formation tops from the Norwegian Offshore Directorate factpages. The task is predicting the sonic curve (DT) from gamma ray, bulk density and the formation column, using a multiple linear regression.</p><p>First, we load the well and assign a formation to each depth sample from the tops:</p><pre><code><span>import lasio
import numpy as np

tops = pd.read_csv(&#8221;wellbore_formation_top.csv&#8221;, encoding=&#8221;utf-8-sig&#8221;)

def add_formations(df, wellbore_name):
    t = tops[(tops.wlbName == wellbore_name) &amp; (tops.lsuLevel == &#8220;FORMATION&#8221;)]
    out = pd.Series(None, index=df.index, dtype=object)
    for _, row in t.iterrows():
        mask = (df.MD &gt;= row.lsuTopDepth) &amp; (df.MD &lt; row.lsuBottomDepth)
        out[mask] = row.lsuName.replace(&#8221; FM&#8221;, &#8220;&#8221;).title()
    return out

df = lasio.read(&#8221;159-19A_LFP.las&#8221;).df().reset_index()[[&#8221;DEPTH&#8221;, &#8220;LFP_GR&#8221;, &#8220;LFP_DT&#8221;, &#8220;LFP_RHOB&#8221;]]
df.columns = [&#8221;MD&#8221;, &#8220;GR&#8221;, &#8220;DT&#8221;, &#8220;RHOB&#8221;]
df[&#8221;formation&#8221;] = add_formations(df, &#8220;15/9-19 A&#8221;)
df = df.dropna(subset=[&#8221;GR&#8221;, &#8220;DT&#8221;, &#8220;RHOB&#8221;, &#8220;formation&#8221;])</span></code></pre><p>That gives us 3,850 samples across ten formations, from the Blod&#248;ks down to the Skagerrak. Before modelling anything, it is worth looking at what label encoding would do to this well. Plotting the mean DT of each formation against its alphabetical integer code:</p><p>The means zigzag along the alphabet. The Draupne hot shale sits up at around 120 us/ft in second position, between the much faster Blod&#248;ks and Heather, and the chalks and sands land wherever their names happen to fall. The dashed line shows the single slope a linear model would be forced to fit through that. It is wrong for nearly every formation at once.</p><p>Now the test. We fit the same regression twice, once with the label encoded formation column and once with the one hot version, and score both with shuffled 5-fold cross validation:</p><pre><code><span>from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import KFold, cross_val_score

le = LabelEncoder()
df[&#8221;form_code&#8221;] = le.fit_transform(df[&#8221;formation&#8221;])
y = df[&#8221;DT&#8221;].values
X_int = df[[&#8221;GR&#8221;, &#8220;form_code&#8221;, &#8220;RHOB&#8221;]].values
ohe = OneHotEncoder(sparse_output=False, drop=&#8221;first&#8221;)
X_ohe = np.column_stack([df[[&#8221;GR&#8221;, &#8220;RHOB&#8221;]].values, ohe.fit_transform(df[[&#8221;formation&#8221;]])])

kf = KFold(n_splits=5, shuffle=True, random_state=42)

kf = KFold(n_splits=5, shuffle=True, random_state=0)
r2_int = cross_val_score(LinearRegression(), X_int, y, cv=kf, scoring=&#8221;r2&#8221;).mean()
r2_ohe = cross_val_score(LinearRegression(), X_ohe, y, cv=kf, scoring=&#8221;r2&#8221;).mean()
print(f&#8221;label encoded: R2 = {r2_int:.3f} | one hot: R2 = {r2_ohe:.3f}&#8221;)</span></code></pre><p>The label encoded model scores an R squared of 0.613. The one hot model scores 0.888.</p><p>Even though this is a simple model, we have the same data, same model, and the same validation process, with the only difference between them is how the formation names were turned into numbers.</p><h2><strong>Summary</strong></h2><p>Encoding is the step where categorical data becomes something a model can use, and it is a choice rather than a formality. Label encoding is compact but invents an ordering, and unless you assign the codes deliberately, that ordering is alphabetical and geologically meaningless. One hot encoding trades a few extra columns for making no assumptions at all, and for the unordered categories that dominate petrophysical work, formations, facies, lithologies, that is usually the right trade. On a real Volve well, it was the difference between an R squared of 0.613 and 0.888.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/p/handling-categorical-geological-data/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/p/handling-categorical-geological-data/comments"><span>Leave a comment</span></a></p><p></p><div><hr></div><p><em>Data: <a href="http://equinor.com/energy/volve-data-sharing">Equinor Volve open dataset (well logs) </a>and the <a href="http://factpages.sodir.no/en">Norwegian Offshore Directorate factpages (formation tops)</a>, used with thanks under their respective licences.</em></p><div><hr></div><p>The example in this article used a linear regression, to keep the focus on the encoding rather than the model. If you would rather see curve prediction done with a neural network, I have a walkthrough on Substack that builds one in Keras to predict bulk density from other logs, using the same Volve data. It also shows how much of a difference normalising the inputs makes to the result.</p><p><a href="/__u/andymcdonaldgeo.substack.com/p/well-log-measurement-prediction-using-neural-networks-with-keras-ef7dfed94077">&#8594; Well Log Measurement Prediction Using Neural Networks with Keras</a></p>]]></content:encoded></item><item><title><![CDATA[From Default to Publication-Ready: The Raincloud Plot]]></title><description><![CDATA[Six steps between the chart ptitprince gives you and one you would want to put in a report]]></description><link>https://andymcdonaldgeo.substack.com/p/from-default-to-publication-ready-a41</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/from-default-to-publication-ready-a41</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Wed, 15 Jul 2026 20:25:31 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Ocdd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58bb678d-dc74-49c3-8725-fdeec3cb9e1d_630x355.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Ocdd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58bb678d-dc74-49c3-8725-fdeec3cb9e1d_630x355.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Ocdd!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58bb678d-dc74-49c3-8725-fdeec3cb9e1d_630x355.png 424w, /__u/substackcdn.com/image/fetch/$s_!Ocdd!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58bb678d-dc74-49c3-8725-fdeec3cb9e1d_630x355.png 848w, /__u/substackcdn.com/image/fetch/$s_!Ocdd!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58bb678d-dc74-49c3-8725-fdeec3cb9e1d_630x355.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Ocdd!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58bb678d-dc74-49c3-8725-fdeec3cb9e1d_630x355.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Ocdd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58bb678d-dc74-49c3-8725-fdeec3cb9e1d_630x355.png" width="630" height="355" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/58bb678d-dc74-49c3-8725-fdeec3cb9e1d_630x355.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:355,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!Ocdd!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58bb678d-dc74-49c3-8725-fdeec3cb9e1d_630x355.png 424w, /__u/substackcdn.com/image/fetch/$s_!Ocdd!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58bb678d-dc74-49c3-8725-fdeec3cb9e1d_630x355.png 848w, /__u/substackcdn.com/image/fetch/$s_!Ocdd!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58bb678d-dc74-49c3-8725-fdeec3cb9e1d_630x355.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Ocdd!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58bb678d-dc74-49c3-8725-fdeec3cb9e1d_630x355.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><a href="https://github.com/pog87/PtitPrince">PtitPrince</a> can create a raincloud plot in a few lines of code. That is one of its main attractions, especially when you want a quick view of how several distributions compare.</p><p>The default output is useful for exploration, but it still looks like something produced inside a notebook. The layout can feel cramped, the labels come directly from the DataFrame, and the relative weight of the cloud, box and individual points is left to the library defaults. This may be fine whilst you are analysing the data, but less so when the figure needs to appear in a report, presentation or article.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>To continue with my series on <a href="https://medium.com/p/e654c87aa50f">From Default to Publication Ready</a>, this article will take the default PtitPrince output and improve it one step at a time. We will change the orientation, apply a more accessible colour palette, rebalance the components, improve the labels, remove unnecessary clutter and add sample sizes.</p><p>The idea is not to produce one perfect design, but to show the small decisions that can turn a basic working plot into a figure that can easily be included in a publication and communicates its message clearly.</p><p>If you are new to raincloud plots, <a href="/__u/andymcdonaldgeo.substack.com/p/from-boxplot-to-raincloud-building">I covered the individual components in an earlier article</a>.</p><h2><strong>The data</strong></h2><p>For this article, we will generate some synthetic data that represents porosity distributions from four different geological formations.</p><p>The names and values are illustrative rather than real geology: two roughly normal distributions at different levels, one bimodal formation, and one that is skewed. This mix allows us to visualise different data distributions and to show the benefits of a raincloud plot</p><pre><code><span>import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import ptitprince as pt

rng = np.random.default_rng(42)
formations = {
    "Vardan": rng.normal(0.21, 0.025, 400),
    "Kelvik": rng.normal(0.15, 0.02, 320),
    "Stenby": np.concatenate([rng.normal(0.10, 0.015, 20),
                              rng.normal(0.18, 0.015, 20)]),
    "Morvik": rng.beta(2, 5, 20) * 0.25,
}
df = pd.DataFrame(
    [(name, val) for name, vals in formations.items() for val in vals],
    columns=["Formation", "Porosity"],
)</span></code></pre><p>With the data in place, we can call upon the raincloud plot like so:</p><pre><code><span>f, ax = plt.subplots(figsize=(8, 6))
pt.RainCloud(x="Formation", y="Porosity", data=df, ax=ax)
plt.show()</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!WxuQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01614ce4-1c35-44c6-a600-b0e3e347e7f0_623x491.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!WxuQ!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01614ce4-1c35-44c6-a600-b0e3e347e7f0_623x491.png 424w, /__u/substackcdn.com/image/fetch/$s_!WxuQ!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01614ce4-1c35-44c6-a600-b0e3e347e7f0_623x491.png 848w, /__u/substackcdn.com/image/fetch/$s_!WxuQ!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01614ce4-1c35-44c6-a600-b0e3e347e7f0_623x491.png 1272w, /__u/substackcdn.com/image/fetch/$s_!WxuQ!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01614ce4-1c35-44c6-a600-b0e3e347e7f0_623x491.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!WxuQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01614ce4-1c35-44c6-a600-b0e3e347e7f0_623x491.png" width="623" height="491" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/01614ce4-1c35-44c6-a600-b0e3e347e7f0_623x491.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:491,&quot;width&quot;:623,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!WxuQ!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01614ce4-1c35-44c6-a600-b0e3e347e7f0_623x491.png 424w, /__u/substackcdn.com/image/fetch/$s_!WxuQ!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01614ce4-1c35-44c6-a600-b0e3e347e7f0_623x491.png 848w, /__u/substackcdn.com/image/fetch/$s_!WxuQ!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01614ce4-1c35-44c6-a600-b0e3e347e7f0_623x491.png 1272w, /__u/substackcdn.com/image/fetch/$s_!WxuQ!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01614ce4-1c35-44c6-a600-b0e3e347e7f0_623x491.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Default vertical raincloud plot from ptitprince with four formations, cramped half-violins, default colours and raw column names as labels</figcaption></figure></div><p>The default output is readable, however, the vertical layout squashes each cloud into a narrow column and we may start competing for space on the x-axis when we add more formations or formations with longer names.</p><h2><strong>Step 1: Flip the orientation</strong></h2><p>Raincloud plots often read better horizontally, in my opinion, particularly when the group labels are long or several distributions are being compared.</p><p>The cloud gets room to spread out and the rain falls in a wide band instead of a narrow vertical strip, and we can display the group names on the y axis, which can be great when we have long names and need to avoid rotating the labels.</p><pre><code><span>f, ax = plt.subplots(figsize=(10, 7))
pt.RainCloud(x="Formation", y="Porosity", data=df, ax=ax,
             orient="h")
plt.show()</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!8wRM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F193d8821-8e48-4fe6-b85a-d850e88371a9_630x451.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!8wRM!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F193d8821-8e48-4fe6-b85a-d850e88371a9_630x451.png 424w, /__u/substackcdn.com/image/fetch/$s_!8wRM!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F193d8821-8e48-4fe6-b85a-d850e88371a9_630x451.png 848w, /__u/substackcdn.com/image/fetch/$s_!8wRM!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F193d8821-8e48-4fe6-b85a-d850e88371a9_630x451.png 1272w, /__u/substackcdn.com/image/fetch/$s_!8wRM!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F193d8821-8e48-4fe6-b85a-d850e88371a9_630x451.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!8wRM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F193d8821-8e48-4fe6-b85a-d850e88371a9_630x451.png" width="630" height="451" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/193d8821-8e48-4fe6-b85a-d850e88371a9_630x451.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:451,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!8wRM!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F193d8821-8e48-4fe6-b85a-d850e88371a9_630x451.png 424w, /__u/substackcdn.com/image/fetch/$s_!8wRM!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F193d8821-8e48-4fe6-b85a-d850e88371a9_630x451.png 848w, /__u/substackcdn.com/image/fetch/$s_!8wRM!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F193d8821-8e48-4fe6-b85a-d850e88371a9_630x451.png 1272w, /__u/substackcdn.com/image/fetch/$s_!8wRM!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F193d8821-8e48-4fe6-b85a-d850e88371a9_630x451.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Horizontal raincloud plot giving each formation&#8217;s cloud and rain room to spread, with formation names on the y axis</p><p>We have also changed the figure size to give more width than height.</p><h2><strong>Step 2: Apply a colour palette</strong></h2><p>The default blue colour is fine if you want to keep the groups consistent, but sometimes it is good to be able to split the groups apart by colour, especially if we have a handful of groups.</p><p>We can easily apply a different colour palette, and it often helps to pick one that is easily accessible to readers, especially those that have varying forms of colour blindness. Additionally, adding a slight bit of transparency can also help soften the colours if they appear to harsh.</p><pre><code><span>f, ax = plt.subplots(figsize=(10, 7))
pt.RainCloud(x="Formation", y="Porosity", data=df, ax=ax,
             orient="h",
             palette="colorblind", alpha=0.65)
plt.show()</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!60NF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733fa103-d8ca-49aa-b773-1fd83035f5b6_630x458.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!60NF!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733fa103-d8ca-49aa-b773-1fd83035f5b6_630x458.png 424w, /__u/substackcdn.com/image/fetch/$s_!60NF!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733fa103-d8ca-49aa-b773-1fd83035f5b6_630x458.png 848w, /__u/substackcdn.com/image/fetch/$s_!60NF!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733fa103-d8ca-49aa-b773-1fd83035f5b6_630x458.png 1272w, /__u/substackcdn.com/image/fetch/$s_!60NF!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733fa103-d8ca-49aa-b773-1fd83035f5b6_630x458.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!60NF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733fa103-d8ca-49aa-b773-1fd83035f5b6_630x458.png" width="630" height="458" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/733fa103-d8ca-49aa-b773-1fd83035f5b6_630x458.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:458,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!60NF!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733fa103-d8ca-49aa-b773-1fd83035f5b6_630x458.png 424w, /__u/substackcdn.com/image/fetch/$s_!60NF!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733fa103-d8ca-49aa-b773-1fd83035f5b6_630x458.png 848w, /__u/substackcdn.com/image/fetch/$s_!60NF!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733fa103-d8ca-49aa-b773-1fd83035f5b6_630x458.png 1272w, /__u/substackcdn.com/image/fetch/$s_!60NF!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F733fa103-d8ca-49aa-b773-1fd83035f5b6_630x458.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The same chart using seaborn&#8217;s colorblind palette with softened fill transparency</p><p>The <code>colorblind</code> palette ships with seaborn, so there&#8217;s nothing extra to install.</p><h2><strong>Step 3: Tune the cloud, the rain and the box</strong></h2><p>A raincloud plot consists of three components sharing a single axis, and the defaults for the plot give each component an arbitrary share of the space.</p><p>With a few width and jitter changes, we can rebalance them so the cloud dominates, the box stays a compact summary, and the rain forms a clear band underneath.</p><pre><code><span>f, ax = plt.subplots(figsize=(10, 7))
pt.RainCloud(x="Formation", y="Porosity", data=df, ax=ax,
             orient="h",
             palette="colorblind", alpha=0.65,
             width_viol=0.7, width_box=0.25,
             point_size=3, jitter=0.08, move=0.25,
             box_showfliers=False)
plt.show()</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!_Ewr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0794b818-2cf3-41af-9c04-19f9ca7479f1_630x439.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!_Ewr!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0794b818-2cf3-41af-9c04-19f9ca7479f1_630x439.png 424w, /__u/substackcdn.com/image/fetch/$s_!_Ewr!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0794b818-2cf3-41af-9c04-19f9ca7479f1_630x439.png 848w, /__u/substackcdn.com/image/fetch/$s_!_Ewr!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0794b818-2cf3-41af-9c04-19f9ca7479f1_630x439.png 1272w, /__u/substackcdn.com/image/fetch/$s_!_Ewr!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0794b818-2cf3-41af-9c04-19f9ca7479f1_630x439.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!_Ewr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0794b818-2cf3-41af-9c04-19f9ca7479f1_630x439.png" width="630" height="439" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0794b818-2cf3-41af-9c04-19f9ca7479f1_630x439.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:439,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!_Ewr!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0794b818-2cf3-41af-9c04-19f9ca7479f1_630x439.png 424w, /__u/substackcdn.com/image/fetch/$s_!_Ewr!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0794b818-2cf3-41af-9c04-19f9ca7479f1_630x439.png 848w, /__u/substackcdn.com/image/fetch/$s_!_Ewr!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0794b818-2cf3-41af-9c04-19f9ca7479f1_630x439.png 1272w, /__u/substackcdn.com/image/fetch/$s_!_Ewr!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0794b818-2cf3-41af-9c04-19f9ca7479f1_630x439.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Rebalanced widths with a fuller cloud, slimmer box, smaller rain points, tighter jitter and no duplicate outlier markers</p><p>The values here are a reasonable starting point rather than a rule. <code>width_viol</code> and <code>width_box</code> control how much room the cloud and box take, <code>point_size</code> and <code>jitter</code> control how the rain reads, and <code>move</code> shifts the rain away from the box so the two don&#8217;t overprint. With more groups or more points per group, these tend to need another review.</p><p>The boxplot layer displays outlier markers by default. Setting <code>box_showfliers=False</code> removes them. In a raincloud plot, displaying them can amount to double counting because every observation, including the outliers, is already shown in the rain.</p><p>By having this on, matplotlib draws hollow circles for its outliers, exactly as a standalone boxplot would. In a raincloud that&#8217;s double counting: every point, outliers included, is already in the rain. The box&#8217;s outlier markers sit at the box position rather than in the rain band, so they read as extra data that isn&#8217;t there. ptitprince routes any argument prefixed with <code>box_</code> through to the underlying boxplot, which is also how you&#8217;d pass other boxplot settings if you need them.</p><div><hr></div><p><em>Rainclouds are one of a handful of chart types that most people never reach for, even when they&#8217;d tell the story far better than a histogram. I&#8217;ve covered three of them, with before and after examples on well log data, over on Substack. Link at the end.</em></p><div><hr></div><h2><strong>Step 4: Say what the chart shows</strong></h2><p>The chart still labels its axes with DataFrame column names, which is not the best, especially if the names are more generic or contain special characters such as an underscore or dash.</p><p>We can tidy this up with a few small changes:</p><pre><code><span>ax.set_xlabel("Porosity (v/v)", fontsize=12)
ax.set_ylabel("")
ax.set_title("Porosity Distribution by Formation",
             fontsize=14, fontweight="bold", loc="left", pad=15)</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!YCNI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33d30f6f-748c-4b01-aed5-7cae9c6336ac_630x470.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!YCNI!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33d30f6f-748c-4b01-aed5-7cae9c6336ac_630x470.png 424w, /__u/substackcdn.com/image/fetch/$s_!YCNI!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33d30f6f-748c-4b01-aed5-7cae9c6336ac_630x470.png 848w, /__u/substackcdn.com/image/fetch/$s_!YCNI!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33d30f6f-748c-4b01-aed5-7cae9c6336ac_630x470.png 1272w, /__u/substackcdn.com/image/fetch/$s_!YCNI!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33d30f6f-748c-4b01-aed5-7cae9c6336ac_630x470.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!YCNI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33d30f6f-748c-4b01-aed5-7cae9c6336ac_630x470.png" width="630" height="470" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33d30f6f-748c-4b01-aed5-7cae9c6336ac_630x470.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:470,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!YCNI!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33d30f6f-748c-4b01-aed5-7cae9c6336ac_630x470.png 424w, /__u/substackcdn.com/image/fetch/$s_!YCNI!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33d30f6f-748c-4b01-aed5-7cae9c6336ac_630x470.png 848w, /__u/substackcdn.com/image/fetch/$s_!YCNI!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33d30f6f-748c-4b01-aed5-7cae9c6336ac_630x470.png 1272w, /__u/substackcdn.com/image/fetch/$s_!YCNI!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33d30f6f-748c-4b01-aed5-7cae9c6336ac_630x470.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The empty y label is intentional. The formation names already tell the reader what that axis is, and a &#8220;Formation&#8221; label would repeat them.</p><p>For reports and presentations, a left-aligned title is easy to scan and works well on a wide figure. Journal figures often leave the title to the caption, so this is a design choice rather than a fixed rule.</p><h2><strong>Step 5: Remove what isn&#8217;t data</strong></h2><p>Matplotlib draws a full box around every plot by default. This is often considered &#8220;chart junk&#8221; by many and can make plots look ugly or distract the reader.</p><p>By adding light grid along the porosity axis we can help the reader&#8217;s eye across the chart. This is the quiet step that does the most for how finished the figure feels.</p><pre><code><span>ax.spines[["top", "right"]].set_visible(False)
ax.xaxis.grid(True, linestyle="--", alpha=0.4)
ax.set_axisbelow(True)</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!1u7E!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce6666-5b5c-44b0-9d43-2c108a9e5bfb_630x464.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!1u7E!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce6666-5b5c-44b0-9d43-2c108a9e5bfb_630x464.png 424w, /__u/substackcdn.com/image/fetch/$s_!1u7E!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce6666-5b5c-44b0-9d43-2c108a9e5bfb_630x464.png 848w, /__u/substackcdn.com/image/fetch/$s_!1u7E!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce6666-5b5c-44b0-9d43-2c108a9e5bfb_630x464.png 1272w, /__u/substackcdn.com/image/fetch/$s_!1u7E!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce6666-5b5c-44b0-9d43-2c108a9e5bfb_630x464.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!1u7E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce6666-5b5c-44b0-9d43-2c108a9e5bfb_630x464.png" width="630" height="464" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/12ce6666-5b5c-44b0-9d43-2c108a9e5bfb_630x464.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:464,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!1u7E!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce6666-5b5c-44b0-9d43-2c108a9e5bfb_630x464.png 424w, /__u/substackcdn.com/image/fetch/$s_!1u7E!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce6666-5b5c-44b0-9d43-2c108a9e5bfb_630x464.png 848w, /__u/substackcdn.com/image/fetch/$s_!1u7E!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce6666-5b5c-44b0-9d43-2c108a9e5bfb_630x464.png 1272w, /__u/substackcdn.com/image/fetch/$s_!1u7E!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12ce6666-5b5c-44b0-9d43-2c108a9e5bfb_630x464.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Labels and decluttering combined. Titled chart with axis units, open top and right spines, and a light dashed grid behind the data</p><p><code>set_axisbelow(True)</code> pushes the gridlines behind the clouds and rain. Without it, dashed lines cut across your data, which can look messy.</p><h2><strong>Step 6: Show your sample sizes, then export properly</strong></h2><p>A raincloud plot invites the reader to trust the cloud, and the cloud is a smoothed estimate. When looking at the rain, it can also be difficult for the reader to estimate how many points are present for each group. For example between Vardan and Kelvik there are only 80 points difference, however, it would be difficult to know that by looking at the rain.</p><p>As a result the sample count is often worth putting directly on the figure, particularly when the groups are uneven. We can append the counts directly onto the group label on the y-axis rather than adding additional annotations.</p><pre><code><span>counts = df.groupby("Formation", sort=False).size()
labels = [f"{name}\n(n={counts[name]})" for name in counts.index]
ax.set_yticks(ax.get_yticks())
ax.set_yticklabels(labels)</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!wkcJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6df4587-26b1-4c6e-b5b1-af97fcddc10b_630x472.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!wkcJ!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6df4587-26b1-4c6e-b5b1-af97fcddc10b_630x472.png 424w, /__u/substackcdn.com/image/fetch/$s_!wkcJ!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6df4587-26b1-4c6e-b5b1-af97fcddc10b_630x472.png 848w, /__u/substackcdn.com/image/fetch/$s_!wkcJ!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6df4587-26b1-4c6e-b5b1-af97fcddc10b_630x472.png 1272w, /__u/substackcdn.com/image/fetch/$s_!wkcJ!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6df4587-26b1-4c6e-b5b1-af97fcddc10b_630x472.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!wkcJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6df4587-26b1-4c6e-b5b1-af97fcddc10b_630x472.png" width="630" height="472" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b6df4587-26b1-4c6e-b5b1-af97fcddc10b_630x472.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:472,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!wkcJ!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6df4587-26b1-4c6e-b5b1-af97fcddc10b_630x472.png 424w, /__u/substackcdn.com/image/fetch/$s_!wkcJ!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6df4587-26b1-4c6e-b5b1-af97fcddc10b_630x472.png 848w, /__u/substackcdn.com/image/fetch/$s_!wkcJ!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6df4587-26b1-4c6e-b5b1-af97fcddc10b_630x472.png 1272w, /__u/substackcdn.com/image/fetch/$s_!wkcJ!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb6df4587-26b1-4c6e-b5b1-af97fcddc10b_630x472.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The finished figure. Publication-ready horizontal raincloud with sample counts under each formation name.</p><h2><strong>The Final Script</strong></h2><p>Here&#8217;s everything in one place, from data to exported figure.</p><pre><code><span>import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import ptitprince as pt

rng = np.random.default_rng(42)
formations = {
    "Vardan": rng.normal(0.21, 0.025, 400),
    "Kelvik": rng.normal(0.15, 0.02, 320),
    "Stenby": np.concatenate([rng.normal(0.10, 0.015, 20),
                              rng.normal(0.18, 0.015, 20)]),
    "Morvik": rng.beta(2, 5, 20) * 0.25,
}
df = pd.DataFrame(
    [(name, val) for name, vals in formations.items() for val in vals],
    columns=["Formation", "Porosity"],
)
f, ax = plt.subplots(figsize=(10, 7))
pt.RainCloud(x="Formation", y="Porosity", data=df, ax=ax,
             orient="h",
             palette="colorblind", alpha=0.65,
             width_viol=0.7, width_box=0.25,
             point_size=3, jitter=0.08, move=0.25,
             box_showfliers=False)
ax.set_xlabel("Porosity (v/v)", fontsize=12)
ax.set_ylabel(")
ax.set_title("Porosity Distribution by Formation",
             fontsize=14, fontweight="bold", loc="left", pad=15)
ax.spines[["top", "right"]].set_visible(False)
ax.xaxis.grid(True, linestyle="--", alpha=0.4)
ax.set_axisbelow(True)
counts = df.groupby("Formation", sort=False).size()
labels = [f"{name}\n(n={counts[name]})" for name in counts.index]
ax.set_yticks(ax.get_yticks())
ax.set_yticklabels(labels)
plt.tight_layout()
plt.savefig("porosity_raincloud.png", dpi=600, bbox_inches="tight")</span></code></pre><h2><strong>Closing Thoughts</strong></h2><p>The difference between a default raincloud plot and a finished figure is not a complicated block of code, but rather a series of small and deliberate choices.</p><p>Changing the orientation gives the distributions more room. Adjusting the palette and transparency can help improve readability. Tuning the cloud, box and rain makes each component contribute without competing for attention and for space. Clear labels provide context, while removing unnecessary spines and adding a light grid help the data stand out. Showing the sample sizes gives the reader a better sense of how much evidence sits behind each distribution.</p><p>Not every figure needs exactly the same settings. More groups, longer labels or larger datasets may require different dimensions, point sizes or spacing. The useful habit is to treat the default output as a starting point rather than the finished result.</p><p>One setting we have deliberately left untouched is the bandwidth used to estimate the cloud. That choice affects how smooth, noisy or even multimodal a distribution appears, and it can change the story the plot seems to tell. This was covered briefly in <a href="/__u/andymcdonaldgeo.substack.com/p/raincloud-plots-with-ptitprince-see">Raincloud Plots with PtitPrince: See What Your Data Is Really Doing</a>.</p><div><hr></div><p><em>Polishing a raincloud plot only pays off if the raincloud was the right chart to begin with. Alongside it sit ridgeline plots, bump charts and beeswarm plots. Over on Substack I&#8217;ve put together a piece covering all three, with before and after examples using well log data, so you can decide which one your data deserves.</em></p><p><em><a href="/__u/andymcdonaldgeo.substack.com/p/3-python-charts-youre-probably-not">&#8594; 3 Python Charts You&#8217;re Probably Not Using (But Should Be)</a></em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Raincloud Plots with PtitPrince: See What Your Data Is Really Doing]]></title><description><![CDATA[Three views of your data distribution in one function call]]></description><link>https://andymcdonaldgeo.substack.com/p/raincloud-plots-with-ptitprince-see</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/raincloud-plots-with-ptitprince-see</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Sat, 04 Jul 2026 10:07:11 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!-k8-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f391fb-b918-41db-b914-d4bf5c53ff01_567x318.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!-k8-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f391fb-b918-41db-b914-d4bf5c53ff01_567x318.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!-k8-!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f391fb-b918-41db-b914-d4bf5c53ff01_567x318.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!-k8-!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f391fb-b918-41db-b914-d4bf5c53ff01_567x318.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!-k8-!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f391fb-b918-41db-b914-d4bf5c53ff01_567x318.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!-k8-!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f391fb-b918-41db-b914-d4bf5c53ff01_567x318.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!-k8-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f391fb-b918-41db-b914-d4bf5c53ff01_567x318.jpeg" width="567" height="318" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/40f391fb-b918-41db-b914-d4bf5c53ff01_567x318.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:318,&quot;width&quot;:567,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!-k8-!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f391fb-b918-41db-b914-d4bf5c53ff01_567x318.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!-k8-!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f391fb-b918-41db-b914-d4bf5c53ff01_567x318.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!-k8-!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f391fb-b918-41db-b914-d4bf5c53ff01_567x318.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!-k8-!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f391fb-b918-41db-b914-d4bf5c53ff01_567x318.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong><a href="/__u/andymcdonaldgeo.substack.com/p/from-boxplot-to-raincloud-building">In my last article I built a raincloud plot from scratch in matplotlib</a>.</strong> This was a combination of a half-violin for the shape (cloud), the raw points jittered underneath (the rain), and a boxplot at the bottom giving a summary of the data distribution.</p><p>Even though we can build the plot by hand (and as usual) there is a dedicated library which wraps most of that up. This allows you to write fewer lines of code to achive what you are looking for.</p><p><code>ptitprince</code> is the Python port of the original <a href="https://github.com/RainCloudPlots/RainCloudPlots">RainCloudPlots</a> project that introduced the chart, and it sits on top of seaborn.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><h2><strong>Setup and data</strong></h2><p>First, we need to install the library from PyPI:</p><pre><code><span>pip install ptitprince</span></code></pre><p>For the data, we are using a synthetic dataset representing porosity for four geological formations, built to show different distributions we may expect in the subsurface.</p><p>Formation A is bimodal, the sort of split you get when a sand and an interbedded shale share a zone. B is tight and unimodal, C is right-skewed, and D has only eighteen samples.</p><pre><code><span>import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import ptitprince as pt

rng = np.random.default_rng(42)

n = 300
form_a = np.concatenate([rng.normal(0.10, 0.015, n // 2),
                         rng.normal(0.22, 0.020, n // 2)])   # bimodal: sand/shale mix
form_b = rng.normal(0.18, 0.012, n)                          # tight, unimodal
form_c = 0.06 + rng.gamma(2.0, 0.018, n)                     # right-skewed
form_d = rng.normal(0.15, 0.020, 18)                         # very small sample

data = pd.DataFrame({
    "formation": (["A"] * len(form_a) + ["B"] * len(form_b)
                  + ["C"] * len(form_c) + ["D"] * len(form_d)),
    "porosity": np.concatenate([form_a, form_b, form_c, form_d]),
})</span></code></pre><h2><strong>Generating the Basic Raincloud Plot</strong></h2><p>The library&#8217;s main entry point is <code>RainCloud</code>, and the basic call looks similar to how seaborn calls plots.</p><pre><code><span>fig, ax = plt.subplots(figsize=(8, 5))
pt.RainCloud(x="formation", y="porosity", data=data, orient="h", ax=ax)
ax.set_xlabel("Porosity (frac)")
ax.set_ylabel("Formation")
</span></code></pre><p>Once we run the above code, we get back the following chart.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!-car!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc745396b-0994-4c7e-a255-08332bda0867_630x416.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!-car!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc745396b-0994-4c7e-a255-08332bda0867_630x416.png 424w, /__u/substackcdn.com/image/fetch/$s_!-car!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc745396b-0994-4c7e-a255-08332bda0867_630x416.png 848w, /__u/substackcdn.com/image/fetch/$s_!-car!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc745396b-0994-4c7e-a255-08332bda0867_630x416.png 1272w, /__u/substackcdn.com/image/fetch/$s_!-car!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc745396b-0994-4c7e-a255-08332bda0867_630x416.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!-car!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc745396b-0994-4c7e-a255-08332bda0867_630x416.png" width="630" height="416" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c745396b-0994-4c7e-a255-08332bda0867_630x416.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:416,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!-car!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc745396b-0994-4c7e-a255-08332bda0867_630x416.png 424w, /__u/substackcdn.com/image/fetch/$s_!-car!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc745396b-0994-4c7e-a255-08332bda0867_630x416.png 848w, /__u/substackcdn.com/image/fetch/$s_!-car!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc745396b-0994-4c7e-a255-08332bda0867_630x416.png 1272w, /__u/substackcdn.com/image/fetch/$s_!-car!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc745396b-0994-4c7e-a255-08332bda0867_630x416.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The default raincloud output from ptitprince.</p><p>With essentially all three layers are there for each formation, we can gain a good understanding of our data very quickly.</p><p>Compared with the thirty-odd lines of the hand-built version, that is a real saving.</p><p>The defaults are not perfect and almost looks like a basic matplotlib plot. Nothing wrong with that, but if you have followed my previous articles you can see how much I enjoy transforming matplotlib figures into something much better looking.</p><p>In this case, the boxplot sits directly on top of the rain, so the box hides some of the points it is meant to summarise, and the outlier fliers on the box duplicate points that are already visible in the rain. The layers are all present, but they are all fighting for the same space.</p><h2><strong>Tidying Up the Raincloud Plot</strong></h2><p>Customisation in <code>ptitprince</code> works in two ways.</p><p>A handful of top-level parameters control the layout, and everything else passes through to the underlying seaborn calls using prefixed keyword arguments: anything starting <code>cloud_</code> goes to the half-violin, <code>box_</code> to the boxplot, <code>rain_</code> to the strip plot, and <code>point_</code> to the optional mean marker.</p><p>The single most useful parameter I have found to tidy up the overlapping boxplot is <code>move</code> This shifts the rain (points) downwards and away from the box:</p><pre><code><span>fig, ax = plt.subplots(figsize=(8, 5))
pt.RainCloud(
    x="formation", y="porosity", data=data, orient="h", ax=ax,
    move=0.2,                 # shift the rain clear of the box
    width_viol=0.7,           # cloud width
    point_size=3,             # rain marker size
    alpha=0.6,                # transparency for cloud and rain
    box_showfliers=False,     # outliers already visible in the rain
)
ax.set_xlabel("Porosity (frac)")
ax.set_ylabel("Formation")</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!-XFq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4882ae39-86c6-4e5d-9868-bb8fea47ced6_630x416.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!-XFq!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4882ae39-86c6-4e5d-9868-bb8fea47ced6_630x416.png 424w, /__u/substackcdn.com/image/fetch/$s_!-XFq!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4882ae39-86c6-4e5d-9868-bb8fea47ced6_630x416.png 848w, /__u/substackcdn.com/image/fetch/$s_!-XFq!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4882ae39-86c6-4e5d-9868-bb8fea47ced6_630x416.png 1272w, /__u/substackcdn.com/image/fetch/$s_!-XFq!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4882ae39-86c6-4e5d-9868-bb8fea47ced6_630x416.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!-XFq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4882ae39-86c6-4e5d-9868-bb8fea47ced6_630x416.png" width="630" height="416" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4882ae39-86c6-4e5d-9868-bb8fea47ced6_630x416.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:416,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!-XFq!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4882ae39-86c6-4e5d-9868-bb8fea47ced6_630x416.png 424w, /__u/substackcdn.com/image/fetch/$s_!-XFq!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4882ae39-86c6-4e5d-9868-bb8fea47ced6_630x416.png 848w, /__u/substackcdn.com/image/fetch/$s_!-XFq!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4882ae39-86c6-4e5d-9868-bb8fea47ced6_630x416.png 1272w, /__u/substackcdn.com/image/fetch/$s_!-XFq!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4882ae39-86c6-4e5d-9868-bb8fea47ced6_630x416.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Tidied up version of the rain cloud plots with the rain sittin clear of the box, no fliers and no competing or overlapping elements.</p><p>By changing a few extra arguments we can make the chart tidier and easier to read.</p><p>Each layer now has it&#8217;s own space, the duplicated outliers are gone, and the transparency lets dense regions of rain show as darker bands. This is enough for making something quickly to help with the analysis.</p><h2><strong>Making it publication ready</strong></h2><p>For a figure going into a <a href="/__u/andymcdonaldgeo.substack.com/p/six-matplotlib-changes-that-improve">report or a paper</a>, the tidied version still needs the usual finishing work: consistent colours, axis labels with units, sample counts, a title, and gridlines.</p><p>There are two ways we can get there.</p><p>The first is the prefixed kwargs handle anything that belongs to a layer, and the second, is by adding standard matplotlib calls because <code>RainCloud</code> draws onto an ordinary matplotlib Axes.</p><pre><code><span>formations = ["A", "B", "C", "D"]
colours = ["#4C72B0", "#55A868", "#C44E52", "#8172B3"]

fig, ax = plt.subplots(figsize=(9, 6), facecolor="white")
pt.RainCloud(
    x="formation", y="porosity", data=data, orient="h", ax=ax,
    palette=colours,
    move=0.2, width_viol=0.7, point_size=3, alpha=0.6,
    box_showfliers=False,
    box_linewidth=1.2,
    box_medianprops={"color": "#2c3e50", "linewidth": 1.6},
)

# Post-hoc polish on the returned axes
counts = data.groupby("formation")["porosity"].size()
labels = [f"{f}\n(n={counts[f]})" for f in formations]
ax.set_yticks(range(len(formations)))
ax.set_yticklabels(labels)
ax.set_xlabel("Porosity (v/v)", fontsize=12, fontweight="bold", labelpad=10)
ax.set_ylabel("Formation", fontsize=12, fontweight="bold", labelpad=10)
ax.set_title("Porosity Distribution by Formation", fontsize=14, fontweight="bold", pad=15)
ax.grid(axis="x", alpha=0.3, linestyle="--", linewidth=0.8)
ax.tick_params(axis="both", labelsize=10)

plt.tight_layout()
plt.savefig("raincloud_porosity.png", dpi=150, bbox_inches="tight", facecolor="white")</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!2tcE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F503a139f-452e-4722-a241-fa2659289b5a_630x416.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!2tcE!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F503a139f-452e-4722-a241-fa2659289b5a_630x416.png 424w, /__u/substackcdn.com/image/fetch/$s_!2tcE!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F503a139f-452e-4722-a241-fa2659289b5a_630x416.png 848w, /__u/substackcdn.com/image/fetch/$s_!2tcE!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F503a139f-452e-4722-a241-fa2659289b5a_630x416.png 1272w, /__u/substackcdn.com/image/fetch/$s_!2tcE!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F503a139f-452e-4722-a241-fa2659289b5a_630x416.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!2tcE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F503a139f-452e-4722-a241-fa2659289b5a_630x416.png" width="630" height="416" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/503a139f-452e-4722-a241-fa2659289b5a_630x416.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:416,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!2tcE!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F503a139f-452e-4722-a241-fa2659289b5a_630x416.png 424w, /__u/substackcdn.com/image/fetch/$s_!2tcE!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F503a139f-452e-4722-a241-fa2659289b5a_630x416.png 848w, /__u/substackcdn.com/image/fetch/$s_!2tcE!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F503a139f-452e-4722-a241-fa2659289b5a_630x416.png 1272w, /__u/substackcdn.com/image/fetch/$s_!2tcE!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F503a139f-452e-4722-a241-fa2659289b5a_630x416.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Another tidied up version of the raincloud plot that would be more suited for including in apublication.</p><p>The split matters more than the individual styling choices. Layer properties, like the box line weight and median colour, go through the prefixed kwargs.</p><p>Everything that belongs to the axes rather than a layer, such as labels, title, gridlines, and the tick text, is plain matplotlib after the raincloud plot call.</p><p>The sample counts under each formation name do the same work the rain does, but provides an instant number for the reader and saves them having to count each point (very few people will do that!).</p><p>This is also where you meet the limits of ptitprince&#8217;s raincloud plot. If you need to restyle an element the kwargs don&#8217;t reach and you then end up digging through the axes&#8217; children to find it, and at that point the hand-built version from the<a href="/__u/andymcdonaldgeo.substack.com/p/from-boxplot-to-raincloud-building"> previous article</a> can appear to be less work, not more.</p><h2><strong>The Parameter That Changes the Interpretation of the Cloud</strong></h2><p>One argument is worth singling out when creating the raincloud plot, because it can change what the chart says rather than how it looks.</p><p><code>bw</code> sets the bandwidth of the kernel density estimate behind the cloud, and it controls how much smoothing is applied to the shape you see.</p><pre><code><span>fig, axs = plt.subplots(1, 3, figsize=(12, 4), sharey=True)
for ax, bw in zip(axs, [0.1, 0.3, 0.8]):
    pt.RainCloud(x="formation", y="porosity",
                 data=data[data.formation == "A"],
                 orient="h", ax=ax, bw=bw, move=0.2, point_size=2.5)
    ax.set_title(f"bw = {bw}")
    ax.set_xlabel("Porosity (frac)")
    ax.set_ylabel("")
axs[0].set_ylabel("Formation")
plt.tight_layout()</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!HdG7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff95a5baf-dc6e-434e-81d2-7c9a11f2206c_630x205.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!HdG7!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff95a5baf-dc6e-434e-81d2-7c9a11f2206c_630x205.png 424w, /__u/substackcdn.com/image/fetch/$s_!HdG7!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff95a5baf-dc6e-434e-81d2-7c9a11f2206c_630x205.png 848w, /__u/substackcdn.com/image/fetch/$s_!HdG7!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff95a5baf-dc6e-434e-81d2-7c9a11f2206c_630x205.png 1272w, /__u/substackcdn.com/image/fetch/$s_!HdG7!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff95a5baf-dc6e-434e-81d2-7c9a11f2206c_630x205.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!HdG7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff95a5baf-dc6e-434e-81d2-7c9a11f2206c_630x205.png" width="630" height="205" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f95a5baf-dc6e-434e-81d2-7c9a11f2206c_630x205.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:205,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!HdG7!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff95a5baf-dc6e-434e-81d2-7c9a11f2206c_630x205.png 424w, /__u/substackcdn.com/image/fetch/$s_!HdG7!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff95a5baf-dc6e-434e-81d2-7c9a11f2206c_630x205.png 848w, /__u/substackcdn.com/image/fetch/$s_!HdG7!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff95a5baf-dc6e-434e-81d2-7c9a11f2206c_630x205.png 1272w, /__u/substackcdn.com/image/fetch/$s_!HdG7!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff95a5baf-dc6e-434e-81d2-7c9a11f2206c_630x205.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The same bimodal formation drawn at three bandwidths. Two sharp humps at 0.1, two clear humps at 0.3, one broad hump at 0.8</p><p>This is the same formation A in all three panels.</p><p>At a bandwidth of 0.1 the two populations are sharp and separate.</p><p>At 0.3 they are still clearly there.</p><p>At 0.8 they have merged into one broad hump, and the cloud now tells you this is a single, wide distribution.</p><p>For a petrophysicist that is the difference between one lithology and a mixed zone, decided by a single smoothing parameter.</p><p>The rain is the safeguard, because the two clusters of points stay where they are, no matter what the smoothing does. If the cloud and the rain disagree about how many populations you have, then we should believe the rain.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>When To Use ptitprince or matplotlib</strong></h2><p>For exploratory work the ptitprince library is an easy choice. One call gets you a complete, readable raincloud for a first look at new data or a quick comparison across formations, and <code>move</code> plus a couple of styling arguments carry it a long way. With axes-level polish on top, it will take you all the way to a publication figure too.</p><p><a href="/__u/andymcdonaldgeo.substack.com/p/from-boxplot-to-raincloud-building">The hand-built version</a> is for when the wrapper gets in the way, for example when an element the prefixed kwargs can&#8217;t reach, or an environment where a thinly maintained dependency is more trouble than thirty lines of matplotlib you own and can control.</p><p>Either way, the same rule from the previous article applies, and figure 4 is the reason: the cloud is an estimate and the rain is the data. Whichever tool draws the chart, when the two disagree, believe the rain.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/p/raincloud-plots-with-ptitprince-see/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/p/raincloud-plots-with-ptitprince-see/comments"><span>Leave a comment</span></a></p><div><hr></div><p>Throughout this article I have mentioned the hand-built version as the fallback for when the wrapper gets in the way. If you want to see how that works, I have written a full walkthrough on Substack where I build the raincloud plot from scratch in matplotlib, layer by layer, using the same four-formation dataset as this article.</p><p><a href="/__u/andymcdonaldgeo.substack.com/p/from-boxplot-to-raincloud-building">&#8594; From Boxplot to Raincloud: Building a Better Distribution Plot in Python</a></p>]]></content:encoded></item><item><title><![CDATA[From Boxplot to Raincloud: Building a Better Distribution Plot in Python]]></title><description><![CDATA[Constructing a raincloud plot one layer at a time]]></description><link>https://andymcdonaldgeo.substack.com/p/from-boxplot-to-raincloud-building</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/from-boxplot-to-raincloud-building</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Sun, 14 Jun 2026 09:16:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!nIbq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5591a-1f41-43ce-98d7-bf5c08f8e9eb_630x353.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!nIbq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5591a-1f41-43ce-98d7-bf5c08f8e9eb_630x353.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!nIbq!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5591a-1f41-43ce-98d7-bf5c08f8e9eb_630x353.png 424w, /__u/substackcdn.com/image/fetch/$s_!nIbq!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5591a-1f41-43ce-98d7-bf5c08f8e9eb_630x353.png 848w, /__u/substackcdn.com/image/fetch/$s_!nIbq!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5591a-1f41-43ce-98d7-bf5c08f8e9eb_630x353.png 1272w, /__u/substackcdn.com/image/fetch/$s_!nIbq!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5591a-1f41-43ce-98d7-bf5c08f8e9eb_630x353.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!nIbq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5591a-1f41-43ce-98d7-bf5c08f8e9eb_630x353.png" width="630" height="353" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aab5591a-1f41-43ce-98d7-bf5c08f8e9eb_630x353.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:353,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!nIbq!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5591a-1f41-43ce-98d7-bf5c08f8e9eb_630x353.png 424w, /__u/substackcdn.com/image/fetch/$s_!nIbq!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5591a-1f41-43ce-98d7-bf5c08f8e9eb_630x353.png 848w, /__u/substackcdn.com/image/fetch/$s_!nIbq!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5591a-1f41-43ce-98d7-bf5c08f8e9eb_630x353.png 1272w, /__u/substackcdn.com/image/fetch/$s_!nIbq!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faab5591a-1f41-43ce-98d7-bf5c08f8e9eb_630x353.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The boxplot is the common tool for comparing measurements across different geological formations, and it can also hide the thing you need to see. <a href="/__u/andymcdonaldgeo.substack.com/p/creating-boxplots-of-well-log-data-using-matplotlib-in-python-34c3816e73f4">Boxplots reduce a distribution to five key numbers, so two formations with completely different shapes can produce similar looking boxes.</a></p><p>This is where <a href="https://wellcomeopenresearch.org/articles/4-63">raincloud plots</a> can help. A raincloud plot stacks three views of a distribution in one: a half-violin for the shape (the &#8220;cloud&#8221;), the raw points jittered underneath (the &#8220;rain&#8221;), and a boxplot for the summary. You get the quick read of a boxplot without letting it hide what the data is actually doing.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Even though there there is a ready-made library for this in Python (<code>ptitprince</code>), we will be building one by hand using matplotlib. Once you have built it you can see precisely what the chart is showing.</p><h2><strong>The data</strong></h2><p>For this article, we will generate some synthetic data that represents porosity distributions from four different geological formations.</p><p>These distributions were chosen to show off the chart and may not necessarily represent real geology.</p><p>We can also import the required libraries, which are numpy, pandas, matplotlib and scipy.stats.</p><pre><code>import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde

rng = np.random.default_rng(42)

n = 300
form_a = np.concatenate([rng.normal(0.10, 0.015, n // 2),
                         rng.normal(0.22, 0.020, n // 2)])   # bimodal: sand/shale mix
form_b = rng.normal(0.18, 0.012, n)                          # tight, unimodal
form_c = 0.06 + rng.gamma(2.0, 0.018, n)                     # right-skewed
form_d = rng.normal(0.15, 0.020, 18)                         # very small sample

data = pd.DataFrame({
    &#8220;formation&#8221;: ([&#8221;A&#8221;] * len(form_a) + [&#8221;B&#8221;] * len(form_b)
                  + [&#8221;C&#8221;] * len(form_c) + [&#8221;D&#8221;] * len(form_d)),
    &#8220;porosity&#8221;: np.concatenate([form_a, form_b, form_c, form_d]),
})

formations = [&#8221;A&#8221;, &#8220;B&#8221;, &#8220;C&#8221;, &#8220;D&#8221;]
groups = [data.loc[data.formation == f, &#8220;porosity&#8221;].values for f in formations]
colours = [&#8221;#4C72B0&#8221;, &#8220;#55A868&#8221;, &#8220;#C44E52&#8221;, &#8220;#8172B3&#8221;]</code></pre><p>Formation A is built from two normal distributions to represent a bimodal distribution which you might see when you have say sand and shale in one formation. Formation B is tight and unimodal, Formation C is right-skewed, and Formation D has only eighteen points.</p><p>Keep this mix of data distributions in mind, because the whole argument for the raincloud is that these four behave very differently and a boxplot will not tell you so.</p><h2><strong>Visualising Data on a Boxplot</strong></h2><p>Before building anything new, plot the boxplot you would normally reach for.</p><pre><code>fig, ax = plt.subplots(figsize=(8, 5))
ax.boxplot(groups, vert=False, widths=0.5)
ax.set_yticks(range(1, len(formations) + 1))
ax.set_yticklabels(formations)
ax.set_xlabel(&#8221;Porosity (frac)&#8221;)

ax.set_ylabel(&#8221;Formation&#8221;)</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!ompv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2508bb94-beeb-4d3d-b1d3-7f631475eb2e_612x403.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!ompv!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2508bb94-beeb-4d3d-b1d3-7f631475eb2e_612x403.png 424w, /__u/substackcdn.com/image/fetch/$s_!ompv!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2508bb94-beeb-4d3d-b1d3-7f631475eb2e_612x403.png 848w, /__u/substackcdn.com/image/fetch/$s_!ompv!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2508bb94-beeb-4d3d-b1d3-7f631475eb2e_612x403.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ompv!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2508bb94-beeb-4d3d-b1d3-7f631475eb2e_612x403.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!ompv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2508bb94-beeb-4d3d-b1d3-7f631475eb2e_612x403.png" width="612" height="403" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2508bb94-beeb-4d3d-b1d3-7f631475eb2e_612x403.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:403,&quot;width&quot;:612,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!ompv!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2508bb94-beeb-4d3d-b1d3-7f631475eb2e_612x403.png 424w, /__u/substackcdn.com/image/fetch/$s_!ompv!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2508bb94-beeb-4d3d-b1d3-7f631475eb2e_612x403.png 848w, /__u/substackcdn.com/image/fetch/$s_!ompv!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2508bb94-beeb-4d3d-b1d3-7f631475eb2e_612x403.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ompv!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2508bb94-beeb-4d3d-b1d3-7f631475eb2e_612x403.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Start with the boxplot. Horizontal boxplots of porosity for formations A to D, where A looks like one broad ordinary spread</figcaption></figure></div><p>If we look at formation A, it shows a wide box across the whole porosity range, its median sits in the middle, and nothing about it suggests anything unusual. As we will see shortly, the median is sitting in the empty gap between two populations in the bimodal distribution, where almost no data actually sits.</p><h2><strong>Building the Raincloud Plot</strong></h2><h3><strong>Layer one: the cloud</strong></h3><p>The cloud layer is a half-violin plot. This is kernel density estimate drawn on one side only. We compute the density with <code>gaussian_kde</code>, scale it to a fixed height so every formation is comparable, and fill it in above the formation&#8217;s baseline.</p><pre><code>def add_cloud(ax, values, ypos, width=0.35, colour=&#8221;#4C72B0&#8221;):
    kde = gaussian_kde(values)
    xs = np.linspace(values.min(), values.max(), 200)
    dens = kde(xs)
    dens = dens / dens.max() * width
    ax.fill_between(xs, ypos, ypos + dens, color=colour, alpha=0.5, linewidth=0)</code></pre><p>Building <code>xs</code> between <code>values.min()</code> and <code>values.max()</code> clips the density to the actual range of the data, so the cloud can&#8217;t drift past your real readings the way a default violin does.</p><p>Dividing by <code>dens.max()</code> normalises every cloud to the same peak height, so you are comparing shapes rather than raw densities between each of the formations.</p><p>That normalisation has a cost worth knowing about: it makes an 18-point dataset can look as substantial as a 300-point dataset, which is exactly why the raw data goes in underneath. Drawing the cloud on one side keeps it compact and leaves room for those points.</p><p>Plotting all four formations:</p><pre><code>fig, ax = plt.subplots(figsize=(8, 5))
for i, (vals, c) in enumerate(zip(groups, colours)):
    add_cloud(ax, vals, i, colour=c)
ax.set_yticks(range(len(formations)))
ax.set_yticklabels(formations)
ax.set_xlabel(&#8221;Porosity (frac)&#8221;)
plt.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Rf63!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f685a1-306e-4601-ac29-33e6a845a930_594x403.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Rf63!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f685a1-306e-4601-ac29-33e6a845a930_594x403.png 424w, /__u/substackcdn.com/image/fetch/$s_!Rf63!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f685a1-306e-4601-ac29-33e6a845a930_594x403.png 848w, /__u/substackcdn.com/image/fetch/$s_!Rf63!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f685a1-306e-4601-ac29-33e6a845a930_594x403.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Rf63!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f685a1-306e-4601-ac29-33e6a845a930_594x403.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Rf63!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f685a1-306e-4601-ac29-33e6a845a930_594x403.png" width="594" height="403" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/86f685a1-306e-4601-ac29-33e6a845a930_594x403.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:403,&quot;width&quot;:594,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!Rf63!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f685a1-306e-4601-ac29-33e6a845a930_594x403.png 424w, /__u/substackcdn.com/image/fetch/$s_!Rf63!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f685a1-306e-4601-ac29-33e6a845a930_594x403.png 848w, /__u/substackcdn.com/image/fetch/$s_!Rf63!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f685a1-306e-4601-ac29-33e6a845a930_594x403.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Rf63!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86f685a1-306e-4601-ac29-33e6a845a930_594x403.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Raincloud Plot &#8212; Layer one, the cloud. Half-violin density clouds per formation, A showing two humps, C its skew, B a narrow spike</figcaption></figure></div><p>Formation A now shows its two humps plainly, which represents our bimodal distribution, C shows its left skew, and B is the narrow spike you would expect from a tight distribution.</p><p>Whilst we can see the distributions of the data a density curve has its own problem: it is a smooth estimate, and with few points it will invent a smoothness the data can&#8217;t support.</p><p>This is why we need to add the actual data points.</p><h3><strong>Layer two: the rain</strong></h3><p>The rain is the raw observations, scattered below the cloud with a little vertical jitter so points do not pile up and hide each other.</p><p>The jitter is purely cosmetic on the vertical axis and carries no meaning. The horizontal position is the real value.</p><p>Adding the rain under each cloud can be done using the following function</p><pre><code>def add_rain(ax, values, ypos, spread=0.12, gap=0.06, colour=&#8221;#4C72B0&#8221;):
    jitter = rng.uniform(0, spread, size=len(values))
    ax.scatter(values, ypos - gap - jitter, s=8, color=colour, alpha=0.5,
               edgecolor=&#8221;none&#8221;)</code></pre><p>and then recreating the plot:</p><pre><code>fig, ax = plt.subplots(figsize=(8, 5))
for i, (vals, c) in enumerate(zip(groups, colours)):
    add_cloud(ax, vals, i, colour=c)
    add_rain(ax, vals, i, colour=c)
ax.set_yticks(range(len(formations)))
ax.set_yticklabels(formations)
ax.set_xlabel(&#8221;Porosity (frac)&#8221;)
plt.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!DGw_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47ca1627-8ee8-4ff4-a5e2-b3733bd685db_594x403.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!DGw_!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47ca1627-8ee8-4ff4-a5e2-b3733bd685db_594x403.png 424w, /__u/substackcdn.com/image/fetch/$s_!DGw_!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47ca1627-8ee8-4ff4-a5e2-b3733bd685db_594x403.png 848w, /__u/substackcdn.com/image/fetch/$s_!DGw_!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47ca1627-8ee8-4ff4-a5e2-b3733bd685db_594x403.png 1272w, /__u/substackcdn.com/image/fetch/$s_!DGw_!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47ca1627-8ee8-4ff4-a5e2-b3733bd685db_594x403.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!DGw_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47ca1627-8ee8-4ff4-a5e2-b3733bd685db_594x403.png" width="594" height="403" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/47ca1627-8ee8-4ff4-a5e2-b3733bd685db_594x403.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:403,&quot;width&quot;:594,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!DGw_!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47ca1627-8ee8-4ff4-a5e2-b3733bd685db_594x403.png 424w, /__u/substackcdn.com/image/fetch/$s_!DGw_!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47ca1627-8ee8-4ff4-a5e2-b3733bd685db_594x403.png 848w, /__u/substackcdn.com/image/fetch/$s_!DGw_!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47ca1627-8ee8-4ff4-a5e2-b3733bd685db_594x403.png 1272w, /__u/substackcdn.com/image/fetch/$s_!DGw_!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47ca1627-8ee8-4ff4-a5e2-b3733bd685db_594x403.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Raincloud Plot: Layer two, the rain. Density clouds with raw points jittered underneath, formation D showing only a sparse scatter beneath a smooth cloud</figcaption></figure></div><p>Formation D shows a smooth, reasonable distribution, but the rain shows only a handful of points. This smoothness was a result of the KDE filling in gaps .</p><p>The rain layer shows the real observations, how they are distributed and how many of them there are.</p><h3><strong>Layer three: the box</strong></h3><p>The summary generated by the boxplot is still worth keeping and can be added a thin horizontal box between the cloud and the rain.</p><p>The trick with matplotlib is setting <code>manage_ticks=False</code>, which stops the boxplot from resetting the y axis we have carefully set up.</p><pre><code>def add_box(ax, values, ypos):
    ax.boxplot([values], positions=[ypos], vert=False, widths=0.08,
               manage_ticks=False, showfliers=False,
               medianprops=dict(color=&#8221;black&#8221;))</code></pre><p>With all three layers in place we can generate the final plot:</p><pre><code>fig, ax = plt.subplots(figsize=(8, 5))
for i, (vals, c) in enumerate(zip(groups, colours)):
    add_cloud(ax, vals, i, colour=c)
    add_box(ax, vals, i)
    add_rain(ax, vals, i, colour=c)
ax.set_yticks(range(len(formations)))
ax.set_yticklabels(formations)
ax.set_xlabel(&#8221;Porosity (frac)&#8221;)
ax.set_ylabel(&#8221;Formation&#8221;)
ax.set_ylim(-0.4, len(formations) - 0.3)
plt.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!X-8n!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc114db8-ce64-4f4e-a63f-c4377681a587_612x403.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!X-8n!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc114db8-ce64-4f4e-a63f-c4377681a587_612x403.png 424w, /__u/substackcdn.com/image/fetch/$s_!X-8n!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc114db8-ce64-4f4e-a63f-c4377681a587_612x403.png 848w, /__u/substackcdn.com/image/fetch/$s_!X-8n!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc114db8-ce64-4f4e-a63f-c4377681a587_612x403.png 1272w, /__u/substackcdn.com/image/fetch/$s_!X-8n!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc114db8-ce64-4f4e-a63f-c4377681a587_612x403.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!X-8n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc114db8-ce64-4f4e-a63f-c4377681a587_612x403.png" width="612" height="403" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cc114db8-ce64-4f4e-a63f-c4377681a587_612x403.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:403,&quot;width&quot;:612,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!X-8n!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc114db8-ce64-4f4e-a63f-c4377681a587_612x403.png 424w, /__u/substackcdn.com/image/fetch/$s_!X-8n!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc114db8-ce64-4f4e-a63f-c4377681a587_612x403.png 848w, /__u/substackcdn.com/image/fetch/$s_!X-8n!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc114db8-ce64-4f4e-a63f-c4377681a587_612x403.png 1272w, /__u/substackcdn.com/image/fetch/$s_!X-8n!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc114db8-ce64-4f4e-a63f-c4377681a587_612x403.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The finished raincloud. Cloud, box, and rain together for each formation, A&#8217;s wide box sitting over two clusters and D&#8217;s tidy box over a handful of points.</figcaption></figure></div><p>For each formation we can now see the shape, the raw data, and the summary at once, and you can check them against each other.</p><p>Formation A&#8217;s whiskers still span the whole range, but now the bimodal distribution and the gap in data points between can be seen.</p><p>Formation D&#8217;s tidy little box sits over a handful of points, so you know to discount it.</p><p>The chart makes the gap between summary and data visible instead of hiding it.</p><h2><strong>The Shortcut to a Raincloud Plot</strong></h2><p>As mentioned at the start of the article, there is also a dedicated library called <strong>PtitPrince </strong>for generating this type of plot, which has different ways of displaying them. <code>ptitprince</code> (<a href="https://github.com/pog87/PtitPrince">GitHub &#8212; pog87/PtitPrince: python version of raincloud &#183; GitHub</a>) wraps all of this into roughly one call:</p><pre><code>import ptitprince as pt

pt.RainCloud(x=&#8221;formation&#8221;, y=&#8221;porosity&#8221;, data=data, orient=&#8221;h&#8221;)</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Qse7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccabd593-3fb6-4899-8fa2-9e3a91fa5ca0_500x388.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Qse7!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccabd593-3fb6-4899-8fa2-9e3a91fa5ca0_500x388.png 424w, /__u/substackcdn.com/image/fetch/$s_!Qse7!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccabd593-3fb6-4899-8fa2-9e3a91fa5ca0_500x388.png 848w, /__u/substackcdn.com/image/fetch/$s_!Qse7!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccabd593-3fb6-4899-8fa2-9e3a91fa5ca0_500x388.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Qse7!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccabd593-3fb6-4899-8fa2-9e3a91fa5ca0_500x388.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Qse7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccabd593-3fb6-4899-8fa2-9e3a91fa5ca0_500x388.png" width="500" height="388" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ccabd593-3fb6-4899-8fa2-9e3a91fa5ca0_500x388.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:388,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!Qse7!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccabd593-3fb6-4899-8fa2-9e3a91fa5ca0_500x388.png 424w, /__u/substackcdn.com/image/fetch/$s_!Qse7!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccabd593-3fb6-4899-8fa2-9e3a91fa5ca0_500x388.png 848w, /__u/substackcdn.com/image/fetch/$s_!Qse7!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccabd593-3fb6-4899-8fa2-9e3a91fa5ca0_500x388.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Qse7!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fccabd593-3fb6-4899-8fa2-9e3a91fa5ca0_500x388.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>It is a fine option, though it has occasionally lagged behind newer seaborn and matplotlib versions, so check it works in your environment. The reason to keep the hand-built version is control: orientation, spacing, colour per group, how much jitter, whether the box shows outliers. Once the three helpers exist, you own all of that.</p><h2><strong>Where The Raincloud Plot Can Fall Over</strong></h2><p>The raincloud shouldn&#8217;t be seen as a default for every situation. Beyond a handful of groups it starts to get busy, because the eye has three elements per row to track, and a ridgeline plot reads more cleanly when you only care about shape across many groups. With well log data running to tens of thousands of samples, the rain turns into a solid smear and you have to thin it with transparency or subsampling, at which point it can mean less. And as formation D shows, with very few points the cloud is mostly interpolation, so trust the rain and treat the cloud as decoration.</p><p>Used in the right place, though, the plot can be a useful tool in the toolbox.</p><p>The density cloud is what makes a raincloud a raincloud, but it isn&#8217;t always worth the extra layer. Sometimes keeping the boxplot and overlaying just the raw points tells you everything you need, especially in exploratory work when you only want to check whether the summary is hiding something. I&#8217;ve written that simpler version up on Substack, building it in seaborn with group means and sample-size labels, along with where it starts to break down.</p><p><a href="/__u/andymcdonaldgeo.substack.com/p/boxplots-are-hiding-your-data-heres">&#8594; Boxplots Are Hiding Your Data. Here&#8217;s a Simple Fix</a></p><p><strong>Reference: </strong>Allen M, Poggiali D, Whitaker K, Marshall TR, van Langen J, Kievit RA. Raincloud plots: a multi-platform tool for robust data visualization. <em>Wellcome Open Research</em> 2021, 4:63. <a href="https://wellcomeopenresearch.org/articles/4-63">https://wellcomeopenresearch.org/articles/4-63</a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[How to Visualise Missing Data with an Interactive Plotly Heatmap]]></title><description><![CDATA[Explore well log data completeness with interactive heatmaps]]></description><link>https://andymcdonaldgeo.substack.com/p/how-to-visualise-missing-data-with</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/how-to-visualise-missing-data-with</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Sat, 13 Jun 2026 15:29:27 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!LyUj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb56f54e-7247-4bc8-bd60-ab36a640295f_630x420.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!LyUj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb56f54e-7247-4bc8-bd60-ab36a640295f_630x420.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!LyUj!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb56f54e-7247-4bc8-bd60-ab36a640295f_630x420.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!LyUj!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb56f54e-7247-4bc8-bd60-ab36a640295f_630x420.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!LyUj!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb56f54e-7247-4bc8-bd60-ab36a640295f_630x420.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!LyUj!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb56f54e-7247-4bc8-bd60-ab36a640295f_630x420.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!LyUj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb56f54e-7247-4bc8-bd60-ab36a640295f_630x420.jpeg" width="630" height="420" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cb56f54e-7247-4bc8-bd60-ab36a640295f_630x420.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:420,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!LyUj!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb56f54e-7247-4bc8-bd60-ab36a640295f_630x420.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!LyUj!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb56f54e-7247-4bc8-bd60-ab36a640295f_630x420.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!LyUj!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb56f54e-7247-4bc8-bd60-ab36a640295f_630x420.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!LyUj!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb56f54e-7247-4bc8-bd60-ab36a640295f_630x420.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Understanding data completeness, is an essential part of any data analysis workflow. In the case of well log data, we often have similarly acquired datasets in multiple wells. One way I like to check this is by using a completeness heatmap.</p><p>At a glance the data completeness heatmap tells you which curves (or columns) you have across multiple measurement locations. In this example we are using different wells.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Form this we can also tell which curves are missing from which wells, and whether the gaps are concentrated in a particular formation or scattered across the well.</p><p>In my previous <a href="/__u/andymcdonaldgeo.substack.com/p/from-default-to-publication-ready">article</a>, I have focused on how to improve the look of static matplotlib figures and there is a problem with them.</p><p>That problem is that static figures, even well-made ones, make your brain work to answer follow-up questions. In cases where we have a large amount of columns and rows in our heatmap, you simply can&#8217;t hover over a cell and see exactly which well (row) and curve (column) combination you&#8217;re looking at. Instead you have to look up which column and row you are in.</p><p><a href="https://medium.com/plotly.com/python/">Plotly</a>, another excellent Python plotting library fixes most of that.</p><p>In this article we will walk through building an interactive well log completeness heatmap from scratch, with hover tooltips, cell labels, white borders, and a multi-formation panel.</p><p>Although this example uses well log data, the same approach works anywhere you need to compare data completeness across repeated measurement locations, whether that means wells, stations, assets, samples, or sites.</p><h2><strong>The Data</strong></h2><p>First, we will create a dataset. This dataset is a synthetic set of 8 wells, 10 curves, and 4 formations. Ideally, you would need to load and compute these completeness values fo your own dataset.</p><pre><code>import pandas as pd

wells  = [&#8217;Ardmore-1&#8217;, &#8216;Ardmore-4&#8217;, &#8216;Brindal-1&#8217;, &#8216;Brindal-3&#8217;,
          &#8216;Brindal-5&#8217;, &#8216;Brindal-8&#8217;, &#8216;Caxton-2&#8217;,  &#8216;Caxton-6&#8217;]

curves = [&#8217;GR&#8217;, &#8216;RDEP&#8217;, &#8216;RSHA&#8217;, &#8216;RHOB&#8217;, &#8216;NPHI&#8217;, &#8216;DTC&#8217;, &#8216;DTS&#8217;, &#8216;PEF&#8217;, &#8216;CALI&#8217;, &#8216;SP&#8217;]

completeness_data = {
    &#8216;Thornfield&#8217;: [
        [100.0, 100.0, 100.0, 26.2, 100.0, 40.8, 61.5, 49.5, 100.0, 54.0],  # Ardmore-1
        [100.0, 100.0, 100.0, 28.0, 100.0, 37.2, 60.8, 52.8, 100.0, 54.5],  # Ardmore-4
        [100.0, 100.0, 100.0, 31.5, 100.0, 38.8, 59.8, 50.5, 100.0, 54.0],  # Brindal-1
        [100.0, 100.0, 100.0, 29.8, 100.0, 41.5, 60.0, 46.5, 100.0, 52.8],  # Brindal-3
        [100.0, 100.0, 100.0, 30.2, 100.0, 37.5, 59.0, 52.2, 100.0, 56.5],  # Brindal-5
        [100.0, 100.0, 100.0, 31.0, 100.0, 40.2, 59.0, 53.5, 100.0, 55.8],  # Brindal-8
        [100.0, 100.0, 100.0, 35.2, 100.0, 37.5, 58.0, 50.7, 100.0, 54.8],  # Caxton-2
        [100.0, 100.0, 100.0, 31.0, 100.0, 43.2, 62.5, 46.5, 100.0, 54.2],  # Caxton-6
    ],
    &#8216;Veldwick&#8217;: [
        [100.0, 100.0, 100.0, 75.0, 100.0, 65.1, 59.8, 52.1, 100.0, 57.9],  # Ardmore-1
        [100.0, 100.0, 100.0, 87.0, 100.0, 73.8, 60.1, 47.8, 100.0, 53.2],  # Ardmore-4
        [100.0, 100.0, 100.0, 84.0, 100.0, 72.0, 62.0, 50.2, 100.0, 58.1],  # Brindal-1
        [100.0, 100.0, 100.0, 82.0, 100.0, 69.6, 62.6, 49.9, 100.0, 56.4],  # Brindal-3
        [100.0, 100.0, 100.0, 81.5, 100.0, 69.9, 59.6, 50.1, 100.0, 52.8],  # Brindal-5
        [100.0, 100.0, 100.0, 88.6, 100.0, 73.5, 59.4, 49.0, 100.0, 55.6],  # Brindal-8
        [100.0, 100.0, 100.0, 75.1, 100.0, 64.5, 59.6, 49.4, 100.0, 58.2],  # Caxton-2
        [100.0, 100.0, 100.0, 84.9, 100.0, 73.0, 61.0, 47.5, 100.0, 55.4],  # Caxton-6
    ],
    &#8216;Oskarby&#8217;: [
        [100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 80.4, 84.4, 100.0, 52.8],  # Ardmore-1
        [100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 85.5, 93.4, 100.0, 58.3],  # Ardmore-4
        [100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 84.1, 91.2, 100.0, 52.3],  # Brindal-1
        [100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 83.2, 90.9, 100.0, 56.1],  # Brindal-3
        [100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 84.0, 88.9, 100.0, 54.3],  # Brindal-5
        [100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 88.6, 93.4, 100.0, 53.7],  # Brindal-8
        [100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 80.3, 86.3, 100.0, 53.3],  # Caxton-2
        [100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 86.2, 91.3, 100.0, 55.0],  # Caxton-6
    ],
    &#8216;Dravenport&#8217;: [
        [100.0, 100.0, 100.0,  80.0,  80.0, 100.0, 100.0, 100.0, 100.0, 54.5],  # Ardmore-1
        [100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 56.5],  # Ardmore-4
        [100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 52.8],  # Brindal-1
        [100.0, 100.0, 100.0,  80.0,  80.0, 100.0, 100.0, 100.0, 100.0, 55.8],  # Brindal-3
        [100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 52.5],  # Brindal-5
        [100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 52.8],  # Brindal-8
        [100.0, 100.0, 100.0,  80.0,  80.0, 100.0, 100.0, 100.0, 100.0, 55.2],  # Caxton-2
        [100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 55.9],  # Caxton-6
    ],
}</code></pre><p>One line to turn that into the <code>results</code> dict the rest of the article uses. In this dictionary we have a pandas dataframe per formation, wells as rows, curves as columns:</p><pre><code>results = {
    formation: pd.DataFrame(matrix, index=wells, columns=curves)
    for formation, matrix in completeness_data.items()
}

formation_names = list(results.keys())</code></pre><h2><strong>Displaying the Basic Heatmap with px.imshow()</strong></h2><p>To begin displaying our heatmap we can call upon <code>px.imshow()</code> .</p><p><code>px.imshow()</code> is Plotly Express&#8217;s equivalent of matplotlib&#8217;s <code>ax.imshow()</code>. You pass it a 2D array or dataframe and it renders a colour-mapped grid.</p><pre><code>import plotly.express as px

completeness_pivot = results[&#8217;Veldwick&#8217;]
fig = px.imshow(
    completeness_pivot,
    color_continuous_scale=&#8221;RdYlGn&#8221;,
    zmin=0,
    zmax=100,
    labels={&#8221;color&#8221;: &#8220;Completeness (%)&#8221;},
    title=&#8221;Well Log Completeness - Veldwick&#8221;,
)
fig.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!grMW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87618372-b9ac-4717-b189-48bc1ea0a287_610x324.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!grMW!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87618372-b9ac-4717-b189-48bc1ea0a287_610x324.png 424w, /__u/substackcdn.com/image/fetch/$s_!grMW!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87618372-b9ac-4717-b189-48bc1ea0a287_610x324.png 848w, /__u/substackcdn.com/image/fetch/$s_!grMW!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87618372-b9ac-4717-b189-48bc1ea0a287_610x324.png 1272w, /__u/substackcdn.com/image/fetch/$s_!grMW!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87618372-b9ac-4717-b189-48bc1ea0a287_610x324.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!grMW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87618372-b9ac-4717-b189-48bc1ea0a287_610x324.png" width="610" height="324" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/87618372-b9ac-4717-b189-48bc1ea0a287_610x324.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:324,&quot;width&quot;:610,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!grMW!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87618372-b9ac-4717-b189-48bc1ea0a287_610x324.png 424w, /__u/substackcdn.com/image/fetch/$s_!grMW!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87618372-b9ac-4717-b189-48bc1ea0a287_610x324.png 848w, /__u/substackcdn.com/image/fetch/$s_!grMW!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87618372-b9ac-4717-b189-48bc1ea0a287_610x324.png 1272w, /__u/substackcdn.com/image/fetch/$s_!grMW!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87618372-b9ac-4717-b189-48bc1ea0a287_610x324.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Single-formation completeness heatmap for Veldwick using <code>px.imshow()</code>.</p><p>The <code>zmin</code> and <code>zmax</code> arguments pin the colour scale to 0-100, which matters when you&#8217;re comparing formations.</p><p>Without these values being defined, Plotly autoscales to the data range.</p><h2><strong>Adding Hover Tooltips</strong></h2><p>By default <code>px.imshow()</code> shows the x, y, and z values on hover.</p><p>To control exactly what appears, and how it&#8217;s formatted we need to pass a <code>hovertemplate</code> via <code>update_traces</code>. The formatting can include regular HTML syntax, which makes it easy to style, and we can easily call upon the x and y values with <code>%{y}</code> , which is the well (row) and <code>%{x}</code> which is the curve (column).</p><pre><code>fig = px.imshow(
    completeness_pivot,
    color_continuous_scale=&#8221;RdYlGn&#8221;,
    zmin=0,
    zmax=100,
    labels={&#8221;color&#8221;: &#8220;Completeness (%)&#8221;},
    title=&#8221;Well Log Completeness &#8212; Veldwick&#8221;,
)

fig.update_traces(
    hovertemplate=(
        &#8220;&lt;b&gt;Well:&lt;/b&gt; %{y}&lt;br&gt;&#8221;
        &#8220;&lt;b&gt;Curve:&lt;/b&gt; %{x}&lt;br&gt;&#8221;
        &#8220;&lt;b&gt;Completeness:&lt;/b&gt; %{z:.1f}%&lt;extra&gt;&lt;/extra&gt;&#8221;
    ),
)
fig.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!T4og!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa52fd93-a140-4d83-ab3b-8806320c7662_535x271.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!T4og!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa52fd93-a140-4d83-ab3b-8806320c7662_535x271.png 424w, /__u/substackcdn.com/image/fetch/$s_!T4og!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa52fd93-a140-4d83-ab3b-8806320c7662_535x271.png 848w, /__u/substackcdn.com/image/fetch/$s_!T4og!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa52fd93-a140-4d83-ab3b-8806320c7662_535x271.png 1272w, /__u/substackcdn.com/image/fetch/$s_!T4og!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa52fd93-a140-4d83-ab3b-8806320c7662_535x271.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!T4og!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa52fd93-a140-4d83-ab3b-8806320c7662_535x271.png" width="535" height="271" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fa52fd93-a140-4d83-ab3b-8806320c7662_535x271.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:271,&quot;width&quot;:535,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!T4og!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa52fd93-a140-4d83-ab3b-8806320c7662_535x271.png 424w, /__u/substackcdn.com/image/fetch/$s_!T4og!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa52fd93-a140-4d83-ab3b-8806320c7662_535x271.png 848w, /__u/substackcdn.com/image/fetch/$s_!T4og!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa52fd93-a140-4d83-ab3b-8806320c7662_535x271.png 1272w, /__u/substackcdn.com/image/fetch/$s_!T4og!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffa52fd93-a140-4d83-ab3b-8806320c7662_535x271.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Custom hover labels showing well, curve, and completeness.</figcaption></figure></div><p>The <code>&lt;extra&gt;&lt;/extra&gt;</code> tag removes the trace name Plotly appends by default.</p><p><code>%{z:.1f}</code> formats the data values to one decimal place, which is enough precision to be useful for this figure.</p><h2><strong>Cell Labels and Borders</strong></h2><p>The hover tooltip is useful when you&#8217;re exploring, but for a published figure it helps to have the percentages visible directly on the chart, in the same way the matplotlib version had annotations in each cell.</p><p>We can do this using <code>texttemplate</code>.</p><p>Also, by adding <code>xgap</code> and <code>ygap</code> we can add pixel-width gaps between cells, which Plotly fills with the background colour. Set the background to white and you get clean white borders.</p><pre><code>fig = px.imshow(
    completeness_pivot,
    color_continuous_scale=&#8221;RdYlGn&#8221;,
    zmin=0,
    zmax=100,
    labels={&#8221;color&#8221;: &#8220;Completeness (%)&#8221;},
    title=&#8221;Well Log Completeness &#8212; Veldwick&#8221;,
)

fig.update_traces(
    xgap=2,
    ygap=2,
    texttemplate=&#8221;%{z:.0f}%&#8221;,
    textfont={&#8221;size&#8221;: 10},
    hovertemplate=(
        &#8220;&lt;b&gt;Well:&lt;/b&gt; %{y}&lt;br&gt;&#8221;
        &#8220;&lt;b&gt;Curve:&lt;/b&gt; %{x}&lt;br&gt;&#8221;
        &#8220;&lt;b&gt;Completeness:&lt;/b&gt; %{z:.1f}%&lt;extra&gt;&lt;/extra&gt;&#8221;
    ),
)
fig.update_layout(
    xaxis_title=&#8221;Curve&#8221;,
    yaxis_title=&#8221;Well&#8221;,
    font={&#8221;family&#8221;: &#8220;monospace&#8221;},
    plot_bgcolor=&#8221;white&#8221;,
    paper_bgcolor=&#8221;white&#8221;,
    width=900,
    height=500,
)
fig.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!gXda!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cba3fd0-4cde-458f-9433-58d5c3ee03be_490x368.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!gXda!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cba3fd0-4cde-458f-9433-58d5c3ee03be_490x368.png 424w, /__u/substackcdn.com/image/fetch/$s_!gXda!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cba3fd0-4cde-458f-9433-58d5c3ee03be_490x368.png 848w, /__u/substackcdn.com/image/fetch/$s_!gXda!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cba3fd0-4cde-458f-9433-58d5c3ee03be_490x368.png 1272w, /__u/substackcdn.com/image/fetch/$s_!gXda!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cba3fd0-4cde-458f-9433-58d5c3ee03be_490x368.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!gXda!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cba3fd0-4cde-458f-9433-58d5c3ee03be_490x368.png" width="490" height="368" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2cba3fd0-4cde-458f-9433-58d5c3ee03be_490x368.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:368,&quot;width&quot;:490,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!gXda!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cba3fd0-4cde-458f-9433-58d5c3ee03be_490x368.png 424w, /__u/substackcdn.com/image/fetch/$s_!gXda!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cba3fd0-4cde-458f-9433-58d5c3ee03be_490x368.png 848w, /__u/substackcdn.com/image/fetch/$s_!gXda!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cba3fd0-4cde-458f-9433-58d5c3ee03be_490x368.png 1272w, /__u/substackcdn.com/image/fetch/$s_!gXda!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2cba3fd0-4cde-458f-9433-58d5c3ee03be_490x368.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Cell Labels and Borders: Veldwick heatmap with white cell borders, percentage labels in each cell, and monospace font</figcaption></figure></div><p><code>%{z:.0f}%</code> in the cell label rounds to a whole number, which can avoid unnecessary decimal places. The monospace font stops the percentage values shifting around as the digit count changes across cells.</p><h2><strong>Switching Formations with a Dropdown</strong></h2><p>In a Jupyter notebook, you can wire the figure up to a formation selector using <code>ipywidgets</code>.</p><p>The <code>@interact</code> decorator creates a dropdown from the list and re-runs the function every time the selection changes. The figure title updates automatically since the formation name is passed as a variable.</p><p>I find this a great way to switch between different formations or groups of data, especially when we are dealing with a large number of groups.</p><pre><code>from ipywidgets import interact

@interact(formation=formation_names)
def plot_formation(formation=&#8217;Veldwick&#8217;):
    pivot = results[formation]
    fig = px.imshow(
        pivot,
        color_continuous_scale=&#8221;RdYlGn&#8221;,
        zmin=0,
        zmax=100,
        labels={&#8221;color&#8221;: &#8220;Completeness (%)&#8221;},
        title=f&#8221;Well Log Completeness - {formation}&#8221;,
    )
    fig.update_traces(
        xgap=2,
        ygap=2,
        texttemplate=&#8221;%{z:.0f}%&#8221;,
        textfont={&#8221;size&#8221;: 10},
        hovertemplate=(
            &#8220;&lt;b&gt;Well:&lt;/b&gt; %{y}&lt;br&gt;&#8221;
            &#8220;&lt;b&gt;Curve:&lt;/b&gt; %{x}&lt;br&gt;&#8221;
            &#8220;&lt;b&gt;Completeness:&lt;/b&gt; %{z:.1f}%&lt;extra&gt;&lt;/extra&gt;&#8221;
        ),
    )
    fig.update_layout(
        xaxis_title=&#8221;Curve&#8221;,
        yaxis_title=&#8221;Well&#8221;,
        font={&#8221;family&#8221;: &#8220;monospace&#8221;},
        plot_bgcolor=&#8221;white&#8221;,
        paper_bgcolor=&#8221;white&#8221;,
        width=900,
        height=500,
    )
    fig.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!nhQe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd969a9b1-8041-4e68-80e9-178fc10f51d5_495x411.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!nhQe!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd969a9b1-8041-4e68-80e9-178fc10f51d5_495x411.png 424w, /__u/substackcdn.com/image/fetch/$s_!nhQe!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd969a9b1-8041-4e68-80e9-178fc10f51d5_495x411.png 848w, /__u/substackcdn.com/image/fetch/$s_!nhQe!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd969a9b1-8041-4e68-80e9-178fc10f51d5_495x411.png 1272w, /__u/substackcdn.com/image/fetch/$s_!nhQe!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd969a9b1-8041-4e68-80e9-178fc10f51d5_495x411.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!nhQe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd969a9b1-8041-4e68-80e9-178fc10f51d5_495x411.png" width="495" height="411" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d969a9b1-8041-4e68-80e9-178fc10f51d5_495x411.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:411,&quot;width&quot;:495,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!nhQe!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd969a9b1-8041-4e68-80e9-178fc10f51d5_495x411.png 424w, /__u/substackcdn.com/image/fetch/$s_!nhQe!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd969a9b1-8041-4e68-80e9-178fc10f51d5_495x411.png 848w, /__u/substackcdn.com/image/fetch/$s_!nhQe!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd969a9b1-8041-4e68-80e9-178fc10f51d5_495x411.png 1272w, /__u/substackcdn.com/image/fetch/$s_!nhQe!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd969a9b1-8041-4e68-80e9-178fc10f51d5_495x411.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Interactive selector switching between Thornfield, Veldwick, Oskarby, and Dravenport formations</figcaption></figure></div><p>This is a Jupyter-specific feature and it won&#8217;t work in a plain Python script or Streamlit app.</p><p>For Streamlit, the equivalent is <code>st.selectbox</code>, which feeds into a similar function. The multi-formation panel in the next section is a better fit if you want all four formations visible at once.</p><h2><strong>Faceting Across Formations</strong></h2><p>The matplotlib version handled multiple formations with a subplot grid, where we have one axis per formation, arranged in a column.</p><p>Although <code>px.imshow()</code> supports some faceting use cases, in this example I am going to use <code>plotly.graph_objects</code> with <code>make_subplots</code>, because we already have one 2D table per formation.</p><p>The <code>results</code> dict from the data section already has one pivot per formation, so no reshaping needed.</p><p>We can build the subplot figure using <code>make_subplots</code>, and then adding a <code>Heatmap</code> trace for each formation:</p><pre><code>from plotly.subplots import make_subplots
import plotly.graph_objects as go

fig = make_subplots(
    rows=len(formation_names),
    cols=1,
    subplot_titles=list(formation_names),
    shared_xaxes=True,
    vertical_spacing=0.06,
)
for i, formation in enumerate(formation_names, start=1):
    pivot = results[formation]
    z_vals = pivot.values
    fig.add_trace(
        go.Heatmap(
            z=z_vals,
            x=pivot.columns.tolist(),
            y=pivot.index.tolist(),
            colorscale=&#8221;RdYlGn&#8221;,
            zmin=0,
            zmax=100,
            xgap=2,
            ygap=2,
            showscale=(i == 1),
            colorbar=dict(
                title=&#8221;Completeness (%)&#8221;,
                len=0.25,
                y=0.88,
            ) if i == 1 else {},
            text=[[f&#8221;{v:.0f}%&#8221; for v in row] for row in z_vals],
            texttemplate=&#8221;%{text}&#8221;,
            textfont={&#8221;size&#8221;: 9, &#8220;family&#8221;: &#8220;monospace&#8221;},
            hovertemplate=(
                &#8220;&lt;b&gt;Well:&lt;/b&gt; %{y}&lt;br&gt;&#8221;
                &#8220;&lt;b&gt;Curve:&lt;/b&gt; %{x}&lt;br&gt;&#8221;
                &#8220;&lt;b&gt;Formation:&lt;/b&gt; &#8220; + formation + &#8220;&lt;br&gt;&#8221;
                &#8220;&lt;b&gt;Completeness:&lt;/b&gt; %{z:.1f}%&lt;extra&gt;&lt;/extra&gt;&#8221;
            ),
        ),
        row=i,
        col=1,
    )
fig.update_layout(
    height=290 * len(formation_names),
    title_text=&#8221;Well Log Completeness by Formation&#8221;,
    title_font_size=16,
    font={&#8221;family&#8221;: &#8220;monospace&#8221;},
    plot_bgcolor=&#8221;white&#8221;,
    paper_bgcolor=&#8221;white&#8221;,
)
fig.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!eYQW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5785a59a-ca30-46f1-81de-0c91e88cc241_630x753.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!eYQW!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5785a59a-ca30-46f1-81de-0c91e88cc241_630x753.png 424w, /__u/substackcdn.com/image/fetch/$s_!eYQW!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5785a59a-ca30-46f1-81de-0c91e88cc241_630x753.png 848w, /__u/substackcdn.com/image/fetch/$s_!eYQW!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5785a59a-ca30-46f1-81de-0c91e88cc241_630x753.png 1272w, /__u/substackcdn.com/image/fetch/$s_!eYQW!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5785a59a-ca30-46f1-81de-0c91e88cc241_630x753.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!eYQW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5785a59a-ca30-46f1-81de-0c91e88cc241_630x753.png" width="630" height="753" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5785a59a-ca30-46f1-81de-0c91e88cc241_630x753.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:753,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!eYQW!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5785a59a-ca30-46f1-81de-0c91e88cc241_630x753.png 424w, /__u/substackcdn.com/image/fetch/$s_!eYQW!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5785a59a-ca30-46f1-81de-0c91e88cc241_630x753.png 848w, /__u/substackcdn.com/image/fetch/$s_!eYQW!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5785a59a-ca30-46f1-81de-0c91e88cc241_630x753.png 1272w, /__u/substackcdn.com/image/fetch/$s_!eYQW!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5785a59a-ca30-46f1-81de-0c91e88cc241_630x753.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Four-panel stacked heatmap (Thornfield, Veldwick, Oskarby, Dravenport) with white cell borders, percentage labels, shared x-axis, and formation-aware hover tooltips</p><p>The <code>shared_xaxes=True</code> argument keeps curve columns aligned across formations so that the same curve appears in the same column regardless of which panel you&#8217;re looking at, which makes vertical comparison straightforward.</p><p>The <code>showscale</code> logic avoids repeating the colour bar across every subplot.</p><h2><strong>Embedding in Streamlit</strong></h2><p>If you&#8217;re building a well review tool or a data QC dashboard, the Plotly figure drops into Streamlit with a single call:</p><pre><code>import streamlit as st

st.title(&#8221;Well Log Completeness Review&#8221;)
st.plotly_chart(fig, use_container_width=True)</code></pre><p><code>use_container_width=True</code> makes the figure fill the column width, which looks better than Plotly&#8217;s fixed default.</p><p>From there you can wire up Streamlit widgets such as formation selectors, well filters, a date picker and the figure will rebuild.</p><p>This isn&#8217;t a Streamlit tutorial, so I&#8217;ll leave the wiring there. If you&#8217;re already working in Streamlit, the move from notebook figure to deployed tool is not much work.</p><h2><strong>What the Switch to Plotly Actually Changes</strong></h2><p>In a static heatmap, checking a specific cell means tracing the row and column yourself. That works for small plots, but it becomes slower as the number of wells, curves, or groups increases. With Plotly, the hover tooltip brings the well, curve, formation, and completeness value directly to you.</p><p>There is still a trade-off. Static matplotlib figures are often better for reports, papers, and slide decks. Plotly is better when the figure is part of an active review, notebook workflow, or dashboard.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Why Your Jupyter Notebooks Are Harder to Read Than They Should Be]]></title><description><![CDATA[Working code is only half the job. The reasoning needs to be readable too.]]></description><link>https://andymcdonaldgeo.substack.com/p/why-your-jupyter-notebooks-are-harder</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/why-your-jupyter-notebooks-are-harder</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Sun, 17 May 2026 10:43:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!NXPf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc415ef32-5eb0-4dc5-be1e-2002b0a81ecd_630x350.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!NXPf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc415ef32-5eb0-4dc5-be1e-2002b0a81ecd_630x350.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!NXPf!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc415ef32-5eb0-4dc5-be1e-2002b0a81ecd_630x350.png 424w, /__u/substackcdn.com/image/fetch/$s_!NXPf!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc415ef32-5eb0-4dc5-be1e-2002b0a81ecd_630x350.png 848w, /__u/substackcdn.com/image/fetch/$s_!NXPf!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc415ef32-5eb0-4dc5-be1e-2002b0a81ecd_630x350.png 1272w, /__u/substackcdn.com/image/fetch/$s_!NXPf!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc415ef32-5eb0-4dc5-be1e-2002b0a81ecd_630x350.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!NXPf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc415ef32-5eb0-4dc5-be1e-2002b0a81ecd_630x350.png" width="630" height="350" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c415ef32-5eb0-4dc5-be1e-2002b0a81ecd_630x350.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:350,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!NXPf!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc415ef32-5eb0-4dc5-be1e-2002b0a81ecd_630x350.png 424w, /__u/substackcdn.com/image/fetch/$s_!NXPf!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc415ef32-5eb0-4dc5-be1e-2002b0a81ecd_630x350.png 848w, /__u/substackcdn.com/image/fetch/$s_!NXPf!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc415ef32-5eb0-4dc5-be1e-2002b0a81ecd_630x350.png 1272w, /__u/substackcdn.com/image/fetch/$s_!NXPf!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc415ef32-5eb0-4dc5-be1e-2002b0a81ecd_630x350.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Jupyter notebooks can become a chaotic mess after a few hours of work.</p><p>All of the focus is initially on getting the code to work, to display some pretty outputs or to visualise machine learning model metrics. Reworking the the code into something that others (or yourself in six months time) can follow is often forgotten.</p><p>The code might be correct, and the the analysis might hold up, but anyone picking it up cold is left to reconstruct your reasoning and thinking from scratch.</p><p>They start to ask questions like</p><ul><li><p>What was filtered out and why?</p></li><li><p>What does that intermediate result means?</p></li><li><p>Why one step leads to the next.</p></li></ul><p>Even when the answers are technically somewhere in the code, they can take far too long to find. The problem is not usually the analysis itself, but rather it is the missing context around it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/subscribe"><span>Subscribe now</span></a></p><h2><strong>The gaps between cells</strong></h2><p>A notebook without narrative tends to look like this: imports, data load, some filtering, a plot, more filtering, another plot.</p><p>The reader doesn&#8217;t know why you did any of these, because you didn&#8217;t say. When you wrote it, you didn&#8217;t need to as it was obvious to you at the time.</p><p>A sentence or two between logical steps is usually enough to close the understanding gap, for example</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!8v7i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb670c160-659b-4bde-af49-aa9139b2f587_630x289.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!8v7i!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb670c160-659b-4bde-af49-aa9139b2f587_630x289.png 424w, /__u/substackcdn.com/image/fetch/$s_!8v7i!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb670c160-659b-4bde-af49-aa9139b2f587_630x289.png 848w, /__u/substackcdn.com/image/fetch/$s_!8v7i!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb670c160-659b-4bde-af49-aa9139b2f587_630x289.png 1272w, /__u/substackcdn.com/image/fetch/$s_!8v7i!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb670c160-659b-4bde-af49-aa9139b2f587_630x289.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!8v7i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb670c160-659b-4bde-af49-aa9139b2f587_630x289.png" width="630" height="289" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b670c160-659b-4bde-af49-aa9139b2f587_630x289.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:289,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!8v7i!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb670c160-659b-4bde-af49-aa9139b2f587_630x289.png 424w, /__u/substackcdn.com/image/fetch/$s_!8v7i!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb670c160-659b-4bde-af49-aa9139b2f587_630x289.png 848w, /__u/substackcdn.com/image/fetch/$s_!8v7i!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb670c160-659b-4bde-af49-aa9139b2f587_630x289.png 1272w, /__u/substackcdn.com/image/fetch/$s_!8v7i!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb670c160-659b-4bde-af49-aa9139b2f587_630x289.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>That markdown cell took thirty seconds to write and it tells the reader what you saw, what you decided, and why the next cell exists. Without it, they would still be guessing and trying to follow what was happening based on the code.</p><h2><strong>Magic numbers are hidden reasoning</strong></h2><p>One of the more common sources of confusion in notebooks is the unnamed constant. A threshold, a cutoff, a multiplier chosen for a good reason at the time of writing now becomes invisible.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Ofzk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c944c-7c3f-4bfb-a8fe-0cdb1c2209d5_630x106.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Ofzk!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c944c-7c3f-4bfb-a8fe-0cdb1c2209d5_630x106.png 424w, /__u/substackcdn.com/image/fetch/$s_!Ofzk!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c944c-7c3f-4bfb-a8fe-0cdb1c2209d5_630x106.png 848w, /__u/substackcdn.com/image/fetch/$s_!Ofzk!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c944c-7c3f-4bfb-a8fe-0cdb1c2209d5_630x106.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Ofzk!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c944c-7c3f-4bfb-a8fe-0cdb1c2209d5_630x106.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Ofzk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c944c-7c3f-4bfb-a8fe-0cdb1c2209d5_630x106.png" width="630" height="106" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/627c944c-7c3f-4bfb-a8fe-0cdb1c2209d5_630x106.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:106,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!Ofzk!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c944c-7c3f-4bfb-a8fe-0cdb1c2209d5_630x106.png 424w, /__u/substackcdn.com/image/fetch/$s_!Ofzk!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c944c-7c3f-4bfb-a8fe-0cdb1c2209d5_630x106.png 848w, /__u/substackcdn.com/image/fetch/$s_!Ofzk!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c944c-7c3f-4bfb-a8fe-0cdb1c2209d5_630x106.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Ofzk!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F627c944c-7c3f-4bfb-a8fe-0cdb1c2209d5_630x106.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Where did 15 come from? What&#8217;s 120? Is 0.4 a standard cutoff or something specific to this well?</p><p>Compare that to:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!TX06!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95d78cce-f60f-4adc-9b62-6f154616a1d9_630x159.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!TX06!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95d78cce-f60f-4adc-9b62-6f154616a1d9_630x159.png 424w, /__u/substackcdn.com/image/fetch/$s_!TX06!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95d78cce-f60f-4adc-9b62-6f154616a1d9_630x159.png 848w, /__u/substackcdn.com/image/fetch/$s_!TX06!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95d78cce-f60f-4adc-9b62-6f154616a1d9_630x159.png 1272w, /__u/substackcdn.com/image/fetch/$s_!TX06!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95d78cce-f60f-4adc-9b62-6f154616a1d9_630x159.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!TX06!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95d78cce-f60f-4adc-9b62-6f154616a1d9_630x159.png" width="630" height="159" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/95d78cce-f60f-4adc-9b62-6f154616a1d9_630x159.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:159,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!TX06!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95d78cce-f60f-4adc-9b62-6f154616a1d9_630x159.png 424w, /__u/substackcdn.com/image/fetch/$s_!TX06!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95d78cce-f60f-4adc-9b62-6f154616a1d9_630x159.png 848w, /__u/substackcdn.com/image/fetch/$s_!TX06!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95d78cce-f60f-4adc-9b62-6f154616a1d9_630x159.png 1272w, /__u/substackcdn.com/image/fetch/$s_!TX06!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95d78cce-f60f-4adc-9b62-6f154616a1d9_630x159.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Same values, same result. The second version takes maybe two extra minutes to write, but anyone reading it can see what the numbers represent and where they came from. If someone needs to change the cutoff, they know exactly where to look.</p><p>Burying the numbers in the equations and code makes the notebook harder to audit and harder to hand over.</p><h2><strong>Cells that do too many things</strong></h2><p>The overloaded cell is easy to miss when everything is working.</p><p>Imports, data loading, column renaming, filtering, and a plot all in one block. It&#8217;s fine until something breaks and you have to unpick the whole thing to find it. An example of this can be seen below.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!RPrM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbf60c3-bc10-41a3-b016-bedba6bb029a_447x205.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!RPrM!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbf60c3-bc10-41a3-b016-bedba6bb029a_447x205.png 424w, /__u/substackcdn.com/image/fetch/$s_!RPrM!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbf60c3-bc10-41a3-b016-bedba6bb029a_447x205.png 848w, /__u/substackcdn.com/image/fetch/$s_!RPrM!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbf60c3-bc10-41a3-b016-bedba6bb029a_447x205.png 1272w, /__u/substackcdn.com/image/fetch/$s_!RPrM!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbf60c3-bc10-41a3-b016-bedba6bb029a_447x205.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!RPrM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbf60c3-bc10-41a3-b016-bedba6bb029a_447x205.png" width="447" height="205" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6dbf60c3-bc10-41a3-b016-bedba6bb029a_447x205.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:205,&quot;width&quot;:447,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!RPrM!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbf60c3-bc10-41a3-b016-bedba6bb029a_447x205.png 424w, /__u/substackcdn.com/image/fetch/$s_!RPrM!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbf60c3-bc10-41a3-b016-bedba6bb029a_447x205.png 848w, /__u/substackcdn.com/image/fetch/$s_!RPrM!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbf60c3-bc10-41a3-b016-bedba6bb029a_447x205.png 1272w, /__u/substackcdn.com/image/fetch/$s_!RPrM!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbf60c3-bc10-41a3-b016-bedba6bb029a_447x205.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Breaking it into logical units doesn&#8217;t mean every line gets its own cell. It means grouping by purpose and giving each group a brief header.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!-Q08!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc93ac6-ca04-4442-ad64-3e382e32e209_494x714.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!-Q08!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc93ac6-ca04-4442-ad64-3e382e32e209_494x714.png 424w, /__u/substackcdn.com/image/fetch/$s_!-Q08!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc93ac6-ca04-4442-ad64-3e382e32e209_494x714.png 848w, /__u/substackcdn.com/image/fetch/$s_!-Q08!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc93ac6-ca04-4442-ad64-3e382e32e209_494x714.png 1272w, /__u/substackcdn.com/image/fetch/$s_!-Q08!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc93ac6-ca04-4442-ad64-3e382e32e209_494x714.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!-Q08!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc93ac6-ca04-4442-ad64-3e382e32e209_494x714.png" width="494" height="714" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8cc93ac6-ca04-4442-ad64-3e382e32e209_494x714.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:714,&quot;width&quot;:494,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!-Q08!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc93ac6-ca04-4442-ad64-3e382e32e209_494x714.png 424w, /__u/substackcdn.com/image/fetch/$s_!-Q08!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc93ac6-ca04-4442-ad64-3e382e32e209_494x714.png 848w, /__u/substackcdn.com/image/fetch/$s_!-Q08!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc93ac6-ca04-4442-ad64-3e382e32e209_494x714.png 1272w, /__u/substackcdn.com/image/fetch/$s_!-Q08!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cc93ac6-ca04-4442-ad64-3e382e32e209_494x714.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The code itself hasn&#8217;t changed, but it has become much easier to navigate, easier to debug when something breaks, and easier for someone else to work out what&#8217;s happening at each step.</p><p><em>Grouping cells by purpose also makes a notebook easier to return to months later. I&#8217;ve written about the naming and folder conventions that make that work in practice on Substack. Link at the end.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>Unlabelled outputs are just as opaque</strong></h2><p>The same readability problem that affects code also affects outputs. A plot with no title, no axis labels, and no surrounding explanation is the visual equivalent of a magic number. The person who made it knows what it shows, but it can be difficult for anybody else to work out what the chart is trying to show.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!rEN2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d2f8674-ea77-454d-8699-98f22556b3d7_509x649.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!rEN2!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d2f8674-ea77-454d-8699-98f22556b3d7_509x649.png 424w, /__u/substackcdn.com/image/fetch/$s_!rEN2!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d2f8674-ea77-454d-8699-98f22556b3d7_509x649.png 848w, /__u/substackcdn.com/image/fetch/$s_!rEN2!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d2f8674-ea77-454d-8699-98f22556b3d7_509x649.png 1272w, /__u/substackcdn.com/image/fetch/$s_!rEN2!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d2f8674-ea77-454d-8699-98f22556b3d7_509x649.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!rEN2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d2f8674-ea77-454d-8699-98f22556b3d7_509x649.png" width="509" height="649" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6d2f8674-ea77-454d-8699-98f22556b3d7_509x649.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:649,&quot;width&quot;:509,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!rEN2!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d2f8674-ea77-454d-8699-98f22556b3d7_509x649.png 424w, /__u/substackcdn.com/image/fetch/$s_!rEN2!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d2f8674-ea77-454d-8699-98f22556b3d7_509x649.png 848w, /__u/substackcdn.com/image/fetch/$s_!rEN2!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d2f8674-ea77-454d-8699-98f22556b3d7_509x649.png 1272w, /__u/substackcdn.com/image/fetch/$s_!rEN2!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d2f8674-ea77-454d-8699-98f22556b3d7_509x649.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For any figures that end up in reports or presentations, this becomes a real problem. An axis label that reads <code>perm</code> is not the same as one that reads <code>Permeability (mD)</code>. Similarly, a title that says <code>Formation scatter</code> tells the reader almost nothing.</p><p><em>I wrote about this recently in <a href="/__u/andymcdonaldgeo.substack.com/p/six-matplotlib-changes-that-improve">Six Matplotlib Changes That Improve Your Figures for Reporting and Publication</a></em></p><p>The fix is the same as for magic numbers: make the context explicit.</p><ul><li><p>Label the axes</p></li><li><p>Add a descriptive title</p></li><li><p>If the figure is making a specific point, say so either in a markdown cell above it or as an annotation directly on the chart</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!XjG2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38236c26-b2ed-47c7-b3d6-a157073e5def_547x581.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!XjG2!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38236c26-b2ed-47c7-b3d6-a157073e5def_547x581.png 424w, /__u/substackcdn.com/image/fetch/$s_!XjG2!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38236c26-b2ed-47c7-b3d6-a157073e5def_547x581.png 848w, /__u/substackcdn.com/image/fetch/$s_!XjG2!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38236c26-b2ed-47c7-b3d6-a157073e5def_547x581.png 1272w, /__u/substackcdn.com/image/fetch/$s_!XjG2!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38236c26-b2ed-47c7-b3d6-a157073e5def_547x581.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!XjG2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38236c26-b2ed-47c7-b3d6-a157073e5def_547x581.png" width="547" height="581" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/38236c26-b2ed-47c7-b3d6-a157073e5def_547x581.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:581,&quot;width&quot;:547,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!XjG2!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38236c26-b2ed-47c7-b3d6-a157073e5def_547x581.png 424w, /__u/substackcdn.com/image/fetch/$s_!XjG2!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38236c26-b2ed-47c7-b3d6-a157073e5def_547x581.png 848w, /__u/substackcdn.com/image/fetch/$s_!XjG2!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38236c26-b2ed-47c7-b3d6-a157073e5def_547x581.png 1272w, /__u/substackcdn.com/image/fetch/$s_!XjG2!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38236c26-b2ed-47c7-b3d6-a157073e5def_547x581.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!m6hB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0665323c-c5dc-4770-a89c-ec3a91f5357f_461x646.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!m6hB!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0665323c-c5dc-4770-a89c-ec3a91f5357f_461x646.png 424w, /__u/substackcdn.com/image/fetch/$s_!m6hB!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0665323c-c5dc-4770-a89c-ec3a91f5357f_461x646.png 848w, /__u/substackcdn.com/image/fetch/$s_!m6hB!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0665323c-c5dc-4770-a89c-ec3a91f5357f_461x646.png 1272w, /__u/substackcdn.com/image/fetch/$s_!m6hB!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0665323c-c5dc-4770-a89c-ec3a91f5357f_461x646.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!m6hB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0665323c-c5dc-4770-a89c-ec3a91f5357f_461x646.png" width="461" height="646" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0665323c-c5dc-4770-a89c-ec3a91f5357f_461x646.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:646,&quot;width&quot;:461,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!m6hB!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0665323c-c5dc-4770-a89c-ec3a91f5357f_461x646.png 424w, /__u/substackcdn.com/image/fetch/$s_!m6hB!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0665323c-c5dc-4770-a89c-ec3a91f5357f_461x646.png 848w, /__u/substackcdn.com/image/fetch/$s_!m6hB!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0665323c-c5dc-4770-a89c-ec3a91f5357f_461x646.png 1272w, /__u/substackcdn.com/image/fetch/$s_!m6hB!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0665323c-c5dc-4770-a89c-ec3a91f5357f_461x646.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A notebook full of readable code, but unlabelled plots is still half finished. The outputs are part of the document too and often times that is what you come back to the notebook for. For example, you may want to grab the figure for your technical paper or for passing onto a client.</p><h2><strong>A notebook is a document too</strong></h2><p>There&#8217;s a useful distinction between a notebook you&#8217;re using to think with and one you&#8217;re handing to someone else. The exploratory version: where you were trying things, backing up, dead-ending, isn&#8217;t the same thing as what a colleague needs to understand or verify your work.</p><p>The gap between the two is smaller than it looks. The things that make a notebook readable (a few markdown observations, named constants, cells grouped by purpose, labelled figures) can add up to less than an hour for a notebook that took days to build. It&#8217;s not glamorous work, but it&#8217;s the difference between something you&#8217;re comfortable handing over and something you need to walk someone through in person.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/p/why-your-jupyter-notebooks-are-harder?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/p/why-your-jupyter-notebooks-are-harder?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share</span></a></p><p><em>Making individual notebooks readable is one half of the problem. The other half is what happens when you have dozens of them across different projects and you need to find something from six months ago. Over on Substack I&#8217;ve written about a lightweight system for naming, organising, and indexing notebooks so older work stays usable without turning maintenance into a side project.</em></p><p><em>Check out ot below</em></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;d9a19af5-9a9f-471b-b065-0f948164e276&quot;,&quot;caption&quot;:&quot;In the previous article, I looked at the project structure around a notebook: a consistent folder layout, a central config.py for paths, reusable code in src/, and a Cookiecutter template to generate the whole setup quickly.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;How to Organise Jupyter Notebooks So Older Work Stays Usable&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:86433898,&quot;name&quot;:&quot;Andy McDonald&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d142b41e-4d2f-4110-9b12-9deca3dca52c_800x800.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-03-21T11:37:18.099Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!BfDG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a41641-999f-40a8-bb2d-383735d35d9f_630x345.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://andymcdonaldgeo.substack.com/p/how-to-organise-jupyter-notebooks&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:191665389,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1902449,&quot;publication_name&quot;:&quot;Subsurface Syntax&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!JLsK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa8f57252-4306-43f0-97f3-c979dce93b0b_790x790.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p></p>]]></content:encoded></item><item><title><![CDATA[Six Matplotlib Changes That Improve Your Figures for Reporting and Publication]]></title><description><![CDATA[Small adjustments. Large visual difference.]]></description><link>https://andymcdonaldgeo.substack.com/p/six-matplotlib-changes-that-improve</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/six-matplotlib-changes-that-improve</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Sat, 16 May 2026 15:14:10 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_V_J!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6469f2d-38be-4038-b477-82238fda85ca_630x408.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!_V_J!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6469f2d-38be-4038-b477-82238fda85ca_630x408.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!_V_J!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6469f2d-38be-4038-b477-82238fda85ca_630x408.png 424w, /__u/substackcdn.com/image/fetch/$s_!_V_J!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6469f2d-38be-4038-b477-82238fda85ca_630x408.png 848w, /__u/substackcdn.com/image/fetch/$s_!_V_J!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6469f2d-38be-4038-b477-82238fda85ca_630x408.png 1272w, /__u/substackcdn.com/image/fetch/$s_!_V_J!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6469f2d-38be-4038-b477-82238fda85ca_630x408.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!_V_J!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6469f2d-38be-4038-b477-82238fda85ca_630x408.png" width="630" height="408" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a6469f2d-38be-4038-b477-82238fda85ca_630x408.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:408,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!_V_J!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6469f2d-38be-4038-b477-82238fda85ca_630x408.png 424w, /__u/substackcdn.com/image/fetch/$s_!_V_J!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6469f2d-38be-4038-b477-82238fda85ca_630x408.png 848w, /__u/substackcdn.com/image/fetch/$s_!_V_J!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6469f2d-38be-4038-b477-82238fda85ca_630x408.png 1272w, /__u/substackcdn.com/image/fetch/$s_!_V_J!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6469f2d-38be-4038-b477-82238fda85ca_630x408.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>It&#8217;s no secret that matplotlib&#8217;s defaults are functional, but they don&#8217;t always do the data justice! I have talked about this multiple times on here and over on Substack.</p><p>You&#8217;ll get a chart with the right data, the right axes, and roughly the right shape. But functional and clear aren&#8217;t the same thing, and the distance between them is mostly a handful of lines of code and personal choices.</p><p>This article covers six of them. They are not clever hacks, instead they are small adjustments that can become automatic once you&#8217;ve made them a few times.</p><p>The example I am using here is synthetic porosity-permeability scatter for four lithology types: Sand A, Sand B, Shaly Sand, and Tight Zone. Whilst this article is geoscience focused, the same changes can be applied to any chart to take them from matplotlib basic to something that you can easily put in a report.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/subscribe"><span>Subscribe now</span></a></p><h2><strong>Setting Up the Basic Matplotlib Plot</strong></h2><p>The starting point is a plain scatter with no styling decisions at all.</p><pre><code>fig, ax = plt.subplots()

for formation, group in df.groupby(&#8221;Formation&#8221;):
    ax.scatter(group[&#8221;Porosity&#8221;], group[&#8221;Permeability&#8221;], label=formation)
ax.set_xlabel(&#8221;Porosity (v/v)&#8221;)
ax.set_ylabel(&#8221;Permeability (mD)&#8221;)
ax.set_title(&#8221;Porosity vs Permeability by Formation&#8221;)
ax.legend()</code></pre><p>When we run the above, we get back the following, yet familiar matplotlib chart:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!eM96!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084d52ec-0832-4012-afd7-18945f015898_630x445.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!eM96!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084d52ec-0832-4012-afd7-18945f015898_630x445.png 424w, /__u/substackcdn.com/image/fetch/$s_!eM96!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084d52ec-0832-4012-afd7-18945f015898_630x445.png 848w, /__u/substackcdn.com/image/fetch/$s_!eM96!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084d52ec-0832-4012-afd7-18945f015898_630x445.png 1272w, /__u/substackcdn.com/image/fetch/$s_!eM96!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084d52ec-0832-4012-afd7-18945f015898_630x445.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!eM96!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084d52ec-0832-4012-afd7-18945f015898_630x445.png" width="630" height="445" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/084d52ec-0832-4012-afd7-18945f015898_630x445.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:445,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!eM96!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084d52ec-0832-4012-afd7-18945f015898_630x445.png 424w, /__u/substackcdn.com/image/fetch/$s_!eM96!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084d52ec-0832-4012-afd7-18945f015898_630x445.png 848w, /__u/substackcdn.com/image/fetch/$s_!eM96!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084d52ec-0832-4012-afd7-18945f015898_630x445.png 1272w, /__u/substackcdn.com/image/fetch/$s_!eM96!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F084d52ec-0832-4012-afd7-18945f015898_630x445.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The raw matplotlib default &#8212; a working chart, but noisy and harder to read than it needs to b</p><p>It works. The data is there. But it&#8217;s also cluttered, visually heavy, and hard to focus on. The full box draws the eye away from the data. The colours fight each other. The axes don&#8217;t reflect the structure of the data. Five changes fix most of this.</p><h2><strong>1. Remove the box</strong></h2><p>The default figure comes with a full box. Four spines, equal weight, none of it called for by the data. Often, these extra elements are referred to as &#8220;Chart Junk&#8221;.</p><p>Removing the top and right spines immediately reduces the visual noise. Softening the remaining two drops their weight without losing the reference they give the reader.</p><pre><code>ax.spines[&#8221;top&#8221;].set_visible(False)
ax.spines[&#8221;right&#8221;].set_visible(False)
ax.spines[&#8221;left&#8221;].set_color(&#8221;#aaaaaa&#8221;)
ax.spines[&#8221;bottom&#8221;].set_color(&#8221;#aaaaaa&#8221;)</code></pre><p>Depending on the chart and what you are trying to get across to your readers, it can sometimes be beneficial to add gridlines, however, adding them should not detract the eye from the data.</p><p>In this data example, we are going to be dealing with a logarithmic Y-axis later on, so it will be helpful to have that horizontal reference line. I often find that the horizontal grid lines lets the eyes read permeability values without having to mentally extend a line across the full chart. I also feel that the the vertical grid mostly adds noise.</p><p>We can turn the grid on for the Y-axis as follows.</p><pre><code>ax.yaxis.grid(True, color=&#8221;#ebebeb&#8221;, linewidth=0.8, zorder=0)
ax.xaxis.grid(False)
ax.set_axisbelow(True)</code></pre><p><code>set_axisbelow(True)</code> keeps the grid behind the data points. This may sound obvious, but by default matplotlib will draw the axis on top of the data, which can look messy.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!keD_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c29416a-c00f-4816-916a-2ed4b67da385_630x231.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!keD_!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c29416a-c00f-4816-916a-2ed4b67da385_630x231.png 424w, /__u/substackcdn.com/image/fetch/$s_!keD_!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c29416a-c00f-4816-916a-2ed4b67da385_630x231.png 848w, /__u/substackcdn.com/image/fetch/$s_!keD_!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c29416a-c00f-4816-916a-2ed4b67da385_630x231.png 1272w, /__u/substackcdn.com/image/fetch/$s_!keD_!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c29416a-c00f-4816-916a-2ed4b67da385_630x231.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!keD_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c29416a-c00f-4816-916a-2ed4b67da385_630x231.png" width="630" height="231" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c29416a-c00f-4816-916a-2ed4b67da385_630x231.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:231,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!keD_!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c29416a-c00f-4816-916a-2ed4b67da385_630x231.png 424w, /__u/substackcdn.com/image/fetch/$s_!keD_!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c29416a-c00f-4816-916a-2ed4b67da385_630x231.png 848w, /__u/substackcdn.com/image/fetch/$s_!keD_!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c29416a-c00f-4816-916a-2ed4b67da385_630x231.png 1272w, /__u/substackcdn.com/image/fetch/$s_!keD_!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c29416a-c00f-4816-916a-2ed4b67da385_630x231.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Before and after removing the box and subduing the grid.</p><p>After applying the change, the difference is immediate and the data is easier to look at and interpret.</p><h2><strong>2. Use an intentional colour palette</strong></h2><p>Matplotlib&#8217;s default colour cycle isn&#8217;t bad. It wasn&#8217;t designed for any particular data structure though, so it assigns similar visual weight to every group regardless of what you&#8217;re trying to say.</p><p>Picking a palette deliberately forces a useful question: what do these groups mean relative to each other, and does the colour scheme reflect that?</p><p><em>Okabe-Ito is one solid starting point, but the right palette for your project depends on the data type, the audience, and how the chart will be reproduced. I&#8217;ve covered four tools on Substack that make that selection process significantly easier. Link at the end.</em></p><p>In this example, we have four different lithologies which have a rough quality order. Sand A and Sand B are the better reservoir rock. Shaly Sand and Tight Zone sit towards the the lower end.</p><p>A palette that groups the two sandy formations together and distinguishes the lower-quality ones makes that structure readable without any additional explanation.</p><p>The Okabe-Ito palette is a good starting point. It&#8217;s colour-blind safe and holds up across screens and print conditions. However, this is not the only palette available, and i would suggest that you explore other options to suit your needs.</p><pre><code>PALETTE = {
    &#8220;Sand A&#8221;:     &#8220;#56B4E9&#8221;,   # sky blue
    &#8220;Sand B&#8221;:     &#8220;#CC79A7&#8221;,   # reddish purple
    &#8220;Shaly Sand&#8221;: &#8220;#E69F00&#8221;,   # golden amber
    &#8220;Tight Zone&#8221;: &#8220;#D55E00&#8221;,   # vermillion
}</code></pre><p>Two blues for the sands, amber and vermillion for the lower-quality formations. This provides a distinct change without being garish, and it can also avoid the red-green pairing that disappears for about 8% of readers.</p><pre><code>for formation, group in df.groupby(&#8221;Formation&#8221;):
    ax.scatter(
        group[&#8221;Porosity&#8221;], group[&#8221;Permeability&#8221;],
        label=formation,
        color=PALETTE[formation],
        s=45, alpha=0.80, edgecolors=&#8221;none&#8221;
    )</code></pre><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!h3AV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06d34be-0b87-419a-ab62-d46c6d525760_630x231.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!h3AV!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06d34be-0b87-419a-ab62-d46c6d525760_630x231.png 424w, /__u/substackcdn.com/image/fetch/$s_!h3AV!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06d34be-0b87-419a-ab62-d46c6d525760_630x231.png 848w, /__u/substackcdn.com/image/fetch/$s_!h3AV!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06d34be-0b87-419a-ab62-d46c6d525760_630x231.png 1272w, /__u/substackcdn.com/image/fetch/$s_!h3AV!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06d34be-0b87-419a-ab62-d46c6d525760_630x231.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!h3AV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06d34be-0b87-419a-ab62-d46c6d525760_630x231.png" width="630" height="231" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f06d34be-0b87-419a-ab62-d46c6d525760_630x231.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:231,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!h3AV!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06d34be-0b87-419a-ab62-d46c6d525760_630x231.png 424w, /__u/substackcdn.com/image/fetch/$s_!h3AV!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06d34be-0b87-419a-ab62-d46c6d525760_630x231.png 848w, /__u/substackcdn.com/image/fetch/$s_!h3AV!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06d34be-0b87-419a-ab62-d46c6d525760_630x231.png 1272w, /__u/substackcdn.com/image/fetch/$s_!h3AV!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff06d34be-0b87-419a-ab62-d46c6d525760_630x231.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Default colour cycle versus the Okabe-Ito palette</p><p>Colour is one of the fastest channels the eye processes. A palette that does nothing to support the data structure is a missed opportunity.</p><p>This is not to say the above palette is the one everyone should use, but being aware of colour blindness is something that is key to creating accessible data visualisations.</p><h2><strong>3. Fix the axes</strong></h2><p>Permeability spans orders of magnitude in most subsurface datasets. The difference between a tight formation and a productive sand isn&#8217;t a linear step, and plotting it that way compresses the variation that actually matters into a small portion of the chart.</p><p>A log-scale y-axis is often more appropriate. For this kind of data, it&#8217;s usually much clearer, and can be done with a one liner:</p><pre><code>ax.set_yscale(&#8221;log&#8221;)</code></pre><p>Once the scale is right, the tick labels need attention. Matplotlib&#8217;s default log formatter tends to produce scientific notation or inconsistent formats depending on the range. For permeability in the 1 to a few thousand millidarcy range, plain whole numbers are easier to read.</p><pre><code>ax.yaxis.set_major_locator(ticker.LogLocator(base=10, numticks=10))
ax.yaxis.set_major_formatter(ticker.FuncFormatter(lambda x, _: f&#8221;{int(x):,}&#8221;))</code></pre><p>The y-axis limits are worth setting explicitly too. Matplotlib&#8217;s automatic limits leave outermost data points at the very edge of the plot area. Extending to the next power of 10 above the data maximum gives the chart some room without arbitrary padding.</p><pre><code>import math

perm_max = df[&#8221;Permeability&#8221;].max()
top = 10 ** math.ceil(math.log10(perm_max))
ax.set_ylim(bottom=0.5, top=top)</code></pre><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!4-nn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1453b179-6c25-4671-b2ba-b4d5eb84308b_630x231.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!4-nn!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1453b179-6c25-4671-b2ba-b4d5eb84308b_630x231.png 424w, /__u/substackcdn.com/image/fetch/$s_!4-nn!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1453b179-6c25-4671-b2ba-b4d5eb84308b_630x231.png 848w, /__u/substackcdn.com/image/fetch/$s_!4-nn!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1453b179-6c25-4671-b2ba-b4d5eb84308b_630x231.png 1272w, /__u/substackcdn.com/image/fetch/$s_!4-nn!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1453b179-6c25-4671-b2ba-b4d5eb84308b_630x231.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!4-nn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1453b179-6c25-4671-b2ba-b4d5eb84308b_630x231.png" width="630" height="231" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1453b179-6c25-4671-b2ba-b4d5eb84308b_630x231.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:231,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!4-nn!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1453b179-6c25-4671-b2ba-b4d5eb84308b_630x231.png 424w, /__u/substackcdn.com/image/fetch/$s_!4-nn!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1453b179-6c25-4671-b2ba-b4d5eb84308b_630x231.png 848w, /__u/substackcdn.com/image/fetch/$s_!4-nn!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1453b179-6c25-4671-b2ba-b4d5eb84308b_630x231.png 1272w, /__u/substackcdn.com/image/fetch/$s_!4-nn!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1453b179-6c25-4671-b2ba-b4d5eb84308b_630x231.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Linear scale with default limits versus log scale with whole-number ticks and extended y-axis</p><p>This one tends to produce the biggest visible shift. On the linear scale, Sand A&#8217;s higher-permeability tail gets stretched out and the lower formations get squashed together. On log scale, the spread within each group is actually readable.</p><h2><strong>4. Add minor gridlines</strong></h2><p>Now, this may sound counter-intuitive to &#8220;removing the clutter&#8221; from a chart, However, when working with logarithmic scaled data I find that just having the major gridlines on the plot can cause issues for interpreting the values correctly.</p><p>Major gridlines on a log-scale axis sit at 1, 10, 100, 1,000. That&#8217;s useful, but it leaves a lot of unguided space in between. A value sitting between 10 and 100 could be anywhere from 11 to 99 without more reference. Minor gridlines fill those gaps at 2, 3, 4, 5, 6, 7, 8, and 9 within each decade.</p><p>The trick is keeping them light enough that they don&#8217;t compete with the data or the major gridlines. A dotted line in a near-white grey does the job.</p><pre><code>ax.yaxis.set_minor_locator(ticker.LogLocator(base=10, subs=np.arange(2, 10) * 0.1, numticks=100))
ax.yaxis.grid(True, which=&#8221;minor&#8221;, color=&#8221;#f0f0f0&#8221;, linewidth=0.5, linestyle=&#8221;:&#8221;)</code></pre><p><code>subs=np.arange(2, 10) * 0.1</code> places minor ticks at the nine intermediate positions within each decade. The dotted style and near-white colour keep them clearly subordinate to the solid major gridlines.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!-oh9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e5ab01-fe2d-4713-ba6e-88ff8adb25f3_630x231.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!-oh9!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e5ab01-fe2d-4713-ba6e-88ff8adb25f3_630x231.png 424w, /__u/substackcdn.com/image/fetch/$s_!-oh9!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e5ab01-fe2d-4713-ba6e-88ff8adb25f3_630x231.png 848w, /__u/substackcdn.com/image/fetch/$s_!-oh9!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e5ab01-fe2d-4713-ba6e-88ff8adb25f3_630x231.png 1272w, /__u/substackcdn.com/image/fetch/$s_!-oh9!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e5ab01-fe2d-4713-ba6e-88ff8adb25f3_630x231.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!-oh9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e5ab01-fe2d-4713-ba6e-88ff8adb25f3_630x231.png" width="630" height="231" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/14e5ab01-fe2d-4713-ba6e-88ff8adb25f3_630x231.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:231,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!-oh9!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e5ab01-fe2d-4713-ba6e-88ff8adb25f3_630x231.png 424w, /__u/substackcdn.com/image/fetch/$s_!-oh9!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e5ab01-fe2d-4713-ba6e-88ff8adb25f3_630x231.png 848w, /__u/substackcdn.com/image/fetch/$s_!-oh9!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e5ab01-fe2d-4713-ba6e-88ff8adb25f3_630x231.png 1272w, /__u/substackcdn.com/image/fetch/$s_!-oh9!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14e5ab01-fe2d-4713-ba6e-88ff8adb25f3_630x231.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Log-scale axis without and with minor gridlines. You may need to expand the image to see the changes.</p><p>The minor gridlines don&#8217;t change what the chart says, they help make it easier to read a value off the y-axis without guessing.</p><h2><strong>5. Annotate the insight</strong></h2><p>If there&#8217;s something specific the figure is supposed to communicate, it&#8217;s worth saying it. Readers can draw their own conclusions from an unlabelled chart, but not always the same one.</p><p>Annotations don&#8217;t need to be elaborate, but a short label and an arrow pointing at the relevant part of the data usually does it.</p><pre><code>ax.annotate(
    &#8220;Higher-quality reservoir\n(Sand A cluster)&#8221;,
    xy=(0.272, 18),
    xytext=(0.125, 150),
    fontsize=9.5,
    color=PALETTE[&#8221;Sand A&#8221;],
    arrowprops=dict(arrowstyle=&#8221;-&gt;&#8221;, color=PALETTE[&#8221;Sand A&#8221;], lw=1.3),
    va=&#8221;center&#8221;,
)</code></pre><p>Matching the annotation colour to the relevant group ties the label to the data visually, without needing a separate callout or legend entry.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!cc4P!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!cc4P!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png 424w, /__u/substackcdn.com/image/fetch/$s_!cc4P!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png 848w, /__u/substackcdn.com/image/fetch/$s_!cc4P!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png 1272w, /__u/substackcdn.com/image/fetch/$s_!cc4P!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!cc4P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png" width="630" height="436" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:436,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!cc4P!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png 424w, /__u/substackcdn.com/image/fetch/$s_!cc4P!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png 848w, /__u/substackcdn.com/image/fetch/$s_!cc4P!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png 1272w, /__u/substackcdn.com/image/fetch/$s_!cc4P!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>It&#8217;s easy to skip this one. The chart looks finished before the annotation goes in. But there&#8217;s a difference between a chart that shows data and one that says something about it, and this is usually where that gap gets closed.</p><h2><strong>6. Finish the figure before exporting</strong></h2><p>Two settings that are easy to overlook: layout and resolution.</p><p><code>constrained_layout=True</code> handles spacing between plot elements automatically. It&#8217;s more reliable than <code>tight_layout()</code> and avoids the common situation where an axis label gets clipped or a legend drifts over the plot area.</p><pre><code>fig, ax = plt.subplots(figsize=(8, 5.5), constrained_layout=True)</code></pre><p>Resolution matters for anything going outside a Jupyter notebook. 150 dpi is fine for quick review. For a Medium article, a report, or a slide, 300 dpi is the sensible default. The file size is larger, but the output holds up at any zoom.</p><pre><code>fig.savefig(&#8221;output_figure.png&#8221;, dpi=300, bbox_inches=&#8221;tight&#8221;)</code></pre><p>These aren&#8217;t visual changes in the way the earlier steps are. They&#8217;re finishing work. The kind that becomes obvious in hindsight when you export a figure and notice the labels are cut off or the text looks soft.</p><h2><strong>Putting it together</strong></h2><p>Here&#8217;s the full figure with all changes applied.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!cc4P!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!cc4P!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png 424w, /__u/substackcdn.com/image/fetch/$s_!cc4P!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png 848w, /__u/substackcdn.com/image/fetch/$s_!cc4P!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png 1272w, /__u/substackcdn.com/image/fetch/$s_!cc4P!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!cc4P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png" width="630" height="436" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:436,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!cc4P!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png 424w, /__u/substackcdn.com/image/fetch/$s_!cc4P!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png 848w, /__u/substackcdn.com/image/fetch/$s_!cc4P!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png 1272w, /__u/substackcdn.com/image/fetch/$s_!cc4P!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fce9d26dd-32d0-4d50-ad13-0c41ff796385_630x436.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The complete code is below, with a helper function to keep the styling consistent across figures.</p><pre><code>import math
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

# &#9472;&#9472; Synthetic dataset 
np.random.seed(42)
n = 80

formations_params = {
    &#8220;Sand A&#8221;:     {&#8221;phi_mean&#8221;: 0.26, &#8220;phi_std&#8221;: 0.025, &#8220;perm_mu&#8221;: 2.4, &#8220;perm_sigma&#8221;: 0.35},
    &#8220;Sand B&#8221;:     {&#8221;phi_mean&#8221;: 0.20, &#8220;phi_std&#8221;: 0.020, &#8220;perm_mu&#8221;: 1.8, &#8220;perm_sigma&#8221;: 0.30},
    &#8220;Shaly Sand&#8221;: {&#8221;phi_mean&#8221;: 0.18, &#8220;phi_std&#8221;: 0.020, &#8220;perm_mu&#8221;: 1.2, &#8220;perm_sigma&#8221;: 0.28},
    &#8220;Tight Zone&#8221;: {&#8221;phi_mean&#8221;: 0.10, &#8220;phi_std&#8221;: 0.015, &#8220;perm_mu&#8221;: 0.3, &#8220;perm_sigma&#8221;: 0.20},
}

frames = []
for name, p in formations_params.items():
    phi  = np.clip(np.random.normal(p[&#8221;phi_mean&#8221;], p[&#8221;phi_std&#8221;], n), 0.04, 0.35)
    perm = np.random.lognormal(p[&#8221;perm_mu&#8221;], p[&#8221;perm_sigma&#8221;], n)
    frames.append(pd.DataFrame({&#8221;Formation&#8221;: name, &#8220;Porosity&#8221;: phi, &#8220;Permeability&#8221;: perm}))
df = pd.concat(frames, ignore_index=True)

# &#9472;&#9472; Palette 
PALETTE = {
    &#8220;Sand A&#8221;:     &#8220;#56B4E9&#8221;,   # sky blue
    &#8220;Sand B&#8221;:     &#8220;#CC79A7&#8221;,   # reddish purple
    &#8220;Shaly Sand&#8221;: &#8220;#E69F00&#8221;,   # golden amber
    &#8220;Tight Zone&#8221;: &#8220;#D55E00&#8221;,   # vermillion
}

def apply_clean_style(ax, yscale=&#8221;linear&#8221;):
    ax.set_facecolor(&#8221;white&#8221;)
    ax.spines[&#8221;top&#8221;].set_visible(False)
    ax.spines[&#8221;right&#8221;].set_visible(False)
    ax.spines[&#8221;left&#8221;].set_color(&#8221;#aaaaaa&#8221;)
    ax.spines[&#8221;bottom&#8221;].set_color(&#8221;#aaaaaa&#8221;)
    ax.yaxis.grid(True, color=&#8221;#ebebeb&#8221;, linewidth=0.8, zorder=0)
    ax.xaxis.grid(False)
    ax.set_axisbelow(True)
    ax.tick_params(axis=&#8221;both&#8221;, colors=&#8221;#555555&#8221;, length=3, width=0.8)
    if yscale == &#8220;log&#8221;:
        ax.set_yscale(&#8221;log&#8221;)

fig, ax = plt.subplots(figsize=(8, 5.5), constrained_layout=True)
fig.patch.set_facecolor(&#8221;white&#8221;)

for formation, group in df.groupby(&#8221;Formation&#8221;):
    ax.scatter(
        group[&#8221;Porosity&#8221;], group[&#8221;Permeability&#8221;],
        label=formation,
        color=PALETTE[formation],
        s=50, alpha=0.82, edgecolors=&#8221;none&#8221;
    )

apply_clean_style(ax, yscale=&#8221;log&#8221;)

perm_max = df[&#8221;Permeability&#8221;].max()
top = 10 ** math.ceil(math.log10(perm_max))
ax.set_ylim(bottom=0.5, top=top)
ax.yaxis.set_major_locator(ticker.LogLocator(base=10, numticks=10))
ax.yaxis.set_major_formatter(ticker.FuncFormatter(lambda x, _: f&#8221;{int(x):,}&#8221;))

ax.yaxis.set_minor_locator(ticker.LogLocator(base=10, subs=np.arange(2, 10) * 0.1, numticks=100))
ax.yaxis.grid(True, which=&#8221;minor&#8221;, color=&#8221;#ebebeb&#8221;, linewidth=0.7, linestyle=&#8221;:&#8221;)

ax.set_xlabel(&#8221;Porosity (v/v)&#8221;, labelpad=10)
ax.set_ylabel(&#8221;Permeability (mD)&#8221;, labelpad=10)
ax.set_title(&#8221;Porosity vs Permeability by Formation&#8221;, pad=14)
ax.set_xlim(0.03, 0.35)

ax.annotate(
    &#8220;Reservoir quality trend&#8221;,
    xy=(0.265, 50),
    xytext=(0.068, 10),
    fontsize=9.5,
    color=&#8221;#555555&#8221;,
    rotation=0,
    arrowprops=dict(arrowstyle=&#8221;-&gt;&#8221;, color=&#8221;#555555&#8221;, lw=1.3),
    va=&#8221;center&#8221;,
    ha=&#8221;left&#8221;,
)

ax.legend(title=&#8221;Formation&#8221;, title_fontsize=9, fontsize=9,
          frameon=True, framealpha=0.95, edgecolor=&#8221;#dddddd&#8221;,
          loc=&#8221;lower right&#8221;)

fig.savefig(&#8221;porosity_permeability.png&#8221;, dpi=300, bbox_inches=&#8221;tight&#8221;)</code></pre><p>None of these changes are complicated. Each one takes a few lines. The combined effect is a figure that&#8217;s easier to read, more honest about the data structure, and clearly finished rather than produced by default.</p><p>The defaults exist for good reasons. They get you to a working chart quickly without any configuration. But &#8220;working&#8221; is a low bar, and clearing it is not the same as making something that communicates well.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share Subsurface Syntax&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share Subsurface Syntax</span></a></p><div><hr></div><p><em>Choosing Okabe-Ito for this example wasn&#8217;t arbitrary. It came down to accessibility, contrast, and whether the colours would hold up across screens and print. For your own projects, those same questions apply, and the answer won&#8217;t always be the same palette. Check out the following article to find four tools that help when selecting a colour palette.</em></p><p><em><a href="/__u/andymcdonaldgeo.substack.com/p/4-essential-tools-to-help-you-select-a-colour-palette-for-your-data-visualisation-ddb2ec92a08">&#8594; 4 Essential Tools to Help You Select a Colour Palette for Your Data Visualisation</a></em></p>]]></content:encoded></item><item><title><![CDATA[AI Verification Depends on the Stakes]]></title><description><![CDATA[I use AI tools every day.]]></description><link>https://andymcdonaldgeo.substack.com/p/you-dont-need-to-verify-everything</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/you-dont-need-to-verify-everything</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Sun, 10 May 2026 10:18:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Eee3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a763b92-dfb3-4e6a-8cd0-0e58d74051ce_1442x810.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Eee3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a763b92-dfb3-4e6a-8cd0-0e58d74051ce_1442x810.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Eee3!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a763b92-dfb3-4e6a-8cd0-0e58d74051ce_1442x810.png 424w, /__u/substackcdn.com/image/fetch/$s_!Eee3!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a763b92-dfb3-4e6a-8cd0-0e58d74051ce_1442x810.png 848w, /__u/substackcdn.com/image/fetch/$s_!Eee3!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a763b92-dfb3-4e6a-8cd0-0e58d74051ce_1442x810.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Eee3!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a763b92-dfb3-4e6a-8cd0-0e58d74051ce_1442x810.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Eee3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a763b92-dfb3-4e6a-8cd0-0e58d74051ce_1442x810.png" width="1442" height="810" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3a763b92-dfb3-4e6a-8cd0-0e58d74051ce_1442x810.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:810,&quot;width&quot;:1442,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1325625,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://andymcdonaldgeo.substack.com/i/196340584?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a763b92-dfb3-4e6a-8cd0-0e58d74051ce_1442x810.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!Eee3!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a763b92-dfb3-4e6a-8cd0-0e58d74051ce_1442x810.png 424w, /__u/substackcdn.com/image/fetch/$s_!Eee3!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a763b92-dfb3-4e6a-8cd0-0e58d74051ce_1442x810.png 848w, /__u/substackcdn.com/image/fetch/$s_!Eee3!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a763b92-dfb3-4e6a-8cd0-0e58d74051ce_1442x810.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Eee3!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a763b92-dfb3-4e6a-8cd0-0e58d74051ce_1442x810.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I use AI tools every day. There is not a day that goes by where I&#8217;m not brainstorming ideas with ChatGPT or building prototypes with Claude Code. </p><p>If you have read my previous articles on the <a href="/__u/andymcdonaldgeo.substack.com/p/the-jagged-frontier-why-ai-amazes">Jagged Frontier</a> and <a href="/__u/andymcdonaldgeo.substack.com/p/ai-that-suggests-vs-ai-that-acts">automation complacency</a>, then you will know that I have argued that if you can&#8217;t check the output then you are exposed.</p><p>So here&#8217;s the part that sounds like a contradiction.</p><p>When I&#8217;m building a lithology model that is going to be fed into a larger geomodel, I check every prediction against the geology, the cuttings, the wireline log character, and what I know about the basin. When I&#8217;m building a personal task manager for myself on a Saturday afternoon, I barely check anything. I accept what works, fix what breaks, and keep moving.</p><p>That probably sounds hypocritical, and I&#8217;ll explain why it isn&#8217;t. The short version: verification isn&#8217;t a fixed standard you either meet or fail. It&#8217;s a sliding scale, and where you sit on it should depend on what&#8217;s at stake and who&#8217;s downstream of your output.</p><p>Most of the conversation around AI verification misses this. It treats verification as binary. Either you check everything or you&#8217;re being reckless. In practice, that framing breaks down the moment you actually use these tools across different kinds of work.</p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><h2>Context changes everything</h2><p>I have built several personal tools and prototypes with AI, including a time tracker and a personal task manager. Some of these were built the same way: fast iterations, accepting what works and fixing what breaks with minimal pausing to verify the code.</p><p>And that&#8217;s fine. Because the only person who relies on them is me.</p><p>If something obvious goes wrong, I usually notice. If something subtle slips through, the consequence is still limited because I&#8217;m the only person using it.</p><p>I then fix it, and the cost of failure is a minor inconvenience. There&#8217;s no impact to anyone else, and there are no commercial decisions hanging on the output or anyone relying on the output from the results from the app.</p><p>Now compare that to client work. When I&#8217;m building a machine learning model to predict lithology, estimate permeability, or identify facies, I verify everything. Model output gets checked against the geology, the cuttings descriptions, the wireline log character, and what I know about the basin and depositional environment. </p><p>Same person, same tools, completely different approach.</p><p>What sits between those two extremes is a spectrum, and where you sit on it should depend on the stakes.</p><p>If it&#8217;s a personal prototype or you&#8217;re trying to take a concept from idea to something visual, the right question is &#8220;does it work for me?&#8221; or &#8220;does this show the idea clearly?&#8221; Often that is good enough, and you can later expand it if it is needed for others or start again with more detailed guardrails in place.</p><p>If you&#8217;re building an internal team tool, others are now using it. It probably needs some resilience built in, and users can flag issues. The level of verification then scales with what the tool is going to be used for and what the impacts are if it gets something wrong.</p><p>If you&#8217;re building a client-facing deliverable, a facies interpretation, a custom coded app, anything that goes outside your own walls, then full verification is necessary. Edge cases need consideration. Output should be checked against independent data. Someone is going to make a decision based on what you produce, and that decision deserves more than &#8220;the code ran.&#8221;</p><p>Understanding what the stakes are, and noticing when they change, is the part that matters. The tools feel the same regardless of which mode you&#8217;re in. The stakes don&#8217;t.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!SXkk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48043e3f-5a85-46ee-946d-1633b1eb9fa2_1471x839.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!SXkk!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48043e3f-5a85-46ee-946d-1633b1eb9fa2_1471x839.png 424w, /__u/substackcdn.com/image/fetch/$s_!SXkk!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48043e3f-5a85-46ee-946d-1633b1eb9fa2_1471x839.png 848w, /__u/substackcdn.com/image/fetch/$s_!SXkk!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48043e3f-5a85-46ee-946d-1633b1eb9fa2_1471x839.png 1272w, /__u/substackcdn.com/image/fetch/$s_!SXkk!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48043e3f-5a85-46ee-946d-1633b1eb9fa2_1471x839.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!SXkk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48043e3f-5a85-46ee-946d-1633b1eb9fa2_1471x839.png" width="1456" height="830" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/48043e3f-5a85-46ee-946d-1633b1eb9fa2_1471x839.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:830,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1550543,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://andymcdonaldgeo.substack.com/i/196340584?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48043e3f-5a85-46ee-946d-1633b1eb9fa2_1471x839.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!SXkk!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48043e3f-5a85-46ee-946d-1633b1eb9fa2_1471x839.png 424w, /__u/substackcdn.com/image/fetch/$s_!SXkk!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48043e3f-5a85-46ee-946d-1633b1eb9fa2_1471x839.png 848w, /__u/substackcdn.com/image/fetch/$s_!SXkk!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48043e3f-5a85-46ee-946d-1633b1eb9fa2_1471x839.png 1272w, /__u/substackcdn.com/image/fetch/$s_!SXkk!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48043e3f-5a85-46ee-946d-1633b1eb9fa2_1471x839.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Vibe coding has a boundary</h3><p>OpenAI co-founder Andrej Karpathy&#8217;s phrase &#8220;vibe coding&#8221; captures something real about how these tools feel in practice. You describe what you want, the model generates something close, you nudge it, test it, accept the next suggestion, and keep moving.</p><p>There is also a distinction worth noting here, as the term is used loosely. Vibe coding, in the strictest sense means accepting AI generated code without reading it. If you have reviewed and understood what the model produced, then that is something different and it starts to move into AI coding assistant territory.</p><p>For personal tools, that can be incredibly effective. You&#8217;re not trying to produce perfect software. You&#8217;re trying to turn an idea into something usable.</p><p>But, vibe coding has a boundary.</p><p>The moment the output affects someone else, informs a decision, or becomes part of a client-facing workflow, the mode has to change. You're no longer just exploring. You're engineering, making claims, creating something someone else may trust. That's where verification stops being optional polish and becomes part of the work.</p><h2>Where it gets dangerous</h2><p>Here&#8217;s the part that concerns me.</p><p>When you&#8217;re prototyping with Copilot or building something quick with an LLM, there&#8217;s a feedback loop. You accept a suggestion. It works. You accept another. That works too. Over time, your threshold for scrutiny starts to drop. Not because you&#8217;ve made a conscious decision, but because the pattern of &#8220;accept and move on&#8221; becomes habitual.</p><p>That&#8217;s manageable when you&#8217;re building something for yourself. It&#8217;s not manageable when you&#8217;re producing something that someone else will rely on.</p><p>The danger isn&#8217;t in the prototype phase. It&#8217;s in the transition. The moment something moves from &#8220;quick tool I built for myself&#8221; to &#8220;a thing that informs a decision.&#8221; If your verification habits don&#8217;t shift with that transition, you&#8217;re carrying prototype-level scrutiny into production-level stakes.</p><p>And this isn&#8217;t a problem that only affects people new to a domain. It affects me too.</p><p>When I&#8217;m working in petrophysics, I have decades of knowledge and experience to fall back on. I can look at a predicted facies log and feel when something is off, even before I&#8217;ve checked it formally. But if I were to step outside the areas where I am comfortable, say into another domain or using AI to write code in a framework I haven&#8217;t used before then I&#8217;m in exactly the same position as anyone else. </p><p>The results may look clean and professional, meaning that no alarm bells are raised. This is because I don&#8217;t have an independent basis to easily notice when something is wrong.</p><p>That&#8217;s the real shape of the problem. It&#8217;s not &#8220;experienced people are safe and inexperienced people aren&#8217;t.&#8221; It&#8217;s that everyone has a set of domains where they can verify meaningfully, and a much larger set of domains where they can&#8217;t. The risk shows up wherever the gap between those two sets gets crossed without you noticing.</p><p>Generative AI tools can give a confidently wrong answer, and whether that goes unnoticed depends entirely on whether the person reviewing it has independent ground to stand on for that specific task.</p><h2>What verification actually looks like</h2><p>When I verify AI output on a client project, I&#8217;m not just checking whether the code runs.</p><p>I&#8217;m asking: </p><ul><li><p>Does this match the geology?</p></li><li><p>Do these predicted facies make sense given what we know about the depositional environment? </p></li><li><p>Does the porosity estimate align with the core data? </p></li><li><p>Are the cuttings descriptions consistent with what the model is suggesting?</p></li><li><p>Does the code look correct and follow standard practice?</p></li></ul><p>That kind of verification requires:</p><ul><li><p>Domain knowledge</p></li><li><p>Independent information, i.e data sources the model hasn&#8217;t seen</p></li><li><p>Knowing what &#8220;wrong but plausible&#8221; looks like in your field</p></li></ul><p>This is the part that worries me about broader AI adoption. </p><p>Not the people who have decades of experience and can spot when an output is geologically nonsensical. But, the people who don&#8217;t have that background, running the same models, getting clean-looking results, and having no independent basis to question them.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/subscribe"><span>Subscribe now</span></a></p><h2>The honest version</h2><p>I think there&#8217;s a version of the AI verification conversation that&#8217;s more honest than what we usually hear.</p><p>It&#8217;s not: &#8220;always verify everything.&#8221;</p><p>It&#8217;s: &#8220;know what you&#8217;re verifying, why it matters, and whether you&#8217;re actually equipped to do it.&#8221;</p><p>I don&#8217;t verify my personal tools rigorously because the stakes don&#8217;t warrant it. I verify my client work carefully because the stakes do, and because I have the domain expertise to do it in a meaningful way.</p><p>The question everyone should be asking isn&#8217;t &#8220;are you using AI responsibly?&#8221; It&#8217;s:</p><p>Do you know which mode you&#8217;re in right now?</p><p>Have your verification habits kept pace with the stakes?</p><p>And if someone downstream is relying on your output, can you actually check it, or are you just assuming it&#8217;s fine because the model seemed confident?</p><h2>The jagged frontier is personal too</h2><p>I don&#8217;t believe <a href="/__u/andymcdonaldgeo.substack.com/p/the-jagged-frontier-why-ai-amazes">the jagged frontier</a> is just about what AI can and can&#8217;t do, I believe it should also be about what <em>you</em> can and can&#8217;t verify.</p><p>The boundary for that verification can shift depending on the task at hand, the stakes and your own knowledge and expertise.</p><p>The people who use AI well aren&#8217;t the ones who verify everything obsessively are the ones who skip verification entirely. They&#8217;re the ones who calibrate and match their level of scrutiny to the level of consequence.</p><p>That&#8217;s harder than it sounds. Because the tools can start to feel the same regardless of what you&#8217;re building. Copilot doesn&#8217;t know whether you&#8217;re prototyping a personal tool or shipping production code. The LLM doesn&#8217;t flag when the stakes have changed, that part is on you to realise and adapt accordingly.</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/p/you-dont-need-to-verify-everything?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading Subsurface Syntax! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/p/you-dont-need-to-verify-everything?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/p/you-dont-need-to-verify-everything?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share</span></a></p></div><p></p>]]></content:encoded></item><item><title><![CDATA[3 Jupyter Notebook Habits That Hold Up When Someone Else Opens Them]]></title><description><![CDATA[Small habits that make notebooks easier to rerun and share]]></description><link>https://andymcdonaldgeo.substack.com/p/3-jupyter-notebook-habits-that-hold</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/3-jupyter-notebook-habits-that-hold</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Tue, 07 Apr 2026 07:16:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!QYuY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e3c2b34-68ee-46b3-9897-eb2e4611ea37_630x404.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!QYuY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e3c2b34-68ee-46b3-9897-eb2e4611ea37_630x404.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!QYuY!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e3c2b34-68ee-46b3-9897-eb2e4611ea37_630x404.png 424w, /__u/substackcdn.com/image/fetch/$s_!QYuY!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e3c2b34-68ee-46b3-9897-eb2e4611ea37_630x404.png 848w, /__u/substackcdn.com/image/fetch/$s_!QYuY!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e3c2b34-68ee-46b3-9897-eb2e4611ea37_630x404.png 1272w, /__u/substackcdn.com/image/fetch/$s_!QYuY!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e3c2b34-68ee-46b3-9897-eb2e4611ea37_630x404.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!QYuY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e3c2b34-68ee-46b3-9897-eb2e4611ea37_630x404.png" width="630" height="404" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4e3c2b34-68ee-46b3-9897-eb2e4611ea37_630x404.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:404,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!QYuY!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e3c2b34-68ee-46b3-9897-eb2e4611ea37_630x404.png 424w, /__u/substackcdn.com/image/fetch/$s_!QYuY!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e3c2b34-68ee-46b3-9897-eb2e4611ea37_630x404.png 848w, /__u/substackcdn.com/image/fetch/$s_!QYuY!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e3c2b34-68ee-46b3-9897-eb2e4611ea37_630x404.png 1272w, /__u/substackcdn.com/image/fetch/$s_!QYuY!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e3c2b34-68ee-46b3-9897-eb2e4611ea37_630x404.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Jupyter notebooks are a great tool for exploration. They can help you move quickly, test ideas, tweak parameters, and follow a line of thought without too much friction.</p><p>That flexibility is part of what makes them so useful, but it&#8217;s also one of the reasons they are easy to break.</p><p>A notebook can look finished, run perfectly in your current session, and still fail the moment someone else opens it from a clean start. Sometimes that someone else is you, six months later, trying to work out what past-you actually did.</p><p>Here are three simple habits I have tried to stick to over the years. They won&#8217;t get in the way while you are working, and they can save a lot of frustration later.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/subscribe"><span>Subscribe now</span></a></p><h2><strong>1. Restart and Run All Before You Share</strong></h2><p>As you start working in a notebook the history of how the cells were run starts to build up.</p><p>These cells can end up getting run out of order, variables get redefined further down the page, and objects can stick around in memory. If you were to restart that notebook, you may find that it falls over and the results start to look different. This is a particular problem if someone is looking at your notebook for the first time.</p><p>To resolve this issue, and before you share a notebook, it is best to restart the kernel and run the whole thing from top to bottom.</p><p>If it breaks, that will tell you something useful. It usually means the notebook depends on hidden state that only existed in your session and not in the notebook itself.</p><p>If you run the notebook from a fresh start and it runs cleanly all the way to the end, then it is a good</p><h2><strong>2. Capture Your Environment at the Top</strong></h2><p>A notebook is often used to record an analysis, but it usually does a poor job of capturing the environment that made that analysis work. This is something I am often guilty of, but I have started to implement the following habit.</p><p>When you run your notebook it may run perfectly on your machine which is setup with one version of pandas or numpy, but when it runs on someone elses machine, it may behave differently. In others, there is a chance it will run just well enough to be misleading, especially if key functionality and behaviour in the libraries has changed.</p><p>The habit is to include a cell near the top of every notebook that captures the key environmental information: package versions, file paths, and any parameters that control the analysis. This doesn&#8217;t need to be sophisticated, and in fact sometimes keeping it simple is often to your advantage.</p><pre><code>import sys
import pandas as pd
import numpy as np
import lasio

# Print environment info
print(f&#8221;Python:  {sys.version}&#8221;)
print(f&#8221;pandas:  {pd.__version__}&#8221;)
print(f&#8221;numpy:   {np.__version__}&#8221;)
print(f&#8221;lasio:   {lasio.__version__}&#8221;)

# Key parameters, change these here, not buried in the notebook
WELL_NAME = &#8220;15/9-F-11A&#8221;
FORMATION_TOP = 2100.0   # mMD
FORMATION_BASE = 2350.0  # mMD
GR_CUTOFF = 75.0         # API units</code></pre><p>The parameters block is often the most useful part. It gives you a single place to see what the notebook assumes, and it makes rerunning the analysis with different inputs much easier.</p><p>It doesn&#8217;t need to be elaborate. The point isn&#8217;t to build a full configuration system inside the notebook, but rather to avoid the user making assumptions.</p><p>One small caveat though, is this only captures the environment at the moment the cell is run. If you reopen the notebook later and run it again after upgrading packages, the printed versions will update as well. That makes this useful for visibility, but not as a historical record.</p><p>If you need a more fixed record of the environment used at the time of the analysis, it is better to also save that information outside the notebook, for example in a <code>requirements.txt</code>, an environment file, or project documentation.</p><h2><strong>3. Treat Raw Data as Read-Only</strong></h2><p>This is the habit that can save the most trouble later.</p><p>It&#8217;s very easy in a notebook to load a dataset, start cleaning or filtering it, and then keep modifying that same object across multiple cells. After a while, it becomes hard to tell what has changed, what the original looked like, or whether a step has already been applied once before.</p><p>That becomes a problem the moment something goes wrong. Maybe a filter was too aggressive. Maybe a unit conversion was applied when it should not have been. Maybe a column was overwritten halfway through the notebook and nobody noticed. Once the raw data has been mixed up with the working copy, getting back to a clean starting point usually means starting again.</p><p>I find this especially painful when preparing data for machine learning modelling, where multiple transforms can build up over time and it becomes much harder to see exactly what has been done to the dataset.</p><p>A much safer habit is to treat raw data as read-only. Load it once, leave it alone, and do your work on a copy. Then save outputs to clearly named files in a separate location.</p><pre><code>import pandas as pd
from pathlib import Path

# Paths -- keep these at the top
DATA_DIR = Path(&#8221;data/raw&#8221;)
OUTPUT_DIR = Path(&#8221;data/processed&#8221;)
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

# Load raw data, do this once, never modify raw_df
raw_df = pd.read_csv(DATA_DIR / &#8220;well_logs_15_9_F11A.csv&#8221;)
print(f&#8221;Loaded {len(raw_df)} rows from raw data&#8221;)
print(raw_df.dtypes)

# Work on a copy
df = raw_df.copy()

# ... your processing steps here ...
# Save outputs with descriptive names
output_path = OUTPUT_DIR / &#8220;well_logs_15_9_F11A_processed.csv&#8221;
df.to_csv(output_path, index=False)
print(f&#8221;Saved processed data to {output_path}&#8221;)</code></pre><p>Using <code>Path</code> objects also makes the notebook easier to move between directories or machines. Hard-coded absolute paths buried halfway through a notebook are one of the easiest ways to make it fragile.</p><p>This habit becomes even more useful when it is paired with a clear folder structure such as <code>data/raw</code>, <code>data/processed</code>, and <code>outputs/figures</code>. That way the flow of data is easy to follow, and someone opening the project for the first time can work out what is going on without having to inspect every cell.</p><p>See the following for a full project structure that I have started to implement to keep things organised: <a href="/__u/andymcdonaldgeo.substack.com/p/the-project-structure-that-makes">The Project Structure That Makes Good Jupyter Notebook Habits Automatic</a></p><p>You also don&#8217;t need to force all of this into a single cell.</p><p>In practice, it is often clearer to split it into a few small setup cells, for example one for imports, one for paths and configuration, and one for loading the raw data.</p><h2><strong>The Common Thread</strong></h2><p>None of these habits require significant effort to implement, but the hard part is doing them consistently. Even I still forget from time to time!</p><p>Reproducibility is not really about being tidy for the sake of it. It is about being able to rely on your own analysis when it matters. That might be when a result gets questioned, when you need to update the work months later, or when someone else has to pick it up and make sense of it.</p><p>These three habits will not make a notebook perfect, but they will make it much easier to rerun, trust, and hand over.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!O-2R!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85838ae3-0304-4b4a-8f78-91f3bd49cd14_604x907.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!O-2R!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85838ae3-0304-4b4a-8f78-91f3bd49cd14_604x907.png 424w, /__u/substackcdn.com/image/fetch/$s_!O-2R!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85838ae3-0304-4b4a-8f78-91f3bd49cd14_604x907.png 848w, /__u/substackcdn.com/image/fetch/$s_!O-2R!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85838ae3-0304-4b4a-8f78-91f3bd49cd14_604x907.png 1272w, /__u/substackcdn.com/image/fetch/$s_!O-2R!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85838ae3-0304-4b4a-8f78-91f3bd49cd14_604x907.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!O-2R!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85838ae3-0304-4b4a-8f78-91f3bd49cd14_604x907.png" width="604" height="907" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/85838ae3-0304-4b4a-8f78-91f3bd49cd14_604x907.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:907,&quot;width&quot;:604,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!O-2R!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85838ae3-0304-4b4a-8f78-91f3bd49cd14_604x907.png 424w, /__u/substackcdn.com/image/fetch/$s_!O-2R!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85838ae3-0304-4b4a-8f78-91f3bd49cd14_604x907.png 848w, /__u/substackcdn.com/image/fetch/$s_!O-2R!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85838ae3-0304-4b4a-8f78-91f3bd49cd14_604x907.png 1272w, /__u/substackcdn.com/image/fetch/$s_!O-2R!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F85838ae3-0304-4b4a-8f78-91f3bd49cd14_604x907.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/p/3-jupyter-notebook-habits-that-hold?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading Subsurface Syntax! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/p/3-jupyter-notebook-habits-that-hold?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/p/3-jupyter-notebook-habits-that-hold?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share</span></a></p></div>]]></content:encoded></item><item><title><![CDATA[Boxplots Are Hiding Your Data. Here’s a Simple Fix]]></title><description><![CDATA[How overlaying raw data points reveals what your summary statistics quietly discard]]></description><link>https://andymcdonaldgeo.substack.com/p/boxplots-are-hiding-your-data-heres</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/boxplots-are-hiding-your-data-heres</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Sat, 28 Mar 2026 14:32:33 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!OcfO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bc713c5-e474-4033-9dd4-87c06e39532c_630x410.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!OcfO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bc713c5-e474-4033-9dd4-87c06e39532c_630x410.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!OcfO!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bc713c5-e474-4033-9dd4-87c06e39532c_630x410.png 424w, /__u/substackcdn.com/image/fetch/$s_!OcfO!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bc713c5-e474-4033-9dd4-87c06e39532c_630x410.png 848w, /__u/substackcdn.com/image/fetch/$s_!OcfO!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bc713c5-e474-4033-9dd4-87c06e39532c_630x410.png 1272w, /__u/substackcdn.com/image/fetch/$s_!OcfO!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bc713c5-e474-4033-9dd4-87c06e39532c_630x410.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!OcfO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bc713c5-e474-4033-9dd4-87c06e39532c_630x410.png" width="630" height="410" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0bc713c5-e474-4033-9dd4-87c06e39532c_630x410.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:410,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!OcfO!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bc713c5-e474-4033-9dd4-87c06e39532c_630x410.png 424w, /__u/substackcdn.com/image/fetch/$s_!OcfO!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bc713c5-e474-4033-9dd4-87c06e39532c_630x410.png 848w, /__u/substackcdn.com/image/fetch/$s_!OcfO!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bc713c5-e474-4033-9dd4-87c06e39532c_630x410.png 1272w, /__u/substackcdn.com/image/fetch/$s_!OcfO!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0bc713c5-e474-4033-9dd4-87c06e39532c_630x410.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><a href="https://seaborn.pydata.org/examples/horizontal_boxplot.html">Boxplots</a> are a great statistical tool for visualising data and are commonly used during the Exploratory Data Analysis (EDA) phase of data science projects. They provide us with a quick statistical summary of the data, help us understand how data is distributed and help identify anomalous data points (outliers).</p><p>They also make it easy to compare data distributions across groups, and they are familiar enough that most technical readers can scan them without too much effort.</p><p>However, one of their problems is that they can hide most of the data behind statistics.</p><p>A simple fix is to keep the boxplot and overlay the raw points. You still get the familiar summary, but you also get to see the observations that summary leaves behind.</p><p>This article walks through that combination in Python using seaborn and matplotlib to illustrate how we can see the individual data points that make up the statistical summary of a boxplot.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/subscribe"><span>Subscribe now</span></a></p><h2><strong>What are Boxplots?</strong></h2><p>A boxplot reduces a distribution to a handful of summary statistics. That is useful, but it also means the shape of the distribution, the clustering, the gaps, and the number of observations behind each group mostly disappear.</p><p>Boxplots are a standardised way to display the distribution of data based on five key numbers:</p><ul><li><p>&#8220;minimum&#8221;</p></li><li><p>1st Quartile (25th percentile)</p></li><li><p>median (2nd Quartile/ 50th Percentile)</p></li><li><p>3rd Quartile (75th percentile)</p></li><li><p>&#8220;maximum&#8221;</p></li></ul><p>The minimum and maximum values are defined as Q1&#8211;1.5 * IQR and Q3 + 1.5 * IQR respectively. Any points that fall outside of these limits are referred to as outliers.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!k6YX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b4c02f1-c0c2-4e7a-81a0-eda5e69ca327_567x164.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!k6YX!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b4c02f1-c0c2-4e7a-81a0-eda5e69ca327_567x164.png 424w, /__u/substackcdn.com/image/fetch/$s_!k6YX!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b4c02f1-c0c2-4e7a-81a0-eda5e69ca327_567x164.png 848w, /__u/substackcdn.com/image/fetch/$s_!k6YX!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b4c02f1-c0c2-4e7a-81a0-eda5e69ca327_567x164.png 1272w, /__u/substackcdn.com/image/fetch/$s_!k6YX!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b4c02f1-c0c2-4e7a-81a0-eda5e69ca327_567x164.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!k6YX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b4c02f1-c0c2-4e7a-81a0-eda5e69ca327_567x164.png" width="567" height="164" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1b4c02f1-c0c2-4e7a-81a0-eda5e69ca327_567x164.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:164,&quot;width&quot;:567,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!k6YX!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b4c02f1-c0c2-4e7a-81a0-eda5e69ca327_567x164.png 424w, /__u/substackcdn.com/image/fetch/$s_!k6YX!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b4c02f1-c0c2-4e7a-81a0-eda5e69ca327_567x164.png 848w, /__u/substackcdn.com/image/fetch/$s_!k6YX!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b4c02f1-c0c2-4e7a-81a0-eda5e69ca327_567x164.png 1272w, /__u/substackcdn.com/image/fetch/$s_!k6YX!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b4c02f1-c0c2-4e7a-81a0-eda5e69ca327_567x164.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><figcaption class="image-caption">Graphical depiction of a boxplot highlighting key components, including the median, quartiles, outliers, and Interquartile Range. Image created by author.</figcaption></figure></div><p>Boxplots can be used to:</p><ul><li><p>Identify outliers or anomalous data points</p></li><li><p>To determine if our data is skewed</p></li><li><p>To understand the spread/range of the data</p></li></ul><p>To construct a boxplot, we first start with the median value (50th percentile). This represents the middle value within our data.</p><p>A box is then formed between the 25th and 75th percentiles (Q1 and Q3 respectively). The range represented by this box is known as the interquartile range (IQR).</p><p>From this box extends two lines, which are also known as the whiskers. These extend to Q1&#8211;1.5 * IQR and Q3 + 1.5 * IQR or to the last data point if it is less than this value.</p><p>Any points that fall beyond the whisker limits are known as outliers.</p><p><em>I have previously written on Medium about creating boxplots, especially for well log data:</em></p><p><em><strong><a href="/__u/andymcdonaldgeo.substack.com/p/creating-boxplots-with-the-seaborn-python-library-f0c20f09bd57?utm_source=publication-search">Creating Boxplots with the Seaborn Python Librar</a>y</strong></em></p><h2><strong>Why Adding Raw Data Points Can Help</strong></h2><p>A boxplot is good at showing the middle of the distribution and the overall spread in a compact way. It becomes less helpful when:</p><ul><li><p>group sizes differ and the chart gives no real sense of how much data supports each box</p></li><li><p>values are clustered or split into sub-groups that the IQR spans but doesn&#8217;t reveal</p></li><li><p>a few high or low values stretch the whiskers in ways that look more important than they really are</p></li></ul><p>Overlaying the raw points helps with all three.</p><p>You can see how many values sit behind each group, whether the distribution looks smooth or broken into pockets, and whether extreme values are isolated or part of a broader tail.</p><p>In seaborn, the quickest way to do this is with <code>stripplot</code>, which adds a small amount of horizontal jitter so the points don&#8217;t sit directly on top of each other. It&#8217;s not the same as a true beeswarm plot, which places points more systematically, but it is simple, effective, and often good enough for exploratory work.</p><h2><strong>Setting Up the Dataset</strong></h2><p>To make the comparison worthwhile, the synthetic data needs to contain patterns that a plain boxplot doesn&#8217;t show very clearly.</p><p>We will use three formations:</p><ul><li><p><strong>Aldwick</strong>: a fairly broad, high-porosity group</p></li><li><p><strong>Brentford</strong>: a bimodal group made from two clusters</p></li><li><p><strong>Castleton</strong>: a low-porosity group with a tight main cluster and a few higher values</p></li></ul><p>The group sizes are also deliberately unequal.</p><pre><code>import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import pandas as pd
import numpy as np

np.random.seed(42)
aldwick = np.random.normal(0.22, 0.04, 80)
brentford = np.concatenate([
    np.random.normal(0.12, 0.015, 30),
    np.random.normal(0.19, 0.015, 30)
])
castleton = np.concatenate([
    np.random.normal(0.075, 0.012, 45),
    np.random.normal(0.13, 0.01, 5)
])
data = pd.DataFrame({
    &#8216;Formation&#8217;: (
        [&#8217;Aldwick&#8217;] * len(aldwick) +
        [&#8217;Brentford&#8217;] * len(brentford) +
        [&#8217;Castleton&#8217;] * len(castleton)
    ),
    &#8216;Porosity&#8217;: np.concatenate([aldwick, brentford, castleton])
})
data[&#8217;Porosity&#8217;] = data[&#8217;Porosity&#8217;].clip(0, 0.35)</code></pre><p>This gives us three groups with different internal structure, rather than three variations of the same general shape.</p><h2><strong>Step 1: Start with a Plain Boxplot</strong></h2><p>A standard boxplot gives a useful baseline and can be generated simply using:</p><pre><code>fig, ax = plt.subplots(figsize=(8, 5))
sns.boxplot(data=data, x=&#8217;Formation&#8217;, y=&#8217;Porosity&#8217;, ax=ax)
plt.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!5eud!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564a67b7-4d33-4d1f-a824-142d0dece371_630x405.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!5eud!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564a67b7-4d33-4d1f-a824-142d0dece371_630x405.png 424w, /__u/substackcdn.com/image/fetch/$s_!5eud!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564a67b7-4d33-4d1f-a824-142d0dece371_630x405.png 848w, /__u/substackcdn.com/image/fetch/$s_!5eud!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564a67b7-4d33-4d1f-a824-142d0dece371_630x405.png 1272w, /__u/substackcdn.com/image/fetch/$s_!5eud!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564a67b7-4d33-4d1f-a824-142d0dece371_630x405.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!5eud!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564a67b7-4d33-4d1f-a824-142d0dece371_630x405.png" width="630" height="405" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/564a67b7-4d33-4d1f-a824-142d0dece371_630x405.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:405,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!5eud!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564a67b7-4d33-4d1f-a824-142d0dece371_630x405.png 424w, /__u/substackcdn.com/image/fetch/$s_!5eud!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564a67b7-4d33-4d1f-a824-142d0dece371_630x405.png 848w, /__u/substackcdn.com/image/fetch/$s_!5eud!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564a67b7-4d33-4d1f-a824-142d0dece371_630x405.png 1272w, /__u/substackcdn.com/image/fetch/$s_!5eud!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F564a67b7-4d33-4d1f-a824-142d0dece371_630x405.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Default boxplot showing the three formations</figcaption></figure></div><p>At first glance, the comparison looks clean and straightforward. Aldwick has the highest median porosity, Castleton the lowest, and Brentford sits in between.</p><p>That is useful, but it doesn&#8217;t tell you much about what is happening inside each group. You can&#8217;t see that Brentford is made from two clusters, nor can you tell how many observations sit behind each box. Additionally, you can&#8217;t tell whether the values inside the IQR are evenly spread or tightly packed into parts of the range.</p><h2><strong>Step 2: Overlay the Raw Data Points</strong></h2><p>The main change is simply to add a strip plot on top of the boxplot.</p><p>One small detail matters here: set <code>fliersize=0</code> on the boxplot first, otherwise the extreme values will appear twice.</p><pre><code>fig, ax = plt.subplots(figsize=(8, 5))

sns.boxplot(
    data=data,
    x=&#8217;Formation&#8217;,
    y=&#8217;Porosity&#8217;,
    ax=ax,
    width=0.4,
    color=&#8217;lightgray&#8217;,
    fliersize=0
)
sns.stripplot(
    data=data,
    x=&#8217;Formation&#8217;,
    y=&#8217;Porosity&#8217;,
    hue=&#8217;Formation&#8217;,
    legend=False,
    ax=ax,
    jitter=True,
    size=4,
    alpha=0.5,
    palette=&#8217;Set2&#8217;
)
plt.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!9HCh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be2dfb6-1434-453f-b615-4f2cd3a2788c_630x404.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!9HCh!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be2dfb6-1434-453f-b615-4f2cd3a2788c_630x404.png 424w, /__u/substackcdn.com/image/fetch/$s_!9HCh!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be2dfb6-1434-453f-b615-4f2cd3a2788c_630x404.png 848w, /__u/substackcdn.com/image/fetch/$s_!9HCh!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be2dfb6-1434-453f-b615-4f2cd3a2788c_630x404.png 1272w, /__u/substackcdn.com/image/fetch/$s_!9HCh!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be2dfb6-1434-453f-b615-4f2cd3a2788c_630x404.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!9HCh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be2dfb6-1434-453f-b615-4f2cd3a2788c_630x404.png" width="630" height="404" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6be2dfb6-1434-453f-b615-4f2cd3a2788c_630x404.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:404,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!9HCh!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be2dfb6-1434-453f-b615-4f2cd3a2788c_630x404.png 424w, /__u/substackcdn.com/image/fetch/$s_!9HCh!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be2dfb6-1434-453f-b615-4f2cd3a2788c_630x404.png 848w, /__u/substackcdn.com/image/fetch/$s_!9HCh!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be2dfb6-1434-453f-b615-4f2cd3a2788c_630x404.png 1272w, /__u/substackcdn.com/image/fetch/$s_!9HCh!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6be2dfb6-1434-453f-b615-4f2cd3a2788c_630x404.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Boxplot with raw data points overlaid</figcaption></figure></div><p>Now the differences are more informative.</p><p>Aldwick shows a broad cloud of values at the high-porosity end. Brentford is no longer just a mid-range box. You can see that the values gather into two bands. Castleton shows a tight low-porosity cluster with a small number of higher values above it, and a few outliers.</p><p>The summary statistics are still there, but they are no longer doing all the talking.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share Subsurface Syntax&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share Subsurface Syntax</span></a></p><h2><strong>Step 3: Reduce Visual Clutter</strong></h2><p>With larger groups, the overlay can quickly become messy. Smaller markers and a little more transparency usually help.</p><pre><code>fig, ax = plt.subplots(figsize=(8, 5))

sns.boxplot(
    data=data,
    x=&#8217;Formation&#8217;,
    y=&#8217;Porosity&#8217;,
    ax=ax,
    width=0.4,
    color=&#8217;lightgray&#8217;,
    fliersize=0
)
sns.stripplot(
    data=data,
    x=&#8217;Formation&#8217;,
    y=&#8217;Porosity&#8217;,
    hue=&#8217;Formation&#8217;,
    legend=False,
    ax=ax,
    jitter=True,
    size=3,
    alpha=0.4,
    palette=&#8217;Set2&#8217;
)
plt.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!hjl7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e6ac2-7bbe-4959-b165-be485d530ef2_630x403.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!hjl7!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e6ac2-7bbe-4959-b165-be485d530ef2_630x403.png 424w, /__u/substackcdn.com/image/fetch/$s_!hjl7!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e6ac2-7bbe-4959-b165-be485d530ef2_630x403.png 848w, /__u/substackcdn.com/image/fetch/$s_!hjl7!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e6ac2-7bbe-4959-b165-be485d530ef2_630x403.png 1272w, /__u/substackcdn.com/image/fetch/$s_!hjl7!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e6ac2-7bbe-4959-b165-be485d530ef2_630x403.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!hjl7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e6ac2-7bbe-4959-b165-be485d530ef2_630x403.png" width="630" height="403" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/362e6ac2-7bbe-4959-b165-be485d530ef2_630x403.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:403,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!hjl7!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e6ac2-7bbe-4959-b165-be485d530ef2_630x403.png 424w, /__u/substackcdn.com/image/fetch/$s_!hjl7!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e6ac2-7bbe-4959-b165-be485d530ef2_630x403.png 848w, /__u/substackcdn.com/image/fetch/$s_!hjl7!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e6ac2-7bbe-4959-b165-be485d530ef2_630x403.png 1272w, /__u/substackcdn.com/image/fetch/$s_!hjl7!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e6ac2-7bbe-4959-b165-be485d530ef2_630x403.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Overlay with smaller markers and more transparency</figcaption></figure></div><p>This version is easier to read. Dense areas begin to appear darker, which gives you a rough visual sense of where values are concentrated without needing a separate density plot.</p><p>It is still a simple chart, but it carries more of the distribution with it.</p><h2><strong>Step 4: Add Group Means</strong></h2><p>A boxplot already gives you the median. In skewed data, the mean can sit in a slightly different place, and that difference can be useful to see.</p><p>A short dashed line across each group keeps the mean marker distinct from the existing boxplot elements.</p><pre><code>formations = [&#8217;Aldwick&#8217;, &#8216;Brentford&#8217;, &#8216;Castleton&#8217;]

fig, ax = plt.subplots(figsize=(8, 5))
sns.boxplot(
    data=data,
    x=&#8217;Formation&#8217;,
    y=&#8217;Porosity&#8217;,
    ax=ax,
    width=0.4,
    color=&#8217;lightgray&#8217;,
    fliersize=0
)
sns.stripplot(
    data=data,
    x=&#8217;Formation&#8217;,
    y=&#8217;Porosity&#8217;,
    hue=&#8217;Formation&#8217;,
    legend=False,
    ax=ax,
    jitter=True,
    size=3,
    alpha=0.4,
    palette=&#8217;Set2&#8217;
)
for i, formation in enumerate(formations):
    mean_val = data.loc[data[&#8217;Formation&#8217;] == formation, &#8216;Porosity&#8217;].mean()
    ax.hlines(
        mean_val,
        i - 0.25, i + 0.25,
        colors=&#8217;black&#8217;,
        linewidths=2,
        linestyles=&#8217;--&#8217;,
        zorder=5
    )
plt.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!cC-s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd509a632-2242-497c-b317-247d83d7aa87_630x412.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!cC-s!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd509a632-2242-497c-b317-247d83d7aa87_630x412.png 424w, /__u/substackcdn.com/image/fetch/$s_!cC-s!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd509a632-2242-497c-b317-247d83d7aa87_630x412.png 848w, /__u/substackcdn.com/image/fetch/$s_!cC-s!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd509a632-2242-497c-b317-247d83d7aa87_630x412.png 1272w, /__u/substackcdn.com/image/fetch/$s_!cC-s!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd509a632-2242-497c-b317-247d83d7aa87_630x412.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!cC-s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd509a632-2242-497c-b317-247d83d7aa87_630x412.png" width="630" height="412" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d509a632-2242-497c-b317-247d83d7aa87_630x412.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:412,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!cC-s!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd509a632-2242-497c-b317-247d83d7aa87_630x412.png 424w, /__u/substackcdn.com/image/fetch/$s_!cC-s!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd509a632-2242-497c-b317-247d83d7aa87_630x412.png 848w, /__u/substackcdn.com/image/fetch/$s_!cC-s!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd509a632-2242-497c-b317-247d83d7aa87_630x412.png 1272w, /__u/substackcdn.com/image/fetch/$s_!cC-s!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd509a632-2242-497c-b317-247d83d7aa87_630x412.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Mean markers added as dashed horizontal lines</figcaption></figure></div><p>This adds one more layer of context without making the chart feel busy. In Castleton, for example, the small number of higher values nudges the mean above the main cluster. That is easier to appreciate when the points are visible as well.</p><h2><strong>Step 5: Final Polish</strong></h2><p>The final version adds a few useful presentation touches:</p><ul><li><p>consistent colours for each formation</p></li><li><p><code>n=</code> labels beneath each group</p></li><li><p>light horizontal gridlines</p></li><li><p>cleaner spines</p></li><li><p>a clearer title and axis labels</p></li></ul><pre><code>formation_colours = {
    &#8216;Aldwick&#8217;: &#8216;#2ecc71&#8217;,
    &#8216;Brentford&#8217;: &#8216;#3498db&#8217;,
    &#8216;Castleton&#8217;: &#8216;#e74c3c&#8217;
}

fig, ax = plt.subplots(figsize=(9, 6), facecolor=&#8217;white&#8217;)
sns.boxplot(
    data=data,
    x=&#8217;Formation&#8217;,
    y=&#8217;Porosity&#8217;,
    ax=ax,
    width=0.4,
    color=&#8217;#ecf0f1&#8217;,
    fliersize=0,
    linewidth=1.2
)
sns.stripplot(
    data=data,
    x=&#8217;Formation&#8217;,
    y=&#8217;Porosity&#8217;,
    hue=&#8217;Formation&#8217;,
    legend=False,
    ax=ax,
    jitter=True,
    size=3.5,
    alpha=0.5,
    palette=list(formation_colours.values())
)
for i, formation in enumerate(formations):
    mean_val = data.loc[data[&#8217;Formation&#8217;] == formation, &#8216;Porosity&#8217;].mean()
    ax.hlines(
        mean_val,
        i - 0.25, i + 0.25,
        colors=&#8217;#2c3e50&#8217;,
        linewidths=2,
        linestyles=&#8217;--&#8217;,
        zorder=5
    )
    
    n = len(data.loc[data[&#8217;Formation&#8217;] == formation])
    ax.text(
        i, -0.015, f&#8217;n={n}&#8217;,
        ha=&#8217;center&#8217;, va=&#8217;top&#8217;,
        fontsize=9, color=&#8217;#555555&#8217;
    )
ax.set_xlabel(&#8217;Formation&#8217;, fontsize=12, fontweight=&#8217;bold&#8217;, labelpad=24)
ax.set_ylabel(&#8217;Porosity (v/v)&#8217;, fontsize=12, fontweight=&#8217;bold&#8217;, labelpad=10)
ax.set_title(&#8217;Porosity Distribution by Formation&#8217;, fontsize=14, fontweight=&#8217;bold&#8217;, pad=15)
ax.spines[[&#8217;top&#8217;, &#8216;right&#8217;]].set_visible(False)
ax.grid(axis=&#8217;y&#8217;, alpha=0.3, linestyle=&#8217;--&#8217;, linewidth=0.8)
ax.tick_params(axis=&#8217;both&#8217;, labelsize=10)
ax.set_ylim(0, 0.32)
mean_patch = mpatches.Patch(color=&#8217;#2c3e50&#8217;, label=&#8217;Group mean (dashed)&#8217;)
ax.legend(handles=[mean_patch], fontsize=9, framealpha=0.9)
plt.tight_layout()
plt.savefig(&#8217;porosity_overlay.png&#8217;, dpi=150, bbox_inches=&#8217;tight&#8217;, facecolor=&#8217;white&#8217;)
plt.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!uVSr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18dbb06-c03c-4df9-8aa2-4763f8addbb3_630x417.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!uVSr!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18dbb06-c03c-4df9-8aa2-4763f8addbb3_630x417.png 424w, /__u/substackcdn.com/image/fetch/$s_!uVSr!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18dbb06-c03c-4df9-8aa2-4763f8addbb3_630x417.png 848w, /__u/substackcdn.com/image/fetch/$s_!uVSr!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18dbb06-c03c-4df9-8aa2-4763f8addbb3_630x417.png 1272w, /__u/substackcdn.com/image/fetch/$s_!uVSr!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18dbb06-c03c-4df9-8aa2-4763f8addbb3_630x417.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!uVSr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18dbb06-c03c-4df9-8aa2-4763f8addbb3_630x417.png" width="630" height="417" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a18dbb06-c03c-4df9-8aa2-4763f8addbb3_630x417.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:417,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!uVSr!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18dbb06-c03c-4df9-8aa2-4763f8addbb3_630x417.png 424w, /__u/substackcdn.com/image/fetch/$s_!uVSr!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18dbb06-c03c-4df9-8aa2-4763f8addbb3_630x417.png 848w, /__u/substackcdn.com/image/fetch/$s_!uVSr!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18dbb06-c03c-4df9-8aa2-4763f8addbb3_630x417.png 1272w, /__u/substackcdn.com/image/fetch/$s_!uVSr!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa18dbb06-c03c-4df9-8aa2-4763f8addbb3_630x417.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Final polished chart with raw point overlay, mean markers, and sample counts</figcaption></figure></div><h2><strong>What the Overlay Reveals</strong></h2><p>The final chart shows things that a plain boxplot only hints at.</p><p>First, the group sizes are immediately more obvious. Aldwick has more observations than Castleton, and the chart no longer makes those boxes look equally well supported.</p><p>Second, Brentford is revealed as a split distribution rather than a single smooth middle range. A plain boxplot summarises that group neatly, but it doesn&#8217;t tell you that the values are gathering into two distinct bands.</p><p>Third, Castleton shows how a small number of higher values can affect the upper part of the summary without changing the fact that most observations remain tightly clustered at the low end.</p><p>This is the real strength of the combination. It lets you keep the summary while checking what kind of data is actually producing it.</p><p>That is particularly useful in exploratory work, where similar-looking boxplots can hide very different underlying patterns.</p><h2><strong>When This Is Most Useful</strong></h2><p>This combination works best when:</p><ul><li><p>you are comparing a small number of groups</p></li><li><p>the number of observations per group is not too large</p></li><li><p>you want to see whether the summary statistics are masking something important</p></li><li><p>sample size, clustering, or outliers could change how you interpret the result</p></li></ul><p>It&#8217;s especially helpful as a check before deciding what kind of final figure to publish.</p><p>A plain boxplot might still be the right choice for the finished chart. But, the overlay is often the thing that tells you whether the plain boxplot is leaving too much unsaid.</p><h2><strong>Drawbacks to Keep in Mind</strong></h2><p>This approach does have limits though.</p><p>Once each group contains several hundred observations, the raw points can start to blur into clutter, even with transparency. At that point, a violin plot, KDE, or another density-based chart may communicate the shape more clearly.</p><p>Jitter also needs a little care. The horizontal spread of the points is artificial. It is there only to stop overlap, not to encode any additional meaning. If a reader treats that width as data, the chart can be misread.</p><p>There is also a judgement call around audience. For exploratory work, the extra detail is often worth it. For presentation slides or reports, it depends whether the raw points genuinely help interpretation or just make the figure busier.</p><div><hr></div><p><em><strong>If this kind of chart interests you, ridgeline plots are worth adding to your toolkit. They work particularly well when you have more groups than a box plot can comfortably handle, and you want to compare distribution shapes rather than just summary statistics.</strong></em></p><p><em><strong>I wrote a full walkthrough on Substack covering how to build them in matplotlib with petrophysical data:</strong></em></p><p><em><strong><a href="/__u/andymcdonaldgeo.substack.com/p/ridgeline-plots-in-matplotlib-an">Ridgeline Plots in Matplotlib: An Alternative to Faceted Histograms</a></strong></em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/p/boxplots-are-hiding-your-data-heres?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Thanks for reading Subsurface Syntax! This post is public so feel free to share it.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/p/boxplots-are-hiding-your-data-heres?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/p/boxplots-are-hiding-your-data-heres?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share</span></a></p></div><p></p>]]></content:encoded></item><item><title><![CDATA[How to Organise Jupyter Notebooks So Older Work Stays Usable]]></title><description><![CDATA[A lightweight workflow for keeping notebook-heavy projects searchable, readable, and easy to return to]]></description><link>https://andymcdonaldgeo.substack.com/p/how-to-organise-jupyter-notebooks</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/how-to-organise-jupyter-notebooks</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Sat, 21 Mar 2026 11:37:18 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!BfDG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a41641-999f-40a8-bb2d-383735d35d9f_630x345.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!BfDG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a41641-999f-40a8-bb2d-383735d35d9f_630x345.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!BfDG!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a41641-999f-40a8-bb2d-383735d35d9f_630x345.png 424w, /__u/substackcdn.com/image/fetch/$s_!BfDG!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a41641-999f-40a8-bb2d-383735d35d9f_630x345.png 848w, /__u/substackcdn.com/image/fetch/$s_!BfDG!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a41641-999f-40a8-bb2d-383735d35d9f_630x345.png 1272w, /__u/substackcdn.com/image/fetch/$s_!BfDG!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a41641-999f-40a8-bb2d-383735d35d9f_630x345.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!BfDG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a41641-999f-40a8-bb2d-383735d35d9f_630x345.png" width="630" height="345" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a2a41641-999f-40a8-bb2d-383735d35d9f_630x345.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:345,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!BfDG!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a41641-999f-40a8-bb2d-383735d35d9f_630x345.png 424w, /__u/substackcdn.com/image/fetch/$s_!BfDG!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a41641-999f-40a8-bb2d-383735d35d9f_630x345.png 848w, /__u/substackcdn.com/image/fetch/$s_!BfDG!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a41641-999f-40a8-bb2d-383735d35d9f_630x345.png 1272w, /__u/substackcdn.com/image/fetch/$s_!BfDG!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa2a41641-999f-40a8-bb2d-383735d35d9f_630x345.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In the<strong> <a href="/__u/andymcdonaldgeo.substack.com/p/when-a-jupyter-notebook-is-the-wrong">previous article</a></strong>, I looked at the project structure around a notebook: a consistent folder layout, a central <code>config.py</code> for paths, reusable code in <code>src/</code>, and a <strong><a href="https://github.com/andymcdgeo/jupyter_notebook_template">Cookiecutter template</a></strong> to generate the whole setup quickly.</p><p>That part solves a lot of the early mess. You stop rebuilding folders from scratch, copying imports from older notebooks, and writing file paths that only work on your machine.</p><p>But even with that in place, another problem shows up once the notebook count starts to grow.</p><p>When you only have a few notebooks, organisation hardly feels like a real issue. You know where things are. The filenames are still familiar. Even a slightly messy folder is manageable because the whole thing still fits in your head.</p><p>That stops working once the collection gets bigger.</p><p>Now you are dealing with exploratory notebooks, finished notebooks, old branches, repeated project folders, and the occasional file whose name made perfect sense at the time and now looks complete gibberish. At that point, the problem is not neatness. It is being able to find the right notebook quickly, know what state it&#8217;s in, and recover enough context to trust it.</p><p>This article picks up where the project structure article left off. The folder layout stays the same. What changes is the way the notebooks inside each project are organised.</p><p>The system is simple: organise by project, put status in the filename, keep a short README in each project, and maintain a lightweight index of finished notebooks. The goal is not to build a perfect archive. It is to make older work usable without turning notebook organisation into a side hobby.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/subscribe"><span>Subscribe now</span></a></p><h2><strong>The Problem With Most Notebook Folders</strong></h2><p>Most notebook folders do not really organise work. They just collect it.</p><p>Working folders that contain notebooks tend to grow and become messy.</p><p>Files get created wherever the current task happens to live, then they get renamed inconsistently, and left behind once the immediate job is done.</p><p>The newest work stays visible. Older work gets buried. After enough time, finding something depends less on structure and more on whether you still remember what you happened to call it six months ago.</p><p>The other issue is that not all notebooks serve the same purpose. Some are rough exploration, whereas others represent the cleaned record of a finished analysis.</p><p>Then you have notebook templates or quick scratch files that were useful to test out an idea, but never looked at again.</p><p>There is also the context problem. Code can show what happened, but it often does not explain why a threshold was chosen, which notebook fed the final figure, or what changed between one version and another. Those details tend to live in memory. And when you try to refresh that memory months later it not only takes time, but can also be draining trying to figure out what you thought several months ago when you wrote it.</p><p>A useful notebook structure needs to do three things well:</p><ul><li><p>it should help you find notebooks quickly</p></li><li><p>tell you what state they are in</p></li><li><p>preserve enough context that older work still makes sense</p></li></ul><h2><strong>Start With the Same Project Structure Every Time</strong></h2><p>This article assumes the project structure from the earlier piece is already in place. If not, you can check it out here:</p><p><strong><a href="/__u/andymcdonaldgeo.substack.com/p/the-project-structure-that-makes">The Project Structure That Makes Good Jupyter Notebook Habits Automatic</a></strong></p><p>That means each project has a standard layout with: <code>notebooks/</code>, <code>src/</code>, <code>data/raw/</code>, <code>data/processed/</code>, <code>outputs/</code>, and a <code>README.md</code>.</p><p>Raw data stays separate from processed data. Reusable code moves into <code>src/</code> once it starts getting copied between notebooks. Paths are handled centrally instead of being scattered through cells.</p><p>That matters because notebook organisation works best when it sits inside a predictable project layout. If every project stores things differently, neat notebook names will not save you.</p><p>This article is not a replacement for that earlier structure. It is the next layer of the same system.</p><h2><strong>Folder Structure: Organise by Project, Not by Topic</strong></h2><p>Once notebook collections get large, it is tempting to organise by topic. When I look back at old notebook folders I initially had the idea to structure them by their use case: one folder for QC, one for plotting, one for feature engineering and one for interpretation work.</p><p>It sounds sensible, however, in practice, it usually becomes awkward, especially as the number of notebooks start to multiply.</p><p>Most real notebooks don&#8217;t belong cleanly to one topic. A QC notebook might load data, validate curves, produce crossplots, summarise completeness by formation, and export something for later modelling. File it under one topic and you may miss it under another. File it under several and you create duplicates or shortcuts that quietly breed like rabbits.</p><p>I have found that project-based organisation is usually much easier to live with. Although it is far from perfect it has started to help with that organisation effort.</p><p>When someone asks about the Volve study from last quarter, there should be one project folder to open. When you come back to an older model build, there should be one place where the notebooks, data, outputs, and notes sit together.</p><p>So the structure still starts with projects:</p><pre><code>projects/
    2025-11_volve-petrophysical-qc/
        notebooks/
        src/
        data/
            raw/
            processed/
        outputs/
            figures/
            exports/
        README.md
    2026-01_ml-lithology-classifier/
        notebooks/
        src/
        data/
            raw/
            processed/
        outputs/
            figures/
            exports/
        README.md
shared/
    templates/
    utils/</code></pre><p>The date prefix helps keeps projects sorted naturally in time. The more important point is that every project has the same shape.</p><p>Once that is true, the next question is how the notebooks inside each project should be named.</p><h2><strong>Naming Conventions: Put Status in the Filename</strong></h2><p>Folder structure helps you find the right project. Filenames should tell you what each notebook is doing inside it.</p><p>The convention that I have started to use is:</p><pre><code>{order}_{descriptor}_{status}.ipynb</code></pre><p>So a notebooks folder might look like this:</p><pre><code>notebooks/
    01_data-loading_explore.ipynb
    02_curve-qc_explore.ipynb
    03_curve-qc_record.ipynb
    04_formation-analysis_explore.ipynb
    05_formation-analysis_record.ipynb
    06_completeness-summary_record.ipynb</code></pre><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!ilWt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07e4556-eae9-46ae-823d-30da8852772b_630x146.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!ilWt!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07e4556-eae9-46ae-823d-30da8852772b_630x146.png 424w, /__u/substackcdn.com/image/fetch/$s_!ilWt!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07e4556-eae9-46ae-823d-30da8852772b_630x146.png 848w, /__u/substackcdn.com/image/fetch/$s_!ilWt!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07e4556-eae9-46ae-823d-30da8852772b_630x146.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ilWt!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07e4556-eae9-46ae-823d-30da8852772b_630x146.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!ilWt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07e4556-eae9-46ae-823d-30da8852772b_630x146.png" width="630" height="146" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c07e4556-eae9-46ae-823d-30da8852772b_630x146.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:146,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!ilWt!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07e4556-eae9-46ae-823d-30da8852772b_630x146.png 424w, /__u/substackcdn.com/image/fetch/$s_!ilWt!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07e4556-eae9-46ae-823d-30da8852772b_630x146.png 848w, /__u/substackcdn.com/image/fetch/$s_!ilWt!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07e4556-eae9-46ae-823d-30da8852772b_630x146.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ilWt!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc07e4556-eae9-46ae-823d-30da8852772b_630x146.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The order prefix gives the folder a readable sequence. The descriptor says what the notebook is actually about. The status suffix tells you whether it is scratch work or the finished version.</p><p>That suffix does most of the heavy lifting.</p><p>I tend to use four:</p><p><code>_explore</code> for active working notebooks<br><code>_record</code> for clean, rerunnable notebooks that represent the finished analysis<br><code>_archive</code> for older notebooks kept for reference<br><code>_template</code> for starter notebooks kept in a shared templates area</p><p>The important split is really just <code>_explore</code> versus <code>_record</code>.</p><p>That distinction stops a notebooks folder from becoming one flat pile where everything looks equally official. It also ties back neatly to the previous article. There, the structure separated notebooks from reusable code, raw data, and outputs. Here, the same idea is applied inside the notebooks layer itself.</p><p>The one catch is obvious: you have to update the suffix when a notebook changes status. If everything stays <code>_explore</code>, the system quietly loses its value.</p><h2><strong>Keep an Archive, but Keep It Tidy</strong></h2><p>An archive can be useful, however, it can easily and quickly become a messy dumping ground.</p><p>Sometimes we have a tendency to think that every little thing we did in a notebook matters and that we will come back to it. In reality, I rarely find myself going back to some of these notebooks as most of the important information and ideas have already been extracted.</p><p>If a notebook has been superseded, but you feel it still matters, move it into an <code>archive/</code> folder or mark it clearly as <code>_archive</code>. That keeps the active notebooks folder readable without throwing away older work.</p><p>The aim isn&#8217;t to preserve every false start in amber. It is just to stop the live folder becoming a geological record of every passing thought.</p><h2><strong>The README: One File That Explains the Project</strong></h2><p>Every project folder should have a <code>README.md</code>.</p><p>This was already part of the earlier project structure, but it becomes even more useful once there are several notebooks involved. The README gives you the top-level context without making you open everything to work out what happened.</p><p>It does not need to be long. It just needs to answer the questions your future self will otherwise have to reconstruct:</p><ul><li><p>what the project is</p></li><li><p>what data or wells it uses</p></li><li><p>what it produced</p></li><li><p>which notebooks matter</p></li><li><p>what decisions or exclusions are important</p></li></ul><p>A simple version might look like this:</p><pre><code># Volve Petrophysical QC

<strong>**Status:**</strong> Complete
<strong>**Period:**</strong> November 2025
<strong>**Wells:**</strong> 15/9-F-1, 15/9-F-4, 15/9-F-11
<strong>**Data source:**</strong> Volve open dataset, DISKOS export

## What This Is
GR, RHOB, and NPHI curve QC and completeness analysis across
three Volve producers.

## Key Outputs
- outputs/exports/curve<em>_completeness_</em>by<em>_formation.csv
- outputs/figures/completeness_</em>heatmap.png

## Notebooks
1. 01<em>_data-loading_</em>record
2. 02<em>_curve-qc_</em>record
3. 03<em>_formation-analysis_</em>record

## Key Decisions
- GR values &lt; 0 excluded as noise
- Brent Group subdivisions collapsed to group level
- Intervals above casing shoe excluded</code></pre><p>The most useful parts are usually the outputs and the decisions. Those are the first things that fade from memory and the things most likely to matter later.</p><p>If you&#8217;re required to produce reports of your findings, you could also add executive summaries of each notebook and key findings into the README file as you go along. That way when it comes to generating the final report you have already done half the work.</p><h2><strong>The Notebook Index: A Lightweight Cross-Project Search Layer</strong></h2><p>Once you have enough projects, even a good folder structure still depends a bit too much on memory.</p><p>You know the notebook exists. You know it dealt with completeness by formation or feature engineering or a specific well set. But which project was it in?</p><p>That is where a small notebook index helps.</p><p>At the top level of the workspace, keep a CSV like this:</p><pre><code>notebooks_index.csv</code></pre><p>Each row is one finished <code>_record</code> notebook:</p><pre><code>project,notebook,status,purpose,last_run,notes
2025-11_volve-qc,03_formation-analysis_record,record,completeness by formation across three Volve producers,2025-11-28,used in ML feature matrix
2026-01_ml-classifier,02_feature-engineering_record,record,builds feature matrix for lithology classifier,2026-01-14,uses volve QC outputs</code></pre><p>I wouldn&#8217;t bother with this at very small scale. But once the notebook count grows, it becomes useful surprisingly quickly.</p><p>The important thing is that the index only tracks <code>_record</code> notebooks. It is there to help you find finished work, not every temporary rabbit hole you disappeared into on a Tuesday afternoon.</p><p>The <code>purpose</code> column matters most. If every row says <code>analysis notebook</code>, the whole thing is pointless. If the descriptions are specific, it becomes genuinely searchable.</p><p>Alternatively, you could create this in markdown if you feel comfortable creating tables.</p><h2><strong>Shared Templates: Make Good Starts Easier</strong></h2><p>This part ties directly back to the earlier article.</p><p>If you already have starter templates, keep using them. A shared <code>template_explore.ipynb</code> and <code>template_record.ipynb</code> make it easier to begin in a consistent way without having to think about setup every time.</p><pre><code>shared/
    templates/
        template_explore.ipynb
        template_record.ipynb</code></pre><p>The explore template can stay minimal. The record template should be a bit more structured, with room for assumptions, exclusions, outputs, and notes.</p><p>This isn&#8217;t about forcing every notebook to look pretty. It&#8217;s about reducing friction and making notebooks easier to read later.</p><p>I have some prepopulated notebook templates in my cookiecutter template on github: <a href="https://github.com/andymcdgeo/jupyter_notebook_template">andymcdgeo/jupyter_notebook_template</a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Sr2M!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7cb32ad-01d0-497c-858a-7d9eb7eedbd0_542x763.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Sr2M!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7cb32ad-01d0-497c-858a-7d9eb7eedbd0_542x763.png 424w, /__u/substackcdn.com/image/fetch/$s_!Sr2M!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7cb32ad-01d0-497c-858a-7d9eb7eedbd0_542x763.png 848w, /__u/substackcdn.com/image/fetch/$s_!Sr2M!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7cb32ad-01d0-497c-858a-7d9eb7eedbd0_542x763.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Sr2M!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7cb32ad-01d0-497c-858a-7d9eb7eedbd0_542x763.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Sr2M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7cb32ad-01d0-497c-858a-7d9eb7eedbd0_542x763.png" width="542" height="763" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d7cb32ad-01d0-497c-858a-7d9eb7eedbd0_542x763.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:763,&quot;width&quot;:542,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!Sr2M!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7cb32ad-01d0-497c-858a-7d9eb7eedbd0_542x763.png 424w, /__u/substackcdn.com/image/fetch/$s_!Sr2M!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7cb32ad-01d0-497c-858a-7d9eb7eedbd0_542x763.png 848w, /__u/substackcdn.com/image/fetch/$s_!Sr2M!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7cb32ad-01d0-497c-858a-7d9eb7eedbd0_542x763.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Sr2M!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd7cb32ad-01d0-497c-858a-7d9eb7eedbd0_542x763.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Example of an explore notebook template that has some preconfigured cells and functions.</figcaption></figure></div><h2><strong>How This Fits With </strong><code>src/</code><strong>, </strong><code>config.py</code><strong>, and the Wider Structure</strong></h2><p>It&#8217;s worth being clear that this article is not arguing for a notebook-only system.</p><p>The earlier project structure still does the heavy lifting around code, paths, and outputs. If you&#8217;re copying functions between notebooks, they should still move into <code>src/</code>. If paths are fragile, fix them in <code>config.py</code>. If outputs are scattered, standardise where they are written.</p><p>If code is shared between multiple projects, then it is time to consider an alternative, such as a personal python library that you develop alongside your projects.</p><p>This article is solving a different layer of the same problem by making the notebook collection inside each project more legible.</p><h2><strong>What This Does Not Solve</strong></h2><p>This system doesn&#8217;t replace version control, and it doesn&#8217;t solve collaborative editing. Shared notebook development still has all the usual Git awkwardness.</p><p>It also doesn&#8217;t stop you creating too many notebooks in the first place. If every small variation becomes a new file, the collection will still grow faster than it should.</p><p>And yes, it needs a bit of maintenance. You have to rename notebooks when they move from exploration to record. You have to keep the README current enough to stay useful. You have to add finished notebooks to the index.</p><p>But those are small jobs. The alternative is usually much more annoying later.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/subscribe"><span>Subscribe now</span></a></p><h2><strong>Closing Thoughts</strong></h2><p>The test of a notebook system isn&#8217;t whether it looks organised on a good day. It&#8217;s whether it still makes sense when you come back to to it months later or under pressure.</p><p>That is where most notebook folders start to fail. Not because the work was bad, but because the structure around it was too loose to hold its shape over time.</p><p><strong><a href="/__u/andymcdonaldgeo.substack.com/p/the-project-structure-that-makes">The project structure</a></strong> from the previous article solves part of that by giving each project a consistent layout. This layer builds on it by making the notebooks inside that structure easier to read, search, and trust.</p><p>If a notebook collection is small, you can get away with memory and loose naming. Once it grows, that stops being enough.</p><p>A little structure helps things go a long way.</p><div><hr></div><p><em>Good organisation becomes more valuable when individual notebooks are also worth organising. The <a href="https://medium.com/p/b6236499b479?postPublishedType=initial#">6 Jupyter Notebook Habits</a> article covers the practices that make notebooks traceable, rerunnable, and readable &#8212; which is what makes the system above worth building around them.</em></p>]]></content:encoded></item><item><title><![CDATA[From Default to Publication-Ready: Transforming Matplotlib Histograms]]></title><description><![CDATA[Seven Steps From a Default Chart to Something Worth Publishing]]></description><link>https://andymcdonaldgeo.substack.com/p/from-default-to-publication-ready-c22</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/from-default-to-publication-ready-c22</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Wed, 18 Mar 2026 07:50:11 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!rYMC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69a66ada-0f0a-4f8c-ac70-22e180eb8728_700x415.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!rYMC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69a66ada-0f0a-4f8c-ac70-22e180eb8728_700x415.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!rYMC!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69a66ada-0f0a-4f8c-ac70-22e180eb8728_700x415.png 424w, /__u/substackcdn.com/image/fetch/$s_!rYMC!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69a66ada-0f0a-4f8c-ac70-22e180eb8728_700x415.png 848w, /__u/substackcdn.com/image/fetch/$s_!rYMC!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69a66ada-0f0a-4f8c-ac70-22e180eb8728_700x415.png 1272w, /__u/substackcdn.com/image/fetch/$s_!rYMC!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69a66ada-0f0a-4f8c-ac70-22e180eb8728_700x415.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!rYMC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69a66ada-0f0a-4f8c-ac70-22e180eb8728_700x415.png" width="700" height="415" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/69a66ada-0f0a-4f8c-ac70-22e180eb8728_700x415.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:415,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!rYMC!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69a66ada-0f0a-4f8c-ac70-22e180eb8728_700x415.png 424w, /__u/substackcdn.com/image/fetch/$s_!rYMC!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69a66ada-0f0a-4f8c-ac70-22e180eb8728_700x415.png 848w, /__u/substackcdn.com/image/fetch/$s_!rYMC!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69a66ada-0f0a-4f8c-ac70-22e180eb8728_700x415.png 1272w, /__u/substackcdn.com/image/fetch/$s_!rYMC!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F69a66ada-0f0a-4f8c-ac70-22e180eb8728_700x415.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>When working with matplotlib the default plots that are generated can be somewhat plain and difficult to read. <strong><a href="https://medium.com/p/2a5fc3a22182">I have discussed this several times in the past</a></strong>. However, with some effort and a few coding tweaks we can convert the default plots into something that is fit to be placed in a publication.</p><p>In this article we will be looking at the histogram.</p><p>A histogram can actually tell you quite a lot. It can show the overall shape of a distribution, reveal skew, hint at bimodality, help spot outliers, and make it easier to compare groups when used carefully.</p><p>The problem is that the default matplotlib version doesn&#8217;t make much of that clear. You get a set of blue bars, automatic binning, and very little else to help the reader interpret what they are looking at.</p><p>In this article, we will take a default <code>plt.hist()</code> chart and improve it step by step into something you would be more comfortable including in a report or publication. The example uses made-up porosity data that is a common measurement within petrophysics and geology, but the steps and techniques can equally be applied to any dataset.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/subscribe"><span>Subscribe now</span></a></p><h2><strong>The Starting Point</strong></h2><p>To keep things simple, I will use some made-up porosity data. The values are designed to look realistic enough for demonstration purposes, with a slightly right-skewed distribution similar to what you might see in subsurface data.</p><pre><code>import matplotlib.pyplot as plt
import numpy as np

np.random.seed(42)
porosity = np.random.beta(a=2.0, b=9.5, size=1000) * 0.45

fig, ax = plt.subplots()
ax.hist(porosity)
plt.show()</code></pre><p>Before any styling, the default histogram looks like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!LGh2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6407349f-ff17-4825-899a-6ea81e3b3094_640x480.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!LGh2!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6407349f-ff17-4825-899a-6ea81e3b3094_640x480.png 424w, /__u/substackcdn.com/image/fetch/$s_!LGh2!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6407349f-ff17-4825-899a-6ea81e3b3094_640x480.png 848w, /__u/substackcdn.com/image/fetch/$s_!LGh2!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6407349f-ff17-4825-899a-6ea81e3b3094_640x480.png 1272w, /__u/substackcdn.com/image/fetch/$s_!LGh2!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6407349f-ff17-4825-899a-6ea81e3b3094_640x480.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!LGh2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6407349f-ff17-4825-899a-6ea81e3b3094_640x480.png" width="640" height="480" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6407349f-ff17-4825-899a-6ea81e3b3094_640x480.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:480,&quot;width&quot;:640,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!LGh2!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6407349f-ff17-4825-899a-6ea81e3b3094_640x480.png 424w, /__u/substackcdn.com/image/fetch/$s_!LGh2!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6407349f-ff17-4825-899a-6ea81e3b3094_640x480.png 848w, /__u/substackcdn.com/image/fetch/$s_!LGh2!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6407349f-ff17-4825-899a-6ea81e3b3094_640x480.png 1272w, /__u/substackcdn.com/image/fetch/$s_!LGh2!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6407349f-ff17-4825-899a-6ea81e3b3094_640x480.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>What you get is the standard matplotlib histogram: blue bars, automatic binning, no axis labels, and very little context for the reader. It is not incorrect, but it does very little to help interpret the distribution.</p><h2><strong>Step 1: Label Your Axes</strong></h2><p>This sounds obvious, but it gets skipped more often than it should, especially in exploratory notebooks. An unlabelled histogram is not much use to anyone who did not make it.</p><pre><code>fig, ax = plt.subplots(figsize=(8, 5))
ax.hist(porosity)
ax.set_xlabel(&#8217;Porosity (fraction)&#8217;, fontsize=12)
ax.set_ylabel(&#8217;Count&#8217;, fontsize=12)
ax.set_title(&#8217;Porosity Distribution&#8217;, fontsize=14)
plt.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!lHGD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58673ba7-de29-4dc7-8a8d-64155aa5938e_700x438.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!lHGD!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58673ba7-de29-4dc7-8a8d-64155aa5938e_700x438.png 424w, /__u/substackcdn.com/image/fetch/$s_!lHGD!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58673ba7-de29-4dc7-8a8d-64155aa5938e_700x438.png 848w, /__u/substackcdn.com/image/fetch/$s_!lHGD!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58673ba7-de29-4dc7-8a8d-64155aa5938e_700x438.png 1272w, /__u/substackcdn.com/image/fetch/$s_!lHGD!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58673ba7-de29-4dc7-8a8d-64155aa5938e_700x438.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!lHGD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58673ba7-de29-4dc7-8a8d-64155aa5938e_700x438.png" width="700" height="438" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/58673ba7-de29-4dc7-8a8d-64155aa5938e_700x438.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:438,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!lHGD!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58673ba7-de29-4dc7-8a8d-64155aa5938e_700x438.png 424w, /__u/substackcdn.com/image/fetch/$s_!lHGD!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58673ba7-de29-4dc7-8a8d-64155aa5938e_700x438.png 848w, /__u/substackcdn.com/image/fetch/$s_!lHGD!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58673ba7-de29-4dc7-8a8d-64155aa5938e_700x438.png 1272w, /__u/substackcdn.com/image/fetch/$s_!lHGD!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58673ba7-de29-4dc7-8a8d-64155aa5938e_700x438.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is a small change, but it immediately makes the chart easier to interpret. The reader can now see what is being plotted, what the bar heights represent, and what the figure is about.</p><h2><strong>Step 2: Choose Your Bin Count Deliberately</strong></h2><p>The number of bins has a big effect on how a histogram reads, so it is worth choosing it deliberately rather than leaving it entirely to the default behaviour.</p><p>There is no single correct answer.</p><p>Fewer bins emphasise the overall shape. More bins reveal more local variation, which might be useful structure or might just be noise. Too many bins and the histogram becomes very difficult to interpret. For many distributions, somewhere between 20 and 50 bins is often a reasonable place to start.</p><p>Working out the right number of bins may take some trial and error.</p><pre><code>fig, ax = plt.subplots(figsize=(8, 5))
ax.hist(porosity, bins=40, color=&#8217;steelblue&#8217;, edgecolor=&#8217;white&#8217;, linewidth=0.5)
ax.set_xlabel(&#8217;Porosity (fraction)&#8217;, fontsize=12)
ax.set_ylabel(&#8217;Count&#8217;, fontsize=12)
ax.set_title(&#8217;Porosity Distribution&#8217;, fontsize=14)
plt.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!acxc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc1abfeb-4218-444e-b98d-df4a735032b6_700x438.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!acxc!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc1abfeb-4218-444e-b98d-df4a735032b6_700x438.png 424w, /__u/substackcdn.com/image/fetch/$s_!acxc!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc1abfeb-4218-444e-b98d-df4a735032b6_700x438.png 848w, /__u/substackcdn.com/image/fetch/$s_!acxc!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc1abfeb-4218-444e-b98d-df4a735032b6_700x438.png 1272w, /__u/substackcdn.com/image/fetch/$s_!acxc!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc1abfeb-4218-444e-b98d-df4a735032b6_700x438.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!acxc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc1abfeb-4218-444e-b98d-df4a735032b6_700x438.png" width="700" height="438" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cc1abfeb-4218-444e-b98d-df4a735032b6_700x438.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:438,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!acxc!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc1abfeb-4218-444e-b98d-df4a735032b6_700x438.png 424w, /__u/substackcdn.com/image/fetch/$s_!acxc!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc1abfeb-4218-444e-b98d-df4a735032b6_700x438.png 848w, /__u/substackcdn.com/image/fetch/$s_!acxc!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc1abfeb-4218-444e-b98d-df4a735032b6_700x438.png 1272w, /__u/substackcdn.com/image/fetch/$s_!acxc!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc1abfeb-4218-444e-b98d-df4a735032b6_700x438.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The <code>edgecolor='white'</code> with a thin <code>linewidth</code> separates adjacent bars cleanly, which makes it much easier to read the boundaries between bins, especially where counts are similar.</p><h2><strong>Step 3: Add a KDE Overlay</strong></h2><p>A KDE (kernel density estimate) curve over the histogram can make the overall distribution shape easier to follow without getting too distracted by the individual bins. It is especially useful for seeing whether a distribution is broadly unimodal, bimodal, or slightly skewed in one direction.</p><p>To overlay a KDE on the histogram properly, the histogram needs to be normalised to density so that both sit on the same vertical scale.</p><pre><code>import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde

fig, ax = plt.subplots(figsize=(8, 5))
ax.hist(
    porosity,
    bins=40,
    color=&#8217;steelblue&#8217;,
    edgecolor=&#8217;white&#8217;,
    linewidth=0.5,
    density=True,
    alpha=0.7
)

kde = gaussian_kde(porosity)
x_range = np.linspace(porosity.min(), porosity.max(), 300)
ax.plot(x_range, kde(x_range), color=&#8217;#1a3a5c&#8217;, linewidth=2)

ax.set_xlabel(&#8217;Porosity (fraction)&#8217;, fontsize=12)
ax.set_ylabel(&#8217;Density&#8217;, fontsize=12)
ax.set_title(&#8217;Porosity Distribution&#8217;, fontsize=14)
plt.show()</code></pre><p>Switching to <code>density=True</code> changes the y-axis from count to density, so the axis label needs to change as well. That is a small detail, but an important one.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!M048!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0883e838-9759-4b8f-bbc9-0fd095babf13_700x438.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!M048!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0883e838-9759-4b8f-bbc9-0fd095babf13_700x438.png 424w, /__u/substackcdn.com/image/fetch/$s_!M048!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0883e838-9759-4b8f-bbc9-0fd095babf13_700x438.png 848w, /__u/substackcdn.com/image/fetch/$s_!M048!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0883e838-9759-4b8f-bbc9-0fd095babf13_700x438.png 1272w, /__u/substackcdn.com/image/fetch/$s_!M048!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0883e838-9759-4b8f-bbc9-0fd095babf13_700x438.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!M048!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0883e838-9759-4b8f-bbc9-0fd095babf13_700x438.png" width="700" height="438" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0883e838-9759-4b8f-bbc9-0fd095babf13_700x438.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:438,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!M048!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0883e838-9759-4b8f-bbc9-0fd095babf13_700x438.png 424w, /__u/substackcdn.com/image/fetch/$s_!M048!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0883e838-9759-4b8f-bbc9-0fd095babf13_700x438.png 848w, /__u/substackcdn.com/image/fetch/$s_!M048!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0883e838-9759-4b8f-bbc9-0fd095babf13_700x438.png 1272w, /__u/substackcdn.com/image/fetch/$s_!M048!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0883e838-9759-4b8f-bbc9-0fd095babf13_700x438.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The KDE is useful as a visual guide, but it is still a smoothed estimate rather than the raw distribution itself. It helps show the overall shape, but it should not be treated as more &#8220;true&#8221; than the histogram underneath it.</p><p><em>A KDE curve on a single histogram is useful. When you need to compare distributions across several formations at once, stacking them as a ridgeline plot often gives a cleaner read. I&#8217;ve written about that approach on Medium. Link at the end.</em></p><h2><strong>Step 4: Annotate Key Statistics</strong></h2><p>The mean and median usually sit invisibly inside a histogram. Adding vertical lines for both can give the reader a couple of useful reference points straight away, especially when the distribution is skewed and the two values do not sit in the same place.</p><pre><code>fig, ax = plt.subplots(figsize=(8, 5))
ax.hist(porosity, bins=40, color=&#8217;steelblue&#8217;, edgecolor=&#8217;white&#8217;,
        linewidth=0.5, density=True, alpha=0.7)

kde = gaussian_kde(porosity)
x_range = np.linspace(porosity.min(), porosity.max(), 300)
ax.plot(x_range, kde(x_range), color=&#8217;#1a3a5c&#8217;, linewidth=2)

mean_val = porosity.mean()
median_val = np.median(porosity)

ax.axvline(mean_val, color=&#8217;#e63946&#8217;, linewidth=1.8, linestyle=&#8217;--&#8217;,
           label=f&#8217;Mean: {mean_val:.3f}&#8217;)
ax.axvline(median_val, color=&#8217;#6a1b9a&#8217;, linewidth=2.0, linestyle=&#8217;-.&#8217;,
           label=f&#8217;Median: {median_val:.3f}&#8217;)

ax.legend(fontsize=11)
ax.set_xlabel(&#8217;Porosity (fraction)&#8217;, fontsize=12)
ax.set_ylabel(&#8217;Density&#8217;, fontsize=12)
ax.set_title(&#8217;Porosity Distribution&#8217;, fontsize=14)
plt.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!RKt_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf40e64d-8edc-4abd-b910-b265a20c3db1_700x438.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!RKt_!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf40e64d-8edc-4abd-b910-b265a20c3db1_700x438.png 424w, /__u/substackcdn.com/image/fetch/$s_!RKt_!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf40e64d-8edc-4abd-b910-b265a20c3db1_700x438.png 848w, /__u/substackcdn.com/image/fetch/$s_!RKt_!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf40e64d-8edc-4abd-b910-b265a20c3db1_700x438.png 1272w, /__u/substackcdn.com/image/fetch/$s_!RKt_!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf40e64d-8edc-4abd-b910-b265a20c3db1_700x438.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!RKt_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf40e64d-8edc-4abd-b910-b265a20c3db1_700x438.png" width="700" height="438" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/df40e64d-8edc-4abd-b910-b265a20c3db1_700x438.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:438,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!RKt_!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf40e64d-8edc-4abd-b910-b265a20c3db1_700x438.png 424w, /__u/substackcdn.com/image/fetch/$s_!RKt_!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf40e64d-8edc-4abd-b910-b265a20c3db1_700x438.png 848w, /__u/substackcdn.com/image/fetch/$s_!RKt_!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf40e64d-8edc-4abd-b910-b265a20c3db1_700x438.png 1272w, /__u/substackcdn.com/image/fetch/$s_!RKt_!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf40e64d-8edc-4abd-b910-b265a20c3db1_700x438.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For porosity data, knowing the mean and median in the same glance as the distribution shape is practically useful. If the distribution is right-skewed, as porosity data often is, the mean will sit noticeably to the right of the median.</p><h2><strong>Step 5: Clean Up the Visual Noise</strong></h2><p>When creating figures for publication it is often worth reducing unnecessary &#8220;chart junk&#8221;. These are elements of the chart that are not always needed. In this case we can remove the top and right spines to help reduce this clutter and keep the reader&#8217;s focus firmly on the data.</p><pre><code>fig, ax = plt.subplots(figsize=(8, 5))
ax.hist(porosity, bins=40, color=&#8217;steelblue&#8217;, edgecolor=&#8217;white&#8217;,
        linewidth=0.5, density=True, alpha=0.7)

kde = gaussian_kde(porosity)
x_range = np.linspace(porosity.min(), porosity.max(), 300)
ax.plot(x_range, kde(x_range), color=&#8217;#1a3a5c&#8217;, linewidth=2)

mean_val = porosity.mean()
median_val = np.median(porosity)

ax.axvline(mean_val, color=&#8217;#e63946&#8217;, linewidth=1.8, linestyle=&#8217;--&#8217;,
           label=f&#8217;Mean: {mean_val:.3f}&#8217;)
ax.axvline(median_val, color=&#8217;#6a1b9a&#8217;, linewidth=2.0, linestyle=&#8217;-.&#8217;,
           label=f&#8217;Median: {median_val:.3f}&#8217;)

ax.spines[&#8217;top&#8217;].set_visible(False)
ax.spines[&#8217;right&#8217;].set_visible(False)
ax.tick_params(labelsize=11)
ax.legend(fontsize=11)

ax.set_xlabel(&#8217;Porosity (fraction)&#8217;, fontsize=12)
ax.set_ylabel(&#8217;Density&#8217;, fontsize=12)
ax.set_title(&#8217;Porosity Distribution&#8217;, fontsize=14)
plt.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!yYvw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bf5547e-6429-4c8f-bd49-201c92c0bcbe_700x438.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!yYvw!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bf5547e-6429-4c8f-bd49-201c92c0bcbe_700x438.png 424w, /__u/substackcdn.com/image/fetch/$s_!yYvw!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bf5547e-6429-4c8f-bd49-201c92c0bcbe_700x438.png 848w, /__u/substackcdn.com/image/fetch/$s_!yYvw!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bf5547e-6429-4c8f-bd49-201c92c0bcbe_700x438.png 1272w, /__u/substackcdn.com/image/fetch/$s_!yYvw!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bf5547e-6429-4c8f-bd49-201c92c0bcbe_700x438.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!yYvw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bf5547e-6429-4c8f-bd49-201c92c0bcbe_700x438.png" width="700" height="438" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5bf5547e-6429-4c8f-bd49-201c92c0bcbe_700x438.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:438,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!yYvw!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bf5547e-6429-4c8f-bd49-201c92c0bcbe_700x438.png 424w, /__u/substackcdn.com/image/fetch/$s_!yYvw!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bf5547e-6429-4c8f-bd49-201c92c0bcbe_700x438.png 848w, /__u/substackcdn.com/image/fetch/$s_!yYvw!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bf5547e-6429-4c8f-bd49-201c92c0bcbe_700x438.png 1272w, /__u/substackcdn.com/image/fetch/$s_!yYvw!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5bf5547e-6429-4c8f-bd49-201c92c0bcbe_700x438.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is a small change, but it shifts the visual weight onto the data rather than the frame around it.</p><h2><strong>Step 6: Add a Sample Count Annotation</strong></h2><p>One thing a histogram rarely includes is the number of observations behind it. For petrophysical data, where sample counts vary substantially depending on the well and the interval being examined, this matters. A smooth-looking distribution built on 40 samples tells a different story than one built on 4,000.</p><pre><code>ax.annotate(f&#8217;n = {len(porosity):,}&#8217;,
            xy=(0.97, 0.95), xycoords=&#8217;axes fraction&#8217;,
            ha=&#8217;right&#8217;, va=&#8217;top&#8217;, fontsize=11,
            color=&#8217;#555555&#8217;)</code></pre><p>Drop this in before <code>plt.show()</code>.It&#8217;s a small detail, but it gives the reader a better sense of how much data the histogram represents.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!HBkM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fcda25-1a77-423c-990b-c1a81de15453_700x438.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!HBkM!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fcda25-1a77-423c-990b-c1a81de15453_700x438.png 424w, /__u/substackcdn.com/image/fetch/$s_!HBkM!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fcda25-1a77-423c-990b-c1a81de15453_700x438.png 848w, /__u/substackcdn.com/image/fetch/$s_!HBkM!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fcda25-1a77-423c-990b-c1a81de15453_700x438.png 1272w, /__u/substackcdn.com/image/fetch/$s_!HBkM!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fcda25-1a77-423c-990b-c1a81de15453_700x438.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!HBkM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fcda25-1a77-423c-990b-c1a81de15453_700x438.png" width="700" height="438" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/98fcda25-1a77-423c-990b-c1a81de15453_700x438.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:438,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!HBkM!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fcda25-1a77-423c-990b-c1a81de15453_700x438.png 424w, /__u/substackcdn.com/image/fetch/$s_!HBkM!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fcda25-1a77-423c-990b-c1a81de15453_700x438.png 848w, /__u/substackcdn.com/image/fetch/$s_!HBkM!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fcda25-1a77-423c-990b-c1a81de15453_700x438.png 1272w, /__u/substackcdn.com/image/fetch/$s_!HBkM!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98fcda25-1a77-423c-990b-c1a81de15453_700x438.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Step 7: Final Polish</strong></h2><p>A few final changes can help bring everything together: a softer background to reduce contrast slightly, consistent font sizing across the figure, and a more informative title than the generic &#8220;Porosity Distribution&#8221;.</p><p>These are more general styling choices and come down to preference. For example, you may not want to change the background colour, especially if the figure is going into a journal article.</p><pre><code>fig, ax = plt.subplots(figsize=(8, 5))
fig.patch.set_facecolor(&#8217;#fafafa&#8217;)
ax.set_facecolor(&#8217;#fafafa&#8217;)

ax.hist(porosity, bins=40, color=&#8217;steelblue&#8217;, edgecolor=&#8217;white&#8217;,
        linewidth=0.5, density=True, alpha=0.7)

kde = gaussian_kde(porosity)
x_range = np.linspace(porosity.min(), porosity.max(), 300)
ax.plot(x_range, kde(x_range), color=&#8217;#1a3a5c&#8217;, linewidth=2)

mean_val = porosity.mean()
median_val = np.median(porosity)

ax.axvline(mean_val, color=&#8217;#e63946&#8217;, linewidth=1.8, linestyle=&#8217;--&#8217;,
           label=f&#8217;Mean: {mean_val:.3f}&#8217;)
ax.axvline(median_val, color=&#8217;#6a1b9a&#8217;, linewidth=2.0, linestyle=&#8217;-.&#8217;,
           label=f&#8217;Median: {median_val:.3f}&#8217;)

ax.spines[&#8217;top&#8217;].set_visible(False)
ax.spines[&#8217;right&#8217;].set_visible(False)
ax.tick_params(labelsize=11)
ax.legend(fontsize=11, framealpha=0.5)

ax.annotate(f&#8217;n = {len(porosity):,}&#8217;,
            xy=(0.97, 0.95), xycoords=&#8217;axes fraction&#8217;,
            ha=&#8217;right&#8217;, va=&#8217;top&#8217;, fontsize=11, color=&#8217;#555555&#8217;)

ax.set_xlabel(&#8217;Porosity (fraction)&#8217;, fontsize=12)
ax.set_ylabel(&#8217;Density&#8217;, fontsize=12)
ax.set_title(&#8217;Histogram of Simulated Porosity Values&#8217;, fontsize=14)

plt.tight_layout()
plt.savefig(&#8217;porosity_histogram.png&#8217;, dpi=300, bbox_inches=&#8217;tight&#8217;)
plt.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!CVdT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6c70da0-b86b-471f-b102-a3f7dd5eee01_700x438.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!CVdT!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6c70da0-b86b-471f-b102-a3f7dd5eee01_700x438.png 424w, /__u/substackcdn.com/image/fetch/$s_!CVdT!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6c70da0-b86b-471f-b102-a3f7dd5eee01_700x438.png 848w, /__u/substackcdn.com/image/fetch/$s_!CVdT!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6c70da0-b86b-471f-b102-a3f7dd5eee01_700x438.png 1272w, /__u/substackcdn.com/image/fetch/$s_!CVdT!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6c70da0-b86b-471f-b102-a3f7dd5eee01_700x438.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!CVdT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6c70da0-b86b-471f-b102-a3f7dd5eee01_700x438.png" width="700" height="438" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f6c70da0-b86b-471f-b102-a3f7dd5eee01_700x438.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:438,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!CVdT!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6c70da0-b86b-471f-b102-a3f7dd5eee01_700x438.png 424w, /__u/substackcdn.com/image/fetch/$s_!CVdT!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6c70da0-b86b-471f-b102-a3f7dd5eee01_700x438.png 848w, /__u/substackcdn.com/image/fetch/$s_!CVdT!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6c70da0-b86b-471f-b102-a3f7dd5eee01_700x438.png 1272w, /__u/substackcdn.com/image/fetch/$s_!CVdT!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6c70da0-b86b-471f-b102-a3f7dd5eee01_700x438.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/subscribe"><span>Subscribe now</span></a></p><h2><strong>What Changed and Why It Matters</strong></h2><p>The final chart and the starting point contain exactly the same data. What changed is how much of that data is actually readable. A reader can now see the overall distribution shape, the central tendency, whether the mean and median diverge, and how many samples sit behind the histogram, all without any extra explanation.</p><p>Most of these improvements took only a few lines of code. None of them required anything especially advanced. The histogram was always capable of showing this information. The default settings just did not do much to bring it forward.</p><div><hr></div><p><em>A histogram works well when you&#8217;re looking at one distribution at a time. When you need to compare porosity across several formations simultaneously, the overlapping bars and separate panels start to get in the way. In a separate article I look at ridgeline plots, which stack KDE curves vertically so every formation shares the same x-axis and differences in shape, skew, and spread are visible in a single glance.</em></p><p><a href="/__u/andymcdonaldgeo.substack.com/p/ridgeline-plots-in-matplotlib-an">-&gt; Ridgeline Plots in Matplotlib: An Underused Way to Compare Distributions</a></p>]]></content:encoded></item><item><title><![CDATA[When a Jupyter Notebook Is the Wrong Tool: Moving Logic Into Modules and Scripts]]></title><description><![CDATA[Notebooks are great for thinking. They are not always the right place to keep the machinery]]></description><link>https://andymcdonaldgeo.substack.com/p/when-a-jupyter-notebook-is-the-wrong</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/when-a-jupyter-notebook-is-the-wrong</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Sun, 15 Mar 2026 08:30:21 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Phjv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F689817b3-ef01-4e2d-a551-124ece5f20f5_560x378.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Phjv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F689817b3-ef01-4e2d-a551-124ece5f20f5_560x378.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Phjv!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F689817b3-ef01-4e2d-a551-124ece5f20f5_560x378.png 424w, /__u/substackcdn.com/image/fetch/$s_!Phjv!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F689817b3-ef01-4e2d-a551-124ece5f20f5_560x378.png 848w, /__u/substackcdn.com/image/fetch/$s_!Phjv!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F689817b3-ef01-4e2d-a551-124ece5f20f5_560x378.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Phjv!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F689817b3-ef01-4e2d-a551-124ece5f20f5_560x378.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Phjv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F689817b3-ef01-4e2d-a551-124ece5f20f5_560x378.png" width="560" height="378" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/689817b3-ef01-4e2d-a551-124ece5f20f5_560x378.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:378,&quot;width&quot;:560,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!Phjv!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F689817b3-ef01-4e2d-a551-124ece5f20f5_560x378.png 424w, /__u/substackcdn.com/image/fetch/$s_!Phjv!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F689817b3-ef01-4e2d-a551-124ece5f20f5_560x378.png 848w, /__u/substackcdn.com/image/fetch/$s_!Phjv!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F689817b3-ef01-4e2d-a551-124ece5f20f5_560x378.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Phjv!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F689817b3-ef01-4e2d-a551-124ece5f20f5_560x378.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Jupyter Notebooks stick around longer than they should.</p><p>That&#8217;s not really a criticism of the format. It&#8217;s a consequence of how well notebooks work at the start.</p><p>A notebook that started as a forty-cell exploration of some well log data becomes the thing everyone references, then the thing someone tries to run in a pipeline, then the thing nobody can quite reproduce reliably. It doesn&#8217;t go wrong suddenly. It drifts over time.</p><p>The problem isn&#8217;t that notebooks are bad tools. They&#8217;re excellent tools for a specific class of work: exploration, communication, teaching, one-off analysis where the narrative matters as much as the code.</p><p>The issue is that the same properties that make them good at that work can make them awkward for almost everything else.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/subscribe"><span>Subscribe now</span></a></p><h2><strong>What Notebooks Are Actually Good For</strong></h2><p>The cell structure, mixed with explanations and inline outputs, is designed to make it easy to tell a story about a dataset or an analysis, to show your working, and to let someone else follow your reasoning.</p><p>That&#8217;s genuinely useful. If you&#8217;re exploring an unfamiliar dataset, a notebook lets you try things, see results immediately, and build up a picture incrementally. If you&#8217;re presenting findings, a notebook can serve as a written record of an analysis. If you&#8217;re teaching, the step-by-step format provides a great tool for anyone trying to replicate what you did.</p><p>All of those use cases have something in common.</p><p>They&#8217;re all modes where the notebook itself is the deliverable, or where the exploratory, document-like format is an asset rather than a liability. The code and the narrative belong together. You want someone to read both.</p><h2><strong>The Signals That It&#8217;s Time to Move</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!yUNj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d9ca6b-3845-489b-a319-f2108b54c57b_546x285.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!yUNj!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d9ca6b-3845-489b-a319-f2108b54c57b_546x285.png 424w, /__u/substackcdn.com/image/fetch/$s_!yUNj!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d9ca6b-3845-489b-a319-f2108b54c57b_546x285.png 848w, /__u/substackcdn.com/image/fetch/$s_!yUNj!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d9ca6b-3845-489b-a319-f2108b54c57b_546x285.png 1272w, /__u/substackcdn.com/image/fetch/$s_!yUNj!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d9ca6b-3845-489b-a319-f2108b54c57b_546x285.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!yUNj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d9ca6b-3845-489b-a319-f2108b54c57b_546x285.png" width="546" height="285" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a6d9ca6b-3845-489b-a319-f2108b54c57b_546x285.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:285,&quot;width&quot;:546,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!yUNj!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d9ca6b-3845-489b-a319-f2108b54c57b_546x285.png 424w, /__u/substackcdn.com/image/fetch/$s_!yUNj!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d9ca6b-3845-489b-a319-f2108b54c57b_546x285.png 848w, /__u/substackcdn.com/image/fetch/$s_!yUNj!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d9ca6b-3845-489b-a319-f2108b54c57b_546x285.png 1272w, /__u/substackcdn.com/image/fetch/$s_!yUNj!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6d9ca6b-3845-489b-a319-f2108b54c57b_546x285.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">A simple decision guide: stay in the notebook while the work is exploratory, but move logic out once reuse, testing, or automation starts to matter.</figcaption></figure></div><p>Most notebooks do not stay in the exploratory phase forever. At some point the analysis produces something useful: a data-cleaning function, a plotting helper, a QC routine. Then you, or someone else, wants to reuse it somewhere else. That is usually where the cracks begin to appear.</p><p>Here are some of the clearest signs that it is time to move logic out of the notebook and into modules or scripts.</p><p><strong>Copying and pasting.</strong> If you find yourself copying a function from one notebook into another, you&#8217;ve already identified something that should be a module.</p><p>Keeping it in a notebook means every change needs to be replicated wherever you&#8217;ve copied it. That&#8217;s not a minor inconvenience. It&#8217;s how subtle inconsistencies creep in and how debugging becomes genuinely painful: you fix a bug in one place and it persists in three others you&#8217;ve forgotten about.</p><p>A common example is a data-loading or QC helper that starts life in one notebook cell because you only need it once. Then it gets copied into a second notebook, then a third, with small changes each time. Before long, you no longer have one version of the logic. You have several near-identical versions, each with slightly different assumptions, and none of them feels safe to change.</p><p><strong>Execution order.</strong> Notebooks run in whatever order you choose. That flexibility is useful while exploring, but it means a notebook&#8217;s state at any given moment depends on its history.</p><p>Hidden state, variables that exist because of cells you ran earlier, or cells you deleted and re-ran, can produce results that look correct in the moment and break unpredictably later. If you&#8217;ve ever handed someone a notebook that works fine on your machine and had to say &#8220;make sure you run cell 4 before cell 7,&#8221; you&#8217;ve felt this problem. It doesn&#8217;t get easier as the notebook grows.</p><p><strong>Testing.</strong> If a function matters enough that you want to verify it still works after a change, it usually belongs in a <code>.py</code> file where a test suite can reach it directly. You can test notebooks to a degree, but logic buried inside notebook cells is much harder to test systematically, review cleanly, and maintain over time.</p><p><strong>Deployment.</strong> If the notebook is meant to run on a schedule, as part of a pipeline, or triggered by some external process rather than being opened and run manually by a human, you&#8217;re already in script territory.</p><p>Tools do exist to execute notebooks headlessly, and sometimes that&#8217;s the right call for a quick automation. But more often it&#8217;s a signal that the notebook format is being asked to do something it wasn&#8217;t designed for, and that friction tends to compound.</p><p><em>If the signals here feel familiar, the follow-up question is usually where the extracted logic should actually go. I&#8217;ve written a piece on Substack about the folder structure that makes this a straightforward decision on every project. Link at the end.</em></p><h2><strong>What Modules and Scripts Give You</strong></h2><p>Moving logic into modules and scripts doesn&#8217;t mean abandoning notebooks. It means separating two things that are better kept apart: the logic, and the narrative.</p><p>A <strong>module</strong> is a <code>.py</code> file that collects functions, classes, or constants. Once something lives there, any notebook in the project can import it. Change the function once, and every notebook that uses it gets the update. You can write tests against it. You can version-control it cleanly. You can read it in any text editor and immediately see the complete definition of a function without running anything first.</p><p>A <strong>script</strong> is a <code>.py</code> file designed to be run from the command line or triggered by a scheduler. It&#8217;s the right tool when the work is execution rather than exploration: clean this dataset, generate this set of figures, produce this output file. It has no hidden state, no cells to run in the wrong order, no reliance on a kernel that&#8217;s been running since Tuesday.</p><p>The combination isn&#8217;t complicated. Notebooks do exploration and communication. Modules hold reusable logic. Scripts handle execution. Each one does the job it&#8217;s actually suited to.</p><p>What you don&#8217;t want is a notebook that has quietly become the only place certain critical logic lives, that can only be run by opening it in a browser and clicking Run All, and that nobody is entirely sure produces the same results as it did six months ago. That situation is more common than it sounds.</p><h2><strong>Making the Move</strong></h2><p>The goal is not to empty the notebook. It is to leave in place the part notebooks are genuinely good at: exploration, comparison, interpretation, and recording decisions.</p><p>A better notebook is not one that does everything. It is one that keeps the thinking visible and moves the machinery elsewhere.</p><p>The practical question is when to do this. The answer is not &#8220;always from the start&#8221;. That tends to make early exploration cumbersome and over-engineer work that might be thrown away after two hours. Not every function you write in a notebook deserves promotion into the wider project. Some code is just scaffolding for thinking, and that is fine.</p><p>It is also not &#8220;never until everything breaks&#8221;.</p><p>A workable rule of thumb is simple: if you have used a piece of logic in more than one place, extract it. If it needs to run without a human at the keyboard, make it a script. If it matters enough to test, it belongs in a module.</p><p>The goal is not to eliminate notebooks. It is to separate narrative, reusable logic, and execution so each part lives in the right place.</p><p>The refactor is usually simpler than it feels. You move a function out of a notebook cell and into a <code>.py</code> file in your project&#8217;s <code>src/</code> directory, then import it wherever you need it. The notebook becomes simpler. The logic becomes portable and testable. The project becomes easier to hand over.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!LUWq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665dfd27-93ca-4c9f-9d5c-336c09b92be0_554x297.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!LUWq!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665dfd27-93ca-4c9f-9d5c-336c09b92be0_554x297.png 424w, /__u/substackcdn.com/image/fetch/$s_!LUWq!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665dfd27-93ca-4c9f-9d5c-336c09b92be0_554x297.png 848w, /__u/substackcdn.com/image/fetch/$s_!LUWq!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665dfd27-93ca-4c9f-9d5c-336c09b92be0_554x297.png 1272w, /__u/substackcdn.com/image/fetch/$s_!LUWq!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665dfd27-93ca-4c9f-9d5c-336c09b92be0_554x297.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!LUWq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665dfd27-93ca-4c9f-9d5c-336c09b92be0_554x297.png" width="554" height="297" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/665dfd27-93ca-4c9f-9d5c-336c09b92be0_554x297.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:297,&quot;width&quot;:554,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!LUWq!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665dfd27-93ca-4c9f-9d5c-336c09b92be0_554x297.png 424w, /__u/substackcdn.com/image/fetch/$s_!LUWq!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665dfd27-93ca-4c9f-9d5c-336c09b92be0_554x297.png 848w, /__u/substackcdn.com/image/fetch/$s_!LUWq!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665dfd27-93ca-4c9f-9d5c-336c09b92be0_554x297.png 1272w, /__u/substackcdn.com/image/fetch/$s_!LUWq!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F665dfd27-93ca-4c9f-9d5c-336c09b92be0_554x297.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The goal is not to eliminate notebooks. It is to separate narrative, reusable logic, and execution so each part lives in the right place.</figcaption></figure></div><h2><strong>What a Better Structure Looks Like</strong></h2><p>One reason notebook-heavy projects become messy is that there is often no obvious destination for the logic once it outgrows the notebook. Functions stay where they were first written because there is nowhere else that feels natural. That is where a simple project structure helps.</p><p>You do not need anything elaborate. You just need clear separation: notebooks for exploration and explanation, a <code>src/</code> directory for reusable logic, a <code>data/</code> area that distinguishes raw inputs from processed outputs, and a consistent place for figures and reports. Once those boundaries exist, the refactor becomes much easier because the question is no longer whether something should move, but where it belongs.</p><p>A structure like this will not do the refactoring for you, but it removes a lot of hesitation. The notebook can stay focused on the analysis itself, while the logic that needs to be reused, tested, or run elsewhere has a proper home.</p><pre><code>project_name/
&#9500;&#9472;&#9472; notebooks/
&#9474;   &#9500;&#9472;&#9472; 01_exploration.ipynb       # scratch pad
&#9474;   &#9500;&#9472;&#9472; 02_analysis.ipynb          # clean story
&#9474;   &#9492;&#9472;&#9472; archive/                   # old versions
&#9500;&#9472;&#9472; src/
&#9474;   &#9500;&#9472;&#9472; __init__.py
&#9474;   &#9500;&#9472;&#9472; config.py                  # all paths
&#9474;   &#9500;&#9472;&#9472; data_loaders.py            # reusable loaders
&#9474;   &#9492;&#9472;&#9472; plotting.py                # plot styling and saving
&#9500;&#9472;&#9472; data/
&#9474;   &#9500;&#9472;&#9472; raw/                       # never modify these
&#9474;   &#9492;&#9472;&#9472; processed/                 # outputs from notebooks
&#9500;&#9472;&#9472; outputs/
&#9474;   &#9492;&#9472;&#9472; figures/
&#9500;&#9472;&#9472; .gitignore
&#9500;&#9472;&#9472; requirements.txt
&#9500;&#9472;&#9472; CHANGELOG.md
&#9492;&#9472;&#9472; README.md</code></pre><p>The <strong><a href="/__u/andymcdonaldgeo.substack.com/p/the-project-structure-that-makes">project structure article in this series</a></strong> covered this setup in more detail: a <code>src/</code> directory with an <code>__init__.py</code>, a consistent place for reusable helpers, and notebooks that stay lean because the heavy lifting happens in importable code. The structure will not do the refactoring for you, but it removes the question of where things should go when you do.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/subscribe"><span>Subscribe now</span></a></p><h2><strong>The Cost of Waiting</strong></h2><p>Notebooks don&#8217;t become the wrong tool through any single decision. It usually happens through gradual accumulation: one more function added to an already cluttered cell, one more place where the same logic gets copied, one more attempt to turn a piece of exploratory analysis into a repeatable process. The format accommodates all of it until, at some point, it doesn&#8217;t.</p><p>If you recognise the signals early: copy-paste, execution-order fragility, the need for testing, the need for deployment, the move is usually manageable.</p><p>Wait long enough and you&#8217;re not refactoring a notebook. You&#8217;re untangling a system.</p><p><em>Knowing when to move logic out of a notebook is one thing. Having a project structure that makes it obvious where it should go is another. On Substack I&#8217;ve written a guide to the folder layout, </em><code>config.py</code><em> pattern, and reusable </em><code>src/</code><em> modules that turn this from a judgement call into a habit -- including a Cookiecutter template that generates the whole setup in a single command.</em></p><p><a href="/__u/andymcdonaldgeo.substack.com/p/the-project-structure-that-makes-good-notebook-habits-automatic">&#8594; The Project Structure That Makes Good Notebook Habits Automatic</a></p>]]></content:encoded></item><item><title><![CDATA[Ridgeline Plots in Matplotlib: An Underused Way to Compare Distributions]]></title><description><![CDATA[Build stacked KDE plots from scratch and with joypy to show shape, skew, and spread across multiple groups]]></description><link>https://andymcdonaldgeo.substack.com/p/ridgeline-plots-in-matplotlib-an</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/ridgeline-plots-in-matplotlib-an</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Wed, 11 Mar 2026 07:54:08 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!67Ng!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e84711a-be49-4de1-9902-faede5b652d7_700x411.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!67Ng!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e84711a-be49-4de1-9902-faede5b652d7_700x411.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!67Ng!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e84711a-be49-4de1-9902-faede5b652d7_700x411.png 424w, /__u/substackcdn.com/image/fetch/$s_!67Ng!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e84711a-be49-4de1-9902-faede5b652d7_700x411.png 848w, /__u/substackcdn.com/image/fetch/$s_!67Ng!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e84711a-be49-4de1-9902-faede5b652d7_700x411.png 1272w, /__u/substackcdn.com/image/fetch/$s_!67Ng!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e84711a-be49-4de1-9902-faede5b652d7_700x411.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!67Ng!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e84711a-be49-4de1-9902-faede5b652d7_700x411.png" width="700" height="411" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0e84711a-be49-4de1-9902-faede5b652d7_700x411.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:411,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!67Ng!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e84711a-be49-4de1-9902-faede5b652d7_700x411.png 424w, /__u/substackcdn.com/image/fetch/$s_!67Ng!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e84711a-be49-4de1-9902-faede5b652d7_700x411.png 848w, /__u/substackcdn.com/image/fetch/$s_!67Ng!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e84711a-be49-4de1-9902-faede5b652d7_700x411.png 1272w, /__u/substackcdn.com/image/fetch/$s_!67Ng!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e84711a-be49-4de1-9902-faede5b652d7_700x411.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>When you need to compare distributions across more than four or five groups, many chart types become harder to read effectively. Faceted histograms force the eye to jump between panels, box plots reduce each group to a compact summary, and violin plots can start to feel crowded as the number of groups increases.</p><p>A ridgeline plot solves this by stacking KDE curves vertically, one per group, with each row slightly offset from the one below. Every distribution shares the same x-axis. You can scan across all groups at once and comparisons between adjacent rows are immediate.</p><p>In this article, we will build ridgeline plots two ways: first from scratch in Matplotlib, then with joypy for a quicker route.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/andymcdonaldgeo.substack.com/subscribe"><span>Subscribe now</span></a></p><h2><strong>Setting Up the Dataset</strong></h2><p>We will use synthetic well log data representing porosity distributions across seven geological formations.</p><pre><code>import matplotlib.pyplot as plt
import matplotlib.cm as cm
import pandas as pd
import numpy as np
from scipy.stats import gaussian_kde

np.random.seed(99)
formations = {
    &#8216;Vardan&#8217;:   np.random.normal(0.32, 0.04, 120),
    &#8216;Kelvik&#8217;:   np.random.normal(0.22, 0.05, 150),
    &#8216;Stenby&#8217;:   np.random.normal(0.18, 0.04, 130),
    &#8216;Morvik&#8217;:   np.random.normal(0.14, 0.03, 100),
    &#8216;Halvard&#8217;:  np.random.beta(2, 8, 90) * 0.5,    # Skewed - mixed facies
    &#8216;Torven&#8217;:   np.random.normal(0.20, 0.06, 140),
    &#8216;Elgfjord&#8217;: np.random.normal(0.16, 0.03, 110),
}
# Clip to realistic porosity range
formations = {k: np.clip(v, 0, 0.5) for k, v in formations.items()}</code></pre><p>The Halvard formation uses a beta distribution to simulate a right-skewed dataset. This is a synthetic representation of a shale-dominated interval in which lower porosity values dominate, with a smaller number of higher-porosity interbeds.</p><h2><strong>Step 1: The Default: Faceted Histograms</strong></h2><p>First, we will start with separate histograms for each formation. This keeps the distributions isolated and avoids overlap, but allows us to use the same axes.</p><pre><code>n = len(formations)
fig, axes = plt.subplots(n, 1, figsize=(8, 10))

for ax, (name, values) in zip(axes, formations.items()):
    ax.hist(values, bins=20)
    ax.set_ylabel(name)
plt.tight_layout()
plt.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!WGQw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f2194b5-8171-494c-abb7-78e7a7033142_700x878.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!WGQw!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f2194b5-8171-494c-abb7-78e7a7033142_700x878.png 424w, /__u/substackcdn.com/image/fetch/$s_!WGQw!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f2194b5-8171-494c-abb7-78e7a7033142_700x878.png 848w, /__u/substackcdn.com/image/fetch/$s_!WGQw!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f2194b5-8171-494c-abb7-78e7a7033142_700x878.png 1272w, /__u/substackcdn.com/image/fetch/$s_!WGQw!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f2194b5-8171-494c-abb7-78e7a7033142_700x878.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!WGQw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f2194b5-8171-494c-abb7-78e7a7033142_700x878.png" width="700" height="878" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2f2194b5-8171-494c-abb7-78e7a7033142_700x878.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:878,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!WGQw!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f2194b5-8171-494c-abb7-78e7a7033142_700x878.png 424w, /__u/substackcdn.com/image/fetch/$s_!WGQw!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f2194b5-8171-494c-abb7-78e7a7033142_700x878.png 848w, /__u/substackcdn.com/image/fetch/$s_!WGQw!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f2194b5-8171-494c-abb7-78e7a7033142_700x878.png 1272w, /__u/substackcdn.com/image/fetch/$s_!WGQw!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f2194b5-8171-494c-abb7-78e7a7033142_700x878.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This works well enough as a first pass. You can see the rough centre and spread of each formation, and any obviously unusual group will still stand out as well as any outliers.</p><p>The problem is that comparison becomes hard as soon as you want to judge the distributions against one another. Your eye has to move up and down the full figure to compare one formation with the next. Shape is also harder to read once the histograms are separated into individual panels, especially if you are looking for subtler differences such as skew or multi-modality.</p><p>That is the point where a ridgeline plot becomes useful. It keeps the same one-group-per-row structure, but turns the figure into something you can scan much more quickly.</p><h2><strong>Step 2: Building the Ridgeline From Scratch</strong></h2><p>The ridgeline version keeps the same basic idea as the faceted histograms: one distribution per formation, stacked vertically. The difference is that instead of showing counts in bins, it uses a smooth density curve for each group and offsets each one slightly above the next.</p><p>That does two useful things. First, it makes the overall shape of each distribution easier to read. Second, because every curve shares the same horizontal axis, it becomes much easier to compare neighbouring formations at a glance.</p><p>The mechanics are straightforward. For each formation, we estimate a KDE over a fixed x-range, then scale it so the peak height is comparable across groups, and then add a vertical offset before filling the area under the curve.</p><pre><code>formations_list = list(formations.keys())
n_groups = len(formations_list)
overlap = 0.8

fig, ax = plt.subplots(figsize=(10, 8))
x_range = np.linspace(0, 0.5, 300)
colours = cm.Blues(np.linspace(0.35, 0.85, n_groups))

for i, (name, colour) in enumerate(zip(formations_list, colours)):
    kde = gaussian_kde(formations[name], bw_method=0.25)
    y = kde(x_range)
    y_norm = y / y.max()
    offset = (n_groups - 1 - i) * overlap
    ax.fill_between(x_range, offset, y_norm + offset,
                    alpha=0.8, color=colour, zorder=i)
    ax.plot(x_range, y_norm + offset,
            color=&#8217;white&#8217;, linewidth=1, zorder=i)
    ax.text(-0.01, offset + 0.05, name,
            ha=&#8217;right&#8217;, va=&#8217;bottom&#8217;,
            fontsize=9, fontweight=&#8217;bold&#8217;,
            fontfamily=&#8217;monospace&#8217;, color=&#8217;#2c3e50&#8217;)
plt.show()</code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!sK7x!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bf6d593-aa0a-4092-bba4-ba78b5890f2d_700x560.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!sK7x!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bf6d593-aa0a-4092-bba4-ba78b5890f2d_700x560.png 424w, /__u/substackcdn.com/image/fetch/$s_!sK7x!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bf6d593-aa0a-4092-bba4-ba78b5890f2d_700x560.png 848w, /__u/substackcdn.com/image/fetch/$s_!sK7x!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bf6d593-aa0a-4092-bba4-ba78b5890f2d_700x560.png 1272w, /__u/substackcdn.com/image/fetch/$s_!sK7x!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bf6d593-aa0a-4092-bba4-ba78b5890f2d_700x560.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!sK7x!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bf6d593-aa0a-4092-bba4-ba78b5890f2d_700x560.png" width="700" height="560" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7bf6d593-aa0a-4092-bba4-ba78b5890f2d_700x560.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:560,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!sK7x!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bf6d593-aa0a-4092-bba4-ba78b5890f2d_700x560.png 424w, /__u/substackcdn.com/image/fetch/$s_!sK7x!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bf6d593-aa0a-4092-bba4-ba78b5890f2d_700x560.png 848w, /__u/substackcdn.com/image/fetch/$s_!sK7x!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bf6d593-aa0a-4092-bba4-ba78b5890f2d_700x560.png 1272w, /__u/substackcdn.com/image/fetch/$s_!sK7x!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bf6d593-aa0a-4092-bba4-ba78b5890f2d_700x560.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The <code>overlap</code> value controls how tightly the ridges sit together. Push it higher and the curves begin to visually blend into one another. Pull it back and the figure starts to behave more like a set of separated mini-plots. Somewhere in the middle usually works best.</p><p>Normalising each KDE is also worth noting. Without that step, formations with different sample sizes or density peaks can dominate the figure visually. Normalisation shifts the emphasis away from absolute density height and onto comparative shape, which is usually the more useful story in this kind of plot.</p><p><em>Ridgeline plots are one of three underused chart types I think every Python data practitioner should know. I&#8217;ve covered all three over on Substack, including when to reach for each one. Link at the end.</em></p><h2><strong>Step 3: Choosing the Right Bandwidth</strong></h2><p>The default KDE bandwidth often over-smooths the data. This matters when you want to see real structure, bimodality, clustering, or a skewed tail.</p><pre><code># Default bandwidth - tends to over-smooth
kde_default = gaussian_kde(formations[&#8217;Halvard&#8217;])

# Narrower bandwidth - preserves more structure
kde_narrow = gaussian_kde(formations[&#8217;Halvard&#8217;], bw_method=0.2)</code></pre><p>For the Halvard formation, the default bandwidth would flatten the right skew. A value of 0.2 to 0.3 preserves it.</p><h2><strong>Step 4: Consistent X-Axis Range</strong></h2><p>Every group must share the same x-axis range. If you allow each KDE to choose its own range automatically, the shapes may look comparable even when they are not. One formation might appear broader or more tightly clustered simply because it has been plotted over a different span of values.</p><p>That breaks the main advantage of a ridgeline plot, which is side-by-side comparison on a common scale.</p><p>In this example, porosity is clipped between 0 and 0.5, so the KDE should also be evaluated over that same fixed interval:</p><pre><code>x_range = np.linspace(0, 0.5, 300)  # Fixed range across all formations</code></pre><p>Defining the range once and reusing it for every formation keeps the visual comparison honest. The peak positions, the spread, and the tails all sit on the same reference frame.</p><p>This is especially important when some groups have narrower distributions than others. If each curve is allowed to autoscale, a tight formation and a broad formation can end up looking more similar than they really are. Fixing the x-axis removes that distortion and lets the differences in shape speak for themselves.</p><h2><strong>Step 5: Final Polish</strong></h2><p>Once the structure of the plot is working, the last step is presentation. Ridgeline plots can become visually noisy quite quickly, especially when several curves overlap. A few small design choices make a big difference to readability.</p><p>First, the filled areas are given a slightly transparent blue gradient so the stacked layers remain distinct without feeling heavy. The white outline on each curve helps separate one ridge from the next, particularly where neighbouring distributions overlap.</p><p>The formation labels are placed directly beside each ridge rather than in a legend. This makes the chart easier to scan because the reader does not have to keep jumping back and forth between the figure and a separate key. Removing the y-axis ticks also helps, since the labels already tell us what each row represents.</p><p>Finally, most of the spines are stripped away so the emphasis stays on the distributions themselves. The only axis that really matters here is the shared porosity scale along the bottom.</p><pre><code>fig, ax = plt.subplots(figsize=(10, 8), facecolor=&#8217;white&#8217;)

x_range = np.linspace(0, 0.5, 300)
colours = cm.Blues(np.linspace(0.35, 0.85, n_groups))
overlap = 0.8

for i, (name, colour) in enumerate(zip(formations_list, colours)):
    kde = gaussian_kde(formations[name], bw_method=0.25)
    y = kde(x_range)
    y_norm = y / y.max()
    offset = (n_groups - 1 - i) * overlap
    ax.fill_between(x_range, offset, y_norm + offset,
                    alpha=0.85, color=colour, zorder=i)
    ax.plot(x_range, y_norm + offset,
            color=&#8217;white&#8217;, linewidth=1.2, zorder=i)
    ax.text(-0.005, offset + 0.06, name,
            ha=&#8217;right&#8217;, va=&#8217;bottom&#8217;,
            fontsize=9, fontweight=&#8217;bold&#8217;,
            fontfamily=&#8217;monospace&#8217;, color=&#8217;#2c3e50&#8217;)

ax.set_xlabel(&#8217;Porosity (v/v)&#8217;, fontsize=12,
              fontweight=&#8217;bold&#8217;, labelpad=10)
ax.set_title(&#8217;Porosity Distribution by Formation&#8217;,
             fontsize=14, fontweight=&#8217;bold&#8217;, pad=15)
ax.set_xlim(0, 0.5)
ax.set_yticks([])
for spine in [&#8217;top&#8217;, &#8216;right&#8217;, &#8216;left&#8217;]:
    ax.spines[spine].set_visible(False)
ax.spines[&#8217;bottom&#8217;].set_visible(True)
ax.tick_params(axis=&#8217;x&#8217;, labelsize=10)
plt.tight_layout()
plt.savefig(&#8217;ridgeline_porosity.png&#8217;, dpi=150,
            bbox_inches=&#8217;tight&#8217;, facecolor=&#8217;white&#8217;)
plt.show()</code></pre><p>The result is a chart that still carries a fair amount of information, but feels much cleaner and easier to read than the default version.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Pj8m!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b8e0c45-533b-4cab-8fca-2e73629be682_700x556.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Pj8m!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b8e0c45-533b-4cab-8fca-2e73629be682_700x556.png 424w, /__u/substackcdn.com/image/fetch/$s_!Pj8m!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b8e0c45-533b-4cab-8fca-2e73629be682_700x556.png 848w, /__u/substackcdn.com/image/fetch/$s_!Pj8m!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b8e0c45-533b-4cab-8fca-2e73629be682_700x556.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Pj8m!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b8e0c45-533b-4cab-8fca-2e73629be682_700x556.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Pj8m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b8e0c45-533b-4cab-8fca-2e73629be682_700x556.png" width="700" height="556" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1b8e0c45-533b-4cab-8fca-2e73629be682_700x556.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:556,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!Pj8m!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b8e0c45-533b-4cab-8fca-2e73629be682_700x556.png 424w, /__u/substackcdn.com/image/fetch/$s_!Pj8m!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b8e0c45-533b-4cab-8fca-2e73629be682_700x556.png 848w, /__u/substackcdn.com/image/fetch/$s_!Pj8m!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b8e0c45-533b-4cab-8fca-2e73629be682_700x556.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Pj8m!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b8e0c45-533b-4cab-8fca-2e73629be682_700x556.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>What the Ridgeline Reveals</strong></h2><p>The Halvard formation immediately stands out. Where every other formation has a roughly symmetric bell curve, Halvard has a right-skewed distribution with a long tail towards higher porosity values. In a box plot this would appear as a positively skewed distribution. In the ridgeline plot you can see exactly where that tail sits and how it relates to the main cluster.</p><p>Its also easier to see multi-modal distributions for each distribution compared to the earlier multi-histogram plot.</p><p>None of this requires reading exact values. The shapes communicate the story directly.</p><h2><strong>Using joypy</strong></h2><p>If you want the ridgeline effect without building each layer manually, <code>joypy</code> gives you a much quicker route. It wraps the KDE calculation, stacking, and offsetting into a single function call, which makes it useful for exploratory work or for getting to a first draft quickly.</p><p>To use it, reshape the data into a simple long-form dataframe with one column for the group labels and another for the values being plotted:</p><pre><code>import joypy

records = []
for name, values in formations.items():
    for v in values:
        records.append({&#8217;Formation&#8217;: name, &#8216;Porosity&#8217;: v})
df = pd.DataFrame(records)

fig, axes = joypy.joyplot(
    df,
    by=&#8217;Formation&#8217;,
    column=&#8217;Porosity&#8217;,
    figsize=(10, 7),
    colormap=cm.Blues,
    alpha=0.8,
    linecolor=&#8217;white&#8217;,
    linewidth=1.2,
    title=&#8217;Porosity Distribution by Formation&#8217;,
    x_range=[0, 0.5]
)</code></pre><p>That is enough to get a readable ridgeline plot with far less code.</p><p>The trade-off is control. A library approach is convenient, but the from-scratch version gives you much more say over bandwidth choice, label placement, colour handling, spacing, and the overall composition of the figure. If you are exploring ideas, <code>joypy</code> is handy. If you are aiming for a chart you would actually publish, the manual version is usually easier to tune properly.</p><h2><strong>When to Use a Ridgeline Plot</strong></h2><p>Ridgeline plots work best when:</p><ul><li><p>You have 4 to 20 groups. Fewer and a simple overlapping KDE or violin plot is cleaner. More and the labels become unreadable.</p></li><li><p>Each group has at least 50 observations. KDE is unreliable on smaller samples.</p></li><li><p>The x-axis is continuous and shared across all groups.</p></li><li><p>You want to show shape, skewness, bimodality, spread rather than just summary statistics.</p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><h2><strong>Full Code</strong></h2><pre><code>import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
from scipy.stats import gaussian_kde

np.random.seed(99)
formations = {
    &#8216;Vardan&#8217;:   np.random.normal(0.32, 0.04, 120),
    &#8216;Kelvik&#8217;:   np.random.normal(0.22, 0.05, 150),
    &#8216;Stenby&#8217;:   np.random.normal(0.18, 0.04, 130),
    &#8216;Morvik&#8217;:   np.random.normal(0.14, 0.03, 100),
    &#8216;Halvard&#8217;:  np.random.beta(2, 8, 90) * 0.5,
    &#8216;Torven&#8217;:   np.random.normal(0.20, 0.06, 140),
    &#8216;Elgfjord&#8217;: np.random.normal(0.16, 0.03, 110),
}
formations = {k: np.clip(v, 0, 0.5) for k, v in formations.items()}
formations_list = list(formations.keys())
n_groups = len(formations_list)
overlap = 0.8

fig, ax = plt.subplots(figsize=(10, 8), facecolor=&#8217;white&#8217;)
x_range = np.linspace(0, 0.5, 300)
colours = cm.Blues(np.linspace(0.35, 0.85, n_groups))

for i, (name, colour) in enumerate(zip(formations_list, colours)):
    kde = gaussian_kde(formations[name], bw_method=0.25)
    y = kde(x_range)
    y_norm = y / y.max()
    offset = (n_groups - 1 - i) * overlap
    ax.fill_between(x_range, offset, y_norm + offset,
                    alpha=0.85, color=colour, zorder=i)
    ax.plot(x_range, y_norm + offset,
            color=&#8217;white&#8217;, linewidth=1.2, zorder=i)
    ax.text(-0.005, offset + 0.06, name,
            ha=&#8217;right&#8217;, va=&#8217;bottom&#8217;,
            fontsize=9, fontweight=&#8217;bold&#8217;,
            fontfamily=&#8217;monospace&#8217;, color=&#8217;#2c3e50&#8217;)

ax.set_xlabel(&#8217;Porosity (v/v)&#8217;, fontsize=12, fontweight=&#8217;bold&#8217;, labelpad=10)
ax.set_title(&#8217;Porosity Distribution by Formation&#8217;,
             fontsize=14, fontweight=&#8217;bold&#8217;, pad=15)
ax.set_xlim(0, 0.5)
ax.set_yticks([])
for spine in [&#8217;top&#8217;, &#8216;right&#8217;, &#8216;left&#8217;]:
    ax.spines[spine].set_visible(False)
ax.spines[&#8217;bottom&#8217;].set_visible(True)
ax.tick_params(axis=&#8217;x&#8217;, labelsize=10)
plt.tight_layout()
plt.savefig(&#8217;ridgeline_porosity.png&#8217;, dpi=150,
            bbox_inches=&#8217;tight&#8217;, facecolor=&#8217;white&#8217;)
plt.show()</code></pre><div><hr></div><p><em>Building a ridgeline plot from scratch gives you full control over the output, but it also raises an obvious question: what other chart types are worth adding to your toolkit? Ridgeline plots sit alongside bump charts and beeswarm plots as visualisation types that most people never reach for, even when they would tell the story far better than a histogram or a box plot. Over on Substack I&#8217;ve put together a piece covering all three, with before-and-after examples using well log data.</em></p><p><em><strong><a href="/__u/andymcdonaldgeo.substack.com/p/3-python-charts-youre-probably-not">&#8594; 3 Python Charts You&#8217;re Probably Not Using (But Should Be)</a></strong></em></p>]]></content:encoded></item><item><title><![CDATA[The Project Structure That Makes Good Jupyter Notebook Habits Automatic]]></title><description><![CDATA[Set it up once. Use it on every project.]]></description><link>https://andymcdonaldgeo.substack.com/p/the-project-structure-that-makes</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/the-project-structure-that-makes</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Sun, 08 Mar 2026 10:20:55 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!L-bb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F559a13a3-c574-4f3d-a315-baf4cc1b6d84_700x475.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!L-bb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F559a13a3-c574-4f3d-a315-baf4cc1b6d84_700x475.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!L-bb!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F559a13a3-c574-4f3d-a315-baf4cc1b6d84_700x475.png 424w, /__u/substackcdn.com/image/fetch/$s_!L-bb!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F559a13a3-c574-4f3d-a315-baf4cc1b6d84_700x475.png 848w, /__u/substackcdn.com/image/fetch/$s_!L-bb!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F559a13a3-c574-4f3d-a315-baf4cc1b6d84_700x475.png 1272w, /__u/substackcdn.com/image/fetch/$s_!L-bb!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F559a13a3-c574-4f3d-a315-baf4cc1b6d84_700x475.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!L-bb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F559a13a3-c574-4f3d-a315-baf4cc1b6d84_700x475.png" width="700" height="475" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/559a13a3-c574-4f3d-a315-baf4cc1b6d84_700x475.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:475,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!L-bb!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F559a13a3-c574-4f3d-a315-baf4cc1b6d84_700x475.png 424w, /__u/substackcdn.com/image/fetch/$s_!L-bb!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F559a13a3-c574-4f3d-a315-baf4cc1b6d84_700x475.png 848w, /__u/substackcdn.com/image/fetch/$s_!L-bb!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F559a13a3-c574-4f3d-a315-baf4cc1b6d84_700x475.png 1272w, /__u/substackcdn.com/image/fetch/$s_!L-bb!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F559a13a3-c574-4f3d-a315-baf4cc1b6d84_700x475.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong><a href="https://medium.com/p/5b396b9cadda">Part one of this series covered six habits:</a></strong> treating the notebook as a decision log rather than a script, writing a header cell before any code runs, separating exploration from the record, naming variables so they carry meaning across stages, the kernel restart test before closing, and the cold open test to check whether the notebook stands on its own without you.</p><p>Those habits make notebooks much easier to return to, hand over, or audit months later.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>But habits without structure only get you so far. Every project still starts from scratch. You still spend a few minutes creating folders. You still copy an imports cell from an old notebook. You still hardcode a file path and then wonder why it breaks on a different machine or a colleague&#8217;s laptop.</p><p>This article is about the structure that makes those habits automatic. A folder layout that stays consistent across every project. A <code>config.py</code> that removes fragile relative paths. And a Cookiecutter template that generates everything in one command, including notebooks that open with your header and imports already in place.</p><p>The example focuses on geoscience project, but the structure applies to any Python data project.</p><h2><strong>The Folder Structure</strong></h2><p>Start every project with the same layout. The exact folder names matter less than the consistency. When every project looks the same, you stop thinking about where things live and start thinking about the work.</p><pre><code>project_name/
&#9500;&#9472;&#9472; notebooks/
&#9474;   &#9500;&#9472;&#9472; 01_exploration.ipynb       # scratch pad
&#9474;   &#9500;&#9472;&#9472; 02_analysis.ipynb          # clean story
&#9474;   &#9492;&#9472;&#9472; archive/                   # old versions
&#9500;&#9472;&#9472; src/
&#9474;   &#9500;&#9472;&#9472; __init__.py
&#9474;   &#9500;&#9472;&#9472; config.py                  # all paths
&#9474;   &#9500;&#9472;&#9472; data_loaders.py            # reusable loaders
&#9474;   &#9492;&#9472;&#9472; plotting.py                # plot styling and saving
&#9500;&#9472;&#9472; data/
&#9474;   &#9500;&#9472;&#9472; raw/                       # never modify these
&#9474;   &#9492;&#9472;&#9472; processed/                 # outputs from notebooks
&#9500;&#9472;&#9472; outputs/
&#9474;   &#9492;&#9472;&#9472; figures/
&#9500;&#9472;&#9472; .gitignore
&#9500;&#9472;&#9472; requirements.txt
&#9500;&#9472;&#9472; CHANGELOG.md
&#9492;&#9472;&#9472; README.md</code></pre><p>Three rules make this structure work.</p><p><code>data/raw/</code><strong> is read only.</strong></p><p>Notebooks read from it, but never write to it. That means you can rerun the entire project from scratch without worrying about overwriting source data.</p><p><strong>All outputs stay with the project.</strong></p><p>Processed data goes to <code>data/processed/</code>. Figures go to <code>outputs/figures/</code>. Nothing ends up scattered across a desktop or Downloads folder.</p><p><code>src/</code><strong> is for reusable code.</strong></p><p>If you have copied something between notebooks more than once, it belongs here. This is especially useful if you have re-usable code functions that you have developed over time, and don&#8217;t want to rewrite from scratch each time. You can place these functions in here. Who knows, eventually these reusable functions may turn into well developed apps in the future!</p><h2><strong>The Quick Profile</strong></h2><p>Not every task needs the full structure shown above. Sometimes you just want to answer a quick question, check something in a dataset, or run a one-off calculation.</p><p>For those sessions a stripped-down layout is more appropriate:</p><pre><code>project_name/
&#9500;&#9472;&#9472; notebooks/
&#9474;   &#9492;&#9472;&#9472; 01_exploration.ipynb
&#9500;&#9472;&#9472; data/
&#9474;   &#9492;&#9472;&#9472; raw/
&#9500;&#9472;&#9472; outputs/
&#9492;&#9472;&#9472; README.md</code></pre><p>No <code>src/</code>, no <code>CHANGELOG</code>, no archive. One notebook and minimal overhead.</p><p>You can always promote a quick project to a full one later if the work grows. Retrofitting structure onto a project that has already started and sprawled is much harder.</p><p>Both layouts can be generated automatically from the same Cookiecutter template (which we will cover later). When creating a new project using Cookiecutter you simply choose between a full or quick profile.</p><h2><strong>config.py: One File for All Your Paths</strong></h2><p>Hardcoded file paths are one of the most common reasons notebooks break. You write something like <code>../../data/raw/volve.csv</code>, move the notebook to another folder, and suddenly nothing runs.</p><p>A simple fix is a central <code>config.py</code> in <code>src/</code> that defines paths once for the entire project.</p><pre><code># src/config.py
from pathlib import Path

PROJECT_ROOT = Path(__file__).parent.parent
LOCAL_RAW    = PROJECT_ROOT / &#8220;data&#8221; / &#8220;raw&#8221;
PROCESSED    = PROJECT_ROOT / &#8220;data&#8221; / &#8220;processed&#8221;
FIGURES      = PROJECT_ROOT / &#8220;outputs&#8221; / &#8220;figures&#8221;
CENTRAL_DATA = Path.home() / &#8220;geoscience_data&#8221;

def find_data(filename: str) -&gt; Path:
    local = LOCAL_RAW / filename
    if local.exists():
        return local
    central = CENTRAL_DATA / filename
    if central.exists():
        return central
    raise FileNotFoundError(
        f&#8221;{filename} not found in:\\n  {LOCAL_RAW}\\n  {CENTRAL_DATA}&#8221;
    )</code></pre><p>This solves two problems that appear frequently in geoscience work.</p><ol><li><p>Many teams maintain large reference datasets that get reused across projects: the Volve dataset, FORCE well logs, shared LAS libraries. Duplicating these inside every project folder wastes space and often leads to version drift.</p></li><li><p>It makes project handovers easier. If a colleague runs the notebook and the data is missing, <code>find_data()</code> raises a clear error showing exactly where the notebook expected to find it.</p></li></ol><p>In a notebook the usage becomes simple:</p><pre><code>import sys
sys.path.append(&#8217;../src&#8217;)

from config import find_data, PROCESSED, FIGURES
well_data = pd.read_csv(find_data(&#8221;volve_well_logs.csv&#8221;))
well_data.to_csv(PROCESSED / &#8220;well_data_clean.csv&#8221;, index=False)</code></pre><p>The <code>sys.path.append('../src')</code> line assumes the notebook sits inside the <code>notebooks/</code> folder. If you reorganise the project structure you may need to adjust this path.</p><p>No fragile relative paths scattered through the notebook.</p><h2><strong>src/plotting.py: Consistent Styles and Automatic Figure Naming</strong></h2><p>Two small annoyances appear on almost every project: inconsistent plot styling across notebooks, and figure files named <code>figure1.png</code> that become impossible to identify later.</p><p>Both issues can be solved with a small helper module called <code>plotting.py</code>. In here you can define the key style for your figures, and modify it later to suit your changing needs. Perhaps you change preference from light mode to dark mode, and need to make all your output figures with a dark background.</p><pre><code># src/plotting.py
import matplotlib.pyplot as plt
import datetime
from config import FIGURES

plt.rcParams.update({
    &#8216;figure.figsize&#8217;: (10, 6),
    &#8216;axes.spines.top&#8217;: False,
    &#8216;axes.spines.right&#8217;: False,
    &#8216;font.family&#8217;: &#8216;Arial&#8217;,
    &#8216;axes.titlesize&#8217;: 14,
    &#8216;axes.labelsize&#8217;: 12,
})

def save_fig(fig, name: str, dpi: int = 150):
    date = datetime.date.today().strftime(&#8221;%Y%m%d&#8221;)
    filepath = FIGURES / f&#8221;{date}_{name}.png&#8221;
    fig.savefig(filepath, dpi=dpi, bbox_inches=&#8217;tight&#8217;)
    print(f&#8221;Saved: {filepath}&#8221;)</code></pre><p>In notebooks, you would call the following:</p><pre><code>from plotting import save_fig

fig, ax = plt.subplots()
ax.plot(df[&#8217;DEPT&#8217;], df[&#8217;GR&#8217;])
save_fig(fig, &#8216;gr_curve_well_15_9_F1&#8217;)
# -&gt; Saved: outputs/figures/20260226_gr_curve_well_15_9_F1.png</code></pre><p>Every figure is automatically dated and saved into the project structure. The figures folder becomes a useful record rather than a mystery archive of unnamed files.</p><h2><strong>CHANGELOG.md and the Archive Convention</strong></h2><p>Two small additions that take very little effort but often prove useful later.</p><p><strong>CHANGELOG.md</strong> records important changes made during the project. It does not replace git history. It simply captures decisions that might otherwise be forgotten.</p><pre><code>## 2026-02-26
- Switched GR normalisation from standard scaler to min-max
- Added formation depth filter

## 2026-02-10
- Initial analysis complete
- Found three wells with incomplete NPHI data</code></pre><p>Writing a quick entry at the end of a session often saves a lot of archaeology later. And if you are stuck for time, you could always use an LLM to pull the git history for the latest changes and write the changelog entry for you.</p><p>The archive convention works the same way. Before restructuring a notebook, move the previous version into <code>notebooks/archive/</code> with a date prefix. The archive folder stays readable instead of becoming a dumping ground.</p><h2><strong>The Cookiecutter Template</strong></h2><p>Once you have the structure setup the way you need it to work, the final step is to automate its creation.</p><p>This can be done with <a href="https://www.cookiecutter.io/">Cookiecutter</a>. It is a great tool for quickly starting new projects without having to recreate the structure every time.</p><p>Cookiecutter generates a complete project structure from a template with a single command. You configure the template once and reuse it for every new project.</p><p>And if you find yourself missing functionality or extra structure in your cookiecutter template, you can always expand it and apply it to any new projects.</p><h3><strong>Installing Cookiecutter</strong></h3><p>To install cookiecutter, open a terminal and type:</p><pre><code>pip install cookiecutter</code></pre><h3><strong>Cookiecutter Template structure</strong></h3><p>Create a template folder somewhere permanent on your machine. Keep it outside your project directories since this is a reusable tool.</p><pre><code>geoscience-project-template/
&#9500;&#9472;&#9472; cookiecutter.json
&#9500;&#9472;&#9472; hooks/
&#9474;   &#9492;&#9472;&#9472; post_gen_project.py
&#9492;&#9472;&#9472; {{cookiecutter.project_slug}}/
    &#9500;&#9472;&#9472; notebooks/
    &#9474;   &#9500;&#9472;&#9472; 01_exploration.ipynb
    &#9474;   &#9500;&#9472;&#9472; 02_analysis.ipynb
    &#9474;   &#9492;&#9472;&#9472; archive/
    &#9474;       &#9492;&#9472;&#9472; .gitkeep
    &#9500;&#9472;&#9472; src/
    &#9474;   &#9500;&#9472;&#9472; __init__.py
    &#9474;   &#9500;&#9472;&#9472; config.py
    &#9474;   &#9500;&#9472;&#9472; data_loaders.py
    &#9474;   &#9492;&#9472;&#9472; plotting.py
    &#9500;&#9472;&#9472; data/
    &#9474;   &#9500;&#9472;&#9472; raw/
    &#9474;   &#9474;   &#9492;&#9472;&#9472; .gitkeep
    &#9474;   &#9492;&#9472;&#9472; processed/
    &#9474;       &#9492;&#9472;&#9472; .gitkeep
    &#9500;&#9472;&#9472; outputs/
    &#9474;   &#9492;&#9472;&#9472; figures/
    &#9474;       &#9492;&#9472;&#9472; .gitkeep
    &#9500;&#9472;&#9472; .gitignore
    &#9500;&#9472;&#9472; requirements.txt
    &#9500;&#9472;&#9472; CHANGELOG.md
    &#9492;&#9472;&#9472; README.md</code></pre><p>One thing I have found helpful, is asking Copilot/Claude to create the above structure for you.</p><h3><strong>cookiecutter.json</strong></h3><p>The <code>cookiecutter.json</code> file defines the prompts that appear when generating a project from the command line / terminal.</p><pre><code>{
  &#8220;project_name&#8221;: &#8220;My Geoscience Project&#8221;,
  &#8220;project_slug&#8221;: &#8220;{{ cookiecutter.project_name.lower().replace(&#8217; &#8216;, &#8216;_&#8217;) }}&#8221;,
  &#8220;author&#8221;: &#8220;Andy McDonald&#8221;,
  &#8220;description&#8221;: &#8220;A short description of the project&#8221;,
  &#8220;python_version&#8221;: &#8220;3.11&#8221;,
  &#8220;profile&#8221;: [&#8221;full&#8221;, &#8220;quick&#8221;]
}</code></pre><h3><strong>The post-generation hook</strong></h3><p>After Cookiecutter creates the project, this small script runs automatically after. It removes files that are not needed for the selected profile (full vs quick).</p><pre><code>import os
import shutil

profile = &#8220;{{cookiecutter.profile}}&#8221;
FULL_ONLY = [
    &#8220;src&#8221;,
    &#8220;notebooks/archive&#8221;,
    &#8220;notebooks/02_analysis.ipynb&#8221;,
    &#8220;data/processed&#8221;,
    &#8220;CHANGELOG.md&#8221;,
    &#8220;.gitignore&#8221;,
]
def remove(path):
    if os.path.isdir(path):
        shutil.rmtree(path)
    elif os.path.isfile(path):
        os.remove(path)
if profile == &#8220;quick&#8221;:
    for item in FULL_ONLY:
        remove(item)
    print(&#8221;\\nQuick project ready. Start with notebooks/01_exploration.ipynb&#8221;)
else:
    print(&#8221;\\nFull project ready.&#8221;)
    print(&#8221;Explore: notebooks/01_exploration.ipynb&#8221;)
    print(&#8221;Analyse: notebooks/02_analysis.ipynb&#8221;)</code></pre><h3><strong>Starter notebooks</strong></h3><p>The template also includes starter notebooks with your usual header and imports already in place.</p><p>Because Jupyter notebooks are JSON files, Cookiecutter can substitute variables inside them automatically. The project name, description, and author fields appear directly in the first markdown cell.</p><p>Instead of starting from a blank notebook every time, you open one that already contains the structure you normally add manually.</p><p>The first notebook is the scratch pad or the exploration notebook:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!7rpo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe337fe1b-5a15-4848-99fa-3f3c92b7e3e7_602x847.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!7rpo!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe337fe1b-5a15-4848-99fa-3f3c92b7e3e7_602x847.png 424w, /__u/substackcdn.com/image/fetch/$s_!7rpo!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe337fe1b-5a15-4848-99fa-3f3c92b7e3e7_602x847.png 848w, /__u/substackcdn.com/image/fetch/$s_!7rpo!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe337fe1b-5a15-4848-99fa-3f3c92b7e3e7_602x847.png 1272w, /__u/substackcdn.com/image/fetch/$s_!7rpo!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe337fe1b-5a15-4848-99fa-3f3c92b7e3e7_602x847.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!7rpo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe337fe1b-5a15-4848-99fa-3f3c92b7e3e7_602x847.png" width="602" height="847" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e337fe1b-5a15-4848-99fa-3f3c92b7e3e7_602x847.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:847,&quot;width&quot;:602,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!7rpo!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe337fe1b-5a15-4848-99fa-3f3c92b7e3e7_602x847.png 424w, /__u/substackcdn.com/image/fetch/$s_!7rpo!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe337fe1b-5a15-4848-99fa-3f3c92b7e3e7_602x847.png 848w, /__u/substackcdn.com/image/fetch/$s_!7rpo!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe337fe1b-5a15-4848-99fa-3f3c92b7e3e7_602x847.png 1272w, /__u/substackcdn.com/image/fetch/$s_!7rpo!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe337fe1b-5a15-4848-99fa-3f3c92b7e3e7_602x847.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The second is a more in depth notebook that allows you keep and prepare the full story.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!DyQ6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6058c1c2-ed37-4605-a75c-d81234065f4a_630x1068.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!DyQ6!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6058c1c2-ed37-4605-a75c-d81234065f4a_630x1068.png 424w, /__u/substackcdn.com/image/fetch/$s_!DyQ6!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6058c1c2-ed37-4605-a75c-d81234065f4a_630x1068.png 848w, /__u/substackcdn.com/image/fetch/$s_!DyQ6!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6058c1c2-ed37-4605-a75c-d81234065f4a_630x1068.png 1272w, /__u/substackcdn.com/image/fetch/$s_!DyQ6!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6058c1c2-ed37-4605-a75c-d81234065f4a_630x1068.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!DyQ6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6058c1c2-ed37-4605-a75c-d81234065f4a_630x1068.png" width="630" height="1068" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6058c1c2-ed37-4605-a75c-d81234065f4a_630x1068.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1068,&quot;width&quot;:630,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!DyQ6!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6058c1c2-ed37-4605-a75c-d81234065f4a_630x1068.png 424w, /__u/substackcdn.com/image/fetch/$s_!DyQ6!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6058c1c2-ed37-4605-a75c-d81234065f4a_630x1068.png 848w, /__u/substackcdn.com/image/fetch/$s_!DyQ6!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6058c1c2-ed37-4605-a75c-d81234065f4a_630x1068.png 1272w, /__u/substackcdn.com/image/fetch/$s_!DyQ6!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6058c1c2-ed37-4605-a75c-d81234065f4a_630x1068.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>The structure here only works if the notebooks inside it are written well. In part one of this series on Substack I cover the six habits that make notebooks readable and rerunnable months later. Link at the end.</em></p><h2><strong>Using the Cookiecutter template</strong></h2><p>Creating a new project becomes a single command from your terminal:</p><pre><code>cookiecutter /path/to/geoscience-project-template/</code></pre><p>You will be prompted for a few details:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!coAv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ecefe49-9324-46f1-93d9-24a8b601c97f_700x366.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!coAv!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ecefe49-9324-46f1-93d9-24a8b601c97f_700x366.png 424w, /__u/substackcdn.com/image/fetch/$s_!coAv!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ecefe49-9324-46f1-93d9-24a8b601c97f_700x366.png 848w, /__u/substackcdn.com/image/fetch/$s_!coAv!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ecefe49-9324-46f1-93d9-24a8b601c97f_700x366.png 1272w, /__u/substackcdn.com/image/fetch/$s_!coAv!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ecefe49-9324-46f1-93d9-24a8b601c97f_700x366.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!coAv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ecefe49-9324-46f1-93d9-24a8b601c97f_700x366.png" width="700" height="366" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5ecefe49-9324-46f1-93d9-24a8b601c97f_700x366.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:366,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!coAv!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ecefe49-9324-46f1-93d9-24a8b601c97f_700x366.png 424w, /__u/substackcdn.com/image/fetch/$s_!coAv!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ecefe49-9324-46f1-93d9-24a8b601c97f_700x366.png 848w, /__u/substackcdn.com/image/fetch/$s_!coAv!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ecefe49-9324-46f1-93d9-24a8b601c97f_700x366.png 1272w, /__u/substackcdn.com/image/fetch/$s_!coAv!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ecefe49-9324-46f1-93d9-24a8b601c97f_700x366.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The full project structure appears immediately, ready to open in Jupyter.</p><h2><strong>Keep the Template Up to Date</strong></h2><p>Over time you will add utilities to <code>src/</code>, improve your default imports, or refine the structure slightly.</p><p>When that happens, update the template as well.</p><p>The template becomes a record of your current working practices. Every new project then begins from that baseline instead of from scratch.</p><h2><strong>Putting It All Together</strong></h2><p>The goal of everything in this article is to remove the friction that stops good habits from sticking and to also speed up the process of creating your geoscience Jupyter Notebook projects.</p><p>A consistent folder structure means you never spend five minutes deciding where things go. A central <code>config.py</code> means you never write a broken relative path again. A shared <code>plotting.py</code> means your figures are consistent without thinking about it. And a Cookiecutter template means all of it is in place before you write a single line of analysis code.</p><p>None of these pieces is complicated on its own. The value comes from using them together, consistently, on every project.</p><p>If you want to get started without building the template from scratch, the full geoscience project template used in this article is available on GitHub:</p><p><strong><a href="https://github.com/andymcdgeo/jupyter_notebook_template">&#8594; Jupyter Notebook Project Template on GitHub</a></strong></p><div><hr></div><p><em>This is part two of a two-part series. Part one covers the six habits that make notebooks easier to maintain and understand over time: treating the notebook as a decision log, writing a header cell before any code runs, separating exploration from the record, naming variables to carry meaning across stages, the kernel restart test, and the cold open test.</em></p><p><em>If the structure described here feels like too much right now, the habits from part one will still get you most of the way there. Once the structure is in place though, those habits become much easier to maintain because the environment nudges you toward them automatically.</em></p><p><em><strong><a href="/__u/andymcdonaldgeo.substack.com/">&#8594; Part 1: 6 Jupyter Notebook Habits That Make Your Work Easy to Return To</a></strong></em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[6 Jupyter Notebook Habits That Make Your Work Easy to Return To]]></title><description><![CDATA[From messy exploration to a record that still makes sense months later]]></description><link>https://andymcdonaldgeo.substack.com/p/6-jupyter-notebook-habits-that-make</link><guid isPermaLink="false">https://andymcdonaldgeo.substack.com/p/6-jupyter-notebook-habits-that-make</guid><dc:creator><![CDATA[Andy McDonald]]></dc:creator><pubDate>Sat, 07 Mar 2026 08:49:51 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!zPo7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2339a2e2-490c-4a0d-aea9-eb1cf44dab42_988x533.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!zPo7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2339a2e2-490c-4a0d-aea9-eb1cf44dab42_988x533.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!zPo7!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2339a2e2-490c-4a0d-aea9-eb1cf44dab42_988x533.png 424w, /__u/substackcdn.com/image/fetch/$s_!zPo7!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2339a2e2-490c-4a0d-aea9-eb1cf44dab42_988x533.png 848w, /__u/substackcdn.com/image/fetch/$s_!zPo7!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2339a2e2-490c-4a0d-aea9-eb1cf44dab42_988x533.png 1272w, /__u/substackcdn.com/image/fetch/$s_!zPo7!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2339a2e2-490c-4a0d-aea9-eb1cf44dab42_988x533.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!zPo7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2339a2e2-490c-4a0d-aea9-eb1cf44dab42_988x533.png" width="988" height="533" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2339a2e2-490c-4a0d-aea9-eb1cf44dab42_988x533.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:533,&quot;width&quot;:988,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:640960,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://andymcdonaldgeo.substack.com/i/190182753?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2339a2e2-490c-4a0d-aea9-eb1cf44dab42_988x533.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!zPo7!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2339a2e2-490c-4a0d-aea9-eb1cf44dab42_988x533.png 424w, /__u/substackcdn.com/image/fetch/$s_!zPo7!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2339a2e2-490c-4a0d-aea9-eb1cf44dab42_988x533.png 848w, /__u/substackcdn.com/image/fetch/$s_!zPo7!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2339a2e2-490c-4a0d-aea9-eb1cf44dab42_988x533.png 1272w, /__u/substackcdn.com/image/fetch/$s_!zPo7!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2339a2e2-490c-4a0d-aea9-eb1cf44dab42_988x533.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here is a situation working in Jupyter Notebooks that many people will recognise.</p><p>A colleague messages you asking how a particular calculation was carried out in a study that was worked on six months ago. You know the answer is somewhere in your jupyter notebook.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>You open it, confident the logic and information is in there.</p><p>Forty minutes later, you are still not sure.</p><p>The code ran. The outputs exist. But the reasoning behind the key decisions is gone. Which Clay Volume cutoff did you use and why? Was normalisation applied to the gamma ray data before or after the formation tops were picked? There is a commented-out cell that might be relevant, or might be an abandoned experiment. You can&#8217;t tell.</p><p>Then you start to notice that a cell further down the page has errored as a variable is not present. After much head scratching you find out it was calculated 5 cells further down.</p><p>This is not a messy notebook problem. Messy exploration is normal and expected. This is a documentation problem: the analysis produced a result, but it did not produce a record.</p><p>In subsurface work, that distinction carries real weight. Notebooks often sit at the base of interpretations that can feed future decisions, reserve estimates, and machine learning model training pipelines. When someone asks why a decision was made, &#8220;I think that was the approach&#8221; is not an audit trail. It is a gap.</p><p>These six habits don&#8217;t slow down exploration. They ensure that what comes out of it is traceable, rerunnable, and readable by someone who was not in the room when it was written, including you, six months later.</p><p>This article does have a geoscience twist to it, but these habits can be applied to any data science project.</p><h2><strong>1. Treat the Notebook as a Decision Log, Not a Script</strong></h2><p>This is the shift that changes everything else.</p><p>Most notebooks are written as scripts: a sequence of code cells that produces an output. The problem with a script framing is that it records <em>what happened,</em> but not <em>why</em>. Code tells you that Gamma Ray values below zero were removed. It doesn&#8217;t tell you whether that was a deliberate interpretation choice, a quality control step, or a temporary workaround that was never revisited.</p><p>A notebook written as a decision log treats every significant step as something that needs to be justified, not just executed.</p><p>The practical difference is in how you use markdown cells. Not as labels for the code below them, but as the reasoning that precedes it.</p><p>Compare these two approaches to the same step.</p><p><strong>Label</strong> (not enough):</p><pre><code>## GR Cleaning
Removing negative values and normalising.</code></pre><p><strong>Decision log</strong> (what you actually need):</p><pre><code>## GR Curve: Cleaning Decisions
Negative GR values present in 3 wells (n=14 samples total). Spike pattern
and depth distribution consistent with tool noise rather than formation signal.
Excluded prior to any formation-level analysis.

Normalisation: min-max applied rather than z-score. Reason: preserves the
shape of the GR distribution across formations, which matters for the
cross-plot in section 4. Z-score would compress the tails where the
lithological contrast is most visible.</code></pre><p>The code that follows executes the decision. The markdown records it.</p><p>When you return to this notebook in four months, you don&#8217;t need to reverse-engineer the reasoning from the code. It is written down. When a colleague picks it up, they know not just what was done but why, and under what assumptions.</p><p>A useful test: if you cannot write a defensible markdown cell for a step, that is a signal the decision itself needs more thought before you commit it to code.</p><h2><strong>2. Write the Header Before You Write Any Code</strong></h2><p>At the start of any notebook, you can save yourself time later on by spending two minutes writing a header cell before writing a single line of code.</p><pre><code># Project: Volve Field -- Petrophysical QC
## Notebook: 03<em>_formation_</em>analysis -- Curve Completeness Assessment

<strong>**Purpose:**</strong>
Assess GR and RHOB curve completeness by formation across the Volve dataset.
Output used to determine viable curve inputs for the lithology classification model.

<strong>**Input:**</strong>
`data/raw/volve_well_logs.csv`
Source: Volve open dataset, DISKOS export, retrieved 2026-01-15.
Wells in scope: 15/9-F-1, 15/9-F-4, 15/9-F-11 (producers only).

<strong>**Assumptions and exclusions:**</strong>
Intervals above casing shoe depth excluded (varies by well, see formation tops file).
GR values &lt; 0 treated as noise and removed prior to analysis.
Brent Group subdivisions collapsed to group level due to sparse tops coverage.

<strong>**Output:**</strong>
`data/processed/curve_completeness_by_formation.csv`
`outputs/figures/completeness_heatmap.png`

<strong>**Last updated:**</strong> 2026-02-26 | <strong>**Status:**</strong> Complete</code></pre><p>The two sections that matter most in geoscience or data science work are data provenance and assumptions.</p><p>Data provenance answers: where did this data come from, when was it obtained, and exactly which subset are you working with. A file path tells you nothing about whether the data came from a live database pull last week or a spreadsheet that was emailed across six months ago. Source and scope belong in the header because they are invisible everywhere else.</p><p>Additionally, a file path that resolves on your machine and nowhere else is not provenance. It is a dead end. If the data lives anywhere other than a shared location the next person is guaranteed to have access to, the header needs to explain how to get it:</p><pre><code><strong>**Input:**</strong>
`data/raw/volve_well_logs.csv`
Source: Volve open dataset, DISKOS export, retrieved 2026-01-15.

Wells in scope: 15/9-F-1, 15/9-F-4, 15/9-F-11 (producers only).

Access: Shared project drive at \\server\projects\volve<em>_qc\data\raw\
If unavailable, contact data management or re-export from DISKOS using
the query parameters documented in `docs/data_</em>export<em>_notes.md`.</em></code></pre><p>The fields that matter when access is not guaranteed: where the file physically lives or can be retrieved from, who to contact if access is unavailable, how it was originally obtained so it can be re-obtained the same way, and any query parameters or export settings used. A re-export with different settings is not the same dataset, and that distinction will not be obvious to the person picking up the notebook six months later.</p><p>Assumptions and exclusions are where the interpretive choices live. Collapsing Brent Group subdivisions is a meaningful geological decision. Excluding above-casing intervals is a meaningful QC decision. Both are defensible. Neither shows up in the code without a comment, and comments are the first thing to disappear during a cleanup. The header cell is permanent.</p><p>A simple status message can inform you and other notebook users whether this is a complete notebook or if it is still a work in progress.</p><h2><strong>3. Separate Exploration From the Record</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!mgxG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d6143-b112-4384-b2bb-425a01d7ad3b_700x381.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!mgxG!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d6143-b112-4384-b2bb-425a01d7ad3b_700x381.png 424w, /__u/substackcdn.com/image/fetch/$s_!mgxG!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d6143-b112-4384-b2bb-425a01d7ad3b_700x381.png 848w, /__u/substackcdn.com/image/fetch/$s_!mgxG!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d6143-b112-4384-b2bb-425a01d7ad3b_700x381.png 1272w, /__u/substackcdn.com/image/fetch/$s_!mgxG!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_webp, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d6143-b112-4384-b2bb-425a01d7ad3b_700x381.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!mgxG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d6143-b112-4384-b2bb-425a01d7ad3b_700x381.png" width="700" height="381" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/844d6143-b112-4384-b2bb-425a01d7ad3b_700x381.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:381,&quot;width&quot;:700,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!mgxG!, /__u/andymcdonaldgeo.substack.com/w_424, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d6143-b112-4384-b2bb-425a01d7ad3b_700x381.png 424w, /__u/substackcdn.com/image/fetch/$s_!mgxG!, /__u/andymcdonaldgeo.substack.com/w_848, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d6143-b112-4384-b2bb-425a01d7ad3b_700x381.png 848w, /__u/substackcdn.com/image/fetch/$s_!mgxG!, /__u/andymcdonaldgeo.substack.com/w_1272, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d6143-b112-4384-b2bb-425a01d7ad3b_700x381.png 1272w, /__u/substackcdn.com/image/fetch/$s_!mgxG!, /__u/andymcdonaldgeo.substack.com/w_1456, /__u/andymcdonaldgeo.substack.com/c_limit, /__u/andymcdonaldgeo.substack.com/f_auto, /__u/andymcdonaldgeo.substack.com/q_auto:good, /__u/andymcdonaldgeo.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F844d6143-b112-4384-b2bb-425a01d7ad3b_700x381.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Splitting out exploration from communication help maintain a clean setup.</figcaption></figure></div><p>Exploration and documentation are not the same activity, and trying to do both in one notebook produces something that serves neither purpose well.</p><p>Exploration is nonlinear by nature. You are testing approaches, hitting dead ends, trying variations you will ultimately discard. That process should be unconstrained. A notebook full of experimental cells, abandoned branches, and intermediate print statements is not a problem. It&#8217;s how analysis actually happens.</p><p>The problem comes when that exploratory notebook is also treated as the record. It looks like an analysis. It produces outputs. But the path through it is not a story. It is a session log.</p><p>The habit is to maintain two notebooks per project.</p><p><strong>The working notebook</strong> is where exploration lives. It has no cleanliness requirement. Dead ends stay in. Commented-out experiments stay in. Variable names that only made sense at 2am stay in. It is never shared and never needs to be.</p><p><strong>The record notebook</strong> is written once you understand the shape of the answer. It tells one story: problem, approach, result. It runs cleanly from top to bottom. Every significant decision has a markdown cell. Outputs are saved to predictable locations.</p><p>A practical signal for when to make the transition: when you can describe the approach out loud, in sequence, without referring to the notebook. If you can&#8217;t do that yet, you are still likely in exploration territory.</p><p>The record notebook is not just a cleaned-up version of the working notebook. It should be written from scratch, with the benefit of already knowing what the analysis is and what functions or code snippets matter from your exploration notebook. That distinction matters. Cleaning up an exploratory notebook tends to preserve its structure while hiding its confusion. Writing the record from scratch produces something that actually reads as intended and can also highlight any issues in your initial analysis.</p><p><em>The record notebook matters even more as AI starts to enter the analysis workflow. A model can help generate interpretations, but someone still has to verify them. And that verification depends on having a clear record of what decisions were made and why. I&#8217;ve written about why verification becomes the hardest problem in technical AI work over on Substack. Link at the end.</em></p><h2><strong>4. Name Variables for the Person Who Comes Back</strong></h2><p>Variable naming is documentation that can&#8217;t be separated from the code. Unlike comments, it can&#8217;t be deleted. Unlike markdown cells, it is present on every line that references the object.</p><p>The goal isn&#8217;t descriptive names for their own sake. It&#8217;s names that carry two pieces of information: what the object contains, and what stage of the workflow it represents.</p><p>A stage-prefix convention makes both visible at a glance:</p><pre><code>raw_logs            # loaded directly from file, unmodified
clean_logs          # nulls removed, depth range trimmed, curves validated
logs_with_vcl       # Vcl appended from a clay volume calculation
train_logs          # subset used for model fitting
test_logs           # held-out subset, not seen during training</code></pre><p>For model objects, include the algorithm and the target variable:</p><pre><code>model_rf_lithology        # random forest classifier, predicts lithology class
model_lgbm_permeability   # LightGBM regressor, predicts permeability (mD)</code></pre><p>The rubric is simple: a name should tell you what it is and where it sits in the workflow. If you can&#8217;t construct a name that does both, the object itself may not be clearly defined yet. That ambiguity is worth resolving before writing code that depends on it.</p><p>One habit that avoids the creeping <code>df2</code>, <code>df3</code>, <code>df_final</code> problem: name the object at creation, not at use. If you plan to rename it later, you will very unlikely rename it later.</p><h2><strong>5. The Kernel Restart Is Not Optional</strong></h2><p>Before closing any record notebook, restart the kernel and run all cells from top to bottom. If it doesn&#8217;t complete cleanly, it is not finished.</p><p>The most common notebook failure mode is hidden state: a variable that was defined in an earlier session and no longer exists in the code, a file loaded from a path that no longer resolves, an import that worked once because another library happened to already be in memory. In the current session, everything appears correct. On a different machine, or six months later, it fails silently or not so silently at a cell with no obvious explanation.</p><p>The restart-and-run check is the only reliable way to surface these problems before they surface for someone else.</p><p>The practical objection is compute time. Some steps are expensive and re-running a full model training pass or reloading a large dataset on every review pass isn&#8217;t realistic. The pattern that handles this cleanly is to cache intermediate outputs to <code>data/processed/</code> and load from cache in subsequent steps. The record notebook reads the cached output rather than re-executing the expensive step. The reproducibility standard still holds: the notebook runs top-to-bottom, deterministically, it just does not re-derive everything from raw data in a single pass.</p><p>The distinction worth being clear on: a notebook that produces the same outputs given the same inputs is reproducible, regardless of whether it uses cached intermediates. A notebook that only works when run in a specific order, with specific variables already in memory from a previous session, is neither reproducible nor fast. It is a future debugging problem waiting to surface at the worst possible moment.</p><h2><strong>6. Apply the Cold Open Test Before Closing</strong></h2><p>The first five habits are about writing the notebook. This one is about checking whether it actually works without you.</p><p>Before treating any record notebook as complete, try this: close it, open it fresh, and ask whether a colleague could follow it without asking you a single question. Not a full peer review. Just a five-minute scan. Can they identify the purpose from the header? Can they locate the key decisions in the markdown? Do they know what to do with the outputs?</p><p>If the answer to any of those is no, the notebook is not finished regardless of whether it runs cleanly.</p><p>In subsurface work this test has particular weight because notebooks routinely outlive the context in which they were written. A petrophysicist moves to another asset, goes on leave, or leaves the organisation. Six months later someone needs to understand what was done and why, and the only record is the notebook. It has to stand on its own.</p><p>A practical checklist to run before marking any record notebook complete:</p><pre><code>## Notebook Sign-Off Checklist
[ ] Purpose is clear from the header without opening any code cells
[ ] Every significant decision has a markdown explanation, not just code
[ ] Data source and date of extraction are recorded in the header
[ ] Assumptions and exclusions are explicitly stated
[ ] Output file names make their contents obvious without opening them
[ ] Header accurately reflects what the notebook actually does
[ ] Kernel restarted and all cells run top-to-bottom without error</code></pre><p>The last point on the header is easy to overlook. A header written at the start of a project often doesn&#8217;t reflect what the notebook became by the end of it. A notebook titled &#8220;exploratory curve QC&#8221; that ended up producing the formation completeness dataset used in model training is misleading to anyone who encounters it later. Update the header when the scope changes. It takes thirty seconds and it is the first thing anyone reads.</p><p>The cold open test is the difference between a notebook that is technically complete and one that is actually usable. In a field where analysis is regularly revisited, audited, or handed over, that gap matters more than it might seem.</p><h2><strong>One Habit at a Time</strong></h2><p>These habits compound, but they don&#8217;t all need to start at once.</p><p>If applying all six on your next project feels like too much overhead, start with the header cell. Write it before you write any code. It takes two minutes, it forces you to state the purpose and the assumptions before you have committed to any of them, and it is still there when you come back to the notebook in three months wondering what it was for.</p><p>The goal is not notebooks that look rigorous. It&#8217;s notebooks that can be explained, re-run, and handed over without a conversation first. A header cell, honest markdown at each decision point, a clean top-to-bottom run, and sixty seconds asking whether a colleague could follow it cold gets you most of the way there without slowing down the exploration that precedes it.</p><div><hr></div><p><em>These habits matter even more as AI starts to enter the analysis workflow. When a model generates an interpretation or a processed output, someone still needs to verify it. That verification depends on having a clear record: what data went in, what decisions shaped it, and what the output actually represents. A notebook without that record doesn&#8217;t just make your own work harder to return to. It makes AI-assisted work harder to trust. I explored this problem directly in a recent piece on Substack &#8212; specifically why verification becomes the binding constraint in technical domains, and what that means for how you build and use AI tools.</em></p><p><em><a href="/__u/andymcdonaldgeo.substack.com/p/the-jagged-frontier-why-ai-amazes">&#8594; The Jagged Frontier: Why AI Amazes and Frustrates in Equal Measure</a></em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://andymcdonaldgeo.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subsurface Syntax is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item></channel></rss>