<?xml version="1.0" encoding="utf-8"?>
  <feed xmlns="http://www.w3.org/2005/Atom">
    <title>Jonathan Chang</title>
    <subtitle>evolutionary biologist</subtitle>
    <link href="https://jonathanchang.org/feed/r.xml" rel="self" type="application/atom+xml"/>
    <link href="https://jonathanchang.org/" rel="alternate" type="text/html"/>
    <id>https://jonathanchang.org/</id>
    <generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator>
    <updated>2026-07-30T06:26:25+00:00</updated>
    <author>
      <name>Jonathan Chang</name>
      <email>me@jonathanchang.org</email>
      <uri>https://jonathanchang.org</uri>
    </author>
    <rights>Copyright © 2026 Jonathan Chang</rights>
    <entry>
      <title><![CDATA[Generating a triangular navigation mesh from H3 hexagons in R]]></title>
      <link rel="alternate" type="text/html" href="https://jonathanchang.org/blog/hexes-to-triangles-h3-r-sf/"/>
      <id>https://jonathanchang.org/blog/hexes-to-triangles-h3-r-sf</id>
      <published>2024-12-31T00:00:00+00:00</published>
      <updated>2024-12-31T00:00:00+00:00</updated>
      <content type="html"><![CDATA[
     <h2>Introduction</h2>
      <p>A <a href="https://en.wikipedia.org/wiki/Navigation_mesh">navigation mesh</a> is a
        data structure used to aid in pathfinding around obstacles. Originally
        used for video games and robotics, we can also apply the concept of this
        navigational mesh to the movement of animals through landscape, using
        methods such as <a href="https://elifesciences.org/articles/61927">FEEMS</a>.</p>
      <p>Consider the following landscape<sup class="footnote-ref"><a href="#fn1" id="fnref1">1</a></sup>:</p>
      <p><img src="/uploads/2024/navmesh1.png" alt="" /></p>
      <p>Finding a path from starting point <em>s</em> to goal point <em>t</em> is
        computationally challenging, as there are many possible routes one might
        take between these two endpoints, and the possibility space is so large
        that it can be challenging to efficiently compute a fast path. Instead,
        we can simplify the space by shrinking the possible locations in the
        landscape to a series of <em>nodes</em>, and the neighboring nodes that you can
        move to are connected by <em>edges</em>. The resulting <em>mesh</em> typically looks
        like a bunch of triangles overlapping the landscape:</p>
      <p><img src="/uploads/2024/navmesh2.png" alt="" /></p>
      <p>In this blog post I’ll discuss how to construct such a mesh using
        real-world shapefiles and the <code>h3</code> library in R, which tessellates
        hexagons across the globe at several resolutions. I also looked into
        alternatives such as
        <a href="https://cran.r-project.org/package=dggridR">dggridR</a> but I found other
        packages to not be satisfactory for a number of reasons (mostly speed).</p>
      <p>To begin with, why h3, and why hexagons? The answer to this is that it
        is very convenient to have a grid system that can be used for any place
        on the planet, and having a variety of different resolutions makes it
        convenient to analyze spatial data on scales ranging from
        continent-level to city-level. Hexagons in particular have a nice
        property where the distance from the center of one hexagon to its
        neighboring hex is roughly equal.<sup class="footnote-ref"><a href="#fn2" id="fnref2">2</a></sup></p>
      <p><img src="/uploads/2024/h3-hex.png" alt="" /></p>
      <h2>Worked example</h2>
      <p>First, load some required packages, including <code>h3jsr</code>, which I found to
        have a nicer interface than other h3 libraries for R.</p>
      <div class="language-r highlighter-rouge">
        <div class="highlight">
          <pre class="highlight"><code data-lang="r"><span class="n">library</span><span class="p">(</span><span class="n">tidyverse</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">h3jsr</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">sf</span><span class="p">)</span><span class="w">
</span><span class="n">requireNamespace</span><span class="p">(</span><span class="s2">"maps"</span><span class="p">)</span><span class="w">
</span></code></pre>
        </div>
      </div>
      <p>For today’s example we’ll be using a simple map showing some counties in
        the San Francisco Bay Area (SFBA). We need to turn off spherical
        geometry in <code>sf</code> since at this scale, everything is approximately planar
        anyway and there are some issues with the geometries provided in the
        <code>maps</code> package.<sup class="footnote-ref"><a href="#fn3" id="fnref3">3</a></sup></p>
      <div class="language-r highlighter-rouge">
        <div class="highlight">
          <pre class="highlight"><code data-lang="r"><span class="n">sf</span><span class="o">::</span><span class="n">sf_use_s2</span><span class="p">(</span><span class="kc">FALSE</span><span class="p">)</span><span class="w">
<p></span><span class="n">bay_counties</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="s2">“alameda”</span><span class="p">,</span><span class="w"> </span><span class="s2">“contra costa”</span><span class="p">,</span><span class="w"> </span><span class="s2">“marin”</span><span class="p">,</span><span class="w"> </span><span class="s2">“napa”</span><span class="p">,</span><span class="w"> </span><span class="s2">“san mateo”</span><span class="p">,</span><span class="w"> </span><span class="s2">“santa clara”</span><span class="p">,</span><span class="w"> </span><span class="s2">“solano”</span><span class="p">,</span><span class="w"> </span><span class="s2">“sonoma”</span><span class="p">,</span><span class="w"> </span><span class="s2">“san francisco”</span><span class="p">)</span><span class="w"></p>
<p></span><span class="n">sfba</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">maps</span><span class="o">::</span><span class="n">map</span><span class="p">(</span><span class="s1">‘county’</span><span class="p">,</span><span class="w"> </span><span class="n">paste0</span><span class="p">(</span><span class="s2">“california,”</span><span class="p">,</span><span class="w"> </span><span class="n">bay_counties</span><span class="p">),</span><span class="w"> </span><span class="n">fill</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w"> </span><span class="n">plot</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
</span><span class="n">st_as_sf</span><span class="p">()</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
</span><span class="n">st_transform</span><span class="p">(</span><span class="m">4326</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
</span><span class="n">st_make_valid</span><span class="p">()</span><span class="w"></p>
<p></span><span class="n">ggplot</span><span class="p">(</span><span class="n">sfba</span><span class="p">,</span><span class="w"> </span><span class="n">aes</span><span class="p">(</span><span class="n">fill</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ID</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w">
</span><span class="n">geom_sf</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w">
</span><span class="n">theme_minimal</span><span class="p">()</span><span class="w">
</span></code></pre>
        </div>
      </div>
    </p>
    <p><img src="/uploads/2024/sfba-counties-1.png" alt="" /><!-- --></p>
    <p>Next, we need to find the h3 hexagons that intersect this shapefile of
      the SFBA. I’d like my hexagons to cover a couple of square kilometers,
      and this <a href="https://h3geo.org/docs/core-library/restable/#average-area-in-km2">corresponds to roughly resolution
        7</a> in
      h3. We must first dissolve the individual features (i.e., remove
      internal borders) then use the <code>polygon_to_cells</code> function to identify
      the correct h3 cells that intersect with our SFBA borders.</p>
    <div class="language-r highlighter-rouge">
      <div class="highlight">
        <pre class="highlight"><code data-lang="r"><span class="n">dissolved_sfba</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">summarise</span><span class="p">(</span><span class="n">sfba</span><span class="p">,</span><span class="w"> </span><span class="n">geom</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">st_union</span><span class="p">(</span><span class="n">geom</span><span class="p">))</span><span class="w">
</span><span class="n">ids</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">polygon_to_cells</span><span class="p">(</span><span class="n">dissolved_sfba</span><span class="p">,</span><span class="w"> </span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">7</span><span class="p">)[[</span><span class="m">1</span><span class="p">]]</span><span class="w">
</span><span class="n">head</span><span class="p">(</span><span class="n">ids</span><span class="p">)</span><span class="w">
</span></code></pre>
      </div>
    </div>
    <div class="highlighter-rouge">
      <div class="highlight">
        <pre class="highlight"><code>## [1] "872830311ffffff" "872830925ffffff" "87283154dffffff" "872836a62ffffff"
## [5] "872830314ffffff" "872830928ffffff"
</code></pre>
      </div>
    </div>
    <p>Plot these h3 hexes to get an idea of what we’re working with.</p>
    <div class="language-r highlighter-rouge">
      <div class="highlight">
        <pre class="highlight"><code data-lang="r"><span class="n">ggplot</span><span class="p">(</span><span class="n">cell_to_polygon</span><span class="p">(</span><span class="n">ids</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_sf</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">theme_minimal</span><span class="p">()</span><span class="w">
</span></code></pre>
      </div>
    </div>
    <p><img src="/uploads/2024/sfba-hexes-1.png" alt="" /><!-- --></p>
    <p>For each cell ID, we need to identify its neighbors using the <code>get_disk</code>
      function with <code>distance = 1</code>. <a href="https://obrl-soil.github.io/h3jsr/reference/get_disk.html">This function’s
        documentation</a>
      says:</p>
    <blockquote>
      <p>The first address returned is the input address, the rest follow in a
        spiral anticlockwise order.</p>
    </blockquote>
    <p>This is perfect for our use case. We take the origin hex and find its
      center. Then do the same for two of its neighbors, and construct a
      triangle between the centers of these three hexes.</p>
    <div class="language-r highlighter-rouge">
      <div class="highlight">
        <pre class="highlight"><code data-lang="r"><span class="n">id</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ids</span><span class="p">[</span><span class="m">1</span><span class="p">]</span><span class="w">
<p></span><span class="n">disk</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">get_disk</span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">ring_size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">)[[</span><span class="m">1</span><span class="p">]]</span><span class="w"></p>
<p></span><span class="n">first_three</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">disk</span><span class="p">[</span><span class="m">1</span><span class="o">:</span><span class="m">3</span><span class="p">]</span><span class="w">
</span><span class="n">first_triangle</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">cell_to_point</span><span class="p">(</span><span class="n">first_three</span><span class="p">,</span><span class="w"> </span><span class="n">simple</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
</span><span class="n">st_combine</span><span class="p">()</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
</span><span class="n">st_cast</span><span class="p">(</span><span class="s2">“POLYGON”</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
</span><span class="n">st_as_sf</span><span class="p">()</span><span class="w"></p>
<p></span><span class="n">ggplot</span><span class="p">(</span><span class="n">first_triangle</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
</span><span class="n">geom_sf</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w">
</span><span class="n">theme_minimal</span><span class="p">()</span><span class="w">
</span></code></pre>
      </div>
    </div>
  </p>
  <p><img src="/uploads/2024/sfba-single-triangle-1.png" alt="" /><!-- --></p>
  <p>We can repeat this procedure to construct triangles between the centers
    of all the hexes surrounding our origin hex. We exclude the origin point
    from the loop and also add in the first neighboring hex in the ring,
    otherwise we’ll only have five triangles, not six, around the origin
    hex.</p>
  <div class="language-r highlighter-rouge">
    <div class="highlight">
      <pre class="highlight"><code data-lang="r"><span class="n">wrapped_vec</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="n">disk</span><span class="p">[</span><span class="m">-1</span><span class="p">],</span><span class="w"> </span><span class="n">disk</span><span class="p">[</span><span class="m">2</span><span class="p">])</span><span class="w">
<p></span><span class="n">results</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">list</span><span class="p">()</span><span class="w">
</span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">ii</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="p">(</span><span class="nf">length</span><span class="p">(</span><span class="n">wrapped_vec</span><span class="p">)</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="p">{</span><span class="w">
</span><span class="n">results</span><span class="p">[[</span><span class="n">ii</span><span class="p">]]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">cell_to_point</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">wrapped_vec</span><span class="p">[</span><span class="n">ii</span><span class="p">],</span><span class="w"> </span><span class="n">wrapped_vec</span><span class="p">[</span><span class="n">ii</span><span class="m">+1</span><span class="p">]),</span><span class="w"> </span><span class="n">simple</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
</span><span class="n">st_combine</span><span class="p">()</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
</span><span class="n">st_cast</span><span class="p">(</span><span class="s2">“POLYGON”</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
</span><span class="n">st_as_sf</span><span class="p">()</span><span class="w">
</span><span class="p">}</span><span class="w"></p>
<p></span><span class="n">tris</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">bind_rows</span><span class="p">(</span><span class="n">results</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">idx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="m">6</span><span class="p">)</span><span class="w"></p>
<p></span><span class="n">ggplot</span><span class="p">(</span><span class="n">tris</span><span class="p">,</span><span class="w"> </span><span class="n">aes</span><span class="p">(</span><span class="n">fill</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">factor</span><span class="p">(</span><span class="n">idx</span><span class="p">)))</span><span class="w"> </span><span class="o">+</span><span class="w">
</span><span class="n">geom_sf</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w">
</span><span class="n">theme_minimal</span><span class="p">()</span><span class="w">
</span></code></pre>
    </div>
  </div>
</p>
<p><img src="/uploads/2024/sfba-multi-triangle-1.png" alt="" /><!-- --></p>
<p>We can build off of this initial work to write a function that will take
  an <code>h3</code> id as input and return a triangular mesh.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">to_triangles</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">id</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">disk</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">get_disk</span><span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">ring_size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">)[[</span><span class="m">1</span><span class="p">]]</span><span class="w">
  </span><span class="n">wrapped_vec</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="n">disk</span><span class="p">[</span><span class="m">-1</span><span class="p">],</span><span class="w"> </span><span class="n">disk</span><span class="p">[</span><span class="m">2</span><span class="p">])</span><span class="w">
  </span><span class="n">lapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="p">(</span><span class="nf">length</span><span class="p">(</span><span class="n">wrapped_vec</span><span class="p">)</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="k">function</span><span class="p">(</span><span class="n">ii</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">tri</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="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">wrapped_vec</span><span class="p">[</span><span class="n">ii</span><span class="p">],</span><span class="w"> </span><span class="n">wrapped_vec</span><span class="p">[</span><span class="n">ii</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">cell_to_point</span><span class="p">(</span><span class="n">tri</span><span class="p">,</span><span class="w"> </span><span class="n">simple</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">st_combine</span><span class="p">()</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">st_cast</span><span class="p">(</span><span class="s2">"POLYGON"</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
      </span><span class="n">st_as_sf</span><span class="p">()</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">bind_rows</span><span class="p">()</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>Use <code>lapply</code> and <code>bind_rows</code> to construct an entire <code>sf</code> dataframe that
  contains the entire triangular mesh. There are a lot of duplicates but
  we can just use <code>distinct</code> to get rid of these. (I use
  <code>parallel::mclapply</code> here as this step can be a bit slow.)</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">all_tris</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">parallel</span><span class="o">::</span><span class="n">mclapply</span><span class="p">(</span><span class="n">ids</span><span class="p">,</span><span class="w"> </span><span class="n">to_triangles</span><span class="p">,</span><span class="w"> </span><span class="n">mc.cores</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">8</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">bind_rows</span><span class="p">()</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w">
  </span><span class="n">distinct</span><span class="p">()</span><span class="w">
<p></span><span class="n">ggplot</span><span class="p">(</span><span class="n">all_tris</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
</span><span class="n">geom_sf</span><span class="p">(</span><span class="n">fill</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">“white”</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
</span><span class="n">theme_minimal</span><span class="p">()</span><span class="w">
</span></code></pre>
  </div>
</div>
</p>
<p><img src="/uploads/2024/sfba-tri-mesh-1.png" alt="" /><!-- --></p>
<p>Observant readers will note that these triangle meshes will include
  nodes that are out in the water or in neighboring counties, since the
  hexes at the edge of our SFBA shapefile will inevitably have a few
  neighbors that are not contained within the SFBA shapefile.</p>
<p>While it wasn’t necessary to do so for my analysis, these can easily be
  removed by using a binary predicate such as <code>st_contains</code>, to never
  include any edge that enters the water or otherwise exits the boundary
  of the shapefile.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">contain_result</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">st_contains</span><span class="p">(</span><span class="n">dissolved_sfba</span><span class="p">,</span><span class="w"> </span><span class="n">all_tris</span><span class="p">)</span><span class="w">
</span><span class="n">contained_mesh</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">all_tris</span><span class="p">[</span><span class="n">unlist</span><span class="p">(</span><span class="n">contain_result</span><span class="p">),</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">ggplot</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_sf</span><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dissolved_sfba</span><span class="p">,</span><span class="w"> </span><span class="n">fill</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"pink"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">geom_sf</span><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">contained_mesh</span><span class="p">,</span><span class="w"> </span><span class="n">fill</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"white"</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
  </span><span class="n">theme_minimal</span><span class="p">()</span><span class="w">
</span></code></pre>
  </div>
</div>
<p><img src="/uploads/2024/sfba-tri-mesh-no-water-1.png" alt="" /><!-- --></p>
<p>This navigational mesh can now be saved using functions such as
  <code>write_sf</code> and used in downstream analysis software.</p>
<h2>Exercises</h2>
<ol>
  <li>How can we avoid the creation of duplicate triangle meshes?</li>
  <li>Use a <code>compact</code> representation to generate a triangle mesh with
    simplified interiors.</li>
</ol>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">comp</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">cell_to_polygon</span><span class="p">(</span><span class="n">h3jsr</span><span class="o">::</span><span class="n">compact</span><span class="p">(</span><span class="n">ids</span><span class="p">),</span><span class="w"> </span><span class="n">simple</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">)</span><span class="w">
<p></span><span class="n">ggplot</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w">
</span><span class="n">geom_sf</span><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dissolved_sfba</span><span class="p">,</span><span class="w"> </span><span class="n">fill</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NA</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
</span><span class="n">geom_sf</span><span class="p">(</span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">comp</span><span class="p">,</span><span class="w"> </span><span class="n">fill</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NA</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">“red”</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w">
</span><span class="n">theme_minimal</span><span class="p">()</span><span class="w">
</span></code></pre>
  </div>
</div>
</p>
<p><img src="/uploads/2024/sfba-compacted-1.png" alt="" /><!-- --></p>
<section class="footnotes">
  <ol>
    <li id="fn1">
      <p>Figures taken from <a href="https://www.cs.umd.edu/class/spring2018/cmsc425/Lects/lect15-nav-mesh.pdf">these lecture
          notes</a>. <a href="#fnref1" class="footnote-backref">↩</a></p>
    </li>
    <li id="fn2">
      <p>For more detail, see <a href="https://www.uber.com/blog/h3/">Uber’s blog post introducing the h3
          system</a>. <a href="#fnref2" class="footnote-backref">↩</a></p>
    </li>
    <li id="fn3">
      <p>For your own analyses, you should probably use shapefiles that can
        be correctly handled by <code>sf</code>. <a href="#fnref3" class="footnote-backref">↩</a></p>
    </li>
  </ol>
</section>
]]></content>
</entry>
<entry>
  <title><![CDATA[Download shapefiles from ESRI ArcGIS Online Story Maps]]></title>
  <link rel="alternate" type="text/html" href="https://jonathanchang.org/blog/downloading-esri-online-shapefiles/"/>
  <id>https://jonathanchang.org/blog/downloading-esri-online-shapefiles</id>
  <published>2022-11-04T00:00:00+00:00</published>
  <updated>2022-11-04T00:00:00+00:00</updated>
  <content type="html"><![CDATA[
     <p>Recently, we needed to get out some shapefiles from an <a href="https://www.arcgis.com/apps/MapSeries/index.html?appid=34603bd48c9f496fa2750a770f655013">ArcGIS Online map</a>. It’s immediately clear that there’s a lot of data, and no obvious way to get it from a download or share link anywhere on the app page. The desired solution is anything <em>but</em> taking a screenshot and tracing it in ImageJ, as that’s an absolute last resort. In this post, I’ll walk through how I managed to get those shapefiles downloaded, and hopefully provide some easy tips to do the same for other ArcGIS online maps.</p>
  <h2>The power of the web inspector</h2>
  <p>This is fundamentally a <a href="https://en.wikipedia.org/wiki/Web_scraping">web scraping task</a>, and I’ll start with opening the web developer tools in Firefox, by right-clicking a promising bit on the page (the map itself) and selecting “Inspect”. Looking through the HTML tree in the web inspector panel that pops up, I can see that while the shapefiles do appear to exist locally, these are parsed into a gnarly embedded <a href="https://en.wikipedia.org/wiki/Scalable_Vector_Graphics">SVG object</a>. This could be used to reconstruct the shapefile, but it seems like a big pain that I don’t want to deal with, so I move on from this avenue.</p>
  <p><img src="/uploads/2022/geojson/inspector-html.png" alt="Screenshot of an HTML source code tree, showing a complex SVG object." srcset="/uploads/2022/geojson/inspector-html.png 2x"></p>
  <p>Next, I’ll check out the network tab. I’ll need to refresh the page, and I can see that there are a ton of requests that go to a lot of different places. But, I suspect that any shapefile that’s loaded will likely be downloaded via <a href="https://en.wikipedia.org/wiki/XMLHttpRequest">XHR</a>, initiated from Javascript, and quite possibly hitting some API endpoint that probably speaks in JSON. I filter by JS and XHR and immediately see an request that pops out at me, to an endpoint at <code>services.arcgis.com</code> called <code>data</code> with a query payload of <code>f=json</code>. Inspecting that response object leads me to another API endpoint that appears to be what I want!</p>
  <p><img src="/uploads/2022/geojson/inspector-json.png" alt="Screenshot of the network panel of the web developer console, showing a JSON response object with interesting URL fields." srcset="/uploads/2022/geojson/inspector-json.png 2x"></p>
  <h2>ESRI API endpoints</h2>
  <p>I’m actually fairly familiar with ESRI’s REST APIs, and I know that I can actually <a href="https://services.arcgis.com/8df8p0NlLFEShl0r/arcgis/rest/services/FHA_Grades/FeatureServer/0">navigate to the API endpoint</a> and it’ll provide a fairly good description of its data. I can also interactively query it in the browser, without having to muck about with cURL in Terminal or anything like that. ESRI is quite humane in this respect, but again, there doesn’t seem to be an easy way to download the full shapefile directly from this endpoint, and I don’t feel quite up to the task of writing out a shapefile by copying and pasting a bunch of stuff.</p>
  <p><img src="/uploads/2022/geojson/esri-endpoint.png" alt="Screenshot of the ESRI REST API query tool, showing the result of a query with complex shapefile geometries." srcset="/uploads/2022/geojson/esri-endpoint.png 2x"></p>
  <p>A quick Google sojourn leads me to <code>pyesridump</code>, <a href="https://github.com/openaddresses/pyesridump">a wonderful tool</a> by the folks over at OpenAddresses. This is actually exactly what I needed! Install the <code>esri2geojson</code> command with <a href="https://pypa.github.io/pipx/">pipx</a>:</p>
  <div class="language-console?prompt=% highlighter-rouge">
    <div class="highlight">
      <pre class="highlight"><code data-lang="console?prompt=%"><span class="gp">%</span><span class="w"> </span>pipx <span class="nb">install </span>esridump
<span class="go">  installed package esridump 1.11.0, installed using Python 3.10.8
  These apps are now globally available
    - esri2geojson
done! ✨ 🌟 ✨
<p></span><span class="gp">%</span><span class="w"> </span>esri2geojson <span class="s2">“<a href="https://services.arcgis.com/8df8p0NlLFEShl0r/ArcGIS/rest/services/FHA_Grades/FeatureServer/0">https://services.arcgis.com/8df8p0NlLFEShl0r/ArcGIS/rest/services/FHA_Grades/FeatureServer/0</a>”</span> fha.geojson
<span class="go">2022-11-03 23:42:54,990 - cli.esridump - INFO - Built 1 requests using resultOffset method
</span></code></pre>
    </div>
  </div>
</p>
<p>Now to fire up R and see that everything looks right by plotting it.</p>
<div class="language-console?lang=r&comments=true&output=plaintext&prompt=> highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="console?lang=r&comments=true&output=plaintext&prompt=>"><span class="gp">&gt;</span><span class="w"> </span><span class="n">library</span><span class="p">(</span><span class="n">sf</span><span class="p">)</span><span class="w">
</span>Linking to GEOS 3.10.2, GDAL 3.4.2, PROJ 8.2.1; sf_use_s2() is TRUE
<p><span class="gp">&gt;</span><span class="w"> </span><span class="n">library</span><span class="p">(</span><span class="n">ggplot2</span><span class="p">)</span><span class="w">
</span>
<span class="gp">&gt;</span><span class="w"> </span><span class="n">xx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">read_sf</span><span class="p">(</span><span class="s2">“fha.geojson”</span><span class="p">)</span><span class="w">
</span>
<span class="gp">&gt;</span><span class="w"> </span><span class="n">xx</span><span class="w">
</span>Simple feature collection with 74 features and 4 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: -77.188 ymin: 38.79005 xmax: -76.8772 ymax: 39.0666
Geodetic CRS:  WGS 84
<span class="c"># A tibble: 74 × 5
</span>     FID Grade Shape__Area Shape__Length                         geometry
&lt;int&gt; &lt;chr&gt;       &lt;dbl&gt;         &lt;dbl&gt;                    &lt;POLYGON [°]&gt;
1     1 E5       2268576.         5849. ((-76.90432 38.85715, -76.9018 …
2     2 G7      13563378.        19322. ((-76.93371 38.87391, -76.90942…
3     3 H2       7772476.        12002. ((-76.88671 38.90218, -76.89007…
4     4 H1      12964128.        20269. ((-76.90942 38.89269, -76.93095…
5     5 G1       6516531.        19844. ((-76.93428 38.88311, -76.93574…
6     6 C4       7199183.        16914. ((-76.93371 38.87391, -76.96229…
7     7 H2       7328078.        14489. ((-76.96229 38.85169, -76.97798…
8     8 E2       9790479.        21957. ((-76.98859 38.8399, -76.9885 3…
9     9 F2       5253684.        15352. ((-76.99618 38.85609, -77.00305…
10    10 H1       1810343.         8218. ((-76.97203 38.89815, -76.9833 …
<span class="c"># … with 64 more rows</p>
<h1>ℹ Use <code>print(n = ...)</code> to see more rows</h1>
</span>
<span class="gp">&gt;</span><span class="w"> </span><span class="n">ggplot</span><span class="p">(</span><span class="n">xx</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">geom_sf</span><span class="p">(</span><span class="n">aes</span><span class="p">(</span><span class="n">fill</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Grade</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">theme_minimal</span><span class="p">()</span><span class="w">
</span></code></pre>
  </div>
</div>
<picture>
  <source type="image/svg+xml" srcset="/uploads/2022/geojson/fha-shapefile.svg">
  <img src="/uploads/2022/geojson/fha-shapefile.png" alt="Plot of the FHA shapefile dataset of Washington, DC.">
</picture>
<p>It looks fantastic, and is ready for further data analysis now!</p>
<h2>A more direct route</h2>
<p>After all of this, I was curious if there was a better way, so I did some digging. This ArcGIS Online tool is called ESRI Story Map Series, and the source code is actually <a href="https://github.com/Esri/storymap-series">available on GitHub</a>. Looking through the repository we can see it’s a Javascript app with a fairly rich library API, intended for ESRI’s customers to develop “story maps” with deep integrations to justify their hefty enterprise contracts. In the README, one of the <a href="https://github.com/Esri/storymap-series/blob/109e94458da8f297cd21b7ed877832b8a8ce9867/README.md#link-between-entries">code suggestions</a> points in an interesting direction, and I reopened the web inspector console to check it out.</p>
<p>Based on the README example, I learned that the top-level object is called <code>app</code>, and that layers can be obtained through a method on the <code>app.map</code> object. I grub around in the app’s internal data structures using the Javascript console, and discover an interesting <code>_layers</code> key inside this object, which seems to have the relevant data that I’m interested in.</p>
<p><img src="/uploads/2022/geojson/inspector-js.png" alt="Screenshot of the console panel of the web developer console, showing a Javascript data object corresponding to the ESRI map being shown in the map app." srcset="/uploads/2022/geojson/inspector-js.png 2x"></p>
<p>The full invocation in the Javascript console to get the ESRI REST API endpoint is therefore:</p>
<div class="language-javascript highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="javascript"><span class="nx">app</span><span class="p">.</span><span class="nx">map</span><span class="p">.</span><span class="nx">_layers</span><span class="p">.</span><span class="nx">FHA_Grades_4159</span><span class="p">.</span><span class="nx">url</span>
<span class="c1">// "https://services.arcgis.com/8df8p0NlLFEShl0r/arcgis/rest/services/FHA_Grades/FeatureServer/0" </span>
</code></pre>
  </div>
</div>
]]></content>
</entry>
<entry>
  <title><![CDATA[Three ways to check and fix ultrametric phylogenies]]></title>
  <link rel="alternate" type="text/html" href="https://jonathanchang.org/blog/three-ways-to-check-and-fix-ultrametric-phylogenies/"/>
  <id>https://jonathanchang.org/blog/three-ways-to-check-and-fix-ultrametric-phylogenies</id>
  <published>2021-07-13T01:55:00+00:00</published>
  <updated>2021-07-13T01:55:00+00:00</updated>
  <content type="html"><![CDATA[
     <p>A recent user <a href="https://github.com/jonchang/tact/issues/230">bug report</a> in my software <a href="https://github.com/jonchang/tact">TACT</a> led me to look into how phylogenetic software varies in the way they determine whether a given phylogenetic tree is ultrametric (where the root-to-tip distance is equal among all tips). If you infer an ultrametric phylogeny using something like BEAST or treePL, your supposedly ultrametric tree can still cause problems for other tools by virtue of <em>not being ultrametric enough</em>.</p>
  <p>This ultrametric purity test was previously a problem during the great BAMM controversy of 2017 (“BAMMghazi”), when the whales phylogeny used as an example in BAMM <a href="https://github.com/macroevolution/bammtools/issues/45">suddenly stopped being ultrametric</a> as measured by the R function <code>ape::is.ultrametric</code>.</p>
  <p>How then, do tools differ in the way that they check for ultrametricity?</p>
  <p><img src="/uploads/2021/ultrametric/preview.jpg" alt="Illustration of a construction worker wielding a ‘stop’ sign in front of a breaching humpback whale. The label  is shown, describing the situation by means of analogy." /></p>
  <h2>Method 1: variance</h2>
  <p>This is <a href="https://github.com/FePhyFoFum/phyx/blob/f6559150a4cf7f78f2ec6f4edab232ce34b99fda/src/tree_utils.cpp#L486-L492">used in phyx</a>, and was <a href="https://github.com/cran/ape/blob/650e3dfdde5de98680ea96e0e3bc6e30f52a51b2/R/is.ultrametric.R#L35">used in ape prior to version 4.0</a>.</p>
  <p>Load the whales tree into R and compute the root-to-tip distances for all tips:</p>
  <div class="language-r highlighter-rouge">
    <div class="highlight">
      <pre class="highlight"><code data-lang="r"><span class="n">tre</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">read.tree</span><span class="p">(</span><span class="s2">"https://raw.githubusercontent.com/macroevolution/bamm/ab1b69be13e9841d9e103170d0f61e4324f78676/examples/diversification/whales/whaletree.tre"</span><span class="p">)</span><span class="w">
<p></span><span class="n">N</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">Ntip</span><span class="p">(</span><span class="n">tre</span><span class="p">)</span><span class="w">
</span><span class="n">root_node</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">N</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="w">
</span><span class="n">root_to_tip</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">dist.nodes</span><span class="p">(</span><span class="n">tre</span><span class="p">)[</span><span class="m">1</span><span class="o">:</span><span class="n">N</span><span class="p">,</span><span class="w"> </span><span class="n">root_node</span><span class="p">]</span><span class="w">
</span></code></pre>
    </div>
  </div>
</p>
<p>Compute the variance using those distances:</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">var</span><span class="p">(</span><span class="n">root_to_tip</span><span class="p">)</span><span class="w">
</span><span class="c1">## [1] 6.519647e-13</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>The default tolerance in ape is the square root of R’s <a href="https://en.wikipedia.org/wiki/Machine_epsilon">machine epsilon</a>, defined as the smallest positive floating-point number <em>x</em> such that 1 + <em>x</em> ≠ 1. This can vary from computer to computer, but on my laptop, this value is:</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="nf">sqrt</span><span class="p">(</span><span class="n">.Machine</span><span class="o">$</span><span class="n">double.eps</span><span class="p">)</span><span class="w">
</span><span class="c1">## [1] 1.490116e-08</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>The whales tree is ultrametric using the variance method, since 1e-8 is much larger than 1e-13.</p>
<h2>Method 2: relative difference</h2>
<p>In ape 4.0, the <code>is.ultrametric</code> method <a href="https://github.com/cran/ape/blob/fa2a72e2814d26112792cf4676f6737abb7f7e0d/R/is.ultrametric.R#L35-L37">was changed</a> to use the relative difference of the minimum and maximum root-to-tip distances. I’ll get into why this was changed, but first, let’s look at how to calculate this value:</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">min_tip</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">min</span><span class="p">(</span><span class="n">root_to_tip</span><span class="p">)</span><span class="w">
</span><span class="n">max_tip</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">max</span><span class="p">(</span><span class="n">root_to_tip</span><span class="p">)</span><span class="w">
</span><span class="p">(</span><span class="n">max_tip</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">min_tip</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">max_tip</span><span class="w">
</span><span class="c1">## [1] 1.115516e-07</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>Immediately we can see why the whales tree stopped being ultrametric in ape 4.0, as this relative difference is larger than the default tolerance.</p>
<p>Why was this change made? It wasn’t merely to cause a lot of problems for everyone; instead, the answer is <a href="https://cran.r-project.org/web/packages/ape/ape.pdf#Rfn.is.ultrametric.1">in the documentation</a>, which tersely states:</p>
<blockquote>
  <p>The default criterion is invariant to linear changes of the branch lengths.</p>
</blockquote>
<p>What does this look like in practice? Let’s first scale the root-to-tip distance by multiplying everything by 1000:</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">scaled_root_to_tip</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">root_to_tip</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="m">1000</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>Now compare the variance and relative difference:</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">var</span><span class="p">(</span><span class="n">scaled_root_to_tip</span><span class="p">)</span><span class="w">
</span><span class="c1">## [1] 6.519647e-07</span><span class="w">
<p></span><span class="n">min_tip</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">min</span><span class="p">(</span><span class="n">scaled_root_to_tip</span><span class="p">)</span><span class="w">
</span><span class="n">max_tip</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">max</span><span class="p">(</span><span class="n">scaled_root_to_tip</span><span class="p">)</span><span class="w">
</span><span class="p">(</span><span class="n">max_tip</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">min_tip</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">max_tip</span><span class="w">
</span><span class="c1">## [1] 1.115516e-07</span><span class="w">
</span></code></pre>
  </div>
</div>
</p>
<p>Uh oh! Our whales are no longer ultrametric per the variance statistic, even though <em>none of the branch lengths have changed</em> relative to each other. You may not think that your phylogenies should be stretching like a taffy pull, but there are probably valid use cases for phylogenies like this.</p>
<p>This is the method that TACT currently uses to determine ultrametricity.</p>
<h2>Method 3: node ages</h2>
<p>The previous two methods all relied on comparing some aspect of root-to-tip distances. Here, we’ll actually compare the distances to the tips of <em>all nodes</em>. This is the method <a href="https://github.com/jeetsukumaran/DendroPy/blob/29fd294bf05d890ebf6a8d576c501e471db27ca1/src/dendropy/datamodel/treemodel.py#L5649-L5655">used in DendroPy</a> and the original impetus for this investigation. I’ll reimplement enough of this method in R to illustrate this technique.</p>
<p>First, reorder the tree for postorder traversal, and set up some convenience variables.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">tre_node_adjust</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">reorder</span><span class="p">(</span><span class="n">tre</span><span class="p">,</span><span class="w"> </span><span class="s2">"postorder"</span><span class="p">)</span><span class="w">
<p></span><span class="n">e1</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">tre_node_adjust</span><span class="o">$</span><span class="n">edge</span><span class="p">[,</span><span class="w"> </span><span class="m">1</span><span class="p">]</span><span class="w"> </span><span class="c1"># parent node</span><span class="w">
</span><span class="n">e2</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">tre_node_adjust</span><span class="o">$</span><span class="n">edge</span><span class="p">[,</span><span class="w"> </span><span class="m">2</span><span class="p">]</span><span class="w"> </span><span class="c1"># child node</span><span class="w">
</span><span class="n">EL</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">tre_node_adjust</span><span class="o">$</span><span class="n">edge.length</span><span class="w">
</span></code></pre>
  </div>
</div>
</p>
<p>Also set up an <code>ages</code> variable that will hold internal calculations for how old a node should be.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">ages</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">numeric</span><span class="p">(</span><span class="n">N</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">tre_node_adjust</span><span class="o">$</span><span class="n">Nnode</span><span class="p">)</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>Next, start iterating…</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">ii</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nf">seq_along</span><span class="p">(</span><span class="n">EL</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>If we haven’t already stored an age for the parent node, go ahead and compute that now from the (left)<sup class="footnote-ref"><a href="#fn1" id="fnref1">1</a></sup> child node  and the current edge length.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">ages</span><span class="p">[</span><span class="n">e1</span><span class="p">[</span><span class="n">ii</span><span class="p">]]</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="m">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="n">ages</span><span class="p">[</span><span class="n">e1</span><span class="p">[</span><span class="n">ii</span><span class="p">]]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ages</span><span class="p">[</span><span class="n">e2</span><span class="p">[</span><span class="n">ii</span><span class="p">]]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">EL</span><span class="p">[</span><span class="n">ii</span><span class="p">]</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>Otherwise, retrieve the stored age for the parent node, and re-compute what the age should be based on the (right)<sup class="footnote-ref"><a href="#fn1" id="fnref1">1</a></sup> child node.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="w">    </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="n">recorded_age</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ages</span><span class="p">[</span><span class="n">e1</span><span class="p">[</span><span class="n">ii</span><span class="p">]]</span><span class="w">
        </span><span class="n">new_age</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">ages</span><span class="p">[</span><span class="n">e2</span><span class="p">[</span><span class="n">ii</span><span class="p">]]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">EL</span><span class="p">[</span><span class="n">ii</span><span class="p">]</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>Now test whether those ages differ. I could actually use either the variance or the relative difference method, but here I’ll just check for absolute difference.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">recorded_age</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">new_age</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="n">cat</span><span class="p">(</span><span class="n">sprintf</span><span class="p">(</span><span class="s2">"node %i age %.6f != %.6f\n"</span><span class="p">,</span><span class="w"> </span><span class="n">e1</span><span class="p">[</span><span class="n">ii</span><span class="p">],</span><span class="w"> </span><span class="n">recorded_age</span><span class="p">,</span><span class="w"> </span><span class="n">new_age</span><span class="p">))</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
<p></span><span class="c1">## node 154 age 3.291163 != 3.291164</span><span class="w">
</span><span class="c1">## node 153 age 4.570892 != 4.570893</span><span class="w">
</span><span class="c1">## node 151 age 5.263495 != 5.263494</span><span class="w">
</span><span class="c1">## node 150 age 6.975185 != 6.975185</span><span class="w">
</span><span class="c1">## node 146 age 3.048675 != 3.048675</span><span class="w">
</span><span class="c1">## node 145 age 4.452720 != 4.452720</span><span class="w">
</span><span class="c1">## node 143 age 6.047030 != 6.047031</span><span class="w">
</span><span class="c1">## node 142 age 8.209050 != 8.209050</span><span class="w">
</span><span class="c1">## node 135 age 5.616381 != 5.616380</span><span class="w">
</span><span class="c1">## node 133 age 14.061554 != 14.061555</span><span class="w">
</span><span class="c1">## node 132 age 17.939426 != 17.939427</span><span class="w">
</span><span class="c1">## node 130 age 24.698214 != 24.698213</span><span class="w">
</span><span class="c1">## node 127 age 5.096384 != 5.096384</span><span class="w">
</span><span class="c1">## node 125 age 5.796587 != 5.796586</span><span class="w">
</span><span class="c1">## node 124 age 6.375631 != 6.375632</span><span class="w">
</span><span class="c1">## node 121 age 7.176680 != 7.176680</span><span class="w">
</span><span class="c1">## node 120 age 7.677424 != 7.677424</span><span class="w">
</span><span class="c1">## node 116 age 13.042869 != 13.042870</span><span class="w">
</span><span class="c1">## node 114 age 11.028304 != 11.028304</span><span class="w">
</span><span class="c1">## node 113 age 14.540283 != 14.540283</span><span class="w">
</span><span class="c1">## node 112 age 15.669702 != 15.669702</span><span class="w">
</span><span class="c1">## node 108 age 31.621529 != 31.621528</span><span class="w">
</span><span class="c1">## node 104 age 22.044391 != 22.044391</span><span class="w">
</span><span class="c1">## node 103 age 33.799003 != 33.799004</span><span class="w">
</span><span class="c1">## node 100 age 11.382958 != 11.382958</span><span class="w">
</span><span class="c1">## node 93 age 26.063016 != 26.063016</span><span class="w">
</span><span class="c1">## node 90 age 8.816019 != 8.816019</span><span class="w">
</span></code></pre>
  </div>
</div>
</p>
<p>Many internal nodes have ages that differ depending on whether you use the left or right child node to compute the age. This metric allows you to pinpoint exactly where in your phylogeny precision issues could be causing ultrametricity issues. I can imagine this being quite useful when you are grafting subtrees onto a backbone phylogeny and trying to figure out if you did the math on your branch lengths correctly.</p>
<h2>Fix 1: extending the tips</h2>
<p>Now that we’re aware of the differences between different ultrametricity checks, let’s look at ways to correct for phylogenies that aren’t quite there.</p>
<p>One possibility is to simply extend the tips of the tree until the root-to-tip distances are completely equal. This is implemented in R as <code>BioGeoBEARS::extend_tips_to_ultrametricize</code><sup class="footnote-ref"><a href="#fn2" id="fnref2">2</a></sup> and <code>phytools::force.ultrametric(method = &quot;extend&quot;)</code>.<sup class="footnote-ref"><a href="#fn3" id="fnref3">3</a></sup></p>
<p>First, set up a copy of the whales tree and compute some important values, including the difference from each root-to-tip distance to their maximum:</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">tre_extend</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">tre</span><span class="w">
</span><span class="n">age_difference</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">max</span><span class="p">(</span><span class="n">root_to_tip</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">root_to_tip</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>Next, grab the edges from the edge matrix that correspond to the tips. Note that the edges in <code>$edge.label</code> correspond to the <em>row numbers</em> in <code>$edge</code>, not the values in those rows! This is tricky and confusing. We can also assume that the tip edges appear in ascending order, from 1 to <em>N</em>.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">tip_edges</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">tre_extend</span><span class="o">$</span><span class="n">edge</span><span class="p">[,</span><span class="w"> </span><span class="m">2</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="n">Ntip</span><span class="p">(</span><span class="n">tre_extend</span><span class="p">)</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>Finally, extend the tips outwards and confirm that the new tree is ultrametric:</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">tre_extend</span><span class="o">$</span><span class="n">edge.length</span><span class="p">[</span><span class="n">tip_edges</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">tre_extend</span><span class="o">$</span><span class="n">edge.length</span><span class="p">[</span><span class="n">tip_edges</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">age_difference</span><span class="w">
</span><span class="n">is.ultrametric</span><span class="p">(</span><span class="n">tre_extend</span><span class="p">)</span><span class="w">
</span><span class="c1">## [1] TRUE</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>We can write a simple function in R that compares two phylogenies with identical topologies but differing branch lengths.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">diff_edge_lengths</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">phy</span><span class="p">,</span><span class="w"> </span><span class="n">phy2</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">diffs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">phy2</span><span class="o">$</span><span class="n">edge.length</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">phy</span><span class="o">$</span><span class="n">edge.length</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>Use the <code>sign</code> function, which returns -1, 0, or 1 when the input is negative, zero, or positive, respectively. Then assign each of those values to a color (or lack thereof). This is <a href="https://colorbrewer2.org/#type=diverging&amp;scheme=PiYG&amp;n=11">ColorBrewer palette PiYG</a>.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="w">    </span><span class="n">cols</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">sign</span><span class="p">(</span><span class="n">diffs</span><span class="p">)</span><span class="w">
    </span><span class="n">cols</span><span class="p">[</span><span class="n">cols</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="o">&lt;-</span><span class="w"> </span><span class="s2">"#7fbc41"</span><span class="w">
    </span><span class="n">cols</span><span class="p">[</span><span class="n">cols</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="o">&lt;-</span><span class="w"> </span><span class="s2">"#de77ae"</span><span class="w">
    </span><span class="n">cols</span><span class="p">[</span><span class="n">cols</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="m">0</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="kc">NA</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>Plot the tree and report the results. The plot needs a bit of adjustment since the defaults of <code>ape::plot.phylo</code> are a bit aggravating.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="w">    </span><span class="n">plot</span><span class="p">(</span><span class="n">phy</span><span class="p">,</span><span class="w"> </span><span class="n">show.tip.label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">,</span><span class="w"> </span><span class="n">no.margin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">)</span><span class="w">
    </span><span class="n">edgelabels</span><span class="p">(</span><span class="n">pch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">15</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">cols</span><span class="p">)</span><span class="w">
    </span><span class="n">sprintf</span><span class="p">(</span><span class="s2">"%i longer branches, %i shorter branches"</span><span class="p">,</span><span class="w"> </span><span class="nf">sum</span><span class="p">(</span><span class="n">diffs</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="m">0</span><span class="p">),</span><span class="w"> </span><span class="nf">sum</span><span class="p">(</span><span class="n">diffs</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="m">0</span><span class="p">))</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>This function places squares on branches that have changed in length, with red meaning a shorter branch and green meaning a longer branch. As expected, nearly all of the terminal branches have been extended to enforce ultrametricity.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">diff_edge_lengths</span><span class="p">(</span><span class="n">tre</span><span class="p">,</span><span class="w"> </span><span class="n">tre_extend</span><span class="p">)</span><span class="w">
</span><span class="c1">## [1] "85 longer branches, 0 shorter branches"</span><span class="w">
</span></code></pre>
  </div>
</div>
<p><img src="/uploads/2021/ultrametric/extend.png" alt="Phylogeny of whales, forced to be ultrametric via the “extend tips” method. 85 terminal branches have increased in length." /></p>
<h2>Fix 2: non-negative least squares</h2>
<p>This is the default approach used in the R function <code>phytools::force.ultrametric</code> and the topic of a <a href="http://blog.phytools.org/2016/08/fixing-ultrametric-tree-whose-edges-are.html">phytools blog post</a>.</p>
<blockquote>
  <p>This will give you the edge lengths that result in the distances between taxa with minimum sum of squared differences from the distances implied by your input tree, under the criterion that the resulting tree is ultrametric.</p>
</blockquote>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">tre_nnls</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">phangorn</span><span class="o">::</span><span class="n">nnls.tree</span><span class="p">(</span><span class="n">cophenetic</span><span class="p">(</span><span class="n">tre</span><span class="p">),</span><span class="w"> </span><span class="n">tre</span><span class="p">,</span><span class="w"> </span><span class="n">rooted</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">)</span><span class="w">
</span><span class="n">is.ultrametric</span><span class="p">(</span><span class="n">tre_nnls</span><span class="p">)</span><span class="w">
</span><span class="c1">## [1] TRUE</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>Since it’s trying to minimize differences among the pairwise tip distance matrix, you’d expect many branches to be adjusted. Plotting the differences show that this is indeed the case:</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">diff_edge_lengths</span><span class="p">(</span><span class="n">tre</span><span class="p">,</span><span class="w"> </span><span class="n">tre_nnls</span><span class="p">)</span><span class="w">
</span><span class="c1">## [1] "86 longer branches, 85 shorter branches"</span><span class="w">
</span></code></pre>
  </div>
</div>
<p><img src="/uploads/2021/ultrametric/nnls.png" alt="Phylogeny of whales, forced to be ultrametric via the “non-negative least squares” method. 171 branches have changed length, with about half becoming longer and half becoming shorter, in different parts of the tree." /></p>
<h2>Fix 3: node adjustment</h2>
<p>This is the approach optionally used in DendroPy,<sup class="footnote-ref"><a href="#fn4" id="fnref4">4</a></sup> and is how TACT fixes ultrametricity issues if asked. Whenever a node’s age differs between its left and right children, correct one of the branch lengths so the node’s age will be calculated the same regardless of whether you’re using the left descendants or the right descendants.<sup class="footnote-ref"><a href="#fn1" id="fnref1">1</a></sup></p>
<p>Change the prior loop that checks node ages to additionally adjust the branch length:</p>
<div class="language-diff highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="diff"> for (ii in seq_along(EL)) {
     if (ages[e1[ii]] == 0) {
         ages[e1[ii]] &lt;- ages[e2[ii]] + EL[ii]
     } else {
         recorded_age &lt;- ages[e1[ii]]
         new_age &lt;- ages[e2[ii]] + EL[ii]
         if (recorded_age != new_age) {
             cat(sprintf("node %i age %.6f != %.6f\n", e1[ii], recorded_age, new_age))
<span class="gi">+            EL[ii] &lt;- recorded_age - ages[e2[ii]]
</span>         }
     }
 }
</code></pre>
  </div>
</div>
<p>Then, update the branch lengths in the phylogeny itself and confirm that it’s ultrametric.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">tre_node_adjust</span><span class="o">$</span><span class="n">edge.length</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">EL</span><span class="w">
</span><span class="n">is.ultrametric</span><span class="p">(</span><span class="n">tre_node_adjust</span><span class="p">)</span><span class="w">
</span><span class="c1">## [1] TRUE</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>Plotting the differences shows that this method actually changes the fewest number of branches.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">diff_edge_lengths</span><span class="p">(</span><span class="n">tre</span><span class="p">,</span><span class="w"> </span><span class="n">tre_node_adjust</span><span class="p">)</span><span class="w">
</span><span class="c1">## [1] "13 longer branches, 14 shorter branches"</span><span class="w">
</span></code></pre>
  </div>
</div>
<p><img src="/uploads/2021/ultrametric/adjust_nodes.png" alt="Phylogeny of whales, forced to be ultrametric via the “node adjustment” method. 27 branches have changed length, with about half becoming longer and half becoming shorter, in different parts of the tree." /></p>
<h2>Issues with large phylogenies</h2>
<p>Another issue identified in the <a href="https://github.com/jonchang/tact/issues/230#issuecomment-871628907">bug report</a> was the inability to use <code>phytools::force.ultrametric</code> on large phylogenies. This is indeed the case when testing a random tree with 50,000 tips.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">library</span><span class="p">(</span><span class="n">ape</span><span class="p">)</span><span class="w">
</span><span class="p">(</span><span class="n">xx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rcoal</span><span class="p">(</span><span class="m">50000</span><span class="p">))</span><span class="w">
</span><span class="c1">## Phylogenetic tree with 50000 tips and 49999 internal nodes.</span><span class="w">
</span><span class="c1">## </span><span class="w">
</span><span class="c1">## Tip labels:</span><span class="w">
</span><span class="c1">##   t11339, t29898, t18919, t6336, t34524, t1665, ...</span><span class="w">
</span><span class="c1">## </span><span class="w">
</span><span class="c1">## Rooted; includes branch lengths.</span><span class="w">
<p></span><span class="n">is.ultrametric</span><span class="p">(</span><span class="n">xx</span><span class="p">)</span><span class="w">
</span><span class="c1">## [1] TRUE</span><span class="w"></p>
<p></span><span class="n">phytools</span><span class="o">::</span><span class="n">force.ultrametric</span><span class="p">(</span><span class="n">xx</span><span class="p">,</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">“nnls”</span><span class="p">)</span><span class="w">
</span><span class="c1">## Error in double(nm * nm) : vector size cannot be NA</span><span class="w">
</span><span class="c1">## In addition: Warning message:</span><span class="w">
</span><span class="c1">## In nm * nm : NAs produced by integer overflow</span><span class="w"></p>
<p></span><span class="n">phytools</span><span class="o">::</span><span class="n">force.ultrametric</span><span class="p">(</span><span class="n">xx</span><span class="p">,</span><span class="w"> </span><span class="n">method</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">“extend”</span><span class="p">)</span><span class="w">
</span><span class="c1">## Error: vector memory exhausted (limit reached?)</span><span class="w">
</span></code></pre>
  </div>
</div>
</p>
<p>With <code>method = &quot;extend&quot;</code>, its current implementation calls <code>diag(vcv(tree))</code>, which requires creating an <em>N</em> by <em>N</em> matrix as a temporary value, where N is the number of tips. With 50,000 tips this implies a vector of length 2.5 billion, which exceeds R’s vector limit of 2.1 billion. This function could be optimized to avoid this storage requirement. With <code>method = &quot;nnls&quot;</code> creating this <em>N</em> by <em>N</em> matrix may be unavoidable, so for large phylogenies consider using the tip extension or the node adjustment methods instead.</p>
<h2>Closing thoughts</h2>
<p>None of this really matters anyway, except in the special case of really big phylogenies, and even then not so much. If you’re certain your phylogeny is ultrametric, use any of these methods and you’ll be able to get your tree to be so precisely ultrametric that even the strictest tools can’t complain.</p>
<p>I’m serious! Look at how well the “extend tips” method works:</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">is.ultrametric</span><span class="p">(</span><span class="n">tre_extend</span><span class="p">,</span><span class="w"> </span><span class="n">tolerance</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">)</span><span class="w">
</span><span class="c1">## [1] TRUE</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>This means there are literally no differences in root-to-tip distances even to a precision of 16 digits. <a href="https://www.jpl.nasa.gov/edu/news/2016/3/16/how-many-decimals-of-pi-do-we-really-need/">This is enough to leave the solar system</a>, so it’s probably good enough for your phylogeny, which likely has more interesting sources of error beyond numerical precision.</p>
<h2>Notes</h2>
<ul>
  <li><a href="/uploads/2021/ultrametric/ultrametric.R">ultrametric.R</a></li>
  <li><a href="/uploads/2021/ultrametric/ultrametric.md">ultrametric.md</a></li>
</ul>
<section class="footnotes">
  <ol>
    <li id="fn1">
      <p>Left and right are arbitrary here and named only to help the reader distinguish between the two. <a href="#fnref1" class="footnote-backref">↩</a></p>
    </li>
    <li id="fn2">
      <p>BioGeoBEARS also has a function to average the heights of tip nodes. <a href="#fnref2" class="footnote-backref">↩</a></p>
    </li>
    <li id="fn3">
      <p>You can also read my <a href="https://github.com/macroevolution/bammtools/issues/45#issuecomment-289850728">embarrassing, non-working original attempt</a>. I’ve gotten better at R, I promise. <a href="#fnref3" class="footnote-backref">↩</a></p>
    </li>
    <li id="fn4">
      <p>Note that DendroPy lets you pick either the maximum or the minimum implied ages; my implementation here just picks the first one it sees, so is sensitive to rearrangements such as from a ladderized tree. <a href="#fnref4" class="footnote-backref">↩</a></p>
    </li>
  </ol>
</section>
]]></content>
</entry>
<entry>
  <title><![CDATA[Introducing fishtree and fishtreeoflife.org]]></title>
  <link rel="alternate" type="text/html" href="https://jonathanchang.org/blog/announcing-fishtree-an-r-package-to-access-phylogenetic-data-for-ray-finned-fishes/"/>
  <id>https://jonathanchang.org/blog/announcing-fishtree-an-r-package-to-access-phylogenetic-data-for-ray-finned-fishes</id>
  <published>2019-03-29T00:00:00+00:00</published>
  <updated>2019-03-29T00:00:00+00:00</updated>
  <content type="html"><![CDATA[
     <p>In our recent publication (<a href="https://doi.org/10.1038/s41586-018-0273-1">Rabosky et al. 2018</a>) we assembled a huge phylogeny of ray-finned fishes: the most comprehensive to date! While all of our data are <a href="https://doi.org/10.5061/dryad.fc71cp4">accessible via Dryad</a>, we felt like we could go the extra mile to make it easy to repurpose and reuse our work. I’m pleased to report that this effort has resulted in two resources for the community: the <a href="https://fishtreeoflife.org">Fish Tree of Life website</a>, and the <a href="https://cran.r-project.org/package=fishtree"><strong>fishtree</strong> R package</a>. The package is available on CRAN now, and you can install it with:</p>
  <div class="language-r highlighter-rouge">
    <div class="highlight">
      <pre class="highlight"><code data-lang="r"><span class="n">install.packages</span><span class="p">(</span><span class="s2">"fishtree"</span><span class="p">)</span><span class="w">
</span></code></pre>
    </div>
  </div>
  <p>The source is on Github in the repository <a href="https://github.com/jonchang/fishtree">jonchang/fishtree</a>. The manuscript describing these resources has been published in <em>Methods in Ecology and Evolution</em> (<a href="https://doi.org/10.1111/2041-210X.13182">Chang et al. 2019</a>).</p>
  <picture>
    <source type="image/svg+xml" srcset="/uploads/2019/fishtree-manuscript-fig-s1.svg">
    <img src="/uploads/2019/fishtree-package.png" alt="Figure S1 from our manuscript showing areas on the fish tree of life of long branch attraction." srcset="/uploads/2019/fishtree-manuscript-fig-s1.png 2x">
  </picture>
  <h2>Website: fishtreeoflife.org</h2>
  <p>The Fish Tree of Life website is intended to serve as a quick resource for when you need to look up information about ray-finned fishes. There are two primary types of pages on the website: <a href="https://fishtreeoflife.org/taxonomy/"><em>taxonomy</em> pages</a> and <a href="https://fishtreeoflife.org/fossils/"><em>fossil</em> pages</a>.</p>
  <p><img src="/uploads/2019/fishtree-website.png" alt="A portion of the backbone phylogeny leading to the taxonomy pages." srcset="/uploads/2019/fishtree-website.png 2x"></p>
  <p>For example, the taxonomy page for <a href="https://fishtreeoflife.org/taxonomy/family/Acanthuridae/">Acanthuridae</a>, the surgeonfishes, indicates that our phylogeny sampled most of the species in this family, and that one fossil calibration was used to date this group. You’ll also see that all associated taxonomic ranks (both more inclusive and less inclusive, if applicable) are listed.</p>
  <p>The download links lead you to subsetted versions of the phylogeny and character matrix constructed for this group. If you’re only interested in the surgeonfishes, you don’t have to download the entire phylogeny to get what you’re interested in.</p>
  <p>The fossil section links to a single species, <a href="https://fishtreeoflife.org/fossils/proacanthurus-tenuis/"><em>Proacanthurus tenuis</em>†</a>, that was used to calibrate the crown age of Acanthuridae. Fossil pages will all list what taxon they calibrate, as well as the minimum age that fossil informs, the computed maximum age, the placement authority reference, the age authority reference, and the fossil locality.</p>
  <p>The computed maximum age is based on the WHETA algorithm and the outgroup sequence listed. If you’re interested in the details, consult the <a href="https://fishtreeoflife.org/methods/#fossil-calibrations">Methods § Fossil Calibration</a> page.</p>
  <p>One thing that we’re especially proud of is that the website is completely static and nearly all text, and loads lightning fast. This is to support researchers working in areas where fast Internet is not available. Our only concession to vanity is on the home page, derived from Figure 3 of the <em>Nature</em> manuscript. The fishes were illustrated by <a href="https://www.lifesciencestudios.com/">Julie Johnson</a>; clicking on the different fish will take you to the appropriate taxon page.</p>
  <h2>R package: fishtree</h2>
  <p>In addition to the website, we’ve also developed an R package that interfaces with the underlying data.</p>
  <div class="language-console?lang=r&output=plaintext&prompt=>&comments=true highlighter-rouge">
    <div class="highlight">
      <pre class="highlight"><code data-lang="console?lang=r&output=plaintext&prompt=>&comments=true"><span class="gp">&gt;</span><span class="w"> </span><span class="n">library</span><span class="p">(</span><span class="n">fishtree</span><span class="p">)</span><span class="w">
</span><span class="gp">&gt;</span><span class="w"> </span><span class="n">library</span><span class="p">(</span><span class="n">ape</span><span class="p">)</span><span class="w">
</span><span class="gp">&gt;</span><span class="w"> </span><span class="n">phy</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">fishtree_phylogeny</span><span class="p">(</span><span class="n">rank</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Acanthuridae"</span><span class="p">)</span><span class="w">
</span><span class="gp">&gt;</span><span class="w"> </span><span class="n">phy</span><span class="w">
</span>
Phylogenetic tree with 67 tips and 66 internal nodes.
<p>Tip labels:
Acanthurus_mata, Acanthurus_blochii, Acanthurus_xanthopterus, Acanthurus_bariene, Acanthurus_dussumieri, Acanthurus_leucocheilus, …</p>
<p>Rooted; includes branch lengths.</p>
<p><span class="gp">&gt;</span><span class="w"> </span><span class="n">par</span><span class="p">(</span><span class="n">mfrow</span><span class="o">=</span><span class="nf">c</span><span class="p">(</span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">))</span><span class="w">
</span><span class="gp">&gt;</span><span class="w"> </span><span class="n">plot</span><span class="p">(</span><span class="n">phy</span><span class="p">,</span><span class="w"> </span><span class="n">show.tip.label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">)</span><span class="w">
</span><span class="gp">&gt;</span><span class="w"> </span><span class="n">ltt.plot</span><span class="p">(</span><span class="n">phy</span><span class="p">)</span><span class="w">
</span></code></pre>
    </div>
  </div>
  <picture></p>
  <source type="image/svg+xml" srcset="/uploads/2019/fishtree-package.svg">
  <img src="/uploads/2019/fishtree-package.png" alt="A phylogeny of Acanthuridae with a lineage-through-time plot." srcset="/uploads/2019/fishtree-package.png 2x">
</picture>
<p>The R package permits easy access to downloads of the phylogeny, sequence alignments, and taxonomic information for the ray-finned fishes. Not only are the pre-computed per-taxon subsets available, but the relevant functions also accept a list of species and will subset the larger dataset to return a sequence matrix or phylogeny including only those species.</p>
<p>Our intent with the R package is to enable more complex analyses with this broad dataset. One example that we showcase in <a href="https://doi.org/10.1111/2041-210X.13182">our manuscript</a> reanalyzes portions of the fish tree of life with <a href="https://cme.h-its.org/exelixis/web/software/raxml/index.html">RAxML</a> and <a href="http://stat.sys.i.kyoto-u.ac.jp/prog/consel/">CONSEL</a> and other programs to search for areas that might have been affected by long branch attraction. The code and data for this analysis are <a href="https://doi.org/10.5061/dryad.6vg974n">available on Dryad</a>.</p>
<p>Two other analyses are presented in the supplement of the manuscript; a more accessible web version is available in the <a href="https://cran.r-project.org/package=fishtree">Vignettes section on CRAN</a>. These cover <a href="https://cran.r-project.org/web/packages/fishtree/vignettes/comparative-analysis.html">a comparative analysis</a> that replicates a previous experiment in the tetradontid fishes (<a href="https://doi.org/10.1111/jeb.12112">Santini et al. 2013</a>), and a <a href="https://cran.r-project.org/web/packages/fishtree/vignettes/community-analysis.html">community phylogenetics analysis</a> looking at community structure in reef-associated fishes.</p>
<p>You may have noticed that each taxonomy page on the website <a href="https://fishtreeoflife.org/api/taxonomy/family/Acanthuridae.json">links to a JSON API file</a>. The <strong>fishtree</strong> package consumes these JSON files under the hood; you are welcome to use these directly, but we can’t guarantee that they won’t change in the future.</p>
<h2>The future</h2>
<p>There’s still plenty to work on for both the R package and the website, as well as ray-finned fish phylogenetics in general. Our plans for the R package are to extend its functionality to include the fossil data we have on the website.</p>
<p>The website is essentially feature-complete: there’s a lot of polish that could be done, and certainly other data sources we could incorporate, or come up with new ways to slice the data for easy consumption. The most important feature on our list is the ability to switch between alternate topologies; for example, how would the taxon pages look if we used older fish phylogenies (e.g., <a href="https://doi.org/10.1038/ncomms2958">Rabosky et al. 2013</a>) instead?</p>
<p>These and other features will have to wait. I have a ton of papers to write and jobs to apply for. My promise to the community is that this website and R package will continue to be maintained as long as I’m involved in scientific research. If you’re interested in helping out, pull requests are welcome on either of the GitHub repositories!</p>
<h2>Feedback</h2>
<p>If you encounter any problems with the R package, please <a href="https://github.com/jonchang/fishtree/issues/new/choose">open an issue on Github</a>. If you spot any bugs with the website, please <a href="https://github.com/jonchang/fishtreeoflife.org/issues/new">open an issue on Github for the website</a>. Feature requests are welcome as well, but I can’t guarantee I’ll get around to implementing your suggestions.</p>
<p>Finally, if you spot errors in the phylogeny (e.g., a rogue taxon or something like that), please report it in this <a href="https://docs.google.com/forms/d/e/1FAIpQLSeyE_NT5WiQA3Er62ZJzIHrRnOP0ASzPYrh294Nr5pOm4kTDg/viewform">Google Form</a> so I can collate these kind of fixes in a future update.</p>
<p>I’m excited to see what kinds of research these resources will enable! If you publish a paper or other analysis with this, please send me an email or <a href="https://twitter.com/chang_jon">tweet at me</a> so I can check it out!</p>
]]></content>
</entry>
<entry>
  <title><![CDATA[What R package for phylogenetics is the most popular?]]></title>
  <link rel="alternate" type="text/html" href="https://jonathanchang.org/blog/what-r-package-for-phylogenetics-is-the-most-popular/"/>
  <id>https://jonathanchang.org/blog/what-r-package-for-phylogenetics-is-the-most-popular</id>
  <published>2018-11-19T23:49:00+00:00</published>
  <updated>2018-11-19T23:49:00+00:00</updated>
  <content type="html"><![CDATA[
     <p>While writing my first R package and its associated manuscript, I needed to talk about some other R packages in the phylogenetics research community. The most obvious choice would be to just cite the ones that I actually use, but that doesn’t necessarily mean that other practicing phylogeneticists do the same. I needed to get some stats on which phylogenetics packages were actually popular, but luckily the R ecosystem has the tools to make this easy.</p>
  <p><a href="#table">Skip straight to the popularity table</a>.</p>
  <h2>Instructions</h2>
  <p>First, let’s install the <a href="https://cran.r-project.org/package=ctv">CRAN Task Views</a> package and the <a href="https://github.com/metacran/cranlogs">CRAN-logs</a> API package. We’ll use the development version of <code>cranlogs</code> since it hasn’t been updated on CRAN in a while and some stuff has changed.</p>
  <div class="language-r highlighter-rouge">
    <div class="highlight">
      <pre class="highlight"><code data-lang="r"><span class="n">install.packages</span><span class="p">(</span><span class="s2">"ctv"</span><span class="p">)</span><span class="w">
</span><span class="n">devtools</span><span class="o">::</span><span class="n">install_github</span><span class="p">(</span><span class="s2">"metacran/cranlogs"</span><span class="p">)</span><span class="w">
<p></span><span class="n">library</span><span class="p">(</span><span class="n">ctv</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">cranlogs</span><span class="p">)</span><span class="w">
</span></code></pre>
    </div>
  </div>
</p>
<p>We can get a list of all the CRAN task views using the <code>available.views</code> function. Annoyingly, there’s no way to filter and extract JUST the <a href="https://cran.r-project.org/view=Phylogenetics">Phylogenetics task view</a>, so we’ll have to write a short filter to extract it.<sup class="footnote-ref"><a href="#fn1" id="fnref1">1</a></sup></p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">available.views</span><span class="p">()</span><span class="w">
<p></span><span class="n">phylo_ctv</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">Filter</span><span class="p">(</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="n">x</span><span class="o">$</span><span class="n">name</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s2">“Phylogenetics”</span><span class="p">,</span><span class="w"> </span><span class="n">available.views</span><span class="p">())[[</span><span class="m">1</span><span class="p">]]</span><span class="w">
</span></code></pre>
  </div>
</div>
</p>
<p>Now we can extract the list of packages that are associated with the “Phylogenetics” task view, and using that list of packages, query the CRAN-logs server to figure out the most popular phylogenetics packages in the last year.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">phylo_ctv</span><span class="o">$</span><span class="n">packagelist</span><span class="w">
</span><span class="n">phylo_packages</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">phylo_ctv</span><span class="o">$</span><span class="n">packagelist</span><span class="o">$</span><span class="n">name</span><span class="w">
<p></span><span class="n">output</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">cran_downloads</span><span class="p">(</span><span class="n">to</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">“2018-10-01”</span><span class="p">,</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">“2017-10-01”</span><span class="p">,</span><span class="w"> </span><span class="n">packages</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">phylo_packages</span><span class="p">)</span><span class="w">
</span><span class="n">head</span><span class="p">(</span><span class="n">output</span><span class="p">)</span><span class="w">
</span></code></pre>
  </div>
</div>
</p>
<p>Of course, what about the phylogenetics packages that aren’t in the Phylogenetics task view? Another way to view it is to consider if a package depends on the <code>ape</code> package. Pretty much every phylogenetics package will use ape in some form or another, so it might also be a good proxy of what a “phylo” package is.</p>
<p>Get a list of all the reverse dependencies of <code>ape</code> using <code>devtools</code>.<sup class="footnote-ref"><a href="#fn2" id="fnref2">2</a></sup></p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">revdep_packages</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">devtools</span><span class="o">::</span><span class="n">revdep</span><span class="p">(</span><span class="s2">"ape"</span><span class="p">)</span><span class="w">
</span><span class="n">all_phylo_packages</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">unique</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="n">phylo_packages</span><span class="p">,</span><span class="w"> </span><span class="n">revdep_packages</span><span class="p">))</span><span class="w">
</span><span class="n">output2</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">cran_downloads</span><span class="p">(</span><span class="n">to</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"2018-10-01"</span><span class="p">,</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"2017-10-01"</span><span class="p">,</span><span class="w"> </span><span class="n">packages</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">all_phylo_packages</span><span class="p">)</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>By default, the output from cranlogs is the number of downloads for a given package on a given date. We want to sum up all of these counts so we have a total number of downloads per package.</p>
<p>Aggregate these data using <code>dplyr</code>.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">library</span><span class="p">(</span><span class="n">dplyr</span><span class="p">)</span><span class="w">
<p></span><span class="n">output2</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> </span><span class="n">group_by</span><span class="p">(</span><span class="n">package</span><span class="p">)</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> </span><span class="n">summarise</span><span class="p">(</span><span class="n">downloads</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">sum</span><span class="p">(</span><span class="n">count</span><span class="p">))</span><span class="w"> </span><span class="o">%&gt;%</span><span class="w"> </span><span class="n">arrange</span><span class="p">(</span><span class="o">-</span><span class="n">downloads</span><span class="p">)</span><span class="w">
</span></code></pre>
  </div>
</div>
</p>
<h2>Exercises</h2>
<ol>
  <li>
    <p>Filter the table to only include packages in both the CRAN task view and reverse dependencies list. (This will exclude e.g., <code>ggplot2</code> and other arguably-peripheral packages.)</p>
  </li>
  <li>
    <p>Use the <a href="https://r4ds.had.co.nz/dates-and-times.html"><code>lubridate</code> package</a> to find the ten most popular packages by year. (The CRAN logs go back to October 2012.)</p>
  </li>
  <li>
    <p>Check out Brian O’Meara’s <a href="https://github.com/bomeara/summarizetaskview/blob/fd990fb7a19cf03cb0c5da9d87c1c808534658cc/README.md">excellent work showing how popular phylogenetics packages changed over time</a>!</p>
  </li>
</ol>
<h2>Table</h2>
<p>Here’s the full version of the table. There’s some packages in here that are only peripherally associated with phylogenetics, but it gives a good picture of what the state of the field looks like. I’ve also annotated each package with which list it came from, the CRAN Task View list or the reverse dependencies list.</p>
<!--
output2 %>% group_by(package) %>% summarise(downloads = sum(count)) %>% mutate(in_ctv = package %in% phylo_packages, in_revdep = package %in% revdep_packages) %>% arrange(-downloads) %>% transmute(rank = row_number(), package = paste0("[", package, "](https://cran.r-project.org/package=", package, ")"), downloads, in_ctv, in_revdep) %>% knitr::kable()
-->
<table>
  <thead>
    <tr>
      <th align="right"></th>
      <th align="left">Package</th>
      <th align="right">Downloads</th>
      <th align="left">CTV?</th>
      <th align="left">Revdep?</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td align="right">1</td>
      <td align="left"><a href="https://cran.r-project.org/package=ggplot2">ggplot2</a></td>
      <td align="right">5624177</td>
      <td align="left">✅</td>
      <td align="left">🚫</td>
    </tr>
    <tr>
      <td align="right">2</td>
      <td align="left"><a href="https://cran.r-project.org/package=igraph">igraph</a></td>
      <td align="right">1248409</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">3</td>
      <td align="left"><a href="https://cran.r-project.org/package=dendextend">dendextend</a></td>
      <td align="right">440772</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">4</td>
      <td align="left"><a href="https://cran.r-project.org/package=ape">ape</a></td>
      <td align="right">433337</td>
      <td align="left">✅</td>
      <td align="left">🚫</td>
    </tr>
    <tr>
      <td align="right">5</td>
      <td align="left"><a href="https://cran.r-project.org/package=vegan">vegan</a></td>
      <td align="right">426398</td>
      <td align="left">✅</td>
      <td align="left">🚫</td>
    </tr>
    <tr>
      <td align="right">6</td>
      <td align="left"><a href="https://cran.r-project.org/package=ade4">ade4</a></td>
      <td align="right">336755</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">7</td>
      <td align="left"><a href="https://cran.r-project.org/package=brms">brms</a></td>
      <td align="right">90632</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">8</td>
      <td align="left"><a href="https://cran.r-project.org/package=phangorn">phangorn</a></td>
      <td align="right">78319</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">9</td>
      <td align="left"><a href="https://cran.r-project.org/package=adegenet">adegenet</a></td>
      <td align="right">68176</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">10</td>
      <td align="left"><a href="https://cran.r-project.org/package=metafor">metafor</a></td>
      <td align="right">64462</td>
      <td align="left">✅</td>
      <td align="left">🚫</td>
    </tr>
    <tr>
      <td align="right">11</td>
      <td align="left"><a href="https://cran.r-project.org/package=data.tree">data.tree</a></td>
      <td align="right">60224</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">12</td>
      <td align="left"><a href="https://cran.r-project.org/package=Seurat">Seurat</a></td>
      <td align="right">55905</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">13</td>
      <td align="left"><a href="https://cran.r-project.org/package=MCMCglmm">MCMCglmm</a></td>
      <td align="right">48496</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">14</td>
      <td align="left"><a href="https://cran.r-project.org/package=phytools">phytools</a></td>
      <td align="right">43721</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">15</td>
      <td align="left"><a href="https://cran.r-project.org/package=HSAUR2">HSAUR2</a></td>
      <td align="right">40079</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">16</td>
      <td align="left"><a href="https://cran.r-project.org/package=HSAUR">HSAUR</a></td>
      <td align="right">38960</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">17</td>
      <td align="left"><a href="https://cran.r-project.org/package=taxize">taxize</a></td>
      <td align="right">34790</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">18</td>
      <td align="left"><a href="https://cran.r-project.org/package=rncl">rncl</a></td>
      <td align="right">31910</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">19</td>
      <td align="left"><a href="https://cran.r-project.org/package=aqp">aqp</a></td>
      <td align="right">30681</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">20</td>
      <td align="left"><a href="https://cran.r-project.org/package=pegas">pegas</a></td>
      <td align="right">29424</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">21</td>
      <td align="left"><a href="https://cran.r-project.org/package=RNeXML">RNeXML</a></td>
      <td align="right">29278</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">22</td>
      <td align="left"><a href="https://cran.r-project.org/package=rotl">rotl</a></td>
      <td align="right">28502</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">23</td>
      <td align="left"><a href="https://cran.r-project.org/package=picante">picante</a></td>
      <td align="right">26858</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">24</td>
      <td align="left"><a href="https://cran.r-project.org/package=geiger">geiger</a></td>
      <td align="right">26292</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">25</td>
      <td align="left"><a href="https://cran.r-project.org/package=phylobase">phylobase</a></td>
      <td align="right">25190</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">26</td>
      <td align="left"><a href="https://cran.r-project.org/package=HSAUR3">HSAUR3</a></td>
      <td align="right">24462</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">27</td>
      <td align="left"><a href="https://cran.r-project.org/package=FD">FD</a></td>
      <td align="right">23007</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">28</td>
      <td align="left"><a href="https://cran.r-project.org/package=EpiModel">EpiModel</a></td>
      <td align="right">21486</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">29</td>
      <td align="left"><a href="https://cran.r-project.org/package=adephylo">adephylo</a></td>
      <td align="right">20348</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">30</td>
      <td align="left"><a href="https://cran.r-project.org/package=poppr">poppr</a></td>
      <td align="right">19874</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">31</td>
      <td align="left"><a href="https://cran.r-project.org/package=vcfR">vcfR</a></td>
      <td align="right">19346</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">32</td>
      <td align="left"><a href="https://cran.r-project.org/package=geomorph">geomorph</a></td>
      <td align="right">18793</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">33</td>
      <td align="left"><a href="https://cran.r-project.org/package=adespatial">adespatial</a></td>
      <td align="right">18084</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">34</td>
      <td align="left"><a href="https://cran.r-project.org/package=ggimage">ggimage</a></td>
      <td align="right">16587</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">35</td>
      <td align="left"><a href="https://cran.r-project.org/package=BoSSA">BoSSA</a></td>
      <td align="right">15993</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">36</td>
      <td align="left"><a href="https://cran.r-project.org/package=asnipe">asnipe</a></td>
      <td align="right">14109</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">37</td>
      <td align="left"><a href="https://cran.r-project.org/package=hierfstat">hierfstat</a></td>
      <td align="right">13967</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">38</td>
      <td align="left"><a href="https://cran.r-project.org/package=caper">caper</a></td>
      <td align="right">13847</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">39</td>
      <td align="left"><a href="https://cran.r-project.org/package=DDD">DDD</a></td>
      <td align="right">11690</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">40</td>
      <td align="left"><a href="https://cran.r-project.org/package=tidygraph">tidygraph</a></td>
      <td align="right">11612</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">41</td>
      <td align="left"><a href="https://cran.r-project.org/package=DHARMa">DHARMa</a></td>
      <td align="right">11596</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">42</td>
      <td align="left"><a href="https://cran.r-project.org/package=paleotree">paleotree</a></td>
      <td align="right">11359</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">43</td>
      <td align="left"><a href="https://cran.r-project.org/package=betapart">betapart</a></td>
      <td align="right">11111</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">44</td>
      <td align="left"><a href="https://cran.r-project.org/package=polysat">polysat</a></td>
      <td align="right">10831</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">45</td>
      <td align="left"><a href="https://cran.r-project.org/package=phyclust">phyclust</a></td>
      <td align="right">10631</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">46</td>
      <td align="left"><a href="https://cran.r-project.org/package=MVA">MVA</a></td>
      <td align="right">10280</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">47</td>
      <td align="left"><a href="https://cran.r-project.org/package=GUniFrac">GUniFrac</a></td>
      <td align="right">9007</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">48</td>
      <td align="left"><a href="https://cran.r-project.org/package=enveomics.R">enveomics.R</a></td>
      <td align="right">8696</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">49</td>
      <td align="left"><a href="https://cran.r-project.org/package=AbSim">AbSim</a></td>
      <td align="right">8432</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">50</td>
      <td align="left"><a href="https://cran.r-project.org/package=stylo">stylo</a></td>
      <td align="right">8336</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">51</td>
      <td align="left"><a href="https://cran.r-project.org/package=phylolm">phylolm</a></td>
      <td align="right">8172</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">52</td>
      <td align="left"><a href="https://cran.r-project.org/package=apTreeshape">apTreeshape</a></td>
      <td align="right">8101</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">53</td>
      <td align="left"><a href="https://cran.r-project.org/package=BioGeoBEARS">BioGeoBEARS</a></td>
      <td align="right">8007</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">54</td>
      <td align="left"><a href="https://cran.r-project.org/package=expands">expands</a></td>
      <td align="right">7764</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">55</td>
      <td align="left"><a href="https://cran.r-project.org/package=mvMORPH">mvMORPH</a></td>
      <td align="right">7579</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">56</td>
      <td align="left"><a href="https://cran.r-project.org/package=BAMMtools">BAMMtools</a></td>
      <td align="right">7344</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">57</td>
      <td align="left"><a href="https://cran.r-project.org/package=sand">sand</a></td>
      <td align="right">7014</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">58</td>
      <td align="left"><a href="https://cran.r-project.org/package=diversitree">diversitree</a></td>
      <td align="right">6946</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">59</td>
      <td align="left"><a href="https://cran.r-project.org/package=homals">homals</a></td>
      <td align="right">6933</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">60</td>
      <td align="left"><a href="https://cran.r-project.org/package=tidytree">tidytree</a></td>
      <td align="right">6378</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">61</td>
      <td align="left"><a href="https://cran.r-project.org/package=convevol">convevol</a></td>
      <td align="right">6351</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">62</td>
      <td align="left"><a href="https://cran.r-project.org/package=ecospat">ecospat</a></td>
      <td align="right">6320</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">63</td>
      <td align="left"><a href="https://cran.r-project.org/package=entropart">entropart</a></td>
      <td align="right">6299</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">64</td>
      <td align="left"><a href="https://cran.r-project.org/package=phylotools">phylotools</a></td>
      <td align="right">6182</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">65</td>
      <td align="left"><a href="https://cran.r-project.org/package=rmetasim">rmetasim</a></td>
      <td align="right">6064</td>
      <td align="left">✅</td>
      <td align="left">🚫</td>
    </tr>
    <tr>
      <td align="right">66</td>
      <td align="left"><a href="https://cran.r-project.org/package=rphast">rphast</a></td>
      <td align="right">5890</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">67</td>
      <td align="left"><a href="https://cran.r-project.org/package=corHMM">corHMM</a></td>
      <td align="right">5836</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">68</td>
      <td align="left"><a href="https://cran.r-project.org/package=apex">apex</a></td>
      <td align="right">5750</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">69</td>
      <td align="left"><a href="https://cran.r-project.org/package=bayou">bayou</a></td>
      <td align="right">5686</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">70</td>
      <td align="left"><a href="https://cran.r-project.org/package=cati">cati</a></td>
      <td align="right">5572</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">71</td>
      <td align="left"><a href="https://cran.r-project.org/package=ouch">ouch</a></td>
      <td align="right">5520</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">72</td>
      <td align="left"><a href="https://cran.r-project.org/package=hisse">hisse</a></td>
      <td align="right">5267</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">73</td>
      <td align="left"><a href="https://cran.r-project.org/package=phyloclim">phyloclim</a></td>
      <td align="right">5223</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">74</td>
      <td align="left"><a href="https://cran.r-project.org/package=rdryad">rdryad</a></td>
      <td align="right">5188</td>
      <td align="left">✅</td>
      <td align="left">🚫</td>
    </tr>
    <tr>
      <td align="right">75</td>
      <td align="left"><a href="https://cran.r-project.org/package=dartR">dartR</a></td>
      <td align="right">5169</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">76</td>
      <td align="left"><a href="https://cran.r-project.org/package=SYNCSA">SYNCSA</a></td>
      <td align="right">5102</td>
      <td align="left">✅</td>
      <td align="left">🚫</td>
    </tr>
    <tr>
      <td align="right">77</td>
      <td align="left"><a href="https://cran.r-project.org/package=OutbreakTools">OutbreakTools</a></td>
      <td align="right">5057</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">78</td>
      <td align="left"><a href="https://cran.r-project.org/package=TreeSim">TreeSim</a></td>
      <td align="right">4988</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">79</td>
      <td align="left"><a href="https://cran.r-project.org/package=ALA4R">ALA4R</a></td>
      <td align="right">4943</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">80</td>
      <td align="left"><a href="https://cran.r-project.org/package=ips">ips</a></td>
      <td align="right">4873</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">81</td>
      <td align="left"><a href="https://cran.r-project.org/package=PCPS">PCPS</a></td>
      <td align="right">4858</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">82</td>
      <td align="left"><a href="https://cran.r-project.org/package=metacoder">metacoder</a></td>
      <td align="right">4796</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">83</td>
      <td align="left"><a href="https://cran.r-project.org/package=OUwie">OUwie</a></td>
      <td align="right">4792</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">84</td>
      <td align="left"><a href="https://cran.r-project.org/package=aphid">aphid</a></td>
      <td align="right">4791</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">85</td>
      <td align="left"><a href="https://cran.r-project.org/package=brranching">brranching</a></td>
      <td align="right">4670</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">86</td>
      <td align="left"><a href="https://cran.r-project.org/package=warbleR">warbleR</a></td>
      <td align="right">4645</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">87</td>
      <td align="left"><a href="https://cran.r-project.org/package=MPSEM">MPSEM</a></td>
      <td align="right">4639</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">88</td>
      <td align="left"><a href="https://cran.r-project.org/package=adhoc">adhoc</a></td>
      <td align="right">4599</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">89</td>
      <td align="left"><a href="https://cran.r-project.org/package=distory">distory</a></td>
      <td align="right">4578</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">90</td>
      <td align="left"><a href="https://cran.r-project.org/package=Momocs">Momocs</a></td>
      <td align="right">4443</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">91</td>
      <td align="left"><a href="https://cran.r-project.org/package=phyloTop">phyloTop</a></td>
      <td align="right">4426</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">92</td>
      <td align="left"><a href="https://cran.r-project.org/package=ggmuller">ggmuller</a></td>
      <td align="right">4403</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">93</td>
      <td align="left"><a href="https://cran.r-project.org/package=paleoTS">paleoTS</a></td>
      <td align="right">4392</td>
      <td align="left">✅</td>
      <td align="left">🚫</td>
    </tr>
    <tr>
      <td align="right">94</td>
      <td align="left"><a href="https://cran.r-project.org/package=BIEN">BIEN</a></td>
      <td align="right">4380</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">95</td>
      <td align="left"><a href="https://cran.r-project.org/package=HTSSIP">HTSSIP</a></td>
      <td align="right">4198</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">96</td>
      <td align="left"><a href="https://cran.r-project.org/package=strap">strap</a></td>
      <td align="right">4153</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">97</td>
      <td align="left"><a href="https://cran.r-project.org/package=nodiv">nodiv</a></td>
      <td align="right">4103</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">98</td>
      <td align="left"><a href="https://cran.r-project.org/package=BPEC">BPEC</a></td>
      <td align="right">4095</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">99</td>
      <td align="left"><a href="https://cran.r-project.org/package=scrm">scrm</a></td>
      <td align="right">4084</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">100</td>
      <td align="left"><a href="https://cran.r-project.org/package=FinePop">FinePop</a></td>
      <td align="right">4031</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">101</td>
      <td align="left"><a href="https://cran.r-project.org/package=idendr0">idendr0</a></td>
      <td align="right">4020</td>
      <td align="left">✅</td>
      <td align="left">🚫</td>
    </tr>
    <tr>
      <td align="right">102</td>
      <td align="left"><a href="https://cran.r-project.org/package=HMPTrees">HMPTrees</a></td>
      <td align="right">4015</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">103</td>
      <td align="left"><a href="https://cran.r-project.org/package=PHYLOGR">PHYLOGR</a></td>
      <td align="right">4011</td>
      <td align="left">✅</td>
      <td align="left">🚫</td>
    </tr>
    <tr>
      <td align="right">104</td>
      <td align="left"><a href="https://cran.r-project.org/package=evobiR">evobiR</a></td>
      <td align="right">4000</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">105</td>
      <td align="left"><a href="https://cran.r-project.org/package=outbreaker">outbreaker</a></td>
      <td align="right">3931</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">106</td>
      <td align="left"><a href="https://cran.r-project.org/package=nLTT">nLTT</a></td>
      <td align="right">3925</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">107</td>
      <td align="left"><a href="https://cran.r-project.org/package=kmer">kmer</a></td>
      <td align="right">3891</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">108</td>
      <td align="left"><a href="https://cran.r-project.org/package=markophylo">markophylo</a></td>
      <td align="right">3885</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">109</td>
      <td align="left"><a href="https://cran.r-project.org/package=DAMOCLES">DAMOCLES</a></td>
      <td align="right">3879</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">110</td>
      <td align="left"><a href="https://cran.r-project.org/package=jaatha">jaatha</a></td>
      <td align="right">3861</td>
      <td align="left">✅</td>
      <td align="left">🚫</td>
    </tr>
    <tr>
      <td align="right">111</td>
      <td align="left"><a href="https://cran.r-project.org/package=TESS">TESS</a></td>
      <td align="right">3860</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">112</td>
      <td align="left"><a href="https://cran.r-project.org/package=SigTree">SigTree</a></td>
      <td align="right">3823</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">113</td>
      <td align="left"><a href="https://cran.r-project.org/package=strataG">strataG</a></td>
      <td align="right">3786</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">114</td>
      <td align="left"><a href="https://cran.r-project.org/package=treeplyr">treeplyr</a></td>
      <td align="right">3732</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">115</td>
      <td align="left"><a href="https://cran.r-project.org/package=phylogram">phylogram</a></td>
      <td align="right">3726</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">116</td>
      <td align="left"><a href="https://cran.r-project.org/package=treebase">treebase</a></td>
      <td align="right">3724</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">117</td>
      <td align="left"><a href="https://cran.r-project.org/package=pmc">pmc</a></td>
      <td align="right">3717</td>
      <td align="left">✅</td>
      <td align="left">🚫</td>
    </tr>
    <tr>
      <td align="right">118</td>
      <td align="left"><a href="https://cran.r-project.org/package=surface">surface</a></td>
      <td align="right">3701</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">119</td>
      <td align="left"><a href="https://cran.r-project.org/package=gamclass">gamclass</a></td>
      <td align="right">3648</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">120</td>
      <td align="left"><a href="https://cran.r-project.org/package=TreePar">TreePar</a></td>
      <td align="right">3647</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">121</td>
      <td align="left"><a href="https://cran.r-project.org/package=PBD">PBD</a></td>
      <td align="right">3591</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">122</td>
      <td align="left"><a href="https://cran.r-project.org/package=RAM">RAM</a></td>
      <td align="right">3546</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">123</td>
      <td align="left"><a href="https://cran.r-project.org/package=Rphylip">Rphylip</a></td>
      <td align="right">3506</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">124</td>
      <td align="left"><a href="https://cran.r-project.org/package=expoTree">expoTree</a></td>
      <td align="right">3447</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">125</td>
      <td align="left"><a href="https://cran.r-project.org/package=HyPhy">HyPhy</a></td>
      <td align="right">3419</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">126</td>
      <td align="left"><a href="https://cran.r-project.org/package=adiv">adiv</a></td>
      <td align="right">3393</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">127</td>
      <td align="left"><a href="https://cran.r-project.org/package=coalescentMCMC">coalescentMCMC</a></td>
      <td align="right">3384</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">128</td>
      <td align="left"><a href="https://cran.r-project.org/package=kdetrees">kdetrees</a></td>
      <td align="right">3330</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">129</td>
      <td align="left"><a href="https://cran.r-project.org/package=adaptiveGPCA">adaptiveGPCA</a></td>
      <td align="right">3325</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">130</td>
      <td align="left"><a href="https://cran.r-project.org/package=MAGNAMWAR">MAGNAMWAR</a></td>
      <td align="right">3324</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">131</td>
      <td align="left"><a href="https://cran.r-project.org/package=phylocanvas">phylocanvas</a></td>
      <td align="right">3302</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">132</td>
      <td align="left"><a href="https://cran.r-project.org/package=iteRates">iteRates</a></td>
      <td align="right">3301</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">133</td>
      <td align="left"><a href="https://cran.r-project.org/package=BBMV">BBMV</a></td>
      <td align="right">3295</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">134</td>
      <td align="left"><a href="https://cran.r-project.org/package=CommEcol">CommEcol</a></td>
      <td align="right">3246</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">135</td>
      <td align="left"><a href="https://cran.r-project.org/package=netdiffuseR">netdiffuseR</a></td>
      <td align="right">3191</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">136</td>
      <td align="left"><a href="https://cran.r-project.org/package=pastis">pastis</a></td>
      <td align="right">3155</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">137</td>
      <td align="left"><a href="https://cran.r-project.org/package=AnnotationBustR">AnnotationBustR</a></td>
      <td align="right">3148</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">138</td>
      <td align="left"><a href="https://cran.r-project.org/package=phyloland">phyloland</a></td>
      <td align="right">3129</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">139</td>
      <td align="left"><a href="https://cran.r-project.org/package=phyext2">phyext2</a></td>
      <td align="right">3119</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">140</td>
      <td align="left"><a href="https://cran.r-project.org/package=Canopy">Canopy</a></td>
      <td align="right">2992</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">141</td>
      <td align="left"><a href="https://cran.r-project.org/package=RPANDA">RPANDA</a></td>
      <td align="right">2952</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">142</td>
      <td align="left"><a href="https://cran.r-project.org/package=BMhyb">BMhyb</a></td>
      <td align="right">2935</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">143</td>
      <td align="left"><a href="https://cran.r-project.org/package=phylopath">phylopath</a></td>
      <td align="right">2912</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">144</td>
      <td align="left"><a href="https://cran.r-project.org/package=GLSME">GLSME</a></td>
      <td align="right">2846</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">145</td>
      <td align="left"><a href="https://cran.r-project.org/package=phylotate">phylotate</a></td>
      <td align="right">2846</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">146</td>
      <td align="left"><a href="https://cran.r-project.org/package=phylosignal">phylosignal</a></td>
      <td align="right">2820</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">147</td>
      <td align="left"><a href="https://cran.r-project.org/package=shazam">shazam</a></td>
      <td align="right">2754</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">148</td>
      <td align="left"><a href="https://cran.r-project.org/package=harrietr">harrietr</a></td>
      <td align="right">2707</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">149</td>
      <td align="left"><a href="https://cran.r-project.org/package=prioritizr">prioritizr</a></td>
      <td align="right">2705</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">150</td>
      <td align="left"><a href="https://cran.r-project.org/package=BarcodingR">BarcodingR</a></td>
      <td align="right">2657</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">151</td>
      <td align="left"><a href="https://cran.r-project.org/package=msaR">msaR</a></td>
      <td align="right">2552</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">152</td>
      <td align="left"><a href="https://cran.r-project.org/package=mvSLOUCH">mvSLOUCH</a></td>
      <td align="right">2522</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">153</td>
      <td align="left"><a href="https://cran.r-project.org/package=bcRep">bcRep</a></td>
      <td align="right">2472</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">154</td>
      <td align="left"><a href="https://cran.r-project.org/package=colordistance">colordistance</a></td>
      <td align="right">2448</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">155</td>
      <td align="left"><a href="https://cran.r-project.org/package=treeman">treeman</a></td>
      <td align="right">2417</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">156</td>
      <td align="left"><a href="https://cran.r-project.org/package=sharpshootR">sharpshootR</a></td>
      <td align="right">2409</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">157</td>
      <td align="left"><a href="https://cran.r-project.org/package=BMhyd">BMhyd</a></td>
      <td align="right">2408</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">158</td>
      <td align="left"><a href="https://cran.r-project.org/package=aptg">aptg</a></td>
      <td align="right">2385</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">159</td>
      <td align="left"><a href="https://cran.r-project.org/package=qlcData">qlcData</a></td>
      <td align="right">2383</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">160</td>
      <td align="left"><a href="https://cran.r-project.org/package=sensiPhy">sensiPhy</a></td>
      <td align="right">2344</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">161</td>
      <td align="left"><a href="https://cran.r-project.org/package=GrammR">GrammR</a></td>
      <td align="right">2229</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">162</td>
      <td align="left"><a href="https://cran.r-project.org/package=treespace">treespace</a></td>
      <td align="right">2219</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">163</td>
      <td align="left"><a href="https://cran.r-project.org/package=dispRity">dispRity</a></td>
      <td align="right">2217</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">164</td>
      <td align="left"><a href="https://cran.r-project.org/package=metricTester">metricTester</a></td>
      <td align="right">2205</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">165</td>
      <td align="left"><a href="https://cran.r-project.org/package=evolqg">evolqg</a></td>
      <td align="right">2181</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">166</td>
      <td align="left"><a href="https://cran.r-project.org/package=geomedb">geomedb</a></td>
      <td align="right">2160</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">167</td>
      <td align="left"><a href="https://cran.r-project.org/package=PhyloMeasures">PhyloMeasures</a></td>
      <td align="right">2139</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">168</td>
      <td align="left"><a href="https://cran.r-project.org/package=CNull">CNull</a></td>
      <td align="right">2107</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">169</td>
      <td align="left"><a href="https://cran.r-project.org/package=taxlist">taxlist</a></td>
      <td align="right">2098</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">170</td>
      <td align="left"><a href="https://cran.r-project.org/package=pez">pez</a></td>
      <td align="right">2079</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">171</td>
      <td align="left"><a href="https://cran.r-project.org/package=phyreg">phyreg</a></td>
      <td align="right">2061</td>
      <td align="left">✅</td>
      <td align="left">🚫</td>
    </tr>
    <tr>
      <td align="right">172</td>
      <td align="left"><a href="https://cran.r-project.org/package=structSSI">structSSI</a></td>
      <td align="right">2047</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">173</td>
      <td align="left"><a href="https://cran.r-project.org/package=MiSPU">MiSPU</a></td>
      <td align="right">2039</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">174</td>
      <td align="left"><a href="https://cran.r-project.org/package=dcGOR">dcGOR</a></td>
      <td align="right">2031</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">175</td>
      <td align="left"><a href="https://cran.r-project.org/package=lefse">lefse</a></td>
      <td align="right">2020</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">176</td>
      <td align="left"><a href="https://cran.r-project.org/package=SeqFeatR">SeqFeatR</a></td>
      <td align="right">2006</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">177</td>
      <td align="left"><a href="https://cran.r-project.org/package=HAP.ROR">HAP.ROR</a></td>
      <td align="right">1970</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">178</td>
      <td align="left"><a href="https://cran.r-project.org/package=symmoments">symmoments</a></td>
      <td align="right">1932</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">179</td>
      <td align="left"><a href="https://cran.r-project.org/package=genBaRcode">genBaRcode</a></td>
      <td align="right">1923</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">180</td>
      <td align="left"><a href="https://cran.r-project.org/package=PhylogeneticEM">PhylogeneticEM</a></td>
      <td align="right">1912</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">181</td>
      <td align="left"><a href="https://cran.r-project.org/package=windex">windex</a></td>
      <td align="right">1900</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">182</td>
      <td align="left"><a href="https://cran.r-project.org/package=phylocurve">phylocurve</a></td>
      <td align="right">1875</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">183</td>
      <td align="left"><a href="https://cran.r-project.org/package=MonoPhy">MonoPhy</a></td>
      <td align="right">1865</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">184</td>
      <td align="left"><a href="https://cran.r-project.org/package=TreeSimGM">TreeSimGM</a></td>
      <td align="right">1844</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">185</td>
      <td align="left"><a href="https://cran.r-project.org/package=spider">spider</a></td>
      <td align="right">1840</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">186</td>
      <td align="left"><a href="https://cran.r-project.org/package=Rsampletrees">Rsampletrees</a></td>
      <td align="right">1837</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">187</td>
      <td align="left"><a href="https://cran.r-project.org/package=Rphylopars">Rphylopars</a></td>
      <td align="right">1830</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">188</td>
      <td align="left"><a href="https://cran.r-project.org/package=graphscan">graphscan</a></td>
      <td align="right">1829</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">189</td>
      <td align="left"><a href="https://cran.r-project.org/package=recluster">recluster</a></td>
      <td align="right">1811</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">190</td>
      <td align="left"><a href="https://cran.r-project.org/package=paco">paco</a></td>
      <td align="right">1804</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">191</td>
      <td align="left"><a href="https://cran.r-project.org/package=phylosim">phylosim</a></td>
      <td align="right">1768</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">192</td>
      <td align="left"><a href="https://cran.r-project.org/package=ecolottery">ecolottery</a></td>
      <td align="right">1723</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">193</td>
      <td align="left"><a href="https://cran.r-project.org/package=outbreaker2">outbreaker2</a></td>
      <td align="right">1708</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">194</td>
      <td align="left"><a href="https://cran.r-project.org/package=STEPCAM">STEPCAM</a></td>
      <td align="right">1697</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">195</td>
      <td align="left"><a href="https://cran.r-project.org/package=primerTree">primerTree</a></td>
      <td align="right">1691</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">196</td>
      <td align="left"><a href="https://cran.r-project.org/package=PhySortR">PhySortR</a></td>
      <td align="right">1675</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">197</td>
      <td align="left"><a href="https://cran.r-project.org/package=gquad">gquad</a></td>
      <td align="right">1673</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">198</td>
      <td align="left"><a href="https://cran.r-project.org/package=gromovlab">gromovlab</a></td>
      <td align="right">1669</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">199</td>
      <td align="left"><a href="https://cran.r-project.org/package=indelmiss">indelmiss</a></td>
      <td align="right">1666</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">200</td>
      <td align="left"><a href="https://cran.r-project.org/package=phybreak">phybreak</a></td>
      <td align="right">1662</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">201</td>
      <td align="left"><a href="https://cran.r-project.org/package=msap">msap</a></td>
      <td align="right">1659</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">202</td>
      <td align="left"><a href="https://cran.r-project.org/package=rase">rase</a></td>
      <td align="right">1629</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">203</td>
      <td align="left"><a href="https://cran.r-project.org/package=rdiversity">rdiversity</a></td>
      <td align="right">1629</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">204</td>
      <td align="left"><a href="https://cran.r-project.org/package=perspectev">perspectev</a></td>
      <td align="right">1617</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">205</td>
      <td align="left"><a href="https://cran.r-project.org/package=ML.MSBD">ML.MSBD</a></td>
      <td align="right">1614</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">206</td>
      <td align="left"><a href="https://cran.r-project.org/package=sidier">sidier</a></td>
      <td align="right">1611</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">207</td>
      <td align="left"><a href="https://cran.r-project.org/package=pcrcoal">pcrcoal</a></td>
      <td align="right">1588</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">208</td>
      <td align="left"><a href="https://cran.r-project.org/package=StructFDR">StructFDR</a></td>
      <td align="right">1586</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">209</td>
      <td align="left"><a href="https://cran.r-project.org/package=idar">idar</a></td>
      <td align="right">1579</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">210</td>
      <td align="left"><a href="https://cran.r-project.org/package=PIGShift">PIGShift</a></td>
      <td align="right">1534</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">211</td>
      <td align="left"><a href="https://cran.r-project.org/package=jrich">jrich</a></td>
      <td align="right">1511</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">212</td>
      <td align="left"><a href="https://cran.r-project.org/package=TotalCopheneticIndex">TotalCopheneticIndex</a></td>
      <td align="right">1509</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">213</td>
      <td align="left"><a href="https://cran.r-project.org/package=subniche">subniche</a></td>
      <td align="right">1508</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">214</td>
      <td align="left"><a href="https://cran.r-project.org/package=Plasmidprofiler">Plasmidprofiler</a></td>
      <td align="right">1495</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">215</td>
      <td align="left"><a href="https://cran.r-project.org/package=TKF">TKF</a></td>
      <td align="right">1487</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">216</td>
      <td align="left"><a href="https://cran.r-project.org/package=rwty">rwty</a></td>
      <td align="right">1485</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">217</td>
      <td align="left"><a href="https://cran.r-project.org/package=TreeSearch">TreeSearch</a></td>
      <td align="right">1474</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">218</td>
      <td align="left"><a href="https://cran.r-project.org/package=PhyInformR">PhyInformR</a></td>
      <td align="right">1402</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">219</td>
      <td align="left"><a href="https://cran.r-project.org/package=skeleSim">skeleSim</a></td>
      <td align="right">1400</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">220</td>
      <td align="left"><a href="https://cran.r-project.org/package=insect">insect</a></td>
      <td align="right">1383</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">221</td>
      <td align="left"><a href="https://cran.r-project.org/package=treeDA">treeDA</a></td>
      <td align="right">1344</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">222</td>
      <td align="left"><a href="https://cran.r-project.org/package=multilaterals">multilaterals</a></td>
      <td align="right">1337</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">223</td>
      <td align="left"><a href="https://cran.r-project.org/package=CollessLike">CollessLike</a></td>
      <td align="right">1304</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">224</td>
      <td align="left"><a href="https://cran.r-project.org/package=vhica">vhica</a></td>
      <td align="right">1299</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">225</td>
      <td align="left"><a href="https://cran.r-project.org/package=motmot.2.0">motmot.2.0</a></td>
      <td align="right">1115</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">226</td>
      <td align="left"><a href="https://cran.r-project.org/package=treedater">treedater</a></td>
      <td align="right">926</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">227</td>
      <td align="left"><a href="https://cran.r-project.org/package=ratematrix">ratematrix</a></td>
      <td align="right">813</td>
      <td align="left">✅</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">228</td>
      <td align="left"><a href="https://cran.r-project.org/package=PVR">PVR</a></td>
      <td align="right">772</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">229</td>
      <td align="left"><a href="https://cran.r-project.org/package=P2C2M">P2C2M</a></td>
      <td align="right">749</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">230</td>
      <td align="left"><a href="https://cran.r-project.org/package=metaboGSE">metaboGSE</a></td>
      <td align="right">747</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">231</td>
      <td align="left"><a href="https://cran.r-project.org/package=RRphylo">RRphylo</a></td>
      <td align="right">692</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">232</td>
      <td align="left"><a href="https://cran.r-project.org/package=ggrasp">ggrasp</a></td>
      <td align="right">677</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">233</td>
      <td align="left"><a href="https://cran.r-project.org/package=CommT">CommT</a></td>
      <td align="right">634</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">234</td>
      <td align="left"><a href="https://cran.r-project.org/package=FossilSim">FossilSim</a></td>
      <td align="right">529</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">235</td>
      <td align="left"><a href="https://cran.r-project.org/package=POUMM">POUMM</a></td>
      <td align="right">513</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">236</td>
      <td align="left"><a href="https://cran.r-project.org/package=rhierbaps">rhierbaps</a></td>
      <td align="right">391</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">237</td>
      <td align="left"><a href="https://cran.r-project.org/package=RPS">RPS</a></td>
      <td align="right">390</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">238</td>
      <td align="left"><a href="https://cran.r-project.org/package=balance">balance</a></td>
      <td align="right">0</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">239</td>
      <td align="left"><a href="https://cran.r-project.org/package=hillR">hillR</a></td>
      <td align="right">0</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">240</td>
      <td align="left"><a href="https://cran.r-project.org/package=kmeRs">kmeRs</a></td>
      <td align="right">0</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">241</td>
      <td align="left"><a href="https://cran.r-project.org/package=phylocomr">phylocomr</a></td>
      <td align="right">0</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">242</td>
      <td align="left"><a href="https://cran.r-project.org/package=rr2">rr2</a></td>
      <td align="right">0</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
    <tr>
      <td align="right">243</td>
      <td align="left"><a href="https://cran.r-project.org/package=slouch">slouch</a></td>
      <td align="right">0</td>
      <td align="left">🚫</td>
      <td align="left">✅</td>
    </tr>
  </tbody>
</table>
<section class="footnotes">
  <ol>
    <li id="fn1">
      <p>Note that we can’t use the typical filtering mechanism using the single square bracket <code>[</code> because of the way lists work. In particular, there’s no good destructuring syntax for lists-of-lists as there are for simple vectors. See <code>?Extract</code> for more details. <a href="#fnref1" class="footnote-backref">↩</a></p>
    </li>
    <li id="fn2">
      <p>The builtin package <code>tools</code> also has it, but it only returns packages that you have currently installed. <a href="#fnref2" class="footnote-backref">↩</a></p>
    </li>
  </ol>
</section>
]]></content>
</entry>
<entry>
  <title><![CDATA[Animating and labeling figures with ImageMagick]]></title>
  <link rel="alternate" type="text/html" href="https://jonathanchang.org/blog/animating-and-labeling-figures-with-imagemagick/"/>
  <id>https://jonathanchang.org/blog/animating-and-labeling-figures-with-imagemagick</id>
  <published>2018-08-07T18:52:00+00:00</published>
  <updated>2018-08-07T18:52:00+00:00</updated>
  <content type="html"><![CDATA[
     <p><a href="https://www.imagemagick.org">ImageMagick</a> is an incredible command-line tool that lets you edit and convert images of all sorts. Suppose you wanted to <a href="https://fishtreeoflife.org/rabosky-et-al-2018-update/">compare some figures that you’ve generated</a>, and label the figures with their respective filenames so that you know which is which. Here’s a quick worked example in R and Terminal that gets you going.</p>
  <p>First, let’s generate some figures to compare. The <a href="https://www.tidyverse.org/articles/2018/07/ggplot2-3-0-0/">latest version of ggplot2 (3.0)</a> recently added the <a href="https://cran.r-project.org/web/packages/viridis/vignettes/intro-to-viridis.html">new viridis color palettes</a> as an option, and by default, that are ordered factors use the viridis palettes when assigned to the color or fill aesthetic. Let’s plot the default <code>diamonds</code> dataset to compare the two:</p>
  <div class="language-r highlighter-rouge">
    <div class="highlight">
      <pre class="highlight"><code data-lang="r"><span class="n">library</span><span class="p">(</span><span class="n">ggplot2</span><span class="p">)</span><span class="w">
</span><span class="c1"># Uses viridis by default</span><span class="w">
</span><span class="n">ggplot</span><span class="p">(</span><span class="n">diamonds</span><span class="p">,</span><span class="w"> </span><span class="n">aes</span><span class="p">(</span><span class="n">carat</span><span class="p">,</span><span class="w"> </span><span class="n">price</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">clarity</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">geom_point</span><span class="p">()</span><span class="w">
</span><span class="n">ggsave</span><span class="p">(</span><span class="s2">"diamonds_ordered.png"</span><span class="p">)</span><span class="w">
<p></span><span class="c1"># Unorder the factor to use the unordered palette</span><span class="w">
</span><span class="n">ggplot</span><span class="p">(</span><span class="n">diamonds</span><span class="p">,</span><span class="w"> </span><span class="n">aes</span><span class="p">(</span><span class="n">carat</span><span class="p">,</span><span class="w"> </span><span class="n">price</span><span class="p">,</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">factor</span><span class="p">(</span><span class="n">clarity</span><span class="p">,</span><span class="w"> </span><span class="n">ordered</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">)))</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">geom_point</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">labs</span><span class="p">(</span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">“clarity”</span><span class="p">)</span><span class="w">
</span><span class="n">ggsave</span><span class="p">(</span><span class="s2">“diamonds_unordered.png”</span><span class="p">)</span><span class="w">
</span></code></pre>
    </div>
  </div>
</p>
<p>Next, let’s fire up ImageMagick to convert this to an animated GIF. Sadly we can’t use this in papers yet but it can easily be blogged or tweeted about. <code>brew install imagemagick</code> if you don’t have it already, then:</p>
<div class="language-bash highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="bash">convert <span class="nt">-resize</span> 33% <span class="nt">-gravity</span> north <span class="nt">-undercolor</span> <span class="s1">'#ffffff80'</span> <span class="nt">-pointsize</span> 16 <span class="nt">-annotate</span> 0 <span class="s2">"%f"</span> diamonds_<span class="k">*</span>.png <span class="nt">-set</span> delay 100 <span class="nt">-loop</span> 0 diamonds_flicker.gif
</code></pre>
  </div>
</div>
<p>This does a few things:</p>
<ul>
  <li>resizes the image to 1/3 the original size</li>
  <li>adds the filename to the top of the image, with a slightly transparent white background</li>
  <li>sets a delay of 100 milliseconds between frames</li>
</ul>
<p>Here’s the end result:</p>
<p><img src="/uploads/2018/diamonds_flicker.gif" alt="A comparison of the two figure drawing methods" srcset="/uploads/2018/diamonds_flicker.gif 2x"></p>
<p>There’s a lot of cool stuff that ImageMagick can do (basically every kind of image manipulation imaginable), so <a href="https://www.imagemagick.org/Usage/">check out the manual</a> and start hacking!</p>
]]></content>
</entry>
<entry>
  <title><![CDATA[How to partially rasterize a figure plotted with R]]></title>
  <link rel="alternate" type="text/html" href="https://jonathanchang.org/blog/how-to-partially-rasterize-a-figure-plotted-with-r/"/>
  <id>https://jonathanchang.org/blog/how-to-partially-rasterize-a-figure-plotted-with-r</id>
  <published>2018-05-25T17:25:00+00:00</published>
  <updated>2018-05-25T17:25:00+00:00</updated>
  <content type="html"><![CDATA[
     <style>img { background: #ccc }</style>
  <p>If you work with datasets that are big enough in R you will eventually encounter situations where your plots are so complex that they do things like crash Preview.app on macOS. For me this happens a lot when I generate huge scatterplots with very dense overplotting. These don’t add much information to the figure but nevertheless must be rendered by your PDF viewer, slowing it down and generally making a mess of things.</p>
  <p>I recently encountered a situation where a journal’s editing office couldn’t handle a particularly complex figure and requested that the figure be converted into a raster format. This is less than ideal compared to a vector format like PDF: you can’t do things like select text from a rasterized PNG and it’s generally just less usable. (<em><a href="http://guides.lib.umich.edu/c.php?g=282942&amp;p=1885352">More info on raster vs. vector images</a></em>). Would it be possible to convert the complex parts of the figure to a raster format while keeping everything else vectorized?</p>
  <p>The answer is yes! And it can all be done in R, with no fiddly conversions by hand and trying to place things precisely in Illustrator.</p>
  <p>Let’s use the built-in <code>mtcars</code> dataset to as an example, and include some colors and a legend:</p>
  <div class="language-r highlighter-rouge">
    <div class="highlight">
      <pre class="highlight"><code data-lang="r"><span class="n">plot</span><span class="p">(</span><span class="n">mpg</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">wt</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mtcars</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">factor</span><span class="p">(</span><span class="n">mtcars</span><span class="o">$</span><span class="n">cyl</span><span class="p">),</span><span class="w"> </span><span class="n">pch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">19</span><span class="p">)</span><span class="w">
</span><span class="n">legend</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">5</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">34</span><span class="p">,</span><span class="w"> </span><span class="n">legend</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">levels</span><span class="p">(</span><span class="n">factor</span><span class="p">(</span><span class="n">mtcars</span><span class="o">$</span><span class="n">cyl</span><span class="p">)),</span><span class="w"> </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="m">3</span><span class="p">),</span><span class="w"> </span><span class="n">pch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">19</span><span class="p">)</span><span class="w">
</span></code></pre>
    </div>
  </div>
  <p><img src="/uploads/2018/mtcars.png" alt="" /></p>
  <p>Note that the legend overlaps the plot area. If you were to simply plot the entire thing as a PNG and then crop out the plot area, you’d either also have to rasterize the legend (and lose the ability to edit the text in Illustrator later) or manually erase the legend (let’s avoid doing things by hand).</p>
  <p>Let’s modify this code step by step. First set up our PDF device, with an output size of 7 by 7 inches.</p>
  <div class="language-r highlighter-rouge">
    <div class="highlight">
      <pre class="highlight"><code data-lang="r"><span class="n">pdf</span><span class="p">(</span><span class="s2">"mtcars.pdf"</span><span class="p">,</span><span class="w"> </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">7</span><span class="p">,</span><span class="w"> </span><span class="n">height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">7</span><span class="p">)</span><span class="w">
</span></code></pre>
    </div>
  </div>
  <p>Next set up the plot axes and legend. These are the same plot commands as before, but here <code>type = &quot;n&quot;</code> is specified, so that only the axes are set up, but no data are actually plotted.</p>
  <div class="language-r highlighter-rouge">
    <div class="highlight">
      <pre class="highlight"><code data-lang="r"><span class="n">plot</span><span class="p">(</span><span class="n">mpg</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">wt</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mtcars</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">factor</span><span class="p">(</span><span class="n">mtcars</span><span class="o">$</span><span class="n">cyl</span><span class="p">),</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"n"</span><span class="p">)</span><span class="w">
</span><span class="n">legend</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">5</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">34</span><span class="p">,</span><span class="w"> </span><span class="n">legend</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">levels</span><span class="p">(</span><span class="n">factor</span><span class="p">(</span><span class="n">mtcars</span><span class="o">$</span><span class="n">cyl</span><span class="p">)),</span><span class="w"> </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="m">3</span><span class="p">),</span><span class="w"> </span><span class="n">pch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">19</span><span class="p">)</span><span class="w">
</span></code></pre>
    </div>
  </div>
  <p>Now we must figure out how big our plot area actually is. To do so, use the <code>par</code> function to extract the plot limits. This returns a 4-element vector, where the first two elements are the x-coordinates and the last two elements are the y-coordinates of the plot area.</p>
  <div class="language-r highlighter-rouge">
    <div class="highlight">
      <pre class="highlight"><code data-lang="r"><span class="n">coords</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">par</span><span class="p">(</span><span class="s2">"usr"</span><span class="p">)</span><span class="w">
</span><span class="c1"># [1] 4.156 8.044 0.764 7.136</span><span class="w">
</span></code></pre>
    </div>
  </div>
  <p>However, these coordinates are in “user” space, meaning that they don’t correspond to the physical dimensions in the plot device. Use the <code>grconvert</code> functions to convert from user space to plot device space, in inches:</p>
  <div class="language-r highlighter-rouge">
    <div class="highlight">
      <pre class="highlight"><code data-lang="r"><span class="n">gx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">grconvertX</span><span class="p">(</span><span class="n">coords</span><span class="p">[</span><span class="m">1</span><span class="o">:</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="s2">"user"</span><span class="p">,</span><span class="w"> </span><span class="s2">"inches"</span><span class="p">)</span><span class="w">
</span><span class="c1"># [1] 0.82 6.58</span><span class="w">
<p></span><span class="n">gy</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">grconvertY</span><span class="p">(</span><span class="n">coords</span><span class="p">[</span><span class="m">3</span><span class="o">:</span><span class="m">4</span><span class="p">],</span><span class="w"> </span><span class="s2">“user”</span><span class="p">,</span><span class="w"> </span><span class="s2">“inches”</span><span class="p">)</span><span class="w">
</span><span class="c1"># [1] 1.02 6.18</span><span class="w"></p>
<p></span><span class="n">width</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">max</span><span class="p">(</span><span class="n">gx</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nf">min</span><span class="p">(</span><span class="n">gx</span><span class="p">)</span><span class="w">
</span><span class="c1"># [1] 5.76</span><span class="w"></p>
<p></span><span class="n">height</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">max</span><span class="p">(</span><span class="n">gy</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nf">min</span><span class="p">(</span><span class="n">gy</span><span class="p">)</span><span class="w">
</span><span class="c1"># [1] 5.16</span><span class="w">
</span></code></pre>
    </div>
  </div>
</p>
<p>Now set up a raster device with the dimensions computed from the vector (PDF) device. Note that the PDF device is still active at this point.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">png</span><span class="p">(</span><span class="s2">"mtcars_panel.png"</span><span class="p">,</span><span class="w"> </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">width</span><span class="p">,</span><span class="w"> </span><span class="n">height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">height</span><span class="p">,</span><span class="w"> </span><span class="n">units</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"in"</span><span class="p">,</span><span class="w"> </span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">300</span><span class="p">,</span><span class="w"> </span><span class="n">bg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"transparent"</span><span class="p">)</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>Since the plot axes are handled in the vector device, it’s unnecessary to set those up. So avoid the high level <code>plot</code> commands and instead set up the plot areas from scratch. <code>plot.window</code> needs the x and y limits computed earlier, but by default R will expand the limits so that a data point right on the edge of the specified limits doesn’t get cut off.</p>
<p>Tell R to turn off this feature by setting <code>xaxs</code> and <code>yaxs</code> to <code>&quot;i&quot;</code>. Also turn off the plot margins with <code>mar = c(0,0,0,0)</code> since that will just be empty space.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">plot.new</span><span class="p">()</span><span class="w">
</span><span class="n">plot.window</span><span class="p">(</span><span class="n">coords</span><span class="p">[</span><span class="m">1</span><span class="o">:</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="m">3</span><span class="o">:</span><span class="m">4</span><span class="p">],</span><span class="w"> </span><span class="n">mar</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">),</span><span class="w"> </span><span class="n">xaxs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"i"</span><span class="p">,</span><span class="w"> </span><span class="n">yaxs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"i"</span><span class="p">)</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>Finally, plot the data points as before and close the PNG device.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">points</span><span class="p">(</span><span class="n">mpg</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">wt</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mtcars</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">factor</span><span class="p">(</span><span class="n">mtcars</span><span class="o">$</span><span class="n">cyl</span><span class="p">),</span><span class="w"> </span><span class="n">pch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">19</span><span class="p">)</span><span class="w">
</span><span class="n">dev.off</span><span class="p">()</span><span class="w">
</span><span class="c1"># pdf</span><span class="w">
</span><span class="c1">#   2</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>Now there are two figures that look like this, one PDF and one PNG:</p>
<p><img src="/uploads/2018/mtcars_axes.png" alt="Iris Axes" />
  <img src="/uploads/2018/mtcars_panel.png" alt="Iris Panel" /></p>
<p>To combine these, read in the generated PNG file using the <code>png</code> library, and then plot it using the <code>rasterImage</code> function. The relevant code looks like this:</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">library</span><span class="p">(</span><span class="n">png</span><span class="p">)</span><span class="w">
</span><span class="n">panel</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">readPNG</span><span class="p">(</span><span class="s2">"mtcars_panel.png"</span><span class="p">)</span><span class="w">
</span><span class="n">rasterImage</span><span class="p">(</span><span class="n">panel</span><span class="p">,</span><span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="m">1</span><span class="p">],</span><span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="m">3</span><span class="p">],</span><span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="m">4</span><span class="p">])</span><span class="w">
</span></code></pre>
  </div>
</div>
<p>Note that the coordinates for <code>rasterImage</code> be specified a different order than for the <code>plot.window</code> function from before.</p>
<p>Wrap up by closing the PDF device.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">dev.off</span><span class="p">()</span><span class="w">
</span><span class="c1"># null device </span><span class="w">
</span><span class="c1">#           1 </span><span class="w">
</span></code></pre>
  </div>
</div>
<p>All together, here is the entire script. It’s a bit different from what’s written above; in particular, I save the rasterized plot area to a temporary file to avoid cluttering up our working directory.</p>
<div class="language-r highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="r"><span class="n">library</span><span class="p">(</span><span class="n">png</span><span class="p">)</span><span class="w">
<p></span><span class="n">pdf</span><span class="p">(</span><span class="s2">“mtcars.pdf”</span><span class="p">,</span><span class="w"> </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">7</span><span class="p">,</span><span class="w"> </span><span class="n">height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">7</span><span class="p">)</span><span class="w"></p>
<p></span><span class="c1"># Set up plot axes and legend</span><span class="w">
</span><span class="n">plot</span><span class="p">(</span><span class="n">mpg</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">wt</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mtcars</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">factor</span><span class="p">(</span><span class="n">mtcars</span><span class="o">$</span><span class="n">cyl</span><span class="p">),</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">“n”</span><span class="p">)</span><span class="w">
</span><span class="n">legend</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">5</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">34</span><span class="p">,</span><span class="w"> </span><span class="n">legend</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">levels</span><span class="p">(</span><span class="n">factor</span><span class="p">(</span><span class="n">mtcars</span><span class="o">$</span><span class="n">cyl</span><span class="p">)),</span><span class="w"> </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="m">3</span><span class="p">),</span><span class="w"> </span><span class="n">pch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">19</span><span class="p">)</span><span class="w"></p>
<p></span><span class="c1"># Extract plot area in both user and physical coordinates</span><span class="w">
</span><span class="n">coords</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">par</span><span class="p">(</span><span class="s2">“usr”</span><span class="p">)</span><span class="w">
</span><span class="n">gx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">grconvertX</span><span class="p">(</span><span class="n">coords</span><span class="p">[</span><span class="m">1</span><span class="o">:</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="s2">“user”</span><span class="p">,</span><span class="w"> </span><span class="s2">“inches”</span><span class="p">)</span><span class="w">
</span><span class="n">gy</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">grconvertY</span><span class="p">(</span><span class="n">coords</span><span class="p">[</span><span class="m">3</span><span class="o">:</span><span class="m">4</span><span class="p">],</span><span class="w"> </span><span class="s2">“user”</span><span class="p">,</span><span class="w"> </span><span class="s2">“inches”</span><span class="p">)</span><span class="w">
</span><span class="n">width</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">max</span><span class="p">(</span><span class="n">gx</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nf">min</span><span class="p">(</span><span class="n">gx</span><span class="p">)</span><span class="w">
</span><span class="n">height</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">max</span><span class="p">(</span><span class="n">gy</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nf">min</span><span class="p">(</span><span class="n">gy</span><span class="p">)</span><span class="w"></p>
<p></span><span class="c1"># Get a temporary file name for our rasterized plot area</span><span class="w">
</span><span class="n">tmp</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">tempfile</span><span class="p">()</span><span class="w"></p>
<p></span><span class="c1"># Can increase resolution from 300 if higher quality is desired.</span><span class="w">
</span><span class="n">png</span><span class="p">(</span><span class="n">tmp</span><span class="p">,</span><span class="w"> </span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">width</span><span class="p">,</span><span class="w"> </span><span class="n">height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">height</span><span class="p">,</span><span class="w"> </span><span class="n">units</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">“in”</span><span class="p">,</span><span class="w"> </span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">300</span><span class="p">,</span><span class="w"> </span><span class="n">bg</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">“transparent”</span><span class="p">)</span><span class="w">
</span><span class="n">plot.new</span><span class="p">()</span><span class="w">
</span><span class="n">plot.window</span><span class="p">(</span><span class="n">coords</span><span class="p">[</span><span class="m">1</span><span class="o">:</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="m">3</span><span class="o">:</span><span class="m">4</span><span class="p">],</span><span class="w"> </span><span class="n">mar</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">),</span><span class="w"> </span><span class="n">xaxs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">“i”</span><span class="p">,</span><span class="w"> </span><span class="n">yaxs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">“i”</span><span class="p">)</span><span class="w">
</span><span class="n">points</span><span class="p">(</span><span class="n">mpg</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="n">wt</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mtcars</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">factor</span><span class="p">(</span><span class="n">mtcars</span><span class="o">$</span><span class="n">cyl</span><span class="p">),</span><span class="w"> </span><span class="n">pch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">19</span><span class="p">)</span><span class="w">
</span><span class="n">dev.off</span><span class="p">()</span><span class="w"></p>
<p></span><span class="c1"># Windows users may have trouble with transparent plot backgrounds; if this is the case,</span><span class="w">
</span><span class="c1"># set bg = “white” above and move the legend plot command below the raster plot command.</span><span class="w">
</span><span class="n">panel</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">readPNG</span><span class="p">(</span><span class="n">tmp</span><span class="p">)</span><span class="w">
</span><span class="n">rasterImage</span><span class="p">(</span><span class="n">panel</span><span class="p">,</span><span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="m">1</span><span class="p">],</span><span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="m">3</span><span class="p">],</span><span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="n">coords</span><span class="p">[</span><span class="m">4</span><span class="p">])</span><span class="w"></p>
<p></span><span class="n">dev.off</span><span class="p">()</span><span class="w">
</span></code></pre>
  </div>
</div>
</p>
<h2>Exercises</h2>
<ol>
  <li>What would you need to change to plot a different type of data, e.g., a line plot or a 3D plot?</li>
  <li>How would you apply this to a multi-panel figure?</li>
  <li>How might this be accomplished with <code>ggplot2</code> graphics? (Hint: <code>annotation_raster</code>, <code>theme_void</code>)</li>
</ol>
<h2>Postscript</h2>
<p>An alternative way to do this would be to write to a null device and use <code>dev.capture</code> to rasterize and copy the the figure to the active device. However, that approach doesn’t appear to work consistently across platforms and devices, so I’ve taken the more portable approach presented here.</p>
]]></content>
</entry>
<entry>
  <title><![CDATA[Using RMarkdown, knitr, and pandoc in TexShop on Mac]]></title>
  <link rel="alternate" type="text/html" href="https://jonathanchang.org/blog/using-rmarkdown-knitr-and-pandoc-in-texshop-on-mac/"/>
  <id>https://jonathanchang.org/blog/using-rmarkdown-knitr-and-pandoc-in-texshop-on-mac</id>
  <published>2014-09-26T07:18:36+00:00</published>
  <updated>2014-09-26T07:18:36+00:00</updated>
  <content type="html"><![CDATA[
     <p>Most people will use RStudio for this sort of workflow, but I use <a href="http://pages.uoregon.edu/koch/texshop/">TeXShop</a> because I prefer the side-by-side editing view with plain text on the left and the formatted version on the right. I don’t think RStudio supports it. Also, TeXShop feels like a native OS X application. RStudio can’t really shed its Qt roots no matter how hard it tries.</p>
  <p>Here’s how to get TexShop working with your RMarkdown workflow.</p>
  <p>I assume you’ve already installed <a href="http://johnmacfarlane.net/pandoc/installing.html">pandoc</a> and <a href="https://tug.org/mactex/">MacTeX</a>. If you don’t already have RMarkdown installed, load up an R session:</p>
  <div class="language-r highlighter-rouge">
    <div class="highlight">
      <pre class="highlight"><code data-lang="r"><span class="n">install.packages</span><span class="p">(</span><span class="s2">"devtools"</span><span class="p">)</span><span class="w">
</span><span class="n">devtools</span><span class="o">::</span><span class="n">install_github</span><span class="p">(</span><span class="s2">"rstudio/rmarkdown"</span><span class="p">,</span><span class="w"> </span><span class="n">dependencies</span><span class="o">=</span><span class="kc">TRUE</span><span class="p">)</span><span class="w">
</span></code></pre>
    </div>
  </div>
  <p>First, you’ll need to add a custom RMarkdown engine for TexShop. These are located in <code>~/Library/TeXShop/Engines</code> and are simple executable script files with the extension <code>.engine</code>. Let’s add an rmarkdown engine. Open up the Terminal:</p>
  <div class="language-bash highlighter-rouge">
    <div class="highlight">
      <pre class="highlight"><code data-lang="bash">vim ~/Library/TeXShop/Engines/rmarkdown.engine        <span class="c"># or nano, etc.</span>
<span class="nb">chmod </span>a+x ~/Library/TeXShop/Engines/rmarkdown.engine
</code></pre>
    </div>
  </div>
  <p>Inside that <code>rmarkdown.engine</code> file you can just paste in these contents:</p>
  <div class="language-sh highlighter-rouge">
    <div class="highlight">
      <pre class="highlight"><code data-lang="sh"><span class="c">#!/bin/bash</span>
<p>Rscript <span class="nt">-e</span> <span class="s2">“rmarkdown::render(</span><span class="se">&quot;</span><span class="nv">$1</span><span class="se">&quot;</span><span class="s2">, encoding=‘UTF-8’)”</span>
</code></pre>
    </div>
  </div>
</p>
<p>TeXShop will pass in the name of your Rmarkdown file as the first argument to your script, so you can pass it to R inside the variable <code>$1</code>. Note that you might have to change the encoding argument to <code>rmarkdown::render</code> if you have TeXShop saving files in something other than UTF8. It’s important to get this right, otherwise non-ASCII characters will cause random paragraphs to turn into <code>NA</code>s. (Fixing this bug is Someone Else’s Problem because the workaround is adequate and there are only so many hours in the day.)</p>
<p>Finally you need to get TeXShop to recognize <code>.Rmd</code> files. By default TeXShop will refuse to let you “typeset” files with extensions that it doesn’t recognize. Though TeXShop does support plain <code>.md</code> files, the RMarkdown package will not knit these and will bypass any R code found in plain Markdown files. So you must write with the <code>.Rmd</code> extension. Fortunately there’s a hidden preference that you can tweak. Simply open Terminal and type:</p>
<div class="language-sh highlighter-rouge">
  <div class="highlight">
    <pre class="highlight"><code data-lang="sh">defaults write TeXShop OtherTeXExtensions <span class="nt">-array-add</span> <span class="s2">"Rmd"</span>
defaults write TeXShop OtherTeXExtensions <span class="nt">-array-add</span> <span class="s2">"rmd"</span>
</code></pre>
  </div>
</div>
<p>Now when you open <code>.Rmd</code> files, simply select the “rmarkdown” engine from the drop down list in the toolbar and type away.</p>
<p><em>Other hidden preferences can be found in TeXShop’s extensive help files. I actually started grepping the source code to hack in this functionality until I figured out that the documentation for TeXShop was actually quite good. I’ve been spoiled by scientific software for too long.</em></p>
]]></content>
</entry>
</feed>