<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>And now it’s all this</title>
	<atom:link href="https://leancrew.com/all-this/feed/" rel="self" type="application/rss+xml" />
	<link>https://leancrew.com/all-this/</link>
	<description>I just said what I said and it was wrong. Or was taken wrong.</description>
	<lastBuildDate>Wed, 30 Sep 2026 21:13:17 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=4.0</generator>
  <atom:link rel="hub" href="http://pubsubhubbub.appspot.com"/>
  <atom:link rel="hub" href="http://aniat.superfeedr.com"/>

<item>
<title>Too dumb for AI</title>
<link>https://leancrew.com/all-this/2026/09/too-dumb-for-ai/</link>
<pubDate>Wed, 30 Sep 2026 21:13:17 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/09/too-dumb-for-ai/</guid>
<description>
  <![CDATA[I’ll admit that the first thing I looked into regarding <a href="https://www.reuters.com/world/us/trump-tech-executives-sign-morally-binding-ai-document-2026-09-29/">yesterday’s “Super Intelligence” meeting</a> at the White House was whether Tim Cook was there. He wasn’t, which was probably a relief to Tim, even though it’s also an admission that Apple isn’t considered a player in AI. By anyone, not even the idiots in the Trump administration.]]>
</description>
<content:encoded>
  <![CDATA[<p>I’ll admit that the first thing I looked into regarding <a href="https://www.reuters.com/world/us/trump-tech-executives-sign-morally-binding-ai-document-2026-09-29/">yesterday’s “Super Intelligence” meeting</a> at the White House was whether Tim Cook was there. He wasn’t, which was probably a relief to Tim, even though it’s also an admission that Apple isn’t considered a player in AI. By anyone, not even the idiots in the Trump administration.</p>
<p>You’ve probably seen the stupid misspelling above Trump’s signature on the “morally binding” agreement that came out of the meeting.</p>
<p><img alt="Signature page" class="ss" src="https://leancrew.com/all-this/images2026/20260930-Signature%20page.jpg" title="Signature page" width="80%"/></p>
<p>Yes, he signed it as the “President of the Unites States” and then proudly <a href="https://truthsocial.com/@realDonaldTrump/posts/117356435739432952">posted it to Truth Social</a>. I can’t imagine the agreement means anything, as you’d be hard-pressed to find a single moral shared among the people in the meeting. I will say, though, that Jensen Huang’s signature is pretty spiffy.</p>
<p>As for who was at the meeting, Trump <a href="https://truthsocial.com/@realDonaldTrump/posts/117355137125519227">also posted</a> this seating chart, which hurts to look at:</p>
<p><a href="https://leancrew.com/all-this/images2026/20260930-Seating%20chart.jpg"><img alt="Seating chart" class="ss" src="https://leancrew.com/all-this/images2026/20260930-Seating%20chart.jpg" title="Seating chart" width="80%"/></a></p>
<p>Click the image to see it at full size, if you dare. Also, don’t feel obligated to follow either of the Truth Social links; I’m including them only because I feel an obligation to give credit—or in this case blame.</p>
<p>Lots of the images Trump posts are AI-generated, but I find it hard to believe that any AI system would produce something with this many graphical errors:</p>
<ul>
<li>The chairs on the left side of the table are vertically offset from those on the right side, even though there are the same number on each side. Poor Will Scharf is nearly off the end.</li>
<li>The chairs on the left are distinctly farther from the table than the chairs on the right.</li>
<li>The vertical spaces between chairs on a given side aren’t equal. The seats near the ends are closer to one another than the seats near the center.</li>
<li>The two lines of chairs aren’t horizontally aligned. This was perhaps the smallest mistake, and you probably can’t see it unless you zoom in, but what drawing app wouldn’t automatically align the items?</li>
<li>The names and chairs aren’t aligned vertically. You’d expect the names to be centered vertically with the chairs, and many of them are at least close to that, but once you see Alex Karp and Andrew Ferguson you’ll notice several similar misalignments.</li>
<li>The names on the left side should be right-aligned but aren’t. Susie Wiles is way out of her chair, while David Sacks has nearly crashed into his.</li>
<li>Similarly, the names on the right side should be left-aligned but aren’t. See Richard Walters and Sanjay Mehrotra for the most extreme misalignments.</li>
</ul>
<p>And then there’s the best one:</p>
<ul>
<li>The chairs are facing away from the table.</li>
</ul>
<p>The meeting probably would have been better for all of us if this had been so.</p>]]>
</content:encoded>
</item>

<item>
<title>A small probability correction</title>
<link>https://leancrew.com/all-this/2026/09/a-small-probability-correction/</link>
<pubDate>Tue, 29 Sep 2026 15:35:09 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/09/a-small-probability-correction/</guid>
<description>
  <![CDATA[<a href="https://youtube.com/shorts/2xw2d46fwYc">This short video</a> appeared in my YouTube feed last week. It’s from <a href="https://hannahfry.co.uk/">Hannah Fry</a>, whom you probably know from her appearances on <a href="https://www.youtube.com/numberphile">Numberphile</a> and other STEM-oriented stuff. If you’re in the UK, you’ve may have seen her on the BBC, too.]]>
</description>
<content:encoded>
  <![CDATA[<p>[Equations in this post may not look right (or appear at all) in your RSS reader. Go to <a href="https://leancrew.com/all-this/2026/09/a-small-probability-correction/">the original article</a> to see them rendered properly.]</p>
  <hr />
  <p><a href="https://youtube.com/shorts/2xw2d46fwYc">This short video</a> appeared in my YouTube feed last week. It’s from <a href="https://hannahfry.co.uk/">Hannah Fry</a>, whom you probably know from her appearances on <a href="https://www.youtube.com/numberphile">Numberphile</a> and other STEM-oriented stuff. If you’re in the UK, you’ve may have seen her on the BBC, too.</p>
<p>The short presents a classic problem in conditional probability, but the answer she comes up with is wrong. It’s a good estimate, and it’s the same answer I got when I stopped the video and tried to work it out in my head, but it’s still wrong. By just a little bit.</p>
<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="720" src="https://www.youtube.com/embed/2xw2d46fwYc" title="YouTube video player" width="405"></iframe>
<p>Here’s the problem: There’s a disease that affects 1 in 1,000 people. A test for the disease is perfect in one sense but imperfect in another. If you have the disease, the test will return a positive result 100% of the time. If you don’t have the disease, the test will return a negative result 95% of the time but a positive result 5% of the time. If you have the test and the result is positive, what is the probability you have the disease?</p>
<p>What makes this a classic problem is that it presents you with conditional probability in one sense (the probability of a positive test given that you have the disease) and asks for a conditional probability in the opposite sense (the probability that you have the disease given that your test was positive). The solution combines <a href="https://en.wikipedia.org/wiki/Conditional_probability">the definition of conditional probability</a>, the <a href="https://en.wikipedia.org/wiki/Commutative_property#Commutative_operations">commutative property of intersections</a>, and <a href="https://en.wikipedia.org/wiki/Law_of_total_probability">the law of total probability</a>.</p>
<p>Let’s define some events. <!-- $D$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mi>D</mi></math> is you having the disease, and <!-- $T$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mi>T</mi></math> is you getting a positive test result. Putting a horizontal bar over these indicates <em>not</em> having the disease and <em>not</em> testing positive (i.e., testing negative), respectively. Therefore</p>
<p><!-- $$ \begin{align*}
P(D) &= 0.001 \\
P(\overline{D}) &= 0.999 \\
P(T \mid D) &= 1 \\
P(\overline{T} \mid \overline{D}) &= 0.95 \\
P(T \mid \overline{D}) &= 0.05
\end{align*} $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mtable><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>D</mi><mo form="postfix" stretchy="false">)</mo></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mo>=</mo><mn>0.001</mn></mtd></mtr><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mover><mi>D</mi><mo accent="true">—</mo></mover><mo form="postfix" stretchy="false">)</mo></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mo>=</mo><mn>0.999</mn></mtd></mtr><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>T</mi><mo>∣</mo><mi>D</mi><mo form="postfix" stretchy="false">)</mo></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mo>=</mo><mn>1</mn></mtd></mtr><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mover><mi>T</mi><mo accent="true">—</mo></mover><mo>∣</mo><mover><mi>D</mi><mo accent="true">—</mo></mover><mo form="postfix" stretchy="false">)</mo><mspace width=".25em"></mspace></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mo>=</mo><mn>0.95</mn></mtd></mtr><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>T</mi><mo>∣</mo><mover><mi>D</mi><mo accent="true">—</mo></mover><mo form="postfix" stretchy="false">)</mo></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mo>=</mo><mn>0.05</mn></mtd></mtr></mtable></math></p>
<p>The vertical bars are read as “given,” meaning the event after the bar is the condition. What we’ve been asked to find is <!-- $P(D \mid T)$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>D</mi><mo>∣</mo><mi>T</mi><mo form="postfix" stretchy="false">)</mo></mrow></math>. Let’s work it out.</p>
<p>By the definition of conditional probability, we can say</p>
<p><!-- $$ P(D \cap T) = P(D \mid T) \, P(T) $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>D</mi><mo>∩</mo><mi>T</mi><mo form="postfix" stretchy="false">)</mo><mo>=</mo><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>D</mi><mo>∣</mo><mi>T</mi><mo form="postfix" stretchy="false">)</mo><mspace width="0.167em"></mspace><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>T</mi><mo form="postfix" stretchy="false">)</mo></mrow></math></p>
<p>where <!-- $\cap$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mi>∩</mi></math> means the intersection of the two events. So</p>
<p><!-- $$ P(D \mid T) = \frac{P(D \cap T)}{P(T)} $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>D</mi><mo>∣</mo><mi>T</mi><mo form="postfix" stretchy="false">)</mo><mo>=</mo><mfrac><mrow><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>D</mi><mo>∩</mo><mi>T</mi><mo form="postfix" stretchy="false">)</mo></mrow><mrow><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>T</mi><mo form="postfix" stretchy="false">)</mo></mrow></mfrac></mrow></math></p>
<p>Because the intersection of events is commutative</p>
<p><!-- $$ P(D \cap T) = P(T \cap D) = P(T \mid D) \, P(D) $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>D</mi><mo>∩</mo><mi>T</mi><mo form="postfix" stretchy="false">)</mo><mo>=</mo><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>T</mi><mo>∩</mo><mi>D</mi><mo form="postfix" stretchy="false">)</mo><mo>=</mo><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>T</mi><mo>∣</mo><mi>D</mi><mo form="postfix" stretchy="false">)</mo><mspace width="0.167em"></mspace><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>D</mi><mo form="postfix" stretchy="false">)</mo></mrow></math></p>
<p>Both terms on the right-hand side of this equation are known, so we can say</p>
<p><!-- $$ P(D \mid T) = \frac{(1)(0.001)}{P(T)} $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>D</mi><mo>∣</mo><mi>T</mi><mo form="postfix" stretchy="false">)</mo><mo>=</mo><mfrac><mrow><mo form="prefix" stretchy="false">(</mo><mn>1</mn><mo form="postfix" stretchy="false">)</mo><mo form="prefix" stretchy="false">(</mo><mn>0.001</mn><mo form="postfix" stretchy="false">)</mo></mrow><mrow><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>T</mi><mo form="postfix" stretchy="false">)</mo></mrow></mfrac></mrow></math></p>
<p>Since <!-- $D$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mi>D</mi></math> and <!-- $\overline{D}$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mover><mi>D</mi><mo accent="true">—</mo></mover></math> are mutually exclusive and collectively exhaustive, the law of total probability says</p>
<p><!-- $$ \begin{align}
P(T) &= P(T \mid D) \, P(D) + P(T \mid \overline{D}) \, P(\overline{D}) \\
 &= (1)(0.001) + (0.05)(0.999) \\
 &= 0.05095
\end{align} $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mtable><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>T</mi><mo form="postfix" stretchy="false">)</mo></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mo>=</mo><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>T</mi><mo>∣</mo><mi>D</mi><mo form="postfix" stretchy="false">)</mo><mspace width="0.167em"></mspace><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>D</mi><mo form="postfix" stretchy="false">)</mo><mo>+</mo><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>T</mi><mo>∣</mo><mover><mi>D</mi><mo accent="true">—</mo></mover><mo form="postfix" stretchy="false">)</mo><mspace width="0.167em"></mspace><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mover><mi>D</mi><mo accent="true">—</mo></mover><mo form="postfix" stretchy="false">)</mo></mtd></mtr><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mo>=</mo><mo form="prefix" stretchy="false">(</mo><mn>1</mn><mo form="postfix" stretchy="false">)</mo><mo form="prefix" stretchy="false">(</mo><mn>0.001</mn><mo form="postfix" stretchy="false">)</mo><mo>+</mo><mo form="prefix" stretchy="false">(</mo><mn>0.05</mn><mo form="postfix" stretchy="false">)</mo><mo form="prefix" stretchy="false">(</mo><mn>0.999</mn><mo form="postfix" stretchy="false">)</mo></mtd></mtr><mtr><mtd columnalign="right" style="text-align: right; padding-right: 0"></mtd><mtd columnalign="left" style="text-align: left; padding-left: 0"><mo>=</mo><mn>0.05095</mn></mtd></mtr></mtable></math></p>
<p>So</p>
<p><!-- $$ P(D \mid T) = \frac{0.001}{0.05095} = 0.019627 $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>P</mi><mo form="prefix" stretchy="false">(</mo><mi>D</mi><mo>∣</mo><mi>T</mi><mo form="postfix" stretchy="false">)</mo><mo>=</mo><mfrac><mn>0.001</mn><mn>0.05095</mn></mfrac><mo>=</mo><mn>0.019627</mn></mrow></math></p>
<p>which is, as I said, pretty close to the 2% answer in the video but not exactly.</p>
<p>This formal approach is how you’re taught to solve problems like this in an introductory probability class, but Dr. Fry and I used a more concrete method to get our nearly correct answers. Here’s what we did:</p>
<p>Imagine 1,000 typical people. Of these, 1 should have the disease (correct) and 50 should test positive (incorrect). That tells us that 1 in 50, or 2%, of the people who test positive will have the disease. Here’s a screenshot from the video that matches this calculation:</p>
<p><img alt="Illustration using 1000 people" class="ss" src="https://leancrew.com/all-this/images2026/20260929-Illustration%20using%201000%20people.jpg" title="Illustration using 1000 people" width="65%"/></p>
<p>What makes this calculation wrong is that it implicitly assumes that 5% of <em>everyone</em> will test positive, not 5% of only those who don’t have the disease. The number who will test positive should be 5% of 999, which is 49.95, plus the 1 who does have the disease. So 1 in 50.95, or 1.9627%, of those who test positive will have the disease. This answer matches that of the formal approach.</p>
<p>This is somewhat unsatisfying, though, as the purpose of this “imagine a bunch of typical people” method is to have all the people counts be integers. Although the numbers work out when you get to the end, it’s distracting to litter the discussion with fractional people. You can get around this by imagining more people—a million, say—but then all the numbers get bigger: 1,000 people with the disease and 49,950 false positives. This isn’t a problem for the kind of people who read this blog but isn’t so great for the more general audience Dr. Fry is addressing.</p>
<p>Personally, I would have been OK with her saying that 5% of 999 is almost 50, so the number who test positive is nearly 51. And 1 out of 51 is just under 2%—call it 2% in round figures. You still make the point that it’s way less than 95% and that problems like this require some care.</p>
  ]]>
</content:encoded>
</item>

<item>
<title>Equinox</title>
<link>https://leancrew.com/all-this/2026/09/equinox/</link>
<pubDate>Wed, 23 Sep 2026 00:05:13 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/09/equinox/</guid>
<description>
  <![CDATA[The episode of <em>The West Wing</em> that’s always bothered me is “Evidence of Things Not Seen,” which is <a href="https://westwing.fandom.com/wiki/Evidence_of_Things_Not_Seen">Episode 20 of Season 4</a>. It takes place on the day of the equinox (in March, not September, but I remembered it because today is an equinox), and one of the continuing subplots is C.J. trying to convince the others that you can stand an egg on its end at the exact moment of the equinox and only at that moment. Oh, and it has to be the vernal equinox, not the autumnal one.<sup id="fnref:south"><a href="#fn:south" rel="footnote">1</a></sup>]]>
</description>
<content:encoded>
  <![CDATA[<p>The episode of <em>The West Wing</em> that’s always bothered me is “Evidence of Things Not Seen,” which is <a href="https://westwing.fandom.com/wiki/Evidence_of_Things_Not_Seen">Episode 20 of Season 4</a>. It takes place on the day of the equinox (in March, not September, but I remembered it because today is an equinox), and one of the continuing subplots is C.J. trying to convince the others that you can stand an egg on its end at the exact moment of the equinox and only at that moment. Oh, and it has to be the vernal equinox, not the autumnal one.<sup id="fnref:south"><a href="#fn:south" rel="footnote">1</a></sup></p>
<p>That someone in the glorious Bartlet White House believes in nonsense isn’t what bothers me. Lots of smart people believe silly things. What’s wrong is that it’s C.J., the primary female character, who believes in superstition while the men around her are pooh-poohing it. I’m sure plenty of women will tell me that’s par for the course for Aaron Sorkin, but it still bothers me.</p>
<p>There are two small counters to the general sexism of the egg subplot. First, the men who tell C.J. she’s full of it are themselves wrong. They think you can’t balance an egg on its end, even though it’s not really that hard.</p>
<p><img alt="Egg balanced on its end" class="ss" src="https://leancrew.com/all-this/images2026/20260922-Egg%20balanced%20on%20its%20end.jpg" title="Egg balanced on its end" width="80%"/></p>
<p>This photo was taken today (yes, on the equinox but not the vernal equinox and not at the exact moment) on my back patio. I didn’t do any tricks like slightly crack the shell or put it on a bit of salt and then blow the salt away. I just kept adjusting its position again and again until it stood. The plain fact is that it just takes a little time to balance an egg on its end, time that most people aren’t willing to spend. (And yes, it helps to have a rough surface.)</p>
<p>The second counter is that Jed seems willing to believe the egg story. He doesn’t defend C.J., but he does try to stand an egg on end in his office. In some ways, this is worse. For all his faults, Jed is supposed to know at least a bit about science. Remember the episode in which C.J. has to remind him to let the NASA experts handle the science questions coming in from schoolkids? If anyone should be instantly dismissive of the standing egg myth, it should be Jed.</p>
<p>At the very end of the show, C.J.’s alone and manages to balance an egg on its end, justifying her faith. The title comes from Hebrews 11:1, which she recites earlier in the episode. I guess this is supposed to get us to think that she hasn’t been spouting nonsense all this time, but what it really means is that she can do what anyone can do if they take their time.</p>
<p>What bothers me the most is the setup for her balancing act. Before trying, she looks pointedly at the clock, which shows midnight. Recall that the balancing is supposed to work only at the exact moment of the equinox. Does Sorkin think equinoxes always occur at midnight? Does he think the audience is dumb enough to believe that? The episode aired in April 2003, so I guess its events take place on March 20 of that year. In Washington, <a href="https://www.windom.org/mycog.com/equinox.htm">the 2003 vernal equinox</a> occurred at about 8:00 pm, not four hours later. Was the show set a year or two earlier? No midnight spring equinoxes those years, either.</p>
<p>If you’re interested in the history of the egg balancing myth, Martin Gardner wrote an article about it in 1996. You can see the text on the <a href="https://web.archive.org/web/20070221082438/http://www.findarticles.com/p/articles/mi_m2843/is_n3_v20/ai_18372128">Wayback Machine</a>.</p>
<div class="footnotes">
<hr/>
<ol>
<li id="fn:south">
<p>No one ever asks whether the egg balancing would work if they were in Buenos Aires instead of Washington. <a href="#fnref:south" rev="footnote">↩</a></p>
</li>
</ol>
</div>]]>
</content:encoded>
</item>

<item>
<title>Cleaning up after Matt Parker</title>
<link>https://leancrew.com/all-this/2026/09/cleaning-up-after-matt-parker/</link>
<pubDate>Thu, 17 Sep 2026 04:36:43 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/09/cleaning-up-after-matt-parker/</guid>
<description>
  <![CDATA[<a href="https://www.youtube.com/watch?v=k3sIHbj-TUI">This video</a> Matt Parker posted a couple of days ago really pissed me off:]]>
</description>
<content:encoded>
  <![CDATA[<p>[Equations in this post may not look right (or appear at all) in your RSS reader. Go to <a href="https://leancrew.com/all-this/2026/09/cleaning-up-after-matt-parker/">the original article</a> to see them rendered properly.]</p>
  <hr />
  <p><a href="https://www.youtube.com/watch?v=k3sIHbj-TUI">This video</a> Matt Parker posted a couple of days ago really pissed me off:</p>
<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="315" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/k3sIHbj-TUI?si=bTN92Wcda6mWQLER" title="YouTube video player" width="560"></iframe>
<p>It’s cheating to say that a result arrived at through classical mechanics—in this case, that <!-- $\frac{1}{2} m v^2$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mfrac><mn>1</mn><mn>2</mn></mfrac><mi>m</mi><msup><mi>v</mi><mn>2</mn></msup></mrow></math> is the kinetic energy of a particle—is wrong because it’s only an approximation to relativistic physics. Of course that’s the case, and I swore at my phone when the “reveal” came.</p>
<p>By the end of the video, though, I had calmed down because I knew I could put together a quick post of my own by rewriting the infinite series equation he derived in a better form. Also, I could provide a general expression for the individual terms.</p>
<p>Let’s start with this result: that the kinetic energy of a particle (including relativistic effects) is</p>
<p><!-- $$ m c^2 ( \gamma - 1 ) $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>m</mi><msup><mi>c</mi><mn>2</mn></msup><mo form="prefix" stretchy="false">(</mo><mi>γ</mi><mo>−</mo><mn>1</mn><mo form="postfix" stretchy="false">)</mo></mrow></math></p>
<p>where <em>m</em> is the mass of the particle, <em>c</em> is the speed of light, and</p>
<p><!-- $$ \gamma = \frac{1}{\sqrt{ 1 - \frac{v^2}{c^2} } } $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>γ</mi><mo>=</mo><mfrac><mn>1</mn><msqrt><mrow><mn>1</mn><mo>−</mo><mfrac><msup><mi>v</mi><mn>2</mn></msup><msup><mi>c</mi><mn>2</mn></msup></mfrac></mrow></msqrt></mfrac></mrow></math></p>
<p>is the <a href="https://en.wikipedia.org/wiki/Lorentz_factor">Lorentz factor</a>, with <em>v</em> as the velocity of the particle. The video gets to this result at about the 8-minute mark.</p>
<p>Matt then does a series expansion of the <!-- $\gamma$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mi>γ</mi></math> term to get</p>
<p><!-- $$ \gamma = 1 + \frac{1}{2}\,\frac{v^2}{c^2} + \frac{3}{8}\,\frac{v^4}{c^4} + \frac{5}{16}\,\frac{v^6}{c^6} + \frac{35}{128}\,\frac{v^8}{c^8} + \ldots $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>γ</mi><mo>=</mo><mn>1</mn><mo>+</mo><mfrac><mn>1</mn><mn>2</mn></mfrac><mspace width="0.167em"></mspace><mfrac><msup><mi>v</mi><mn>2</mn></msup><msup><mi>c</mi><mn>2</mn></msup></mfrac><mo>+</mo><mfrac><mn>3</mn><mn>8</mn></mfrac><mspace width="0.167em"></mspace><mfrac><msup><mi>v</mi><mn>4</mn></msup><msup><mi>c</mi><mn>4</mn></msup></mfrac><mo>+</mo><mfrac><mn>5</mn><mn>16</mn></mfrac><mspace width="0.167em"></mspace><mfrac><msup><mi>v</mi><mn>6</mn></msup><msup><mi>c</mi><mn>6</mn></msup></mfrac><mo>+</mo><mfrac><mn>35</mn><mn>128</mn></mfrac><mspace width="0.167em"></mspace><mfrac><msup><mi>v</mi><mn>8</mn></msup><msup><mi>c</mi><mn>8</mn></msup></mfrac><mo>+</mo><mi>…</mi></mrow></math></p>
<p>The leading 1 of this series gets canceled by the 1 that’s subtracted from <em>γ</em>, giving</p>
<p><!-- $$ \frac{1}{2}\,m v^2 + \frac{3}{8}\,m \frac{v^4}{c^2} + \frac{5}{16}\,m \frac{v^6}{c^4} + \frac{35}{128}\,m \frac{v^8}{c^6} + \ldots $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mfrac><mn>1</mn><mn>2</mn></mfrac><mspace width="0.167em"></mspace><mi>m</mi><msup><mi>v</mi><mn>2</mn></msup><mo>+</mo><mfrac><mn>3</mn><mn>8</mn></mfrac><mspace width="0.167em"></mspace><mi>m</mi><mfrac><msup><mi>v</mi><mn>4</mn></msup><msup><mi>c</mi><mn>2</mn></msup></mfrac><mo>+</mo><mfrac><mn>5</mn><mn>16</mn></mfrac><mspace width="0.167em"></mspace><mi>m</mi><mfrac><msup><mi>v</mi><mn>6</mn></msup><msup><mi>c</mi><mn>4</mn></msup></mfrac><mo>+</mo><mfrac><mn>35</mn><mn>128</mn></mfrac><mspace width="0.167em"></mspace><mi>m</mi><mfrac><msup><mi>v</mi><mn>8</mn></msup><msup><mi>c</mi><mn>6</mn></msup></mfrac><mo>+</mo><mi>…</mi></mrow></math></p>
<p>for the kinetic energy. The leading term is the one we get from classical mechanics and the others are essentially zero unless you’re in a particle accelerator (which is discussed later in the video).</p>
<p>I don’t like this form for the equation. It’s cleaner if you factor out all the terms that give the expression the units of energy and then have a nondimensional expression afterward. Like this:</p>
<p><!-- $$ \frac{1}{2}\,m v^2 \, \left( 1 + \frac{3}{4}\,\frac{v^2}{c^2} + \frac{5}{8}\,\frac{v^4}{c^4}+ \frac{35}{64}\,\frac{v^6}{c^6} + \ldots \right) $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mfrac><mn>1</mn><mn>2</mn></mfrac><mspace width="0.167em"></mspace><mi>m</mi><msup><mi>v</mi><mn>2</mn></msup><mspace width="0.167em"></mspace><mrow><mo form="prefix" stretchy="true">(</mo><mn>1</mn><mo>+</mo><mfrac><mn>3</mn><mn>4</mn></mfrac><mspace width="0.167em"></mspace><mfrac><msup><mi>v</mi><mn>2</mn></msup><msup><mi>c</mi><mn>2</mn></msup></mfrac><mo>+</mo><mfrac><mn>5</mn><mn>8</mn></mfrac><mspace width="0.167em"></mspace><mfrac><msup><mi>v</mi><mn>4</mn></msup><msup><mi>c</mi><mn>4</mn></msup></mfrac><mo>+</mo><mfrac><mn>35</mn><mn>64</mn></mfrac><mspace width="0.167em"></mspace><mfrac><msup><mi>v</mi><mn>6</mn></msup><msup><mi>c</mi><mn>6</mn></msup></mfrac><mo>+</mo><mi>…</mi><mo form="postfix" stretchy="true">)</mo></mrow></mrow></math></p>
<p>Isn’t this nicer? The expression in the parentheses is a function of the ratio of the velocity of the particle to the speed of light. If we call that</p>
<p><!-- $$ \phi = \frac{v}{c} $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>ϕ</mi><mo>=</mo><mfrac><mi>v</mi><mi>c</mi></mfrac></mrow></math></p>
<p>then the kinetic energy is</p>
<p><!-- $$ \frac{1}{2}\,m v^2 \, \left( 1 + \frac{3}{4}\,\phi^2 + \frac{5}{8}\,\phi^4 + \frac{35}{64}\,\phi^6 + \ldots \right) $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mfrac><mn>1</mn><mn>2</mn></mfrac><mspace width="0.167em"></mspace><mi>m</mi><msup><mi>v</mi><mn>2</mn></msup><mspace width="0.167em"></mspace><mrow><mo form="prefix" stretchy="true">(</mo><mn>1</mn><mo>+</mo><mfrac><mn>3</mn><mn>4</mn></mfrac><mspace width="0.167em"></mspace><msup><mi>ϕ</mi><mn>2</mn></msup><mo>+</mo><mfrac><mn>5</mn><mn>8</mn></mfrac><mspace width="0.167em"></mspace><msup><mi>ϕ</mi><mn>4</mn></msup><mo>+</mo><mfrac><mn>35</mn><mn>64</mn></mfrac><mspace width="0.167em"></mspace><msup><mi>ϕ</mi><mn>6</mn></msup><mo>+</mo><mi>…</mi><mo form="postfix" stretchy="true">)</mo></mrow></mrow></math></p>
<p>and it’s much easier to see why the terms after the 1 are vanishingly small for most situations—all the situations for which classical mechanics applies.</p>
<p>One last thing. Matt sort of explained how to calculate the terms of the expansion of <em>γ</em> in <a href="https://www.youtube.com/watch?v=VyYKwJanACI">the companion video</a>, but there was a lot of handwaving and he bailed out after the second term. It doesn’t take too much effort to show that the series expansion of <em>γ</em> can be written like this:</p>
<p><!-- $$ \gamma = \sum_{n = 0, 2, 4, \ldots}^\infty \; \frac{n!}{2^n \; \left[\left(n/2\right)! \right]^2} \; \phi^n $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>γ</mi><mo>=</mo><munderover><mo>∑</mo><mrow><mi>n</mi><mo>=</mo><mn>0</mn><mo>,</mo><mn>2</mn><mo>,</mo><mn>4</mn><mo>,</mo><mi>…</mi></mrow><mo accent="false">∞</mo></munderover><mspace width="0.278em"></mspace><mfrac><mrow><mi>n</mi><mi>!</mi></mrow><mrow><msup><mn>2</mn><mi>n</mi></msup><mspace width="0.278em"></mspace><msup><mrow><mo form="prefix" stretchy="true">[</mo><mrow><mo form="prefix" stretchy="true">(</mo><mi>n</mi><mi>/</mi><mn>2</mn><mo form="postfix" stretchy="true">)</mo></mrow><mi>!</mi><mo form="postfix" stretchy="true">]</mo></mrow><mn>2</mn></msup></mrow></mfrac><mspace width="0.278em"></mspace><msup><mi>ϕ</mi><mi>n</mi></msup></mrow></math></p>
<p>That means the kinetic energy, <!-- $m c^2 (\gamma - 1)$ --><math xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mi>m</mi><msup><mi>c</mi><mn>2</mn></msup><mo form="prefix" stretchy="false">(</mo><mi>γ</mi><mo>−</mo><mn>1</mn><mo form="postfix" stretchy="false">)</mo></mrow></math>, is</p>
<p><!-- $$ \frac{1}{2} \, m v^2 \sum_{n = 2, 4, 6, \ldots}^\infty \; \frac{n!}{2^{n-1} \; \left[\left(n/2\right)! \right]^2} \; \phi^{n-2} $$ -->
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML"><mrow><mfrac><mn>1</mn><mn>2</mn></mfrac><mspace width="0.167em"></mspace><mi>m</mi><msup><mi>v</mi><mn>2</mn></msup><munderover><mo>∑</mo><mrow><mi>n</mi><mo>=</mo><mn>2</mn><mo>,</mo><mn>4</mn><mo>,</mo><mn>6</mn><mo>,</mo><mi>…</mi></mrow><mo accent="false">∞</mo></munderover><mspace width="0.278em"></mspace><mfrac><mrow><mi>n</mi><mi>!</mi></mrow><mrow><msup><mn>2</mn><mrow><mi>n</mi><mo>−</mo><mn>1</mn></mrow></msup><mspace width="0.278em"></mspace><msup><mrow><mo form="prefix" stretchy="true">[</mo><mrow><mo form="prefix" stretchy="true">(</mo><mi>n</mi><mi>/</mi><mn>2</mn><mo form="postfix" stretchy="true">)</mo></mrow><mi>!</mi><mo form="postfix" stretchy="true">]</mo></mrow><mn>2</mn></msup></mrow></mfrac><mspace width="0.333em"></mspace><msup><mi>ϕ</mi><mrow><mi>n</mi><mo>−</mo><mn>2</mn></mrow></msup></mrow></math></p>
<p>You can confirm that these terms match the equation we saw earlier by plugging values of <em>n</em> from 2 through 8 into this expression. And now we can extend the series as far as we like, even though the additional terms add essentially nothing.</p>
  ]]>
</content:encoded>
</item>

<item>
<title>Apple drawings</title>
<link>https://leancrew.com/all-this/2026/09/apple-drawings/</link>
<pubDate>Tue, 15 Sep 2026 01:09:55 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/09/apple-drawings/</guid>
<description>
  <![CDATA[Everyone’s favorite Apple archivist, Stephen Hackett, wrote <a href="https://512pixels.net/2026/09/apple-dimensional-drawings/">a short post</a> last week in which he linked to Apple’s page with <a href="https://developer.apple.com/accessories/dimensional-drawings/">dimensional drawings</a> of its products. He included this drawing of the iPhone 17 Pro:]]>
</description>
<content:encoded>
  <![CDATA[<p>Everyone’s favorite Apple archivist, Stephen Hackett, wrote <a href="https://512pixels.net/2026/09/apple-dimensional-drawings/">a short post</a> last week in which he linked to Apple’s page with <a href="https://developer.apple.com/accessories/dimensional-drawings/">dimensional drawings</a> of its products. He included this drawing of the iPhone 17 Pro:</p>
<p><a href="https://leancrew.com/all-this/images2026/20260914-Apple%20iPhone%2017%20Pro%20300%20dpi.png"><img alt="Apple iPhone 17 Pro drawing" class="ss" src="https://leancrew.com/all-this/images2026/20260914-Apple%20iPhone%2017%20Pro%20drawing.png" title="Apple iPhone 17 Pro drawing" width="100%"/></a></p>
<p>You can click on it to see a 300 dpi version. The Apple page linked above will give you infinite-resolution PDFs.</p>
<p>When I zoomed in on Stephen’s page, I was initially confused, but then I realized what seemed odd to me. You see, I’ve spent an awful lot of the past 40 years looking at engineering drawings, and I can’t think of any that looked like this. That’s because the drawings I’m used to are for making the depicted product, but the drawings Apple’s offering up here aren’t for that.<sup id="fnref:secret"><a href="#fn:secret" rel="footnote">1</a></sup> These are for <em>others</em>—not Apple and not its suppliers—to use for making cases and other accessories. I figured it was worth a quick post on what makes a drawing like the one above so different from what I’m used to.</p>
<p>First off, there are so many dimensions. Drawings of full products are typically called <em>assembly drawings</em>, and they have almost no dimensions. What they do is show how the various component pieces (subassemblies) are put together. The subassemblies have their own drawings that show how they are put together from their components, which are themselves typically subassemblies. Eventually, you get down to the individual part drawings, which depict a single piece of metal or glass or plastic. At each level in this hierarchy, the drawings tend to include only the dimensions necessary to build the object depicted. You don’t include the length and diameter of a screw on an assembly drawing; you simply call out its part number (which is also its drawing number).</p>
<p>Speaking of drawing numbers, if you look in the title block you’ll see that there is no drawing number. Design drawings always have these. They also have the names or initials of the draftsmen and checkers and, usually, a list of revisions. These public drawings don’t reveal any of that internal information.</p>
<p>There are names for many of the items and dimensions: product length, display active area, volume button, rear sensor, etc. These are not common in design drawings because they tend to be of no value to those doing the manufacturing. You don’t need to know what this hole is for; just make it this big and put it here.</p>
<p>Which leads us to tolerances, which are absent. Dimensions and positions are given to the nearest hundredth of a millimeter and there is no give or take. To the outside world, Apple’s dimensions are absolute and invariable. Inside Apple and its suppliers, people know better.</p>
<p>Also absent is any specification of material, other than a few generic mentions of glass. While assembly drawings don’t usually include materials, parts drawings always refer to material specs, usually included in another company document.</p>
<p>The last thing I’ll mention is the Notes section in the upper left corner. It has instructions clearly meant for people outside of Apple and making accessories. If I’d zoomed in on this section first, I probably wouldn’t have been even momentarily confused.</p>
<div class="footnotes">
<hr/>
<ol>
<li id="fn:secret">
<p>Obviously. Apple isn’t going to post its design drawings; they’re among its most tightly controlled proprietary documents. Even for a company that considers everything a secret, the design drawings are <em>really</em> secret. <a href="#fnref:secret" rev="footnote">↩</a></p>
</li>
</ol>
</div>]]>
</content:encoded>
</item>

<item>
<title>New Apple Watches have ears</title>
<link>https://leancrew.com/all-this/2026/09/new-apple-watches-have-ears/</link>
<pubDate>Thu, 10 Sep 2026 03:42:27 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/09/new-apple-watches-have-ears/</guid>
<description>
  <![CDATA[Starting about 45 minutes into <a href="https://www.apple.com/apple-events/">today’s Apple Event</a>, during the segment on the new Apple Watch, Ron Huang introduced a couple of features of the Series 12 and Ultra 4 that struck me as both disturbing and possibly illegal. They certainly seem at odds with Apple’s commitment to privacy.]]>
</description>
<content:encoded>
  <![CDATA[<p>Starting about 45 minutes into <a href="https://www.apple.com/apple-events/">today’s Apple Event</a>, during the segment on the new Apple Watch, Ron Huang introduced a couple of features of the Series 12 and Ultra 4 that struck me as both disturbing and possibly illegal. They certainly seem at odds with Apple’s commitment to privacy.</p>
<p>First came Live Rewind, a feature that lets you “go back in time” to pick up on something you missed that was said in the past 15 seconds. Here’s how Apple describes it <a href="https://www.apple.com/apple-watch-series-12/">on the website</a>:</p>
<blockquote>
<p>Live Rewind catches what you missed. Did someone say something you couldn’t hear in a loud restaurant or share cooking instructions you didn’t quite catch? Just double-click the Digital Crown, and Live Rewind transcribes what was said in the last 15 seconds. You can ask Siri about the content of the transcript or save it to the new Siri app to revisit later.</p>
</blockquote>
<p>In the Event, this was illustrated by a waitress going through the restaurant’s specials too quickly for the diners to remember all of them. One of the diners double-clicks the crown on his watch and reads what she said.</p>
<p><img alt="Screenshot of Live Rewind demo" class="ss" src="https://leancrew.com/all-this/images2026/20260909-Screenshot%20of%20Live%20Rewind%20demo.jpg" title="Screenshot of Live Rewind demo" width="100%"/></p>
<p>It seems like your watch will be listening continuously, keeping the last 15 seconds in a buffer, ready to be transcribed when you ask. That’s fine for you, because you know it’s happening, but what about those around you? Did they agree to this? I doubt wait staff will be upset with their spiel being transcribed, but the idea behind Live Rewind seems to be that <em>everyone and everything</em> near your watch is being recorded for 15 seconds. Surely, some people wouldn’t consent to that if they were asked. Significantly, though, they’re not being asked. I’m sure Apple will argue that there’s no privacy issue because the recordings aren’t being saved. But if there’s a transcription, isn’t that a type of saving?</p>
<p>And then there’s the question of legality. A <a href="https://en.wikipedia.org/wiki/Telephone_call_recording_laws">great variety of law</a>, both in the United States and across the world, concerns the recording of electronic and in-person conversations. I’m sure Apple has considered this, but I have to think some jurisdictions will frown on Live Rewind. If Apple has to defend it, that’s fine. What’s one more squadron of lawyers to Apple? But if an Apple Watch <em>owner</em> has to defend a transcription made innocently—made with Apple’s encouragement, in fact—that seems like an unfair burden. Maybe Apple should indemnify all future Watch buyers from privacy-related legal action.</p>
<p>Right after Live Rewind, we get the introduction of Siri Recap. From the Series 12 web page:</p>
<blockquote>
<p>Siri Recap can summarize your chats to refresh your memory. Siri Recap transforms your conversations into high-level notes, so you can stay focused and present. Each Siri Recap includes a title, a summary, and key points that you can review in the Siri app on your Apple Watch or iPhone. You can turn Siri Recap on or off at any time from Control Center or choose where and when it takes notes, such as only at work or never at night.</p>
</blockquote>
<p>Siri Recap does a summary, not a transcription, and it’s not automatic the way Live Rewind is, although it does sound as if you can set it up to always record when you’re at a certain place, so that’s kind of automatic. Apple touted Siri Recap’s privacy with three bullet points:</p>
<p><img alt="Screenshot of Siri Recap protections" class="ss" src="https://leancrew.com/all-this/images2026/20260909-Screenshot%20of%20Siri%20Recap%20protections.jpg" title="Screenshot of Siri Recap protections" width="100%"/></p>
<p>I think Apple is slicing the baloney pretty thin when it says audio isn’t recorded. Surely there’s some recording because summaries require context; they can’t be made on the fly. Maybe a transcription is done in near-real time and the summary is drawn from that. If so, the transcription is a type of recording, albeit one that isn’t stored after the summary is made. As for not identifying the speakers, I don’t see how that matters. Old-fashioned audio recordings don’t identify speakers, either, but they’re still intrusions on the speakers’ privacy if they didn’t give consent.</p>
<p>Apple has always focused on the privacy of its customers: <em>your</em> information is encrypted, <em>your</em> data stays on <em>your</em> device. It’s following that same pattern here, but the privacy concerns of Live Rewind and Siri Recap aren’t limited to the owners of Apple Watches. Apple now needs think about the privacy of non-customers and be able to explain how that’s being protected, too.</p>
<p>And while an argument can be made that textual transcriptions and recaps aren’t <em>as</em> invasive as saved audio recordings, that doesn’t mean they aren’t invasive at all. That little lock animation Apple’s been using to describe its commitment to privacy has been very effective, but its binary nature doesn’t fit when the products start putting privacy on a sliding scale.</p>
<div class="update">
<p><strong>Update 10 Sep 2026 8:14 PM</strong><br/>
On Mastodon, Bill Lloyd <a href="https://mastodon.social/@wklj/117248345208895614">sent me a link</a> to <a href="https://www.apple.com/privacy/docs/Audio_Intelligence_Privacy_Overview_Sep_2026.pdf">this longer document</a> from Apple with further explanation of Live Rewind and Siri Recap. The hoops Apple jumped through to avoid certain words is pretty funny. For example, in the discussion of Live Rewind, we learn that</p>
<blockquote>
<p>Audio from the microphone flows into the audio buffer inside the Secure Exclave on the S11 chip.</p>
</blockquote>
<p>You think your watch records the audio around you? No no no. The audio <em>flows</em> into the Secure Enclave, and flowing is definitely not recording because it’s a different word. Apple’s documentation poets liked <em>flow</em> so much, they also used it in the description of Siri Recap:</p>
<blockquote>
<p>If a conversation is detected, audio flows into a protected buffer inside the Secure Exclave on the S11 chip.</p>
</blockquote>
<p>Note also that the audio flows into a protected buffer <em>inside</em> the Secure Enclave, so that must make it doubly private.</p>
<p>Thanks for the entertaining link, Bill!</p>
</div>]]>
</content:encoded>
</item>

<item>
<title>Web and native apps</title>
<link>https://leancrew.com/all-this/2026/08/web-and-native-apps/</link>
<pubDate>Mon, 17 Aug 2026 19:19:12 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/08/web-and-native-apps/</guid>
<description>
  <![CDATA[This is sacrilegious among some parts of Apple fandom, but I like using web apps. Oh, sure, I can imagine a world in which there’s a carefully crafted Mac app that has all the functions of a website I use but has all the Mac-like interface features I love and isn’t hobbled by a web UI. But I don’t live in that world and don’t expect I ever will.]]>
</description>
<content:encoded>
  <![CDATA[<p>This is sacrilegious among some parts of Apple fandom, but I like using web apps. Oh, sure, I can imagine a world in which there’s a carefully crafted Mac app that has all the functions of a website I use but has all the Mac-like interface features I love and isn’t hobbled by a web UI. But I don’t live in that world and don’t expect I ever will.</p>
<p>For example, will the <a href="https://www.python.org/psf-landing/">Python Software Foundation</a> ever make a Mac app that includes all the documentation I regularly use? And even if they do, is it likely they’ll put in the effort to make better than just visiting the <a href="https://docs.python.org/3.13/index.html">Python Docs web page</a>? Would that be in keeping with their mission? So instead of waiting around for something that will never happen, I used <a href="https://www.bzgapps.com/unite">Unite Pro</a> to make a Python Docs site-specific browser.</p>
<p><img alt="SSB for Python documentation" class="ss" src="https://leancrew.com/all-this/images2026/20260817-SSB%20for%20Python%20documentation.png" title="SSB for Python documentation" width="100%"/></p>
<p>Launching it takes me straight to the <a href="https://docs.python.org/3.13/library/index.html">standard library home page</a>, from which I can quickly jump to the docs of whichever module I’m invoking in my current script. I have similar SSBs for the <a href="https://matplotlib.org/">Matplotlib</a> and <a href="https://pandas.pydata.org/">Pandas</a> documentation.</p>
<p>You might argue that these SSBs aren’t really web apps, that they’re basically a set of static web pages connected by links. That’s nearly true, but while they don’t have the kind of interaction that you see in, say, Google Sheets, they do have search features that make them more interactive than just a list of links. Regardless of how interactive they are, they serve my needs.</p>
<p>You might also suggest I use <a href="https://kapeli.com/dash">Dash</a>, an actual app, instead of an SSB for browsing documentation. It incorporates many many sets of documentation and uses local copies so you don’t have to be online to use it. These are all good points and are why I tried Dash several years ago. It just didn’t fit me, probably because searches in Dash returned too many results across too many libraries. There are probably ways to get it to serve up more focused results, but I didn’t want to become a Dash expert (I’m not <a href="https://brettterpstra.com/2022/02/18/keybindings-cheat-sheet-for-dash/">Brett Terpstra</a>). I just wanted to see documentation relevant to what I was working on at the time. Initially that meant going to the appropriate website; now it means launching the appropriate SSB.</p>
<p>But I’m not a web app absolutist. I recently ended a web app experiment that’s brought me back to a native app. This was with Mastodon and <a href="https://apps.apple.com/us/app/mona-7-for-mastodon/id6755672518">Mona</a>. When <a href="https://mastodon.social/@MonaApp">Mona 7 came out</a> at the end of last year, I decided to hold off on buying its Ultra in-app purchase. After all, I thought, Mastodon exists on the web. Does it really need an app?</p>
<p>So I made an SSB with Unite Pro for the Mac and used the free versions of Mona on iOS and iPadOS. Eventually, I felt guilty about using an app without paying the developer, so I did the Add to Home Screen thing on my iPhone and iPad a couple of weeks ago to even things out and use the web on all three platforms. It was terrible.</p>
<p>The biggest problem was the lack of timeline syncing and updating. In theory, this problem should have started when I was using Mona on two devices and an SSB on the third. In practice, it wasn’t so bad because I do almost all of my Mastodon reading and posting on my phone. It was when I started using “web Mastodon” on my phone that I noticed how poorly Mastodon updates the timeline.</p>
<p>On the web, Mastodon puts a link at the top of the page saying there are new posts to load into your timeline. When you tap the link, the new posts load, but your position in the timeline can jump around wildly, and then you have to scroll to get back to where you were. A small annoyance, perhaps, but one that happens again and again every day.</p>
<p>And there’s no syncing between platforms with web Mastodon. This is, as I said, less of a problem for me because I’m mainly reading and posting on my phone, but the scrolling necessary on one device to get to where I had been on another just reminded me of all the scrolling described in the previous paragraph, and it seemed worse.</p>
<p>So I subscribed to Mona Ultra and switched to it on all three platforms. The Mastodon web Home Screen icons are gone from my iPhone and iPad, and the SSB is gone from my Mac. Ultra’s extended settings are nice, and I’m happy to pay for them, but the main advantage is the smoother experience I get with a real app written to use the features of the platform(s) it runs on. In this case, one of those features is iCloud syncing.</p>
<p>(By the way, if you feel tempted to tell me about another Mastodon app, like Ivory, don’t. I know about them—I’m happy with Mona.)</p>
<p>In summary, I don’t have any magic tricks for choosing between web apps and native apps. I just know that it’s worth a little time to try out both and see what fits. Every app and every person is different, and you have to decide from direct experience.</p>]]>
</content:encoded>
</item>

<item>
<title>Apple Music weirdness</title>
<link>https://leancrew.com/all-this/2026/08/apple-music-weirdness/</link>
<pubDate>Sat, 15 Aug 2026 22:18:03 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/08/apple-music-weirdness/</guid>
<description>
  <![CDATA[I was out on a walk yesterday, listening to the ’70s Hits Radio Station on Apple Music, when “Don’t Leave Me This Way” came on. I pulled my phone out of my pocket to look at a text while the song was playing and was surprised at what the Music app told me about the song.]]>
</description>
<content:encoded>
  <![CDATA[<p>I was out on a walk yesterday, listening to the ’70s Hits Radio Station on Apple Music, when “Don’t Leave Me This Way” came on. I pulled my phone out of my pocket to look at a text while the song was playing and was surprised at what the Music app told me about the song.</p>
<p><img alt="Album art and artist error" class="ss" src="https://leancrew.com/all-this/images2026/20260815-Album%20art%20and%20artist%20error.jpg" title="Album art and artist error" width="60%"/></p>
<p>I’m pretty sure George Benson never covered “Don’t Leave Me This Way,” but even if he did, that’s not the version I was listening to. It was the version everybody knows by Thelma Houston, with her incredible voice and that fun bass part during the chorus. I’ve been listening to it for 50 years, and it’s unmistakable.</p>
<p>So how did Apple Music get the artist wrong? Is the info provided with the album wrong and Apple is just repeating someone else’s mistake? When I got home, I checked this <a href="https://music.apple.com/us/album/heartbreak-hits/1445668885"><em>Heartbreak Hits</em></a> compilation album on other services. <a href="https://www.amazon.com/Heartbreak-Hits-Explicit-Various-artists/dp/B079KL1SR5?dplnkId=42b30c15-e915-441b-bb64-c50d7aa6b506&amp;nodl=1">Amazon Music</a>, <a href="https://open.spotify.com/album/6S3vni7DhQ6XliFnarFgFv">Spotify</a>, and <a href="https://tidal.com/album/84236359/track/84236373">Tidal</a> all had the album, and they all had the artist listed correctly as Thelma Houston. Only Apple got it wrong.</p>
<p>I don’t think I’ve ever seen mistaken artist attribution like this before, but Apple’s weird choice of album to pluck the song from is very familiar. I listen to a lot of Apple’s Radio Stations and its Essentials and Deep Cuts playlists, and it’s common for a song to be assigned to a compilation album instead of the original source. Even when Apple has the original album in its library. As you might have guessed, I find this annoying.</p>
<p>It’s not exactly <em>wrong</em> to show a song as being on a compilation album; most hit songs have been put on “best of” and other sorts of compilations. But it’s bad scholarship. Yes, if the song was released as a standalone single—as many Beatle songs were—the only album you can assign it to is a compilation, but that’s fairly rare.</p>
<p>If, for example, you look at the <a href="https://music.apple.com/us/playlist/prince-essentials/pl.3c4819913dad4dbe80dc5cddca6431a7">Prince Essentials playlist</a>—which you should; it’s fantastic—you’ll see that both “Gett Off” and “1999” are shown as being from his <em>The Hits/The B-Sides</em> album. That’s certainly a fun album to listen to, but neither of those songs “belong” to that album. “Gett Off” is from <em>Diamonds and Pearls</em>, and if I have to tell you where “1999” is from, I don’t know why you’ve read this far.</p>
<p>Apple likes to say that music is part of its DNA. I suggest they schedule some genetic counseling.</p>]]>
</content:encoded>
</item>

<item>
<title>A planet position widget</title>
<link>https://leancrew.com/all-this/2026/08/a-planet-position-widget/</link>
<pubDate>Wed, 12 Aug 2026 17:16:30 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/08/a-planet-position-widget/</guid>
<description>
  <![CDATA[After I learned how to <a href="https://leancrew.com/all-this/2026/08/a-two-month-calendar-on-my-desktop/">make a Mac widget with TerminalWidget</a> and how to <a href="https://leancrew.com/all-this/2026/08/ill-follow-the-sun/">determine the locations of celestial objects with Astropy</a>, the natural thing for me to do was combine the two into a widget that tracks the planets.]]>
</description>
<content:encoded>
  <![CDATA[<p>After I learned how to <a href="https://leancrew.com/all-this/2026/08/a-two-month-calendar-on-my-desktop/">make a Mac widget with TerminalWidget</a> and how to <a href="https://leancrew.com/all-this/2026/08/ill-follow-the-sun/">determine the locations of celestial objects with Astropy</a>, the natural thing for me to do was combine the two into a widget that tracks the planets.</p>
<p><img alt="Planet position widget" class="ss" src="https://leancrew.com/all-this/images2026/20260811-Planet%20position%20widget.png" title="Planet position widget" width="80%"/></p>
<p>I’m using the ancient definition of planet, which includes the Sun and Moon but not anything past Saturn. The numbers are the azimuth and altitude, in that order, and are given to the nearest degree. The idea is to tell me what may be visible and where it is. This particular screenshot was taken just after 10:00 last night; there was no reason to go outside because everything was below the horizon.</p>
<p>I was torn on whether to include the Sun. Few of us need help finding the Sun in the sky, and you can’t see anything other than the Moon when the Sun is up. But I decided to include it anyway, partly for completeness, and partly because the visibility of some bodies depends on their separation from the Sun.</p>
<p>Let’s start with the code that generates the widget’s text. It’s a Python script called <code>planets</code>:</p>
<pre><code>python:
 1:  import astropy.units as u
 2:  from astropy.time import Time
 3:  from astropy.coordinates import get_body, AltAz, EarthLocation
 4:  from subprocess import run
 5:  
 6:  def direction(az):
 7:    'Return a string indication of the azimuth (given in degrees).'
 8:  
 9:    dirs = 'N NNE NE ENE E ESE SE SSE S SSW SW WSW W WNW NW NNW'.split()
10:    i = int(((az + 11.25) % 360) / 22.5)
11:    return dirs[i]
12:  
13:  # Current time in UTC.
14:  ut = Time.now()
15:  
16:  # Observation location.
17:  home = EarthLocation(lat=41.81433*u.deg, lon=-88.07093*u.deg, height=208*u.m)
18:  
19:  # Bodies of interest.
20:  planets = 'Moon Sun Mercury Venus Mars Jupiter Saturn'.split()
21:  
22:  # Current positions of all the bodies.
23:  pos = {}
24:  const = {}
25:  for p in planets:
26:      pos[p] = get_body(p, ut).transform_to(AltAz(obstime=ut, location=home))
27:      const[p] = pos[p].get_constellation()
28:  
29:  # Assemble the results.
30:  output = []
31:  for p in planets:
32:      output.append(f'{p:&gt;8s}: {pos[p].az.value:3.0f} \
33:  {direction(pos[p].az.value):3s} {pos[p].alt.value:3.0f} {const[p]}')
34:  
35:  # Pipe the results through TerminalWidget.
36:  tw = '/Applications/TerminalWidget.app/Contents/MacOS/TerminalWidget\
37:   --target planets --font Menlo --bg eeeeee --fg 000000 --text -'.split()
38:  run(tw, input='\n'.join(output).encode())
39:  
40:  # print('\n'.join(output))
</code></pre>
<p>There’s no shebang line because of how it gets called by <code>launchd</code>, which we’ll get to later.</p>
<p>After <code>planets</code> imports the necessary modules, Lines 6–11 define the <code>direction</code> function, which takes the azimuth and returns a string with the corresponding point of the compass. I have this because 223°, which is how Astropy reports the azimuth, doesn’t immediately say “southwest” to me. The function assumes a 16-point compass, like this one:</p>
<p><img alt="Compass rose from Wikipedia" class="ss" src="https://leancrew.com/all-this/images2026/20260812-Compass%20rose%20from%20Wikipedia.png" title="Compass rose from Wikipedia" width="60%"/></p>
<p class="caption">Image from <a href="https://en.wikipedia.org/wiki/Points_of_the_compass">Wikipedia</a>.</p>
<p>The points are separated by 22.5°, which is why there’s a division by 22.5 in Line 10. The other parts of Line 10 adjust for the fact that North starts at 348.75° (-11.25°), the azimuth resets at 360°, and the index of a list must be an integer. Astropy may already have a function that does what <code>direction</code> does, but I thought it would be easier (and more fun) to write the function myself than to search through the documentation.</p>
<p>Lines 14 and 17 define the time and place of observation. <code>planets</code> will be run every half hour to update the widget, so what’s being displayed is never more than 30 minutes out of date. The <code>home</code> location you see above is actually the Morton Arboretum; my version of the script uses the latitude and longitude of my house.</p>
<p>Line 20 defines the <code>planets</code> list, and Lines 23–27 create a pair of dictionaries, <code>pos</code> and <code>const</code>, which contain the <a href="https://docs.astropy.org/en/stable/api/astropy.coordinates.AltAz.html"><code>AltAz</code></a> position and constellation of each planet. The <a href="https://docs.astropy.org/en/stable/api/astropy.coordinates.get_body.html"><code>get_body</code> function</a> (Line 26) gets the position, and the <a href="https://docs.astropy.org/en/stable/api/astropy.coordinates.get_constellation.html"><code>get_constellation</code> function</a> (Line 27) uses that position to figure out the constellation the body is in.</p>
<p>Lines 30–33 create the list of <code>output</code> lines, and Lines 36–38 use the <a href="https://docs.python.org/3.13/library/subprocess.html#subprocess.run"><code>run</code> function</a> of the <code>subprocess</code> module to send the output lines to TerminalWidget. The <code>tw</code> list contains both the full path to the <code>TerminalWidget</code> executable and all the options passed to it. The <code>input</code> parameter to <code>run</code> is the previously defined <code>output</code>, converted to a single string separated by linefeeds and encoded as bytes.</p>
<p>Line 40 is basically a debugging line that I’ve left in for future development. While writing <code>planets</code>, I had Lines 36–38 commented out and Line 40 uncommented so I could see the results immediately in the Terminal.</p>
<p><code>planets</code> is run by <code>launchd</code> every 30 minutes, on the hour and half-hour, via this launch agent, <code>com.leancrew.planets.plist</code>:</p>
<pre><code>xml:
 1:  &lt;?xml version="1.0" encoding="UTF-8"?&gt;
 2:  &lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
 3:  &lt;plist version="1.0"&gt;
 4:  &lt;dict&gt;
 5:    &lt;key&gt;Label&lt;/key&gt;
 6:    &lt;string&gt;com.leancrew.planets&lt;/string&gt;
 7:    &lt;key&gt;ProgramArguments&lt;/key&gt;
 8:    &lt;array&gt;
 9:      &lt;string&gt;/path/to/python&lt;/string&gt;
10:      &lt;string&gt;/path/to/planets&lt;/string&gt;
11:    &lt;/array&gt;
12:    &lt;key&gt;StartCalendarInterval&lt;/key&gt;
13:    &lt;array&gt;
14:      &lt;dict&gt;
15:        &lt;key&gt;Minute&lt;/key&gt;
16:        &lt;integer&gt;0&lt;/integer&gt;
17:      &lt;/dict&gt;
18:      &lt;dict&gt;
19:        &lt;key&gt;Minute&lt;/key&gt;
20:        &lt;integer&gt;30&lt;/integer&gt;
21:      &lt;/dict&gt;
22:    &lt;/array&gt;
23:  &lt;/dict&gt;
24:  &lt;/plist&gt;
</code></pre>
<p>The first item in the <code>ProgramArguments</code> array is the full path to the Python executable (this is why <code>planets</code> doesn’t need a shebang line), and the second item is the full path to the <code>planets</code> script itself. The schedule for running <code>planets</code> is in the <code>StartCalendarInterval</code> array—whenever the minute is 0 or 30, the script is run.</p>
<p>As I write this, a solar eclipse is nearly underway. Here in the Chicago area, it’s going to be a very partial eclipse—only 1% of the Sun will be blocked. Since 100% of the Sun is being blocked by clouds, I won’t be able to see any of the eclipse. But my planets widget is showing me, more or less, that it’s happening above the clouds.</p>
<p><img alt="Planets widget near the solar eclipse time" class="ss" src="https://leancrew.com/all-this/images2026/20260812-Planets%20widget%20near%20the%20solar%20eclipse%20time.png" title="Planets widget near the solar eclipse time" width="80%"/></p>
<p>Rounding the Sun and Moon’s positions to the nearest degree isn’t precise enough to determine an eclipse, but it’s a decent hint.</p>
<div class="update">
<p><strong>Update 20 Aug 2026 4:41 PM</strong><br/>
You won’t be surprised to learn that Brett took my script and <a href="https://terminalwidget.app/recipes/planets-updated">energized it</a> with some new TerminalWidget features. He’s been updating both the widget and the app over the past week, but I didn’t link to any of his work until now because it was clear he hadn’t finished yet. Now I think he’s basically done and moving on to other things.</p>
<p><img alt="Terpstra planets widget" class="ss" src="https://leancrew.com/all-this/images2026/20260820-Terpstra%20planets%20widget.jpg" title="Terpstra planets widget" width="100%"/></p>
<p>The new TW features that make his version of the Planets widget look better will clean up the look of any widget that displays a table of information. Brett’s been pushing the App Store review team pretty hard recently (just look at <a href="https://apps.apple.com/us/app/terminalwidget/id6764288419">the app’s version history</a>), but I think he’s taking a little break now.</p>
</div>]]>
</content:encoded>
</item>

<item>
<title>I’ll follow the Sun</title>
<link>https://leancrew.com/all-this/2026/08/ill-follow-the-sun/</link>
<pubDate>Tue, 11 Aug 2026 03:49:46 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/08/ill-follow-the-sun/</guid>
<description>
  <![CDATA[Through an odd coincidence, I started writing this post when the Sun was in Cancer, but by the time I’m done and get it published, the Sun will be in Leo. Don’t worry, this isn’t an astrology post—I don’t want to <a href="https://daringfireball.net/2026/08/retraction_app_store_rejection_of_the_week">anger John Gruber</a>—but it is a further coincidence that I’ve been puttering around with observational astronomy calculations at a time when there’s been <a href="https://mjtsai.com/blog/2026/08/07/dark-hours-rejected-from-the-app-store/">some controversy in the Apple world</a> about astronomy vs. astrology.]]>
</description>
<content:encoded>
  <![CDATA[<p>Through an odd coincidence, I started writing this post when the Sun was in Cancer, but by the time I’m done and get it published, the Sun will be in Leo. Don’t worry, this isn’t an astrology post—I don’t want to <a href="https://daringfireball.net/2026/08/retraction_app_store_rejection_of_the_week">anger John Gruber</a>—but it is a further coincidence that I’ve been puttering around with observational astronomy calculations at a time when there’s been <a href="https://mjtsai.com/blog/2026/08/07/dark-hours-rejected-from-the-app-store/">some controversy in the Apple world</a> about astronomy vs. astrology.</p>
<p>I’m often out walking at night, and a few months ago—when Mercury, Venus, and Jupiter were near each other and close to Castor and Pollux, the <a href="https://en.wikipedia.org/wiki/Gemini_(constellation)">Gemini twins</a>—I started thinking it would be nice to be able to write little scripts that would tell me where the planets were and when they’d be easily visible. One of the fun features would be including information on which constellation the planets were in.</p>
<p>This led me on a fun journey of discovery, the sort of thing AI companies want us to forget how to do. I learned that <a href="https://www.iau.org/IAU/IAU/Astronomy-FAQs/Constellations.aspx">official boundaries</a> for the Western constellations were defined only about a century ago and that although they were defined in a very simple way, corresponding to lines of <a href="https://skyandtelescope.org/astronomy-resources/right-ascension-declination-celestial-coordinates/">right ascension and declination</a>, <a href="https://pwg.gsfc.nasa.gov/stargaze/Sprecess.htm">precession of the equinoxes</a> has since made those boundaries more complicated. They still look pretty well aligned, but they aren’t. Here’s the <a href="https://iauarchive.eso.org/static/public/constellations/gif/ARI.gif">IAU image of Aries</a>:</p>
<p><img alt="IAU Aries boundaries" class="ss" src="https://leancrew.com/all-this/images2026/20260810-IAU%20Aries%20boundaries.gif" title="IAU Aries boundaries" width="100%"/></p>
<p>If you look at <a href="https://iauarchive.eso.org/static/public/constellations/txt/ari.txt">the right ascension and declination coordinates</a> of the points on its boundary, you’ll see that successive points <em>don’t</em> have the same RA or Dec values. Close, but not the same.</p>
<pre><code>02 06 39.6594| 10.5143948|ARI 
01 46 37.3761| 10.5432396|ARI 
01 46 58.7219| 25.6263351|ARI 
02 02 03.2907| 25.6050701|ARI 
02 02 07.3479| 27.8550186|ARI 
02 32 16.8357| 27.8047638|ARI 
02 32 24.7665| 31.2213154|ARI 
02 50 30.8112| 31.1865025|ARI 
03 29 42.4003| 31.1003609|ARI 
03 29 09.7494| 19.4343338|ARI 
03 24 08.9363| 19.4461136|ARI 
03 23 47.1387| 10.3632069|ARI 
</code></pre>
<p>I also learned that there are <a href="https://coordswap.eukosmos.com/">a crapload of reference frames</a>, and you had better know which one is being used for the data you’re accessing.</p>
<p>This is one of the things that led me to abandon Mathematica and the Wolfram Language for these calculations. Despite Wolfram’s <a href="https://www.wolfram.com/solutions/industry/astronomy/">promises</a> of great solutions for astronomy, my test notebook showed that the RA and Dec for the Sun, for example, aren’t reported in the same frame as the RA and Dec of stars. This isn’t necessarily bad—it could be more natural to use one frame for one sort of object and another frame for another sort—but the documentation needs to tell you what frames are being used and how to convert between them. I never found that explanation, so I started exploring Python solutions.</p>
<p>There are two main Python modules for doing the kind of calculation I’m interested in: <a href="https://www.astropy.org/">Astropy</a> and <a href="https://rhodesmill.org/skyfield/">Skyfield</a>. Astropy is the standard Python module (or set of modules) for doing all sorts of astronomical calculations; Skyfield seems to be more focused on observational astronomy. That would suggest I should use Skyfield, but after looking through the documentation, I decided to get my feet wet with Astropy because it looked simpler. I can always switch to Skyfield if I find myself bumping up against some limitations in Astropy.</p>
<p>(I should also mention <a href="https://naif.jpl.nasa.gov/naif/toolkit.html">SPICE</a>, which is NASA’s software toolkit for working with the positions of planets and other objects in space. It has a Python wrapper for its C version, and having the imprimatur of NASA certainly made it attractive. But it doesn’t have actual Python documentation; it wants users to refer to the C documentation to figure out how the Python functions work, and I’m not interested in that.)</p>
<p>Having settled on Astropy, I wrote up a little script this afternoon to see if my brief review of the documentation was enough to do some of the calculations I was interested in. The script calculates the position of the Sun today at 2:00 PM CDT, and converts the result into a form that I can compare with the <a href="https://gml.noaa.gov/grad/solcalc/">NOAA Solar Calculator page</a>.</p>
<p><img alt="NOAA Sun position page" class="ss" src="https://leancrew.com/all-this/images2026/20260810-NOAA%20Sun%20position%20page.png" title="NOAA Sun position page" width="100%"/></p>
<p>I’ve set the observation point to the visitor center at the <a href="https://mortonarb.org/">Morton Arboretum</a>. The results of interest are the azimuth and altitude (or elevation) of the Sun at the appointed date and time. Here’s a zoomed-in view of the lower right corner:</p>
<p><img alt="NOAA Sun azimuth and altitude" class="ss" src="https://leancrew.com/all-this/images2026/20260810-NOAA%20Sun%20azimuth%20and%20altitude.png" title="NOAA Sun azimuth and altitude" width="60%"/></p>
<p>So a person at the Arboretum at 2:00 would see the Sun in the southwest, 211.59° from north, at 60.35° up from the horizon. NOAA’s altitude calculation includes an atmospheric correction, the formula for which is given on <a href="https://gml.noaa.gov/grad/solcalc/calcdetails.html">a linked page</a>.</p>
<p>Here’s my little script:</p>
<pre><code>python:
 1:  #!/usr/bin/env python3
 2:  
 3:  from astropy.coordinates import ICRS, AltAz, EarthLocation, get_sun
 4:  from astropy.time import Time
 5:  import astropy.units as u
 6:  from trigd import *
 7:  
 8:  # 2:00 PM Central Daylight Time on August 10, 2026.
 9:  utcoffset = -5*u.hour
10:  time = Time('2026-8-10 14:00:00') - utcoffset
11:  
12:  # Location of Morton Arboretum visitor center.
13:  morton = EarthLocation(lat=41.81433*u.deg, lon=-88.07093*u.deg, height=208*u.m)
14:  
15:  # Sun position in GCRS (default).
16:  sun = get_sun(time)
17:  
18:  # The constellation it's in.
19:  constellation = sun.get_constellation()
20:  
21:  # Sun position as azimuth and altitude
22:  sun_altaz = sun.transform_to(AltAz(obstime=time, location=morton))
23:  
24:  # The default transformation to AltAz makes no adjustment for refraction.
25:  # Use the NOAA refraction formula to adjust altitude for comparison
26:  # with the NOAA value.
27:  def noaa_refraction(alt):
28:    t = tand(alt)
29:    return (58.1/t - .007/t**3 + .000086/t**5)/3600
30:  
31:  # Print the results.
32:  az = sun_altaz.az.value
33:  alt = sun_altaz.alt.value
34:  alt_adj = alt + noaa_refraction(alt)
35:  print(f'      Azimuth: {az:-6.2f}°')
36:  print(f'     Altitude: {alt:-6.2f}° (without refraction)')
37:  print(f'     Altitude: {alt_adj:-6.2f}° (with NOAA refraction)')
38:  print(f'Constellation: {constellation}')
</code></pre>
<p>The script starts by importing various Astropy submodules and <a href="https://leancrew.com/all-this/2024/12/calculators-and-jupyter/">my <code>trigd</code> module</a>, which calculates trigonometric functions for degrees instead of radians. We’ll use that to match NOAA’s atmospheric correction formula.</p>
<p>Lines 9 and 10 set the date and time as an Astropy <code>Time</code> object. Line 13 sets the location to the visitor center. Line 16 <a href="https://docs.astropy.org/en/stable/api/astropy.coordinates.get_sun.html">gets the position of the Sun</a> at that time in the <a href="https://docs.astropy.org/en/latest/api/astropy.coordinates.GCRS.html">Geocentric Celestial Reference System (GCRS) frame</a>. Line 22 then <a href="https://docs.astropy.org/en/stable/api/astropy.coordinates.SkyCoord.html#astropy.coordinates.SkyCoord.transform_to">transforms the position</a> to <a href="https://docs.astropy.org/en/stable/api/astropy.coordinates.AltAz.html">azimuth and altitude</a> as viewed from the visitor center.</p>
<p>Because I didn’t include any atmospheric information in the <code>AltAz</code> specification, it assumes a vacuum and does no refraction adjustment. I did it that way so I could use NOAA’s refraction formula instead of whatever Astropy does. That formula is defined in Lines 27–29 (which uses the <code>tand</code> function to calculate the tangent of an angle given in degrees), and the adjusted altitude is calculated in Line 34.</p>
<p>Lines 35–38 print out the results, which look like this:</p>
<pre><code>      Azimuth: 211.59°
     Altitude:  60.34° (without refraction)
     Altitude:  60.35° (with NOAA refraction)
Constellation: Cancer
</code></pre>
<p>We’ll return to the constellation part later. As you can see, the azimuth and adjusted altitude match the NOAA values to two decimal places, which made me feel pretty good.</p>
<p>As for the constellation the Sun was in at the specified time, that’s calculated by the aptly named <code>get_constellation</code> function on Line 19. The great thing about <code>get_constellation</code> is that it understands the reference frame of the object it’s called from and does whatever transformations are needed (in this case to <a href="https://docs.astropy.org/en/stable/api/astropy.coordinates.ICRS.html">ICRS</a>) to figure out which constellation that object is in. The answer was printed out by Line 38.</p>
<p>To check on the constellation answer, I went to <a href="https://heavens-above.com">Heavens Above</a>, a site I’ve been using since the late 90s. I entered the Arboretum location and 2:00 PM today as the time, and HA told me the Sun was in Cancer, just like Astropy. It also showed me this sky chart:</p>
<p><img alt="Heavens Above sky chart" class="ss" src="https://leancrew.com/all-this/images2026/20260810-Heavens%20Above%20sky%20chart.png" title="Heavens Above sky chart" width="80%"/></p>
<p>The Sun was clearly near the end of its time in Cancer and would soon be in Leo. So I began a trial-and-error search at Heavens Above and with Astropy in an interactive Python session to find out when the Sun would move from Cancer to Leo.</p>
<p>Heavens Above told me that the Sun would enter Leo at 8:00:15 PM today. Astropy said it would happen at 8:07:23 PM. I suspect Heavens Above is giving the better answer, as I’ve been using Astropy in its most basic configuration. There are ways to set up Astropy to use <a href="https://docs.astropy.org/en/stable/coordinates/solarsystem.html">ephemerides data</a>, which should give more accurate positions. That’ll be my next step.</p>
<p>By the way, a 7-minute difference isn’t much. The Sun moves along the ecliptic at a rate of about 1° per day (roughly 360° in 365 days), and 7 minutes is about 0.005 of a day (7/1440). That means the difference between the Heavens Above Sun position and the Astropy Sun position is about 0.005°. Do I need the position of the Sun (or any of the planets) to a greater precision than that? No, but that won’t stop me from exploring ways to do so.</p>]]>
</content:encoded>
</item>

<item>
<title>A two-month calendar on my Desktop</title>
<link>https://leancrew.com/all-this/2026/08/a-two-month-calendar-on-my-desktop/</link>
<pubDate>Wed, 05 Aug 2026 21:34:08 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/08/a-two-month-calendar-on-my-desktop/</guid>
<description>
  <![CDATA[A few days ago, I posted this image <a href="https://fosstodon.org/@drdrang/117021219801155387">on Mastodon</a>:]]>
</description>
<content:encoded>
  <![CDATA[<p>A few days ago, I posted this image <a href="https://fosstodon.org/@drdrang/117021219801155387">on Mastodon</a>:</p>
<p><img alt="Two-month calendar in TerminalWidget" class="ss" src="https://leancrew.com/all-this/images2026/20260805-Two-month%20calendar%20in%20TerminalWidget.png" title="Two-month calendar in TerminalWidget" width="70%"/></p>
<p>The code I ran to get this two-month calendar on my Desktop was</p>
<pre><code>bash:
if [ $(date +"%-d") -gt 15 ]; then
  cal -A 1
else
  cal -B 1
fi |\
terminal-widget --target cal --font Menlo --bg "#eeeeee" --text -
</code></pre>
<p>I have since updated a couple of things and need more room to talk about it all.</p>
<p>First, the app that put that widget on my Desktop is Brett Terpstra’s <a href="https://terminalwidget.app/">TerminalWidget</a>, which he <a href="https://brettterpstra.com/2026/08/01/introducing-terminalwidget/">just released</a>. As a one-time user of <a href="https://leancrew.com/all-this/2007/06/album-art-with-geektool/">GeekTool</a>, I’ve been waiting for TerminalWidget since Brett first began talking about it. While GeekTool and the similar <a href="https://leancrew.com/all-this/2011/03/nerdtool-picts-and-buddhism/">NerdTool</a> might still work, I wanted an app that worked with macOS’s modern widget system. TerminalWidget does.</p>
<p>I put a medium-sized widget in the upper-left corner of my Desktop by dragging one out from the widget editor window. If you’ve never done this before (I hadn’t), read <a href="https://support.apple.com/guide/mac-help/add-and-customize-widgets-mchl52be5da5/mac">Apple’s instructions</a>.</p>
<p><img alt="Widget editor window" class="ss" src="https://leancrew.com/all-this/images2026/20260805-Widget%20editor%20window.png" title="Widget editor window" width="100%"/></p>
<p>I then right-clicked on the widget and renamed it <code>cal</code>. Now it’s ready for me to run the command that puts the calendar into the widget.</p>
<p>The command is a pipeline, and the executable after the pipe is <code>terminal-widget</code>. This is a symbolic link to the command that’s buried in the TerminalWidget app package. I created the link with</p>
<pre><code>ln -s /Applications/TerminalWidget.app/Contents/MacOS/TerminalWidget ~/bin/terminal-widget
</code></pre>
<p>where <code>~/bin</code> is a directory in my <code>$PATH</code>.</p>
<p>I won’t go through the options given to <code>terminal-widget</code>; they’re explained in <a href="https://terminalwidget.app/cli">TerminalWidget’s CLI page</a>. Suffice it to say that the options put whatever is sent to <code>terminal-widget</code> into the <code>cal</code> widget and format it the way I want.</p>
<p>Instead, let’s talk about the command that comes before the pipe:</p>
<pre><code>bash:
if [ $(date +"%-d") -gt 15 ]; then
  cal -A 1
else
  cal -B 1
fi
</code></pre>
<p>I think this will run in any Bourne-like shell. I know it runs in zsh and bash.</p>
<p>The idea is to run the <a href="https://leancrew.com/all-this/man/man1/cal.html"><code>cal</code> command</a>, displaying the current month and either the month before or the month after. Which other month to show is determined by the <code>if</code> statement at the top. It runs the <a href="https://leancrew.com/all-this/man/man1/date.html"><code>date</code> command</a> and formats the output as just the day of the month with no leading zero. If this is greater than 15, <code>cal</code> is passed the option to show one month after the current month. Otherwise, <code>cal</code> is passed the option to show one month before the current month.</p>
<p>(Brett uses <code>cal</code> as one of his <a href="https://terminalwidget.app/widgets">example widgets</a>, but his command doesn’t include an <code>if</code>. It always displays the current month and the month after.)</p>
<p>By the way, if you run the above command in the Terminal, you’ll notice its output is slightly different from what’s shown in the widget:</p>
<p><img alt="Output from cal in Terminal" class="ss" src="https://leancrew.com/all-this/images2026/20260805-Output%20from%20cal%20in%20Terminal.png" title="Output from cal in Terminal" width="70%"/></p>
<p>The current date is highlighted. I think this is because <code>cal</code> is written to format its output as plain text when it’s being saved to a file or piped to another command but jumps back to invert today’s date when it’s being run in a terminal. It’s similar to the way <a href="https://leancrew.com/all-this/man/man1/ls.html"><code>ls</code></a> lists files one per line when being piped but formats them in columns when its output goes to a terminal.</p>
<p>OK, this is nice for testing out TerminalWidget, but running this command once won’t get it to change when we get past the 15th of the month. It needs to be run every day, preferably in the morning.</p>
<p>The classic Unix way to schedule tasks is <a href="https://leancrew.com/all-this/man/man8/cron.html"><code>cron</code></a>, but <a href="https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/ScheduledJobs.html#//apple_ref/doc/uid/10000172i-CH1-SW2">according to Apple</a> your Mac must be awake when a <code>cron</code> job is scheduled. If it isn’t, the job runs the next time the job is scheduled <em>and</em> the Mac is awake. Because I don’t know when my MacBook Pro will be awake, <code>cron</code> is not a good option for scheduling this task. I need to use <a href="https://leancrew.com/all-this/man/man8/launchd.html"><code>launchd</code></a>, which will run the specified command at the scheduled time <em>or</em> the next time the Mac wakes up.</p>
<p>The easiest way to set up <code>launchd</code> agents is to use <a href="https://www.soma-zone.com/LaunchControl/">LaunchControl</a> and let its GUI handle the tricky bits, but you can do it by hand if you must. First, make a plist file that describes what command is to be run and when and save it in your <code>~/Library/LaunchAgents</code> folder. Here’s the file, named <code>com.leancrew.calwidget.plist</code>, that LaunchControl built for me:</p>
<pre><code>xml:
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
&lt;plist version="1.0"&gt;
&lt;dict&gt;
  &lt;key&gt;Label&lt;/key&gt;
  &lt;string&gt;com.leancrew.calwidget&lt;/string&gt;
  &lt;key&gt;ProgramArguments&lt;/key&gt;
  &lt;array&gt;
    &lt;string&gt;/bin/zsh&lt;/string&gt;
    &lt;string&gt;/Users/drang/bin/calwidget.sh&lt;/string&gt;
  &lt;/array&gt;
  &lt;key&gt;StartCalendarInterval&lt;/key&gt;
  &lt;array&gt;
   &lt;dict&gt;
    &lt;key&gt;Hour&lt;/key&gt;
    &lt;integer&gt;6&lt;/integer&gt;
    &lt;key&gt;Minute&lt;/key&gt;
    &lt;integer&gt;30&lt;/integer&gt;
   &lt;/dict&gt;
  &lt;/array&gt;
&lt;/dict&gt;
&lt;/plist&gt;
</code></pre>
<p>The <code>ProgramArguments</code> section tells <code>launchd</code> to run my <code>calwidget.sh</code> script via zsh. Because <code>launchd</code> commands aren’t run under my usual environment, I give full file paths to both zsh and my script. The <code>StartCalendarInterval</code> section tells <code>launchd</code> to run the job every day at 6:30 AM.</p>
<p>The <code>calwidget.sh</code> script is basically what we’ve seen before, but with a couple of small changes:</p>
<pre><code>bash:
if [ $(date +"%-d") -gt 7 ]; then
  cal -A 1
else
  cal -B 1
fi |\
/Applications/TerminalWidget.app/Contents/MacOS/TerminalWidget \
  --target cal --font Menlo --fg "000000" --bg "eeeeee" --text -
</code></pre>
<p>I decided I’d prefer to see the month before the current month only if we’re a week or less into the current month. Hence the change from <code>15</code> to <code>7</code> in the <code>if</code> statement. Also, I changed the <code>terminal-widget</code> command to the full path of the executable it’s linked to because <code>launchd</code> doesn’t know my usual <code>$PATH</code>. And finally, I added a <code>--fg</code> option to ensure that both the foreground and background colors are what I want—I decided not to rely on defaults.</p>
<p>I loaded this job into <code>launchd</code> via LaunchControl, but I also tested doing it with <a href="https://leancrew.com/all-this/man/man1/launchctl.html"><code>launchctl</code></a>:</p>
<pre><code>launchctl load -w ~/Library/LaunchAgents/com.leancrew.calwidget.plist
</code></pre>
<p>The <code>launchctl</code> man page calls <code>load</code> a “legacy subcommand,” suggesting that I should learn the newer way of doing things, but I find the descriptions of the recommended subcommands incomprehensible. And I haven’t found a good tutorial for them. Again, LaunchControl just does what I want it to do.</p>
<div class="update">
<p><strong>Update 5 Aug 2026 10:36 PM</strong><br/>
Eric Hemmeter <a href="https://fosstodon.org/@ehemmete@mastodon.social/117045209633791732">sent me a link on Mastodon</a> to <a href="https://babodee.wordpress.com/2016/04/09/launchctl-2-0-syntax/">this 2016 article</a> by Babo D, which does a much better job of explaining <code>launchctl</code>’s “new” syntax than Apple does anywhere that I’ve seen. While I don’t think this will take the place of LaunchControl for me, I certainly understand the recommended subcommands better now than I ever have. I’ve saved a web archive version of the page in case it disappears. Thanks, Eric!</p>
</div>
<p>While this exercise was mostly a proof of concept, I do like having quick access to a two-month calendar and will probably keep this widget. Now I need to think about what other information I want immediate access to.</p>]]>
</content:encoded>
</item>

<item>
<title>My rules for using spreadsheets</title>
<link>https://leancrew.com/all-this/2026/08/my-rules-for-using-spreadsheets/</link>
<pubDate>Sat, 01 Aug 2026 15:29:24 +0000</pubDate>
<dc:creator>
  <![CDATA[Dr. Drang]]>
</dc:creator>
<guid>https://leancrew.com/all-this/2026/08/my-rules-for-using-spreadsheets/</guid>
<description>
  <![CDATA[My fundamental rule is <em>Don’t</em>, but a single word wouldn’t make for much of a blog post.]]>
</description>
<content:encoded>
  <![CDATA[<p>[Equations in this post may not look right (or appear at all) in your RSS reader. Go to <a href="https://leancrew.com/all-this/2026/08/my-rules-for-using-spreadsheets/">the original article</a> to see them rendered properly.]</p>
  <hr />
  <p>My fundamental rule is <em>Don’t</em>, but a single word wouldn’t make for much of a blog post.</p>
<p>In what follows, I hope to explain how I’ve come to that rule and the exceptions I make to it. I’ve been thinking about how I use and don’t use spreadsheets quite a bit lately. This introspection was inspired in part by <a href="https://macstockconferenceandexpo.com/schedule/#:~:text=How%20the%20Cool%20Kids%20Really%20Use%20Spreadsheets">Allison Sheridan’s presentation at Macstock</a> (which you can see <a href="https://www.podfeet.com/blog/2026/07/macstock-2026-how-the-cool-kids-really-use-spreadsheets/">on her site</a> along with a couple of <a href="https://www.podfeet.com/blog/2026/07/visicalc-ken-case/">other</a> recent <a href="https://www.podfeet.com/blog/2026/07/excel-find-dependencies/">spreadsheet posts</a>) and in part by my recent use of Numbers to <a href="https://leancrew.com/all-this/2026/07/sum-of-cubes-via-difference-tables/">make difference tables</a> and <a href="https://leancrew.com/all-this/2026/07/plotting-baseball-team-progress/">clean up a table of data</a>.</p>
<p>Let’s start by considering what makes spreadsheets so attractive. Right off the bat, you’re presented with a grid of cells that act as data containers. You don’t have to define these containers, you don’t have to name them, you don’t have to initialize them—they’re just there, waiting for you to fill them as you see fit.</p>
<p>When it comes time to start operating on this data, you <em>still</em> don’t have to name the cells. You just click (or click and drag) to fill in the function arguments. The spreadsheet app fills in the appropriate row/column reference. If you want a reminder of what a cell is for, you can type a name or description in an adjacent cell. Similarly, you don’t have to figure out the appropriate order of the operations. The app works out the cell dependency chain and recalculates everything, everywhere, all at once.</p>
<p>And because you can set the size, color, border, and font styling of every cell, your spreadsheet can generate nice-looking tables for inserting into your reports, memos, and slideshows.</p>
<p>So a spreadsheet is a data store, a logic machine, and a presentation tool. Are you getting it?</p>
<p>But if spreadsheets are all that, where does my <em>Don’t</em> rule come from? There are many sources, but I’d have to say I’ve been strongly influenced by the last 10–15 years of my working life, during which time I had to analyze dozens and dozens of data sets, all of which were sent to me as Excel spreadsheets. The engineering firms that sent me the spreadsheets had created them not simply as data stores. They included some of their own analysis (which typically overlapped slightly with mine), and they formatted the spreadsheets as tables to put into their own reports. This made my work harder for a few reasons:</p>
<ol>
<li><p>Because I had to make sure I understood and agreed with their analysis, I had to review all their formulas. Some of these formulas were complex—nested <code>IF</code> statements are easy to follow in a traditional programming language, but they’re a mess in a spreadsheet. Some were inconsistent—different rows in the same table would have different formulas, as if they were written by different people at different times or adapted from a spreadsheet on a previous project. Some of them referred to cells that were far away and required a lot of scrolling to track down. None of them—not a single one in over a decade—used cell names to help make the formulas easier to understand.</p>
<p>The complicated formulas mentioned above sometimes—not often, but sometimes—contained mistakes. And sometimes the formulas were correct, but the descriptions in the header cells were wrong. This meant phone calls were needed to resolve the discrepancies, further slowing the analysis.</p></li>
<li>It was common for the data to be split over two or more sheets. I think this was done mainly to make the tables fit better into the other engineers’ reports, which was fine for their purposes but not for mine. I had to recombine the data for my analyses. Also, the sheets often had complicated, multiline headers, which meant I couldn’t just export them as CSV files.</li>
<li>Every engineer I worked with built their spreadsheets in a different way. Those who worked for the same firm didn’t adhere to an “ABC Engineering” house style. Even individual engineers would change their spreadsheet styling from one project to the next. Basically, every spreadsheet that came in the door was <em>sui generis</em>, and I had to do all the data cleaning by hand. This slowed me down, not only because I couldn’t rely on automation for this step, but also because I had to double- and triple-check my work to avoid copy/paste mistakes.</li>
</ol>
<p>Fundamentally, this experience—especially Item 1—soured me on the use of spreadsheets for anything large or complex. The engineers I was working with were smart, but their spreadsheets weren’t. My conclusion was that the simplicity of the typical click-and-drag method of assembling a spreadsheet encouraged poor organization and errors as the spreadsheets grew or were adapted to new data.  It’s easy to say “Oh, I would never do that,” but I’m old enough to know that I <em>would</em> do that. I see the ease with which I can build spreadsheets with today’s apps as a Siren song that will lead me onto the rocks.</p>
<p>(If you’re getting ready to write to me about the <a href="https://retractionwatch.com/2013/04/18/influential-reinhart-rogoff-economics-paper-suffers-database-error/">Reinhart/Rogoff paper</a>, you can relax. It is the prime example of elementary spreadsheet errors—errors that two Harvard professors would <em>surely</em> never make—and it led to a lot of suffering through unnecessary government austerity policies. And if you’re now getting ready to write to me about how Reinhart and Rogoff’s errors don’t negate the essential truth of their conclusions, you can just fuck off.)</p>
<p>The convenience of having the data and the analysis logic in the same document becomes a problem when you have to apply that logic to several datasets, especially when they differ in size. Spreadsheet templates are great when the data allow you make several spreadsheets with the exact same layout, but the data I tend to deal with don’t fit that rigid pattern. If I’m doing, for example, analyses and plots of several time series, those series seldom extend over the same length of time and the same number of data points. It’s far easier to deal with these size differences when the logic is in a program, separated from the data.</p>
<p>Another problem with spreadsheets is that the amount of data they can contain is more limited than when you use other data analysis workflows. The size limits on spreadsheets are, admittedly, quite large, but in an era of Big Data “quite large” may not be big enough. In her <a href="https://www.podfeet.com/blog/2026/07/macstock-2026-how-the-cool-kids-really-use-spreadsheets/">Macstock talk</a>, Allison shows how she ran into that problem with the data set of <a href="https://catalog.data.gov/dataset/baby-names-from-social-security-card-applications-national-data">US baby names</a>. Let’s take a detour to talk about handling that data.</p>
<hr/>
<p>One of the ways you can download the baby name dataset is as a zipped collection of CSV files. Each file in the collection is associated with one year and has a name like <code>yob1960.txt</code>. The contents look like this:</p>
<pre><code>Mary,F,51472
Susan,F,39208
Linda,F,37316
Karen,F,36378
Donna,F,34138
[etc]
</code></pre>
<p>where the first item is the name, the second is the sex at birth, and the third is the number of babies given that name in that year. The lines are ordered first by sex and then by number. If you concatenate all the files, you’ll find there are 2,181,032 entries. As Allison found out, this won’t fit into an Excel spreadsheet, as Excel is limited to 1,048,576 rows. That’s the very computery number <math xmlns="http://www.w3.org/1998/Math/MathML"><msup><mn>2</mn><mn>20</mn></msup></math> or <math xmlns="http://www.w3.org/1998/Math/MathML"><msup><mn>1024</mn><mn>2</mn></msup></math>. The limit in Numbers is the less computery but more human 1,000,000 rows.</p>
<p>Allison got around the size problem by… er… cheating. She eliminated the less popular names to get the list to fit into Excel, and then demonstrated some pivot table stuff. You can see it starting at 1:13:50 <a href="https://www.podfeet.com/blog/2026/07/macstock-2026-how-the-cool-kids-really-use-spreadsheets/">in the video</a>.</p>
<p>I decided to do something similar to her work but without the cheating. First, I concatenated all the individual files into one big CSV file that also included a field for the year. That was done through these shell commands:</p>
<pre><code>echo 'Year,Name,Sex,Count' &gt; all-years.csv
for f in yob*.txt; do
  y=${f:3:4}
  sed -e "s/\r$//;s/^/$y,/" $f &gt;&gt; all-years.csv
done
</code></pre>
<p>The year is extracted from the file name through <a href="https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html#:~:text=Substring%20Expansion.">substring expansion</a> and then added to the beginning of each line via <code>sed</code>. The original files are in Windows format with CRLF line endings, so the <code>sed</code> command also deletes the CR characters. The upshot of all this is a file (with Unix line endings) named <code>all-years.csv</code> that looks like this:</p>
<pre><code>Year,Name,Sex,Count
1880,Mary,F,7065
1880,Anna,F,2604
1880,Emma,F,2003
1880,Elizabeth,F,1939
1880,Minnie,F,1746
[etc]
</code></pre>
<p>(Yes, even though the data set is said to have come from Social Security registrations, it starts in 1880, decades before the Social Security Act. I can’t explain that. Nor can I explain how Minnie was once the fifth most popular girls’ name.)</p>
<p>I’m going to use Python and <a href="https://pandas.pydata.org/">Pandas</a> to extract the five most popular girls’ names from 2001 through 2025 (the last year in the dataset). Here’s the start of a simple interactive Python session that does it:</p>
<pre><code>&gt;&gt;&gt; import pandas as pd
&gt;&gt;&gt; df = pd.read_csv('all-years.csv')
&gt;&gt;&gt; cols = ['Name', 'Count']
</code></pre>
<p>This reads the CSV file into a dataframe and defines the columns of the dataframe that we want to include in our output. The <code>&gt;&gt;&gt;</code> at the beginning of each line is the interactive Python prompt. Here’s how we get the list of names we’re interested in:</p>
<pre><code>&gt;&gt;&gt; df[(df.Sex=='F') &amp; (df.Year&gt;2000)][cols].groupby('Name')\
... .sum().sort_values('Count', ascending=False)[:5]
           Count
Name            
Emma      449576
Olivia    423613
Isabella  381577
Sophia    368619
Emily     353077
</code></pre>
<p>The <code>...</code> indicates a continuation input line. Everything after that is output.</p>
<p>Reading through the command, we see that we’re</p>
<ol>
<li>getting the subset of data consisting of girls born after 2000;</li>
<li>limiting the output to the Name and Count fields;</li>
<li>grouping the output by Name;</li>
<li>summing the Counts for each Name;</li>
<li>sorting the results by Count in descending order; and</li>
<li>limiting the output to the top five names.</li>
</ol>
<p>That’s obviously a long command, but you can see how it’s constructed in a logical fashion.</p>
<p>If we want to compare these to the popular girls’ names from a century earlier, the command is very similar:</p>
<pre><code>&gt;&gt;&gt; df[(df.Sex=='F') &amp; (df.Year&gt;1900) &amp; (df.Year&lt;=1925)][cols].groupby('Name')\
... .sum().sort_values('Count', ascending=False)[:5]
            Count
Name             
Mary      1056333
Helen      505522
Dorothy    475151
Margaret   402317
Ruth       364923
</code></pre>
<p>My wife and I had great aunts with some of these names.</p>
<p>If you’re a database maven, you recognize the Pandas <a href="https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.groupby.html"><code>groupby</code> function</a> as a copy of the SQL <a href="https://en.wikipedia.org/wiki/Group_by_(SQL)"><code>GROUP BY</code> construct</a>. Let’s redo this in an interactive session with <a href="https://sqlite.org/">SQLite</a>. We start by importing the data from the CSV file:</p>
<pre><code>sqlite&gt; .mode csv
sqlite&gt; .import all-years.csv names
sqlite&gt; .mode columns
</code></pre>
<p>The <a href="https://sqlite.org/climode.html">mode</a> is set to <code>csv</code> in order to import the data, then set back to <code>columns</code> to make the output look the way we want.</p>
<p>Now we get the top five girls’ names from the 21st century and show them in descending order:</p>
<pre><code>sqlite&gt; select Name, sum(Count) from names
     ...&gt; where Sex is "F" and Year &gt; 2000
     ...&gt; group by Name order by sum(Count) desc limit 5;
Name      sum(Count)
--------  ----------
Emma      449576    
Olivia    423613    
Isabella  381577    
Sophia    368619    
Emily     353077    
</code></pre>
<p>SQL is certainly more English-like, but you can see the parallels between it and Pandas. Now for the early 20th century:</p>
<pre><code>sqlite&gt; select Name, sum(Count) from names
     ...&gt; where Sex is "F" and Year &gt; 1900 and Year &lt;= 1925
     ...&gt; group by Name order by sum(Count) desc limit 5;
Name      sum(Count)
--------  ----------
Mary      1056333   
Helen     505522    
Dorothy   475151    
Margaret  402317    
Ruth      364923    
</code></pre>
<p>Allison does similar things with her truncated Excel file using <a href="https://en.wikipedia.org/wiki/Pivot_table">pivot tables</a>. I hate the name “pivot table,” because I think it’s an obscure term for the simple operations of grouping and summarizing. For some reason, my feelings on this don’t matter, and pivot tables are here to stay. Pandas has even added a <a href="https://pandas.pydata.org/docs/reference/api/pandas.pivot_table.html"><code>pivot_table</code> function</a> to placate people who’ve come over from Excel. Under the hood, <code>pivot_table</code> calls <code>groupby</code>.</p>
<hr/>
<p>Well, that was kind of a long detour, and I forgive you if you’ve forgotten where we were. I had just gone through a list of things that made me leery of using spreadsheets—why my first rule of using spreadsheets is <em>Don’t</em>.</p>
<p>But I allow for exceptions. My two main exceptions are:</p>
<ol>
<li><p>When the problem is both small enough to see on the screen with almost no scrolling and the operations are simple enough to be easily understood without counting commas and parentheses. That was what I did for my <a href="https://leancrew.com/all-this/2026/07/sum-of-cubes-via-difference-tables/">sum of cubes difference tables</a>. The formulas consisted mainly of subtractions, with some power and division operations here and there. Only the simultaneous equations solution in the lower right involved actual function calls, and there were no nested calls.</p>
<p><img alt="Cubic sum spreadsheet" class="ss" src="https://leancrew.com/all-this/images2026/20260731-Cubic%20sum%20spreadsheet.png" title="Cubic sum spreadsheet" width="100%"/></p></li>
<li>When I’m using the spreadsheet as a way station for editing data before passing it along. I did that in the <a href="https://leancrew.com/all-this/2026/07/plotting-baseball-team-progress/">baseball team progress</a> post to edit down the large and unwieldy <a href="https://www.baseball-reference.com/teams/CHC/2026-schedule-scores.shtml">season results tables</a> from Baseball Reference. It was fast and easy to select the table in Safari, paste it into Numbers, and then delete the columns and rows I didn’t need. But I did it this way only because this was a one-off project. If I were given the job of making progress charts for all 30 teams every day of the season, I’d never do it by hand like that. I’d use the Pandas <a href="https://pandas.pydata.org/docs/reference/api/pandas.read_html.html"><code>read_html</code> function</a> to pull the HTML table into a dataframe and a variety of <a href="https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.drop.html#pandas.DataFrame.drop"><code>drop</code> commands</a> to pare it down.</li>
</ol>
<p>I used to use spreadsheets for data entry, too, but not anymore. It was once the only reliable way to put a table of numbers found in a book into electronic form. But OCR has gotten so much better, I can’t remember the last time I did this.</p>
<p>I know there are lots of people who love using spreadsheets. They’ve spent a lot of time learning the ins and outs and don’t want to switch to another tool. That’s fine. This post was about <em>my</em> rules, not anyone else’s. I’m not saying good, accurate, complex work can’t be done in spreadsheets. It just won’t be done by me.</p>
  ]]>
</content:encoded>
</item>

</channel>
</rss>

