<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>NYC on kieranhealy.org</title>
    <link>https://kieranhealy.org/categories/nyc/</link>
    <description>Recent content in NYC on kieranhealy.org</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-US</language>
    <lastBuildDate>Sat, 02 May 2026 08:59:15 -0400</lastBuildDate>
    
        <atom:link href="https://kieranhealy.org/categories/nyc/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>Bad Weather and the Subway</title>
      <link>https://kieranhealy.org/blog/archives/2026/05/02/bad-weather-and-the-subway/</link>
      <pubDate>Sat, 02 May 2026 08:59:15 -0400</pubDate>
      
      <guid>https://kieranhealy.org/blog/archives/2026/05/02/bad-weather-and-the-subway/</guid>
      <description>&lt;figure class=&#34;full-width&#34;&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2026/05/02/bad-weather-and-the-subway/snow-in-nyc.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2026/05/02/bad-weather-and-the-subway/snow-in-nyc.png&#34;
         alt=&#34;Two figures walking in the snow; trees in the distance.&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;Snow in Inwood, New York. Photograph by the author.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Recently I&amp;rsquo;ve been looking at hourly ridership data from the New York City Subway. Last time we learned that &lt;a href=&#34;https://kieranhealy.org/blog/archives/2026/04/25/hourly-subway-station-flows/&#34;&gt;people go to work in the morning and come home in the evening&lt;/a&gt;, for example. (All together now: &amp;ldquo;Only in New York, baby!&amp;rdquo;) Today we&amp;rsquo;ll learn that bad weather makes people stay at home. Except, sometimes it doesn&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;Regular readers will recall that the subway system &lt;a href=&#34;https://kieranhealy.org/blog/archives/2025/02/19/mta-ridership/&#34;&gt;carries a &lt;em&gt;lot&lt;/em&gt; of passengers every day&lt;/a&gt;. The ridership data for the whole of 2025 represents just over 1.3 billion entries into the system via an OMNY tap or Metrocard. It&amp;rsquo;s available aggregated to hourly resolution by station complex. With that data in hand, we can calculate average hourly ridership for every day of the week. This gives us a profile of what, for example, a Monday or a Wednesday typically looks like, by hour. When calculating the average day-of-the week profile we exclude holidays and the like.&lt;/p&gt;
&lt;p&gt;Meanwhile, the National Weather Service provides data on severe weather events that affected the New York City region in 2025. We could get more fine-grained if we wanted to, but for now we&amp;rsquo;ll just use the &lt;a href=&#34;https://www.weather.gov/okx/stormarchive&#34;&gt;general list of events&lt;/a&gt; the NWS provides. Then we plot the Subway ridership profile for that specific date against the average profile for whatever day of the week the event happened on.&lt;/p&gt;
&lt;figure class=&#34;full-width&#34;&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2026/05/02/bad-weather-and-the-subway/rhythms_2025_weather.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2026/05/02/bad-weather-and-the-subway/rhythms_2025_weather.png&#34;
         alt=&#34;Small multiple showing generally suppressive relatinship between subway ridership and adverse weather days in 2025.&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;Bad weather suppresses Subway ridership, in general. But not always.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The gray lines are the baseline. The red ones are the bad weather day. The basic shape of the gray lines (and many of the red ones) is set by the rhythm of daily life. The sharp double-peak pattern is what someone I&amp;rsquo;ve shown too many of these graphs to has taken to calling &amp;ldquo;The Giant Cat-Ears of Employment&amp;rdquo;. The cat-ear shapes vary by work day (which might be the topic of another post), but are most sharply-contrasted with the weekends, which look more like little hillocks or &lt;a href=&#34;https://en.wikipedia.org/wiki/Drumlin&#34;&gt;drumlins&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We can see a few different cases in the panels. First are days when the weather event put no dent at all in people&amp;rsquo;s day. This is because &lt;del&gt;of the incredible toughness and resilience of New Yorkers, something they are surprisingly very modest about&lt;/del&gt; even though there was a weather event in the region that day, it just didn&amp;rsquo;t impinge on the city much, or at all. The light snow on February 11th or the heavy rain on March 6th are examples here. People just continued to go about their business.&lt;/p&gt;
&lt;p&gt;Second are cases where there&amp;rsquo;s a lot of travel suppression but it&amp;rsquo;s not really&amp;mdash;or not wholly&amp;mdash;the weather that&amp;rsquo;s responsible. The winter storm on Friday December 26th is a case of this. Bad weather; strongly suppressed travel profile; but that&amp;rsquo;s not a regular Friday. Many people were staying at home anyway, because it&amp;rsquo;s the day after Christmas.&lt;/p&gt;
&lt;p&gt;Third are cases where the weather does seem to have suppressed travel. These are days like the snow on January 19th, or the shitty weather on Sunday February 16th. These events look like they made people stay at home. Some of these are more severe than others. The strongest example is the flash flooding on Thursday July 31st. That happened in the back half of the day and affected the evening commute directly.&lt;/p&gt;
&lt;p&gt;Our fourth and final category is my favorite one. Sometimes snow makes no difference at all, especially if it&amp;rsquo;s on a workday. Sometimes it&amp;rsquo;s snowy on the weekend but you&amp;rsquo;re kind of sick of it, maybe because it&amp;rsquo;s late in the winter, so you&amp;rsquo;re either going about your business as usual or you&amp;rsquo;re just staying indoors. But there&amp;rsquo;s another kind of snow day.&lt;/p&gt;
&lt;figure class=&#34;full-width&#34;&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2026/05/02/bad-weather-and-the-subway/rhythms_2025_weather_storm_dec13.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2026/05/02/bad-weather-and-the-subway/rhythms_2025_weather_storm_dec13.png&#34;
         alt=&#34;A close up of Dec 13th and 14th, when the first snow of the season fell and it made people want to go outside.&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;Let&amp;rsquo;s go exploring.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The weekend of &lt;a href=&#34;https://www.weather.gov/okx/20251213_14&#34;&gt;December 13th and 14th 2025&lt;/a&gt; brought the city&amp;rsquo;s &lt;a href=&#34;https://weather.com/news/news/2025-12-14-first-snow-new-york-city&#34;&gt;first measurable snow of the year&lt;/a&gt;, and in decent amounts, too&amp;mdash;&lt;a href=&#34;https://www.weather.gov/okx/20251213_14&#34;&gt;between four and eight inches of accumulation&lt;/a&gt;. Reports remarked on how long it had been in arriving. The result was that, over the weekend, ridership on the subway went &lt;em&gt;up&lt;/em&gt;. Maybe on the Saturday it was to go out and buy the mandatory bread, milk, and eggs.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;  But maybe it was also just to be out in the snow. The next day, the people who didn&amp;rsquo;t have to go work slept in as usual. But that day, too, across the afternoon, more people than usual headed outside and took the subway somewhere. I&amp;rsquo;d like to think a bunch of them had a sled under their arm.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Maybe, at least for some New Yorkers, it was because it made more sense to take the subway than drive. Though this probably wouldn&amp;rsquo;t be all that many people. It&amp;rsquo;d be somewhat possible to investigate this with the data at hand, especially if e.g. outlying stations showed higher ridership rates.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>Hourly Subway Station Flows</title>
      <link>https://kieranhealy.org/blog/archives/2026/04/25/hourly-subway-station-flows/</link>
      <pubDate>Sat, 25 Apr 2026 11:12:39 -0400</pubDate>
      
      <guid>https://kieranhealy.org/blog/archives/2026/04/25/hourly-subway-station-flows/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://socviz.co/08-polishing.html#saying-no-to-pie&#34;&gt;Pie charts are bad&lt;/a&gt;, as
any fule kno. We&amp;rsquo;re not as good at judging relative differences between angles
and areas as we are at judging relative differences in lengths on a common
baseline. This is especially true when we have more than two things to compare
at the same time. So, as a rule, you shouldn&amp;rsquo;t use them. You should figure out
some other way of viewing your data instead. On the other hand, I just made 424
animated pie charts because if you&amp;rsquo;re going to break a rule you should break
it good and hard.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2026/04/25/hourly-subway-station-flows/subway-map.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2026/04/25/hourly-subway-station-flows/subway-map.png&#34;
         alt=&#34;A view of the New York City Subway System (excluding the SIR). We&amp;#39;ll animate this in a minute.&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;A view of the New York City Subway System (excluding the SIR). We&amp;rsquo;ll animate this in just a minute.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The New York City Subway system is very large and carries &lt;a href=&#34;https://kieranhealy.org/blog/archives/2025/02/19/mta-ridership/&#34;&gt;a &lt;em&gt;lot&lt;/em&gt; of passengers
every day&lt;/a&gt;. The
&lt;a href=&#34;https://www.mta.info&#34;&gt;MTA&lt;/a&gt; makes quite a bit of data available about the
subway, including data on hourly flow through the system. Now, the MTA can&amp;rsquo;t
track individual pathways people take through the subway. If you use an &lt;a href=&#34;https://omny.info&#34;&gt;OMNY
card&lt;/a&gt; (or before that, a Metrocard) to enter the system, this
signals the start of a trip from some specific station or station complex. But
unlike some systems, you don&amp;rsquo;t need to &amp;ldquo;tag out&amp;rdquo; of the subway, you just exit
through a turnstile. So the system doesn&amp;rsquo;t know where you exit it. In addition,
while many stations are just on a single line, some (like 34 St/Penn Station, or
Fulton Street) are station complexes that serve many lines and allow transfers
between them.&lt;/p&gt;
&lt;p&gt;However, the MTA does publish hourly &lt;a href=&#34;https://data.ny.gov/Transportation/MTA-Subway-Origin-Destination-Ridership-Estimate-2/y2qv-fytt/about_data&#34;&gt;Origin-Destination
estimates&lt;/a&gt;
for all pairs of stations. These are their &lt;a href=&#34;https://data.ny.gov/api/views/y2qv-fytt/files/c912f0c9-7371-44c9-a7a3-95c5389b82fe?download=true&amp;amp;filename=MTA_SubwayOriginDestinationRidershipEstimate_Overview.pdf&#34;&gt;best
guess&lt;/a&gt;
about the flow of traffic from any particular station to any other. Because
there are so many combinations, visualizing that sort of data is quite tricky.
Even then, you don&amp;rsquo;t get information about &lt;em&gt;routes&lt;/em&gt; through the system, just
start and end points. Transit analysts and planners can go further by
introducing some further assumptions about subway users. For example we might assume that commuters take the most efficient route between any given pair of entry and exit stations, and build from there to a picture of flow through the system.&lt;/p&gt;
&lt;p&gt;I do something rather more simple here. I use the MTA&amp;rsquo;s hourly
origin-destination estimates and aggregate them on a station-by-station basis to
calculate in-and-out flows across 424 subway stations or station
complexes. These specific numbers are averaged over all Mondays in 2025. For
each hour of the we calculate the total passenger volume at the station, and the
share of that volume that are estimated arrivals and departures. Then we draw a
pie chart for each station, coloring it yellow for departures,
purple for arrivals. The circle size reflects total volume and the pie slice
proportions show the flow balance.&lt;/p&gt;
&lt;p&gt;The flow data is pretty bulky. The original dataset has about 121 million rows. But working with it is pretty straightforward, thanks to the magic of parquet files, &lt;a href=&#34;https://duckdb.org&#34;&gt;duckdb&lt;/a&gt;, and &lt;a href=&#34;https://duckplyr.tidyverse.org&#34;&gt;duckplyr&lt;/a&gt;. Having patiently downloaded the data via its API, I put it in a parquet file. The CSV is about 17GB but the parquet file boils it down to 1.5GB. Then I made a small R package that bundled that data with a few convenience functions. This lets me use the data without copying it into any single project. So I can write, e.g.,&lt;/p&gt;
&lt;div class=&#34;highlight-wrapper&#34;&gt;
    
    
        &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-r&#34; data-lang=&#34;r&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;nycsubwayodr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;nyc_subway_odr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt; # A duckplyr data frame: 15 variables&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt;     year month day_of_week hour_of_day timestamp           day_of_month origin_station_complex_id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt;    &amp;lt;int&amp;gt; &amp;lt;int&amp;gt; &amp;lt;chr&amp;gt;             &amp;lt;int&amp;gt; &amp;lt;dttm&amp;gt;                     &amp;lt;int&amp;gt;                     &amp;lt;int&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt;  1  2025     1 Monday                1 2025-01-06 01:00:00            6                       189&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt;  2  2025     1 Monday                1 2025-01-06 01:00:00            6                       313&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt;  3  2025     1 Monday                1 2025-01-06 01:00:00            6                       611&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt;  4  2025     1 Monday                1 2025-01-06 01:00:00            6                       125&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt;  5  2025     1 Monday                1 2025-01-06 01:00:00            6                       313&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt;  6  2025     1 Monday                1 2025-01-06 01:00:00            6                       154&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt;  7  2025     1 Monday                1 2025-01-06 01:00:00            6                       167&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt;  8  2025     1 Monday                1 2025-01-06 01:00:00            6                       612&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt;  9  2025     1 Monday                1 2025-01-06 01:00:00            6                       272&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt; 10  2025     1 Monday                1 2025-01-06 01:00:00            6                       167&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt; # ℹ more rows&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt; # ℹ 8 more variables: origin_station_complex_name &amp;lt;chr&amp;gt;, origin_latitude &amp;lt;dbl&amp;gt;, origin_longitude &amp;lt;dbl&amp;gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt; #   destination_station_complex_id &amp;lt;int&amp;gt;, destination_station_complex_name &amp;lt;chr&amp;gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt; #   destination_latitude &amp;lt;dbl&amp;gt;, destination_longitude &amp;lt;dbl&amp;gt;, estimated_average_ridership &amp;lt;dbl&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
    
&lt;/div&gt;

&lt;p&gt;From there, we lazily query the data and duckdb does the work of doing the calculations. The whole table is never loaded into your R session, and duckdb is very fast. From there, we take our hourly flow summaries, join them to a tibble of station and line data, and export the result to some JSON files that &lt;a href=&#34;https://d3js.org&#34;&gt;D3js&lt;/a&gt; animates for us.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the result. There are three views. Initially, you see just the schematic subway map. If you click the &amp;ldquo;Map&amp;rdquo; button in the top left, it will switch to the ticking pie-chart view, which puts a pie on every station complex, with each tick being an hour of the day. The pies pile up on one another in the geographic view (in a not wholly uninformative way), but click again to have them expand to a somewhat more abstracted, force-directed network view of the system. Then click again to go back to the map. You can hover over or tap on nodes to get information about the bit of data it&amp;rsquo;s currently showing.&lt;/p&gt;
&lt;link rel=&#34;stylesheet&#34; href=&#34;subway-transition.css&#34;&gt;
&lt;div id=&#34;odr-controls&#34; style=&#34;display:flex;gap:8px;align-items:center;margin-bottom:8px;font-family:&#39;Helvetica Neue&#39;,Helvetica,sans-serif&#34;&gt;
  &lt;button id=&#34;mode-btn&#34; title=&#34;Cycle: Map → Net Flow → Network&#34;
    style=&#34;background:var(--bs-btn-bg,#f8f9fa);border:1px solid var(--bs-btn-border-color,#dee2e6);border-radius:4px;padding:4px 10px;cursor:pointer;font-size:12px&#34;&gt;Map&lt;/button&gt;
  &lt;button id=&#34;theme-btn&#34; title=&#34;Toggle light/dark theme&#34;
    style=&#34;background:var(--bs-btn-bg,#f8f9fa);border:1px solid var(--bs-btn-border-color,#dee2e6);border-radius:4px;padding:4px 8px;cursor:pointer;font-size:14px&#34;&gt;
    &lt;svg width=&#34;14&#34; height=&#34;14&#34; viewBox=&#34;0 0 384 512&#34; fill=&#34;currentColor&#34; style=&#34;vertical-align:-2px&#34;&gt;&lt;path d=&#34;M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4c5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3c6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z&#34;/&gt;&lt;/svg&gt; /
    &lt;svg width=&#34;14&#34; height=&#34;14&#34; viewBox=&#34;0 0 512 512&#34; fill=&#34;currentColor&#34; style=&#34;vertical-align:-2px&#34;&gt;&lt;path d=&#34;M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM256 160a96 96 0 1 0 0 192 96 96 0 1 0 0-192z&#34;/&gt;&lt;/svg&gt;
  &lt;/button&gt;
  &lt;div id=&#34;odr-hour-slider-wrap&#34; style=&#34;display:none;flex-direction:column;gap:2px&#34;&gt;
    &lt;div style=&#34;display:flex;align-items:center;gap:4px&#34;&gt;
      &lt;button id=&#34;play-btn&#34; title=&#34;Play/pause&#34; style=&#34;background:#f0f0f0;border:1px solid #999;border-radius:4px;padding:5px 8px;cursor:pointer;line-height:1;display:flex;align-items:center&#34;&gt;
        &lt;svg id=&#34;play-icon&#34; width=&#34;12&#34; height=&#34;12&#34; viewBox=&#34;0 0 320 512&#34; fill=&#34;#333&#34;&gt;&lt;path d=&#34;M48 64C21.5 64 0 85.5 0 112V400c0 26.5 21.5 48 48 48H80c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48H48zm192 0c-26.5 0-48 21.5-48 48V400c0 26.5 21.5 48 48 48h32c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48H240z&#34;/&gt;&lt;/svg&gt;
      &lt;/button&gt;
      &lt;input id=&#34;hour-slider&#34; type=&#34;range&#34; min=&#34;0&#34; max=&#34;23&#34; step=&#34;1&#34; value=&#34;8&#34; style=&#34;width:340px&#34;&gt;
    &lt;/div&gt;
    &lt;div id=&#34;tick-labels&#34; style=&#34;display:flex;justify-content:space-between;font-size:10px;color:#666;margin-left:24px;width:340px&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div id=&#34;odr-legend&#34; style=&#34;display:none;align-items:center;gap:6px;font-size:11px;color:#666;margin-left:8px&#34;&gt;
    &lt;svg width=&#34;24&#34; height=&#34;24&#34; viewBox=&#34;-12 -12 24 24&#34;&gt;
      &lt;path id=&#34;legend-dep&#34; d=&#34;M0,0 L0,-10 A10,10 0 1,1 -5.88,8.09 Z&#34; fill=&#34;#fde725&#34; stroke=&#34;#000&#34; stroke-width=&#34;0.5&#34; opacity=&#34;0.95&#34;/&gt;
      &lt;path id=&#34;legend-arr&#34; d=&#34;M0,0 L-5.88,8.09 A10,10 0 0,1 0,-10 Z&#34; fill=&#34;#440154&#34; stroke=&#34;#000&#34; stroke-width=&#34;0.5&#34; opacity=&#34;0.95&#34;/&gt;
    &lt;/svg&gt;
    &lt;div style=&#34;display:flex;flex-direction:column;line-height:1.2&#34;&gt;
      &lt;span id=&#34;legend-dep-label&#34; style=&#34;color:#c8b900&#34;&gt;Departures&lt;/span&gt;
      &lt;span id=&#34;legend-arr-label&#34; style=&#34;color:#440154&#34;&gt;Arrivals&lt;/span&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;div id=&#34;odr-chart&#34;&gt;&lt;/div&gt;
&lt;script src=&#34;https://d3js.org/d3.v7.min.js&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;subway-network-odr.js&#34;&gt;&lt;/script&gt;
&lt;script&gt;
(async function() {
  const [networkData, boroughs] = await Promise.all([
    fetch(&#34;network_odr_monday.json&#34;).then(r =&gt; r.json()),
    fetch(&#34;boroughs.geojson&#34;).then(r =&gt; r.json())
  ]);

  const chart = createSubwayNetworkODR(d3, networkData, boroughs, {
    width: Math.min(window.innerWidth - 40, 1800),
    height: Math.min(900, Math.max(500, window.innerHeight - 100))
  });

  document.getElementById(&#34;odr-chart&#34;).appendChild(chart.node);
  chart.setThemePageBody(false);

  const states = [&#34;geo&#34;, &#34;volume&#34;, &#34;network&#34;];
  const labels = [&#34;Map&#34;, &#34;Net Flow&#34;, &#34;Network&#34;];
  let stateIdx = 0;
  let theme = &#34;light&#34;;
  let animating = true;
  let animInterval = null;

  const modeBtn = document.getElementById(&#34;mode-btn&#34;);
  modeBtn.addEventListener(&#34;click&#34;, () =&gt; {
    stateIdx = (stateIdx + 1) % states.length;
    modeBtn.textContent = labels[stateIdx];
    chart.update(states[stateIdx]);
  });

  const themeBtn = document.getElementById(&#34;theme-btn&#34;);
  themeBtn.addEventListener(&#34;click&#34;, () =&gt; {
    theme = theme === &#34;light&#34; ? &#34;dark&#34; : &#34;light&#34;;
    chart.setTheme(theme);
  });

  const slider = document.getElementById(&#34;hour-slider&#34;);
  slider.addEventListener(&#34;input&#34;, () =&gt; chart.updateHour(+slider.value));

  const tickLabels = [&#34;12am&#34;,&#34;&#34;,&#34;&#34;,&#34;&#34;,&#34;4am&#34;,&#34;&#34;,&#34;&#34;,&#34;&#34;,&#34;8am&#34;,&#34;&#34;,&#34;&#34;,&#34;&#34;,&#34;12pm&#34;,&#34;&#34;,&#34;&#34;,&#34;&#34;,&#34;4pm&#34;,&#34;&#34;,&#34;&#34;,&#34;&#34;,&#34;8pm&#34;,&#34;&#34;,&#34;&#34;,&#34;11pm&#34;];
  const tickContainer = document.getElementById(&#34;tick-labels&#34;);
  tickLabels.forEach(t =&gt; {
    const span = document.createElement(&#34;span&#34;);
    span.textContent = t;
    span.style.width = &#34;0&#34;;
    span.style.textAlign = &#34;center&#34;;
    span.style.overflow = &#34;visible&#34;;
    span.style.whiteSpace = &#34;nowrap&#34;;
    tickContainer.appendChild(span);
  });

  const playBtn = document.getElementById(&#34;play-btn&#34;);
  const playIconSvg = &#39;&lt;svg width=&#34;12&#34; height=&#34;12&#34; viewBox=&#34;0 0 384 512&#34; fill=&#34;#333&#34;&gt;&lt;path d=&#34;M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z&#34;/&gt;&lt;/svg&gt;&#39;;
  const pauseIconSvg = &#39;&lt;svg width=&#34;12&#34; height=&#34;12&#34; viewBox=&#34;0 0 320 512&#34; fill=&#34;#333&#34;&gt;&lt;path d=&#34;M48 64C21.5 64 0 85.5 0 112V400c0 26.5 21.5 48 48 48H80c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48H48zm192 0c-26.5 0-48 21.5-48 48V400c0 26.5 21.5 48 48 48h32c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48H240z&#34;/&gt;&lt;/svg&gt;&#39;;

  function startAnimation() {
    animating = true;
    playBtn.innerHTML = pauseIconSvg;
    animInterval = setInterval(() =&gt; {
      const next = (+slider.value + 1) % 24;
      slider.value = next;
      chart.updateHour(next);
    }, 1000);
  }

  function stopAnimation() {
    animating = false;
    playBtn.innerHTML = playIconSvg;
    if (animInterval) { clearInterval(animInterval); animInterval = null; }
  }

  playBtn.addEventListener(&#34;click&#34;, () =&gt; {
    if (animating) stopAnimation(); else startAnimation();
  });

  chart.setSliderElement(document.getElementById(&#34;odr-hour-slider-wrap&#34;));
  chart.updateHour(8);
  startAnimation();
})();
&lt;/script&gt;
&lt;p&gt;Now, you might reasonably say, Kieran, that&amp;rsquo;s a lot of data to show that people go to work in the morning and come home in the evening. I&amp;rsquo;m not saying there&amp;rsquo;s nothing to that criticism. But there are quite a few interesting details in there as the data pick up traffic to different parts of town. The big interchanges naturally dominate the view, but even here there are things of interest about the balance of flow, as e.g. Penn Station has people coming in on New Jersey Transit during morning rush hour and then entering the subway, which does a lot to balance its net flow during rush-hour and even tip it towards net departures. But more importantly, who doesn&amp;rsquo;t want to sit back and contemplate more than 400 pie charts, each one pulsing with life as another hour ticks by?&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>New York City Hexmaps</title>
      <link>https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/</link>
      <pubDate>Sun, 19 Apr 2026 10:05:54 -0400</pubDate>
      
      <guid>https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/</guid>
      <description>&lt;p&gt;The five boroughs of New York City can be informally or formally carved up into many different pieces, depending on what it is that you&amp;rsquo;re doing. As part of an ongoing project, I recently made an R package, &lt;a href=&#34;https://kjhealy.github.io/nycmaps/&#34;&gt;&lt;code&gt;nycmaps&lt;/code&gt;&lt;/a&gt;, that lets you draw maps of some of these geographies. Things being what they are, these spatial units don&amp;rsquo;t necessarily overlap in compatible ways. City, State, and Congressional  Districts, School Districts, Police Precincts, Fire Companies, Election Precincts, Municipal Court Districts, Zip Codes &amp;hellip; there are loads of them. Some of them are quite straightforward; others patiently lie in wait to trap unwary analysts (I&amp;rsquo;m looking at you, Zip Codes / &lt;a href=&#34;https://www.census.gov/programs-surveys/geography/guidance/geo-areas/zctas.html&#34;&gt;ZCTAs&lt;/a&gt;).&lt;/p&gt;
&lt;h3 id=&#34;mapping-tracts-and-ntas&#34;&gt;Mapping Tracts and NTAs&lt;/h3&gt;
&lt;p&gt;Two classifications of particular interest to people like me are Census Tracts and Neighborhood Tabulation Areas (NTAs). Census Tracts are defined by the Census Bureau and form part of a nested set of geographical units that go from the smallest unit the Census keeps track of (the Block) up to the largest (the whole country). Blocks aggregate to Block groups, Block groups aggregate to Tracts. Tracts aggregate to Counties. There are of course &lt;a href=&#34;https://help.socialexplorer.com/hc/en-us/articles/24930135416733-Census-Geographies&#34;&gt;several complications&lt;/a&gt;. Ideally, &lt;a href=&#34;https://www.census.gov/programs-surveys/geography/about/glossary.html#par_textimage_13&#34;&gt;the Census would like&lt;/a&gt; tracts to be contiguous, sub-county geographical areas with about 4,000 people in them, or at least between 1,200 and 8,000 people. This means tracts can vary considerably in geographical area. They generally follow visible features of the environment, whether physical or built. Uninhabited areas also get tract designations, so in principle we can get a full tract-level map of an area with no gaps. Here&amp;rsquo;s what a tract-level map of New York City looks like:&lt;/p&gt;
&lt;div class=&#34;highlight-wrapper&#34;&gt;
    
    
        &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-r&#34; data-lang=&#34;r&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;ggplot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nycmaps&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nyc_census_tracts_2020_sf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;geom_sf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;aes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fill&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;boro_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;color&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;black&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;linewidth&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0.1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;scale_fill_brewer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;palette&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Set2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;labs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fill&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Borough&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;theme_void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
    
&lt;/div&gt;

&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/nychex-ct-geo-bare.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/nychex-ct-geo-bare.png&#34;
         alt=&#34;A tract-level map of NYC with the boroughs colored in.&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;2020 NYC Census Tract boundaries.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You can see the variation in tract size (compare e.g. Staten Island tracts with those in lower Manhattan). And you can also see features that are included on the map but, at least to a first approximation, don&amp;rsquo;t have permanent residents. That big roundy blob in the southeast corner of Queens, for instance, is JFK Airport. There are about 2,300 Census Tracts in New York City. (Naturally, their number and spatial layout changes from decennial census to decennial census, because why should life be easy?)&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.nyc.gov/content/planning/pages/resources/datasets/neighborhood-tabulation&#34;&gt;Neighborhood Tabulation Areas&lt;/a&gt;, meanwhile, are not official Census units. They are one of several subdivisions used by New York City government. The idea is to aggregate tracts into units that roughly correspond to neighborhoods that people conventionally refer to. This is, of course, an impossible task, because people don&amp;rsquo;t agree on neighborhood boundaries. But the idea is good. You want something bigger than a tract because those are small enough to be noisy on many measures produced by the main source of tract-level data, the &lt;a href=&#34;https://www.census.gov/programs-surveys/acs.html&#34;&gt;American Community Survey&lt;/a&gt;. But you want something smaller than the next level up, which is a Community District Tabulation Area. Presently, there are 262 NTAs. They look like this:&lt;/p&gt;
&lt;div class=&#34;highlight-wrapper&#34;&gt;
    
    
        &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-r&#34; data-lang=&#34;r&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;ggplot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nycmaps&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nyc_nta20_sf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;geom_sf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;aes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fill&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;boro_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;color&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;black&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;linewidth&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0.1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;scale_fill_brewer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;palette&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Set2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;labs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fill&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Borough&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;theme_void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
    
&lt;/div&gt;

&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/nychex-nta-geo-bare.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/nychex-nta-geo-bare.png&#34;
         alt=&#34;NTA boundaries&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;2020 Neighborhood Tabulation Areas&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;NTAs have recognizable names:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-r&#34; data-lang=&#34;r&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;nycmaps&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nyc_nta20_sf&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;select&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nta2020&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nta_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;nta_abbrev&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;pre&gt;&lt;code&gt;Simple feature collection with 262 features and 3 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 913175.1 ymin: 120128.4 xmax: 1067383 ymax: 272844.3
Projected CRS: NAD83 / New York Long Island (ftUS)
First 10 features:
   nta2020                            nta_name nta_abbrev
1   BK0101                          Greenpoint      Grnpt
2   BK0102                        Williamsburg   Wllmsbrg
3   BK0103                  South Williamsburg  SWllmsbrg
4   BK0104                   East Williamsburg  EWllmsbrg
5   BK0201                    Brooklyn Heights      BkHts
6   BK0202 Downtown Brooklyn-DUMBO-Boerum Hill   DwntwnBk
7   BK0203                         Fort Greene      FtGrn
8   BK0204                        Clinton Hill    ClntnHl
9   BK0261                  Brooklyn Navy Yard   BkNvyYrd
10  BK0301           Bedford-Stuyvesant (West)    BdSty_W
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we have a table like this, we can get tract-level data from the Census, for example on educational attainment, and aggregate it to the NTA level. Then we can join that data to the &lt;a href=&#34;https://r-spatial.github.io/sf/&#34;&gt;simple feature collection&lt;/a&gt; that has our geometries in it. With a little polishing (which you can &lt;a href=&#34;https://socviz.co&#34;&gt;read all about in what I personally think of as a very useful book&lt;/a&gt;), we get something like this:&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/nychex-nta-geo-ba.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/nychex-nta-geo-ba.png&#34;
         alt=&#34;BA degrees or higher within NTAs, ACS 5-year estimates.&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;BA degrees or higher within NTAs.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Nominally zero-population NTAs get grayed out (JFK, LGA, various parks and cemeteries, Brooklyn Navy Yards, the United Nations, etc).&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what this data looks like at the tract level:&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/nychex-ct-geo-ba.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/nychex-ct-geo-ba.png&#34;
         alt=&#34;BA degrees or higher within tracts, ACS 5-year estimates.&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;BA degrees or higher within tracts.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&#34;hexgrids&#34;&gt;Hexgrids&lt;/h3&gt;
&lt;p&gt;Sometimes we want a more schematic representation of geographies, because &lt;a href=&#34;https://socviz.co/07-maps.html#americas-ur-choropleths&#34;&gt;choropleth maps can be tricky to work with&lt;/a&gt;. There are &lt;a href=&#34;https://kieranhealy.org/blog/archives/2025/11/06/mamdani-vs-sliwa-and-cuomo/&#34;&gt;many&lt;/a&gt; &lt;a href=&#34;https://kieranhealy.org/blog/archives/2023/12/06/dorling-cartograms/&#34;&gt;possibilities&lt;/a&gt; here, including not drawing maps at all. One option is to make a kind of cartogram by turning our map polygons into a tessellated grid where each unit gets a single tile. My &lt;a href=&#34;https://kjhealy.github.io/nychex/&#34;&gt;&lt;code&gt;nychex&lt;/code&gt; package&lt;/a&gt; provides hexagonal and square tilings for NTAs and tracts in New York City. Turning geographically accurate polygons into regular tiled grids can be a bit tricky, especially when the polygons you are trying to tile contain &amp;ldquo;holes&amp;rdquo;. But thanks to the &lt;a href=&#34;https://kaerosen.github.io/tilemaps/&#34;&gt;&lt;code&gt;tilemaps&lt;/code&gt;&lt;/a&gt; and &lt;a href=&#34;http://andyteucher.ca/rmapshaper/&#34;&gt;&lt;code&gt;rmapshaper&lt;/code&gt;&lt;/a&gt; packages we can get reasonably far in a semi-automated way, and then tweak things by manually nudging tiles around. Our baseline NTA hexmap looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight-wrapper&#34;&gt;
    
    
        &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-r&#34; data-lang=&#34;r&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;ggplot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nychex&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nyc_nta20_hex_sf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;geom_sf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;aes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fill&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;boro_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;color&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;black&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;linewidth&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0.3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;scale_fill_brewer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;palette&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Set2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;labs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fill&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Borough&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;theme_void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
    
&lt;/div&gt;

&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/nychex-nta-hex-bare.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/nychex-nta-hex-bare.png&#34;
         alt=&#34;Bare NTA hexmap&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;The opening gameboard for my upcoming strategy game, &lt;em&gt;Ticket to Ridgewood&lt;/em&gt;&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;And here is what our BA prevalence map looks like when mapped with it:&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/nychex-nta-hex-ba.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/nychex-nta-hex-ba.png&#34;
         alt=&#34;BA prevalence, NTA hexmap edition.&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;Labeled NTA hexmap&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Here I&amp;rsquo;ve labeled the hexes with the (sometimes highly) abbreviated version of their NTA name. Maps like this don&amp;rsquo;t magically solve the difficulties of spatially representing population-based data, but they have their uses. They can be handy if you want to quickly get a sense of variation across units while retaining a roughly spatial layout. They also make some kinds of small-multiple or faceted plots a little easier.&lt;/p&gt;
&lt;p&gt;We don&amp;rsquo;t have to stop at NTA-level resolution. We can do a tract-level one, too. Here&amp;rsquo;s a tract-level base hexmap:&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/nychex-ct-hex-bare.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/nychex-ct-hex-bare.png&#34;
         alt=&#34;Bare tract-level hexmap&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;Ticket to Ridgewood, advanced edition&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This map makes a series of compromises with city geography in order to make room for each tract&amp;rsquo;s hexagon. In particular, northern Manhattan is more detached from the Bronx than is ideal, and it was also necessary to sever Brooklyn from Queens along their shared border. (Our simplify-and-tile algorithm had a very hard time with the undifferentiated Brooklyn/Queens landmass.) With a fully hand-drawn hexmap we could probably avoid most of these problems, but that would involve quite a substantial amount of work. Even when we generate the main components algorithmically, quite a bit of hand-adjustment in the overall positioning and layout is still required (especially for things like the Rockaways and other islands or quasi-islands). Sadly there&amp;rsquo;s no magic way to integrate the main borough polygons while preserving the orientation of all the hexes. Still, the result isn&amp;rsquo;t bad.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s our BA map in tract-level hexagonal form:&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/nychex-ct-hex-ba.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2026/04/19/new-york-city-hexmaps/nychex-ct-hex-ba.png&#34;
         alt=&#34;Tract-level BA hexmap&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;Tract-level BA map.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The usual benefits and disadvantages of regularized choropleths are in evidence here. Lower Manhattan&amp;rsquo;s population gets a fairer shout, as do parts of Brooklyn and the Bronx. The geography still mostly works. The difficulties flow mostly from the map being at the tract-level in the first place, rather than the tiling. That is, some tracts have unusual shapes that result in quite noisy estimates. For example, sometimes a tract will consist &lt;em&gt;mostly&lt;/em&gt; of a park or an industrial area, but have a few residential segments. This can mean it ends up being measured too high or too low on the thing we&amp;rsquo;re counting. Or, by contrast, a tract might be almost entirely one kind of entity, like a retirement home, producing results that might seem odd if you don&amp;rsquo;t know what&amp;rsquo;s in that spot. You can see why the City aims at the NTA level for a lot of its summaries. It has ten times fewer units, but things get smoothed out in a way that may be more useful. Any real-world method of measurement comes with some rate of error, which the Census helpfully provides estimates of. Nice maps tempt you to reify observations and spin yarns about what you see, whether it&amp;rsquo;s a finely-detailed spatial polygon or a pleasingly regular hexagon. The finer the observational grain, the more important it is for you to know about the situation on the ground. Literally.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Mamdani vs Sliwa and Cuomo</title>
      <link>https://kieranhealy.org/blog/archives/2025/11/06/mamdani-vs-sliwa-and-cuomo/</link>
      <pubDate>Thu, 06 Nov 2025 12:57:44 -0500</pubDate>
      
      <guid>https://kieranhealy.org/blog/archives/2025/11/06/mamdani-vs-sliwa-and-cuomo/</guid>
      <description>&lt;p&gt;Mamdani&amp;rsquo;s victory in the New York City mayoral election gave me the opportunity to draw a few maps, and also to learn a bit about incorporating additional spatial data into maps drawn in R. R is not a specialized piece of GIS software. ESRI&amp;rsquo;s &lt;a href=&#34;https://www.arcgis.com/&#34;&gt;ArcGIS&lt;/a&gt; is the 800lb gorilla in this world and &lt;a href=&#34;https://qgis.org&#34;&gt;QGIS&lt;/a&gt;  the &lt;a href=&#34;https://www.gimp.org&#34;&gt;GIMP&lt;/a&gt; to its Photoshop, so to speak.&lt;/p&gt;
&lt;p&gt;Still, you can do a lot of spatial stuff in R, grounded in the &lt;a href=&#34;https://r-spatial.github.io/sf/&#34;&gt;&lt;code&gt;sf&lt;/code&gt; package&lt;/a&gt; and its many friends. Plus you get the benefit of all the data manipulation and analysis that R is really good at. So, having gotten the precinct-level results for the election, some maps from New York City (e.g., the &lt;a href=&#34;https://www.nyc.gov/content/planning/pages/resources/datasets/borough-boundaries&#34;&gt;clipped borough boundaries map&lt;/a&gt;), and &lt;a href=&#34;https://www.mta.info/developers&#34;&gt;GTFS data from the MTA&lt;/a&gt; describing the structure of the subway system, I was able to draw some things. I strongly approve of the existence of the &lt;a href=&#34;https://gtfs.org&#34;&gt;GTFS&lt;/a&gt;, by the way. It&amp;rsquo;s a spec for encoding transit data and lots of cities use it. Really handy.&lt;/p&gt;
&lt;p&gt;Anyway, here&amp;rsquo;s a map. For each precinct with more than twenty voters, I combined the Sliwa/Cuomo vote into what we might call (purely for compactness reasons) the Slimo vote, calculated the Mamdani and Slimo vote shares as a proportion, and subtracted the former from the latter. That gets us a score raning from -1 to +1. I then cut that into bins, ten on each side of the zero line, to get deciles in each direction. That&amp;rsquo;s what we fill the precincts with. For the map I also drew the subway stations and lines. Several lines that are right next to each other are in effect drawn on top of one another in several places, e.g. the A and the C, or the 1 and the 3, etc, but that doesn&amp;rsquo;t matter for this map. (You may have heard that drawing transit maps meant for navigation is a really hard information design challenge.) We then use a discrete, diverging scale. Here&amp;rsquo;s the result.&lt;/p&gt;
&lt;figure class=&#34;full-width&#34;&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2025/11/06/mamdani-vs-sliwa-and-cuomo/subway-mamdani-slimo.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2025/11/06/mamdani-vs-sliwa-and-cuomo/subway-mamdani-slimo.png&#34;
         alt=&#34;Precinct-level vote shares for Mamdani vs Sliwa/Cuomo&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;Oh, choropleths&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I chose a blue-green color gradient partly to experiment with it and partly because neither the election nor this particular cut of the data is quite the usual Blue vs Red, Democrat vs Republican. For one thing we have amalgamated two candidates on one side of the spectrum. For another, Cuomo is in some sense a Democrat, so the way voters were split is trickier than it normally would be. &lt;a href=&#34;https://statmodeling.stat.columbia.edu/2025/11/06/if-cuomo-had-been-able-to-run-against-mamdani-head-to-head/&#34;&gt;Andrew Gelman has some more thoughts on this today&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can see a bunch of nice neighborhood patterns, such as e.g. the Hasidic communities in Brooklyn who voted strongly for Cuomo. And you can also see evidence of the &lt;a href=&#34;https://kieranhealy.org/blog/archives/2015/06/12/americas-ur-choropleths/&#34;&gt;characteristic weakness of choropleths&lt;/a&gt;, which is the way they can force a number characterizing some number of persons to be represented by a shape representing some area of space.&lt;/p&gt;
&lt;p&gt;One solution to this is to make a dot-density map. You put a dot on the map for every person (or maybe every n people) you want to represent. Here&amp;rsquo;s what that looks like, with a 1-to-1 representation of dots to voters.&lt;/p&gt;
&lt;figure class=&#34;full-width&#34;&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2025/11/06/mamdani-vs-sliwa-and-cuomo/subway-mamdani-slimo-dotmap300.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2025/11/06/mamdani-vs-sliwa-and-cuomo/subway-mamdani-slimo-dotmap300.png&#34;
         alt=&#34;Precinct-level vote shares for Mamdani vs Sliwa/Cuomo, dot-density version&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;Dot dot dot.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This is better than a choropleth in some key ways. For one thing, you can see that&amp;mdash;even in New York City&amp;mdash;some places are both much more densely populated than others and also more likely to turn out to vote. To be clear, when I say &amp;ldquo;each dot represents one vote&amp;rdquo;, it&amp;rsquo;s not as if I know the identity of every voter, or how they voted, or can precisely locate them to their home address. I promise I don&amp;rsquo;t know that. Only the NSA and the Phone Company know that stuff. What I do know is how many votes were cast for each candidate within each of the 4,200 or so precincts. And I have a polygon that represents the shape of each precinct. So I spatially sample without replacement within each polygon to randomly place a dot for each voter within their precinct. With two million or so votes the pointillist effect ends up being quite effective.&lt;/p&gt;
&lt;figure class=&#34;full-width&#34;&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2025/11/06/mamdani-vs-sliwa-and-cuomo/subway-mamdani-slimo-detail2.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2025/11/06/mamdani-vs-sliwa-and-cuomo/subway-mamdani-slimo-detail2.png&#34;
         alt=&#34;Sample&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;A slice across Manhattan in the upper 50s and lower 60s and over into Queens.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The main reason for doing this is that, from the point of view of the election, precincts aren&amp;rsquo;t real. They are closely related to the social geography of the city, which is one of the reasons we want to draw a map like this, but in and of themselves they are at best proxies for the thing we care about, so we should take care not to reify them. The sampling method still brings out the basis of the data, so you can see the precinct-patchwork that forms the underlying grid, especially in densely-populated areas. But those polygons are filled in proportion to the number of people who actually voted. I saw someone on social media observe that this &amp;ldquo;conflated&amp;rdquo; partisan lean and population density. But again, precincts aren&amp;rsquo;t real. The distribution of partsian lean across population density is what we&amp;rsquo;re trying to bring out with this quasi-person-level approach.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2025/11/06/mamdani-vs-sliwa-and-cuomo/subway-mamdani-slimo-detail3.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2025/11/06/mamdani-vs-sliwa-and-cuomo/subway-mamdani-slimo-detail3.png&#34;
         alt=&#34;Precinct-level vote shares for Mamdani vs Sliwa/Cuomo, dot-density version, high-res detail&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;Detail of the PDF.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;There are costs, of course. Color choice is harder, especially with more than one category of dot at once. The image tends to look less bright because you&amp;rsquo;re not using a big swatch of paint across a large area. The visual impression is also very sensitive to small changes in the size of the dots and to settings like their alpha transparency. The order that the layers are drawn also matters a great deal at this sort of resolution. In addition, when you draw a few million dots on a large grid of pixels then your file size gets big real fast. The main dot-density image above is a 300dpi 4,500 by 4,500 pixel PNG and it&amp;rsquo;s 12mb in size before being crushed down further (with &lt;code&gt;optipng&lt;/code&gt;) to 9mb or so. I could make a JPG of course, which would be a lot smaller, but then you start running into the question of why you made a dot-density map in the first place, because you lose detail in the raster. In fairness, you&amp;rsquo;d still get the benefit of having larger and less densely-populated (or lower-turnout) precincts not appear fully filled-in, even if you wouldn&amp;rsquo;t be able to zoom in at all.&lt;/p&gt;
&lt;p&gt;Meanwhile, thanks to the wonders of multiplication, a 600dpi version of the same image is much sharper to zoom in on but is also about 35mb in size. A PDF, which is a vector rather than a raster format, is even bigger, weighing in at 63MB. Rendering a PDF with that many vector elements does not make Preview or Illustrator happy, let me tell you. The benefit of course is that you can scale it up to any size you like without loss, as shown above. Because browsers are crazy and so is javascript, it&amp;rsquo;s possible to serve up dot density maps like this in real time in a way that makes them zoomable and fluid, but that&amp;rsquo;s not my department.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Manhattan Plot of Manhattan</title>
      <link>https://kieranhealy.org/blog/archives/2025/10/25/manhattan-plot-of-manhattan/</link>
      <pubDate>Sat, 25 Oct 2025 11:38:02 -0400</pubDate>
      
      <guid>https://kieranhealy.org/blog/archives/2025/10/25/manhattan-plot-of-manhattan/</guid>
      <description>&lt;figure class=&#34;full-width&#34;&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2025/10/25/manhattan-plot-of-manhattan/skyline-plot.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2025/10/25/manhattan-plot-of-manhattan/skyline-plot.png&#34;
         alt=&#34;Skyline plot&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;Here I continue my efforts to design visualizations that are as poorly-suited as possible to being displayed on phones. It looks pretty good on a big monitor, or six feet wide on a wall.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I made a version of this plot a few years ago. I ended up revisiting it this morning  because I&amp;rsquo;m updating various datasets and code. A &lt;a href=&#34;https://en.wikipedia.org/wiki/Manhattan_plot&#34;&gt;Manhattan
plot&lt;/a&gt; is a term sometimes used to describe a kind of scatter plot where the x-values are fairly continuous, and
the y values have distributions with long tails, so the plot looks like a skyline. This one here is a bar chart rather than a scatter plot but it&amp;rsquo;s still a kind of Manhattan plot of Manhattan.&lt;/p&gt;
&lt;p&gt;The plot shows the heights of almost all currently-existing buildings&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; in Manhattan (on the y-axis) by their year of construction on the x-axis. What I want is a plot that gives a sense of the distribution of building heights over time. To make the plot work I play a few tricks. First, the resolution of the x-axis is only to the year,which would result in way too much overplotting. (We have almost 35,000 buildings to draw.) So we add a small amount of random noise to the x-values, which makes buildings distribute themselves around their year of construction. There&amp;rsquo;s still overplotting, but now it works in our favor. It contributes to a feeling of building density.&lt;/p&gt;
&lt;p&gt;Second, there are so many buildings that we can&amp;rsquo;t plot everything as solid, filled rectangle. Instead, we make make the outlines of each rectangle a very thin white line, so everything looks like a vector-driven video game from 1981. Then we make a variable that bins buildings by height into ten categories, one for every hundred feet of additional roof height. We map that to fill color of the rectangles (darker purples for shorter buildings through bright yellow for taller ones), which means the taller a building the brighter it looks. But again, we can&amp;rsquo;t just plot those as solid colors. So we also take the roof heights and rescale them to a range of 0.4 to 0.85. Then we map &lt;em&gt;that&lt;/em&gt; number directly to the alpha channel of the fill color. So taller buildings are not only brighter in color, they are more opaque. Alpha runs from 0 (fully transparent) to 1 (fully opaque). The specific values of 0.4 to 0.85 are just from trial and error. We want the taller buildings to stand out, and because there are fewer of them they need to be more opaque. Whereas the fills of many more shorter buildings will overlap, so they can be individually more transparent.&lt;/p&gt;
&lt;p&gt;You can really see the recent rise of supertall fancy apartment buildings in Manhattan in the last ten years or so&amp;mdash;buildings like the curséd &lt;a href=&#34;https://en.wikipedia.org/wiki/111_West_57th_Street&#34;&gt;111 West 57th&lt;/a&gt; and
&lt;a href=&#34;https://en.wikipedia.org/wiki/53W53&#34;&gt;53 West 53rd&lt;/a&gt;. The cursédness 111 W57th extends to dataset:&lt;/p&gt;
&lt;div class=&#34;highlight-wrapper&#34;&gt;
    
    
        &lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-r&#34; data-lang=&#34;r&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;manhat&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nf&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;heightroof&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1428&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt; Simple feature collection with 1 feature and 19 fields&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt; Geometry type: POLYGON&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt; Dimension:     XY&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt; Bounding box:  xmin: 990374 ymin: 217856.8 xmax: 990559.1 ymax: 218081.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt; Projected CRS: NAD83 / New York Long Island (ftUS)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt;   name     bin cnstrct_yr date_lstmo   time_lstmo lststatype doitt_id heightroof feat_code groundelev shape_area shape_len   base_bbl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt; 1 &amp;lt;NA&amp;gt; 1023728       1924 2021-01-04 00:00:00.000     Merged  1260269       1428      2100         58          0         0 1010100025&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt;   mpluto_bbl geomsource BoroCode  BoroName Shape_Leng Shape_Area                       geometry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#&amp;gt; 1 1010107507 Other (Man        1 Manhattan   359993.1  636620786 POLYGON ((990461.3 217856.8...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
    
&lt;/div&gt;

&lt;p&gt;It&amp;rsquo;s recorded as having been completed in 1924, instead of 2019. Why? There&amp;rsquo;s a hint in the &lt;code&gt;lststatype&lt;/code&gt; column, which says &amp;ldquo;Merged&amp;rdquo;. Technically the building took over and &amp;ldquo;renovated&amp;rdquo; Steinway Hall, which &lt;em&gt;was&lt;/em&gt; built in 1924. There are a few cases like this in the dataset for buildings that are going to be salient in the figure&amp;mdash;i.e. very tall ones. There&amp;rsquo;s also some missing data, with eight buildings over 600 feet tall where the construction year is not available. At least two of those are because the building was still under construction when the data were recorded. As always, 90% of data analysis is data cleaning.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Data on building heights and construction years come from the &lt;a href=&#34;https://data.cityofnewyork.us/Housing-Development/Building-Footprints/nqwf-w8eh&#34;&gt;NYC Open Data portal&lt;/a&gt;. The data are restricted to buildings constructed after 1899 that are currently standing in Manhattan.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
    </item>
    
    <item>
      <title>MTA Ridership</title>
      <link>https://kieranhealy.org/blog/archives/2025/02/19/mta-ridership/</link>
      <pubDate>Wed, 19 Feb 2025 20:31:00 -0500</pubDate>
      
      <guid>https://kieranhealy.org/blog/archives/2025/02/19/mta-ridership/</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s a Figure of the Day. The &lt;a href=&#34;https://mta.info&#34;&gt;MTA&lt;/a&gt;, and especially the Subway, moves a &lt;em&gt;lot&lt;/em&gt; of people every day. They also make daily ridership data available. The lines here are weekly averages, either of daily ridership or traffic volume through toll booths.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2025/02/19/mta-ridership/mta_volumes.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2025/02/19/mta-ridership/mta_volumes.png&#34;
         alt=&#34;MTA Volume&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;More rides are processed through the Subway every day on average than there were people living in the country I grew up in.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;Updates:&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I redrew this to use &lt;a href=&#34;https://lubridate.tidyverse.org/reference/week.html&#34;&gt;epiweeks&lt;/a&gt; rather than weeks, so as not to generate artificially large dips in the final week of the year.&lt;/li&gt;
&lt;li&gt;Updated the figure with data through March 31st 2025, because this stupid talking point keeps coming up.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>A New York City Adults and Children Dotmap</title>
      <link>https://kieranhealy.org/blog/archives/2024/06/01/a-new-york-city-adults-and-children-dotmap/</link>
      <pubDate>Sat, 01 Jun 2024 10:57:58 -0400</pubDate>
      
      <guid>https://kieranhealy.org/blog/archives/2024/06/01/a-new-york-city-adults-and-children-dotmap/</guid>
      <description>&lt;p&gt;One more NYC dot-density map. Apart from a basic population count and the race/ethnicity data, about the only other block-level data we have from the 2020 decennial Census is information on the number of adults aged 18 and over. This is because the Census Bureau has to provide these counts quickly to Congress to allow for redistricting. Because we have the total count, we can infer the number of children (persons under 18 years). So here is a dot-density map of that.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2024/06/01/a-new-york-city-adults-and-children-dotmap/ny_dotpop_n10_2020_points_adults_kids_5000px.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2024/06/01/a-new-york-city-adults-and-children-dotmap/ny_dotpop_n10_2020_points_adults_kids_5000px.png&#34;
         alt=&#34;NYC Adult/Children dot-density map&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;The distribution of adults and children in New York City in 2020&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href=&#34;ny_dotpop_n10_2020_points_adults_kids_1000ppi.png&#34;&gt;A higher-resolution PNG is available as well&lt;/a&gt;, weighing in at about 20MB.&lt;/p&gt;
&lt;p&gt;The method is the same as described for &lt;a href=&#34;https://kieranhealy.org/blog/archives/2024/05/30/a-population-dotmap-of-new-york-city/&#34;&gt;earlier maps&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A New York City Race and Ethnicity Dotmap</title>
      <link>https://kieranhealy.org/blog/archives/2024/05/31/a-new-york-city-race-and-ethnicity-dotmap/</link>
      <pubDate>Fri, 31 May 2024 17:38:15 -0400</pubDate>
      
      <guid>https://kieranhealy.org/blog/archives/2024/05/31/a-new-york-city-race-and-ethnicity-dotmap/</guid>
      <description>&lt;p&gt;As promised in the &lt;a href=&#34;https://kieranhealy.org/blog/archives/2024/05/30/a-population-dotmap-of-new-york-city/&#34;&gt;previous installment&lt;/a&gt;, where we drew a dot-density map of New York City&amp;rsquo;s population, I did the same again only coloring it by the race and ethnicity reported by respondents in the 2020 Census. The direct comparison for this map are the block-level &lt;a href=&#34;https://kieranhealy.org/blog/archives/2024/05/29/race-and-ethnicity-in-new-york-city/&#34;&gt;race and ethnicity choropleths&lt;/a&gt; I drew the other day. The data source is the same, it&amp;rsquo;s just that now we use it to make a dot map with a point for every ten people (on average, we round to the nearest ten) of a particular racial or ethnic group in a given Census block unit. As before we use the current Census categories. We have &amp;ldquo;racial&amp;rdquo; categories of White, Black, Asian, and Other. The latter collapses the Census categories  &amp;ldquo;Native Hawaiian and Other Pacific Islander&amp;rdquo;, &amp;ldquo;American Indian and Alaska Native&amp;rdquo;, and &amp;ldquo;Some Other Race&amp;rdquo; where the numbers are too small to appear on this map. Meanwhile we separately track those reporting &amp;ldquo;Two or More Races&amp;rdquo;. The Census breaks this out into further multiracial categories but here we just code &amp;ldquo;Two or More&amp;rdquo; as &amp;ldquo;Multiracial&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Meanwhile, we have to cross-classify the race categories with responses to the &amp;ldquo;Hispanic or Latino&amp;rdquo; question. In the Census&amp;rsquo;s schema, Hispanics may be of any race. (Again, there is an extensive literature on why things are like this in the Census and how it continues to change.) For the purposes of this map it makes sense to try to show racial and Hispanic/Latino categories jointly for larger groups. So we end up with nine categories. As you can see on the map, many people reporting themselves to be Hispanic/Latino will also say they are &amp;ldquo;Some other race&amp;rdquo;. Smaller proportions of Hispanic/Latino respondents will identify as White or Black alone.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the map.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2024/05/31/a-new-york-city-race-and-ethnicity-dotmap/ny_dotpop_n10_2020_points_hisp_race_5000px.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2024/05/31/a-new-york-city-race-and-ethnicity-dotmap/ny_dotpop_n10_2020_points_hisp_race_5000px.png&#34;
         alt=&#34;NYC population dot-density map by race and ethnicity&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;Dot-density map for New York City&amp;rsquo;s population by race and ethnicity.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;There&amp;rsquo;s also a &lt;a href=&#34;ny_dotpop_n10_2020_points_hisp_race_1000ppi.png&#34;&gt;higher-resolution PNG version&lt;/a&gt; which is about 20MB in size.&lt;/p&gt;
&lt;p&gt;As before, this map was made with R along with the ggplot2, tidycensus and sf packages (primarily). Some post-processing in Illustrator was a good lesson in how to make a really quite powerful computer run very slowly indeed.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Population Dotmap of New York City</title>
      <link>https://kieranhealy.org/blog/archives/2024/05/30/a-population-dotmap-of-new-york-city/</link>
      <pubDate>Thu, 30 May 2024 19:38:23 -0400</pubDate>
      
      <guid>https://kieranhealy.org/blog/archives/2024/05/30/a-population-dotmap-of-new-york-city/</guid>
      <description>&lt;p&gt;Our &lt;a href=&#34;https://kieranhealy.org/blog/archives/2024/05/29/race-and-ethnicity-in-new-york-city/&#34;&gt;New York City map adventure&lt;/a&gt; continues, this time with a first cut at a population dotmap. We take the block-level Census count for 2020, divide it by ten, and round that to the nearest whole number. Then we sample that number of points at random within the area of each block. So, for example, if a Census block is recorded as having 571 people in it, we mark 57 dots at random within the spatial area of that block. Then we convert each of those dots to coordinates on our map and plot them. The result is a map with a lot of dots that gives a pretty good sense of the population distribution within the city. Here is a relatively scaled-down version because in a city of more than eight million people we end up with a &lt;em&gt;lot&lt;/em&gt; of dots.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2024/05/30/a-population-dotmap-of-new-york-city/ny_dotpop_n10_2020_points_minimal_1200ppi.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2024/05/30/a-population-dotmap-of-new-york-city/ny_dotpop_n10_2020_points_minimal_1200ppi.png&#34;
         alt=&#34;Population dotmap of New York City&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;Population Dotmap of NYC&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;There&amp;rsquo;s also a &lt;a href=&#34;ny_dotpop_n10_2020_points_minimal_2400ppi.png&#34;&gt;higher-resolution version&lt;/a&gt; which is about 17MB in size. Contrary to my usual practice I won&amp;rsquo;t share a PDF version of this one, because it&amp;rsquo;s really pretty big.&lt;/p&gt;
&lt;p&gt;I very much like the pointillist look of this sort of map. They do a good job of compensating for some of the weaknesses of choropleths even though, we have to bear in mind once again, they are ultimately representations of a block-level count. A natural next step (which I imagine I&amp;rsquo;ll get to) would be to take the counts e.g. by race and point-map those with different colors.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Race and Ethnicity in New York City</title>
      <link>https://kieranhealy.org/blog/archives/2024/05/29/race-and-ethnicity-in-new-york-city/</link>
      <pubDate>Wed, 29 May 2024 07:33:56 -0400</pubDate>
      
      <guid>https://kieranhealy.org/blog/archives/2024/05/29/race-and-ethnicity-in-new-york-city/</guid>
      <description>&lt;p&gt;I&amp;rsquo;m about to start work on a second edition of my &lt;a href=&#34;https://socviz.co&#34;&gt;Data Visualization&lt;/a&gt; book. As a result I continue to mess around with stuff I&amp;rsquo;m considering including in a new edition. The other day I pulled some block-level Census data and drew a map of the distribution of people of color in New York City, which is to say the share of the population that reports being something other than Non-Hispanic White. I&amp;rsquo;ve polished that map a little more and drawn some additional ones. As before, the main substantive issues to bear in mind are (a) how the Census measures and classifies race and ethnicity, and (b) the new and I am inclined to think &lt;a href=&#34;https://www.aeaweb.org/articles?id=10.1257/pandp.20191107&#34;&gt;unwise&lt;/a&gt; practice of &amp;ldquo;differential privacy&amp;rdquo;. The main thing to know about the former is that in the present US Census Bureau schema people of Hispanic or Latino origin may be of any race. (I follow the Census&amp;rsquo;s nomenclature here, by the way, as it&amp;rsquo;s their data.) This is the reason that &amp;ldquo;Non-Hispanic White&amp;rdquo; is a category for example. The main thing to know about the latter is that it deliberately introduces noise into counts within units where the observed N is small.&lt;/p&gt;
&lt;p&gt;The other thing to remember is every &lt;a href=&#34;https://kieranhealy.org/blog/archives/2015/06/12/americas-ur-choropleths/&#34;&gt;choropleth maker&amp;rsquo;s&lt;/a&gt; oldest friend, the &lt;a href=&#34;https://en.wikipedia.org/wiki/Modifiable_areal_unit_problem&#34;&gt;Modifiable Areal Unit Problem&lt;/a&gt;. Census Blocks are the smallest spatial unit we can make a choropleth map of, but they&amp;rsquo;re not &amp;ldquo;real&amp;rdquo;, so to speak.&lt;/p&gt;
&lt;p&gt;As usual, the tools used to make these maps are R, ggplot, and the &lt;a href=&#34;https://walker-data.com/tidycensus/&#34;&gt;tidycensus&lt;/a&gt; and &lt;a href=&#34;https://r-spatial.github.io/sf/&#34;&gt;sf&lt;/a&gt; packages. Together, of course, with the really phenomenal range of data made available by the &lt;a href=&#34;https://www.census.gov/data/developers/data-sets.html&#34;&gt;Census Bureau API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For these new versions I decided to add a little more context by sketching in some of the coastline, particularly the outline of New Jersey to the west, along with a reminder that Long Island continues to exist past the Queens border, etc These  coastal outlines come from the &lt;a href=&#34;https://nsde.ngs.noaa.gov&#34;&gt;NOAA CUSP maps&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2024/05/29/race-and-ethnicity-in-new-york-city/ny_pctpoc_2020_out_600.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2024/05/29/race-and-ethnicity-in-new-york-city/ny_pctpoc_2020_out_600.png&#34;
         alt=&#34;New York City&amp;rsquo;s POC population.&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;New York City&amp;rsquo;s POC population.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2024/05/29/race-and-ethnicity-in-new-york-city/ny_pct_black_alone_2020_out_600.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2024/05/29/race-and-ethnicity-in-new-york-city/ny_pct_black_alone_2020_out_600.png&#34;
         alt=&#34;New York City population, percent reporting &amp;lsquo;Black Alone&amp;rsquo; to race question.&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;New York City population, percent reporting &amp;lsquo;Black Alone&amp;rsquo; to race question.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2024/05/29/race-and-ethnicity-in-new-york-city/ny_pct_hispanic_2020_out_600.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2024/05/29/race-and-ethnicity-in-new-york-city/ny_pct_hispanic_2020_out_600.png&#34;
         alt=&#34;New York City population, percent Hispanic/Latino origin.&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;New York City population, percent Hispanic/Latino origin.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;a href=&#34;https://kieranhealy.org/blog/archives/2024/05/29/race-and-ethnicity-in-new-york-city/ny_pct_asian_alone_2020_out_600.png&#34; data-fancybox&gt;
    &lt;img src=&#34;https://kieranhealy.org/blog/archives/2024/05/29/race-and-ethnicity-in-new-york-city/ny_pct_asian_alone_2020_out_600.png&#34;
         alt=&#34;New York City population, precent reporting &amp;lsquo;Asian Alone&amp;rsquo; to race question.&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;New York City population, precent reporting &amp;lsquo;Asian Alone&amp;rsquo; to race question.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;PDFs of these maps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;ny_pctpoc_2020_out.pdf&#34;&gt;Percent POC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;ny_pct_black_alone_2020_out.pdf&#34;&gt;Percent Black Alone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;ny_pct_hispanic_2020_out.pdf&#34;&gt;Percent Hispanic/Latino&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;ny_pct_asian_alone_2020_out.pdf&#34;&gt;Percent Asian Alone&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
  </channel>
</rss>
