<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://joeroe.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://joeroe.io/" rel="alternate" type="text/html" /><updated>2026-02-23T16:33:54+00:00</updated><id>https://joeroe.io/feed.xml</id><title type="html">Joe Roe</title><subtitle>Joe Roe is a computational archaeologist specialising in the ecology and economy of the world&apos;s farmers in prehistoric Southwest Asia.</subtitle><author><name>Joe Roe</name></author><entry><title type="html">When was the plough invented?</title><link href="https://joeroe.io/2025/08/24/ploughing.html" rel="alternate" type="text/html" title="When was the plough invented?" /><published>2025-08-24T00:00:00+00:00</published><updated>2025-08-24T00:00:00+00:00</updated><id>https://joeroe.io/2025/08/24/ploughing</id><content type="html" xml:base="https://joeroe.io/2025/08/24/ploughing.html"><![CDATA[<figure>
    <img src="/images/ploughing/potts_in_jursa_2021.png" alt="Ancient Mesopotamian depiction of an ox-drawn plough" class="align-center" style="width: auto;" />
    <figcaption class="align-center">
        Ancient Mesopotamian depiction of an ox-drawn plough (after <a href="https://doi.org/10.1002/9781118970959.ch8">Potts in Jursa, 2021</a>, all rights reserved)
    </figcaption>
</figure>

<p>The invention of the ox-drawn plough was a turning point in the prehistory of agriculture because it was the first time <a href="https://doi.org/10.15184/aqy.2019.105">food production was decoupled from human labour</a>. 
This is why, from Medieval Europe to the <a href="https://doi.org/10.1002/9781118970959.ch8">ancient Near East</a> farms were measured by the number of oxen (later horses etc.) working it, not people.</p>

<p>Yet it’s surprisingly difficult to pinpoint when this actually happened.</p>

<figure>
    <img src="/images/ploughing/van_willigen_et_al_2024_fig1.png" alt="Map of Europe showing the location of archaeological sites with ancient ploughmarks. Points are concentrated in Denmark and Switzerland, with a smaller number in Great Britain, the Netherlands, Italy, and Poland." class="align-center" style="width: auto;" />
    <figcaption class="align-center">
        Archaeological sites with evidence for early ploughing in Europe (<a href="https://doi.org/10.1057/s41599-024-02837-5">van Willigen et al. 2024, fig. 1</a>, CC BY)
    </figcaption>
</figure>

<p>The earliest direct evidence we have for ploughing are marks made in ancient sediment, though these are very rarely preserved. In Europe the oldest known are from <a href="https://doi.org/10.1057/s41599-024-02837-5">Switzerland (5000 BCE)</a>, <a href="https://doi.org/10.1016/j.jas.2012.08.042">Denmark (4000 BCE)</a> and Italy (4300 BCE, at Saint-Martin-de-Corléans).
In each case the date coincidences with, or is shortly after, the arrival of agriculture in that region.
This implies that ploughing was part of European farming from the beginning. In other words, like agriculture itself, it must have been brought there from the Middle East.</p>

<figure>
    <img src="/images/ploughing/van_willigen_et_al_2024_fig3b.png" alt="Photo showing an archaeologist working in a trench with faint parallel lines visible on the exposed surface" class="align-center" style="width: auto;" />
    <figcaption class="align-center">
        Ploughmarks from Anciens Arsenaux in Switzerland – the earliest known direct evidence for ploughing (<a href="https://doi.org/10.1057/s41599-024-02837-5">van Willigen et al. 2024, fig. 3</a>, CC BY)
    </figcaption>
</figure>

<p>Unfortunately we haven’t (yet) found the right conditions for preserved ploughmarks in the Middle East.
Instead, archaeologists have relied on <a href="https://www.jstor.org/stable/44870382">indirect evidence of ploughing</a>. 
The main source of evidence is cattle bones, which can display work-related injuries or signs of castration (oxen are usually castrated to make them easier to work with).
If we accept this evidence, ploughing could have been invented as early as 8500 BCE – right after cattle were domesticated.</p>

<figure>
    <img src="/images/ploughing/helmer_et_al_2018_fig1.png" alt="Photographs of three cattle phalanx bones, one visibly much older than the others." class="align-center" style="width: auto;" />
    <figcaption class="align-center">
        Ancient and modern cattle phalanx bones with pathologies thought to be caused by work (<a href="https://www.jstor.org/stable/44870382">Helmer et al. 2018, fig. 1</a>, all rights reserved)
    </figcaption>
</figure>

<p>There are some problems, though.</p>

<p>The pathologies used to identify working animals are rare in the archaeological record and can also be caused by illness. 
Castrates are distinguished by comparing measurements of their bones to modern animals, but we can’t be sure this is a valid comparison. 
At this early stage cattle were still being domesticated and probably were in contact with wild auroch populations, so they likely displayed more morphological variation than modern domestic animals.
Some experts even <a href="https://doi.org/10.1093/af/vfab015">dispute the fact that cattle were domesticated by 8500 BCE</a>. instead putting it up to a thousand years later.
As with working, we largely rely on indirect evidence for cattle domestication, so it is hard to pinpoint with confidence.</p>

<p>So where does this leave us? Based on the evidence from Europe, ploughing was invented before 5000 BCE, but could go as far back 7500–8500 BCE in the Middle East.
In other words, we don’t know if the plough was a late addition to prehistoric agriculture—adopted somewhere on the way from the Middle East to Europe—or if it was an integral part of it from the start.</p>

<figure>
    <img src="/images/ploughing/gronenborn_et_al_2023_1.png" alt="Map showing the spread of farming in Western Eurasia. Begins in the 'Fertile Crescent' of the Middle East around 9000 BCE; spreads to southeast Europe, the Caucasus and Persia by 6000 BCE; to Italy and southern Europe via a 'sea route' by 5500 BCE; to Central Europe via a 'land route' by 5000 BCE; and to the British Isles, Low Countries, and southern Scandinavia by 4000 BCE." class="align-center" style="width: auto;" />
    <figcaption class="align-center">
        Spread of farming in Western Eurasia (<a href="https://doi.org/10.5281/zenodo.10047818">Gronenborn et al. 2023</a>, CC BY)
    </figcaption>
</figure>

<p>This is a big problem for our understanding of human social and economic prehistory. We think the earliest farmers practised a sort of ‘garden agriculture’ based on limited tilling with hand tools. 
Ploughing up whole fields to (presumably) sow them with monocultures is a another beast entirely, ecologically speaking.
Animal labour is also what made it possible for one person to reap the harvest of more land than they themselves could physically work – or compel other people to do it for them!</p>

<p>Was the potential for proto-serfdom and cattle-powered ecocide inherent in agriculture from the beginning?
Or did our farming ancestors spend as much as <em>three thousand years</em> as essentially egalitarian gardeners before things took a turn for the worse?
That’s the difference dating the plough makes!</p>

<p><small>Adapted from a <a href="https://archaeo.social/@joeroe/114986894481472137">Mastodon thread</a> posted to <a href="https://archaeo.social">archaeo.social</a> on 7 August 2025.</small></p>]]></content><author><name>Joe Roe</name></author><summary type="html"><![CDATA[Ancient Mesopotamian depiction of an ox-drawn plough (after Potts in Jursa, 2021, all rights reserved) The invention of the ox-drawn plough was a turning point in the prehistory of agriculture because it was the first time food production was decoupled from human labour. This is why, from Medieval Europe to the ancient Near East farms were measured by the number of oxen (later horses etc.) working it, not people. Yet it’s surprisingly difficult to pinpoint when this actually happened. Archaeological sites with evidence for early ploughing in Europe (van Willigen et al. 2024, fig. 1, CC BY) The earliest direct evidence we have for ploughing are marks made in ancient sediment, though these are very rarely preserved. In Europe the oldest known are from Switzerland (5000 BCE), Denmark (4000 BCE) and Italy (4300 BCE, at Saint-Martin-de-Corléans). In each case the date coincidences with, or is shortly after, the arrival of agriculture in that region. This implies that ploughing was part of European farming from the beginning. In other words, like agriculture itself, it must have been brought there from the Middle East. Ploughmarks from Anciens Arsenaux in Switzerland – the earliest known direct evidence for ploughing (van Willigen et al. 2024, fig. 3, CC BY) Unfortunately we haven’t (yet) found the right conditions for preserved ploughmarks in the Middle East. Instead, archaeologists have relied on indirect evidence of ploughing. The main source of evidence is cattle bones, which can display work-related injuries or signs of castration (oxen are usually castrated to make them easier to work with). If we accept this evidence, ploughing could have been invented as early as 8500 BCE – right after cattle were domesticated. Ancient and modern cattle phalanx bones with pathologies thought to be caused by work (Helmer et al. 2018, fig. 1, all rights reserved) There are some problems, though. The pathologies used to identify working animals are rare in the archaeological record and can also be caused by illness. Castrates are distinguished by comparing measurements of their bones to modern animals, but we can’t be sure this is a valid comparison. At this early stage cattle were still being domesticated and probably were in contact with wild auroch populations, so they likely displayed more morphological variation than modern domestic animals. Some experts even dispute the fact that cattle were domesticated by 8500 BCE. instead putting it up to a thousand years later. As with working, we largely rely on indirect evidence for cattle domestication, so it is hard to pinpoint with confidence. So where does this leave us? Based on the evidence from Europe, ploughing was invented before 5000 BCE, but could go as far back 7500–8500 BCE in the Middle East. In other words, we don’t know if the plough was a late addition to prehistoric agriculture—adopted somewhere on the way from the Middle East to Europe—or if it was an integral part of it from the start. Spread of farming in Western Eurasia (Gronenborn et al. 2023, CC BY) This is a big problem for our understanding of human social and economic prehistory. We think the earliest farmers practised a sort of ‘garden agriculture’ based on limited tilling with hand tools. Ploughing up whole fields to (presumably) sow them with monocultures is a another beast entirely, ecologically speaking. Animal labour is also what made it possible for one person to reap the harvest of more land than they themselves could physically work – or compel other people to do it for them! Was the potential for proto-serfdom and cattle-powered ecocide inherent in agriculture from the beginning? Or did our farming ancestors spend as much as three thousand years as essentially egalitarian gardeners before things took a turn for the worse? That’s the difference dating the plough makes! Adapted from a Mastodon thread posted to archaeo.social on 7 August 2025.]]></summary></entry><entry><title type="html">era 0.5.0: chronological ordering and extremes</title><link href="https://joeroe.io/2024/11/20/era-0.5.0.html" rel="alternate" type="text/html" title="era 0.5.0: chronological ordering and extremes" /><published>2024-11-20T00:00:00+00:00</published><updated>2024-11-20T00:00:00+00:00</updated><id>https://joeroe.io/2024/11/20/era-0.5.0</id><content type="html" xml:base="https://joeroe.io/2024/11/20/era-0.5.0.html"><![CDATA[<p><strong><a href="https://era.joeroe.io">era</a></strong> v0.5.0 is now available <a href="https://CRAN.R-project.org/package=era">on CRAN</a>:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">install.packages</span><span class="p">(</span><span class="s2">"era"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>This minor release adds functions for <a href="https://era.joeroe.io/reference/yr_sort.html">chronological ordering</a> (<code class="language-plaintext highlighter-rouge">yr_sort()</code>) of year vectors:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Forward-counting era:</span><span class="w">
</span><span class="n">x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">yr</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="m">200</span><span class="p">,</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="m">300</span><span class="p">),</span><span class="w"> </span><span class="s2">"CE"</span><span class="p">)</span><span class="w">
</span><span class="n">yr_earliest</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # CE years &lt;yr[1]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 100</span><span class="w">
</span><span class="c1">#&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0</span><span class="w">
</span><span class="n">yr_latest</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # CE years &lt;yr[1]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 300</span><span class="w">
</span><span class="c1">#&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0</span><span class="w">
</span><span class="n">yr_range</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # CE years &lt;yr[2]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 100 300</span><span class="w">
</span><span class="c1">#&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0</span><span class="w">

</span><span class="c1"># Backward-counting era:</span><span class="w">
</span><span class="n">y</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">yr</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="m">200</span><span class="p">,</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="m">300</span><span class="p">),</span><span class="w"> </span><span class="s2">"BCE"</span><span class="p">)</span><span class="w">
</span><span class="n">yr_earliest</span><span class="p">(</span><span class="n">y</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # BCE years &lt;yr[1]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 300</span><span class="w">
</span><span class="c1">#&gt; # Era: Before Common Era (BCE): Gregorian years (365.2425 days), counted backwards from 1</span><span class="w">
</span><span class="n">yr_latest</span><span class="p">(</span><span class="n">y</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # BCE years &lt;yr[1]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 100</span><span class="w">
</span><span class="c1">#&gt; # Era: Before Common Era (BCE): Gregorian years (365.2425 days), counted backwards from 1</span><span class="w">
</span><span class="n">yr_range</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # CE years &lt;yr[2]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 100 300</span><span class="w">
</span><span class="c1">#&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0</span><span class="w">
</span></code></pre></div></div>

<p>And for <a href="https://era.joeroe.io/reference/yr_extremes.html">calculating their extreme values</a> (<code class="language-plaintext highlighter-rouge">yr_earliest()</code>, <code class="language-plaintext highlighter-rouge">yr_latest()</code>, and <code class="language-plaintext highlighter-rouge">yr_range()</code>)</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Forward-counting era:</span><span class="w">
</span><span class="n">x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">yr</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="m">200</span><span class="p">,</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="m">300</span><span class="p">),</span><span class="w"> </span><span class="s2">"CE"</span><span class="p">)</span><span class="w">
</span><span class="n">yr_earliest</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # CE years &lt;yr[1]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 100</span><span class="w">
</span><span class="c1">#&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0</span><span class="w">
</span><span class="n">yr_latest</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # CE years &lt;yr[1]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 300</span><span class="w">
</span><span class="c1">#&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0</span><span class="w">
</span><span class="n">yr_range</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # CE years &lt;yr[2]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 100 300</span><span class="w">
</span><span class="c1">#&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0</span><span class="w">

</span><span class="c1"># Backward-counting era:</span><span class="w">
</span><span class="n">y</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">yr</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="m">200</span><span class="p">,</span><span class="w"> </span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="m">300</span><span class="p">),</span><span class="w"> </span><span class="s2">"BCE"</span><span class="p">)</span><span class="w">
</span><span class="n">yr_earliest</span><span class="p">(</span><span class="n">y</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # BCE years &lt;yr[1]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 300</span><span class="w">
</span><span class="c1">#&gt; # Era: Before Common Era (BCE): Gregorian years (365.2425 days), counted backwards from 1</span><span class="w">
</span><span class="n">yr_latest</span><span class="p">(</span><span class="n">y</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # BCE years &lt;yr[1]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 100</span><span class="w">
</span><span class="c1">#&gt; # Era: Before Common Era (BCE): Gregorian years (365.2425 days), counted backwards from 1</span><span class="w">
</span><span class="n">yr_range</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # CE years &lt;yr[2]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 100 300</span><span class="w">
</span><span class="c1">#&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0</span><span class="w">
</span></code></pre></div></div>

<p>Essentially these are all wrappers for base functions (<code class="language-plaintext highlighter-rouge">sort()</code>, <code class="language-plaintext highlighter-rouge">min()</code>, <code class="language-plaintext highlighter-rouge">max()</code> and <code class="language-plaintext highlighter-rouge">range()</code>) that are aware of the directionality of the era system attached to the vector: “CE” years are counted forwards, “BCE” years are counted backwards, etc.
I decided to implement them as prefixed functions instead of S3 methods for <code class="language-plaintext highlighter-rouge">yr</code> vectors because I didn’t want to suprise people when they used e.g. <code class="language-plaintext highlighter-rouge">max()</code> expecting the numerical maximum and got the chronologically latest value instead.</p>

<h2 id="links">Links</h2>

<ul>
  <li><a href="https://CRAN.R-project.org/package=era">era on CRAN</a></li>
  <li><a href="https://era.joeroe.io/">era package documentation</a></li>
  <li><a href="https://era.joeroe.io/articles/era.html">Introductory vignette</a></li>
  <li><a href="https://github.com/joeroe/era">Source code</a> (GitHub)</li>
</ul>]]></content><author><name>Joe Roe</name></author><category term="R" /><summary type="html"><![CDATA[era v0.5.0 is now available on CRAN: install.packages("era") This minor release adds functions for chronological ordering (yr_sort()) of year vectors: # Forward-counting era: x &lt;- yr(c(200, 100, 300), "CE") yr_earliest(x) #&gt; # CE years &lt;yr[1]&gt;: #&gt; [1] 100 #&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0 yr_latest(x) #&gt; # CE years &lt;yr[1]&gt;: #&gt; [1] 300 #&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0 yr_range(x) #&gt; # CE years &lt;yr[2]&gt;: #&gt; [1] 100 300 #&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0 # Backward-counting era: y &lt;- yr(c(200, 100, 300), "BCE") yr_earliest(y) #&gt; # BCE years &lt;yr[1]&gt;: #&gt; [1] 300 #&gt; # Era: Before Common Era (BCE): Gregorian years (365.2425 days), counted backwards from 1 yr_latest(y) #&gt; # BCE years &lt;yr[1]&gt;: #&gt; [1] 100 #&gt; # Era: Before Common Era (BCE): Gregorian years (365.2425 days), counted backwards from 1 yr_range(x) #&gt; # CE years &lt;yr[2]&gt;: #&gt; [1] 100 300 #&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0 And for calculating their extreme values (yr_earliest(), yr_latest(), and yr_range()) # Forward-counting era: x &lt;- yr(c(200, 100, 300), "CE") yr_earliest(x) #&gt; # CE years &lt;yr[1]&gt;: #&gt; [1] 100 #&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0 yr_latest(x) #&gt; # CE years &lt;yr[1]&gt;: #&gt; [1] 300 #&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0 yr_range(x) #&gt; # CE years &lt;yr[2]&gt;: #&gt; [1] 100 300 #&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0 # Backward-counting era: y &lt;- yr(c(200, 100, 300), "BCE") yr_earliest(y) #&gt; # BCE years &lt;yr[1]&gt;: #&gt; [1] 300 #&gt; # Era: Before Common Era (BCE): Gregorian years (365.2425 days), counted backwards from 1 yr_latest(y) #&gt; # BCE years &lt;yr[1]&gt;: #&gt; [1] 100 #&gt; # Era: Before Common Era (BCE): Gregorian years (365.2425 days), counted backwards from 1 yr_range(x) #&gt; # CE years &lt;yr[2]&gt;: #&gt; [1] 100 300 #&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0 Essentially these are all wrappers for base functions (sort(), min(), max() and range()) that are aware of the directionality of the era system attached to the vector: “CE” years are counted forwards, “BCE” years are counted backwards, etc. I decided to implement them as prefixed functions instead of S3 methods for yr vectors because I didn’t want to suprise people when they used e.g. max() expecting the numerical maximum and got the chronologically latest value instead. Links era on CRAN era package documentation Introductory vignette Source code (GitHub)]]></summary></entry><entry><title type="html">Deploy a Fly app with Woodpecker CI</title><link href="https://joeroe.io/2024/01/09/deploy-fly-woodpecker-ci.html" rel="alternate" type="text/html" title="Deploy a Fly app with Woodpecker CI" /><published>2024-01-09T00:00:00+00:00</published><updated>2024-01-09T00:00:00+00:00</updated><id>https://joeroe.io/2024/01/09/deploy-fly-woodpecker-ci</id><content type="html" xml:base="https://joeroe.io/2024/01/09/deploy-fly-woodpecker-ci.html"><![CDATA[<p>This post describes how to set up continuous deployment to <a href="https://fly.io">fly.io</a> using <a href="https://woodpecker-ci.org/">Woodpecker</a>, an open source continuous integration engine.
The Fly documentation includes instructions on how to set up continuous deployment <a href="https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/">with GitHub Actions</a> and <a href="https://fly.io/blog/continuous-deployment-with-gitlab/">with GitLab CI/CD</a>, but I couldn’t find anything for Woodpecker.
Fortunately, the process is almost identical to setting it up with GitLab CI/CD, so I could figure it out by following along that tutorial and making minor changes.</p>

<p>My use case was to deploy from a <a href="https://codeberg.org">Codeberg</a> repository, but the instructions below should also work for any <a href="https://woodpecker-ci.org/docs/administration/forges/overview">forge supported by Woodpecker</a> (currently GitHub, Gitea, Forgejo, GitLab, and Bitbucket).</p>

<h2 id="connect-your-repository-to-woodpecker">Connect your repository to Woodpecker</h2>

<p>First things first, you’ll need access to a Woodpecker instance.
Codeberg users can <a href="https://codeberg.org/Codeberg-e.V./requests">request access</a> to a hosted version, as long as you intend to use it for public, freely-licensed code.
Otherwise you can <a href="https://woodpecker-ci.org/docs/administration/deployment/overview">self-host it</a>.
Woodpecker come with both a web interface and a <a href="https://woodpecker-ci.org/docs/cli">command line interface</a>.
You can connect the CLI to your Woodpecker instance using the personal access token displayed under <em>User Settings → API</em>.</p>

<p>Once you have Woodpecker up and running, <a href="https://woodpecker-ci.org/docs/usage/intro">activate it for the repository</a> that you want to deploy, i.e. the one that contains your <code class="language-plaintext highlighter-rouge">fly.toml</code> configuration file.
Be sure to <strong>disable ‘Allow Pull Requests’</strong> in the project settings, otherwise anybody who can make a pull request will have access to your Fly machines!</p>

<h2 id="create-a-workflow-file">Create a workflow file</h2>

<p>Create <code class="language-plaintext highlighter-rouge">.woodpecker/deploy.yml</code> in your repository with the following contents:</p>

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">steps</span><span class="pi">:</span>
  <span class="na">deploy</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">golang</span>
    <span class="na">commands</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">apt-get update -qq &amp;&amp; apt-get install -y curl</span>
      <span class="pi">-</span> <span class="s">curl -L https://fly.io/install.sh | sh</span>
      <span class="pi">-</span> <span class="s">/root/.fly/bin/flyctl deploy</span>
    <span class="na">secrets</span><span class="pi">:</span> <span class="pi">[</span> <span class="nv">fly_access_token</span> <span class="pi">]</span>
</code></pre></div></div>

<p>This sets up a ‘deploy’ workflow which downloads a shell script to install fly, then runs <code class="language-plaintext highlighter-rouge">flyctl deploy</code>.
The choice of base image is arbitrary since all we really need installed is curl and bash.</p>

<h2 id="set-up-a-fly-access-token">Set up a Fly access token</h2>

<p>In order to deploy securely from the container created by Woodpecker, we need to authenticate with Fly in such a way that the credentials do not leak into the (public) CI logs.
To do this, we will generate a unique access token and pass it into the container as an environment variable using Woodpecker’s <a href="https://woodpecker-ci.org/docs/usage/secrets">secrets store</a>.</p>

<p>First, generate a new deploy token using the Fly web interface or command line:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>flyctl tokens create deploy <span class="nt">-n</span> woodpecker
</code></pre></div></div>

<p>It’s a good idea to use a dedicated token for woodpecker deployments, so if you need to you can revoke it without affecting any other integrations you might have.
You might also want to set a shorter expiry time with the <code class="language-plaintext highlighter-rouge">-x</code> option (the default is twenty years).
Copy or save your newly generated secret somewhere now, because it’s only shown once.</p>

<p>Next, create a secret named <code class="language-plaintext highlighter-rouge">fly_access_token</code> that contains your access token.
You can do this using the Woodpecker web interface or the command line:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>woodpecker secret add <span class="nt">-name</span> fly_access_token <span class="nt">-event</span> push <span class="nt">-event</span> manual <span class="nt">-repository</span> &lt;your_repository&gt; <span class="nt">-value</span> <span class="s1">'&lt;your_access_token&gt;'</span>
</code></pre></div></div>

<p>Note that we only make the secret available when the workflow is triggered by a push from an authorised contributor to the repository or run manually by somebody with access to Woodpecker CI.
Your usage might vary, but you probably should not make it available on pull requests, otherwise a careless or malicious pull request could leak your access token.</p>

<h2 id="deploy">Deploy</h2>

<p>That’s it!
When you push these changes to your remote repository, Woodpecker should immediately deploy to Fly.
You can monitor the status of the deployment in the Woodpecker CI web interface.
In the Woodpecker repository settings, under <em>Badge</em>, you’ll also find a badge that you can add to your README to show the status of the last deployment.</p>

<p><img src="/images/pipeline_success.svg" alt="'Success' status badge from Woodpecker CI" /></p>]]></content><author><name>Joe Roe</name></author><summary type="html"><![CDATA[This post describes how to set up continuous deployment to fly.io using Woodpecker, an open source continuous integration engine. The Fly documentation includes instructions on how to set up continuous deployment with GitHub Actions and with GitLab CI/CD, but I couldn’t find anything for Woodpecker. Fortunately, the process is almost identical to setting it up with GitLab CI/CD, so I could figure it out by following along that tutorial and making minor changes. My use case was to deploy from a Codeberg repository, but the instructions below should also work for any forge supported by Woodpecker (currently GitHub, Gitea, Forgejo, GitLab, and Bitbucket). Connect your repository to Woodpecker First things first, you’ll need access to a Woodpecker instance. Codeberg users can request access to a hosted version, as long as you intend to use it for public, freely-licensed code. Otherwise you can self-host it. Woodpecker come with both a web interface and a command line interface. You can connect the CLI to your Woodpecker instance using the personal access token displayed under User Settings → API. Once you have Woodpecker up and running, activate it for the repository that you want to deploy, i.e. the one that contains your fly.toml configuration file. Be sure to disable ‘Allow Pull Requests’ in the project settings, otherwise anybody who can make a pull request will have access to your Fly machines! Create a workflow file Create .woodpecker/deploy.yml in your repository with the following contents: steps: deploy: image: golang commands: - apt-get update -qq &amp;&amp; apt-get install -y curl - curl -L https://fly.io/install.sh | sh - /root/.fly/bin/flyctl deploy secrets: [ fly_access_token ] This sets up a ‘deploy’ workflow which downloads a shell script to install fly, then runs flyctl deploy. The choice of base image is arbitrary since all we really need installed is curl and bash. Set up a Fly access token In order to deploy securely from the container created by Woodpecker, we need to authenticate with Fly in such a way that the credentials do not leak into the (public) CI logs. To do this, we will generate a unique access token and pass it into the container as an environment variable using Woodpecker’s secrets store. First, generate a new deploy token using the Fly web interface or command line: flyctl tokens create deploy -n woodpecker It’s a good idea to use a dedicated token for woodpecker deployments, so if you need to you can revoke it without affecting any other integrations you might have. You might also want to set a shorter expiry time with the -x option (the default is twenty years). Copy or save your newly generated secret somewhere now, because it’s only shown once. Next, create a secret named fly_access_token that contains your access token. You can do this using the Woodpecker web interface or the command line: woodpecker secret add -name fly_access_token -event push -event manual -repository &lt;your_repository&gt; -value '&lt;your_access_token&gt;' Note that we only make the secret available when the workflow is triggered by a push from an authorised contributor to the repository or run manually by somebody with access to Woodpecker CI. Your usage might vary, but you probably should not make it available on pull requests, otherwise a careless or malicious pull request could leak your access token. Deploy That’s it! When you push these changes to your remote repository, Woodpecker should immediately deploy to Fly. You can monitor the status of the deployment in the Woodpecker CI web interface. In the Woodpecker repository settings, under Badge, you’ll also find a badge that you can add to your README to show the status of the last deployment.]]></summary></entry><entry><title type="html">rpaleoclim v1.0.0: paleoclimate data in R</title><link href="https://joeroe.io/2023/05/02/rpaleoclim-1.0.0.html" rel="alternate" type="text/html" title="rpaleoclim v1.0.0: paleoclimate data in R" /><published>2023-05-02T00:00:00+00:00</published><updated>2023-05-02T00:00:00+00:00</updated><id>https://joeroe.io/2023/05/02/rpaleoclim-1.0.0</id><content type="html" xml:base="https://joeroe.io/2023/05/02/rpaleoclim-1.0.0.html"><![CDATA[<p><a href="https://www.paleoclim.org">PaleoClim</a> (<a href="https://doi.org/10.1038/sdata.2018.254">Brown et
al. 2018</a>, <em>Scientific Data</em>) is
a set of high-resolution paleoclimate surfaces covering the whole world.
The data is derived from <a href="https://en.wikipedia.org/wiki/HadCM3">HadCM3</a>,
one of the major ‘general circulation models’ that is used to forecast
climate change, turned backwards to ‘predict’ conditions for key climate
periods in the past. This is then ‘downscaled’ to a high spatial
resolution (up to 2.5 minutes) using modern climate data. A continuous
set of reconstructions from the near-present to the Last Glacial Maximum
(c. 0.3–21 ka, divided into 7 periods) are available, plus snapshots for
the Last Interglacial (c. 130 ka), MIS19 (c. 787 ka), mid-Pliocene
(c. 3.205 Ma and 3.3 Ma). For convenience, modern data from
<a href="https://doi.org/10.1038/sdata.2017.122">CHELSA</a> (used for downscaling)
is also bundled in the same format.</p>

<p>PaleoClim is my go-to paleoclimate dataset for modelling prehistoric
environments because as far as I know it has the best spatial resolution
available for my main period of interest, the Late Pleistocene and Early
Holocene.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> It’s also generally more accessible and easier to use than
‘raw’ GCM predictions and the choice of time slices is a good fit for
prehistoric archaeology.<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> Its API is very simple: from the PaleoClim
website you can download archives for each period at a number of
resolutions, with each archive including a set of GeoTIFFs representing
the <a href="http://www.paleoclim.org/methods/#BIOCLIMS">19 bioclimatic
variables</a> widely used in
ecological modelling.</p>

<p>Alternatively, <strong><a href="https://rpaleoclim.joeroe.io">rpaleoclim</a></strong> is a
simple R package that automates the process of downloading, reading and
cropping PaleoClim data. It’s been availble on GitHub for a few years,
but the latest release v1.0.0 adds support for
<a href="https://cran.r-project.org/web/packages/terra/index.html">terra</a>-format
rasters and also brings the package <a href="https://CRAN.R-project.org/package=rpaleoclim">to
CRAN</a>. This means you can
now install it easily with:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">install.packages</span><span class="p">(</span><span class="s2">"rpaleoclim"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>The way <a href="/2020/11/05/30daymapchallenge-blue.html">I usually use
rpaleoclim</a> is to get the
reconstructions for a particular region for a set of periods. For
example, Europe in the Late Holocene:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="s2">"rpaleoclim"</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="s2">"terra"</span><span class="p">)</span><span class="w">

</span><span class="n">europe</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">-15</span><span class="p">,</span><span class="w"> </span><span class="m">45</span><span class="p">,</span><span class="w"> </span><span class="m">30</span><span class="p">,</span><span class="w"> </span><span class="m">90</span><span class="p">)</span><span class="w">
</span><span class="n">europe_lh</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">paleoclim</span><span class="p">(</span><span class="s2">"lh"</span><span class="p">,</span><span class="w"> </span><span class="s2">"10m"</span><span class="p">,</span><span class="w"> </span><span class="n">region</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">europe</span><span class="p">)</span><span class="w">

</span><span class="n">plot</span><span class="p">(</span><span class="n">europe_lh</span><span class="p">[[</span><span class="s2">"bio_12"</span><span class="p">]],</span><span class="w"> </span><span class="n">main</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"total annual precipitation"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p><img src="/images/rpaleoclim-1.0.0/rpaleoclim-example-1.png" alt="" /><!-- --></p>

<p>Further functionality is explained in the <a href="https://rpaleoclim.joeroe.io/articles/rpaleoclim.html">introduction to
rpaleoclim</a>
vignette. One thing I’d highlight is the caching facility, which my
default uses a temporary directory to store downloaded PaleoClim for the
length of the R session. To keep things reproducible, I recommend using
the <code class="language-plaintext highlighter-rouge">cache_path</code> argument to use a location within your project
directory instead.<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup> That way, if the online versions of the PastClim
data ever disappears or moves, the dataset you rely on will still be
there – something I’ve been caught out by many, many times!</p>

<p>Please note that rpaleoclim simply provides a convenient way to use the
PaleoClim dataset in R; I don’t claim any credit for the actual data. If
you use it in a publication, the PaleoClim authors request that you cite
both their derived dataset (i.e. PaleoClim, <a href="https://doi.org/10.1038/sdata.2018.254">Brown et
al. 2018</a>) and the original
climatologies used. These references are included in the package:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">citation</span><span class="p">(</span><span class="s2">"rpaleoclim"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>## Please cite both PaleoClim and original datasets used.
## 
## PaleoClim and dataset 'mis19':
## 
##   Brown Jason L, et al. "PaleoClim, high spatial resolution
##   paleoclimate surfaces for global land areas". Scientific Data 5.
##   (2018): 180254.
## 
## Datasets 'lh', 'mh', 'eh', 'yds', 'ba' and 'hs1':
## 
##   Fordham Damien A, et al. "PaleoView: a tool for generating continuous
##   climate projections spanning the last 21 000 years at regional and
##   global scales". Ecography 40. (2017): 1348-1358.
## 
## Dataset 'lig':
## 
##   Otto-Bliesner Bette L., et al. "Simulating Arctic Climate Warmth and
##   Icefield Retreat in the Last Interglaciation". Science 311. (2006):
##   1751-1753.
## 
## Dataset 'mpwp':
## 
##   Hill Daniel J. "The non-analogue nature of Pliocene temperature
##   gradients ". Earth and Planetary Science Letters 425. (2015):
##   232-241.
## 
## Dataset 'm2':
## 
##   Dolan Aisling M, et al. "Modelling the enigmatic Late Pliocene
##   Glacial Event — Marine Isotope Stage M2". Global and Planetary Change
##   128. (2015): 47-60.
## 
## Dataset 'cur' and 'lgm' (CHELSA):
## 
##   Karger Dirk Nikolaus, et al. "Climatologies at high resolution for
##   the earth’s land surface areas". Scientific Data 4. (2017): 170122.
## 
## To see these entries in BibTeX format, use 'print(&lt;citation&gt;,
## bibtex=TRUE)', 'toBibtex(.)', or set
## 'options(citation.bibtex.max=999)'.
</code></pre></div></div>

<p>Last year another package,
<a href="https://evolecolgroup.github.io/pastclim/">pastclim</a> was released
offering similar functionality to rpaleoclim. It includes two
datasets—<a href="https://doi.org/10.1038/s41597-020-0552-1">Beyer et al. 2020</a>
and <a href="https://doi.org/10.1038/s41597-021-01009-3">Krapp et al. 2021</a>—that
considerably extends the range of reconstructions, as well as providing
functions for processing new datasets in netCDF format. Unfortunately
the spatial resolution of these datasets is quite coarse compared to
PaleoClim (0.5°), so they’re not so useful for my purposes. So I’ll be
keeping an eye on pastclim, but for the time being at least I intend to
continue developing rpaleoclim. I would like to add support for
complementary datasets like
<a href="https://doi.org/10.1038/s41597-020-00663-3">StableClim</a>. I’m also
unsure about whether to stick with terra for raster data or move to
<a href="https://r-spatial.github.io/stars/">stars</a> – any input on this is very
welcome!</p>

<h2 id="notes">Notes</h2>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>I could well be wrong. It’s been a few years since I actually
looked into it. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>Even better would be
<a href="https://doi.org/10.1111/ecog.03031">PaleoView</a>, which offers
<em>decadal</em> temporal resolution over the last 21,000 years.
Unfortunately, I could never get its Python-based frontend to run on
Linux. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>Following the <a href="https://github.com/benmarwick/rrtools">rrtools</a>
conventions, I use <code class="language-plaintext highlighter-rouge">analyis/data/derived_data</code>. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Joe Roe</name></author><category term="R" /><summary type="html"><![CDATA[PaleoClim (Brown et al. 2018, Scientific Data) is a set of high-resolution paleoclimate surfaces covering the whole world. The data is derived from HadCM3, one of the major ‘general circulation models’ that is used to forecast climate change, turned backwards to ‘predict’ conditions for key climate periods in the past. This is then ‘downscaled’ to a high spatial resolution (up to 2.5 minutes) using modern climate data. A continuous set of reconstructions from the near-present to the Last Glacial Maximum (c. 0.3–21 ka, divided into 7 periods) are available, plus snapshots for the Last Interglacial (c. 130 ka), MIS19 (c. 787 ka), mid-Pliocene (c. 3.205 Ma and 3.3 Ma). For convenience, modern data from CHELSA (used for downscaling) is also bundled in the same format. PaleoClim is my go-to paleoclimate dataset for modelling prehistoric environments because as far as I know it has the best spatial resolution available for my main period of interest, the Late Pleistocene and Early Holocene.1 It’s also generally more accessible and easier to use than ‘raw’ GCM predictions and the choice of time slices is a good fit for prehistoric archaeology.2 Its API is very simple: from the PaleoClim website you can download archives for each period at a number of resolutions, with each archive including a set of GeoTIFFs representing the 19 bioclimatic variables widely used in ecological modelling. Alternatively, rpaleoclim is a simple R package that automates the process of downloading, reading and cropping PaleoClim data. It’s been availble on GitHub for a few years, but the latest release v1.0.0 adds support for terra-format rasters and also brings the package to CRAN. This means you can now install it easily with: install.packages("rpaleoclim") The way I usually use rpaleoclim is to get the reconstructions for a particular region for a set of periods. For example, Europe in the Late Holocene: library("rpaleoclim") library("terra") europe &lt;- c(-15, 45, 30, 90) europe_lh &lt;- paleoclim("lh", "10m", region = europe) plot(europe_lh[["bio_12"]], main = "total annual precipitation") Further functionality is explained in the introduction to rpaleoclim vignette. One thing I’d highlight is the caching facility, which my default uses a temporary directory to store downloaded PaleoClim for the length of the R session. To keep things reproducible, I recommend using the cache_path argument to use a location within your project directory instead.3 That way, if the online versions of the PastClim data ever disappears or moves, the dataset you rely on will still be there – something I’ve been caught out by many, many times! Please note that rpaleoclim simply provides a convenient way to use the PaleoClim dataset in R; I don’t claim any credit for the actual data. If you use it in a publication, the PaleoClim authors request that you cite both their derived dataset (i.e. PaleoClim, Brown et al. 2018) and the original climatologies used. These references are included in the package: citation("rpaleoclim") ## Please cite both PaleoClim and original datasets used. ## ## PaleoClim and dataset 'mis19': ## ## Brown Jason L, et al. "PaleoClim, high spatial resolution ## paleoclimate surfaces for global land areas". Scientific Data 5. ## (2018): 180254. ## ## Datasets 'lh', 'mh', 'eh', 'yds', 'ba' and 'hs1': ## ## Fordham Damien A, et al. "PaleoView: a tool for generating continuous ## climate projections spanning the last 21 000 years at regional and ## global scales". Ecography 40. (2017): 1348-1358. ## ## Dataset 'lig': ## ## Otto-Bliesner Bette L., et al. "Simulating Arctic Climate Warmth and ## Icefield Retreat in the Last Interglaciation". Science 311. (2006): ## 1751-1753. ## ## Dataset 'mpwp': ## ## Hill Daniel J. "The non-analogue nature of Pliocene temperature ## gradients ". Earth and Planetary Science Letters 425. (2015): ## 232-241. ## ## Dataset 'm2': ## ## Dolan Aisling M, et al. "Modelling the enigmatic Late Pliocene ## Glacial Event — Marine Isotope Stage M2". Global and Planetary Change ## 128. (2015): 47-60. ## ## Dataset 'cur' and 'lgm' (CHELSA): ## ## Karger Dirk Nikolaus, et al. "Climatologies at high resolution for ## the earth’s land surface areas". Scientific Data 4. (2017): 170122. ## ## To see these entries in BibTeX format, use 'print(&lt;citation&gt;, ## bibtex=TRUE)', 'toBibtex(.)', or set ## 'options(citation.bibtex.max=999)'. Last year another package, pastclim was released offering similar functionality to rpaleoclim. It includes two datasets—Beyer et al. 2020 and Krapp et al. 2021—that considerably extends the range of reconstructions, as well as providing functions for processing new datasets in netCDF format. Unfortunately the spatial resolution of these datasets is quite coarse compared to PaleoClim (0.5°), so they’re not so useful for my purposes. So I’ll be keeping an eye on pastclim, but for the time being at least I intend to continue developing rpaleoclim. I would like to add support for complementary datasets like StableClim. I’m also unsure about whether to stick with terra for raster data or move to stars – any input on this is very welcome! Notes I could well be wrong. It’s been a few years since I actually looked into it. &#8617; Even better would be PaleoView, which offers decadal temporal resolution over the last 21,000 years. Unfortunately, I could never get its Python-based frontend to run on Linux. &#8617; Following the rrtools conventions, I use analyis/data/derived_data. &#8617;]]></summary></entry><entry><title type="html">What did the first farmers actually farm?</title><link href="https://joeroe.io/2023/04/12/founder-crops.html" rel="alternate" type="text/html" title="What did the first farmers actually farm?" /><published>2023-04-12T00:00:00+00:00</published><updated>2023-04-12T00:00:00+00:00</updated><id>https://joeroe.io/2023/04/12/founder-crops</id><content type="html" xml:base="https://joeroe.io/2023/04/12/founder-crops.html"><![CDATA[<p>Eight so-called <a href="https://en.wikipedia.org/wiki/Founder_crops">founder crops</a>—emmer wheat, einkorn wheat, barley, lentil, pea, chickpea, bitter vetch, and flax—have long been thought to have been the bedrock of Neolithic economies. 
But early prehistoric sites in Southwest Asia keep turning up more ‘exotic’ (to us) species, like the sedge tubers mixed into the <a href="https://doi.org/10.1073/pnas.1801071115">earliest known breads</a> or the <a href="https://doi.org/10.1016/j.jas.2020.105258">new glume wheat</a>, an important ancient crop that’s now extinct.</p>

<figure>
    <img src="/images/founder_crops_wikipedia.png" alt="Illustrations of the eight founder crops: emmer wheat (Triticum turgidum subsp. dicoccum), einkorn wheat (Triticum monococcum), barley (Hordeum vulgare), lentil (Lens culinaris), pea (Pisum sativum), chickpea (Cicer arietinum), bitter vetch (Vicia ervilia), and flax (Linum usitatissimum)" class="align-center" style="width: auto;" />
    <figcaption class="align-center">Illustrations of the eight founder crops. Source: <a href="https://en.wikipedia.org/wiki/Founder_crops">Wikipedia</a>.</figcaption>
</figure>

<p>Is it a coincidence that the founder crops are all species that remains staples today? 
In <a href="/papers/2023/founder_crops">our new paper</a>, Amaia Arranz-Otaegui and I examine what plants actually turn up the most in the remains of the earliest agricultural societies. 
We gathered data on 144 million botanical remains from 135 prehistoric sites across Southwest Asia. 
Our analysis included the Neolithic (c. 9700 to 4500 BCE) and the periods immediately before and after –  10,000 years of plant exploitation.
 We found that Neolithic economies were much more diverse than previously thought, incorporating dozens of species of cereals, legumes, small-seeded grasses, brassicas, pseudocereals, sedges, flowering plants, trees, and shrubs. 
Free-threshing wheat, grass pea, faba bean, and ‘new’ glume wheat were especially widely cultivated.</p>

<figure class="half">
    <img src="/images/founder_crops_fig3.webp" alt="Graphs showing the abundance of the eight most common grasses, pulses, wild plants, and fruits/nuts from 11.7 to 6.5 ka, showing several other species as prominently as the founder crops" />
    <img src="/images/founder_crops_fig4.webp" alt="Graphs showing the ubiquity and abundance of the eight founder crops from 11.7 to 6.5 ka, showing that less than 50% of assemblages contained 4 or more founder crops throughout the period, and that apart from wheat the cross-assemblage abundance of founder crops remained relatively stable at around 30%" />
    <figcaption>Cross-assemblage abundance of the most common plant taxa (left) and ubiquity and abundance of the founder crops (right) through the Neolithic. Source: <a href="https://doi.org/10.5281/zenodo.5911218">Arranz-Otaegui &amp; Roe 2023</a>, figs. 3 and 4.</figcaption>
</figure>

<p>It turns out that the ‘founder crops’ weren’t particularly special at all: rarely cultivated for the first thousand years of the Neolithic, they were never ubiquitous, weren’t the first species to be domesticated, or the first or only to be spread outside of Southwest Asia. 
They did become more common over the course of the Neolithic but, remarkably, this can be explained almost entirely by a steadily increasing reliance on wheat as a staple crop. There’s a story waiting to be told there, I’m sure! 
For us, it no longer makes sense to talk about crop packages. 
Neolithic farmers cultivated, managed and otherwise experimented with a huge diversity of plants. 
Tracing the  <em>individual</em> trajectories of these crops in the accumulated archaeobotanical remains reveals many fascinating new stories.</p>

<p>For more details, see our <a href="https://doi.org/10.1007/s00334-023-00917-1">new open access paper</a> in <em>Vegetation History &amp; Archaeobotany</em>. 
The data and R code supporting the analysis is available on <a href="https://github.com/joeroe/SWAsiaNeolithicFounderCrops">GitHub</a> and <a href="https://doi.org/10.5281/zenodo.5911218">Zenodo</a>.</p>]]></content><author><name>Joe Roe</name></author><summary type="html"><![CDATA[Eight so-called founder crops—emmer wheat, einkorn wheat, barley, lentil, pea, chickpea, bitter vetch, and flax—have long been thought to have been the bedrock of Neolithic economies. But early prehistoric sites in Southwest Asia keep turning up more ‘exotic’ (to us) species, like the sedge tubers mixed into the earliest known breads or the new glume wheat, an important ancient crop that’s now extinct. Illustrations of the eight founder crops. Source: Wikipedia. Is it a coincidence that the founder crops are all species that remains staples today? In our new paper, Amaia Arranz-Otaegui and I examine what plants actually turn up the most in the remains of the earliest agricultural societies. We gathered data on 144 million botanical remains from 135 prehistoric sites across Southwest Asia. Our analysis included the Neolithic (c. 9700 to 4500 BCE) and the periods immediately before and after – 10,000 years of plant exploitation. We found that Neolithic economies were much more diverse than previously thought, incorporating dozens of species of cereals, legumes, small-seeded grasses, brassicas, pseudocereals, sedges, flowering plants, trees, and shrubs. Free-threshing wheat, grass pea, faba bean, and ‘new’ glume wheat were especially widely cultivated. Cross-assemblage abundance of the most common plant taxa (left) and ubiquity and abundance of the founder crops (right) through the Neolithic. Source: Arranz-Otaegui &amp; Roe 2023, figs. 3 and 4. It turns out that the ‘founder crops’ weren’t particularly special at all: rarely cultivated for the first thousand years of the Neolithic, they were never ubiquitous, weren’t the first species to be domesticated, or the first or only to be spread outside of Southwest Asia. They did become more common over the course of the Neolithic but, remarkably, this can be explained almost entirely by a steadily increasing reliance on wheat as a staple crop. There’s a story waiting to be told there, I’m sure! For us, it no longer makes sense to talk about crop packages. Neolithic farmers cultivated, managed and otherwise experimented with a huge diversity of plants. Tracing the individual trajectories of these crops in the accumulated archaeobotanical remains reveals many fascinating new stories. For more details, see our new open access paper in Vegetation History &amp; Archaeobotany. The data and R code supporting the analysis is available on GitHub and Zenodo.]]></summary></entry><entry><title type="html">Graham Hancock is right about pseudoarchaeology on Wikipedia</title><link href="https://joeroe.io/2022/11/14/hancock-wikipedia.html" rel="alternate" type="text/html" title="Graham Hancock is right about pseudoarchaeology on Wikipedia" /><published>2022-11-14T00:00:00+00:00</published><updated>2022-11-14T00:00:00+00:00</updated><id>https://joeroe.io/2022/11/14/hancock-wikipedia</id><content type="html" xml:base="https://joeroe.io/2022/11/14/hancock-wikipedia.html"><![CDATA[<p>In a recent interview on a popular podcast, pseudoarchaeologist <a href="https://en.wikipedia.org/wiki/Graham_Hancock">Graham Hancock</a> had this to say about Wikipedia:</p>

<blockquote>
  <p>[T]he point about Wikipedia is, that’s the first place, when somebody hears my name or hears about my ideas, first place they gonna go have a look is Wikipedia. And immediately they’re gonna get turned off.</p>
</blockquote>

<p>And he’s absolutely right, of course. 
If you’re itching to debunk the racist garbage he spreads in Netflix’s new series <em>Ancient Apocalypse</em>, consider editing Wikipedia. It really makes a difference.</p>

<p>Hancock goes on to say that “you can’t edit my Wikipedia page, they’ve locked it, and it’s controlled by a group of academics”, whichis not true.
Anybody with an account older than four days can edit the article about him. And anyone can edit the articles about the archaeological sites he misrepresents, with or without an account.
Hancock fans regularly do try to distort and whitewash these pages, and the small group of volunteer who edit archaeology-related topics are struggling to stay on top of it. Help from academics and other people with subject-matter expertise is really, really appreciated.</p>

<p>The article on <a href="https://en.wikipedia.org/wiki/Gunung_Padang">Gunung Padang</a>, an Indonesian archaeological site featured in the first episode of the series, has almost no reliably-sourced information on the real archaeology there!
Two other sites Hancock tells tales about are <a href="https://en.wikipedia.org/wiki/G%C4%A7ar_Dalam">Għar Dalam</a> in Malta and <a href="https://en.wikipedia.org/wiki/Derinkuyu">Derinkuyu</a> in Turkey.
These articles are in an okay state, but so short that a viewer might think he’s right when he says archaeologists are ignoring them.
Or there’s <a href="https://en.wikipedia.org/wiki/Göbekli_TG%C3%B6bekli_Tepe">Göbekli Tepe</a>, a favourite of many pseudoarchaeologists (it “changes everything!”). 
There are featured articles on the site in the German and French Wikipedias, but the English one is underdeveloped. So translators could really help here.</p>

<p>I think editing Wikipedia is the <a href="/2022/01/12/wikipedia-pseudoarchaeology.html">most impactful thing</a> we can do for the public understanding of archaeology. But unfortunately, it is sometimes easier said than done. If prefer not to edit directly, writing responses and rebuttals that Wikipedians can cite is also a huge help!</p>

<p><em>Addendum (2022-11-17)</em>: 
The chart below shows the daily pageviews of #Wikipedia’s article on Göbekli Tepe over the last year – spot the <em>Ancient Apocalypse</em> effect! 
It’s even more stark for Gunung Padang, a site that was apparently all but unknown to a general audience before Graham Hancock featured it in the first episode of his pseudoarchaeology documentary.</p>

<figure class="half ">
  
    
      <img src="/images/hancock_wikipedia_gt_views.png" alt="Graph showing the daily page views of the Wikipedia article on Göbekli Tepe over the last year. There is a large spike in November, from less than 5,000 views per day to nearly 20,000 views per day." />
    
  
    
      <img src="/images/hancock_wikipedia_gp_views.png" alt="Graph showing the daily page views of the Wikipedia article on Gunang Padang over the last year. There is a huge spike in November, from less than 1,000 views per day to nearly 30,000 views per day." />
    
  
  
    <figcaption>Daily page views for the English Wikipedia articles on Göbekli Tepe (left) and Gunang Padang (right).
</figcaption>
  
</figure>

<p><small>Adapted from the <a href="https://twitter.com/joeroe90/status/1592092070554238977">original Twitter thread</a> on 2023-04-13.</small></p>]]></content><author><name>Joe Roe</name></author><category term="Wikipedia" /><summary type="html"><![CDATA[In a recent interview on a popular podcast, pseudoarchaeologist Graham Hancock had this to say about Wikipedia: [T]he point about Wikipedia is, that’s the first place, when somebody hears my name or hears about my ideas, first place they gonna go have a look is Wikipedia. And immediately they’re gonna get turned off. And he’s absolutely right, of course. If you’re itching to debunk the racist garbage he spreads in Netflix’s new series Ancient Apocalypse, consider editing Wikipedia. It really makes a difference. Hancock goes on to say that “you can’t edit my Wikipedia page, they’ve locked it, and it’s controlled by a group of academics”, whichis not true. Anybody with an account older than four days can edit the article about him. And anyone can edit the articles about the archaeological sites he misrepresents, with or without an account. Hancock fans regularly do try to distort and whitewash these pages, and the small group of volunteer who edit archaeology-related topics are struggling to stay on top of it. Help from academics and other people with subject-matter expertise is really, really appreciated. The article on Gunung Padang, an Indonesian archaeological site featured in the first episode of the series, has almost no reliably-sourced information on the real archaeology there! Two other sites Hancock tells tales about are Għar Dalam in Malta and Derinkuyu in Turkey. These articles are in an okay state, but so short that a viewer might think he’s right when he says archaeologists are ignoring them. Or there’s Göbekli Tepe, a favourite of many pseudoarchaeologists (it “changes everything!”). There are featured articles on the site in the German and French Wikipedias, but the English one is underdeveloped. So translators could really help here. I think editing Wikipedia is the most impactful thing we can do for the public understanding of archaeology. But unfortunately, it is sometimes easier said than done. If prefer not to edit directly, writing responses and rebuttals that Wikipedians can cite is also a huge help! Addendum (2022-11-17): The chart below shows the daily pageviews of #Wikipedia’s article on Göbekli Tepe over the last year – spot the Ancient Apocalypse effect! It’s even more stark for Gunung Padang, a site that was apparently all but unknown to a general audience before Graham Hancock featured it in the first episode of his pseudoarchaeology documentary. Daily page views for the English Wikipedia articles on Göbekli Tepe (left) and Gunang Padang (right). Adapted from the original Twitter thread on 2023-04-13.]]></summary></entry><entry><title type="html">Wikipedia is the best way to counter pseudoarchaeology</title><link href="https://joeroe.io/2022/01/12/wikipedia-pseudoarchaeology.html" rel="alternate" type="text/html" title="Wikipedia is the best way to counter pseudoarchaeology" /><published>2022-01-12T00:00:00+00:00</published><updated>2022-01-12T00:00:00+00:00</updated><id>https://joeroe.io/2022/01/12/wikipedia-pseudoarchaeology</id><content type="html" xml:base="https://joeroe.io/2022/01/12/wikipedia-pseudoarchaeology.html"><![CDATA[<p>I was glad to see a brief nod to @Wikipedia in a recent <em>Input</em> article on <a href="https://www.inverse.com/input/culture/alternative-historians-youtube-who-built-pyramids-not-aliens">pseudoarchaeology in social media</a>.
Its importance in countering pseudoarchaeology and disinformation about the past is often neglected.</p>

<p>Ask yourself: what do you do when you encounter a new idea and want to check it or read about it further? 
For most people, the answer is “look it up on Wikipedia”. 
Editing it is one of the most effective public engagement tools out there, and has almost no barrier to entry.
Pseudoarchaeologists know this very well. They relentlessly try to add their newest theories to articles, rewrite biographies of advocates to remove criticism and make them sound more legitimate, and ‘edit war’ with regular editors to keep their changes.</p>

<p>On the other side you have a small group of volunteer editors, almost all <em>not</em> professional archaeologists, fighting to keep articles in line with mainstream science. It can seem like a thankless task, but it works.
To take a petty-but-satisfying example from the <em>Input</em> article: “When Graham Hancock’s Wikipedia page was edited in 2019 to include references to him engaging in pseudoarchaeology, it set off a stream of furious commentary […] calling it ‘attempted character assassination.’”</p>

<p>But Wikipedia editors rely on academics to give them the ammunition for this battle: “editors look to the consensus of professional archaeologists in deciding whether to label something ‘pseudoarchaeology’ or list it as a credible theory”.
verything on Wikipedia must be cited to ‘reliable sources’ (e.g. journal articles). If there are no critical reliable sources, then its editors cannot fight pseudoarchaeology effectively. And they cannot create those sources themselves: that’s the job of archaeologists.
This is why the widespread belief that “giving alternative theories professional attention only serves to legitimize them” is so frustrating. As an archaeologist, <em>you</em> might know that the latest ‘alternative theory’ is rubbish. But others probably can’t spot that without help.
Writing rebuttals and critiques to pseudoarchaeology might seem like a waste of time, but if archaeologists don’t use make their knowledge available to Wikipedians, there is no way that Wikipedians can make it available to the public.</p>

<p>As an archaeologist, you don’t need a big social media following to effectively combat pseudoarchaeology. Just use the tools that are available to you—your privilaged ability to produce ‘reliable sources’ in your area of expertise—and Wikipedia editors will do the rest.</p>

<p><small>Adapted from the <a href="https://twitter.com/joeroe90/status/1481313710564122631">original Twitter thread</a> on 2023-04-13.</small></p>]]></content><author><name>Joe Roe</name></author><category term="Wikipedia" /><summary type="html"><![CDATA[I was glad to see a brief nod to @Wikipedia in a recent Input article on pseudoarchaeology in social media. Its importance in countering pseudoarchaeology and disinformation about the past is often neglected. Ask yourself: what do you do when you encounter a new idea and want to check it or read about it further? For most people, the answer is “look it up on Wikipedia”. Editing it is one of the most effective public engagement tools out there, and has almost no barrier to entry. Pseudoarchaeologists know this very well. They relentlessly try to add their newest theories to articles, rewrite biographies of advocates to remove criticism and make them sound more legitimate, and ‘edit war’ with regular editors to keep their changes. On the other side you have a small group of volunteer editors, almost all not professional archaeologists, fighting to keep articles in line with mainstream science. It can seem like a thankless task, but it works. To take a petty-but-satisfying example from the Input article: “When Graham Hancock’s Wikipedia page was edited in 2019 to include references to him engaging in pseudoarchaeology, it set off a stream of furious commentary […] calling it ‘attempted character assassination.’” But Wikipedia editors rely on academics to give them the ammunition for this battle: “editors look to the consensus of professional archaeologists in deciding whether to label something ‘pseudoarchaeology’ or list it as a credible theory”. verything on Wikipedia must be cited to ‘reliable sources’ (e.g. journal articles). If there are no critical reliable sources, then its editors cannot fight pseudoarchaeology effectively. And they cannot create those sources themselves: that’s the job of archaeologists. This is why the widespread belief that “giving alternative theories professional attention only serves to legitimize them” is so frustrating. As an archaeologist, you might know that the latest ‘alternative theory’ is rubbish. But others probably can’t spot that without help. Writing rebuttals and critiques to pseudoarchaeology might seem like a waste of time, but if archaeologists don’t use make their knowledge available to Wikipedians, there is no way that Wikipedians can make it available to the public. As an archaeologist, you don’t need a big social media following to effectively combat pseudoarchaeology. Just use the tools that are available to you—your privilaged ability to produce ‘reliable sources’ in your area of expertise—and Wikipedia editors will do the rest. Adapted from the original Twitter thread on 2023-04-13.]]></summary></entry><entry><title type="html">Dodgy radiocarbon modelling at Tell el-Hammam (not-Sodom)</title><link href="https://joeroe.io/2021/09/22/tell-el-hammam-radiocarbon.html" rel="alternate" type="text/html" title="Dodgy radiocarbon modelling at Tell el-Hammam (not-Sodom)" /><published>2021-09-22T00:00:00+00:00</published><updated>2021-09-22T00:00:00+00:00</updated><id>https://joeroe.io/2021/09/22/tell-el-hammam-radiocarbon</id><content type="html" xml:base="https://joeroe.io/2021/09/22/tell-el-hammam-radiocarbon.html"><![CDATA[<p>A <a href="https://www.nature.com/articles/s41598-021-97778-3">recent paper</a> in the Nature Group’s <em>Scientific Reports</em> claimed that <a href="https://en.wikipedia.org/wiki/Tell_el-Hammam">Tell el-Hammam</a>, a prehistoric site in Jordan, was destroyed by a “Tunguska-sized airburst” around 1650 BCE – strongly implying it is therefore the site of the biblical Sodom.
The paper has been roundly debunked on social media, most notably by impact physicist Mark Boslough, who has exposed the <a href="https://twitter.com/MarkBoslough/status/1440377970497966089">murky backgrounds of the authors</a>.
I can add that the radiocarbon modelling is also completely incoherent.</p>

<p>The authors say they used <a href="https://c14.arch.ox.ac.uk/oxcal.html">OxCal’s</a> “Combine routine that statistically tests the hypothesis that multiple radiocarbon dates relate to the same event” which “determined that 20 of 26 14C dates are statistically synchronous and likely represent a single event”.
In fact, ‘Combine’ is used to combine (see the <a href="https://c14.arch.ox.ac.uk/oxcalhelp/hlp_commands.html">OxCal manual</a>; but the clue is in the name) dates that you <em>already know</em> represent a single event. In other words it says, if we assume all these radiocarbon dates are really the same date, what’s the best estimate of what that date is?
It doesn’t “test” anything because, big surprise: if you build a model with the assumption that all these things happened at the same time, that model is going to tell you that they all happened at the same time.
You only have to eyeball the un-modelled dates (the light grey curves in the background of their figure below) to see that the dates from their “destruction layer” could actually have taken anything up to 300 years to accumulate.</p>

<figure class="half">
    <img src="/images/tell_el_hammam_original_model.jpg" />
    <img src="/images/tell_el_hammam_revised_model.png" />
    <figcaption>Modelled radiocarbon dates from Tell el-Hammam. Left: Bunch et al.'s original model, with the assumption that dates represent the same event. Right: a revised model, with the assumption that they simply belong to the same phase.</figcaption>
</figure>

<p>So their ‘model’ of the radiocarbon dates actually just regurgitated their assumption that all their dates related to the same catastrophic event.
But it’s not hard to model this data properly.
We can just run the dates with a more reasonable set of assumptions: that the radiocarbon samples are related to each other (because they were found in the same archaeological layer), but that we don’t know a priori how long that (1.5 m-thick!) layer took to form.</p>

<p>With this model, the so-called “burn layer” dates to somewhere between 1770 and 1575 BCE. OxCal also tells us that it could have taken anywhere up to 155 years to accumulate (with 95% probability).
You could probably narrow it down a bit further with dates from layers above and below the event of interest, but, as the authors note and then ignore, radiocarbon dating has its limitations and will never be precise to more than a few decades.</p>

<p>This matters because the signs of high-temperature burning they point to can also be produced by infrequent but normal events like lightning strikes or furnace fires. If you are analysing <em>150 years</em> of accumulated sediment, it is obviously much more likely you will find these.
Similarly the evidence for the ‘destruction’ of the site, like the fragments of bone scattered around (which as Chris Stantis
 explains is <a href="https://twitter.com/ChrisStantis/status/1440404380386160646">already not that much for a tell site</a>), looks even more like normal debris and decay if it built up slowly.</p>

<p>I’m not qualified to judge the geophysical evidence for an impact (Boslough is, and doesn’t think much of it).
Even if the geophysical evidence is there, it rests on the assumption that all the samples they looked at came from the same event but based on the radiocarbon and archaeological evidence in the paper, the so-called “destruction layer” or “burn layer” just looks like the kind of slow collapse of buildings and accumulation of trash that happens whenever a site (or part of a site) is abandoned for a while.</p>

<p><small>Adapted from the original Twitter threads (<a href="https://twitter.com/joeroe90/status/1440443321894064136">1</a>, <a href="https://twitter.com/joeroe90/status/1440624311417524224">2</a>) on 2023-04-13.</small></p>]]></content><author><name>Joe Roe</name></author><summary type="html"><![CDATA[A recent paper in the Nature Group’s Scientific Reports claimed that Tell el-Hammam, a prehistoric site in Jordan, was destroyed by a “Tunguska-sized airburst” around 1650 BCE – strongly implying it is therefore the site of the biblical Sodom. The paper has been roundly debunked on social media, most notably by impact physicist Mark Boslough, who has exposed the murky backgrounds of the authors. I can add that the radiocarbon modelling is also completely incoherent. The authors say they used OxCal’s “Combine routine that statistically tests the hypothesis that multiple radiocarbon dates relate to the same event” which “determined that 20 of 26 14C dates are statistically synchronous and likely represent a single event”. In fact, ‘Combine’ is used to combine (see the OxCal manual; but the clue is in the name) dates that you already know represent a single event. In other words it says, if we assume all these radiocarbon dates are really the same date, what’s the best estimate of what that date is? It doesn’t “test” anything because, big surprise: if you build a model with the assumption that all these things happened at the same time, that model is going to tell you that they all happened at the same time. You only have to eyeball the un-modelled dates (the light grey curves in the background of their figure below) to see that the dates from their “destruction layer” could actually have taken anything up to 300 years to accumulate. Modelled radiocarbon dates from Tell el-Hammam. Left: Bunch et al.'s original model, with the assumption that dates represent the same event. Right: a revised model, with the assumption that they simply belong to the same phase. So their ‘model’ of the radiocarbon dates actually just regurgitated their assumption that all their dates related to the same catastrophic event. But it’s not hard to model this data properly. We can just run the dates with a more reasonable set of assumptions: that the radiocarbon samples are related to each other (because they were found in the same archaeological layer), but that we don’t know a priori how long that (1.5 m-thick!) layer took to form. With this model, the so-called “burn layer” dates to somewhere between 1770 and 1575 BCE. OxCal also tells us that it could have taken anywhere up to 155 years to accumulate (with 95% probability). You could probably narrow it down a bit further with dates from layers above and below the event of interest, but, as the authors note and then ignore, radiocarbon dating has its limitations and will never be precise to more than a few decades. This matters because the signs of high-temperature burning they point to can also be produced by infrequent but normal events like lightning strikes or furnace fires. If you are analysing 150 years of accumulated sediment, it is obviously much more likely you will find these. Similarly the evidence for the ‘destruction’ of the site, like the fragments of bone scattered around (which as Chris Stantis explains is already not that much for a tell site), looks even more like normal debris and decay if it built up slowly. I’m not qualified to judge the geophysical evidence for an impact (Boslough is, and doesn’t think much of it). Even if the geophysical evidence is there, it rests on the assumption that all the samples they looked at came from the same event but based on the radiocarbon and archaeological evidence in the paper, the so-called “destruction layer” or “burn layer” just looks like the kind of slow collapse of buildings and accumulation of trash that happens whenever a site (or part of a site) is abandoned for a while. Adapted from the original Twitter threads (1, 2) on 2023-04-13.]]></summary></entry><entry><title type="html">era 0.3.1: year-based time scales in R</title><link href="https://joeroe.io/2021/02/07/era-0.3.1.html" rel="alternate" type="text/html" title="era 0.3.1: year-based time scales in R" /><published>2021-02-07T00:00:00+00:00</published><updated>2021-02-07T00:00:00+00:00</updated><id>https://joeroe.io/2021/02/07/era-0.3.1</id><content type="html" xml:base="https://joeroe.io/2021/02/07/era-0.3.1.html"><![CDATA[<p><strong><a href="https://era.joeroe.io">era</a></strong> v0.3.1 is now available <a href="https://CRAN.R-project.org/package=era">on CRAN</a>:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">install.packages</span><span class="p">(</span><span class="s2">"era"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>I wrote about the basic concept of the package <a href="https://joeroe.io/2020/11/12/era-beta.html">in my last post</a>.
It provides a representation of year-based time scales in R, built around two core classes: <a href="https://era.joeroe.io/reference/yr.html">yr</a>, a vector of years, and <a href="https://era.joeroe.io/reference/era.html">era</a>, defining the parameters that relate those years to a moment in time.
The idea is to make it easier to handle different era systems in a tidy analysis, and convert between them, as described in the <a href="https://era.joeroe.io/articles/era.html">introductory vignette</a>:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="s2">"tibble"</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="s2">"dplyr"</span><span class="p">)</span><span class="w">

</span><span class="n">tribble</span><span class="p">(</span><span class="w">
  </span><span class="o">~</span><span class="n">period</span><span class="p">,</span><span class="w">           </span><span class="o">~</span><span class="n">start_ka</span><span class="p">,</span><span class="w">
  </span><span class="s2">"Late Holocene"</span><span class="p">,</span><span class="w">   </span><span class="m">4.2</span><span class="p">,</span><span class="w">
  </span><span class="s2">"Mid Holocene"</span><span class="p">,</span><span class="w">    </span><span class="m">8.326</span><span class="p">,</span><span class="w">
  </span><span class="s2">"Early Holocene"</span><span class="p">,</span><span class="w">  </span><span class="m">11.7</span><span class="p">,</span><span class="w">
  </span><span class="s2">"Younger Dryas"</span><span class="p">,</span><span class="w">   </span><span class="m">12.9</span><span class="p">,</span><span class="w">
  </span><span class="s2">"Bølling-Allerød"</span><span class="p">,</span><span class="w"> </span><span class="m">14.7</span><span class="p">,</span><span class="w">
  </span><span class="s2">"Heinrich 1"</span><span class="p">,</span><span class="w">      </span><span class="m">17.0</span><span class="w">
</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> 
  </span><span class="n">mutate</span><span class="p">(</span><span class="n">start_ka</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">yr</span><span class="p">(</span><span class="n">start_ka</span><span class="p">,</span><span class="w"> </span><span class="s2">"ka"</span><span class="p">))</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> 
  </span><span class="n">mutate</span><span class="p">(</span><span class="n">start_bp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">yr_transform</span><span class="p">(</span><span class="n">start_ka</span><span class="p">,</span><span class="w"> </span><span class="n">era</span><span class="p">(</span><span class="s2">"BP"</span><span class="p">)),</span><span class="w">
         </span><span class="n">start_bce</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">yr_transform</span><span class="p">(</span><span class="n">start_ka</span><span class="p">,</span><span class="w"> </span><span class="n">era</span><span class="p">(</span><span class="s2">"BCE"</span><span class="p">)))</span><span class="w">
</span><span class="c1">#&gt; # A tibble: 6 x 4</span><span class="w">
</span><span class="c1">#&gt;   period          start_ka start_bp start_bce</span><span class="w">
</span><span class="c1">#&gt;   &lt;chr&gt;               &lt;yr&gt;     &lt;yr&gt;      &lt;yr&gt;</span><span class="w">
</span><span class="c1">#&gt; 1 Late Holocene     4.2 ka  4200 BP  2250 BCE</span><span class="w">
</span><span class="c1">#&gt; 2 Mid Holocene    8.326 ka  8326 BP  6376 BCE</span><span class="w">
</span><span class="c1">#&gt; 3 Early Holocene   11.7 ka 11700 BP  9750 BCE</span><span class="w">
</span><span class="c1">#&gt; 4 Younger Dryas    12.9 ka 12900 BP 10950 BCE</span><span class="w">
</span><span class="c1">#&gt; 5 Bølling-Allerød  14.7 ka 14700 BP 12750 BCE</span><span class="w">
</span><span class="c1">#&gt; 6 Heinrich 1         17 ka 17000 BP 15050 BCE</span><span class="w">
</span></code></pre></div></div>

<p>The major changes since the beta release (v0.2.0) flesh out the definition of an “era” so that a broader range of time scales and calendric year numbering systems can be represented as <code class="language-plaintext highlighter-rouge">yr</code> vectors:</p>

<ul>
  <li><a href="https://era.joeroe.io/reference/yr_transform.html">yr_transform()</a> can now handle eras with different units.</li>
  <li>‘Year units’ are now explicitly defined as the length of a year in days, with a new class <a href="https://era.joeroe.io/reference/era_year.html">era_year</a>.</li>
  <li>Built-in definitions for lunar and solar <em>hijra</em> year numbering systems, and the Julian calendar, are now included (as examples of era systems that use different units).</li>
</ul>

<p>These and other minor changes are described in full in the <a href="https://era.joeroe.io/news/index.html">changelog</a>.
In the rest of this post, I want to explain the concepts behind this representation of years.</p>

<h2 id="years-as-a-real-number-time-scale">Years as a real number time scale</h2>

<p>era is concerned with year-based <em>time scales</em>; a measurement of the length of time elapsed between one instant and another, counted in years.
This is the sense used by archaeologists when they say an object is about 5,000 years old, or geologists when they say the Holocene started 11.7 thousand years ago.
It’s not quite the same as the everyday notion of a year, which is date (or part of a date), describing a span of time on a calendar.</p>

<p>To tie a set of numbers to a concrete measurement of time, we need to know:</p>

<ul>
  <li>the <strong>epoch</strong> – the datum instant from which measurements are made, which could be a calendar epoch (e.g. the birth of Jesus in the Common Era) or some other conventionally-agreed point in time (e.g. the “Present” in radiometric dating);</li>
  <li>the <strong>year unit</strong> used;</li>
  <li>the <strong>direction</strong> in which years are counted, i.e. forwards or backwards from the epoch;</li>
  <li>the multiplicative <strong>scale</strong> used, when measurements are made in e.g. thousands of years rather than single years.</li>
</ul>

<p>Together with two attributes used human-friendly printing (<code class="language-plaintext highlighter-rouge">name</code> and <code class="language-plaintext highlighter-rouge">label</code>), these four parameters comprise the <code class="language-plaintext highlighter-rouge">era</code> attribute of a <code class="language-plaintext highlighter-rouge">yr</code> vector in era.
They also enable us to derive a generalised algorithm for transforming years between different era systems (<code class="language-plaintext highlighter-rouge">yr_transform()</code>).</p>

<p>Rather than something decomposable to a set of calendar dates (12 months, 365 days, etc.), <code class="language-plaintext highlighter-rouge">yr</code> vectors are treated as a real number scale with a year as its base unit.
For example, it makes sense to think about both fractional years and negative years:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="s2">"era"</span><span class="p">)</span><span class="w">
</span><span class="n">frac_year</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="nf">as.numeric</span><span class="p">(</span><span class="n">format</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="s2">"%Y"</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="nf">as.numeric</span><span class="p">(</span><span class="n">format</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="s2">"%j"</span><span class="p">))</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="m">365.2425</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="c1"># It is now:</span><span class="w">
</span><span class="n">yr</span><span class="p">(</span><span class="n">frac_year</span><span class="p">(</span><span class="n">Sys.Date</span><span class="p">()),</span><span class="w"> </span><span class="s2">"CE"</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # CE years &lt;yr[1]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 2021.104</span><span class="w">
</span><span class="c1">#&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0</span><span class="w">

</span><span class="c1"># Julius Caesar was born:</span><span class="w">
</span><span class="n">yr_transform</span><span class="p">(</span><span class="n">yr</span><span class="p">(</span><span class="m">100</span><span class="p">,</span><span class="w"> </span><span class="s2">"BCE"</span><span class="p">),</span><span class="w"> </span><span class="s2">"CE"</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # CE years &lt;yr[1]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] -100</span><span class="w">
</span><span class="c1">#&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0</span><span class="w">
</span></code></pre></div></div>

<p>Unfortunately, a “year” is not a single or well-defined unit.
era used the <code class="language-plaintext highlighter-rouge">era_year</code> class to represents different year units, defined as their average length in days.
Although some units (e.g. the astronomical Julian year) can be precisely defined in this way, for most it is an approximation.
For the most part we can get away with this because we are not concerned with exact dates, but it does have implications for the accuracy of some transformations, especially when they involve calendar eras.</p>

<h2 id="time-scales-and-calendars">Time scales and calendars</h2>

<p>In addition to “Before Present”-style eras used by palaeoscientists, era supports, and includes built-in definitions for, year numbering systems from contemporary and historic calendars.
But since the package treats years as a time scale, not as dates, it can only represents the <em>year numbering</em> components of these calendars.
If we were to implement the modern <a href="https://web.archive.org/web/20050311055900/http://wwwusr.obspm.fr/~heydari/divers/ir-cal-eng.html">Iranian calendar</a> as a <code class="language-plaintext highlighter-rouge">Solar Hijri (SH)</code> era, for example, we would only consider its:</p>

<ul>
  <li>Epoch for year numbering – the day of <em>Nowruz</em> (the vernal equinox) in the year 622 CE. Since epochs are defined in <code class="language-plaintext highlighter-rouge">era("CE")</code>, we define this as approximately <code class="language-plaintext highlighter-rouge">622.2218</code>, and subtract 1 because the numbering started at 1 SH.</li>
  <li>Year unit – the length of time between two <em>Nowruz</em>, which is close but not exactly the same as a Gregorian year. We define this as approximately <code class="language-plaintext highlighter-rouge">365.2424</code> days, based on astronomical data from the last few millennia.</li>
  <li>Direction and scale</li>
</ul>

<p>This gives us:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sh</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">era</span><span class="p">(</span><span class="n">label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"SH"</span><span class="p">,</span><span class="w">
          </span><span class="n">epoch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">622.2218</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">,</span><span class="w">
          </span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Solar Hijri"</span><span class="p">,</span><span class="w">
          </span><span class="n">unit</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">era_year</span><span class="p">(</span><span class="s2">"Nowruz"</span><span class="p">,</span><span class="w"> </span><span class="m">365.2424</span><span class="p">),</span><span class="w">
          </span><span class="n">scale</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">,</span><span class="w">
          </span><span class="n">direction</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>These parameters should lead to a good approximation of the transformation between the <code class="language-plaintext highlighter-rouge">SH</code> era we defined and others, especially over longer time scales.
But it isn’t precise, which we might see if we try to distinguish points close in time.
For example, transforming the current beginning of this year in the Common Era (<code class="language-plaintext highlighter-rouge">yr(2021.0, "CE")</code>) into our new era gives us:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">yr_transform</span><span class="p">(</span><span class="n">yr</span><span class="p">(</span><span class="m">2021</span><span class="p">,</span><span class="w"> </span><span class="s2">"CE"</span><span class="p">),</span><span class="w"> </span><span class="n">sh</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # SH years &lt;yr[1]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 1399.779</span><span class="w">
</span><span class="c1">#&gt; # Era: Solar Hijri (SH): Nowruz years (365.2424 days), counted forwards from 621.2218</span><span class="w">
</span></code></pre></div></div>

<p>In reality the 1 January 2021 was 12 Dey 1399.
1399 is a leap year, with 366 days, and 12 Dey is the 228th day of the year, so this is close to the value we would expect: <code class="language-plaintext highlighter-rouge">1399.787</code>.</p>

<h2 id="next-release">Next release</h2>

<p>For the next release of era, I plan to focus on the built-in era definitions, especially those derived from contemporary calendar systems.
The development version already includes tweaks to the definitions of the Islamic calendars that have made the conversion from Gregorian years more accurate:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">this_year</span><span class="p">()</span><span class="w">
</span><span class="c1">#&gt; # CE years &lt;yr[1]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 2021</span><span class="w">
</span><span class="c1">#&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0</span><span class="w">

</span><span class="n">this_year</span><span class="p">(</span><span class="s2">"AH"</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # AH years &lt;yr[1]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 1442</span><span class="w">
</span><span class="c1">#&gt; # Era: Anno Hegirae (AH): Islamic lunar years (354.36708 days), counted forwards from 621.539367680377</span><span class="w">

</span><span class="n">this_year</span><span class="p">(</span><span class="s2">"SH"</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # SH years &lt;yr[1]&gt;:</span><span class="w">
</span><span class="c1">#&gt; [1] 1399</span><span class="w">
</span><span class="c1">#&gt; # Era: Solar Hijri (SH): Nowruz years (365.2424 days), counted forwards from 621.221770467566</span><span class="w">
</span></code></pre></div></div>

<p>If you have any suggestions of eras to include, please do <a href="https://github.com/joeroe/era/issues">submit an issue</a> on GitHub.</p>

<h2 id="links">Links</h2>

<ul>
  <li><a href="https://CRAN.R-project.org/package=era">era on CRAN</a></li>
  <li><a href="https://era.joeroe.io/">era package documentation</a></li>
  <li><a href="https://era.joeroe.io/articles/era.html">Introductory vignette</a></li>
  <li><a href="https://github.com/joeroe/era">Source code</a> (GitHub)</li>
</ul>]]></content><author><name>Joe Roe</name></author><category term="R" /><summary type="html"><![CDATA[era v0.3.1 is now available on CRAN: install.packages("era") I wrote about the basic concept of the package in my last post. It provides a representation of year-based time scales in R, built around two core classes: yr, a vector of years, and era, defining the parameters that relate those years to a moment in time. The idea is to make it easier to handle different era systems in a tidy analysis, and convert between them, as described in the introductory vignette: library("tibble") library("dplyr") tribble( ~period, ~start_ka, "Late Holocene", 4.2, "Mid Holocene", 8.326, "Early Holocene", 11.7, "Younger Dryas", 12.9, "Bølling-Allerød", 14.7, "Heinrich 1", 17.0 ) %&gt;% mutate(start_ka = yr(start_ka, "ka")) %&gt;% mutate(start_bp = yr_transform(start_ka, era("BP")), start_bce = yr_transform(start_ka, era("BCE"))) #&gt; # A tibble: 6 x 4 #&gt; period start_ka start_bp start_bce #&gt; &lt;chr&gt; &lt;yr&gt; &lt;yr&gt; &lt;yr&gt; #&gt; 1 Late Holocene 4.2 ka 4200 BP 2250 BCE #&gt; 2 Mid Holocene 8.326 ka 8326 BP 6376 BCE #&gt; 3 Early Holocene 11.7 ka 11700 BP 9750 BCE #&gt; 4 Younger Dryas 12.9 ka 12900 BP 10950 BCE #&gt; 5 Bølling-Allerød 14.7 ka 14700 BP 12750 BCE #&gt; 6 Heinrich 1 17 ka 17000 BP 15050 BCE The major changes since the beta release (v0.2.0) flesh out the definition of an “era” so that a broader range of time scales and calendric year numbering systems can be represented as yr vectors: yr_transform() can now handle eras with different units. ‘Year units’ are now explicitly defined as the length of a year in days, with a new class era_year. Built-in definitions for lunar and solar hijra year numbering systems, and the Julian calendar, are now included (as examples of era systems that use different units). These and other minor changes are described in full in the changelog. In the rest of this post, I want to explain the concepts behind this representation of years. Years as a real number time scale era is concerned with year-based time scales; a measurement of the length of time elapsed between one instant and another, counted in years. This is the sense used by archaeologists when they say an object is about 5,000 years old, or geologists when they say the Holocene started 11.7 thousand years ago. It’s not quite the same as the everyday notion of a year, which is date (or part of a date), describing a span of time on a calendar. To tie a set of numbers to a concrete measurement of time, we need to know: the epoch – the datum instant from which measurements are made, which could be a calendar epoch (e.g. the birth of Jesus in the Common Era) or some other conventionally-agreed point in time (e.g. the “Present” in radiometric dating); the year unit used; the direction in which years are counted, i.e. forwards or backwards from the epoch; the multiplicative scale used, when measurements are made in e.g. thousands of years rather than single years. Together with two attributes used human-friendly printing (name and label), these four parameters comprise the era attribute of a yr vector in era. They also enable us to derive a generalised algorithm for transforming years between different era systems (yr_transform()). Rather than something decomposable to a set of calendar dates (12 months, 365 days, etc.), yr vectors are treated as a real number scale with a year as its base unit. For example, it makes sense to think about both fractional years and negative years: library("era") frac_year &lt;- function(x) { as.numeric(format(x, "%Y")) + (as.numeric(format(x, "%j")) / 365.2425) } # It is now: yr(frac_year(Sys.Date()), "CE") #&gt; # CE years &lt;yr[1]&gt;: #&gt; [1] 2021.104 #&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0 # Julius Caesar was born: yr_transform(yr(100, "BCE"), "CE") #&gt; # CE years &lt;yr[1]&gt;: #&gt; [1] -100 #&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0 Unfortunately, a “year” is not a single or well-defined unit. era used the era_year class to represents different year units, defined as their average length in days. Although some units (e.g. the astronomical Julian year) can be precisely defined in this way, for most it is an approximation. For the most part we can get away with this because we are not concerned with exact dates, but it does have implications for the accuracy of some transformations, especially when they involve calendar eras. Time scales and calendars In addition to “Before Present”-style eras used by palaeoscientists, era supports, and includes built-in definitions for, year numbering systems from contemporary and historic calendars. But since the package treats years as a time scale, not as dates, it can only represents the year numbering components of these calendars. If we were to implement the modern Iranian calendar as a Solar Hijri (SH) era, for example, we would only consider its: Epoch for year numbering – the day of Nowruz (the vernal equinox) in the year 622 CE. Since epochs are defined in era("CE"), we define this as approximately 622.2218, and subtract 1 because the numbering started at 1 SH. Year unit – the length of time between two Nowruz, which is close but not exactly the same as a Gregorian year. We define this as approximately 365.2424 days, based on astronomical data from the last few millennia. Direction and scale This gives us: sh &lt;- era(label = "SH", epoch = 622.2218 - 1, name = "Solar Hijri", unit = era_year("Nowruz", 365.2424), scale = 1, direction = 1) These parameters should lead to a good approximation of the transformation between the SH era we defined and others, especially over longer time scales. But it isn’t precise, which we might see if we try to distinguish points close in time. For example, transforming the current beginning of this year in the Common Era (yr(2021.0, "CE")) into our new era gives us: yr_transform(yr(2021, "CE"), sh) #&gt; # SH years &lt;yr[1]&gt;: #&gt; [1] 1399.779 #&gt; # Era: Solar Hijri (SH): Nowruz years (365.2424 days), counted forwards from 621.2218 In reality the 1 January 2021 was 12 Dey 1399. 1399 is a leap year, with 366 days, and 12 Dey is the 228th day of the year, so this is close to the value we would expect: 1399.787. Next release For the next release of era, I plan to focus on the built-in era definitions, especially those derived from contemporary calendar systems. The development version already includes tweaks to the definitions of the Islamic calendars that have made the conversion from Gregorian years more accurate: this_year() #&gt; # CE years &lt;yr[1]&gt;: #&gt; [1] 2021 #&gt; # Era: Common Era (CE): Gregorian years (365.2425 days), counted forwards from 0 this_year("AH") #&gt; # AH years &lt;yr[1]&gt;: #&gt; [1] 1442 #&gt; # Era: Anno Hegirae (AH): Islamic lunar years (354.36708 days), counted forwards from 621.539367680377 this_year("SH") #&gt; # SH years &lt;yr[1]&gt;: #&gt; [1] 1399 #&gt; # Era: Solar Hijri (SH): Nowruz years (365.2424 days), counted forwards from 621.221770467566 If you have any suggestions of eras to include, please do submit an issue on GitHub. Links era on CRAN era package documentation Introductory vignette Source code (GitHub)]]></summary></entry><entry><title type="html">Beta release of era</title><link href="https://joeroe.io/2020/11/12/era-beta.html" rel="alternate" type="text/html" title="Beta release of era" /><published>2020-11-12T00:00:00+00:00</published><updated>2020-11-12T00:00:00+00:00</updated><id>https://joeroe.io/2020/11/12/era-beta</id><content type="html" xml:base="https://joeroe.io/2020/11/12/era-beta.html"><![CDATA[<p><strong><a href="https://era.joeroe.io">era</a></strong> is a small R package for working with different year numbering systems.
There are a few systems in use in archaeology, geology, and other palaeosciences;
for example, the year 10,000 BCE is 11,950 <a href="https://en.wikipedia.org/wiki/Before_Present">Before Present</a> or 11.95 <a href="https://en.wikipedia.org/wiki/Year#Symbols">ka</a>.
It is usually fine to store years as a plain numeric vector in R, but sometimes it helps to be explicit about which system is being used:</p>

<ul>
  <li>When you have data that mixes different systems</li>
  <li>When you want to transform years between different systems</li>
  <li>When you need to do arithmetic with years</li>
</ul>

<p>The era package helps in these cases by providing classes which define the ‘era’ associated with a vector of years and functions for formatting, combining, and transforming years with different eras.
I wrote it to support two other packages I’m working on, <a href="https://stratigraphr.joeroe.io">stratigraphr</a> and <a href="https://rintchron.joeroe.io">rintchron</a>, because whilst converting between any two eras (e.g. BP→BCE) is trivial, I wanted something more generalised and predictable when encountering lots of them.
It was also a good way to learn more about <a href="https://vctrs.r-lib.org/">vctrs</a>, the new backend package used to implement S3 vector classes in the tidyverse, which I found very useful for getting a grip on how size and type stability works (or doesn’t work) in R at a low level, and how to write classes that behave consistently and intuitively.</p>

<p>I released the first beta version of era today. You can install it from GitHub with the <a href="https://remotes.r-lib.org/">remotes</a> package:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">remotes</span><span class="o">::</span><span class="n">install_github</span><span class="p">(</span><span class="s2">"joeroe/era"</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="s2">"era"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>I plan to submit the next release to CRAN; so please, try it out and <a href="https://github.com/joeroe/era/issues">submit an issue</a> if you find any bugs!</p>

<h2 id="using-era">Using era</h2>

<p>The <a href="https://era.joeroe.io/articles/era.html">introductory vignette</a> details the main features of the package, but briefly,
<code class="language-plaintext highlighter-rouge">yr()</code> defines the era associated with a vector of years:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">yr</span><span class="p">(</span><span class="m">10010</span><span class="o">:</span><span class="m">10001</span><span class="p">,</span><span class="w"> </span><span class="s2">"cal BP"</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; # cal BP years &lt;yr[10]&gt;:</span><span class="w">
</span><span class="c1">#&gt;  [1] 10010 10009 10008 10007 10006 10005 10004 10003 10002 10001</span><span class="w">
</span><span class="c1">#&gt; # Era: Before Present (cal BP): calendar years, counted backwards from 1950</span><span class="w">
</span></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">yr</code> is a vector class is based on <code class="language-plaintext highlighter-rouge">vctrs</code>.
This means it behaves in a consistent, type-stable way across base R and other packages.
Some common calendar systems and time scales are built into the package (see <code class="language-plaintext highlighter-rouge">?eras()</code>) and can be referenced by simply passing their abbreviated label to <code class="language-plaintext highlighter-rouge">yr()</code>. 
Other eras can be defined using the <code class="language-plaintext highlighter-rouge">era()</code> function directly, which allows for arbitrary user-defined eras:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">era</span><span class="p">(</span><span class="s2">"T.A."</span><span class="p">,</span><span class="w"> </span><span class="n">epoch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">-9021</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Third Age"</span><span class="p">,</span><span class="w"> </span><span class="n">direction</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"forwards"</span><span class="p">)</span><span class="w">
</span><span class="c1">#&gt; &lt;era[1]&gt;</span><span class="w">
</span><span class="c1">#&gt; [1] Third Age (T.A.): calendar years, counted forwards from -9021</span><span class="w">
</span></code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">yr_transform()</code> is used to convert between eras, included user-defined ones:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">yr</span><span class="p">(</span><span class="m">10010</span><span class="o">:</span><span class="m">10001</span><span class="p">,</span><span class="w"> </span><span class="s2">"cal BP"</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> 
  </span><span class="n">yr_transform</span><span class="p">(</span><span class="n">era</span><span class="p">(</span><span class="s2">"BCE"</span><span class="p">))</span><span class="w">
</span><span class="c1">#&gt; # BCE years &lt;yr[10]&gt;:</span><span class="w">
</span><span class="c1">#&gt;  [1] 8060 8059 8058 8057 8056 8055 8054 8053 8052 8051</span><span class="w">
</span><span class="c1">#&gt; # Era: Before Common Era (BCE): calendar years, counted backwards from 0</span><span class="w">
</span></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">yr</code> vectors fit nicely into tables, both base data frames and tibbles:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">tribble</span><span class="p">(</span><span class="w">
  </span><span class="o">~</span><span class="n">period</span><span class="p">,</span><span class="w">           </span><span class="o">~</span><span class="n">start_ka</span><span class="p">,</span><span class="w">
  </span><span class="s2">"Late Holocene"</span><span class="p">,</span><span class="w">   </span><span class="m">4.2</span><span class="p">,</span><span class="w">
  </span><span class="s2">"Mid Holocene"</span><span class="p">,</span><span class="w">    </span><span class="m">8.326</span><span class="p">,</span><span class="w">
  </span><span class="s2">"Early Holocene"</span><span class="p">,</span><span class="w">  </span><span class="m">11.7</span><span class="p">,</span><span class="w">
  </span><span class="s2">"Younger Dryas"</span><span class="p">,</span><span class="w">   </span><span class="m">12.9</span><span class="p">,</span><span class="w">
  </span><span class="s2">"Bølling-Allerød"</span><span class="p">,</span><span class="w"> </span><span class="m">14.7</span><span class="p">,</span><span class="w">
  </span><span class="s2">"Heinrich 1"</span><span class="p">,</span><span class="w">      </span><span class="m">17.0</span><span class="w">
</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">mutate</span><span class="p">(</span><span class="n">start_ka</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">yr</span><span class="p">(</span><span class="n">start_ka</span><span class="p">,</span><span class="w"> </span><span class="s2">"ka"</span><span class="p">))</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> 
  </span><span class="n">mutate</span><span class="p">(</span><span class="n">start_bp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">yr_transform</span><span class="p">(</span><span class="n">start_ka</span><span class="p">,</span><span class="w"> </span><span class="n">era</span><span class="p">(</span><span class="s2">"BP"</span><span class="p">)),</span><span class="w">
         </span><span class="n">start_bce</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">yr_transform</span><span class="p">(</span><span class="n">start_ka</span><span class="p">,</span><span class="w"> </span><span class="n">era</span><span class="p">(</span><span class="s2">"BCE"</span><span class="p">)))</span><span class="w">
</span><span class="c1">#&gt; # A tibble: 6 x 4</span><span class="w">
</span><span class="c1">#&gt;   period          start_ka start_bp start_bce</span><span class="w">
</span><span class="c1">#&gt;   &lt;chr&gt;               &lt;yr&gt;     &lt;yr&gt;      &lt;yr&gt;</span><span class="w">
</span><span class="c1">#&gt; 1 Late Holocene      4.200     4200      2250</span><span class="w">
</span><span class="c1">#&gt; 2 Mid Holocene       8.326     8326      6376</span><span class="w">
</span><span class="c1">#&gt; 3 Early Holocene    11.700    11700      9750</span><span class="w">
</span><span class="c1">#&gt; 4 Younger Dryas     12.900    12900     10950</span><span class="w">
</span><span class="c1">#&gt; 5 Bølling-Allerød   14.700    14700     12750</span><span class="w">
</span><span class="c1">#&gt; 6 Heinrich 1        17.000    17000     15050</span><span class="w">
</span></code></pre></div></div>

<h2 id="links">Links</h2>

<ul>
  <li><a href="https://era.joeroe.io/">era package documentation</a></li>
  <li><a href="https://era.joeroe.io/articles/era.html">Introductory vignette</a></li>
  <li><a href="https://github.com/joeroe/era">Source code</a> (GitHub)</li>
</ul>]]></content><author><name>Joe Roe</name></author><category term="R" /><summary type="html"><![CDATA[era is a small R package for working with different year numbering systems. There are a few systems in use in archaeology, geology, and other palaeosciences; for example, the year 10,000 BCE is 11,950 Before Present or 11.95 ka. It is usually fine to store years as a plain numeric vector in R, but sometimes it helps to be explicit about which system is being used: When you have data that mixes different systems When you want to transform years between different systems When you need to do arithmetic with years The era package helps in these cases by providing classes which define the ‘era’ associated with a vector of years and functions for formatting, combining, and transforming years with different eras. I wrote it to support two other packages I’m working on, stratigraphr and rintchron, because whilst converting between any two eras (e.g. BP→BCE) is trivial, I wanted something more generalised and predictable when encountering lots of them. It was also a good way to learn more about vctrs, the new backend package used to implement S3 vector classes in the tidyverse, which I found very useful for getting a grip on how size and type stability works (or doesn’t work) in R at a low level, and how to write classes that behave consistently and intuitively. I released the first beta version of era today. You can install it from GitHub with the remotes package: remotes::install_github("joeroe/era") library("era") I plan to submit the next release to CRAN; so please, try it out and submit an issue if you find any bugs! Using era The introductory vignette details the main features of the package, but briefly, yr() defines the era associated with a vector of years: yr(10010:10001, "cal BP") #&gt; # cal BP years &lt;yr[10]&gt;: #&gt; [1] 10010 10009 10008 10007 10006 10005 10004 10003 10002 10001 #&gt; # Era: Before Present (cal BP): calendar years, counted backwards from 1950 yr is a vector class is based on vctrs. This means it behaves in a consistent, type-stable way across base R and other packages. Some common calendar systems and time scales are built into the package (see ?eras()) and can be referenced by simply passing their abbreviated label to yr(). Other eras can be defined using the era() function directly, which allows for arbitrary user-defined eras: era("T.A.", epoch = -9021, name = "Third Age", direction = "forwards") #&gt; &lt;era[1]&gt; #&gt; [1] Third Age (T.A.): calendar years, counted forwards from -9021 The yr_transform() is used to convert between eras, included user-defined ones: yr(10010:10001, "cal BP") %&gt;% yr_transform(era("BCE")) #&gt; # BCE years &lt;yr[10]&gt;: #&gt; [1] 8060 8059 8058 8057 8056 8055 8054 8053 8052 8051 #&gt; # Era: Before Common Era (BCE): calendar years, counted backwards from 0 yr vectors fit nicely into tables, both base data frames and tibbles: tribble( ~period, ~start_ka, "Late Holocene", 4.2, "Mid Holocene", 8.326, "Early Holocene", 11.7, "Younger Dryas", 12.9, "Bølling-Allerød", 14.7, "Heinrich 1", 17.0 ) %&gt;% mutate(start_ka = yr(start_ka, "ka")) %&gt;% mutate(start_bp = yr_transform(start_ka, era("BP")), start_bce = yr_transform(start_ka, era("BCE"))) #&gt; # A tibble: 6 x 4 #&gt; period start_ka start_bp start_bce #&gt; &lt;chr&gt; &lt;yr&gt; &lt;yr&gt; &lt;yr&gt; #&gt; 1 Late Holocene 4.200 4200 2250 #&gt; 2 Mid Holocene 8.326 8326 6376 #&gt; 3 Early Holocene 11.700 11700 9750 #&gt; 4 Younger Dryas 12.900 12900 10950 #&gt; 5 Bølling-Allerød 14.700 14700 12750 #&gt; 6 Heinrich 1 17.000 17000 15050 Links era package documentation Introductory vignette Source code (GitHub)]]></summary></entry></feed>