<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title/><link>https://tdhopper.com/</link><description>Recent content on</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Mon, 02 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tdhopper.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Getting to Know Tim Hopper with Brenton Mallen</title><link>https://tdhopper.com/blog/getting-to-know-tim-hopper-with-brenton-mallen/</link><pubDate>Mon, 02 Feb 2026 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/getting-to-know-tim-hopper-with-brenton-mallen/</guid><description>&lt;img src="https://tdhopper.com/images/robot-assistant.png" alt="Featured image of post Getting to Know Tim Hopper with Brenton Mallen" /&gt;&lt;h2 id="listen"&gt;Listen
&lt;/h2&gt;&lt;iframe src="https://open.spotify.com/embed/episode/3LY6E71ScXr2f889g0YamD" width="100%" height="232" frameBorder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="links"&gt;Links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.linkedin.com/in/brentonmallen/" target="_blank" rel="noopener"
&gt;Brenton Mallen on LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com" target="_blank" rel="noopener"
&gt;Tim Hopper&amp;rsquo;s website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://shouldigetaphd.com" target="_blank" rel="noopener"
&gt;Should I Get a PhD?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://pydevtools.com" target="_blank" rel="noopener"
&gt;Python Developer Tooling Handbook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.goodreads.com/book/show/54493401-project-hail-mary" target="_blank" rel="noopener"
&gt;&lt;em&gt;Project Hail Mary&lt;/em&gt; by Andy Weir&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.ea.com/games/it-takes-two" target="_blank" rel="noopener"
&gt;&lt;em&gt;It Takes Two&lt;/em&gt; (video game)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.gcc.edu/" target="_blank" rel="noopener"
&gt;Grove City College&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.rti.org/" target="_blank" rel="noopener"
&gt;RTI International&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.amazonrobotics.com/" target="_blank" rel="noopener"
&gt;Kiva Systems (now Amazon Robotics)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.or.ncsu.edu/" target="_blank" rel="noopener"
&gt;NC State Operations Research&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://en.wikipedia.org/wiki/Encarta" target="_blank" rel="noopener"
&gt;Encarta&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.goodreads.com/series/49073-the-chronicles-of-narnia" target="_blank" rel="noopener"
&gt;&lt;em&gt;The Chronicles of Narnia&lt;/em&gt; by C.S. Lewis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.goodreads.com/series/66175-the-lord-of-the-rings" target="_blank" rel="noopener"
&gt;&lt;em&gt;The Lord of the Rings&lt;/em&gt; by J.R.R. Tolkien&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="subscribe"&gt;Subscribe
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/podcast/feed" target="_blank" rel="noopener"
&gt;RSS Feed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://podcasts.apple.com/us/podcast/into-the-hopper/id1499693201" target="_blank" rel="noopener"
&gt;Apple Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://open.spotify.com/show/63NrgKMVb0VTwkklGboIjy" target="_blank" rel="noopener"
&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://overcast.fm/itunes1499693201/into-the-hopper" target="_blank" rel="noopener"
&gt;Overcast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary"&gt;Summary
&lt;/h2&gt;&lt;p&gt;In this special episode of &lt;em&gt;Into the Hopper&lt;/em&gt;, the tables are turned as my friend and former colleague Brenton Mallen interviews me. We worked together at two different companies over the years, and Brenton realized that despite knowing me for over a decade, he didn&amp;rsquo;t really know the full Tim Hopper story.&lt;/p&gt;
&lt;p&gt;We cover a lot of ground: growing up in southern West Virginia, my winding educational path through physics, computer science, and math, my time working at a children&amp;rsquo;s home in Tennessee, two PhD programs I started but didn&amp;rsquo;t finish, how I stumbled into data science through Twitter and grad school, meeting my wife Maggie, becoming a parent to four kids, hobbies like woodworking, photography, and knitting, and our recent move to Indiana to be closer to family. We also discuss the impact of AI on education and what it means for raising kids in this rapidly changing world.&lt;/p&gt;
&lt;h2 id="transcript"&gt;Transcript
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; So we don&amp;rsquo;t waste it—take one! Hi, welcome to Into the Hopper. I&amp;rsquo;m Brenton Mallen, and I&amp;rsquo;m here with my special guest, Tim Hopper. Thanks for having me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Thanks for having me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Well, you&amp;rsquo;re welcome. How&amp;rsquo;d I get here?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Beats me. I don&amp;rsquo;t know how we got here. Actually, maybe we&amp;rsquo;ll talk about that. What are we doing today?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; For context, I had messaged you—I don&amp;rsquo;t even remember, it&amp;rsquo;s been a while now—asking when someone was going to interview you for your podcast. And I guess I volunteered by asking that question.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Very good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; As we started talking about doing this and I wrote down some stuff to talk about, I realized a couple of things. One, I&amp;rsquo;ve just been doing a lot of thinking about friendship and what it means to be a friend and what it means to have a friend. We throw that term around quite a bit. I&amp;rsquo;ve been trying to realize what relationships I have that are friends, what that means, and can I make those relationships and connections a bit deeper. I&amp;rsquo;ve known you, I don&amp;rsquo;t know, 11 years now? Ten? Over a decade.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Just over 10, because we started working together in October of 2015.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Right. Was that PyData where we met?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; We were already working together for a few weeks, and then we went to PyData.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I think I met you for the first time in person at PyData, yeah, definitely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; In New York.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; PyData 2015.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; At Capital One or no—I don&amp;rsquo;t know—the World Trade Center.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I realized I&amp;rsquo;ve quote-unquote &amp;ldquo;known&amp;rdquo; you for over a decade, but we talk decently regularly and I don&amp;rsquo;t know the Tim Hopper. So I thought at the very least we can have a conversation, I could get to know you a little bit better, and you can get a podcast episode out of it. If not, at least we&amp;rsquo;ll have a conversation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Everyone will know the real Tim Hopper after this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; We&amp;rsquo;ll see—could be good, could be bad. Who knows? But then again, your data&amp;rsquo;s out there already.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Usually it&amp;rsquo;s selectively shared, but today whatever you ask, I&amp;rsquo;m obligated to answer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I will make note of that. I just wanted to give you an opportunity to tell your story, coming from rural West Virginia to the big Kardashian machine of data science you&amp;rsquo;ve become.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Maybe we should explain first—we worked together at two different companies, actually, from 2015 to 2017 and then 2020 during early COVID until &amp;lsquo;21, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I think so. Something like that. My memory is not what it used to be.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; It&amp;rsquo;s all right. I&amp;rsquo;m pretty sure I&amp;rsquo;m right about that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; As you look at a resume. But yeah, so let&amp;rsquo;s start there, I guess, at the very beginning. You grew up in West Virginia, right?&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="growing-up-in-west-virginia"&gt;Growing Up in West Virginia
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I did, yeah. I was born and raised in southern West Virginia, about as far south as you can be. My dad is a family practice doctor, and he had moved there after medical school to work in family practice. I lived in the same house my whole childhood. My parents moved back to North Carolina—where they lived previously—in 2007 when I was a senior in college, but my whole childhood was West Virginia.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Did you move from West Virginia to North Carolina when you were at school?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; To give the short story: I grew up in West Virginia, went to college in western Pennsylvania, then lived in Tennessee for a year, Virginia for a year, North Carolina for 15 years, and as of two weeks ago, I live in Indiana.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; So what was West Virginia like?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; It was great. My mom and I were just talking about this the other day. She said when we were younger, people would say, &amp;ldquo;Oh, you shouldn&amp;rsquo;t raise your kids there. You need them in a place where there&amp;rsquo;s opportunity and different things.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m sure I had some discontentment with various things as a kid, but I really have no complaints about growing up in West Virginia. I tell people that it&amp;rsquo;s only been in the last 10 years or so that I&amp;rsquo;ve started to tell people I grew up in &lt;em&gt;rural&lt;/em&gt; West Virginia, because the town I grew up in was 6,000 people—I think it&amp;rsquo;s the 13th biggest town in West Virginia. We had Walmart. It wasn&amp;rsquo;t rural. We were right off the interstate—the town is Princeton, right off I-77—so we had interstate amenities. They have Starbucks there now.&lt;/p&gt;
&lt;p&gt;But West Virginia gets real rural real fast. In comparison, I didn&amp;rsquo;t grow up in the hollers, way off the beaten path. It&amp;rsquo;s stunningly beautiful. It just has been snowing here in Indiana, and I&amp;rsquo;ve realized I was very spoiled growing up in a valley where you could just sled wherever. My eight-year-old wanted to sled, so we had to drive somewhere to find a hill, which is a new experience for me.&lt;/p&gt;
&lt;p&gt;I think it was a very safe place to grow up. People were very nice and welcoming. I think I had sufficient opportunities. I didn&amp;rsquo;t have the opportunities that a lot of kids have in bigger areas, but I didn&amp;rsquo;t have real complaints. I was satisfied with my education. I&amp;rsquo;m very glad I grew up there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; You were an only child?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; No, I&amp;rsquo;m the youngest of four. I have three older sisters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Did I know that?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I don&amp;rsquo;t know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I feel like we&amp;rsquo;ve talked more about Maggie&amp;rsquo;s siblings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Maggie has four sisters. I have three. So we have a lot of aunties in our family, as I like to say.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; What was that like? For context, I&amp;rsquo;m the middle of three boys, which I imagine is just really different.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Honestly, in going to college, I made a lot of really good male friends. I had good friends growing up, but in college I made friends that just became like brothers to me—and they&amp;rsquo;re still my friends to this day. Now I have two boys, a six-year-old and an eight-year-old, and seeing their bond—it&amp;rsquo;s just different from anything I had with my sisters. But I don&amp;rsquo;t think I had any complaints having sisters. Being the only boy and the youngest was probably kind of lonely at times, but that was fine. They used to paint my nails. My mom says my oldest sister used to like to play school down in the basement, and I would get upset that we didn&amp;rsquo;t have more recess during school, that kind of thing. But I still have a good relationship with my sisters to this day. I&amp;rsquo;m very thankful for my upbringing and for my whole family.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="school-sports-and-early-interests"&gt;School, Sports, and Early Interests
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; What about going through school—grade school, friends, hobbies? What do kids do in West Virginia?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; For school, I had kind of a mixed bag. My mom homeschooled me from kindergarten to fourth grade. Then I went to a private Christian school for fifth and sixth grade. Then seventh to 12th, I went to the public school, middle school and high school. So I went to a variety of schools.&lt;/p&gt;
&lt;p&gt;As a kid, I loved to play soccer—not basketball, as some people might be surprised to hear.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Are you longer in the legs or longer in the torso? I forget.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I&amp;rsquo;m all torso and no coordination. But I played soccer from when I was pretty young—maybe like six or whenever you can start—until I was in middle school. I played basketball for fifth and sixth grade. I was just kind of a stereotypical boy as a younger kid, very restless and wanted to be outside playing things. We played outside a lot.&lt;/p&gt;
&lt;p&gt;I kind of lost interest in sports going on. I&amp;rsquo;m not that competitive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Sounds like you were more sporty back then, and now you&amp;rsquo;re less so.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I was never that good. I&amp;rsquo;m just not that competitive or driven. As a kid, I probably was not that good at working for things. I was always very good at math and school as a whole came pretty easy to me—spelling never did, still doesn&amp;rsquo;t. I didn&amp;rsquo;t really work hard for sports.&lt;/p&gt;
&lt;p&gt;I started to play some instruments—piano with a private teacher and then some instruments in school—but never really liked to practice. Same problem.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve always been kind of into reading a lot. My mom says I used to love to read the World Book Encyclopedia when I was a kid.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; We had a set of those.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. I played with my sisters. I was in Cub Scouts from right when you can do that, and then I was a Boy Scout all the way through high school, so that was a big part of my life.&lt;/p&gt;
&lt;p&gt;We did family things. My dad was a big golfer, so I picked up golf for a while with him until I was too tall to use my extra-length golf clubs. Then I was like, &amp;ldquo;All right, this isn&amp;rsquo;t worth it for me.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;In a lot of ways, pretty normal. In fifth grade, we got a Windows 95 computer. We had had like a DOS computer, maybe a 3.1, before that. But in fifth grade we got a Windows computer with &lt;strong&gt;Encarta&lt;/strong&gt; on it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Okay.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Encarta 95. That just rocked my world. My mom talks about how, when I was in fifth grade, I did a report on Thomas Edison in school. As soon as we got Encarta, I found Thomas Edison, and you could play the clip from one of the early recordings—he does like &amp;ldquo;Mary Had a Little Lamb&amp;rdquo; or something, there&amp;rsquo;s still a recording of. I played that in Encarta, and that blew her mind—both that it was a thing and that I found it so fast.&lt;/p&gt;
&lt;p&gt;I got more and more interested in computers and just tinkering with computers as time went on, which in a lot of ways led to what I do professionally. It was always kind of a side interest.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="early-programming-and-lord-of-the-rings-websites"&gt;Early Programming and Lord of the Rings Websites
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; You said you ran a website—I think like a GeoCities thing or something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, in high school I started to make websites. My friend and I started making GeoCities and AngelFire pages—that was one of the competitors—probably starting in late middle school. I ran a &lt;em&gt;Lord of the Rings&lt;/em&gt; website. Actually, my AngelFire website you can still find—I probably shouldn&amp;rsquo;t link to it—but I made a &lt;em&gt;Princess Bride&lt;/em&gt; fan site around 2000, &amp;lsquo;99. I was just copying content from other websites. I learned HTML by—in those days you didn&amp;rsquo;t have all this complex stuff—so I could just go open other websites and copy and paste the HTML.&lt;/p&gt;
&lt;p&gt;Then I started doing &lt;em&gt;Lord of the Rings&lt;/em&gt; websites. Through that in high school, I started to get curious about programming, but I literally had no way of learning programming really. I wasn&amp;rsquo;t into BBS and forums and things around programming back then. So I graduated from high school in 2004—this is the early 2000s—and I just wasn&amp;rsquo;t in tune with what was available through the internet.&lt;/p&gt;
&lt;p&gt;I was trying to find books to learn programming and bought a book on PHP. That was really where I first started programming, to make &lt;em&gt;Lord of the Rings&lt;/em&gt; websites. I did that through high school. It was like 2001 to 2003 when the &lt;em&gt;Lord of the Rings&lt;/em&gt; movies came out. I was in high school.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; It was the perfect time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I had already read the books and was a big fan. The main site was like TheOneRing.net or something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, I won a big competition on TheOneRing.net and won this big &lt;strong&gt;Weta&lt;/strong&gt; gift that was like worth hundreds of dollars. I was active on the &lt;em&gt;Lord of the Rings&lt;/em&gt; forums and stuff. That kind of thing got me started in programming beyond HTML—and TI-BASIC, the really basic programming you could do on a TI calculator—that&amp;rsquo;s where I really cut my teeth.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="reading-and-sci-fi"&gt;Reading and Sci-Fi
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; When did you first read &lt;em&gt;Lord of the Rings&lt;/em&gt;? And what do you like to read—more fantasy?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; My cousin gave me his copies of &lt;em&gt;Lord of the Rings&lt;/em&gt;. I have two male cousins who are like six years older than me that I idolized—shout out Justin and Joseph if you&amp;rsquo;re listening. Joseph gave me copies of &lt;em&gt;Lord of the Rings&lt;/em&gt;. I was pretty young, maybe like nine or ten, and they sat on my shelf for a long time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Did you start with &lt;em&gt;The Hobbit&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; That&amp;rsquo;s a good question. I don&amp;rsquo;t know the answer. I finally got to reading &lt;em&gt;Lord of the Rings&lt;/em&gt; around late middle school, like 2000-ish. I&amp;rsquo;ve never actually been a huge fantasy reader and I&amp;rsquo;m still not that much.&lt;/p&gt;
&lt;p&gt;My mom read us &lt;em&gt;Narnia&lt;/em&gt; as kids, so those were in my repertoire. These days I read some fantasy stuff to my kids. We&amp;rsquo;ve been reading &lt;em&gt;The Chronicles of Prydain&lt;/em&gt;—Lloyd Alexander—they&amp;rsquo;re really old, from like 50 years ago. We&amp;rsquo;ve read the &lt;em&gt;Narnia&lt;/em&gt; series. Eventually we&amp;rsquo;ll read &lt;em&gt;Lord of the Rings&lt;/em&gt;. We&amp;rsquo;ve read &lt;em&gt;The Hobbit&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve not been a big fantasy person. I do enjoy sci-fi, usually either hard sci-fi or kind of fluff sci-fi that&amp;rsquo;s really easy. I like the hard sci-fi for the science part—like Andy Weir stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; That kind of stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Oh yeah, I love Andy Weir. &lt;em&gt;Project Hail Mary&lt;/em&gt; is one of my favorites.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I&amp;rsquo;ve read that like seven or eight times now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I should read it again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; The audio book is really good with the way the alien talks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, I think I listened to it. These days I read a lot of history, some theology, biography, and I enjoy novels—I just don&amp;rsquo;t make a lot of time for them. I do listen to, like, Jack Reacher and just kind of dumb mystery thriller type stuff when I need a mental break. But I don&amp;rsquo;t have a lot of time for reading these days.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="college-from-computer-engineering-to-math"&gt;College: From Computer Engineering to Math
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; So &lt;em&gt;Lord of the Rings&lt;/em&gt; websites—that&amp;rsquo;s early 2000s, you&amp;rsquo;re graduating high school in 2004?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; 2004, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I was 2005.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; The day Ronald Reagan died—graduated from high school.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Was he giving the commencement speech?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; He wasn&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; So then school after that—did you know where you wanted to go? Did you know what you wanted to do?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I thought in high school that I wanted to study computer engineering. I don&amp;rsquo;t think I really knew what computer engineering was, and I didn&amp;rsquo;t really know how to learn what it was. I just thought computers were really cool.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t know that I even knew—I mean, I had heard of computer science, but I don&amp;rsquo;t know that I knew it was necessarily an alternative. We had a small public university nearby where I knew some people studied computing, like two-year degrees for network admin type roles. That didn&amp;rsquo;t seem that interesting to me. I didn&amp;rsquo;t love programming, even though it was something I wanted to learn and do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Did your parents try to influence you into a certain direction?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; That&amp;rsquo;s a good question.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Like, no one asked you to be a doctor?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; No. My dad&amp;rsquo;s a doctor, my mom&amp;rsquo;s a nurse, and they never pressured us to pursue medical things. I think they largely tried not to pressure us, for better or worse. They really wanted us to go to college—that was their big goal for us. But I don&amp;rsquo;t think they necessarily pressured me a lot.&lt;/p&gt;
&lt;p&gt;I just didn&amp;rsquo;t know people around me who were programmers or computer scientists or computer engineers. I just knew that I thought computers were neat and seemed to be kind of the future. So I had computer engineering in mind.&lt;/p&gt;
&lt;p&gt;I was looking at Virginia Tech, which was fairly near where I grew up and obviously a very reputable school. But I&amp;rsquo;m a Christian and grew up in church, and my parents encouraged us to think about looking at Christian colleges. The college I went to was called &lt;strong&gt;Grove City College&lt;/strong&gt; in western Pennsylvania. It&amp;rsquo;s a Christian college that happened to have computer engineering, which is pretty rare for smaller schools.&lt;/p&gt;
&lt;p&gt;I actually ended up visiting there, and without that much perspective, I was like, &amp;ldquo;Oh, this is great. I&amp;rsquo;ll go here.&amp;rdquo; I went to visit Virginia Tech and Grove City, then applied to Grove City for early admission—which is basically like if you get in early, you&amp;rsquo;re supposed to go. So I applied early admission and then didn&amp;rsquo;t apply to any other colleges.&lt;/p&gt;
&lt;p&gt;It was a fairly limited information decision. I had met somebody while I was in high school who had gone there like 20 years previously, and he really recommended it to me. I trusted him, so I decided to go as a computer engineering student.&lt;/p&gt;
&lt;p&gt;But before I matriculated, I changed my mind to pursue physics instead, because I really loved physics in high school. I had a great physics teacher named Mr. Smith who was inspirational to me—I was his teaching assistant. I really loved the math side of physics.&lt;/p&gt;
&lt;p&gt;In hindsight, I enjoyed that because in high school math, besides geometry, math is so routine—you&amp;rsquo;re just learning the mechanics. In physics, you&amp;rsquo;re actually getting to solve problems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; It&amp;rsquo;s like applied math to a degree. It frames a problem you&amp;rsquo;re trying to solve or some observation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Exactly. I never really enjoyed labs that much—partially my laziness—but I enjoyed the math and problem-solving side. So before I started, I decided to do physics. I was like, &amp;ldquo;I&amp;rsquo;ll become a physics professor, that makes sense.&amp;rdquo; I don&amp;rsquo;t think I knew any physics professors. My dad had one friend from college who had gotten a PhD in physics and then been stuck in really mediocre jobs his whole life. He was always just kind of like, &amp;ldquo;Well, be careful about that.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Which is the inspiration for &lt;em&gt;Should You Get a PhD?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, I mean, it was good advice. To jump ahead, later I jumped out of two PhD programs and am fairly cynical now about whether PhD programs are a good route for people.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I ended up in college as a math major. I studied physics and started out—first-year physics and engineering is all pretty similar, you&amp;rsquo;re taking basic classes.&lt;/p&gt;
&lt;p&gt;To jump back a little: in high school, I got ahead in math through basically just wanting to go faster. My parents were able to get me to take classes at a faster pace. By the end of junior year, I&amp;rsquo;d finished calculus. My senior year, I took some college math classes at a nearby college.&lt;/p&gt;
&lt;p&gt;So I was actually ahead—I had finished calculus basically before I got to college because I&amp;rsquo;d already taken it. As a result, in college I started to explore some higher math stuff and was kind of enjoying physics, but I really didn&amp;rsquo;t enjoy the labs and hands-on side of it. More and more, I was like, &amp;ldquo;I just really love math classes.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;But I was also enjoying—I&amp;rsquo;d taken a computer programming class my freshman year, a C++ class. Despite being very different from what I&amp;rsquo;d done with PHP in high school, it really set me up for understanding what I was getting in C++. So that was fun and really scratched the problem-solving itch.&lt;/p&gt;
&lt;p&gt;My sophomore year, I decided to change to computer science.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; So you went in as a math major?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I applied as computer engineering, went in as physics, and then a year later changed to computer science.&lt;/p&gt;
&lt;p&gt;I continued to take some physics classes on the side and finished a physics minor. But sophomore year in computer science, you take &lt;strong&gt;theory of computing&lt;/strong&gt;, which is basically a math class—you&amp;rsquo;re trying to prove things that are computable, doing Turing machines, NP problems, that kind of stuff.&lt;/p&gt;
&lt;p&gt;I really loved that class—I thought it was really fascinating. The professor I had for my first programming class and also that theory class was a former mathematician, which is probably relevant. But then I realized all the computer science majors hated that class. They just wanted to be programming. I wasn&amp;rsquo;t just enthusiastic about programming or wanting to write programs necessarily, but I was interested in the problem-solving.&lt;/p&gt;
&lt;p&gt;So as I was taking it and realizing it was a math class, I was like, &amp;ldquo;I should just really be a math major.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Did you change again?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. Freshman year I was a physics major, sophomore year I was a computer science major, and then junior year I changed to math. As a math major it was a liberal arts degree, so I had to take a language at my undergrad. I ended up my junior year starting freshman French—which I had to do four semesters of with these freshmen as a junior.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; How&amp;rsquo;s your French now?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; It&amp;rsquo;s horrible, and I still have nightmares about French class. I literally wake up in the night sweating that I&amp;rsquo;m missing a French test or something.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="liberal-arts-education-and-history"&gt;Liberal Arts Education and History
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Did your school have gen ed courses? Did you take something that you might not have thought of before that maybe you gained an appreciation for?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. It&amp;rsquo;s a liberal arts school, and we had to do kind of a core liberal arts curriculum—really good stuff like history, art, and some basic philosophy. Being a Christian school, there was a Bible component also.&lt;/p&gt;
&lt;p&gt;It got me interested in philosophy for sure, and it helped develop my interest in history, which is one of my biggest side interests now. Before college, I had zero interest in history.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I feel like it&amp;rsquo;s something you gain an appreciation for over time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, and I can&amp;rsquo;t necessarily point to one thing particularly. It broadened my world in a lot of ways and got me to think about a lot of things.&lt;/p&gt;
&lt;p&gt;I ended up liking one of the history professors a lot. He also taught philosophy of education, so I took his philosophy of education track that&amp;rsquo;s normally just education majors. It helped me think a lot about the purpose of education.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;His big thing was that the purpose of education is really &amp;ldquo;what&amp;rsquo;s the purpose of people?&amp;quot;—and our education should help shape people towards that.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Do you have anything you walked away from that you carry with you today?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, I think that point I just made. As you think about education—I have four children now—what you&amp;rsquo;re trying to get out of it&amp;hellip; is education just trying to make people better workers in a capitalist society? I think there is value in being a productive contributor to society, but it&amp;rsquo;s much bigger than just &amp;ldquo;you go and work.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;It really helped me think of education—and thus parenting—as shaping humans. What&amp;rsquo;s the full picture of what we want our humans to be? For my kids&amp;rsquo; school, it&amp;rsquo;s not just about &amp;ldquo;is what they&amp;rsquo;re doing valuable?&amp;rdquo; The question is not necessarily &amp;ldquo;when are they ever going to use this?&amp;rdquo; but &amp;ldquo;is their mind being opened to be people who understand the world and are interested in the world and interested in people?&amp;rdquo; Hopefully having a bigger-orbed perspective on education.&lt;/p&gt;
&lt;p&gt;And recognizing that education doesn&amp;rsquo;t just mean schooling. I don&amp;rsquo;t know if it&amp;rsquo;s a real quote, but there&amp;rsquo;s an often-quoted Mark Twain line: &amp;ldquo;I don&amp;rsquo;t let my schooling interfere with my education.&amp;rdquo; The conversations you have with your kid for 20 minutes before bedtime can be just as much of their education as their math lessons.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; In D&amp;amp;D, it&amp;rsquo;s like the difference between wisdom and intelligence.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, I think that&amp;rsquo;s a real thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; We&amp;rsquo;ll come back to the kids and parenting—I&amp;rsquo;ve got some questions there I&amp;rsquo;d like to get more of your perspective on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Also, after college I started a program in the history of math as a PhD program. I only stayed in for a year, but that was definitely a result of my liberal arts education—being interested not just in things as &amp;ldquo;what labor produces value that makes money&amp;rdquo; but just ideas.&lt;/p&gt;
&lt;p&gt;Some of my professors helped me be interested in history of math, but it was also my own personal development. Being curious and interested in things came through my college experience and made me interested in that as a topic.&lt;/p&gt;
&lt;p&gt;I looked at programs like philosophy of science—a lot of departments are history and philosophy of science together. I actually took a class in history of science or philosophy of science in college as well. It just got me interested in exploring ideas for their own sake and not necessarily because they have a specific use for anyone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Do you have a particular period of history that you&amp;rsquo;re most interested in or find yourself studying more?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; People can fast forward like 10 minutes if they want to get back to the interesting part of the podcast.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I&amp;rsquo;m here to have a conversation with you, so&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I&amp;rsquo;m really interested in the history of science and history of math. It&amp;rsquo;s been less of something I&amp;rsquo;ve pursued since my early twenties when I started this PhD program. I ended up leaving after a year for a number of reasons, but partially because I was like, &amp;ldquo;Okay, it&amp;rsquo;s good to be interested in ideas, but I also want to eat for the rest of my life, so I should be thinking about what&amp;rsquo;s going to give me a job.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;In that program, I was interested in like applied math in the 20th century. So much happened around—a character I was interested in is like &lt;strong&gt;Von Neumann&lt;/strong&gt;, who was such a massive figure, influential in the Manhattan Project but also shaped so much of modern mathematics. The 20th century math is really interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I think you&amp;rsquo;re a fan of Claude Shannon too?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, for sure. I think that kind of stuff is just so remarkable—like Bell Labs and so many players in the Manhattan Project, for better or worse. It&amp;rsquo;s really fascinating. And Feynman—I used to read Feynman&amp;rsquo;s books back in the day. That era really fascinated me.&lt;/p&gt;
&lt;p&gt;But also earlier stuff—all the greats: Gauss, Euler, Fermat, Fourier. All of that 18th, 19th century stuff is really intriguing too.&lt;/p&gt;
&lt;p&gt;These days, my interest is much more in &lt;strong&gt;American religious history&lt;/strong&gt;. I&amp;rsquo;m interested in my own religious tradition, which is Presbyterianism and Presbyterian history in particular. But I&amp;rsquo;m also just interested in the socio-historical questions: how Christianity—primarily, not exclusively—has developed in America, and what the American experiment has enabled in terms of getting so many variants of Christianity. What kind of cultural, social, and theological perspectives caused that to develop?&lt;/p&gt;
&lt;p&gt;My weird nerd habit is I love to drive around—I&amp;rsquo;m going to have to get some new skills here in Indiana—but in North Carolina, I&amp;rsquo;d drive around, see country churches, and think about what immigration and different religious movements led to that particular type of church being in a specific area.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s kind of my guilty pleasure hobby: just thinking about those ideas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Are you exploring the history of how things are founded and built and grow and change, or different theological perspectives of why there are different denominations?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; It&amp;rsquo;s very intertwined. Both dimensions influence each other. You definitely have the theological thing, but the theological influences aren&amp;rsquo;t necessarily separated from the cultural or immigration patterns.&lt;/p&gt;
&lt;p&gt;If you just look at the US broadly, the places Germans immigrated, you have Lutheran churches. The places Scots-Irish immigrated, you have Presbyterian churches. The places the English immigrated, you have Episcopal churches. And then all of that also morphed into the plethora of Methodist and Baptist churches.&lt;/p&gt;
&lt;p&gt;The American religious landscape in Christianity and its variants—not to mention all other religions—is so wildly diverse in a way that would have been unimaginable 300 years ago. I just think it&amp;rsquo;s really fascinating to think about why and how that happened.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s what I lay in bed and think about at night to take my mind off other things. I actually just became the official webmaster of the North Carolina Presbyterian Historical Society.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Just in time to leave.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, well, I was like, &amp;ldquo;I&amp;rsquo;m moving. I can still do it if you want.&amp;rdquo; I&amp;rsquo;ve been spending the last few weeks, aside from moving, trying to get several elderly people to help me find the password to change the DNS to fix the website. It&amp;rsquo;s been my task of late—nobody knows the password to log in.&lt;/p&gt;
&lt;p&gt;My family is part Presbyterian for 500 years. So there&amp;rsquo;s also a personal family history aspect of it to me, which is really how it started—as an adult, learning more about grandparents and great-grandparents that I didn&amp;rsquo;t know. Piecing together their stories with American history is fascinating to me.&lt;/p&gt;
&lt;p&gt;Anyway, that&amp;rsquo;s a topic of interest to a limited audience, but there you go.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="graduating-and-the-math-prize"&gt;Graduating and the Math Prize
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; All right, well then we&amp;rsquo;ll come back to the mainstream. So you finally decided to switch to being a math major. Did you graduate as a math major?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I did, yeah. I graduated math with highest honors, which no one has ever cared about since. I worked too hard to be a summa cum laude math major.&lt;/p&gt;
&lt;p&gt;Actually, there was a cash prize for the highest average grade math major, and I shared it with another guy. I wouldn&amp;rsquo;t have if I had studied for my real analysis final—if I had studied for it and not gotten a B, I would have gotten the full cash prize. But instead I shared it with another guy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Well, like most things, it&amp;rsquo;s best shared, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Probably.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Being a math major, do you have opinions on brands of chalk, or chalk versus whiteboard?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; That&amp;rsquo;s an interesting question because I&amp;rsquo;ve always hated handwriting—from when I was a child. Being homeschooled, my mom had a hard time with me hating handwriting. She used to ask, &amp;ldquo;What are you going to do when you need to write a note when you&amp;rsquo;re older?&amp;rdquo; And she says when I was pretty young, I told her I would just type it on the computer. Now she just laughs about that because that&amp;rsquo;s all we ever do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; That&amp;rsquo;s what we call a thought leader.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Again, it&amp;rsquo;s really laziness. I still hate handwriting. I just had to sign documents to move, and they&amp;rsquo;re like, &amp;ldquo;Oh, well, it&amp;rsquo;d be good if your signature kind of spelled your name.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I do like, in theory, writing on a board—I know mathematicians are big about chalk—and I love teaching math. But I&amp;rsquo;ve never been that great at it, and I find it very tiring. I&amp;rsquo;ve never been into ordering chalk from Japan or something.&lt;/p&gt;
&lt;p&gt;When I did teach math, I was very poor as a grad student. An undergrad experience that was meaningful for me: the summer after my junior year, I did a &lt;strong&gt;REU program&lt;/strong&gt;—Research Experience for Undergrads, an NSF-funded research program.&lt;/p&gt;
&lt;p&gt;That was meaningful partially because I got there wanting to do a math program, and my advisor&amp;rsquo;s first question when I got there was, &amp;ldquo;How good are you at programming?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;All we did all summer was write C programs for computational graph theory—generate all these graphs and try to find the properties you want in the graphs. I went there to do math and did programming all summer, which was an impactful experience for me.&lt;/p&gt;
&lt;p&gt;Then I came back to college my senior year, and they wanted me to do a research presentation. My roommate and one of my friends who&amp;rsquo;s like a brother to me—Todd—actually came and coached me on writing on a chalkboard because I wanted to do a good job. He stood behind me and critiqued me as I tried to give my presentation so that I would be able to do a better job.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Just for the way it looked, handwriting-wise and composition?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, and just making sure it&amp;rsquo;s not sloping down to the side and that kind of thing. My handwriting&amp;rsquo;s not good, my spelling&amp;rsquo;s not good. It&amp;rsquo;s really not a good thing for me to be doing.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="grad-school-history-of-math-at-uva"&gt;Grad School: History of Math at UVA
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; So math major, then grad school. Was that something you wanted to do immediately, or were you convinced to do?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Ever since I thought &amp;ldquo;I&amp;rsquo;ll study physics in college and be a physics professor,&amp;rdquo; I was like, &amp;ldquo;Well, I&amp;rsquo;ll just go to grad school. I like school, I&amp;rsquo;m good at school.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Was this still the mindset of going to be a professor?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, I think that&amp;rsquo;s really what was in my mind. For undergrads who really enjoy the experience—which I did—it&amp;rsquo;s very tempting to think your professors have the dream. They&amp;rsquo;re the pinnacle. So I was going to go to grad school for math.&lt;/p&gt;
&lt;p&gt;I considered a variety of things: math, logic in the philosophy department. I visited the University of Pittsburgh&amp;rsquo;s History and Philosophy of Math department. But then I applied and surprisingly got accepted to the &lt;strong&gt;University of Virginia&amp;rsquo;s History of Math program&lt;/strong&gt;, which seems very esoteric. It&amp;rsquo;s only one professor and she&amp;rsquo;s very selective, so it was kind of surprising that I got selected. I probably shamed her by dropping out after a year.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s part of the math department. Your PhD is basically a math PhD, but you write a dissertation on a history topic. You take the math courses, the math qualifying exams, and then do history stuff on top of it, plus two foreign languages.&lt;/p&gt;
&lt;p&gt;I applied to that and got accepted. But during college, I had gone every spring during spring break for four years—college students went on different mission trips around the world. I went with a group to a &lt;strong&gt;children&amp;rsquo;s home in Tennessee&lt;/strong&gt;, a Christian children&amp;rsquo;s home. We just went and did manual labor projects to help them—building trails and steps, cleaning out barns, that kind of stuff.&lt;/p&gt;
&lt;p&gt;I had a really good experience doing that. It&amp;rsquo;s a small children&amp;rsquo;s home for children in crisis situations, not of their own making—so not kids who have been particularly bad kids, but they&amp;rsquo;re from bad situations. They take students either on summer breaks during college or often right after college to come work for one to three years. You live in the home with the kids and help teach them, cook for them, and just be there with them.&lt;/p&gt;
&lt;p&gt;I asked the University of Virginia if I could defer my admission for a year, which is pretty common in graduate programs. They said I could. So I went to work at this children&amp;rsquo;s home from 2008 to 2009 for 12 months. I lived in a house with nine boys, middle-school-age boys, and did all that—they had a kind of hybrid homeschool model. They come in with a lot of different situations, and their schooling isn&amp;rsquo;t necessarily grade level. So you&amp;rsquo;re just teaching them to what their ability is—math, history, English, reading.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; That sounds really humbling.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; It was, yeah. My uncle said a very helpful thing to me: &amp;ldquo;Tim, when you go to grad school, you&amp;rsquo;ll learn how to be a mathematician. But if you do this, I think you&amp;rsquo;ll learn how to be a man.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a lot to that. Even now being a parent, it was a really helpful experience—and very hard, very tiring. But I&amp;rsquo;m really glad I did that. It was in the Smoky Mountains in Tennessee, like a half mile from the Smoky Mountain National Park.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Do you keep in touch with any of them?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Some, off and on through the years. It&amp;rsquo;s been a while. A few of them got dismissed, not necessarily in good situations—although actually one of those who got kicked out was messaging me on Facebook maybe 18 months ago. We had some exchanges. I actually just found out recently one of them died, which was really sad to hear.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve connected with a few of them on Facebook. They&amp;rsquo;re all like in their probably 30s now, which is kind of crazy. You freeze them in your mind as middle schoolers, but they&amp;rsquo;re grown-ups with kids and things.&lt;/p&gt;
&lt;p&gt;So I did that for a year. At the same time, I was practicing my French because for this history of math program, you have to have reading competency in two foreign languages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I was thinking like Latin or something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Latin could be one of them. I wasn&amp;rsquo;t interested in stuff that old necessarily. But I was practicing French reading—reading is a lot easier than speaking or hearing French. I can&amp;rsquo;t understand any spoken French. I know you&amp;rsquo;re speaking French, and that&amp;rsquo;s all I can tell.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; My grad professor was from France.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. So I did that, and then I moved straight to Charlottesville after a year and started this history of math program. I did pretty well.&lt;/p&gt;
&lt;p&gt;My education was really good, but it was tough going up against students who had gone to bigger universities. You find out people started having taken graduate classes as undergrads, or they sat in on graduate classes, which was something I didn&amp;rsquo;t have the opportunity for at all. They just had more exposure to things.&lt;/p&gt;
&lt;p&gt;But my grad school experience really convinced me my undergrad gave me a really solid foundation and taught me how to think—especially one professor, Dr. Thompson, who intentionally made his undergrad classes similar to graduate classes. He would give take-home tests with really open-ended questions where we were learning new material on the take-home test. That was really good preparation for grad school.&lt;/p&gt;
&lt;p&gt;But it was still very hard. While I was there, I was kind of having something of an existential crisis around &amp;ldquo;what am I doing with my life?&amp;rdquo; and realizing I had just been going to grad school thinking &amp;ldquo;this is what I&amp;rsquo;m going to do.&amp;rdquo; I hadn&amp;rsquo;t really thought through the implications of it.&lt;/p&gt;
&lt;p&gt;I started to realize the history of math isn&amp;rsquo;t actually that marketable of a degree to have, and you spend half your 20s getting it. Former students of my advisor, who I got to meet—they were great—but I was like, &amp;ldquo;Okay, so I&amp;rsquo;m gonna do this and then maybe hopefully get to teach at some podunk university? Maybe that&amp;rsquo;s not the best use of my time.&amp;rdquo;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="discovering-operations-research"&gt;Discovering Operations Research
&lt;/h3&gt;&lt;p&gt;Somewhere along—late in college—I discovered &lt;strong&gt;operations research&lt;/strong&gt; as a discipline: using math and optimization models to solve business and industrial problems. I discovered that through Cornell&amp;rsquo;s program. While I was looking at grad schools, I found it and was like, &amp;ldquo;Oh, this sounds really interesting.&amp;rdquo; It matched a lot of how I think about things—optimizing things, efficiency, math, and computation all in one.&lt;/p&gt;
&lt;p&gt;I basically decided to leave halfway through my first year at UVA. I applied to an OR program at UVA, one at North Carolina State University, and a master&amp;rsquo;s program at University of Tennessee—mostly just because I loved that area.&lt;/p&gt;
&lt;p&gt;For a variety of reasons, I decided to go to North Carolina State. My parents had moved from West Virginia to Greensboro, North Carolina—an hour and a half away—when I was in college. Two of my sisters actually lived in Durham, which is one city over from Raleigh where NC State is. For various other reasons—I knew some other people down there—I decided to go to NC State for a PhD program in operations research.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Did you go straight into a PhD from undergrad, or did you do a master&amp;rsquo;s?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I didn&amp;rsquo;t do a master&amp;rsquo;s. In the US, a PhD program has you take the master&amp;rsquo;s coursework for the first two years, but I didn&amp;rsquo;t do an independent master&amp;rsquo;s ahead of that.&lt;/p&gt;
&lt;p&gt;So I graduated from college in &amp;lsquo;08, went straight to work at the children&amp;rsquo;s home, one year later started at UVA as a PhD student. But for the first two years, you&amp;rsquo;re doing the coursework.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="teaching-calculus"&gt;Teaching Calculus
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Was your teaching before you started the PhD program, or was that part of your deal there?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I was a TA my first year, but usually you don&amp;rsquo;t teach your first year—you&amp;rsquo;re an assistant to another grad student who&amp;rsquo;s teaching. I was a TA for calculus one and two, where you&amp;rsquo;re grading and having office hours but not actually lecturing.&lt;/p&gt;
&lt;p&gt;But that summer, I was lecturing, not having ever done it before. Actually, there&amp;rsquo;s a funny story—to me anyway. I&amp;rsquo;d actually never taken calculus two. In high school, I took calculus early like I said, but I did the AB calculus test because that&amp;rsquo;s all we had. We didn&amp;rsquo;t offer calc two. My senior year, I signed up for calculus three at that local college. Then I got my undergrad to waive the fact that I&amp;rsquo;d never taken calc two, where you learn all these tricks for integration like trig substitution. I didn&amp;rsquo;t know any of that.&lt;/p&gt;
&lt;p&gt;By the time I TA&amp;rsquo;d, I had mostly learned it. But this girl came to my office one day when I was a TA with a question I did not know the answer to at all. I knew nothing about calc two. She actually ended up dropping the class after she came to my office, which I feel horrified about—that I ruined her life because I was doing something I was unqualified for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I think calculus did that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; But when I taught calc two, I learned. Teaching a week&amp;rsquo;s worth of material in a day is not only hard for the students—it&amp;rsquo;s hard for me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Did you have to come up with the curriculum and lessons too?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, basically the grad students had all these PDFs of notes they would pass around to each other. I would get somebody&amp;rsquo;s notes, but you&amp;rsquo;d go teach for four hours in the morning and then go home and grade and get ready for the next day. It was very intense.&lt;/p&gt;
&lt;p&gt;But I learned so much calculus standing at the board, writing in chalk, explaining it. I&amp;rsquo;m like, &amp;ldquo;Oh, that&amp;rsquo;s what that means&amp;rdquo;—quietly to myself so the kids don&amp;rsquo;t think I don&amp;rsquo;t know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Quietly to yourself so the kids don&amp;rsquo;t think, you know&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Right. But discovering a very good life lesson that teaching is such a great way to learn things. As you know, I still love teaching things when I work—trying to teach my colleagues, and through various online resources I make. I still love teaching. People think I enjoy other people learning, but it also helps me learn as well.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="nc-state-and-operations-research"&gt;NC State and Operations Research
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; So I went to North Carolina State, started doing classes again. They didn&amp;rsquo;t really care that I had been in a PhD program for a year previously. I didn&amp;rsquo;t get a degree or anything from UVA.&lt;/p&gt;
&lt;p&gt;The motivation for doing operations research was: okay, here&amp;rsquo;s interesting problem-solving but also something that seems like it has jobs related to it because it&amp;rsquo;s useful. I don&amp;rsquo;t know if that&amp;rsquo;s actually true for operations research, but at least I was learning useful skills.&lt;/p&gt;
&lt;p&gt;I was just doing my coursework, and then I found an advisor after a year. He asked me if I wanted to work with him. He did healthcare optimization stuff—treatment optimization. You use these mathematical models to figure out, like, what&amp;rsquo;s the optimal schedule to give a chemotherapy drug to treat cancer? He also did healthcare operations problems—like, how do you schedule your patients most efficiently so they&amp;rsquo;re not sitting in the waiting room for a long time?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Would that be from clinical trial data, trying to build out those models?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; It can be from a variety of things. They use real data, they make stuff up. I ended up doing the operations side—we partnered with the oncology clinic, collected real data on patient wait times, and built a &lt;strong&gt;simulation model&lt;/strong&gt; of the oncology clinic.&lt;/p&gt;
&lt;p&gt;Another part of operations research besides optimization is simulations—discrete event simulations. If you can build a realistic simulation of the situation, then you can modify the simulation and see if you can remove bottlenecks. Similar to Six Sigma and that kind of business optimization stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Is that what brought about the PhD downfall with Six Sigma?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; No, I ended up working on semi-theoretical problems. My advisor was an expert in something called &lt;strong&gt;stochastic integer linear programming&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A big part of operations research is optimization problems where you have a mathematical formula you&amp;rsquo;re trying to optimize with mathematical constraints. You can describe business problems that way. But stochastic optimization says, &amp;ldquo;Oh, I&amp;rsquo;m not just optimizing these, but maybe my coefficients are random variables.&amp;rdquo; So I want to minimize the worst outcome or maximize the average or something—which is mathematically significantly more complicated.&lt;/p&gt;
&lt;p&gt;He worked on a problem on patient scheduling. When you call your doctor and make an appointment, they&amp;rsquo;re not operating under certainty—they don&amp;rsquo;t know who else is going to schedule. So trying to quantify that uncertainty and make the best decision. He made this huge theoretical optimization problem that he could only solve for &lt;strong&gt;three patients&lt;/strong&gt;. So if your clinic sees a total of three patients, you can get the optimal solution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; We call it the three-body problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; So I worked on essentially an approximation solution using what&amp;rsquo;s now called &lt;strong&gt;reinforcement learning&lt;/strong&gt;—computer scientists had always called it that, but in operations research they called it approximate dynamic programming. It&amp;rsquo;s essentially the same thing: you simulate possible outcomes and use that to figure out your optimization.&lt;/p&gt;
&lt;p&gt;Which was good. And another strain going on at the same time: in late 2009, I had gotten on Twitter. As a lazy, procrastinating grad student, I spent a lot of time on Twitter. &lt;strong&gt;Data science&lt;/strong&gt; in that era was becoming a thing, in no small part because of people blogging about it and talking about it on Twitter. I was like, &amp;ldquo;Oh, this is really interesting—they&amp;rsquo;re using data and computation and math to solve real problems.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;This machine learning stuff sounded really interesting. I didn&amp;rsquo;t have any exposure to that in college or before.&lt;/p&gt;
&lt;p&gt;Because of the way my program was at NC State, I was able to start taking some machine learning coursework. My research was essentially turning optimization problems into machine learning pattern recognition problems—simulating all these possible outcomes and using machine learning models to learn those patterns, then figuring out how to optimize over that.&lt;/p&gt;
&lt;p&gt;This is 2010 to 2012, learning machine learning and data science stuff as it was becoming relevant in a way it had never been before. That was partially luck, but also me trying to shape my curriculum into things that seemed relevant in industry and useful to me. That helped set the stage for a lot of stuff.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="kiva-systems-internship"&gt;Kiva Systems Internship
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; That summer—after one year at NC State—I talked my way into an internship at &lt;strong&gt;Kiva Systems&lt;/strong&gt;, which is now Amazon Robotics, doing warehouse automation. I read about it over Christmas break in &lt;em&gt;Wired Magazine&lt;/em&gt;, and found out the head of research at Kiva Systems was Pete Werman, who was an NC State professor before going to work for his old friend at Kiva Systems.&lt;/p&gt;
&lt;p&gt;I cold-emailed him and was like, &amp;ldquo;Can I come work for you for the summer?&amp;rdquo; I turned that into an internship, which is wild that he hired me, having only talked on the phone. I drove up to Boston that summer, met him on my first day of work, and worked on simulation models for warehouse optimization.&lt;/p&gt;
&lt;p&gt;That gave me exposure to real-world applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Applications, yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Optimization and basically operations research in the real world. A good taste. And it paid better than grad school did, which is another good taste of the future.&lt;/p&gt;
&lt;p&gt;So I did that, went back, and kept going with grad school my second year. After I came back from Boston, all my free time that summer I&amp;rsquo;d been studying for my qualifying exams. I came back and passed all my qualifying exams, which is one of the big steps towards the PhD.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; That&amp;rsquo;s an accomplishment in and of itself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. I&amp;rsquo;ve always been good at tests, which is a completely useless skill, but it helps in school.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I mean, some people say life is a test.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I don&amp;rsquo;t think taking math tests is a good life skill, but I am good at it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="leaving-the-phd-program"&gt;Leaving the PhD Program
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I came back and did research for a year. Then the following May—after the end of my second year—my advisor called me into his office and said, &amp;ldquo;I&amp;rsquo;m taking a job at the University of Michigan in Ann Arbor. I would like for you to stay here at NC State and continue working with me on your PhD.&amp;rdquo; He was going to have a guest appointment so he could continue to advise me.&lt;/p&gt;
&lt;p&gt;I was like, &amp;ldquo;Okay, that&amp;rsquo;s fine. I&amp;rsquo;ll do that.&amp;rdquo; I kept working with him through the summer. The summer was a very lonely time in the office—just staring at a computer in this sterile office doing research. He was gone, actually in China for a lot of that summer. There was nothing to break up the day. I wasn&amp;rsquo;t teaching. I would go to the gym—which is something that became a hobby in my adult life—but it was a very lonely and somewhat depressing time.&lt;/p&gt;
&lt;p&gt;By August, I was like, &amp;ldquo;He&amp;rsquo;s not even good at working with me from across the hall. How is he going to work with me from Michigan to North Carolina, which is far away?&amp;rdquo; There were other students going to have the same arrangement with him, but I was the newest student, so I was going to be in it for the longest—probably at least three more years if I&amp;rsquo;m lucky. That&amp;rsquo;s just not a good situation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; You&amp;rsquo;re going to be there for all the learning and growing pains of trying to do that remotely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; For at least three more years. And I was not making much money—the money at NC State was worse as a PhD student than at UVA. I was making like $17,000 a year or something. I didn&amp;rsquo;t have that many expenses, but that&amp;rsquo;s not a lot of money.&lt;/p&gt;
&lt;p&gt;So that August, I started looking for jobs—mostly locally but in the data science-ish realm. I was doing that totally on the side. I didn&amp;rsquo;t tell my advisor because I needed him to keep paying me in the meantime, and he was in Michigan, so it was okay. I was taking one class that last semester—a &lt;strong&gt;Bayesian networks class&lt;/strong&gt;, like a machine learning class.&lt;/p&gt;
&lt;p&gt;Another part of this story: over those two years of getting interested in data science stuff, I started teaching myself &lt;strong&gt;Python&lt;/strong&gt; and &lt;strong&gt;R&lt;/strong&gt; as well—which I learned about from Twitter. I did my research in Python, which my advisor didn&amp;rsquo;t know and he didn&amp;rsquo;t want me to do. He wanted me to do C++, which he knew. But I was like, &amp;ldquo;Oh, Python&amp;rsquo;s the up-and-coming thing, and it has this thing called scikit-learn where you can access all these machine learning models&amp;rdquo;—which we could use for our reinforcement learning. That was really good that I did that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; You&amp;rsquo;re very proactive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I tried to be. I really think I just got lucky in so many ways.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Well, that&amp;rsquo;s not without effort, you know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; My effort was not always—I didn&amp;rsquo;t always know what I was doing. I don&amp;rsquo;t think it&amp;rsquo;s always good advice, like &amp;ldquo;just follow your interests and it&amp;rsquo;ll work out.&amp;rdquo; But for me, it did in a lot of ways.&lt;/p&gt;
&lt;p&gt;I interviewed for a variety of jobs. I got offered one at a local chain of retirement homes in North Carolina. They had hired a grad student to build optimization models for their pricing structure of their nursing homes. She was rolling off the project, and they wanted somebody to do it full time. They offered me a job—for the amount of money they thought they were going to make, they didn&amp;rsquo;t offer me very much money.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; But still more than grad school was paying.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. I also got offered a job at &lt;strong&gt;RTI International&lt;/strong&gt;, which is a nonprofit research company in the Raleigh-Durham area. They do government contract stuff—not defense, but kind of humanitarian government stuff: education, economics, health.&lt;/p&gt;
&lt;p&gt;I think some executive had read an article in some business magazine about data science and thought, &amp;ldquo;We should hire a data scientist.&amp;rdquo; They basically hired me out of hype, when it was a company that was essentially doing data science for 50 years—statisticians, programmers, doing data analysis, surveys, all this stuff. They didn&amp;rsquo;t really know what they were hiring me for.&lt;/p&gt;
&lt;p&gt;They offered me that job in October of 2012, and I decided to take it because it seemed less depressing than working in the nursing home administrative office.&lt;/p&gt;
&lt;p&gt;So then I called my advisor. I was like, &amp;ldquo;Oh, I&amp;rsquo;m dropping out, by the way.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; How did he take that news?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I don&amp;rsquo;t think he was totally surprised. I think he was probably disappointed that I hadn&amp;rsquo;t led on to it anymore. But I just had to protect myself at that point.&lt;/p&gt;
&lt;p&gt;So I finished up that semester, finished up the last class, and started working late October of 2012. I&amp;rsquo;ve been in industry ever since then.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="meeting-maggie-and-getting-married"&gt;Meeting Maggie and Getting Married
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; What year was this?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; 2012, yeah. October 2012.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; When did you meet Maggie?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I didn&amp;rsquo;t meet Maggie until a few years later. We got married in August of 2015.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; It was right before Distill then, right?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, literally right before Distill. To jump ahead a little bit, right before I got married, I found out I was needing a new job three weeks before I got married—which was a crazy time. And then came to work with you at Distill.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I didn&amp;rsquo;t realize those events were that close together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; We got married in August. I started working there in October, I believe. But three weeks before my wedding, my boss called me and was basically like, &amp;ldquo;The whole project is falling apart&amp;rdquo;—I worked on a DARPA project, government stuff.&lt;/p&gt;
&lt;p&gt;At that point before Maggie, I was single, lived alone, ate a lot of peanut butter, gained weight. Not actually the best time of my life.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Well, you know, we all go through our ups and downs. So then—okay, Distill—and then I&amp;rsquo;m debating if we want to talk about your career, because&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; We can, it&amp;rsquo;s pretty public. It&amp;rsquo;s on my resume.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Sure. I&amp;rsquo;m more interested in terms of—at least for me, selfishly—I&amp;rsquo;d like to know more about you becoming a parent. In terms of hobbies, I know you were doing the workout stuff and strongman competitions at one point. And life outside of work. Let&amp;rsquo;s start there instead of going into career stuff. Life outside of work from that point.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="hobbies-woodworking-and-hiking"&gt;Hobbies: Woodworking and Hiking
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; One thing I discovered late in grad school: NC State has a place called the Craft Center. It&amp;rsquo;s a non-academic place for students to just go do stuff—and alumni. One thing they had is a wood shop. I actually got into &lt;strong&gt;woodworking&lt;/strong&gt; for a few years there, which I wish I had known about all through grad school. I think it would have been good for my mental health.&lt;/p&gt;
&lt;p&gt;Maybe my last semester I took a wood class. It&amp;rsquo;s an incredible wood shop—it really spoiled me because I could never have the setup they have there. For a little bit, they had a guy who was just a master woodworker teaching. I think that was actually right after I finished grad school—I continued to take some classes there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; What did you build there?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; In the classes, I made some basic stuff—a Shaker shelf, a little side table. I had been working on a big project to make a top for a standing desk, a big walnut top. I was also making a monitor stand. I never actually totally finished it, which is unfortunate. I was going to make these really cool swing-out drawers—I built nice walnut drawers that were going to be on heavy-duty pivot hinges, just pivot out on one point to open, and then mount it on standard standing desk legs.&lt;/p&gt;
&lt;p&gt;I still have the desktop—my 3D printer sits on it now—but I never finished the monitor hutch part. I learned a lot. Wood is such an interesting, complex medium to work in because it&amp;rsquo;s so precise but also—wood is very alive because of moisture things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; But it&amp;rsquo;s approachable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. It makes you learn to appreciate the ability to press the undo button, because there&amp;rsquo;s no undo button in woodworking.&lt;/p&gt;
&lt;p&gt;I also got more into &lt;strong&gt;hiking&lt;/strong&gt; in that era. I&amp;rsquo;d been in Boy Scouts, but I didn&amp;rsquo;t really love the outdoors when I was younger.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; What didn&amp;rsquo;t you like about it back then?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; That&amp;rsquo;s a good question. I just—it didn&amp;rsquo;t really&amp;hellip; I think part of it is I just had a very good childhood. When I used to golf with my dad, he golfed a lot to blow off steam, and I just didn&amp;rsquo;t need to blow off steam because I had a very stable home. School was very stable. I don&amp;rsquo;t think I needed to go blow off steam that much.&lt;/p&gt;
&lt;p&gt;But during college, I started to really enjoy the outdoors more. During and right after grad school, I got more into hiking. North Carolina just has some world-class scenery—not world-class height of mountains, but stunning scenery. I started to explore North Carolina more, the mountains more.&lt;/p&gt;
&lt;p&gt;That also became more of an opportunity as I had money to drive myself out to the mountains, stay in a hotel, and hike.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Speaking of driving, when did you get your Matrix?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I bought my &lt;strong&gt;Toyota Matrix&lt;/strong&gt; that I still drive in 2010. Right after I moved to Raleigh. I had been driving an old Mercury Mountaineer, which is like a Ford Explorer. It was falling apart—V8, all-wheel drive, horrible gas mileage—and gas had just gotten really expensive. I lived a long way from school and was like, &amp;ldquo;I can&amp;rsquo;t afford to keep driving this.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;So I bought a Toyota Matrix with 107,000 miles on it that I still drive now with &lt;strong&gt;260,000 miles&lt;/strong&gt; on it. It&amp;rsquo;s my pride and joy. I recently joined a Toyota Matrix owner&amp;rsquo;s Facebook group.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I had a Matrix! I had to get rid of it mainly because I was commuting 90 miles one way and it was a manual. Jolene couldn&amp;rsquo;t drive it, so we just got something else.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Mine&amp;rsquo;s the automatic, and the transmission has been rock solid.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; It&amp;rsquo;s such a good car. You can do all kinds of stuff—the seats go down and it&amp;rsquo;s got so much room.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I hauled 28 bags of mulch from Lowe&amp;rsquo;s last summer in the back. We just moved, and I packed an incredible number of boxes flat in there to take to the recycling center. I&amp;rsquo;m going to keep driving it until I can&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; When I took a fundamentals of engineering course, we did one of those newspaper bridges. We did this massive suspension bridge and had to put down the seats on one side so it would go from the back of the trunk to the front of the car to transport it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. Also right as I was finishing grad school, I became more involved in my church. I became the &lt;strong&gt;treasurer&lt;/strong&gt; for my church, which I did until three weeks ago—for a long time. And I became a deacon at the church. In the Presbyterian tradition, a deacon is someone who helps people who have financial needs or other material needs in the church or outside of the church. So I did that for a while.&lt;/p&gt;
&lt;p&gt;Those early days right out of grad school felt very busy—getting used to having a full-time job. I also started living by myself for the first time with no roommates. So I couldn&amp;rsquo;t blame anyone for the bathroom or kitchen not being cleaned.&lt;/p&gt;
&lt;p&gt;I actually like to cook a lot too. I don&amp;rsquo;t cook much anymore, but I cooked a lot through grad school and those early days. I enjoyed having people in my house—having friends over and cooking for them.&lt;/p&gt;
&lt;p&gt;And getting into reading more—kind of back into reading in that era, being more deliberate about it. Those were my good years of reading, kind of reading in solitude before having kids. Just having a quiet house to read in.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="photography-and-wildlife"&gt;Photography and Wildlife
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Photography also started interesting me more in that era too. My dad&amp;rsquo;s always been a big photographer, and I&amp;rsquo;d taken a photography class in high school but hadn&amp;rsquo;t really stuck.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Was it always nature photography in particular, or just photography in general?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; My dad has always been really good at photographing people and events—that had never really been my thing. But I think it started to come back as I started to get more into hiking and being out in nature.&lt;/p&gt;
&lt;p&gt;North Carolina has some world-class waterfalls. I would go to waterfalls and try to take pictures on my iPhone 5 or something—just worthless. Waterfalls are actually really hard to photograph, and maybe not worth photographing that much. But I was like, &amp;ldquo;How can I capture this?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Neutral density filter?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, I started to learn more about the techniques and bought different cameras.&lt;/p&gt;
&lt;p&gt;It was actually 10 years ago last weekend that I got into—after I was married—we had an ice storm come through in January of 2016. I had a Sony a6300 or something, a little crop sensor camera. I was photographing birds on our bird feeder on the ice during the ice storm, and Maggie saw I enjoyed it so much. She said, &amp;ldquo;You should get a nicer camera.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;That kind of kicked off my interest in &lt;strong&gt;wildlife photography&lt;/strong&gt;, which is now—I don&amp;rsquo;t know, we haven&amp;rsquo;t talked about photography as much in the last few years—almost exclusively my wildlife is my children at this point. Which is harder than any animal.&lt;/p&gt;
&lt;p&gt;But it&amp;rsquo;s still my love. I would love to do more wildlife stuff—even more than landscapes. I think wildlife is what I really love.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="becoming-a-parent"&gt;Becoming a Parent
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Speaking of wildlife and kids, let&amp;rsquo;s talk about becoming a parent. Did you always want to be a parent?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, I&amp;rsquo;ve always loved kids. In elementary school, my parents were foster parents. I didn&amp;rsquo;t have younger siblings, but my mom&amp;rsquo;s a pediatric nurse. So she was specifically a foster parent for babies with medical needs—that was her expertise.&lt;/p&gt;
&lt;p&gt;We had, not for long term—they&amp;rsquo;re mostly short term—but we had &lt;strong&gt;25 different foster kids&lt;/strong&gt; in my elementary school years. Some as short as like a weekend, and some for maybe months at a time—but not years.&lt;/p&gt;
&lt;p&gt;My mom was kind of the revolving door of child care for her friends—her friends&amp;rsquo; kids were all at our house all the time. So I was just around a lot of kids growing up, and that was a good experience for me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Do you think that might have influenced or motivated you to do the children&amp;rsquo;s home stuff?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, probably. Those were older kids—middle-school age—kind of a different demographic in a lot of ways, but I&amp;rsquo;m sure that was influential.&lt;/p&gt;
&lt;p&gt;I always hoped I would have kids. I was around a lot of big families growing up. I grew up in a family of four, which is a big family—it&amp;rsquo;s above average.&lt;/p&gt;
&lt;p&gt;Maggie and I got married in August of 2015.&lt;/p&gt;
&lt;p&gt;Another kind of crazy story you probably don&amp;rsquo;t know: Maggie&amp;rsquo;s older sister had married one of my best friends from college two years earlier. That&amp;rsquo;s how we met. My brother-in-law is one of my closest friends from college. She had grown up in a big family as well and always wanted to have kids.&lt;/p&gt;
&lt;p&gt;We had our first, James, two years and one day after our wedding. We checked into the hospital on our second anniversary—he was a week late.&lt;/p&gt;
&lt;p&gt;And now we have four: eight, six, almost four, and two. Boy, boy, girl, girl—kind of in the even gap in this little window. Which is a good distribution. It&amp;rsquo;s crazy—you could not have planned it any better.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s really fun to see the kids get to enjoy each other. They all enjoy each other, but the boys really pair off and do a lot of boy stuff, and the girls pair off and giggle and talk about princesses a lot. It&amp;rsquo;s very fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I&amp;rsquo;m curious about your thoughts and philosophies of parenting. You had a pretty happy, healthy childhood. What are some things you take away from your parents that you try to mimic with your kids?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I think one thing with my parents is I always knew that they loved and supported us and just had our backs. That&amp;rsquo;s a big thing we want our children to see—that we&amp;rsquo;re there for them.&lt;/p&gt;
&lt;p&gt;We also want them to know we&amp;rsquo;re in charge. I&amp;rsquo;m not as much into some of the modern parenting that&amp;rsquo;s more like you&amp;rsquo;re chummy with your children. We really enjoy the time with them, but we are in charge. They hopefully really know that we&amp;rsquo;re supporting them.&lt;/p&gt;
&lt;p&gt;As I was describing my college situation and liberal arts education—really opening their minds—my kids are homeschooled for right now. Maggie teaches them at home, the boys anyway—they&amp;rsquo;re eight and six.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re trying to make education not just a really rote thing—&amp;ldquo;here you just have to follow these steps&amp;rdquo;—or just learning things that are useful in the sense of &amp;ldquo;you&amp;rsquo;re going to be able to get a job one day.&amp;rdquo; I think it&amp;rsquo;s good to be able to get a job—we&amp;rsquo;ve talked about that. But at the same time, we want them to really understand and think deeply and understand the world and even just enjoy the world.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s teaching your kids to just enjoy things. Liberal arts education—often that&amp;rsquo;s in literature and stuff, really enjoying it. For me, having studied math, I just intrinsically enjoy math. I think it&amp;rsquo;s really beautiful—people hear that and think &amp;ldquo;oh, arithmetic&amp;rdquo;—and arithmetic is in its own way—but math at a higher level is past arithmetic.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I think math is intrinsically a beautiful thing, and we&amp;rsquo;re trying to teach the kids to embrace that beauty of things.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; The further you go into something, the more nuanced it becomes. The more you appreciate the challenges or perspectives, and it broadens your perspective in general.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. Feynman said something like—to paraphrase—nothing&amp;rsquo;s boring enough if you go deep enough into it. I don&amp;rsquo;t know if that&amp;rsquo;s totally true, but there&amp;rsquo;s a lot of truth to that.&lt;/p&gt;
&lt;p&gt;You and I both got into &amp;ldquo;data science&amp;rdquo; interested in doing more of the interesting machine learning models. Both of us have done a lot more infrastructure stuff. I know you probably do some model stuff, but I exclusively do infrastructure-type engineering things now.&lt;/p&gt;
&lt;p&gt;I gave a talk about this a couple of years ago, but learning to embrace the beauty even of that has been a formative thing for me. I want to instill that in my kids—not about programming necessarily, but just about the things they do.&lt;/p&gt;
&lt;p&gt;With all the hardships and evil in the world, there&amp;rsquo;s such a great world to live in. So many beautiful things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; There&amp;rsquo;s a Brandon Sanderson book, and there&amp;rsquo;s a quote I really liked. It&amp;rsquo;s something along the lines of: &amp;ldquo;It&amp;rsquo;s a difficult time to live. It doesn&amp;rsquo;t have to be a difficult time to love.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. There&amp;rsquo;s a C.S. Lewis quote along the same lines—where he&amp;rsquo;s defending writing things during World War II. He basically talks about, you know, if the bombs fall on us while we do it, we should just be making the most of it while the bombs fall.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t know if that&amp;rsquo;s my political philosophy necessarily, but I do try to look at the world that way. Even in the drudgery of my own work, I try to really embrace the beautiful parts of it.&lt;/p&gt;
&lt;p&gt;We both love our different hobbies. Hobbies are an opportunity to just enjoy good things—even if you&amp;rsquo;re not sharing it, even if nobody knows about it. You do all kinds of things in your own home that people don&amp;rsquo;t know about.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I think one of my favorite things about different hobbies is learning that things are complicated. Problems are easier when they&amp;rsquo;re not yours, kind of a thing. It helps you appreciate that somebody does something—you can appreciate that it took a lot of effort or some courage to do.&lt;/p&gt;
&lt;p&gt;I used to watch a lot of Bob Ross, and one of the things he used to talk about was—he would paint the whole background and then you want to put the big tree in the front, the foreground. He&amp;rsquo;d say, &amp;ldquo;Okay, now you got to be brave and put this thing in.&amp;rdquo; I was laughing—what is this guy talking about, being brave while you&amp;rsquo;re painting? It&amp;rsquo;s just a painting.&lt;/p&gt;
&lt;p&gt;But I&amp;rsquo;ve painted, and you have to have some courage to mess up the thing you just spent a lot of time doing—to take that risk and put something new in front of it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. In my attempt to not just do things in life that I&amp;rsquo;m good at, I&amp;rsquo;ve learned as an adult to really work hard and push through things and just try things. That&amp;rsquo;s something I&amp;rsquo;m trying to instill in my kids.&lt;/p&gt;
&lt;p&gt;One of them doesn&amp;rsquo;t like to do something if it&amp;rsquo;s not going to turn out well. I tell them about the Edison quote—&amp;ldquo;we&amp;rsquo;ve learned a thousand ways that don&amp;rsquo;t work.&amp;rdquo; That&amp;rsquo;s what I do in my job all day long—try things, see it doesn&amp;rsquo;t work, try something else. I just restarted a failing computation job right before you and I got on. Although now my AI tools are pretty good at doing that for me, so maybe I&amp;rsquo;m gonna get back to being lazy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; How do you think being a parent has impacted you?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I&amp;rsquo;d have to give that some reflection. One thing I&amp;rsquo;ve definitely experienced—and a lot of people, and maybe more surprisingly a lot of fathers, talk about this—being a parent makes you a lot more emotional. You can&amp;rsquo;t watch an emotional scene in a movie the same way. Or any movie where a kid gets hurt—it&amp;rsquo;s just impossible to watch. You experience it differently when you don&amp;rsquo;t have kids.&lt;/p&gt;
&lt;p&gt;I think it keeps you young in some ways. Your kids are at the stage they&amp;rsquo;re at, and you can be an adult most of the time, but sometimes you can just sit down with a two-year-old.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; You experience childhood vicariously, I guess.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. One of the cool things—all my kids have had this, and I&amp;rsquo;ve seen some people on Twitter mention it, I think it&amp;rsquo;s fairly universal but I don&amp;rsquo;t know why—two-year-olds have a stage where they get obsessed with seeing the moon. Every time they get out of the car, it&amp;rsquo;s like, &amp;ldquo;Look, the moon, the moon!&amp;rdquo; Around two years and three months or so.&lt;/p&gt;
&lt;p&gt;Those kinds of things are such a good reminder—like, oh, this is crazy. There&amp;rsquo;s this huge rock flying around us every day, and we totally take it for granted. There are so many things kids are learning for the first time. It&amp;rsquo;s like, &amp;ldquo;Oh yeah, I should stop and appreciate that the moon is up there, appreciate the stars are up there.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; One of my favorite things about astronomy and astrophotography is it&amp;rsquo;s very humbling, I think.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. I was hoping at our new house—you gave me some telescope recommendations a while ago. My eight-year-old keeps asking about it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Those have probably changed by now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Well, I was hoping our new house would be perfect, but they recently installed some new street lights right behind us that I didn&amp;rsquo;t know were going to be there. It&amp;rsquo;s not as dark as I thought. But we&amp;rsquo;re kind of on the edge of Indianapolis, so I&amp;rsquo;m hoping we can get out a little bit more and start getting into some astronomy together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Really fun, especially with the kids—everyone&amp;rsquo;s seen it for the first time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. There are a lot of things that are just neat to—like reading to the kids. That&amp;rsquo;s been one of my favorite things as a parent. Some stuff I&amp;rsquo;ve been reading to them for the first time—that&amp;rsquo;s fun—but like getting to read &lt;em&gt;The Chronicles of Narnia&lt;/em&gt; to them, which my mom read to us. Or reading &lt;em&gt;The Hobbit&lt;/em&gt; to them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Are you going to do &lt;em&gt;Lord of the Rings&lt;/em&gt; and &lt;em&gt;The Hobbit&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I&amp;rsquo;m a little torn on when to read &lt;em&gt;The Lord of the Rings&lt;/em&gt;. There&amp;rsquo;s a Tolkien quote about how &lt;em&gt;Lord of the Rings&lt;/em&gt; isn&amp;rsquo;t a children&amp;rsquo;s book, and you can only read it for the first time once, so don&amp;rsquo;t do it too soon. I would love to start it now, but I think I want to wait till they&amp;rsquo;re a little older.&lt;/p&gt;
&lt;p&gt;But even then, just getting to think, &amp;ldquo;What other stuff can we read together?&amp;rdquo; My boys and I read most evenings before bed. Recently they&amp;rsquo;ve gotten into the Three Stooges—they sometimes beg me to watch the Three Stooges instead of reading to them. But that&amp;rsquo;s been really fun.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="ai-and-the-future"&gt;AI and the Future
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; With the way things are—AI taking over the world for better or worse, the Pandora&amp;rsquo;s box is open—the world is changing very dramatically. How does that impact your hopes and thoughts about your kids and their future, their prospects?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; It&amp;rsquo;s a big question. I&amp;rsquo;ve thought about it some, and I really just don&amp;rsquo;t know the answers.&lt;/p&gt;
&lt;p&gt;The short answer for me is: for them, not much needs to change yet. My boys love to do image generation on ChatGPT or Gemini. It&amp;rsquo;s usually like &amp;ldquo;make a baby riding a motorcycle on Mars&amp;rdquo; or something silly.&lt;/p&gt;
&lt;p&gt;With my eight-year-old—he just has endless questions about things: animals, technology, TV shows. So we&amp;rsquo;ll ask the AI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; You had a World Book Encyclopedia to flip through.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, I should get him some—I try to get him a lot of reference-type books because he&amp;rsquo;s a lot like me and likes that kind of stuff.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been trying to teach my boys some basic programming using Scratch—a visual programming thing—and some iPad stuff. With my eight-year-old, I think we could probably &lt;strong&gt;vibe code&lt;/strong&gt; a game using Claude Code and some kind of text-to-speech thing. He could just talk to the computer and build some kind of game. I&amp;rsquo;m excited to show him something like that.&lt;/p&gt;
&lt;p&gt;But at the same time, it seems to still be really valuable to learn the fundamentals. I don&amp;rsquo;t want to derail him from learning the fundamentals.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; It makes me think of calculus three—that was the time we were allowed to start using a graphing calculator. That is the moment I stopped learning calculus.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. I remember when I was teaching calculus, Wolfram Alpha had come out and was showing the steps on integration and derivation.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m really glad I was in school in an era where I just had to grind through problems. That professor who would give us long take-homes that you&amp;rsquo;d spend eight, twelve hours on, just really wrestling through problems—probably now, if you took his abstract algebra take-home test, ChatGPT could one-shot solve the entire test.&lt;/p&gt;
&lt;p&gt;Which is awesome—I use these tools all day, every day, and it&amp;rsquo;s fantastic. But I&amp;rsquo;m so glad I was schooled in foundations in a lot of ways.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Similarly, I&amp;rsquo;m glad we grew up before social media and the internet kind of took over. I don&amp;rsquo;t know what it would be like to grow up with that just being in your face all the time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Absolutely, yeah. We&amp;rsquo;re slow to introduce technology things to my kids.&lt;/p&gt;
&lt;p&gt;It seems unquestionable that AI stuff is going to have an impact on whatever they choose to do in their careers—and at some point in their schooling if they&amp;rsquo;re going to college. I don&amp;rsquo;t want to keep them from learning how to use those tools well.&lt;/p&gt;
&lt;p&gt;But at the same time, with programming stuff, I use AI tools literally eight-plus hours a day. Maybe I deceive myself sometimes, but I think I&amp;rsquo;m better at using them because of a lot of experience prior to using them—understanding Python really well and how things work.&lt;/p&gt;
&lt;p&gt;I have a lot of both professional experience and education and fundamentals that are still useful. I can still think about the time complexity of an algorithm that the AI tool generates.&lt;/p&gt;
&lt;p&gt;I honestly don&amp;rsquo;t know what all those answers look like. But it&amp;rsquo;s probably to some degree comparable to not rushing to using the graphing calculator—or the TI-89 that could do symbolic integration. It is worth actually grinding through and learning some stuff first.&lt;/p&gt;
&lt;p&gt;But it&amp;rsquo;s going to be harder and harder for that. Kids in public school are getting Chromebooks, and once you have Google Docs, you&amp;rsquo;re getting access to Gemini.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; The cognitive offload is pretty real.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Absolutely. All of us are wrestling with that in our own jobs—what the implications are, what&amp;rsquo;s good and bad about it.&lt;/p&gt;
&lt;p&gt;Even just writing—I don&amp;rsquo;t think I&amp;rsquo;m an amazing writer, but I went through school having to really write and being graded on that and learning from that. I want my kids to learn how to write well because I want them to be able to communicate their ideas well—not just generate essays.&lt;/p&gt;
&lt;p&gt;I honestly don&amp;rsquo;t know all the answers. I&amp;rsquo;ve been asking everybody on my podcasts about the future, and nobody really knows. I don&amp;rsquo;t know either.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="moving-to-indiana"&gt;Moving to Indiana
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Speaking more about the future and maybe wrapping up—you just did a pretty big move. What are your aspirations going from here, from a hobby perspective or family perspective?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; We moved to Indiana to be closer to family—my wife&amp;rsquo;s family specifically. She grew up in the town we&amp;rsquo;re living in now.&lt;/p&gt;
&lt;p&gt;In a lot of ways, we&amp;rsquo;re just trying to develop that. Neither of us have really lived that close to family as adults.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Especially being as close with family as you guys are.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; With kids, our kids have not grown up spending a lot of time with family. Now they have a cousin who&amp;rsquo;s a mile away, which is a whole new experience. We&amp;rsquo;re hoping to lean into that and really make the most of it.&lt;/p&gt;
&lt;p&gt;Maggie and I are currently—if you have any opinions—Maggie wants us to find a new hobby that she and I can do together in the evenings when the kids are in bed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I was going to say board games, but we know where that&amp;rsquo;s going to go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I&amp;rsquo;m not a board game player. Our off-and-on activity has been playing Cat Quest on the Switch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; There&amp;rsquo;s a studio that makes exclusively two-player games—the most recent one was called &lt;em&gt;Split Fiction&lt;/em&gt;, which was really good. And they have &lt;em&gt;It Takes Two&lt;/em&gt;, which is like a relationship-sort-of game. It&amp;rsquo;s really good—I would recommend that for video games. I don&amp;rsquo;t know if it&amp;rsquo;s on the Switch though.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; My buddy&amp;rsquo;s been playing that with his wife.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; The most recent one is &lt;em&gt;Split Fiction&lt;/em&gt;—more like a platformer puzzly kind of thing, but it does things in such a neat way.&lt;/p&gt;
&lt;p&gt;And then painting—I like painting quite a bit. Try to constrain how much you&amp;rsquo;re doing in a given time. I get these little four-by-six canvas blocks and just do something small.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; My sister-in-law who lives a mile from us is a very successful professional painter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Well, don&amp;rsquo;t invite them. Or get tips and then don&amp;rsquo;t tell Maggie—then you can show them off.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I like the idea of foraying into something I don&amp;rsquo;t have a lot of ability in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; We did pottery for a while—that&amp;rsquo;s not something you can do readily at home without some investment. But we did a pottery class for like eight weeks and then rented studio space and did that for quite a while. It was a lot of fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; At the place I did woodworking, we took one pottery class before we had kids. That was really enjoyable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Anything like making or going through that creative process together is always fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. We&amp;rsquo;ve just spent the past eight years having kids—a very intense time of our life. We&amp;rsquo;re not planning to have more. Now we have a sort of clean slate. What kinds of things can we do?&lt;/p&gt;
&lt;p&gt;We also have a big basement now, which is awesome. I have space to do stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; For the board game collection.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, you know me. Unfortunately, my kids are enjoying games. Unfortunately. I&amp;rsquo;m not going to get into that. All Maggie&amp;rsquo;s family here loves games too, so&amp;hellip; might have to&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I&amp;rsquo;ll have the podcast part two—I&amp;rsquo;ll have to get some more understanding of your perspective there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Part of it is I&amp;rsquo;m not competitive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Okay, that makes sense.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="making-things-and-knitting"&gt;Making Things and Knitting
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Part of it is—something we haven&amp;rsquo;t really talked about—I really do love to make stuff. I loved woodworking. One of my main hobbies now is making websites—putting stuff online. My current big project is the &lt;strong&gt;Python Developer Tooling Handbook&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; What about the basketball thing? Is that just a chain of tweets?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; It was a Twitter and Instagram account. I&amp;rsquo;ve tried to turn it into a website—I need to use Claude Code now to get that going again. My &amp;ldquo;Do You Play Ball&amp;rdquo; site. And I do some religious history-related stuff online too.&lt;/p&gt;
&lt;p&gt;I really love making things. Even loving to read—that&amp;rsquo;s actually a tension for me in my hobbies. Reading is like you&amp;rsquo;re not making anything. When I sit down to do something, I love to just tinker. A website is a common outlet for that.&lt;/p&gt;
&lt;p&gt;Even photography, I think for me, is making something. Photographers talk about &amp;ldquo;making an image&amp;rdquo;—and I really do think that&amp;rsquo;s true. Not just in how you capture it and how you decide to capture it and how you choose to edit it, but even choosing what images you show versus which ones you delete is part of the making process.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; People delete pictures now?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I delete a lot. Quite a bit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; It sounds like one thing to add some variety would be the tangible kind of creation—something that&amp;rsquo;s not on the computer, like the woodworking kind of thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I also like to &lt;strong&gt;knit&lt;/strong&gt;. Do you know about me knitting?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I do know about you knitting. I forgot to bring that up too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; That&amp;rsquo;s something Maggie and I do sometimes together. She crochets, and I knit as a hobby. We do that and watch movies or something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; And your rocking chairs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, I wish I had a rocking chair.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I know someone who&amp;rsquo;s making a rocking chair.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; My boys are actually trying to teach them how to crochet right now, which is fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; That&amp;rsquo;s cool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Knitting is a very slow way to make things, but it&amp;rsquo;s something I started back in college 20 years ago. I picked it up again recently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; You&amp;rsquo;re gonna need it for all the cold weather.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; It has been unbelievably cold here. It was six below Fahrenheit this morning when I woke up. And now it&amp;rsquo;s 10 PM, it is four above zero. It has been so cold for the two weeks I&amp;rsquo;ve been here.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a hat that I knit—I never actually really finished it, but&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; That&amp;rsquo;s really nice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I&amp;rsquo;m actually a very good knitter. People get intimidated when they see it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; You could go to the knitting circle. Jolene&amp;rsquo;s been eyeing one at the local store—try and encourage her to go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; It&amp;rsquo;s all ladies, but it&amp;rsquo;s fun.&lt;/p&gt;
&lt;p&gt;An interesting thing about knitting is that knitting patterns are written in a really abstract way. If you&amp;rsquo;re used to programming and math, they&amp;rsquo;re very unintimidating to read. They&amp;rsquo;re not even complicated logic—it&amp;rsquo;s just really concise so they can put it on one page. I think that&amp;rsquo;s why people find the actual mechanics of complicated things aren&amp;rsquo;t usually that hard—it&amp;rsquo;s just, are you willing to be patient with the abstract stuff? Which is a no-brainer to me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; There&amp;rsquo;s also the geometry aspect too, in terms of crochet—there&amp;rsquo;s like a&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Knitting is less geometric in the sense that when you&amp;rsquo;re knitting, you basically only are in one dimension at a time. Crochet is sort of more dimensional because it&amp;rsquo;s less constrained. But knitting is so constrained that if you can understand the abstraction, it&amp;rsquo;s only a very limited scope at any given time. Because you&amp;rsquo;re in one place and you can&amp;rsquo;t be in other places—I mean, there are some exceptions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; The other thing that&amp;rsquo;s neat about crochet is there&amp;rsquo;s no machine that can crochet. So every crochet piece you find somewhere in a store—that&amp;rsquo;s been handmade. There&amp;rsquo;s something special there, I think.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. That&amp;rsquo;s what Jolene points out to me all the time. That&amp;rsquo;s cool. And she picked up knitting too, so she&amp;rsquo;s been doing a lot of that.&lt;/p&gt;
&lt;p&gt;You can machine knit too, but to get really intricate stuff, usually you can&amp;rsquo;t machine knit it as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; You like the Fair Isle stuff where you make the pictures and things?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, I enjoy Fair Isle as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; Or the pixel art sort of stuff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, it&amp;rsquo;s very fun.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="closing"&gt;Closing
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; All right. Hey, buddy. It&amp;rsquo;s been lovely to talk to you. It&amp;rsquo;s been a while.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Do you feel like you know me better?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; I feel like I know you better. I feel like there are things we can still talk about. I&amp;rsquo;d like to talk more—you know, philosophy, theology, those types of things. I think would be a fun conversation to have. So if this thing gets like 10,000 likes or something, we can do that part two. Sounds good.&lt;/p&gt;
&lt;p&gt;Where can people find you? Do you have anything you want to plug?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I mean, this podcast is at &lt;a class="link" href="https://podcast.tdhopper.com" target="_blank" rel="noopener"
&gt;podcast.tdhopper.com&lt;/a&gt;, which is my main website, and that links to most of my stuff. I&amp;rsquo;m &lt;a class="link" href="https://x.com/tdhopper" target="_blank" rel="noopener"
&gt;tdhopper&lt;/a&gt; on Twitter/X and Instagram. &amp;ldquo;tdhopper&amp;rdquo; is kind of my online identity;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Brenton:&lt;/strong&gt; All right, well, send my love to Maggie and the kids. It&amp;rsquo;s been nice spending more time with you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, thanks Brenton. Thank you. Bye.&lt;/p&gt;</description></item><item><title>AI-Assisted SQL Generation with Till Döhmen</title><link>https://tdhopper.com/blog/ai-assisted-sql-generation-with-till-d%C3%B6hmen/</link><pubDate>Fri, 30 Jan 2026 01:05:00 -0500</pubDate><guid>https://tdhopper.com/blog/ai-assisted-sql-generation-with-till-d%C3%B6hmen/</guid><description>&lt;img src="https://tdhopper.com/images/podcast.png" alt="Featured image of post AI-Assisted SQL Generation with Till Döhmen" /&gt;&lt;h2 id="listen"&gt;Listen
&lt;/h2&gt;&lt;iframe src="https://open.spotify.com/embed/episode/3wJY3GJpSUVNa0Uvw7QNwO" width="100%" height="232" frameBorder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="links"&gt;Links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.linkedin.com/in/tdoehmen/" target="_blank" rel="noopener"
&gt;Till&amp;rsquo;s LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://tdoehmen.github.io/" target="_blank" rel="noopener"
&gt;Till&amp;rsquo;s Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://motherduck.com/" target="_blank" rel="noopener"
&gt;MotherDuck&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://duckdb.org/" target="_blank" rel="noopener"
&gt;DuckDB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.hopsworks.ai/" target="_blank" rel="noopener"
&gt;Hopsworks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://changelog.com/practicalai/292" target="_blank" rel="noopener"
&gt;Till on Practical AI Podcast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://yale-lily.github.io/spider" target="_blank" rel="noopener"
&gt;Spider Text-to-SQL Benchmark&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/matsonj/mviz" target="_blank" rel="noopener"
&gt;mviz - Observable Dashboard Skill by Jacob Matson&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="subscribe"&gt;Subscribe
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/podcast/feed" target="_blank" rel="noopener"
&gt;RSS Feed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://podcasts.apple.com/us/podcast/into-the-hopper/id1499693201" target="_blank" rel="noopener"
&gt;Apple Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://open.spotify.com/show/63NrgKMVb0VTwkklGboIjy" target="_blank" rel="noopener"
&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://overcast.fm/itunes1499693201/into-the-hopper" target="_blank" rel="noopener"
&gt;Overcast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary"&gt;Summary
&lt;/h2&gt;&lt;p&gt;In this episode of &lt;em&gt;Into the Hopper&lt;/em&gt;, I sit down with Till Döhmen, AI Lead at MotherDuck, to explore the evolving landscape of AI-assisted SQL generation. Till brings a unique perspective from his PhD research in databases and his work building AI features at MotherDuck, the serverless data warehouse built on DuckDB.&lt;/p&gt;
&lt;p&gt;We discuss how text-to-SQL has matured from academic benchmarks to practical tools, the importance of documentation and schema context for agents, and the emerging role of MCP servers and skills in customizing agent behavior. Till shares insights on building trust in agent-generated queries, why thinking about what a new hire would need to know helps you build better AI workflows, and how agents are reshaping the analyst&amp;rsquo;s role from SQL writing to question refinement.&lt;/p&gt;
&lt;h2 id="transcript"&gt;Transcript
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Welcome to Into the Hopper podcast. Today I&amp;rsquo;m sitting down with Till Döhmen, the AI Lead at MotherDuck. With a background spanning PhD research in databases to ML ops engineering at Hopsworks, Till sits at the intersection of database theory and modern AI. Welcome, Till.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; Thanks for having me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; We&amp;rsquo;re going to dive into AI-assisted SQL generation, which is something I&amp;rsquo;ve explored a little bit but don&amp;rsquo;t know a lot about. We&amp;rsquo;ll move beyond the simple text-to-query prompts to discuss agentic workflows, optimization, and whether letting AI write our queries changes how we understand our data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; Great.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="about-motherduck"&gt;About MotherDuck
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; First of all, do you want to tell us about MotherDuck?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; Yeah, for sure. I joined MotherDuck two and a half years ago, after ChatGPT came out. I joined as the AI guy. Before that I was working at Hopsworks, mostly working on classical machine learning ops. Hopsworks is a feature store platform.&lt;/p&gt;
&lt;p&gt;Coming from my PhD research, I was super interested in how we can automate data engineering for data science pipelines, and that felt like a great place to work on these types of problems. Hopsworks was doing feature processing and data engineering workloads on that platform. They were using Spark, and there were small and medium-sized datasets that felt a little bit painful because of startup times and so on. There was a demo we were doing for customers—I think it took seven minutes or so to run end to end.&lt;/p&gt;
&lt;p&gt;My biggest project there ended up being migrating these workflows to a second path where we could do the data engineering with DuckDB. That got me even more interested in working more on DuckDB.&lt;/p&gt;
&lt;p&gt;Research-wise, I was looking into foreign key detection—how can we identify which columns in tables have foreign key references in the database? LLMs turned out to be really useful for that. The second step was realizing that LLMs are actually really good at translating text to SQL. Surprisingly good at that time, though still far away from being super useful in practice. But that&amp;rsquo;s how my journey at MotherDuck started.&lt;/p&gt;
&lt;p&gt;MotherDuck is a data warehouse for analytics based on DuckDB. We run DuckDB instances in the cloud, and users use those instances to process their queries. It&amp;rsquo;s integrated pretty nicely with the local DuckDB client. People typically use DuckDB or Polars to process data locally very efficiently—you can process tens or even hundreds of gigabytes on your local machine.&lt;/p&gt;
&lt;p&gt;When the dataset gets really big, or when you&amp;rsquo;re processing data stored in the cloud—say you want to analyze a hundred gigabytes or a terabyte of partitioned data, or Iceberg tables—the processing power of the local machine isn&amp;rsquo;t optimal anymore. We provide a very simple way to connect your local DuckDB client to the instances we host and expand the processing capabilities seamlessly. You can keep writing the same queries, but the processing happens on the server side when needed.&lt;/p&gt;
&lt;p&gt;The other benefits are data sharing. DuckDB is usually single-player mode—you have a local database file or just data in memory. With MotherDuck, you can work more within teams. You can share data between users, have a shared workspace. You also have a web UI where you can query DuckDB—in that case, DuckDB runs in the browser with WebAssembly.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s also pretty interesting from a technology standpoint because you can do a lot of interesting things. Potentially you can save the network overhead—you don&amp;rsquo;t have to go to the server for everything. It&amp;rsquo;s a web application, but still you have an entire database running in your browser that can do query parsing and binding. That also plays a role in the AI features we have at MotherDuck.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="building-ai-features-at-motherduck"&gt;Building AI Features at MotherDuck
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; I was the AI guy, so I did everything that involved AI. In the beginning I worked on text-to-SQL features. But they weren&amp;rsquo;t at the point yet where they were useful enough to position prominently—this was end of 2023. We couldn&amp;rsquo;t confidently put that in the primary path and say, &amp;ldquo;Hey, when you come to the UI, you don&amp;rsquo;t have to write SQL anymore. Just give us your question and we&amp;rsquo;ll generate the SQL for you.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;It was more framed as a way to help SQL analysts be a little more efficient. And then it turns out, when you focus on that, there are other problems worth solving beyond text-to-SQL.&lt;/p&gt;
&lt;p&gt;For example, fixing queries. That&amp;rsquo;s very common—you type a query, mix up two letters, and get an error message. DuckDB actually has pretty useful error messages. But still, it&amp;rsquo;s so obvious when the database says, &amp;ldquo;This table does not exist. Did you mean this other table?&amp;rdquo; And 99.9% of the time, yes, that&amp;rsquo;s exactly what I meant. Can you please just use it?&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s basically what this feature we call &amp;ldquo;Fix It&amp;rdquo; does. It kicks in and asks a language model to do the fix for the user. We added other things like editing queries—if you&amp;rsquo;re not sure how a function signature looks, you can mark that part of the query and describe in natural language what kind of change you want to apply. Those things have been very useful.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="the-history-of-text-to-sql"&gt;The History of Text-to-SQL
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Do you know much about the world pre-2023 of automation in SQL generation?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; I don&amp;rsquo;t think I&amp;rsquo;ve been around long enough in the data world to really go back to the beginnings. When I hear Jordan talk about this, he refers to things that happened probably when I was still a student—10, 12, 15 years ago. And the history goes back even further.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Probably to the seventies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; I think it&amp;rsquo;s even part of the philosophy of SQL when you read the very first papers. They wanted to design it to be close to natural language. So this vision has always been there.&lt;/p&gt;
&lt;p&gt;But when I started looking into it, there was a ton of research around text-to-SQL. Not so many products, but a lot of papers. It was the time of fine-tuning—everyone was fine-tuning small models for text-to-SQL. Before large language models, it was BERT-style models or very small transformers.&lt;/p&gt;
&lt;p&gt;There were these benchmarks—Spider and others—that&amp;rsquo;s what people were typically competing on. When you look at the benchmarks, they&amp;rsquo;re very simple: small databases with three to five tables, very well-known columns. They&amp;rsquo;re not really a reflection of enterprise database reality. So there was always this limitation because the results didn&amp;rsquo;t translate to the real world.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="the-state-of-text-to-sql-today"&gt;The State of Text-to-SQL Today
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I&amp;rsquo;m interested in what&amp;rsquo;s possible today from two angles. First, if I&amp;rsquo;m an analyst with a business problem I think I can describe, and I don&amp;rsquo;t want to write a bunch of CTEs and conditionals—how far can I get using natural language to generate queries? And how does that differ from other agentic software development?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; I remember about four years ago, there was a Hacker News discussion about what&amp;rsquo;s exciting about LLMs. Some people talked about text-to-SQL, others about code generation. Code generation felt much more unattainable at that time. SQL has a relatively simple grammar compared to a general-purpose programming language.&lt;/p&gt;
&lt;p&gt;When I think back to that time and where we are now—where people routinely use AI tools for coding—that has changed massively. That makes me say more comfortably that language models are actually really good at doing these types of things in SQL.&lt;/p&gt;
&lt;p&gt;I use LLMs as a coding assistant. I so often use them to debug user queries. When a customer has some issue with a query, I have conceptual ideas in my mind for how to optimize it, but these might be huge queries. Especially for experimentation—could this approach be useful? Could the other one? I just let them do it more efficiently than I could ever do manually.&lt;/p&gt;
&lt;p&gt;But it&amp;rsquo;s in a scenario where I assume the user has some degree of SQL knowledge, and maybe is also opinionated about how the SQL should be written. So I can see some friction, especially when models are used out of the box without additional context or prompting. Which dialect should they even write? DuckDB is not that prevalent in the pre-training data. There are things in the DuckDB documentation that models out of the box aren&amp;rsquo;t aware of.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="mcp-servers-and-documentation"&gt;MCP Servers and Documentation
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Say I&amp;rsquo;m a Postgres developer wanting to do analytics. What&amp;rsquo;s the best toolset out there? Can I just tell Claude Code I have access to psql, say &amp;ldquo;go learn my schemas,&amp;rdquo; and then give it my prompt? Or are there more specific agents doing this better?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; There are multiple aspects to the answer. The question understanding part and translating that to a query that semantically makes sense—you give the LLM access to the schemas so it knows the tables, the column names. It can potentially also explore the data a bit, because data content is quite important for filters and so on. If the model doesn&amp;rsquo;t have access to data content, it&amp;rsquo;s pretty certain it&amp;rsquo;ll hallucinate filter parameters.&lt;/p&gt;
&lt;p&gt;Providing that access nowadays is super simple. You give an agent the right tools. There&amp;rsquo;s a Postgres MCP server. I would probably just take Cursor or Claude Code or whatever tool I like, plug in a Postgres MCP server, and start kicking the tires. I&amp;rsquo;d expect that to work fairly reliably.&lt;/p&gt;
&lt;p&gt;I recently wrote a research paper about this—it&amp;rsquo;s not published yet. I was looking at the DuckDB and Postgres database documentation, every single function, every single statement, and probing language models for their knowledge about these things.&lt;/p&gt;
&lt;p&gt;It was interesting that even Postgres functions that have been around for quite some time—the frontier models have very little knowledge about some function names, parameters, and how to use them. It&amp;rsquo;s a small subset of functionality, but I could imagine as a database admin or data engineer deep into optimizing certain aspects of workloads, I care about those features. At that point you actually need to provide the human-written documentation as context to the LLM.&lt;/p&gt;
&lt;p&gt;Some MCP servers do that. It&amp;rsquo;s basically a RAG pipeline that chunks and indexes the documentation and makes it retrievable through natural language questions. If that&amp;rsquo;s exposed in the MCP server, the agent can call an &amp;ldquo;ask docs question&amp;rdquo; tool when it&amp;rsquo;s in doubt about functionality. It passes a natural language question—&amp;ldquo;How does this feature work?&amp;quot;—and gets an answer grounded in the documentation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; So your MCP can talk to the documentation as well as your own data?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; Exactly. We&amp;rsquo;re actually using the exact same documentation chatbot for the MCP that we use for our human-facing documentation.&lt;/p&gt;
&lt;p&gt;The point of the paper I was writing is that these things should actually be built into the database to some degree. There are functions in DuckDB—you can do &lt;code&gt;SELECT * FROM duckdb_functions()&lt;/code&gt; and get a table of all functions with descriptions. But the description field is only populated for half the functions. Database developers assume humans can always go to the documentation website.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s similar for Postgres—only 30-40% of functions have descriptions, and the textual descriptions are often uninformative. I believe if database developers invested a little more into built-in documentation, that problem could easily go away, because agents are quite good at searching for information that way.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="the-challenge-of-undocumented-data"&gt;The Challenge of Undocumented Data
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; What about data documentation? At my previous job at a bank, we had a large data warehouse with data dumps from production tables into AWS data lake storage. It was weakly documented in terms of what the tables meant. Even analysts were looking at things and trying to infer from the data what columns they were looking at. Sometimes even wondering if a column is a key for another table with different names. Are agents getting good at discovering those kinds of things? Or is that just a way to shoot yourself in the foot by letting the agent hallucinate what it&amp;rsquo;s seeing?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; If you give the agent infinite time to do data exploration—you prompt it saying &amp;ldquo;here&amp;rsquo;s my question, please write the SQL to answer it, but before you start, explore the entire database, look at every table, every column, think about what the columns are about, make a plan to explore the database extensively, write a report, and only then start writing the SQL&amp;rdquo;—I think that could work quite effectively.&lt;/p&gt;
&lt;p&gt;But you have to think about it this way: what would happen if you gave a proficient analyst who has never seen your database access to your data warehouse and asked that person the same question? How much of a hard time would this person have? What are the things this person cannot know or cannot infer from the data? The same limitations apply to the LLM.&lt;/p&gt;
&lt;p&gt;If there are things that are really not documented, non-obvious, or not inferable from how the data looks, it definitely needs to be provided in context in some shape or form. But this exploration takes an insane amount of time and it&amp;rsquo;s expensive. You have to run queries and you&amp;rsquo;re wasting tokens. You don&amp;rsquo;t want to do this every single time.&lt;/p&gt;
&lt;p&gt;Another thing is query history, if you have access to that—the queries people have run in the past, sometimes even comments in those queries. That&amp;rsquo;s also very good for the agent to dig into.&lt;/p&gt;
&lt;p&gt;I think this exploration of schema and query history and collection of relevant context shouldn&amp;rsquo;t happen on the user path where the user asks a question. It should be considered part of data engineering work or data maintenance work.&lt;/p&gt;
&lt;p&gt;If I create a new table as a data engineer, I might usually have written into a data catalog or some internal documentation page what the data is about. Maybe I&amp;rsquo;ve sent a Slack message or added info to a Linear ticket. But nowadays, it&amp;rsquo;s probably also part of the job to document this in a form that agents can consume easily.&lt;/p&gt;
&lt;p&gt;I wish I could tell you there&amp;rsquo;s an established standard for how to do this. Different companies try different things, and we&amp;rsquo;re one of those companies with a particular approach. DuckDB and other databases have table and column comments in the metadata catalog. You can add natural language comments. It&amp;rsquo;s a bit underused, but we recommend people start with using those to add context.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a gap in our product where I think there&amp;rsquo;s a real need to capture knowledge at a database level and organization level. Our MCP server could surface this information to the agent. It could be as simple as a markdown document.&lt;/p&gt;
&lt;p&gt;Other companies go more toward the semantic layer direction—a much more structured format to represent semantic information about your data. Which columns and tables are related—this goes back to foreign key detection.&lt;/p&gt;
&lt;p&gt;My experience specifically with foreign keys is that LLMs are really good out of the box at finding them. But if the schema is very complex, or you have multiple possible tables—staging or landing tables, different transformation layers side by side—you don&amp;rsquo;t want the join path to use unclean data.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="building-trust-in-agent-generated-sql"&gt;Building Trust in Agent-Generated SQL
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I tweeted three years ago when ChatGPT was fresh that World War III was going to be started by AI-generated SQL. My fear is that this potentially makes it seem too easy. It&amp;rsquo;s somewhat easy to get syntactically correct SQL or something that looks like SQL, and then with an agent, something that runs.&lt;/p&gt;
&lt;p&gt;Two questions. One: what are good practices to build confidence that agent-generated SQL is correct? As anyone who&amp;rsquo;s spent time with SQL knows, that&amp;rsquo;s also a challenge with human-generated SQL—testing SQL is a hard problem.&lt;/p&gt;
&lt;p&gt;Two: do you have a concern that by not writing our SQL, we&amp;rsquo;re not thinking about our data enough?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; I would say we&amp;rsquo;re thinking about it in different ways now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; So what practices can help us know that agent-generated queries are correct?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; It really depends on the type of question you&amp;rsquo;re asking and what the stakes are. If I&amp;rsquo;m doing exploratory data analysis, just interested in digging into things, I always have the option to question certain results. If the agent gives me an answer that feels off, I have an intuition for the data. Building that intuition is a starting point.&lt;/p&gt;
&lt;p&gt;But an agent can be useful for that as well. If I would do it manually, I&amp;rsquo;d run a SELECT star on the table, look at summary statistics, drill down into dimensions. The agent can do the same thing for me. The SQL it generates is typically not that complex. If I want to build trust, I could double-check the SQL. Also, there&amp;rsquo;s no reason for the agent to be intentionally deceiving. On average, the things the agent does are going in the right direction.&lt;/p&gt;
&lt;p&gt;Maybe it&amp;rsquo;s off on a few things because it&amp;rsquo;s making implicit assumptions that are wrong. Part of the issue could be the prompting or not having provided enough context. As I said earlier, it&amp;rsquo;s important to think: how would someone approach this who has never seen that data before? If I have a new hire onboarding today and ask them to calculate this metric, could that person succeed? What would be the typical mistakes? What can I do to help that person or the agent succeed?&lt;/p&gt;
&lt;p&gt;This exploration helps build intuition about the data. Either way, whether SQL is hand-written or agent-written. If I have doubt, I can ask the model to explain it.&lt;/p&gt;
&lt;p&gt;I believe in this Socratic questioning approach. If I just keep questioning and asking, and there are no contradictions in the evidence, I can build up certainty and trust. I don&amp;rsquo;t need to know the answers, but by double-checking and poking, I can find out whether what the agent said is correct.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; An interesting thing with SQL is you can do the inverse. Generate the SQL and then in a different session with new context, say &amp;ldquo;given this SQL, tell me what it does&amp;rdquo; and see if that matches what you think.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; That&amp;rsquo;s a neat feature of Cursor now—you can run multiple models and have multiple responses.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; Oh, I didn&amp;rsquo;t know that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; You click which models you want, each model can run multiple times, and you review the outputs. If reviews are coming in similar from OpenAI, Google, and Anthropic, I feel pretty confident.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; One of the things I really like about Claude Code is how self-critical it can be at times. It almost feels like it stops and thinks for a second whether the direction it&amp;rsquo;s going is good or not. This is not a quality of the base model itself—if I use Opus in Claude Code versus Cursor versus the Claude web app, it behaves very differently. A lot of the behavior that makes these agents good comes down to the implementation of the agent on top of the model.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s the same with handwriting you mentioned, or writing itself. It&amp;rsquo;s a way to train the mind, keep the mind sharp, think more clearly about what I&amp;rsquo;m writing. I think it&amp;rsquo;s true in some sense that at this level of abstraction, you&amp;rsquo;ll probably start to lose a little bit of intuition. But it frees up mental space for other things.&lt;/p&gt;
&lt;p&gt;For analysts—I&amp;rsquo;m not an analyst myself, I have to think about it from the outside—I could imagine this really frees up time. If SQL is not the main part of my work anymore&amp;hellip; I&amp;rsquo;ve written a lot of SQL in my life and never gotten really good at it. I&amp;rsquo;m always dreading having to write a window function. I&amp;rsquo;d rather try to do the analysis in a slightly different way so I don&amp;rsquo;t have to write that stupid window function.&lt;/p&gt;
&lt;p&gt;With the agent, I can just let it do that. All the ideas I have of things that could be interesting, I can play through all of them with very little effort. Once I&amp;rsquo;m through that, I have mental capacity left to think about what other data sources could be interesting to integrate—something I wouldn&amp;rsquo;t have had time for if I&amp;rsquo;d spent all my time just figuring out the SQL syntax.&lt;/p&gt;
&lt;p&gt;It has the potential to reshape the scope of the analyst&amp;rsquo;s work. At the same time, I could start thinking more about the actual business question.&lt;/p&gt;
&lt;p&gt;We were talking to a company working on a text-to-SQL agent. One interesting story: their customers were using the agent to help business users refine their questions. The agent was basically a thinking partner. The user would say, &amp;ldquo;I kind of want to look into that, but I don&amp;rsquo;t even know if we have data for that.&amp;rdquo; The agent would go look. At the end, the output was a very refined question—and then they sent it to the human analyst to write the SQL. But this whole part of refining the question was also taking up a lot of time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I&amp;rsquo;ve found in Claude Code you can use the Ask User Question tool to get it to help me think through what problem I&amp;rsquo;m trying to solve. It surfaces things I know I should be thinking about but don&amp;rsquo;t always think about. That&amp;rsquo;s a very helpful way to work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; Indeed.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="skills-and-agent-customization"&gt;Skills and Agent Customization
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Let&amp;rsquo;s talk about skills—this idea Anthropic came up with and is now making available to others. What are agent skills providing in this realm? You and I talked a little about being able to encode business-specific things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; First of all, I think skills are—just like MCP itself and Claude Code and Sonnet—I have the feeling Anthropic really understands their users well. I really like skills because they fill this gap of shareable prompts and context that was difficult before.&lt;/p&gt;
&lt;p&gt;When I think about agents, MCPs, and skills and how they work together: agents and tools are pretty clear. MCPs give the agent the capability to use tools—which could be a database, with tools like &amp;ldquo;run this query&amp;rdquo; or &amp;ldquo;list all tables.&amp;rdquo; There&amp;rsquo;s only a limited amount of context the MCP server comes with: the set of tools, short descriptions of what they do, and some clients support an initial system prompt.&lt;/p&gt;
&lt;p&gt;In that prompt you can explain things—we explain specifics of DuckDB SQL syntax, for example. But when you go beyond that, especially if you want to customize things, the MCP server doesn&amp;rsquo;t give you that option unless you fork it and change the system prompt.&lt;/p&gt;
&lt;p&gt;Skills, especially because they&amp;rsquo;re shareable in Claude Code—that&amp;rsquo;s what I really like. I feel like overall in the skill ecosystem, what&amp;rsquo;s missing is a general sharing mechanism. There&amp;rsquo;s an effort that Val did, there&amp;rsquo;s this NPX skill distribution mechanism. I also saw there&amp;rsquo;s something coming in the MCP spec, potentially a proposal for providing an MCP service to surface skills.&lt;/p&gt;
&lt;p&gt;Currently the biggest hurdle is distribution of skills. But once that&amp;rsquo;s solved, they really let you customize the behavior of the agent to a useful extent. Skills can be not only prompts but also scripts. Coding agents with skills that contain scripts can be really powerful and provide better guardrails.&lt;/p&gt;
&lt;p&gt;If the agent figures out the user has a specific intent and there&amp;rsquo;s a script for that in the skill, it can use the script instead of writing its own Python or its own solution—which might be wrong. These skills provide better guardrails. And they&amp;rsquo;re plug and play.&lt;/p&gt;
&lt;p&gt;Jacob, one of our DevRel folks, released an Observable dashboard skill. If I load this skill in Claude Code, the agent suddenly has the capability to write very well-designed dashboards following a specific design principle. It also gives the agent knowledge of how to weave in MotherDuck for live-querying dashboards. Skills are pretty cool and useful for customization of agent behavior.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="memory-and-the-future-of-agents"&gt;Memory and the Future of Agents
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Another thing people are talking about more—we discussed it in my last podcast—is memory as a useful tool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I&amp;rsquo;m not sure everybody agrees on what memory means.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; Indeed. I almost think of skills as a very primitive form of memory. If the agent had a way to also author skills—it&amp;rsquo;s basically just markdown—that&amp;rsquo;s a very simple form of memory. Could be a markdown file that the agent writes and can read.&lt;/p&gt;
&lt;p&gt;I think memory is extremely interesting and will help a lot with making agents better at what they&amp;rsquo;re doing. The current frontier of developments in the AI space is not in the model space anymore—it&amp;rsquo;s more on the agent layer. Memory will be a very important part of the agent layer.&lt;/p&gt;
&lt;p&gt;If you have to explain your agent over and over again how not to make the same stupid mistake, that&amp;rsquo;s very annoying. If I have a skill I can plug in that already explains to the model how not to do that, great. But it&amp;rsquo;s still some manual effort. Memory, there are a lot of things still to be developed and explored.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not only RAG anymore, or just saving a markdown file. I&amp;rsquo;m very curious where this is going. I don&amp;rsquo;t really have a good answer for how memory looks like today beyond markdown files. I&amp;rsquo;m sure there&amp;rsquo;s a lot of interesting research at labs and in industry on memory architectures. We&amp;rsquo;ll see it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="looking-forward"&gt;Looking Forward
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; That brings me to a great concluding question. If we look back two years, we didn&amp;rsquo;t have agent tools particularly—we didn&amp;rsquo;t have Claude Code for sure, maybe Cursor was starting to be around. What do you think the next two years looks like? At MotherDuck or in the industry in general?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; This is almost impossible to answer. Thinking about how the world looked two years ago, and assuming things are maybe exponentially accelerating—where are we going to be? I have no idea.&lt;/p&gt;
&lt;p&gt;As I said, I think short to midterm, these agent architectures and memory are probably a space where developments will happen.&lt;/p&gt;
&lt;p&gt;I wish context windows would become bigger. That could also solve a bit of the memory and retrieval problems.&lt;/p&gt;
&lt;p&gt;I also find interesting things like diffusion models or new architectures for LLMs. Faster inference—if you&amp;rsquo;re working with Claude Code, it&amp;rsquo;s great, but it can be a little bit slow.&lt;/p&gt;
&lt;p&gt;If I get off this call, I&amp;rsquo;ll have 20 more things on my mind, but right now I&amp;rsquo;m blank.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; The less you say now, the better you&amp;rsquo;ll look in two years.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; I guess so. But thanks so much for having me. It was fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Any places people can find you online besides motherduck.com?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Till:&lt;/strong&gt; People can go to my LinkedIn. I&amp;rsquo;m not active on Twitter or X. LinkedIn is a great place to connect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Thanks for joining me on Into the Hopper.&lt;/p&gt;</description></item><item><title>Build for the Model Six Months from Now</title><link>https://tdhopper.com/blog/build-for-the-model-six-months-from-now/</link><pubDate>Fri, 30 Jan 2026 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/build-for-the-model-six-months-from-now/</guid><description>&lt;p&gt;Boris Cherny, who built Claude Code at Anthropic, &lt;a class="link" href="https://www.youtube.com/watch?v=PTkE7eaPJvY" target="_blank" rel="noopener"
&gt;recently shared&lt;/a&gt; advice he received from Anthropic co-founder Ben Mann:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Don&amp;rsquo;t build for the model of today, build for the model 6 months from now.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When Cherny first developed Claude Code, it wasn&amp;rsquo;t a great product. The models weren&amp;rsquo;t capable enough, and he only used the tool for about 10% of his own coding. But Mann pushed him to trust the scaling laws and design for where models would be, not where they were.&lt;/p&gt;
&lt;p&gt;Six months later, with the release of Claude 3.5 Sonnet and Opus, the models caught up to the tool&amp;rsquo;s design. Cherny&amp;rsquo;s usage jumped to 80-90%.&lt;/p&gt;
&lt;p&gt;This is a useful mental model for anyone building AI-powered tools: if you only design for today&amp;rsquo;s capabilities, your product may be obsolete by the time it ships.&lt;/p&gt;</description></item><item><title>The Evolution of AI Agents with Ben Labaschin</title><link>https://tdhopper.com/blog/the-evolution-of-ai-agents-with-ben-labaschin/</link><pubDate>Fri, 09 Jan 2026 16:41:00 -0500</pubDate><guid>https://tdhopper.com/blog/the-evolution-of-ai-agents-with-ben-labaschin/</guid><description>&lt;img src="https://tdhopper.com/images/podcast.png" alt="Featured image of post The Evolution of AI Agents with Ben Labaschin" /&gt;&lt;h2 id="listen"&gt;Listen
&lt;/h2&gt;&lt;iframe src="https://open.spotify.com/embed/episode/2IcWcUKJRdjaXsLKh2J4ca" width="100%" height="232" frameBorder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="links"&gt;Links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://twitter.com/EconoBen" target="_blank" rel="noopener"
&gt;Ben&amp;rsquo;s Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://econoben.dev/" target="_blank" rel="noopener"
&gt;Ben’s Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.workhelix.com" target="_blank" rel="noopener"
&gt;WorkHelix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.oreilly.com/library/view/what-are-ai/9781098159726/" target="_blank" rel="noopener"
&gt;What Are AI Agents - O&amp;rsquo;Reilly Book&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.oreilly.com/library/view/managing-memory-for/9798341661257/" target="_blank" rel="noopener"
&gt;Managing Memory for AI Agents - O&amp;rsquo;Reilly Book&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/steveyegge/beads" target="_blank" rel="noopener"
&gt;Bead Development Tool&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="subscribe"&gt;Subscribe
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/podcast/feed" target="_blank" rel="noopener"
&gt;RSS Feed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://podcasts.apple.com/us/podcast/into-the-hopper/id1499693201" target="_blank" rel="noopener"
&gt;Apple Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://open.spotify.com/show/63NrgKMVb0VTwkklGboIjy" target="_blank" rel="noopener"
&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://overcast.fm/itunes1499693201/into-the-hopper" target="_blank" rel="noopener"
&gt;Overcast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary"&gt;Summary
&lt;/h2&gt;&lt;p&gt;In this episode of &lt;em&gt;Into the Hopper&lt;/em&gt;, I sit down with Ben Labaschin, a Principal Machine Learning Engineer at WorkHelix and author of the O&amp;rsquo;Reilly books &lt;em&gt;What Are AI Agents&lt;/em&gt; and &lt;em&gt;Managing Memory for AI Agents&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We dive deep into the current state of AI engineering, moving beyond the hype to discuss the &amp;ldquo;brass tacks&amp;rdquo; of developer workflows. We cover how AI is reshaping personal projects, the shift from &amp;ldquo;coder&amp;rdquo; to &amp;ldquo;conductor,&amp;rdquo; and the specific tool stacks (like Beads and Spec-Driven Development) that Ben uses to manage agent memory effectively.&lt;/p&gt;
&lt;h2 id="transcript"&gt;Transcript
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Welcome to the Into the Hopper podcast. Today, I&amp;rsquo;m joined by Ben Labaschin, a principal machine learning engineer at WorkHelix, who has been instrumental in the company&amp;rsquo;s growth from seed stage to its $75 million Series A valuation.&lt;/p&gt;
&lt;p&gt;Ben is the author of O&amp;rsquo;Reilly Books, &lt;em&gt;What Are AI Agents&lt;/em&gt; and &lt;em&gt;Managing Memory for AI Agents&lt;/em&gt;. His work spans the full stack from building enterprise causal inference architectures and async-parallelized LLM APIs to delivering over $8 million in savings for global logistics through optimized machine learning systems.&lt;/p&gt;
&lt;p&gt;Beyond the Code, Ben is a published researcher focusing on firm-level exposure to LLMs and the economic impact of those technologies on labor. Welcome, Ben.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; Hey, man. I appreciate it. Thanks for having me, Tim.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; You and I have known each other a while through the internet, though I think this is really the first time we&amp;rsquo;ve kind of talked one-on-one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; Yeah. You know, the internet can be really easy to communicate, but it&amp;rsquo;s great to be able to actually speak to you in person.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah. We&amp;rsquo;re both former NormConf speakers. Shout out to Vicki Boykis and speaking on our NormConf official Shure microphones, which sound good.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; It&amp;rsquo;s one of the best things to come from the conference other than all the awesome talks.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="ai-workflows-and-developer-experience"&gt;AI Workflows and Developer Experience
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; This is an interesting podcast for me because I&amp;rsquo;ve gotten more and more interested in AI workflows and how AI is impacting the developer experience. I did three interviews last year. And obviously, it&amp;rsquo;s been a wild ride of 2025 and so many changes from what we were doing.&lt;/p&gt;
&lt;p&gt;I interviewed Ravi Modi and at the time he talked about how his workflow was still copying and pasting code snippets into ChatGPT and that was his iterative process. I believe from talking to him he&amp;rsquo;s given up on that. But that&amp;rsquo;s what a lot of us were doing at least 18 months ago. And now, I think agents are taking over the world. You&amp;rsquo;re an interesting case because not only are you using these tools as a developer, but your company is also looking at these tools from a different angle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; That&amp;rsquo;s right. I mean, I&amp;rsquo;ve gotten the benefit of seeing both sides of things. We kind of started doing that work before AI agents really were a thing. So that&amp;rsquo;s been a fascinating experience to see how companies were anticipating what was coming versus when it actually came.&lt;/p&gt;
&lt;p&gt;The long and short of that is that companies really weren&amp;rsquo;t interested in being told, &amp;ldquo;Hey, there&amp;rsquo;s this new paradigm coming, you should prepare for it.&amp;rdquo; When it actually happened, they weren&amp;rsquo;t prepared for it. And now they&amp;rsquo;re starting to adapt to it. So yeah, I get to use the tools and then I get to see how individuals actually use them at enterprise companies and sort of trying to measure the impact of that usage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; What&amp;rsquo;s the TLDR of what WorkHelix provides for the companies?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; Basically what we do is we do a deep dive into a company&amp;rsquo;s labor system and say, &lt;em&gt;&amp;ldquo;Here&amp;rsquo;s where your workers are. Here are some opportunities to leverage AI. Oh, you&amp;rsquo;re already leveraging AI? Let&amp;rsquo;s take that work that you&amp;rsquo;re doing and measure quantifiably the impact that&amp;rsquo;s having in real dollars on your company.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Because the philosophy is: &lt;strong&gt;if you can&amp;rsquo;t measure it, then you can&amp;rsquo;t really manage it.&lt;/strong&gt; And we&amp;rsquo;re trying to help companies manage it because, quite frankly, I don&amp;rsquo;t think there&amp;rsquo;s a real grasp on how to manage using these tools, what&amp;rsquo;s effective, and what&amp;rsquo;s not.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="measuring-ai-impact-on-productivity"&gt;Measuring AI Impact on Productivity
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Are AI agents making developers more efficient?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; It&amp;rsquo;s a really loaded conversation. Constantly you&amp;rsquo;ll see published papers that seemingly contradict each other. One paper says, &amp;ldquo;Game&amp;rsquo;s over. All jobs are going to be replaced.&amp;rdquo; Another says, &amp;ldquo;We have evidence here that work can&amp;rsquo;t be replaced.&amp;rdquo; You can probably guess that it&amp;rsquo;s somewhere in the middle.&lt;/p&gt;
&lt;p&gt;If you measure it as &lt;strong&gt;more lines of code&lt;/strong&gt;, then the answer is yes. More lines of code are being produced at a rapid speed. But is it effective? By effective, I mean, you&amp;rsquo;re seeing more LLM-generated code being deployed into production where end users are actually using it.&lt;/p&gt;
&lt;p&gt;At a personal level, I would say 100%. I am far more effective as a developer with an LLM than not. And the way that I measure that is: &lt;em&gt;How many personal projects am I actually finishing in a given year?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Yeah, I think that&amp;rsquo;s something a lot of us are seeing—the ability to work on personal projects.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="personal-projects-and-ai-tools"&gt;Personal Projects and AI Tools
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; My output on personal projects has gone down rapidly with my number of children. But two things are being powerful:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Cloud Code on the phone:&lt;/strong&gt; The ability to code on the go is so cool.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Codex/Agents grinding:&lt;/strong&gt; I just see on Twitter people talking about how Codex can just grind away on things. The ability to give it a task and just let it go for a few hours has been very cool.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I just deployed last night. I was thinking, it would be cool if I could write more applications that I just deploy on my local network. We are both home lab nerds. I told Codex: &lt;em&gt;&amp;ldquo;Get me a VPS, install Coolify on it, and put it behind my Tailscale network.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It probably took 15 minutes of interacting with it for what would have been hours of work. That to me is so fun on the personal side.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; It is. And I think that there&amp;rsquo;s this universal pressure that we all know exists in this field of wanting to stay up to date and wanting to build more. I know the feeling of not feeling like I can take myself away from a problem. And now it&amp;rsquo;s like, I can check my phone and it&amp;rsquo;s working and then I can get back to being present.&lt;/p&gt;
&lt;p&gt;However, I am multitasking in ways that I never did before. I have five different terminals open, perhaps more, of different projects. No one is forcing me to do that&amp;hellip; but context switching and having all of these things you&amp;rsquo;re attending to in your mind is probably not super healthy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; The observation folks have had is that we&amp;rsquo;re becoming more like managers or conductors. I look at my manager&amp;rsquo;s calendar and she&amp;rsquo;s in meetings all day while in three different Slack discussions. That&amp;rsquo;s kind of more how we&amp;rsquo;re all operating now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; I think the expectations are slowly going to change for workers too. I haven&amp;rsquo;t felt it yet, but as a hard worker, if I can do seven things at once, suddenly people start to say, &lt;em&gt;&amp;ldquo;Well, this is just the rate that Ben works.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The rubber will meet the road when the quality of work starts going down or things start getting messed up. That&amp;rsquo;s when people start saying, &amp;ldquo;Okay, we got to make sure that there&amp;rsquo;s not too much being worked on simultaneously.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I agree. One of the things we&amp;rsquo;re still emphasizing a lot is human review. I&amp;rsquo;ve been working on a Claude skill to review code. Using it to look for the things that &lt;em&gt;I&lt;/em&gt; want to look for that are relevant to our team—it&amp;rsquo;s finding bugs that I never would have found.&lt;/p&gt;
&lt;p&gt;I’ve been pondering: How analogous is this to the transition off of punch card machines 40 years ago? You could bemoan that we missed the time when we had to really think through the instructions before putting them into the machine. Now, you get into a terminal and you can iterate fast.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; Looking back at economic history is generally very helpful. I look back at the 18th and 19th centuries to understand technological shifts.&lt;/p&gt;
&lt;p&gt;I think the &lt;strong&gt;printing press&lt;/strong&gt; is a good analogy. It was a democratization of information. I feel agents are parallel to that because it&amp;rsquo;s a democratization of being able to &lt;em&gt;build&lt;/em&gt; things. I don&amp;rsquo;t know what the consequences are going to be in 20 years, but I think they&amp;rsquo;ll be seismic.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="specialization-in-the-age-of-ai"&gt;Specialization in the Age of AI
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; One thing I think we wanted to talk about is this idea of specialization. Where does specialization go with the advent of agents? I think it&amp;rsquo;s being flattened. I&amp;rsquo;m not a front-end engineer, but I can now do front-end work because agents are getting better at it.&lt;/p&gt;
&lt;p&gt;However, I would argue that specialization is still going to be valuable. Just because you can generate art with Midjourney doesn&amp;rsquo;t mean the meaning of a human artist painting a picture isn&amp;rsquo;t valuable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; If you think about the &lt;strong&gt;&amp;ldquo;T-shaped engineer&amp;rdquo;&lt;/strong&gt; idea—breadth and depth—you wonder, does this just enable us to have a bigger T?&lt;/p&gt;
&lt;p&gt;I just merged a front-end task today using the Claude front-end skill on a Next.js project. I don&amp;rsquo;t know JavaScript particularly well, and I definitely don&amp;rsquo;t know Next.js. I implemented something that looks really nice and does what I want. This was previously an infinite-length task for me—go learn all the mechanics of Next.js.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; Opus 4.5 has been one of those game-changing moments. It’s more expensive, yes, but it can do more with less, so it starts becoming cheaper because you&amp;rsquo;re not coding as much.&lt;/p&gt;
&lt;p&gt;But there is a detriment. In the past, you would have struggled through AWS documentation or CSS, and by failing, you learned. Junior engineers might leverage these tools to get ahead, but as a consequence, they might not be super knowledgeable about the depth of these things themselves.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; It’s easy to have rose-colored glasses. The joke from 2010 to 2021 was that we just copy and paste things from Stack Overflow. In a lot of ways, we were really bad large language models!&lt;/p&gt;
&lt;p&gt;You have to continue to be &lt;strong&gt;deliberate to learn&lt;/strong&gt;. Yesterday I did some Kubernetes ops work. At the end, I asked my Cloud Code session: &lt;em&gt;&amp;ldquo;Teach me about all the things that we just did. Walk me through the steps.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; I completely agree. The tactics you&amp;rsquo;re using to get that learning might look different, but you have to be deliberate.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="toolset-evolution-over-time"&gt;Toolset Evolution Over Time
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Let&amp;rsquo;s step back and talk about what your toolset has been. How has that changed over the last year?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; My toolset has certainly changed. I used to focus on prompts. Now, I have an &lt;code&gt;agents.md&lt;/code&gt; or a &lt;code&gt;claude.md&lt;/code&gt; that I symlink so they are the same for different models.&lt;/p&gt;
&lt;p&gt;If there are three things I use, it&amp;rsquo;s:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; For that local vs. remote tracking of memory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Beads:&lt;/strong&gt; For consolidating how local memory is being used and breaking those down into atomic tasks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spec-Driven Development:&lt;/strong&gt; I think the most important thing you can do to improve the memory and agentic power of your tool is to simply break down the problem into a spec that the tool can always return to.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I do a lot of spec-driven stuff too. One of the Claude Code developers shared a skill just for building a spec by using the &amp;ldquo;Ask User Question&amp;rdquo; skill. It forces me to think through things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; I have a rule in my &lt;code&gt;claude.md&lt;/code&gt;. I say:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There should be an &lt;code&gt;architecture.md&lt;/code&gt; (diagrams, high-level goals).&lt;/li&gt;
&lt;li&gt;There should be a log that it returns to.&lt;/li&gt;
&lt;li&gt;Before we do anything, read the architecture, read the readme, read the log.&lt;/li&gt;
&lt;li&gt;Create &lt;strong&gt;Beads&lt;/strong&gt; tasks from our next step in the architecture document.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It tends to be very effective.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I keep coming back to Fred Brooks in &lt;em&gt;The Mythical Man-Month&lt;/em&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;I believe the hard part of building software to be the specification, design, and testing of this conceptual construct, not the labor of representing it.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I’m spending more time articulating what I want than I&amp;rsquo;ve ever done in my entire career.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; I just realized&amp;hellip; I would always say, &lt;em&gt;&amp;ldquo;Being in the code helps me figure out what I&amp;rsquo;m going to do.&amp;rdquo;&lt;/em&gt; I&amp;rsquo;m so the opposite now. That&amp;rsquo;s crazy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; And at the same time, it allows you the freedom to come up with a plan that you can throw away without being wed to it because you didn&amp;rsquo;t spend hours typing it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; That&amp;rsquo;s so astute. My ego is less invested in this code because I put less of myself into the code.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="resources--closing"&gt;Resources &amp;amp; Closing
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; This was great. Can you tell us a little bit about your books with O&amp;rsquo;Reilly?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ben:&lt;/strong&gt; I&amp;rsquo;ve written two publications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What Are AI Agents&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Managing Memory for AI Agents&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My focus for the last year and a half has been agent memory. If we can work with their memory effectively, I believe we can get what we want done faster.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; You can also find more about Ben at &lt;a class="link" href="htps://econoben.dev" &gt;econoben.dev&lt;/a&gt;. Thanks for joining us!&lt;/p&gt;</description></item><item><title>Lessons from Using Claude Code Effectively</title><link>https://tdhopper.com/blog/lessons-from-using-claude-code-effectively/</link><pubDate>Thu, 08 Jan 2026 08:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/lessons-from-using-claude-code-effectively/</guid><description>&lt;img src="https://tdhopper.com/images/mr-men-claude-code.png" alt="Featured image of post Lessons from Using Claude Code Effectively" /&gt;&lt;p&gt;I&amp;rsquo;ve been relying on Claude Code more and more since March 2025, not just for development but for &lt;a class="link" href="https://tdhopper.com/blog/how-im-using-ai-at-the-end-of-2025/" target="_blank" rel="noopener"
&gt;all things computer automation&lt;/a&gt;.
Developers are transitioning to be the conductor of the orchestra more than flute player. I believe 2025 marked a fundamental shift in software development. These tools are changing what our jobs are.&lt;/p&gt;
&lt;p&gt;Here are some practical patterns I&amp;rsquo;ve learned over the last year:&lt;/p&gt;
&lt;h2 id="let-it-drive-your-git"&gt;Let It Drive Your Git
&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;ve embraced letting Claude handle many of my git operations. It&amp;rsquo;s great at finding the right changes to commit and &lt;a class="link" href="https://github.com/tdhopper/dotfiles2.0/blob/master/.claude/skills/creating-pull-requests/SKILL.md" target="_blank" rel="noopener"
&gt;opening PRs&lt;/a&gt;. It&amp;rsquo;s also amazing at more complex tasks like cherry-picks, rebases, splitting large features into logical PRs.&lt;/p&gt;
&lt;p&gt;I recently had Claude split a large feature into four separate, logically sequenced PRs in a single session. The PRs were much cleaner than I would have done myself, and I &lt;a class="link" href="https://www.johndcook.com/blog/2015/12/22/automate-to-save-mental-energy-not-time/" target="_blank" rel="noopener"
&gt;saved mental energy&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="start-fresh-when-stuck"&gt;Start Fresh When Stuck
&lt;/h2&gt;&lt;p&gt;When Claude starts giving consistently bad answers, don&amp;rsquo;t keep pushing it. Clear the context and start a new chat. A fixated agent won&amp;rsquo;t suddenly become unfixated. Starting fresh gives you better results faster than trying to course-correct an existing conversation.&lt;/p&gt;
&lt;h2 id="use-it-for-system-automation-not-just-code"&gt;Use It for System Automation, Not Just Code
&lt;/h2&gt;&lt;p&gt;Claude Code is a misnomer; it&amp;rsquo;s a computer automation tool. It&amp;rsquo;s far better at using CLI tools than I&amp;rsquo;ll ever be. I use it for git operations, generating complex shell commands, and even working with cloud CLI tools to quickly learn data patterns from blob storage. Tasks that used to take me time and energy to piece together now happen in seconds.&lt;/p&gt;
&lt;h2 id="build-skills-and-slash-commands-for-repeated-tasks"&gt;Build Skills and Slash Commands for Repeated Tasks
&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;ve developed a &lt;a class="link" href="https://github.com/tdhopper/dotfiles2.0/blob/master/.claude/skills/creating-pull-requests/SKILL.md" target="_blank" rel="noopener"
&gt;skill for opening pull requests&lt;/a&gt; with well-structured, useful messages. I also use a slash command for &lt;a class="link" href="https://github.com/tdhopper/dotfiles2.0/tree/eb26bddb146951c51d94a0ed0cc56cdb5279f8b9/.claude/skills/reviewing-code" target="_blank" rel="noopener"
&gt;code reviews&lt;/a&gt; that focuses on correctness, performance impact, and unnecessary complexity. These tools make repeated workflows consistent and fast.&lt;/p&gt;
&lt;h2 id="fast-feedback-is-essential"&gt;Fast Feedback is Essential
&lt;/h2&gt;&lt;p&gt;The value of fast feedback can&amp;rsquo;t be overstated. Being able to execute code and validate assumptions quickly makes development fundamentally different. Agents benefit from this in the same way as humans; the more they can validate the work, the better their results.&lt;/p&gt;
&lt;p&gt;I think of Claude&amp;rsquo;s capability the way a &lt;a class="link" href="https://www.gcc.edu/Home/Staff-Directory/Staff-Detail/gary-l-thompson" target="_blank" rel="noopener"
&gt;favorite math professor&lt;/a&gt; once described his advantage: &amp;ldquo;I&amp;rsquo;m not smarter than you. I can just recover from mistakes faster than you.&amp;rdquo; Agents recover from errors at speeds humans can&amp;rsquo;t match (and, usually, don&amp;rsquo;t get as frustrated); feedback enables recovery.&lt;/p&gt;
&lt;h2 id="use-it-to-navigate-unfamiliar-code"&gt;Use It to Navigate Unfamiliar Code
&lt;/h2&gt;&lt;p&gt;Agents excel at building understanding of complex codebases. They can grep, search, and trace dependencies far faster than I can manually. When I joined a new project, Claude helped me understand where components fit together and how data flowed through the system.&lt;/p&gt;
&lt;h2 id="define-the-problem-before-starting"&gt;Define the Problem Before Starting
&lt;/h2&gt;&lt;p&gt;The hardest part is still &lt;a class="link" href="https://tdhopper.com/blog/no-silver-bullet/" target="_blank" rel="noopener"
&gt;understanding what problem you&amp;rsquo;re actually solving&lt;/a&gt;. I&amp;rsquo;ve wasted plenty of agent time by not thinking through what I wanted before asking. Spending five minutes clarifying the goal (sometimes &lt;a class="link" href="https://github.com/tdhopper/dotfiles2.0/blob/master/.claude/commands/spec.md" target="_blank" rel="noopener"
&gt;&lt;em&gt;with the agent&lt;/em&gt;&lt;/a&gt;) saves thirty minutes of iterating on the wrong solution.&lt;/p&gt;
&lt;h2 id="prototype-aggressively-throw-away-freely"&gt;Prototype Aggressively, Throw Away Freely
&lt;/h2&gt;&lt;p&gt;I prototype more now than I ever did before. The cost of trying something dropped dramatically. I&amp;rsquo;ll have Claude generate a quick script or CLI to test an idea, knowing I&amp;rsquo;ll probably throw it away. This changes how I approach problems: I can test assumptions with real implementations.&lt;/p&gt;</description></item><item><title>How I'm Using AI at the End of 2025</title><link>https://tdhopper.com/blog/how-im-using-ai-at-the-end-of-2025/</link><pubDate>Wed, 31 Dec 2025 12:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/how-im-using-ai-at-the-end-of-2025/</guid><description>&lt;img src="https://tdhopper.com/images/puppets.png" alt="Featured image of post How I'm Using AI at the End of 2025" /&gt;&lt;h2 id="chat"&gt;Chat
&lt;/h2&gt;&lt;p&gt;I subscribe to &lt;a class="link" href="https://openai.com/chatgpt/pricing/" target="_blank" rel="noopener"
&gt;ChatGPT Plus&lt;/a&gt; and using 5.2 Thinking for most of my chat sessions. According to my 2025 stats, I had 1,884 chats and 7,661 messages this year. I use this for researching things, asking home and car repair questions, as a sounding board for ideas, workout ideas, creating recipes, and answering my 8-year-old&amp;rsquo;s many questions (&amp;ldquo;what was the longest war in history&amp;rdquo;).&lt;/p&gt;
&lt;p&gt;At work, I have access to &lt;a class="link" href="https://openai.com/chatgpt/pricing/" target="_blank" rel="noopener"
&gt;ChatGPT Pro&lt;/a&gt; through work and occasionally use the 5.2 Pro for deeper thinking (e.g., I recently asked &amp;ldquo;What were the best in class ai models at the end of 2025&amp;rdquo;).&lt;/p&gt;
&lt;h2 id="image-generation"&gt;Image Generation
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://blog.google/technology/ai/nano-banana-pro/" target="_blank" rel="noopener"
&gt;Nano Banana Pro&lt;/a&gt; has become my only tool for image generation. It&amp;rsquo;s just &lt;em&gt;so good&lt;/em&gt;. I don&amp;rsquo;t subscribe to &lt;a class="link" href="https://gemini.google.com" target="_blank" rel="noopener"
&gt;Gemini&lt;/a&gt; personally, but I find the free tier gives me enough for things I want to generate (usually silly).&lt;/p&gt;
&lt;h2 id="code"&gt;Code
&lt;/h2&gt;&lt;p&gt;A year ago, I had not used a coding agent. I mostly relied on copying and pasting code into ChatGPT and auto complete in &lt;a class="link" href="https://github.com/features/copilot" target="_blank" rel="noopener"
&gt;Github Copilot&lt;/a&gt; to write code for me.&lt;/p&gt;
&lt;p&gt;Early in the year, I started using &lt;a class="link" href="https://www.cursor.com" target="_blank" rel="noopener"
&gt;Cursor&lt;/a&gt; at home and at work. By mid year, I had almost exclusively shifted to &lt;a class="link" href="https://www.anthropic.com/claude/code" target="_blank" rel="noopener"
&gt;Claude Code&lt;/a&gt; which I exclusively use in &lt;code&gt;--dangerously-skip-permissions&lt;/code&gt; mode. &lt;a class="link" href="https://www.anthropic.com/news/claude-opus-4-5" target="_blank" rel="noopener"
&gt;Opus 4.5&lt;/a&gt; has made that an even better experience, and I find it reliably helps me refine what I&amp;rsquo;m asking for and then provide top tier implementations. I live in Claude Code these days.&lt;/p&gt;
&lt;p&gt;A lesson I&amp;rsquo;ve learned over the past 6 months is that Claude Code isn&amp;rsquo;t just good for code automation but also computer automation. It&amp;rsquo;s far better at using CLI tools than I will ever be, and it&amp;rsquo;s ability to combine them enables automation of lots of work. For example, I rarely use &lt;code&gt;git&lt;/code&gt; directly any more and let Claude handle it for me (even &lt;a class="link" href="https://github.com/tdhopper/dotfiles2.0/blob/master/.claude/skills/creating-pull-requests/SKILL.md" target="_blank" rel="noopener"
&gt;opening pull requests&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I subscribe to the $20/month pro plan and have &lt;a class="link" href="https://support.claude.com/en/articles/12429409-extra-usage-for-paid-claude-plans" target="_blank" rel="noopener"
&gt;extra usage&lt;/a&gt; enabled so I can pay for tokens after hitting the Pro limits.&lt;/p&gt;
&lt;h2 id="writing"&gt;Writing
&lt;/h2&gt;&lt;p&gt;As Robert Ghrist &lt;a class="link" href="https://tdhopper.com/blog/writing-a-math-textbook-with-claude-with-professor-ghrist/" target="_blank" rel="noopener"
&gt;told me last year&lt;/a&gt;, Claude has the best writing style. I often rely on it to help me refine things I&amp;rsquo;m saying.&lt;/p&gt;
&lt;h2 id="learning"&gt;Learning
&lt;/h2&gt;&lt;p&gt;More and more I&amp;rsquo;m using LLMs to help me learn things better from understanding research to code bases to random topics of interest.&lt;/p&gt;
&lt;p&gt;For random topics, this often starts with a ChatGPT Thinking session with web search enabled.&lt;/p&gt;
&lt;p&gt;For code, I have found Claude Code can do excellent breakdowns and patiently fields my questions in discovering corners I don&amp;rsquo;t understand.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://notebooklm.google.com" target="_blank" rel="noopener"
&gt;NotebookLM&lt;/a&gt; is amazing at helping me work through a variety of sources. Generating slides based on some documents or sources is a common starting point for my understanding these days and helps me refine the questions I&amp;rsquo;m asking. I also love generating a podcast with a prompt that guides them in the direction I&amp;rsquo;m interested in so I can learn on the go.&lt;/p&gt;
&lt;h2 id="transcription"&gt;Transcription
&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;ve never been a bit voice to text person, because I mumble with a choppy cadence. However, I&amp;rsquo;m learning the modern tools do not care how annoying I am. I can stumble through my words for 15 minutes and they carefully transcribe and punctuate.&lt;/p&gt;
&lt;p&gt;I have been experimenting with &lt;a class="link" href="https://wisprflow.ai/" target="_blank" rel="noopener"
&gt;Whispr Flow&lt;/a&gt; for personal uses; this has largely been using their iOS keyboard for vastly superior text-to-speech that the built in Apple Option.&lt;/p&gt;
&lt;p&gt;I have &lt;a class="link" href="https://whispermemos.com/" target="_blank" rel="noopener"
&gt;Whisper Memos&lt;/a&gt; connect to my iPhone action button. This is a great one-trick-pony tool that starts recording on launch and then can email the transcript somewhere; mine goes to my &lt;a class="link" href="https://getdrafts.com/" target="_blank" rel="noopener"
&gt;Drafts inbox&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I just installed &lt;a class="link" href="https://sotto.to/" target="_blank" rel="noopener"
&gt;Sotto&lt;/a&gt; on my work machine this week which provides local model voice transcription. I&amp;rsquo;m interested in using this for interacting with Slack and Claude Code, but I haven&amp;rsquo;t made much progress yet.&lt;/p&gt;</description></item><item><title>The Ph.D. Blueprint: Five Questions to Design Your Future</title><link>https://tdhopper.com/blog/the-phd-blueprint/</link><pubDate>Wed, 10 Dec 2025 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/the-phd-blueprint/</guid><description>&lt;img src="https://tdhopper.com/blog/the-phd-blueprint/page-01.png" alt="Featured image of post The Ph.D. Blueprint: Five Questions to Design Your Future" /&gt;&lt;p&gt;Years ago, I published a &lt;a class="link" href="https://tdhopper.com/phd" &gt;collection of interviews&lt;/a&gt; with people I respected about whether a young person should pursue a Ph.D. I still hear from people who found them helpful.&lt;/p&gt;
&lt;p&gt;This is a slide deck version of the key questions from those interviews. If you&amp;rsquo;re wrestling with this decision, I hope it gives you a useful framework.&lt;/p&gt;
&lt;figure&gt;&lt;a href="page-02.png"&gt;&lt;img src="https://tdhopper.com/blog/the-phd-blueprint/page-02.png"
alt="You&amp;#39;re at a Crossroads: You&amp;#39;re academically talented, you enjoy your subject, and you have a funded Ph.D. offer. You also suspect you could get a good job right now. This guide gives you the right questions to ask yourself."&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;a href="page-03.png"&gt;&lt;img src="https://tdhopper.com/blog/the-phd-blueprint/page-03.png"
alt="Question 1: Why do you really want a Ph.D.?"&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;a href="page-04.png"&gt;&lt;img src="https://tdhopper.com/blog/the-phd-blueprint/page-04.png"
alt="Deconstructing Your Why: For Personal Satisfaction (love your subject), For a Specific Career (become a professor), or As a Default Path (warning against this)."&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;a href="page-05.png"&gt;&lt;img src="https://tdhopper.com/blog/the-phd-blueprint/page-05.png"
alt="Question 2: Are you prepared for the reality of the path?"&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;a href="page-06.png"&gt;&lt;img src="https://tdhopper.com/blog/the-phd-blueprint/page-06.png"
alt="Specification Sheet: The Ph.D. by the Numbers - Timeline (5-7 years), Career Odds (2-3% tenure track), Financials (10x less than industry), Attrition Rate (~35% drop out)."&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;a href="page-07.png"&gt;&lt;img src="https://tdhopper.com/blog/the-phd-blueprint/page-07.png"
alt="A Ph.D. is a fundamentally different experience: Undergrad is learning known information with clear deadlines; Ph.D. is producing new knowledge with little external guidance."&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;a href="page-08.png"&gt;&lt;img src="https://tdhopper.com/blog/the-phd-blueprint/page-08.png"
alt="Question 3: Are you built for this?"&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;a href="page-09.png"&gt;&lt;img src="https://tdhopper.com/blog/the-phd-blueprint/page-09.png"
alt="The Researcher&amp;#39;s Personality Test: Inquisitive, Disciplined, Obsessive, and Delusional (believing you can uncover something new and important)."&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;a href="page-10.png"&gt;&lt;img src="https://tdhopper.com/blog/the-phd-blueprint/page-10.png"
alt="The Skills Checklist: Technical (analytical software, programming), Academic (self-study, reading literature), Communication (structured writing)."&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;a href="page-11.png"&gt;&lt;img src="https://tdhopper.com/blog/the-phd-blueprint/page-11.png"
alt="Question 4: What are your alternatives (and have you seriously considered them)?"&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;a href="page-12.png"&gt;&lt;img src="https://tdhopper.com/blog/the-phd-blueprint/page-12.png"
alt="Exploring the Alternatives: Go to Industry, Get a Master&amp;#39;s First, or Take Time Off (with pros and cons for each)."&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;a href="page-13.png"&gt;&lt;img src="https://tdhopper.com/blog/the-phd-blueprint/page-13.png"
alt="Question 5: If you go, what is your plan to succeed?"&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;a href="page-14.png"&gt;&lt;img src="https://tdhopper.com/blog/the-phd-blueprint/page-14.png"
alt="Designing Your Program &amp;amp; Process: Choose Your Advisor Wisely, Investigate the Culture, Set Your Own Agenda."&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;a href="page-15.png"&gt;&lt;img src="https://tdhopper.com/blog/the-phd-blueprint/page-15.png"
alt="Your Blueprint is Personal: 1. Why do you really want this? 2. Are you prepared for the reality? 3. Are you built for this? 4. What are your alternatives? 5. What is your plan to succeed?"&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;hr&gt;
&lt;p&gt;Want to hear directly from Ph.D. holders about their experiences? Check out the full interview series at &lt;a class="link" href="http://tdhopper.com/phd" target="_blank" rel="noopener"
&gt;Should I Get a Ph.D.?&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Streaming Webcam to Browser with MediaMTX</title><link>https://tdhopper.com/blog/streaming-webcam-to-browser-with-mediamtx/</link><pubDate>Mon, 10 Nov 2025 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/streaming-webcam-to-browser-with-mediamtx/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Streaming Webcam to Browser with MediaMTX" /&gt;&lt;p&gt;I wanted to view my webcam feed from a browser on my local network. This turns out to be useful for testing video conferencing setups, monitoring a room, or checking camera angles before a recording. The challenge is that browsers can&amp;rsquo;t directly access webcam RTSP streams, but they do support WebRTC.&lt;/p&gt;
&lt;p&gt;After some tinkering, I found a clean solution: &lt;a class="link" href="https://ffmpeg.org/" target="_blank" rel="noopener"
&gt;ffmpeg&lt;/a&gt; captures the webcam and streams it via RTSP, while &lt;a class="link" href="https://github.com/bluenviron/mediamtx" target="_blank" rel="noopener"
&gt;MediaMTX&lt;/a&gt; converts that RTSP feed to WebRTC that browsers can consume.&lt;/p&gt;
&lt;h2 id="installation"&gt;Installation
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew install ffmpeg mediamtx
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="setup"&gt;Setup
&lt;/h2&gt;&lt;p&gt;You need to run two processes in parallel.&lt;/p&gt;
&lt;h3 id="1-start-mediamtx-server"&gt;1. Start MediaMTX server
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mediamtx
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This starts the MediaMTX server which will handle the protocol conversion and serve the web interface.&lt;/p&gt;
&lt;h3 id="2-stream-webcam-with-ffmpeg"&gt;2. Stream webcam with ffmpeg
&lt;/h3&gt;&lt;p&gt;In a separate terminal:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -f avfoundation -framerate &lt;span class="m"&gt;30&lt;/span&gt; -i &lt;span class="s2"&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -vcodec libx264 -preset ultrafast -tune zerolatency &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -f rtsp rtsp://127.0.0.1:8554/webcam
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;-f avfoundation&lt;/code&gt; flag is macOS-specific for capturing video devices. On Linux, you&amp;rsquo;d use &lt;code&gt;-f v4l2&lt;/code&gt; instead. The &lt;code&gt;&amp;quot;0&amp;quot;&lt;/code&gt; refers to the first video device (your default webcam). The &lt;code&gt;ultrafast&lt;/code&gt; preset and &lt;code&gt;zerolatency&lt;/code&gt; tuning minimize encoding overhead for real-time streaming.&lt;/p&gt;
&lt;h2 id="viewing-the-stream"&gt;Viewing the Stream
&lt;/h2&gt;&lt;p&gt;Once both processes are running, open your browser and navigate to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;http://127.0.0.1:8889/webcam/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The feed should appear in your browser. You can access this URL from any device on your local network by replacing &lt;code&gt;127.0.0.1&lt;/code&gt; with your machine&amp;rsquo;s IP address.&lt;/p&gt;
&lt;p&gt;MediaMTX handles all the WebRTC negotiation and serves a basic web player interface. If you need to customize the player or embed it elsewhere, MediaMTX also exposes a &lt;a class="link" href="https://github.com/bluenviron/mediamtx#webrtc-api" target="_blank" rel="noopener"
&gt;WebRTC API&lt;/a&gt; you can integrate with.&lt;/p&gt;</description></item><item><title>Interview with the Pybites podcast</title><link>https://tdhopper.com/blog/interview-with-the-pybites-podcast/</link><pubDate>Wed, 30 Jul 2025 09:18:00 -0400</pubDate><guid>https://tdhopper.com/blog/interview-with-the-pybites-podcast/</guid><description>&lt;img src="https://tdhopper.com/images/podcast-interview-min.png" alt="Featured image of post Interview with the Pybites podcast" /&gt;&lt;p&gt;&lt;a class="link" href="https://www.linkedin.com/in/bbelderbos/" target="_blank" rel="noopener"
&gt;Bob Belderbos&lt;/a&gt; invited me on the &lt;a class="link" href="https://www.pybitespodcast.com/1501156/episodes/17574426-198-tim-hopper-on-uv-and-smarter-python-development" target="_blank" rel="noopener"
&gt;Pybites podcast&lt;/a&gt; to talk about my career, the &lt;a class="link" href="https://pydevtools.com/handbook/" target="_blank" rel="noopener"
&gt;Python Developer Tooling Handbook&lt;/a&gt;, &lt;a class="link" href="https://pydevtools.com/handbook/reference/uv/" target="_blank" rel="noopener"
&gt;uv&lt;/a&gt;, &lt;a class="link" href="https://photos.tdhopper.com/" target="_blank" rel="noopener"
&gt;photography&lt;/a&gt;, and more. Bob was a great interviewer and I hope you enjoy.&lt;/p&gt;
&lt;iframe data-testid="embed-iframe" style="border-radius:12px" src="https://open.spotify.com/embed/episode/7xyb2HUcqPEpvLYo8qjQrV?utm_source=generator&amp;theme=0&amp;t=0" width="100%" height="352" frameBorder="0" allowfullscreen="" allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"&gt;&lt;/iframe&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.pybitespodcast.com/1501156/episodes/17574426-198-tim-hopper-on-uv-and-smarter-python-development" target="_blank" rel="noopener"
&gt;Listen on pybitespodcast.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://open.spotify.com/episode/7xyb2HUcqPEpvLYo8qjQrV?si=b828375cdfc243fa" target="_blank" rel="noopener"
&gt;Listen on Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://podcasts.apple.com/us/podcast/198-tim-hopper-on-uv-and-smarter-python-development/id1545551340?i=1000719733285" target="_blank" rel="noopener"
&gt;Listen on Apple Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.youtube.com/watch?v=5DT_zL7SiAI" target="_blank" rel="noopener"
&gt;Watch on Youtube&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>How Developers are Using LLMs</title><link>https://tdhopper.com/blog/how-developers-are-using-llms/</link><pubDate>Tue, 11 Mar 2025 12:12:00 +0000</pubDate><guid>https://tdhopper.com/blog/how-developers-are-using-llms/</guid><description>&lt;img src="https://tdhopper.com/images/ai-agent-coder.jpg" alt="Featured image of post How Developers are Using LLMs" /&gt;&lt;p&gt;I&amp;rsquo;m very interested in &lt;em&gt;how&lt;/em&gt; people are using LLMs to augment their work, especially in software development.&lt;/p&gt;
&lt;p&gt;I did three interviews with friends who are using them in different ways.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/blog/building-a-startup-with-ai-with-jowanza-joseph/" target="_blank" rel="noopener"
&gt;Jowanza Joseph&lt;/a&gt; is building a startup where &lt;a class="link" href="https://www.cursor.com" target="_blank" rel="noopener"
&gt;Cursor&lt;/a&gt; is doing much of the legwork.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/blog/ai-assisted-programming-with-ravi-mody/" target="_blank" rel="noopener"
&gt;Ravi Mody&lt;/a&gt; primarily interacts with web-based chat clients like &lt;a class="link" href="https://chatgpt.com" target="_blank" rel="noopener"
&gt;ChatGPT&lt;/a&gt; to write his code.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/blog/writing-a-math-textbook-with-claude-with-professor-ghrist/" target="_blank" rel="noopener"
&gt;Robert Ghrist&lt;/a&gt; &amp;ldquo;directed&amp;rdquo; &lt;a class="link" href="https://claude.ai" target="_blank" rel="noopener"
&gt;Claude&lt;/a&gt; on how to write a beautiful linear algebra textbook in his style.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;ve enjoyed several other pieces lately on how others are using LLMs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://harper.blog/2025/02/16/my-llm-codegen-workflow-atm/" target="_blank" rel="noopener"
&gt;Harper Reed&lt;/a&gt; posted a detailed account of his workflow to robustly generate new code bases. I&amp;rsquo;ve been playing with his provided prompts and am impressed.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://simonwillison.net/2025/Mar/11/using-llms-for-code/" target="_blank" rel="noopener"
&gt;Simon Willison&lt;/a&gt; wrote about using LLMs to write code, especially enabling him to build things &lt;em&gt;he wouldn&amp;rsquo;t bother with otherwise&lt;/em&gt;. He shows concrete examples with his &lt;a class="link" href="https://tools.simonwillison.net" target="_blank" rel="noopener"
&gt;LLM generated tools page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://youtu.be/EWvNQjAaOHw?si=mKi9NeISLy0DGEbT" target="_blank" rel="noopener"
&gt;Andrej Karpathy&lt;/a&gt; published a 2-hour long video that is the best introduction I&amp;rsquo;ve seen to the current state of LLMs and what they can do.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;d love to hear about your use of LLMs. Please drop me a note in the form below.&lt;/p&gt;</description></item><item><title>Writing a Math Textbook with Claude with Professor Ghrist</title><link>https://tdhopper.com/blog/writing-a-math-textbook-with-claude-with-professor-ghrist/</link><pubDate>Sat, 18 Jan 2025 10:03:00 +0000</pubDate><guid>https://tdhopper.com/blog/writing-a-math-textbook-with-claude-with-professor-ghrist/</guid><description>&lt;img src="https://tdhopper.com/images/podcast.png" alt="Featured image of post Writing a Math Textbook with Claude with Professor Ghrist" /&gt;&lt;p&gt;In this episode, Professor Robert Ghrist from the University of Pennsylvania discusses his beautiful new linear algebra book created with the help of the Claude LLM in just 55 days. Professor Ghrist explains how he used Claude to assist with the book’s outline, writing style, formatting, and consistency, emphasizing his role as a director guiding the LLM.&lt;/p&gt;
&lt;h2 id="listen"&gt;Listen
&lt;/h2&gt;&lt;iframe src="https://open.spotify.com/embed/episode/1JMdPOSlmk2oSsiYEfJzIs" width="100%" height="232" frameBorder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="links"&gt;Links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www2.math.upenn.edu/~ghrist/" target="_blank" rel="noopener"
&gt;Professor Robert Grist&lt;/a&gt; from the University of Pennsylvania.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://x.com/robertghrist" target="_blank" rel="noopener"
&gt;Professor Ghrist&amp;rsquo;s Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Professor G&amp;rsquo;s &lt;a class="link" href="https://x.com/robertghrist/status/1874105560641220830" target="_blank" rel="noopener"
&gt;Twitter thread&lt;/a&gt; about composing a linear algebra book with the assistance of the Claude LLM.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www2.math.upenn.edu/~ghrist/FLCT/" target="_blank" rel="noopener"
&gt;Funny Little Calculus Text (FLC)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www2.math.upenn.edu/~ghrist/notes.html" target="_blank" rel="noopener"
&gt;Elementary Applied Topology&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www2.math.upenn.edu/~ghrist/preprints/LAEF.pdf" target="_blank" rel="noopener"
&gt;PDF of the new linear algebra book&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://amzn.to/4apUkbe" target="_blank" rel="noopener"
&gt;Order a print copy of the linear algebra book from Amazon&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="subscribe"&gt;Subscribe
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/podcast/feed" target="_blank" rel="noopener"
&gt;RSS Feed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://podcasts.apple.com/us/podcast/into-the-hopper/id1499693201" target="_blank" rel="noopener"
&gt;Apple Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://open.spotify.com/show/63NrgKMVb0VTwkklGboIjy" target="_blank" rel="noopener"
&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://overcast.fm/itunes1499693201/into-the-hopper" target="_blank" rel="noopener"
&gt;Overcast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>The Sony A7 iii after 5 years</title><link>https://tdhopper.com/blog/sonya7iii/</link><pubDate>Sun, 12 Jan 2025 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sonya7iii/</guid><description>&lt;img src="https://tdhopper.com/blog/sonya7iii/20210215-DSC02383.jpg" alt="Featured image of post The Sony A7 iii after 5 years" /&gt;&lt;p&gt;Over the last 10 years, I&amp;rsquo;ve become more interested in the art of photography. What
started with me try to get better photographs of waterfalls than I could with my iPhone
five has led to an era where I mostly photograph my four children (ages one to seven).&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve used a lot of different cameras over the years&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;, but the one that has consistently
brought me the most joy and satisfaction is my Sony a7 III. I&amp;rsquo;ve had the camera for five
years, and it
consistently helps me make delightful and beautiful images of my kids that I will
cherish for the rest of my life.&lt;/p&gt;
&lt;p&gt;When my second child was almost due, some friends gave me money as a gift, and I used
that to move from my a6500 setup to a used Sony a7 III. While it isn&amp;rsquo;t the camera I use
the most since my iPhone de jour is always closest at hand, I&amp;rsquo;m always glad to have
pulled it out. While modern iPhones have incredible cameras that make beautiful images
in the right conditions, the conditions I&amp;rsquo;m photographing my kids in are rarely ideal,
and the photos I take with my iPhone often disappoint me with their lack of sharpness,
motion blur, and lack of subject separation.&lt;/p&gt;
&lt;p&gt;My full-frame Sony mirrorless, on the other hand, rarely disappoints.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/blog/sonya7iii/20211021-DSC06619.jpg"
width="2630"
height="4096"
srcset="https://tdhopper.com/blog/sonya7iii/20211021-DSC06619_hu_915ca3d2d94a9704.jpg 480w, https://tdhopper.com/blog/sonya7iii/20211021-DSC06619_hu_1f1605b752421b53.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="64"
data-flex-basis="154px"
&gt; &lt;img src="https://tdhopper.com/blog/sonya7iii/20210215-DSC02383.jpg"
width="4096"
height="3566"
srcset="https://tdhopper.com/blog/sonya7iii/20210215-DSC02383_hu_725b2b86f748a360.jpg 480w, https://tdhopper.com/blog/sonya7iii/20210215-DSC02383_hu_3b73ad173852b2ca.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="114"
data-flex-basis="275px"
&gt; &lt;img src="https://tdhopper.com/blog/sonya7iii/20210729-DSC05630.jpg"
width="2630"
height="4096"
srcset="https://tdhopper.com/blog/sonya7iii/20210729-DSC05630_hu_f4713bbc0ff6a780.jpg 480w, https://tdhopper.com/blog/sonya7iii/20210729-DSC05630_hu_986fd45520e59888.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="64"
data-flex-basis="154px"
&gt;&lt;/p&gt;
&lt;h2 id="fast-and-responsive"&gt;Fast and Responsive
&lt;/h2&gt;&lt;p&gt;When I&amp;rsquo;m not photographing my kids, my other main photography interest is &lt;a class="link" href="https://photos.tdhopper.com/great-blue-herons" target="_blank" rel="noopener"
&gt;wildlife&lt;/a&gt;.
Wildlife photography presents a unique set of challenges - conditions are rarely ideal,
and subjects move unpredictably. As a wildlife photographer, I&amp;rsquo;m constantly pursuing
what Jay Maisel calls &lt;a class="link" href="https://www.jaymaisel.com/products/light-gesture-and-color" target="_blank" rel="noopener"
&gt;Gesture, Light, and Color&lt;/a&gt; in his book. You have to stay ready,
alert to fleeting opportunities.&lt;/p&gt;
&lt;p&gt;Photography kids is exactly the same.&lt;/p&gt;
&lt;p&gt;Having a fast and responsive camera isn&amp;rsquo;t everything - the tool doesn&amp;rsquo;t make the art -
but it opens doors to capturing those precious moments that might otherwise slip away.
The Sony a7 III excels in this regard in several key ways:&lt;/p&gt;
&lt;p&gt;First, it boots almost instantly. While many cameras need several seconds to wake up, my
Sony (like other prosumer/professional cameras) is ready to shoot the moment I flip the
switch. This responsiveness has saved countless shots that slower cameras would have
missed. The quick recovery from sleep mode is equally valuable.&lt;/p&gt;
&lt;p&gt;Beyond pure speed, the camera&amp;rsquo;s physical controls are thoughtfully designed for rapid
adjustments. When shooting in aperture priority mode, I can quickly dial in aperture and
exposure compensation. If I need to freeze fast action, switching to shutter priority
and adjusting speed happens in seconds. The customizable buttons let me access my
most-used settings without diving into menus.&lt;/p&gt;
&lt;h2 id="amazing-autofocus"&gt;Amazing Autofocus
&lt;/h2&gt;&lt;p&gt;Another aspect of speed is the camera&amp;rsquo;s autofocus. Because my kids are on the move, I
almost always shoot in continuous autofocus. The face detection does a great job keeping
track of my kids, and I consistently get sharp images. Autofocus tends to be great on
modern cameras, but I love having a camera where it excels.&lt;/p&gt;
&lt;p&gt;The camera also also has eye detection autofocus, though it requires pressing a separate
button unlike more modern cameras. I have my AEL button set to eye autofocus, and I
instinctively press it when trying to get a portrait.&lt;/p&gt;
&lt;p&gt;Autofocus is one area where cameras are improving every generation, and I look forward to
benefiting from that next time I upgrade&lt;/p&gt;
&lt;h2 id="large-sensor"&gt;Large Sensor
&lt;/h2&gt;&lt;p&gt;Unlike all the other cameras I&amp;rsquo;ve used, the Sony a7 III is a full-frame camera. This
large sensor allows the camera to capture more light and produce cleaner images in low
light settings. While my iPhone often disappoints indoors and in the evenings, my
Sony&amp;ndash;especially paired with a fast prime lens&amp;ndash;consistently produces sharp and
beautiful images, even at ISO
12800.&lt;/p&gt;
&lt;p&gt;The large sensor also allows for more subject separation (bokeh) than smaller sensors.
When I&amp;rsquo;m photographing my kids, I often want to blur the background to make the subject
stand out. The Sony a7 III does this beautifully.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/blog/sonya7iii/20231109-DSC01340.jpg"
width="2630"
height="4096"
srcset="https://tdhopper.com/blog/sonya7iii/20231109-DSC01340_hu_6101dd49b93fff4b.jpg 480w, https://tdhopper.com/blog/sonya7iii/20231109-DSC01340_hu_b1657b9d9e47b5ef.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="64"
data-flex-basis="154px"
&gt; &lt;img src="https://tdhopper.com/blog/sonya7iii/20210528-DSC04315.jpg"
width="4096"
height="2641"
srcset="https://tdhopper.com/blog/sonya7iii/20210528-DSC04315_hu_5e7566f57276ee3b.jpg 480w, https://tdhopper.com/blog/sonya7iii/20210528-DSC04315_hu_d1bf6996e20866af.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="155"
data-flex-basis="372px"
&gt; &lt;img src="https://tdhopper.com/blog/sonya7iii/20241223-DSC04982.jpg"
width="2630"
height="4096"
srcset="https://tdhopper.com/blog/sonya7iii/20241223-DSC04982_hu_1c439e872a7fabdc.jpg 480w, https://tdhopper.com/blog/sonya7iii/20241223-DSC04982_hu_1822c1ca75678e87.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="64"
data-flex-basis="154px"
&gt;&lt;/p&gt;
&lt;h2 id="durable-and-reliable"&gt;Durable and Reliable
&lt;/h2&gt;&lt;p&gt;The Sony a7 III is a professional-grade camera. It&amp;rsquo;s built to last. While I try not to
abuse it, it&amp;rsquo;s been banged around on hikes, sandy at the beach, and bounced around in my
backpack. Over five years later, you&amp;rsquo;d have no idea other than some surface wear on the
body.&lt;/p&gt;
&lt;p&gt;Because the camera is weather-sealed, I can take it out in the rain or snow without
worrying about damaging it. Last week, I took my five year old out in freezing rain and
snow and was able to capture a few beautiful (low-light!) images of him. When I came
home, I just wiped the camera down with a towel and didn&amp;rsquo;t have to worry about it.&lt;/p&gt;
&lt;p&gt;The camera also has excellent battery life, unlike some compact and mirrorless cameras
I&amp;rsquo;ve used. The battery lasts for hundreds of shots, and I can go weeks without charging it.
I almost never have a trip or outing that requires charging. With a USB-C port, the
camera is easy to charge whenever I need to.&lt;/p&gt;
&lt;h2 id="image-quality"&gt;Image Quality
&lt;/h2&gt;&lt;p&gt;The Sony a7 III produces beautiful images. The colors are vibrant, the images are sharp,
and the dynamic range is excellent. The camera produces images that are a joy to look at
and share. Without fail, my favorite photos of my kids are the ones I&amp;rsquo;ve taken with my
Sony.&lt;/p&gt;
&lt;h2 id="silent-shooting"&gt;Silent Shooting
&lt;/h2&gt;&lt;p&gt;A small benefit of the mirrorless design is the ability to shoot totally silently.
This isn&amp;rsquo;t a feature I knew I wanted, but it has been valuable on many occasions.
Sometimes when photographing kids, it&amp;rsquo;s helpful to be able to take a photo without
them knowing.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/blog/sonya7iii/20220326-DSC07952.jpg"
width="4096"
height="3566"
srcset="https://tdhopper.com/blog/sonya7iii/20220326-DSC07952_hu_7911cc54c95a9edc.jpg 480w, https://tdhopper.com/blog/sonya7iii/20220326-DSC07952_hu_d7dbf263801e3cf4.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="114"
data-flex-basis="275px"
&gt; &lt;img src="https://tdhopper.com/blog/sonya7iii/20240827-DSC04280.jpg"
width="3116"
height="4096"
srcset="https://tdhopper.com/blog/sonya7iii/20240827-DSC04280_hu_2602d1511063c173.jpg 480w, https://tdhopper.com/blog/sonya7iii/20240827-DSC04280_hu_271aaa23cffb9be.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="76"
data-flex-basis="182px"
&gt; &lt;img src="https://tdhopper.com/blog/sonya7iii/20240219-DSC02347.jpg"
width="2630"
height="4096"
srcset="https://tdhopper.com/blog/sonya7iii/20240219-DSC02347_hu_7b93dbaca250726.jpg 480w, https://tdhopper.com/blog/sonya7iii/20240219-DSC02347_hu_1e99e0d378f33ca8.jpg 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="64"
data-flex-basis="154px"
&gt;&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Capturing the fleeting moments of my children&amp;rsquo;s life is a joy and a privilege. Like most
parents of our age, I mostly use my iPhone to capture these moments. However, almost
without exception,
the photos I take with my Sony a7 III are the ones I cherish the most. The camera is
fast, responsive, and reliable. It produces beautiful images that I will keep looking at
for the rest of my life.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Cameras I&amp;rsquo;ve used since 2014: iPhones from 5 up to 16 Pro Max, &lt;a class="link" href="https://www.dpreview.com/products/olympus/compacts/oly_tg3" target="_blank" rel="noopener"
&gt;Olympus Tough TG-3&lt;/a&gt;, &lt;a class="link" href="https://www.dpreview.com/products/sony/slrs/sony_nex5t" target="_blank" rel="noopener"
&gt;Sony NEX-5T&lt;/a&gt;, &lt;a class="link" href="https://www.dpreview.com/reviews/canon-eos-7d-mark-ii" target="_blank" rel="noopener"
&gt;Canon 7D II&lt;/a&gt;, &lt;a class="link" href="https://www.dpreview.com/reviews/sony-alpha-a6500" target="_blank" rel="noopener"
&gt;Sony a6500&lt;/a&gt;, &lt;a class="link" href="https://www.dpreview.com/reviews/sony-cybershot-dsc-rx100-v-review" target="_blank" rel="noopener"
&gt;Sony RX100V&lt;/a&gt;, &lt;a class="link" href="https://www.dpreview.com/reviews/fujifilm-x100v-review" target="_blank" rel="noopener"
&gt;Fuji X100V&lt;/a&gt;, &lt;a class="link" href="https://www.dpreview.com/reviews/sony-a7-iii-review" target="_blank" rel="noopener"
&gt;Sony a7 III&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>AI interpretation of tdhopper.com</title><link>https://tdhopper.com/blog/ai-interpretation-of-tdhopper.com/</link><pubDate>Tue, 31 Dec 2024 10:54:00 +0000</pubDate><guid>https://tdhopper.com/blog/ai-interpretation-of-tdhopper.com/</guid><description>&lt;img src="https://tdhopper.com/images/computer-phone.png" alt="Featured image of post AI interpretation of tdhopper.com" /&gt;&lt;p&gt;I dumped all my posts from the last 14 years into &lt;a class="link" href="https://claude.ai" target="_blank" rel="noopener"
&gt;Claude&lt;/a&gt; and asked it to make any conclusions about me from them. These are spot on:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Evolution in Technical Identity&lt;/strong&gt;: Your writing shows an ongoing tension between pure mathematics and practical engineering. Early posts focus heavily on mathematical concepts, but over time you seem to embrace a more pragmatic engineering mindset while still maintaining deep appreciation for theoretical foundations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Teaching as Processing&lt;/strong&gt;: You often write about technical concepts right after learning them yourself, using blogging/teaching as a way to solidify your own understanding. This suggests you process and internalize knowledge best by trying to explain it to others.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Meta-Learning Focus&lt;/strong&gt;: Many of your posts aren&amp;rsquo;t just about technical topics, but about HOW to learn technical topics. You seem particularly interested in the process of skill acquisition and knowledge transfer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Honesty About Failure&lt;/strong&gt;: Unlike many technical bloggers, you&amp;rsquo;re remarkably open about professional setbacks and rejections. Your post about being rejected from data science jobs stands out as unusually candid for the field.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Long-Form Thinking&lt;/strong&gt;: You maintain ideas and threads over very long periods. For example, you&amp;rsquo;ve written multiple follow-ups and updates to your &amp;ldquo;How I Became a Data Scientist&amp;rdquo; post over several years as your perspective evolved.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation as Service&lt;/strong&gt;: Many of your technical posts seem motivated by wanting to document solutions to problems you struggled with, specifically to help others avoid the same struggles. This suggests a strong service orientation in your technical writing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-Domain Connection Making&lt;/strong&gt;: You often draw interesting parallels between seemingly unrelated domains - connecting operations research to data science, or academic math to practical programming.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;</description></item><item><title>Accessing my home server around the world with custom domain names</title><link>https://tdhopper.com/blog/accessing-my-home-server-around-the-world-with-custom-domain-names/</link><pubDate>Fri, 20 Dec 2024 11:47:00 +0000</pubDate><guid>https://tdhopper.com/blog/accessing-my-home-server-around-the-world-with-custom-domain-names/</guid><description>&lt;img src="https://tdhopper.com/images/network.png" alt="Featured image of post Accessing my home server around the world with custom domain names" /&gt;&lt;p&gt;Last year, I invested in a &lt;a class="link" href="https://www.synology.com/" target="_blank" rel="noopener"
&gt;Synology NAS&lt;/a&gt; as a home server and backup drive. Like many Synology users, I&amp;rsquo;ve quickly found it has many more uses than I initially expected. Beyond the usual file storage and Time Machine backups, my Synology has become the backbone of my home technology.&lt;/p&gt;
&lt;p&gt;On my NAS, I use &lt;a class="link" href="https://https://pi-hole.net" target="_blank" rel="noopener"
&gt;Pihole&lt;/a&gt; for network-wide ad blocking. A Webdav server syncs my &lt;a class="link" href="https://devonthink.com" target="_blank" rel="noopener"
&gt;Devonthink&lt;/a&gt; databases to manage my research and documents. I back up my Apple Photos library on &lt;a class="link" href="https://www.synology.com/en-us/dsm/feature/photos" target="_blank" rel="noopener"
&gt;Synology Photos&lt;/a&gt;. I&amp;rsquo;m transitioning my home camera setup to &lt;a class="link" href="https://www.synology.com/en-global/surveillance" target="_blank" rel="noopener"
&gt;Surveillance Station&lt;/a&gt; (so all my footage is now local). Using Synology&amp;rsquo;s Docker support, I run &lt;a class="link" href="https://openaudible.org" target="_blank" rel="noopener"
&gt;OpenAudible&lt;/a&gt; to manage my audiobook collection. I run &lt;a class="link" href="https://www.synology.com/en-us/dsm/feature/mailplus" target="_blank" rel="noopener"
&gt;Synology MailPlus&lt;/a&gt; to back up my email. And there&amp;rsquo;s more!&lt;/p&gt;
&lt;p&gt;Of course, now that I have all these services running, I want to access them from anywhere.
Synology offers the &lt;a class="link" href="https://kb.synology.com/en-us/DSM/help/DSM/AdminCenter/connection_quickconnect?version=7" target="_blank" rel="noopener"
&gt;QuickConnect&lt;/a&gt; service, but I wanted something more secure and independent.&lt;/p&gt;
&lt;p&gt;While browsing Synology&amp;rsquo;s subreddit and Facebook Groups, I discovered Tailscale, a mesh VPN service for solving my exact problem. After (easily) installing Tailscale on my Synology, personal computers, and phone, my devices are securely connected from anywhere in the world as if they were on the same local network. With Tailscale, my NAS has a private, static IP address through which I can access the services via their dedicated ports.&lt;/p&gt;
&lt;p&gt;To make my services more accessible, I purchased a domain name through &lt;a class="link" href="https://cloudflare.com" target="_blank" rel="noopener"
&gt;Cloudflare&lt;/a&gt;. I created a subdomain with an A record pointing to my always-on Mac Mini&amp;rsquo;s Tailscale IP address for each of my services. This IP address is accessible from anywhere but only through my devices authenticated with Tailscale.&lt;/p&gt;
&lt;p&gt;I run a &lt;a class="link" href="https://caddyserver.com" target="_blank" rel="noopener"
&gt;Caddy server&lt;/a&gt; on my Mac Mini as a reverse proxy to map the subdomains to my Synology services. The magic of Caddy is that it automatically manages SSL certificates through Let&amp;rsquo;s Encrypt, giving me https access to my services.&lt;/p&gt;
&lt;p&gt;Caddy was painless to install with Homebrew and easy to configure with a Caddyfile. Here&amp;rsquo;s an example of my Caddyfile:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cat Caddyfile
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; email &amp;lt;MY_EMAIL&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;*.mydomain.com mydomain.com {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; tls {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; dns cloudflare &amp;lt;CLOUDFLARE_API_TOKEN&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pihole.mydomain.com {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; reverse_proxy 192.168.68.21:8765
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;webdav.mydomain.com {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; reverse_proxy https://192.168.68.21:5006 {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; transport http {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; tls_insecure_skip_verify
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The last piece of the puzzle involved configuring the Synology to know these subdomains. In the DSM Control Panel, under the Login Portal&amp;rsquo;s Applications tab, I set up the specific subdomains that tell Synology how to launch applications like MailPlus and Synology Photos. With this final step complete, I don&amp;rsquo;t need to remember IP addresses and ports. I can access all my services through simple, memorable domain names from anywhere in the world.&lt;/p&gt;
&lt;p&gt;This setup has transformed how I interact with my home server, making it both more powerful and more convenient to use. The combination of Tailscale&amp;rsquo;s security, Cloudflare&amp;rsquo;s domain management, and Caddy&amp;rsquo;s reverse proxy capabilities has created a easy to configure and user-friendly system that serves all my home server needs.&lt;/p&gt;</description></item><item><title>Building a Startup with AI with Jowanza Joseph</title><link>https://tdhopper.com/blog/building-a-startup-with-ai-with-jowanza-joseph/</link><pubDate>Tue, 17 Dec 2024 12:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/building-a-startup-with-ai-with-jowanza-joseph/</guid><description>&lt;img src="https://tdhopper.com/images/podcast.png" alt="Featured image of post Building a Startup with AI with Jowanza Joseph" /&gt;&lt;h2 id="listen"&gt;Listen
&lt;/h2&gt;&lt;iframe src="https://open.spotify.com/embed/episode/5ZmKcu4HgAyAvL5sJFY9mP" width="100%" height="232" frameBorder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="links"&gt;Links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://twitter.com/jowanza" target="_blank" rel="noopener"
&gt;Jowanza&amp;rsquo;s Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://jowanza.com" target="_blank" rel="noopener"
&gt;Jowanza&amp;rsquo;s website &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.parakeetfinancial.com/" target="_blank" rel="noopener"
&gt;Parakeet Financial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/blog/ai-assisted-programming-with-ravi-mody/" target="_blank" rel="noopener"
&gt;AI Assisted Programming with Ravi Mody&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://cursor.sh/" target="_blank" rel="noopener"
&gt;Cursor Editor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.magicpatterns.com/" target="_blank" rel="noopener"
&gt;Magic Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://v0.dev/" target="_blank" rel="noopener"
&gt;V0 by Vercel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.crewai.com/" target="_blank" rel="noopener"
&gt;Crew AI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/features/copilot" target="_blank" rel="noopener"
&gt;GitHub Copilot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="subscribe"&gt;Subscribe
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/podcast/feed" target="_blank" rel="noopener"
&gt;RSS Feed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://podcasts.apple.com/us/podcast/into-the-hopper/id1499693201" target="_blank" rel="noopener"
&gt;Apple Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://open.spotify.com/show/63NrgKMVb0VTwkklGboIjy" target="_blank" rel="noopener"
&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://overcast.fm/itunes1499693201/into-the-hopper" target="_blank" rel="noopener"
&gt;Overcast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>A builder of systems, a path to unveil</title><link>https://tdhopper.com/blog/a-builder-of-systems-a-path-to-unveil/</link><pubDate>Thu, 05 Dec 2024 10:18:00 +0000</pubDate><guid>https://tdhopper.com/blog/a-builder-of-systems-a-path-to-unveil/</guid><description>&lt;img src="https://tdhopper.com/images/singing.jpeg" alt="Featured image of post A builder of systems, a path to unveil" /&gt;&lt;p&gt;I recently joined the Spotify &lt;a class="link" href="https://research.atspotify.com/audio-intelligence/" target="_blank" rel="noopener"
&gt;Audio Intelligence&lt;/a&gt; team as a machine learning infrastructure engineer. At our team offsite, we did lightning round introductions of ourselves, so I used &lt;a class="link" href="https://www.linkedin.com/company/aichatgpt/" target="_blank" rel="noopener"
&gt;ChatGPT&lt;/a&gt; and &lt;a class="link" href="https://www.linkedin.com/company/tad-ai/" target="_blank" rel="noopener"
&gt;tad.ai&lt;/a&gt; to generate a song about my career history.&lt;/p&gt;
&lt;div class="video-wrapper"&gt;
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/v8mc-26Q6fQ"
allowfullscreen
title="YouTube Video"
&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Started at RTI, solving problems with care,&lt;br&gt;
Social media models and GIS in the air. &lt;br&gt;
Then Parsley called, real-time streams to refine, &lt;br&gt;
With Python and Storm, data aligned.&lt;/p&gt;
&lt;p&gt;From research to engineering, skills took flight, &lt;br&gt;
Building platforms through day and night. &lt;br&gt;
Each role a step, each tool a stage, &lt;br&gt;
A career evolving, page by page.&lt;/p&gt;
&lt;p&gt;At Cylance, you scaled machine learning with ease, &lt;br&gt;
Serverless pipelines and malware’s disease. &lt;br&gt;&lt;/p&gt;
&lt;p&gt;D.T.N. brought platforms, Airflow in play, &lt;br&gt;
Cloud tools for data, clearing the way. &lt;br&gt;
Varo’s features took center stage, &lt;br&gt;
With Tecton and Airflow, systems engaged.&lt;/p&gt;
&lt;p&gt;Fraud detection, insights in view, &lt;br&gt;
Scaling platforms, something new. &lt;br&gt;
From data science to ML core, &lt;br&gt;
Building systems that teams adore.&lt;/p&gt;
&lt;p&gt;Now at Spotify, the journey goes on, &lt;br&gt;
Tim’s career—focused, strong. &lt;br&gt;
Each milestone builds the next design, &lt;br&gt;
A career in motion, sharp and aligned.&lt;/p&gt;
&lt;p&gt;Tim’s journey tells a thoughtful tale, &lt;br&gt;
A builder of systems, a path to unveil.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Lessons from Developing a Machine Learning Feature Platform</title><link>https://tdhopper.com/blog/lessons-from-developing-a-machine-learning-feature-platform/</link><pubDate>Thu, 14 Nov 2024 12:00:00 -0500</pubDate><guid>https://tdhopper.com/blog/lessons-from-developing-a-machine-learning-feature-platform/</guid><description>&lt;img src="https://tdhopper.com/images/featureplat.png" alt="Featured image of post Lessons from Developing a Machine Learning Feature Platform" /&gt;&lt;p&gt;Much of my career has been focused on helping machine learning researchers get the data they need, where they need it, and when they need it.
Over the past three years, I helped lead the development of a new machine learning feature platform at the bank startup where I worked.
Our platform could serve a variety of machine learning and analytics applications by enabling creation of custom data transformations to generate batch and real time data for model training and inference.&lt;/p&gt;
&lt;p&gt;Here are some lessons from my experience:&lt;/p&gt;
&lt;h2 id="point-in-time-correctness-is-harder-than-it-looks"&gt;Point-in-time correctness is harder than it looks
&lt;/h2&gt;&lt;p&gt;Training data must reflect what was knowable at prediction time, not what you know now. This seems obvious until you face late-arriving events, slowly changing dimensions, or aggregations that span the temporal boundary. A customer&amp;rsquo;s balance &amp;ldquo;as of yesterday&amp;rdquo; needs yesterday&amp;rsquo;s knowledge, not today&amp;rsquo;s corrected ledger. Build point-in-time correctness into the platform from day one. Retrofitting is expensive and painful.&lt;/p&gt;
&lt;h2 id="event-time-vs-processing-time-matters"&gt;Event time vs processing time matters
&lt;/h2&gt;&lt;p&gt;Teams need a common understanding of feature availability and event time versus processing time semantics. When you query for &amp;ldquo;the features as of timestamp T,&amp;rdquo; are you getting the data that was available at time T, or the data whose events occurred at time T? This distinction is fundamental to correct temporal joins and real-time feature computation.&lt;/p&gt;
&lt;h2 id="reduce-skew-by-reducing-code-duplication"&gt;Reduce skew by reducing code duplication
&lt;/h2&gt;&lt;p&gt;Many machine learning applications require offline data retrieval in batch to train the models but then up-to-date, realtime features for model inference. Because of technical challenges, these two sources are often provided through unique code paths. As soon as feature logic is reimplemented, you open the door to subtle (or not so subtle) bugs that can skew the features and the resulting model predictions. If you&amp;rsquo;re doing online and offline retrieval, do whatever you must to have single code paths for the features.&lt;/p&gt;
&lt;h2 id="unified-batch-and-streaming-requires-operational-maturity"&gt;Unified batch and streaming requires operational maturity
&lt;/h2&gt;&lt;p&gt;The reality is that most unified systems (Flink, Beam, etc.) still require different execution engines and operational expertise. The goal of avoiding multiple code paths for feature logic is achievable, but don&amp;rsquo;t underestimate the operational complexity of running both a batch system and a streaming system in production.&lt;/p&gt;
&lt;h2 id="feature-discoverability-unlocks-reuse"&gt;Feature discoverability unlocks reuse
&lt;/h2&gt;&lt;p&gt;A centralized feature store becomes valuable not just for machine learning &lt;a class="link" href="https://tdhopper.com/talks/streaming-machine-learning/" target="_blank" rel="noopener"
&gt;but in rules engines and analytics&lt;/a&gt; as well. Well documented and discoverable data in the feature store and the upstream data warehouse are essential for teams to find and reuse features instead of recreating them. This multiplies the value of every feature created and reduces the maintenance burden.&lt;/p&gt;
&lt;h2 id="governance-reins-in-complexity"&gt;Governance reins in complexity
&lt;/h2&gt;&lt;p&gt;You need clear rules about who can modify which data, who can change feature names, and who can create new features in specific domains. Without governance, the system quickly becomes unmanageable. The feature store platform team should democratize feature creation while maintaining guardrails that prevent chaos.&lt;/p&gt;
&lt;h2 id="metadata-and-lineage-are-debugging-lifelines"&gt;Metadata and lineage are debugging lifelines
&lt;/h2&gt;&lt;p&gt;Tracking where features come from and how they&amp;rsquo;re built is essential for debugging, compliance, and understanding downstream impacts. When (not &lt;em&gt;if&lt;/em&gt;) something goes wrong, you need to be able to trace the problem back to its source quickly.&lt;/p&gt;
&lt;h2 id="monitoring-and-alerting-are-essential"&gt;Monitoring and alerting are essential
&lt;/h2&gt;&lt;p&gt;As with governance, monitoring ensures the quality and reliability of the platform which is essential for building trust of your internal users and solving your business problems. Detecting drift, staleness, missing updates are essential. Without ML-aware monitoring, subtle issues can degrade model performance for weeks before anyone notices.&lt;/p&gt;
&lt;h2 id="ml-platforms-arent-just-data-engineering"&gt;ML platforms aren&amp;rsquo;t just data engineering
&lt;/h2&gt;&lt;p&gt;Feature platforms have specific concerns that distinguish them from general data engineering platforms. The focus on data leakage prevention, point-in-time correctness, and training-serving consistency requires specialized thinking. You can&amp;rsquo;t simply treat ML data like any other data pipeline.&lt;/p&gt;
&lt;h2 id="tooling-enforces-what-platforms-guarantee"&gt;Tooling enforces what platforms guarantee
&lt;/h2&gt;&lt;p&gt;There&amp;rsquo;s enormous value in good developer tooling: precommit hooks, tests, and CI systems that do automated checks. These tools enforce style guidelines and catch basic errors before production. But be clear about responsibilities: the platform team ensures infrastructure correctness (joins, freshness, consistency) while feature owners ensure semantic correctness (business logic, data quality). Confusing this boundary creates a liability gap.&lt;/p&gt;
&lt;h2 id="enforcement-creates-tension"&gt;Enforcement creates tension
&lt;/h2&gt;&lt;p&gt;Requiring teams to use the platform (not deploying models that bypass it) creates tension with customer-centricity. The way to resolve this: make the platform so valuable, reliable, and easy to use that teams want to adopt it, then enforce adoption to prevent the chaos of fragmented tooling. Build carrots first, then use the stick.&lt;/p&gt;
&lt;h2 id="cost-management-means-thinking-about-serving"&gt;Cost management means thinking about serving
&lt;/h2&gt;&lt;p&gt;In batch feature creation, your users will need to learn to define their features efficiently to avoid throwing away cost on expensive compute. On the other hand, the storage and compute for real-time lookups typically dwarf batch join costs. Without careful attention to serving costs and careful selection of what needs to be real-time versus what can be precomputed, the computational demands can blow your budget.&lt;/p&gt;
&lt;h2 id="customer-centricity-is-non-negotiable"&gt;Customer-centricity is non-negotiable
&lt;/h2&gt;&lt;p&gt;Platform teams must stay close to their end users. Build a tool that actually solves the problems they have, not the problems you think they should have. Listen to their pain points, understand their workflows, and iterate based on real usage patterns. A technically perfect platform that nobody wants to use is a failure.&lt;/p&gt;</description></item><item><title>My summer doing math (?) research</title><link>https://tdhopper.com/blog/my-summer-doing-math-research/</link><pubDate>Thu, 19 Sep 2024 13:20:00 +0000</pubDate><guid>https://tdhopper.com/blog/my-summer-doing-math-research/</guid><description>&lt;img src="https://tdhopper.com/images/graphtheory.jpg" alt="Featured image of post My summer doing math (?) research" /&gt;&lt;p&gt;I started college as a physics major, but I switched to computer science my sophomore year when I realized I didn&amp;rsquo;t want to take more lab classes. After a year as a CS major, I realized I loved the theory classes but didn&amp;rsquo;t care as much about the practical and programming portions of the curriculum. Going into my junior year, I switched—for the last time—to major in math.&lt;/p&gt;
&lt;p&gt;During the winter of my junior year, I applied to a bunch of math &lt;a class="link" href="https://new.nsf.gov/funding/opportunities/reu-research-experiences-undergraduates" target="_blank" rel="noopener"
&gt;Research Experience for Undergraduates&lt;/a&gt; programs at schools around the country. I was thrilled to be accepted to the program at Rochester Institute of Technology organized by &lt;a class="link" href="https://www.rit.edu/science/directory/dansma-darren-narayan" target="_blank" rel="noopener"
&gt;Darren Narayan&lt;/a&gt; for the summer of 2007.&lt;/p&gt;
&lt;p&gt;I headed to Rochester starry eyed about spending the summer doing math research. On my first day, I found out I&amp;rsquo;d be working with the inimitable mathematician &lt;a class="link" href="https://en.wikipedia.org/wiki/Stanis%C5%82aw_Radziszowski" target="_blank" rel="noopener"
&gt;Stanisław P. Radziszowski&lt;/a&gt; on computational combinatorics with &lt;a class="link" href="https://www.linkedin.com/in/evanheidtmann/" target="_blank" rel="noopener"
&gt;another student researcher&lt;/a&gt;, Evan Heidtmann. The first question Dr. Radziszowski had for us was &amp;ldquo;How are your programming skills?&amp;rdquo; This was not what I expected&amp;ndash;or wanted&amp;ndash;to hear at a math research program.&lt;/p&gt;
&lt;p&gt;We spent the summer investigating the &lt;a class="link" href="https://en.wikipedia.org/wiki/Ramsey%27s_theorem" target="_blank" rel="noopener"
&gt;Ramsey number&lt;/a&gt; $R_4(C_4)$. This is the smallest number $n$ such that a &lt;a class="link" href="https://en.wikipedia.org/wiki/Complete_graph" target="_blank" rel="noopener"
&gt;complete graph&lt;/a&gt; with $n$ vertices where the edges are colored by four colors is &lt;em&gt;guaranteed&lt;/em&gt; to have a monochromatic cycle with 4 vertices.&lt;/p&gt;
&lt;p&gt;At the time, the number was known to be either 18 or 19. Due to combinatorial explosion, it&amp;rsquo;s not possible to enumerate every graph 4-coloring for graphs of this size. A graph with 18 vertices has 153 edges which could be colored in $4^{153}$ ways (ignoring isomorphisms). This is a very, very big number.&lt;/p&gt;
&lt;p&gt;Since the number was one of two options, one way to prove it had to be 19 would be to find a complete graph with 18 vertices where the edges are colored by four colors while not containing a 4-cycle subgraph in one color. Evan and I spent the summer hunting for this mythical graph coloring by writing C code to generate and check graphs.&lt;/p&gt;
&lt;p&gt;Before showing up in Rochester, I had no experience writing C code. Fortunately, my brilliant and patient research partner Evan did, and he quickly got me up to speed enough to manipulate and generate graphs using &lt;a class="link" href="https://users.cecs.anu.edu.au/bdm/nauty/" target="_blank" rel="noopener"
&gt;nauty&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I had also never used version control, but Evan was running a version control (I don&amp;rsquo;t recall which one) platform on his home server, so we were able to easily version and collaborate on our code.&lt;/p&gt;
&lt;p&gt;We also had access to fifty Unix machines in the RIT computing lab, so this summer introduced me to the Unix command line, shell scripting, and distributed computing. I wrote bash scripts that would send our processing jobs out to these machines and aggregate results back to a host node.&lt;/p&gt;
&lt;p&gt;Well, we failed to solve our problem. It turns out the solution was actually &lt;a class="link" href="https://utilitasmathematica.com/index.php/Index/article/view/481" target="_blank" rel="noopener"
&gt;published by Chinese mathematicians&lt;/a&gt; while we were working on it. $R_4(C_4)=18$, so the graph we looked for didn&amp;rsquo;t even exist.&lt;/p&gt;
&lt;p&gt;Nonetheless, that experience ended up being one of the most important of my higher education. I learned some of the most fundamental skills of my career (version control, working in terminal, writing fast code, and persevering through computer challenges), and I learned to enjoy writing code.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t write much code between 2007 and starting my operations research master&amp;rsquo;s degree research in 2011, but when I started again, the skills and tenacity I developed at that REU set me up for success in grad school and joining industry as a data scientist in 2012. I wouldn&amp;rsquo;t have picked that research project had I been given a choice; I&amp;rsquo;m so glad I wasn&amp;rsquo;t asked.&lt;/p&gt;</description></item><item><title>AI Assisted Programming with Ravi Mody</title><link>https://tdhopper.com/blog/ai-assisted-programming-with-ravi-mody/</link><pubDate>Wed, 18 Sep 2024 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/ai-assisted-programming-with-ravi-mody/</guid><description>&lt;img src="https://tdhopper.com/images/robot-assistant.png" alt="Featured image of post AI Assisted Programming with Ravi Mody" /&gt;&lt;h2 id="listen"&gt;Listen
&lt;/h2&gt;&lt;iframe src="https://open.spotify.com/embed/episode/2B9nyjupOdPUBHhMyEVOmB" width="100%" height="232" frameBorder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="links"&gt;Links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.linkedin.com/in/ravi-mody/" target="_blank" rel="noopener"
&gt;Ravi Mody on LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://chatgpt.com/" target="_blank" rel="noopener"
&gt;ChatGPT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://claude.ai/" target="_blank" rel="noopener"
&gt;Claude.ai&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.barebones.com/products/bbedit/" target="_blank" rel="noopener"
&gt;BBEdit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.cursor.so/" target="_blank" rel="noopener"
&gt;Cursor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.drewconway.com/zia/2013/3/26/the-data-science-venn-diagram" target="_blank" rel="noopener"
&gt;Drew Conway&amp;rsquo;s Data Science Venn Diagram&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="subscribe"&gt;Subscribe
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/podcast/feed" target="_blank" rel="noopener"
&gt;RSS Feed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://podcasts.apple.com/us/podcast/into-the-hopper/id1499693201" target="_blank" rel="noopener"
&gt;Apple Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://open.spotify.com/show/63NrgKMVb0VTwkklGboIjy" target="_blank" rel="noopener"
&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://overcast.fm/itunes1499693201/into-the-hopper" target="_blank" rel="noopener"
&gt;Overcast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary"&gt;Summary
&lt;/h2&gt;&lt;p&gt;In this episode of &lt;em&gt;Into the Hopper&lt;/em&gt;, I sit down with Ravi Mody, a data science leader with 17 years of experience across companies like Spotify and Daily Harvest. We dig into how large language models—tools like ChatGPT, Claude, and Cursor—are changing the day-to-day reality of programming.&lt;/p&gt;
&lt;p&gt;Ravi shares his unique perspective as someone who took a four-year break from hands-on coding while in management at Spotify, only to return right as LLMs were taking off. We discuss moving from syntax-level assistance to function-level code generation, the dangers and opportunities for junior developers, and why we&amp;rsquo;re both spending less time on Stack Overflow.&lt;/p&gt;
&lt;h2 id="transcript"&gt;Transcript
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Welcome to episode nine of the Into the Hopper podcast, back after maybe an 18-month hiatus. My name is Tim Hopper. I&amp;rsquo;m joined today by my friend Ravi Mody. Ravi has 17 years of data science and leadership experience across a variety of companies like Spotify and Daily Harvest.&lt;/p&gt;
&lt;p&gt;Ravi and I have been discussing how large language models are impacting how we program. We&amp;rsquo;re seeing more and more tools like Cursor, and Copilot has actually been out for three years now, which is wild to me. Copilot in particular has become a valuable part of my daily workflow. I&amp;rsquo;m interested in how others are using these tools too. Sometimes it&amp;rsquo;s hard to know how broad the impact is, or if it&amp;rsquo;s just people on social media talking about it. Ravi&amp;rsquo;s own development has been really impacted, and I asked him to come chat about what that looks like day to day.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; Tim, thanks so much for having me. This is definitely a topic I think about a lot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Do you want to expand on your intro anymore? I summarized your very long resume in one sentence.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; Yeah, I mean, when I talk about programming, I&amp;rsquo;m largely coming at it from the perspective of building machine learning systems. That&amp;rsquo;s my specialty—usually recommender systems specifically. So some of my use of LLM coding is probably very domain-specific.&lt;/p&gt;
&lt;p&gt;Another piece of context: I&amp;rsquo;ve been programming since I was about 10, but I took a four-year break when I was at Spotify. I was there until February of 2024, and for those four years, I didn&amp;rsquo;t really program. I was a people manager. It was an amazing job, but when I joined Raya, my current company, I was rusty at programming. So the timing of this new generation of LLMs was really interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; That&amp;rsquo;s actually really helpful context. The timing is good. At the most basic layer, these tools are just being that mental assist and not requiring you to remember all the little things about syntax—things that might have been easy to forget over four years.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; For sure. On the syntax side, I save a ton of time compared to Stack Overflow or even going through docs. I still think it&amp;rsquo;s valuable to have some muscle memory around common functions, but Cursor and these other tools make it so easy to just say what you want and get functional code out of it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="current-toolset"&gt;Current Toolset
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Why don&amp;rsquo;t we start with an overview of your current tools? Obviously for all of us, these tool chains are changing quickly. People are jumping back and forth between Anthropic and OpenAI as their chat tools improve. What&amp;rsquo;s your current setup?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; I have access to ChatGPT, Claude 3.5, and Cursor. I go back and forth between them. The honest truth is I&amp;rsquo;m actually very comfortable with a basic text editor. I&amp;rsquo;m not a backend engineer—I&amp;rsquo;m really building ML models. So I&amp;rsquo;ve always been most comfortable in almost a notepad-style of programming. I use BBEdit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; BBEdit&amp;rsquo;s your primary editor?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; Yeah, it has been for most of my career.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I don&amp;rsquo;t think you&amp;rsquo;ve ever mentioned that. That&amp;rsquo;s interesting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; It&amp;rsquo;s kind of embarrassing, honestly. I don&amp;rsquo;t like to tell people. I think it makes you a different kind of programmer if you&amp;rsquo;re not using a real IDE.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; A lot of people have really relied on it. So BBEdit doesn&amp;rsquo;t currently have any copilot or cursor-type integration in the editor—you&amp;rsquo;re going to the chat interfaces directly?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; Yeah. I&amp;rsquo;m honestly not even sure if it does. I&amp;rsquo;m not a power user of BBEdit. I literally just use it as a notepad with syntax highlighting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; That&amp;rsquo;s wild. I&amp;rsquo;m very interested to hear that. So on a day to day, what are you turning to AI tools to assist with?&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="thinking-at-the-function-level"&gt;Thinking at the Function Level
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; One thing this means is that I&amp;rsquo;m not really getting code completion. Sometimes I&amp;rsquo;ll use Cursor because there is a little bit of friction otherwise. But I&amp;rsquo;ve never found that the speed of my typing is what really slows me down on projects. It&amp;rsquo;s usually one level of abstraction higher—taking a more complex idea and working at the level of functions and classes.&lt;/p&gt;
&lt;p&gt;For example, if I need a function that transforms a dataset, normally I would have cleared an hour of my calendar, designed it out, whiteboarded it, and then typed it. The typing is like 10% of that time. Where I&amp;rsquo;m most comfortable is co-programming with the chat program. I say what I need, look at the code it generates, and essentially do a code review on it. I&amp;rsquo;m usually generating at the function level.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hours become five to ten minutes now.&lt;/strong&gt; That&amp;rsquo;s where I&amp;rsquo;ve been finding my productivity multiples. It&amp;rsquo;s not completing a little piece of syntax—it&amp;rsquo;s thinking at the function and class level.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; That&amp;rsquo;s interesting. I do a lot of that, especially for utilities—trying to transform a file from one format to another, or restructuring a CSV. Just &amp;ldquo;give me a DuckDB command that&amp;rsquo;s going to restructure this the way I want.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; One of my favorite things now is exactly that. My least favorite programming was always changing formats or saving objects. I love watching it churn out the code I hated writing—&amp;ldquo;save this JSON file to this folder and then load it from that folder.&amp;rdquo; I&amp;rsquo;m trying to automate away the parts I just did not enjoy about the job.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; It&amp;rsquo;s interesting historically how the ability to do that has been such a superpower. Going back to Drew Conway&amp;rsquo;s data science Venn diagram from 13 years ago—that hacking skill, being able to manipulate data. So many people just don&amp;rsquo;t have that paradigm. It&amp;rsquo;ll be interesting to see if this opens the door for more people to do that kind of thing.&lt;/p&gt;
&lt;p&gt;I was just doing something similar for personal reasons. My county lets you download all the real estate records in one massive 400-megabyte Excel file. It&amp;rsquo;s impossible to work with, but DuckDB is perfect for this. In the past, I&amp;rsquo;ve spent so much time hacking around with stuff like that. I kind of enjoy it in some ways, but now I just describe what I want and get those DuckDB commands out. It&amp;rsquo;s reinvigorating those little side trails that I did a lot of in the past but now—with a busy job and four kids—I just don&amp;rsquo;t want to spend time on anymore.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; Exactly. If I&amp;rsquo;m going to take two hours on a data project, how do I want to spend that time? I used to love being in the low-level code, but one of the things I&amp;rsquo;ve realized in my career is &lt;strong&gt;the higher level of abstraction you can think, the more productive you&amp;rsquo;ll be&lt;/strong&gt;. Forcing myself to assume it can take care of the basic code, and then thinking one level of abstraction higher about how all the pieces connect—that&amp;rsquo;s been the biggest game changer for me.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="code-completion-and-reduced-mental-overhead"&gt;Code Completion and Reduced Mental Overhead
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I&amp;rsquo;m actually surprised you&amp;rsquo;re not using it more at the code completion level. That&amp;rsquo;s been the biggest change for me. I&amp;rsquo;ve always relied on some level of code completion in an editor, even just really dumb things like completing variable names. But I find it&amp;rsquo;s so good at recognizing patterns.&lt;/p&gt;
&lt;p&gt;In Python, for example, there&amp;rsquo;s no great auto-importer. You use some new method from another package, and I can type it in, then in VS Code you can say &amp;ldquo;try to fix this error that this import doesn&amp;rsquo;t exist&amp;rdquo;—and it&amp;rsquo;s really bad at detecting that. But with Copilot, I jump up to my imports and Copilot almost always knows exactly what import I was trying to add. I don&amp;rsquo;t have to type out &amp;ldquo;from pandas import something.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; There&amp;rsquo;s also something almost magical about when it types exactly what you were going to type. I do pull up Cursor every now and then when I need to get lower level, and at that point the code completion is really valuable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; The other thing is if you&amp;rsquo;re drawing some kind of structure. I often have something written out in JSON or YAML and I want to write that as Python objects. I&amp;rsquo;ll paste it into my editor, comment it out, start typing the Python, and it figures out really quickly how to translate that structure. I&amp;rsquo;ve spent many hours writing regex to do that kind of thing. Now it just does it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; One thing I&amp;rsquo;ve been really enjoying is—and this is partially because of the chat interface—I&amp;rsquo;ll write a simple function and then, you know, in Python the typing and doc strings take quite a bit of time to type out. So what I&amp;rsquo;ve sometimes done is have a skeleton of a function and throw it into Claude, saying &amp;ldquo;This is what I&amp;rsquo;m trying to get out of this. Give me any advice, but also just write all of this boilerplate stuff.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I think of that as code completion at the function level. I&amp;rsquo;m asking it to think cohesively about that function: &amp;ldquo;This is what the function needs to do, this is the context of why I&amp;rsquo;m doing it.&amp;rdquo; Something that would have taken me half an hour takes a minute now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I do that a lot with doc strings. I like to add them in theory, but I actually hate writing them. You can go to the top of a function and Copilot can usually figure out fairly accurately a doc string and even generate examples. I&amp;rsquo;m not writing big libraries used by lots of people—it&amp;rsquo;s little internal things. How much is it worth spending time on? But when it can do it for you, it&amp;rsquo;s pretty nice.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="implications-for-junior-developers"&gt;Implications for Junior Developers
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; You&amp;rsquo;re in a senior position in your current role but still doing heavy IC work. How are you thinking through these tools in the hands of someone straight out of college? I know that&amp;rsquo;s been a concern people have had.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; That&amp;rsquo;s a great question. This AI stuff, at least at this point where it&amp;rsquo;s still error-prone and can&amp;rsquo;t think about large systems cohesively—it&amp;rsquo;s a huge double-edged sword. Clearly a lot of us are finding huge speed-ups in our programming, but it also makes it really easy to not look at your code at all, to just generate something, run it, and if it works, move on.&lt;/p&gt;
&lt;p&gt;This is particularly dangerous for people who don&amp;rsquo;t have the experience of understanding edge cases where things may fail, or understanding inefficiencies in the code.&lt;/p&gt;
&lt;p&gt;I mentioned earlier that I always code review anything it generates. I do a little mini PR. I go through it. Even with Claude, which is one of the more advanced LLMs right now, almost always it&amp;rsquo;s going to do something where I&amp;rsquo;m like, &amp;ldquo;Hey, take a look at that.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;With more junior employees or people straight out of college who don&amp;rsquo;t have that experience, code review will still be important. As I start to hire people for whom this is a bigger risk, I&amp;rsquo;m probably going to try to set up something where they make it clear when something was LLM-generated. During code review we can pay more attention to that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;At this moment in time, we cannot 100% trust LLMs, and we need to embrace that it&amp;rsquo;s not completely doing our job right now.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; At the same time, there&amp;rsquo;s really no going back. We can&amp;rsquo;t pretend people aren&amp;rsquo;t going to use these tools. Being explicit about it is going to be essential—being open about it and trying to give people good guidance.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve had the same concerns over the years about people copying and pasting code snippets from Stack Overflow that they didn&amp;rsquo;t understand. I&amp;rsquo;ve done it many times. In some ways, we&amp;rsquo;re just speeding up the ability to do that, with some possible other risks. Teams and leaders are going to have to be very open about it. That idea of indicating it in code review is good—teaching people to look over their own code, not just take the generated code for granted.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; That&amp;rsquo;s a great point—we&amp;rsquo;ve been doing this forever with Stack Overflow. We copy and paste code in. It&amp;rsquo;s always been best practice to understand the code, to even cite where you got it from.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="shadow-llm-use"&gt;Shadow LLM Use
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; There&amp;rsquo;s an interesting issue of companies being hesitant about this. There are legitimate legal concerns and related issues, but at every company now there&amp;rsquo;s going to be this shadow LLM use where you can&amp;rsquo;t talk about it because maybe you&amp;rsquo;re not allowed to use it. That has to be an incentive for companies to figure it out. They can do everything they want to lock things down, but if people can&amp;rsquo;t do it on their work computers, they&amp;rsquo;re pulling up their phones and using ChatGPT.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; Absolutely. It&amp;rsquo;s so interesting to see how different companies are approaching this. Some are fully embracing it—internal tools that make it easy to use LLMs and ensure the code&amp;rsquo;s not getting shared back inappropriately. But definitely there are companies just trying to ban it, and I think that&amp;rsquo;s a huge mistake. You&amp;rsquo;re going to make your employees less productive, plus they&amp;rsquo;re just going to go off and do it on the side anyway.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I know for a fact that&amp;rsquo;s happening. I talk to a lot of people doing that kind of thing. There&amp;rsquo;s basically no way to stop it unless you&amp;rsquo;re putting people in an air-gapped lab. If they have their phones, they have access to these tools. People are using Slack to copy and paste something and then open it up on their phone.&lt;/p&gt;
&lt;p&gt;I work at a bank where we have particular data privacy risks, but I&amp;rsquo;ve been very impressed—they&amp;rsquo;ve been making an effort. We explored Amazon&amp;rsquo;s CodeWhisperer offering, did a company-blessed trial, but it ended up not being very good. Now we&amp;rsquo;re doing a trial with Copilot, and I&amp;rsquo;ve been able to be on it. I love having access to it at work. I get a lot of speed-up from it, and just the ability to free up brain cycles is really nice.&lt;/p&gt;
&lt;p&gt;Imagining going to another company with total prohibition just feels crazy to me. I assume companies are going to start seeing pushback in hiring—people are going to ask, &amp;ldquo;Can we use these tools?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; As you said, there&amp;rsquo;s no going back. Increasingly it&amp;rsquo;s just going to become a normal part of the dev experience. It would be the equivalent of saying, &amp;ldquo;Hey, you can&amp;rsquo;t use an IDE to program.&amp;rdquo;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="where-llms-fall-short"&gt;Where LLMs Fall Short
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Have you come across patterns or particular problems where the tools have fallen short? Things you know you aren&amp;rsquo;t going to be able to get answers for?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; All the time. I don&amp;rsquo;t think the current best-in-class LLMs are capable of reasoning at a system level very well. As the codebase I share gets larger and more complex, as I try to fill the context with that, it starts losing track of the bigger picture. If you think about what an LLM is doing, they&amp;rsquo;re not really designed to think at that system level. They&amp;rsquo;re still really good at thinking in terms of paragraphs instead of books, for example.&lt;/p&gt;
&lt;p&gt;I think this is changing quickly. I&amp;rsquo;m really curious about how chain of thought changes this. I&amp;rsquo;m sure there&amp;rsquo;s a near-term future where LLMs can actually run code and see what happens. But one big limitation right now is that system level. I&amp;rsquo;d like to think in terms of abstractions and go up levels. I&amp;rsquo;m really excited for the day where I can think in terms of classes working together instead of going into the class and working on that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; We probably should have opened this by explaining what models we&amp;rsquo;re on now for people listening in the future.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; Yeah, Claude 3.5 and ChatGPT-4 and O1.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; The thing that&amp;rsquo;s been interesting to me recently—I don&amp;rsquo;t get to use Cursor in a work context, only in personal projects—but Cursor has this Composer element where you&amp;rsquo;re giving it free rein to understand the whole repository and make changes across multiple files.&lt;/p&gt;
&lt;p&gt;I have personal website projects I&amp;rsquo;d like to do, but I don&amp;rsquo;t have front-end knowledge and very limited JavaScript knowledge. I&amp;rsquo;ve been trying to hack around with what people are doing—bootstrapping an entire project in Cursor and having it generate and update stuff. Every time I&amp;rsquo;ve done it, I&amp;rsquo;ve hit a wall where it starts cycling through wrong solutions to something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; Yeah.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I haven&amp;rsquo;t spent much time on it—literally hours. But I&amp;rsquo;ve gotten way further than I ever would in my free time trying to do it by hand. I spent many Christmas breaks over the years trying to teach myself enough JavaScript to get something running. I&amp;rsquo;ve gotten to where I actually have a functional React app, for example. But then interacting with data storage layers—Supabase, SQLite cloud, these pseudo-serverless free-tier cloud storage things—it ends up falling on its face.&lt;/p&gt;
&lt;p&gt;The point is that it&amp;rsquo;s trying to look at the bigger picture in a way that Copilot has been able to do for a bit, but Cursor is trying to take that to another level. Even if it isn&amp;rsquo;t perfect at writing new code, just the ability to open a repository and have an LLM understand it and explain it to you—&lt;strong&gt;how many of us have spent so many hours coming into a company, trying to understand the codebase?&lt;/strong&gt; I think it&amp;rsquo;s going to be really valuable to just open it up and ask your robot, &amp;ldquo;What is this codebase? What parts are important? How do I even run this?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; I love it for that. One thing I&amp;rsquo;ll often do in a Claude session is load it up with multiple parts of my code and give a brief description: &amp;ldquo;I&amp;rsquo;m building a recommender system, here&amp;rsquo;s some context about it.&amp;rdquo; It can often understand the individual pieces and how they roughly go together. I can ask it to give me code to run something or explain how a functionality works.&lt;/p&gt;
&lt;p&gt;But there is a major danger zone in current LLMs, and that&amp;rsquo;s the context limit. What I call it is &lt;strong&gt;the LLM goes off the rails&lt;/strong&gt;. Its error rate or intelligence starts going down at some point. It&amp;rsquo;s important to understand the limitations. LLMs do struggle with larger and larger contexts. You load a lot of code, and if you load too much, it starts struggling.&lt;/p&gt;
&lt;p&gt;Also, as you talk to it more and iterate more, it starts struggling. I ran an experiment a couple of days ago where I had a function I was trying to speed up. I could already see three places I could optimize it—almost like an interview question. I asked, &amp;ldquo;Tell me some ways I can speed it up.&amp;rdquo; It started rewriting it. I could run the code and see the time—maybe it starts at 200 milliseconds, gets to 180, 150. It&amp;rsquo;s actually doing something.&lt;/p&gt;
&lt;p&gt;Then I keep telling it, &amp;ldquo;Can you make it faster?&amp;rdquo; And it&amp;rsquo;s always like, &amp;ldquo;Of course!&amp;rdquo; But then you see the time actually start going up—250, 300 milliseconds. There&amp;rsquo;s a certain overconfidence in these LLMs. It&amp;rsquo;s like, &amp;ldquo;Yeah, I can do whatever you ask me.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; That&amp;rsquo;s an area where it requires not checking out. Your brain has to keep working. It&amp;rsquo;s going to be interesting, and we&amp;rsquo;re at such an early stage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; It&amp;rsquo;ll be interesting to listen to this in a few years and just be like, &amp;ldquo;Wow, I can&amp;rsquo;t believe how basic it was.&amp;rdquo;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="concerns-about-skill-atrophy"&gt;Concerns About Skill Atrophy
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Do you have any concerns about losing particular skills? You&amp;rsquo;re thinking about generating code at the function level—are you worried it&amp;rsquo;s making you more rusty? Or are you comfortable with the knowledge and experience you have being able to dive in when you need to?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; That&amp;rsquo;s a good question. I feel like I should be worried about it. But honestly, because I don&amp;rsquo;t think algorithms are getting worse, there are certain things that I&amp;rsquo;m slowly deciding I no longer need to know. I now trust it to abstract some things away from me.&lt;/p&gt;
&lt;p&gt;A concrete example is data visualization. When I moved from R to Python, ggplot was torn away from me. I loved ggplot—it was a grammar of graphics. Once you know the language, everything is intuitive. I never found that with Python. With Python, data visualization was always &amp;ldquo;look at the docs, read the docs, understand it&amp;rdquo; with any of the popular libraries.&lt;/p&gt;
&lt;p&gt;Now I find LLMs are perfectly capable of taking a description of what I want—something I would have mapped into ggplot language—and building the code. I&amp;rsquo;m okay with being rusty at putting a visualization into code. Increasingly, these small three or four line pieces of code, I&amp;rsquo;m going to trust them. I can go back and read it and understand what I was doing, but I&amp;rsquo;m okay not knowing that syntax. I&amp;rsquo;m okay just looking at the visualization and saying &amp;ldquo;cool, it did what I wanted.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; The flip side is that you also have the ability to ask the LLM to go back and explain what you did.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; That&amp;rsquo;s a great point.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I&amp;rsquo;ve done that with SQL queries. It doesn&amp;rsquo;t always get it perfectly, but I deal with a lot of SQL in my current role and it can be hard to understand sometimes. Just getting the assistant to paste something in and say &amp;ldquo;can you add comments to this, help me explain it&amp;rdquo;—without changing anything—is really powerful.&lt;/p&gt;
&lt;p&gt;Again, it&amp;rsquo;s really hard to know how this is going to impact us in the long run. We have the great benefit of having learned the foundations. To younger listeners, I would encourage you to build your foundations and don&amp;rsquo;t punt too much on what you know. But at the same time, we&amp;rsquo;ve got these assets that are huge, and don&amp;rsquo;t ignore that either.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; I completely agree. I don&amp;rsquo;t want to sound like an old dude lecturing young people, but there&amp;rsquo;s a real danger that you get mid-level in your career and it turns out you don&amp;rsquo;t really know what you&amp;rsquo;re doing. I think that wasn&amp;rsquo;t really possible when we were starting off. That&amp;rsquo;s becoming increasingly possible. You can ship error-prone code. You can not actually be able to explain your code. There are so many danger points.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="the-foundation-of-math-and-learning"&gt;The Foundation of Math and Learning
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; My background was in math. There&amp;rsquo;s been a lot of discussion recently about LLMs for math, and Terry Tao, one of the greatest living mathematicians, has actually been digging into LLMs and is very optimistic about their benefits for math.&lt;/p&gt;
&lt;p&gt;I imagine having studied math—it&amp;rsquo;s in many ways the extension of your teacher in seventh grade telling you not to use the calculator for everything. Those hours spent toiling away in the library struggling with proofs, where now an undergrad math curriculum&amp;hellip; an LLM could probably solve almost every proof you have set before you. Learning how to reason through that was what made me successful in math, but I see that as a huge benefit to my whole career—just the way I learned how to solve math problems.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t really know what the solution is, but I&amp;rsquo;m glad I didn&amp;rsquo;t have what could be a crutch. At the same time, if I were a professional mathematician today, I would absolutely be looking at LLMs—if nothing else, to help with my LaTeX typesetting. But even problem solving. There&amp;rsquo;s no reason you wouldn&amp;rsquo;t want to use that to advance problem solving. It&amp;rsquo;s an interesting balance we&amp;rsquo;re going to have to figure out.&lt;/p&gt;
&lt;p&gt;Have you looked at any of the AI code review tools yet?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; I probably should. What&amp;rsquo;s out there?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I haven&amp;rsquo;t used them either. I&amp;rsquo;ve only heard people talk about them. There are integrations with GitHub that go in and add comments. I don&amp;rsquo;t know how you tune those, but I think it&amp;rsquo;ll be interesting in the future.&lt;/p&gt;
&lt;p&gt;One thing I&amp;rsquo;m really excited about—hopefully going to have another episode soon with a mutual friend of ours who&amp;rsquo;s running a small company and has been able to do way more because of LLM-assisted development. I think he&amp;rsquo;s only himself and one other engineer.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m really interested in how this is going to enable entrepreneurs and small teams to develop things. Code review is really valuable, and if an LLM is good enough to provide useful feedback as an individual developer—you don&amp;rsquo;t always need code review, but it&amp;rsquo;s easy to miss things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; One of the nice things about code review is that with code generation, if it generates wrong code with edge cases, that can be really dangerous. But with code review, the false positives are not a big deal. If it says &amp;ldquo;this function doesn&amp;rsquo;t look right&amp;rdquo; but it&amp;rsquo;s actually right, maybe you wasted 30 seconds. I can imagine it will be much more thorough than the typical PR review I&amp;rsquo;m used to—&amp;ldquo;looks good to me&amp;rdquo; or &amp;ldquo;hey, why&amp;rsquo;d you do it this way?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; And the possibility of being more thorough. People used to put in things like McCabe complexity checks—very formulaic. The ability for something to think with more context and say &amp;ldquo;maybe this is too complex and we could split it up&amp;rdquo; would be really cool. The next step beyond that is maybe automated suggested changes—open a PR into your PR with recommended changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; Just click through the changes. It&amp;rsquo;s interesting to see how we&amp;rsquo;re slowly iterating towards more of our code being written and reasoned about by AI. We&amp;rsquo;re clearly not there, but I think we&amp;rsquo;ll keep seeing iteratively the LLMs being able to take over more of it. I&amp;rsquo;m very curious if there&amp;rsquo;s an endpoint, or maybe 10 years from now they&amp;rsquo;re just doing our jobs for us. But we&amp;rsquo;re just in the early stages where it&amp;rsquo;s a huge boost to our productivity, and we&amp;rsquo;re still ultimately in charge of our code.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="looking-forward"&gt;Looking Forward
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; I was going to conclude by asking if you had any forecasts. Any hope of 10 years seems like an insane time window, but two-year or five-year predictions?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; I think there&amp;rsquo;s probably a ton of low-hanging fruit specifically in our space. One thing I mentioned earlier is LLMs actually running code. I&amp;rsquo;m sure that&amp;rsquo;s being done. But I think as we treat LLMs more as an agent in a codebase, even the models that exist now can probably do more.&lt;/p&gt;
&lt;p&gt;Clearly the models are getting better. We&amp;rsquo;re throwing a ton of money into making these models reason better. It seems like the more computation we throw at it, the better it gets. I&amp;rsquo;m sure we&amp;rsquo;ll hit limits with the current technology—I&amp;rsquo;m deeply skeptical that we&amp;rsquo;re going to hit general intelligence with what we have now. But for the last two years, we&amp;rsquo;ve seen consistent, huge jumps in performance. I believe very much in momentum. I think we&amp;rsquo;re on the path towards LLMs owning more of a codebase. We&amp;rsquo;re not there yet, but I think we&amp;rsquo;re moving in that direction.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; It&amp;rsquo;ll be fun to see. It&amp;rsquo;s very interesting, and very hard to make predictions. There are a lot of crazy predictions out there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; Again, I don&amp;rsquo;t want to come back and listen to this and be like, &amp;ldquo;Wow, I was so wrong.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Could we have even imagined this? You&amp;rsquo;ve been doing machine learning a few years longer than me, but in 2010, could we have imagined this? It seems wild. A &amp;ldquo;language model&amp;rdquo; back then was a Markov chain model. They were so dumb. You&amp;rsquo;re thinking about language in terms of bag of words. Even 10 years ago, I was working on really advanced Bayesian models for language, and they had such a simplistic understanding of the language.&lt;/p&gt;
&lt;p&gt;Just the ability to generate text that even looks sensible, much less actually is sensible, is wild. It&amp;rsquo;s going to be really cool to see what that turns into.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; Even just five years ago, GPT-2 was released almost exactly five years ago. Everybody thought it was kind of a toy or a joke. That was actually very much the same technology—it&amp;rsquo;s wild to see what five years of just pushing on the same concepts has gotten us, let alone 10 years ago when it was just a joke.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tim:&lt;/strong&gt; Very cool. All right, well if you don&amp;rsquo;t have anything else to add, we&amp;rsquo;ll wrap up there. This has been a great discussion. I appreciate your perspective.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ravi:&lt;/strong&gt; Likewise. Thanks for having me.&lt;/p&gt;</description></item><item><title>How to use streaming machine learning for real-time data</title><link>https://tdhopper.com/talks/streaming-machine-learning/</link><pubDate>Thu, 08 Aug 2024 00:00:00 +0000</pubDate><guid>https://tdhopper.com/talks/streaming-machine-learning/</guid><description>&lt;img src="https://tdhopper.com/preso.png" alt="Featured image of post How to use streaming machine learning for real-time data" /&gt;&lt;div class="video-wrapper"&gt;
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/Y1-Ls5zt1h0"
allowfullscreen
title="YouTube Video"
&gt;
&lt;/iframe&gt;
&lt;/div&gt;</description></item><item><title>How I am Productive</title><link>https://tdhopper.com/blog/how-i-am-productive/</link><pubDate>Mon, 13 May 2024 14:14:00 +0000</pubDate><guid>https://tdhopper.com/blog/how-i-am-productive/</guid><description>&lt;img src="https://tdhopper.com/images/todo.png" alt="Featured image of post How I am Productive" /&gt;&lt;p&gt;I consider myself a productive and organized person. At a minimum, people come to me for things because they know I get things done. This doesn&amp;rsquo;t come naturally to me, and it&amp;rsquo;s a skill I&amp;rsquo;ve developed over the past 15 years.&lt;/p&gt;
&lt;p&gt;Here are some of the things I do to be productive:&lt;/p&gt;
&lt;h2 id="write-things-down"&gt;Write things down
&lt;/h2&gt;&lt;p&gt;I loosely follow the &lt;a class="link" href="https://getdrafts.com" target="_blank" rel="noopener"
&gt;Getting Things Done&lt;/a&gt; method developed by David Allen. The most important aspect to me has been capturing tasks into a single inbox. Capturing tasks means I don&amp;rsquo;t try to keep them in my head (which is unreliable and burns a lot of brain power trying to remember things). Capturing to a single inbox means tasks won&amp;rsquo;t get lost in piles of Post-Its and napkins.&lt;/p&gt;
&lt;p&gt;I use the &lt;a class="link" href="https://getdrafts.com" target="_blank" rel="noopener"
&gt;Drafts&lt;/a&gt; app on iOS and macOS, which lets me capture notes with zero friction. If it&amp;rsquo;s a task I&amp;rsquo;m writing down, I use a Drafts action to send it to my &lt;a class="link" href="https://www.omnigroup.com/omnifocus" target="_blank" rel="noopener"
&gt;Omnifocus&lt;/a&gt; inbox. I regularly triage my inbox to organize my tasks into relevant projects and contexts (e.g., tasks that can be done on the computer and tasks to do at home).&lt;/p&gt;
&lt;h2 id="using-nagging-reminders"&gt;Using nagging reminders
&lt;/h2&gt;&lt;p&gt;Tasks that &lt;em&gt;must&lt;/em&gt; be done at a certain time aren&amp;rsquo;t fit for my Omnifocus task management systems. For example, my trash must go out on Monday nights, and I need to be reminded until it happens. I love the &lt;a class="link" href="https://www.dueapp.com" target="_blank" rel="noopener"
&gt;Due app&lt;/a&gt; on iOS, which will remind me repeatedly until the task is complete.&lt;/p&gt;
&lt;h2 id="tame-email"&gt;Tame email
&lt;/h2&gt;&lt;p&gt;I keep my email tidy. A big part of this is using &lt;a class="link" href="https://www.sanebox.com" target="_blank" rel="noopener"
&gt;Sanebox&lt;/a&gt; to keep unimportant emails out of my inbox; Sanebox automatically detects, labels, and archives inbox clutter like receipts, marketing, and newsletters. These typically don&amp;rsquo;t require any action and can be triaged as necessary. I aggressively hit the &amp;ldquo;Unsubscribe&amp;rdquo; button on things I don&amp;rsquo;t need to be bothered with (including most marketing).&lt;/p&gt;
&lt;p&gt;When an email can be handled quickly (within a minute or two), I try to handle it as soon as I receive it. If an email requires work to follow up on, I forward it to my &lt;a class="link" href="" &gt;Omnifocus&lt;/a&gt; inbox and make it a task.&lt;/p&gt;
&lt;p&gt;When this works well, my email inbox is normally empty.&lt;/p&gt;
&lt;h2 id="forget-passwords"&gt;Forget passwords
&lt;/h2&gt;&lt;p&gt;I have hundreds (thousands?) of logins to websites and apps. Even if I could use the same password for all of them, this is a notorious security risk.&lt;/p&gt;
&lt;p&gt;Every password I use lives in my &lt;a class="link" href="https://1password.com" target="_blank" rel="noopener"
&gt;1Password&lt;/a&gt; account. I also use it to manage 2-factor authentication pins, credit cards, WiFi passwords, and personal information (social security numbers, driver&amp;rsquo;s license scans, etc.). Using the 1Password keyboard shortcuts, I can quickly log in to any of my accounts.&lt;/p&gt;
&lt;p&gt;I know my 1Password master passwords and almost no other passwords to my accounts. They&amp;rsquo;re typically long, randomly generated mixtures of characters.&lt;/p&gt;
&lt;p&gt;1Password also lets me have a shared vault with my wife, which is invaluable for our shared services.&lt;/p&gt;
&lt;h2 id="keep-slack-under-control"&gt;Keep Slack under control
&lt;/h2&gt;&lt;p&gt;Like many tech workers, much of my day is spent in Slack. A few years ago, I stumbled upon &lt;a class="link" href="https://randsinrepose.com/archives/how-i-slack-2022/" target="_blank" rel="noopener"
&gt;Michael Lopp&amp;rsquo;s guide to optimizing Slack&lt;/a&gt;. His approach greatly improved my experience with Slack and kept it from being a constant distraction. &lt;a class="link" href="https://randsinrepose.com/archives/how-i-slack-2022/" target="_blank" rel="noopener"
&gt;Go read his post.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="dont-waste-15-minutes"&gt;Don&amp;rsquo;t waste 15 minutes
&lt;/h2&gt;&lt;p&gt;Like the next guy, I&amp;rsquo;m often inclined to spend 15 minutes of downtime scrolling Instagram. However, I once heard advice from a productive person that a key to productivity is not wasting those short intervals.&lt;/p&gt;
&lt;p&gt;When I have free minutes (sitting in a waiting room or during a short interval between meetings), I look for a task I can knock off my to-do list. I&amp;rsquo;m regularly surprised that daunting tasks can be finished in little time.&lt;/p&gt;
&lt;h2 id="dont-waste-idle-brain-cycles"&gt;Don&amp;rsquo;t waste idle brain cycles
&lt;/h2&gt;&lt;p&gt;Some tasks (like writing this blog post) are hard to complete in one sitting. I have difficulty developing new ideas in front of my computer with all its distractions. On the other hand, I&amp;rsquo;m a &lt;a class="link" href="https://tdhopper.com/blog/on-inspiration/" target="_blank" rel="noopener"
&gt;strong believer in the power of noodling on ideas&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When developing ideas, I prime my brain by dumping my thoughts on the topic into a Drafts app note and then thinking about it as I go about my day. When I have things to add, I pull up the note and jot down my ideas.&lt;/p&gt;</description></item><item><title>Good programming languages are nice but not vital</title><link>https://tdhopper.com/blog/good-programming-languages-are-nice-but-not-vital/</link><pubDate>Wed, 03 Apr 2024 11:46:00 +0000</pubDate><guid>https://tdhopper.com/blog/good-programming-languages-are-nice-but-not-vital/</guid><description>&lt;img src="https://tdhopper.com/images/complaining.png" alt="Featured image of post Good programming languages are nice but not vital" /&gt;&lt;blockquote&gt;
&lt;p&gt;When someone says, “I want a programming language in which I need only say what I wish done,” give him a lollipop. (Alan Perlis, &lt;a class="link" href="https://cpsc.yale.edu/epigrams-programming" target="_blank" rel="noopener"
&gt;Epigrams in Programming&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;People love to talk trash about programming languages on Twitter. Every day there&amp;rsquo;s a new viral tweet &lt;a class="link" href="https://x.com/holdenmatt/status/1774866242282672365?s=20" target="_blank" rel="noopener"
&gt;about&lt;/a&gt; &lt;a class="link" href="https://twitter.com/markopolojarvi/status/1753668713671475288?s=20" target="_blank" rel="noopener"
&gt;the&lt;/a&gt; &lt;a class="link" href="https://twitter.com/nominalthoughts/status/1750431818883694935?s=20" target="_blank" rel="noopener"
&gt;inadequacies&lt;/a&gt; &lt;a class="link" href="https://twitter.com/andrewwhite01/status/1734707815145422943?s=20" target="_blank" rel="noopener"
&gt;of&lt;/a&gt; &lt;a class="link" href="https://twitter.com/Josh_Merfeld/status/1701563560315547807?s=20" target="_blank" rel="noopener"
&gt;Python&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I have worked with many programming languages and studied others in school, but I&amp;rsquo;ve spent most of the last ten years deep into Python. Of course, Python has warts and wrinkles; I&amp;rsquo;m intimately familiar with many of them! At the same time, people worldwide effectively use Python to solve all kinds of problems. As it turns out, many common frustrations can be set aside with a little effort (e.g., by integrating modern tooling like &lt;a class="link" href="https://github.com/astral-sh/ruff" target="_blank" rel="noopener"
&gt;Ruff&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m regularly convinced that Brian Kernighan knew everything there is to know about software engineering practice, and he wrote it down before I was born in 1986. He doesn&amp;rsquo;t miss the mark in his &lt;a class="link" href="https://archive.org/details/programming-style" target="_blank" rel="noopener"
&gt;1979 paper with Plauger&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;hellip;many people try to excuse badly written programs by blaming inadequacies of the language that must be used. We have seen repeatedly that even Fortran can be tamed with proper discipline. The presence of bad features is not an invitation to use them, nor is the absence of good features an excuse to avoid simulating them as cleanly as possible. Good languages are nice, but not vital.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;People forget that two of the top three most visited websites (Youtube and Facebook) were originally implemented in PHP, a language rarely considered a Platonic ideal.&lt;/p&gt;
&lt;p&gt;Bjarne Stroustrup, creator of C++, says, &amp;ldquo;There are only two kinds of languages: the ones people complain about and the ones nobody uses.&amp;rdquo; He&amp;rsquo;s right, and I&amp;rsquo;m guessing we&amp;rsquo;ll never move entirely beyond that (even as language improvement continues). I, for one, am willing to embrace this and learn to do the best with the tools we have.&lt;/p&gt;</description></item><item><title>Announcing the Python Developer Tooling Handbook</title><link>https://tdhopper.com/blog/announcing-the-python-developer-tooling-handbook/</link><pubDate>Sat, 10 Feb 2024 16:31:16 +0000</pubDate><guid>https://tdhopper.com/blog/announcing-the-python-developer-tooling-handbook/</guid><description>&lt;img src="https://tdhopper.com/images/pydevtools-announcement.png" alt="Featured image of post Announcing the Python Developer Tooling Handbook" /&gt;&lt;p&gt;I’ve been a professional Python developer for over a decade. Like many Python developers, I’ve faced the many challenges of Python packaging and dependency management. I’ve also come to love helping developers find the right tools to make their work easier and more productive.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m excited to announce that I&amp;rsquo;m writing the &lt;a class="link" href="https://pydevtools.com/" target="_blank" rel="noopener"
&gt;Python Developer Tooling Handbook&lt;/a&gt;, a free
ebook on Python developer tooling. This handbook covers a wide range of topics,
including build tools, linting, formatting, dependency management, virtual environments,
and more.&lt;/p&gt;
&lt;p&gt;The book is currently a work in progress, and I&amp;rsquo;m excited to share it with you when it&amp;rsquo;s
ready. If you want to be notified as soon as it&amp;rsquo;s released, please consider signing up
for my mailing list &lt;a class="link" href="https://buttondown.email/pdth?tag=github" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>On Inspiration</title><link>https://tdhopper.com/blog/on-inspiration/</link><pubDate>Mon, 05 Feb 2024 23:48:54 +0000</pubDate><guid>https://tdhopper.com/blog/on-inspiration/</guid><description>&lt;img src="https://tdhopper.com/images/inspiration.png" alt="Featured image of post On Inspiration" /&gt;&lt;p&gt;Good ideas are hard to come by, but I find I&amp;rsquo;m often looking in the wrong places.&lt;/p&gt;
&lt;p&gt;When I&amp;rsquo;m trying to develop new ideas, sitting at my desk staring at my computer (and all the world&amp;rsquo;s distractions) seems to be the worst place. Instead, I like to prime my mind by writing down whatever thoughts I have on the topic and then trying to ruminate as I go about life.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m inspired by stories of brilliant people coming up with ideas at surprising times:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="http://garfield.library.upenn.edu/classics1981/A1981MS54100001.pdf" target="_blank" rel="noopener"
&gt;Hirotugu Akaike said&lt;/a&gt; the idea for his eponymous &lt;a class="link" href="https://en.wikipedia.org/wiki/Akaike_information_criterion" target="_blank" rel="noopener"
&gt;Akaike Information Criterion&lt;/a&gt; while taking his seat on the bus.&lt;/li&gt;
&lt;li&gt;Grant Wood, most famous for painting American Gothic, once said &amp;ldquo;All the really good ideas I&amp;rsquo;ve ever had came to me while I was milking a cow.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://permalink.lanl.gov/object/tr?what=info:lanl-repo/lareport/LA-UR-88-9068" target="_blank" rel="noopener"
&gt;Stanisław Ulam, a key physicist in the Manhattan Project, recounted how he thought&lt;/a&gt; of Monte Carlo methods while home sick and playing solitaire. (Monte Carlo methods essential in modern artificial intelligence model training!)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.worldscientific.com/doi/10.1142/9789814434430_0002" target="_blank" rel="noopener"
&gt;Paul Dirac, a founder of quantum mechanics, said,&lt;/a&gt; &amp;ldquo;I would like to mention that I found the best ideas usually came, not when one was actively striving for them, but when one was in a more relaxed state.&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course, this method can be abused. I had a classmate in grad school who used to use &amp;ldquo;you can&amp;rsquo;t force inspiration&amp;rdquo; as an excuse to never do any work. I try to balance these anecdotes with Stephen King&amp;rsquo;s proverb: &amp;ldquo;Amateurs sit and wait for inspiration, the rest of us just get up and go to work.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Annual Accomplishments</title><link>https://tdhopper.com/annual-accomplishments/</link><pubDate>Tue, 10 Jan 2023 20:47:17 +0000</pubDate><guid>https://tdhopper.com/annual-accomplishments/</guid><description>&lt;img src="https://tdhopper.com/images/zen.png" alt="Featured image of post Annual Accomplishments" /&gt;&lt;h2 id="2025"&gt;2025
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Launched &lt;a class="link" href="https://pydevtools.com" target="_blank" rel="noopener"
&gt;pydevtools.com&lt;/a&gt;, the first comprehensive resource on Python developer tooling; now over over 43k words and growing!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Made it &lt;a class="link" href="https://news.ycombinator.com/item?id=44977645" target="_blank" rel="noopener"
&gt;to the front page of Hacker News&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Led adoption of AI-assisted software development on my team&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Interviewed Prof. Robert Ghrist on &lt;a class="link" href="https://tdhopper.com/blog/writing-a-math-textbook-with-claude-with-professor-ghrist/" target="_blank" rel="noopener"
&gt;Writing a Math Textbook with Claude&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Interviewed by &lt;a class="link" href="https://tdhopper.com/blog/interview-with-the-pybites-podcast/" target="_blank" rel="noopener"
&gt;Pybites Podcast&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Hit 380 lbs for 5 reps on the trap bar (highest weight post-back surgery)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Celebrated 10th wedding anniversary&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open sourced &lt;a class="link" href="https://github.com/tdhopper/wt" target="_blank" rel="noopener"
&gt;wt&lt;/a&gt; and &lt;a class="link" href="https://github.com/python-developer-tooling-handbook/uvhow" target="_blank" rel="noopener"
&gt;uvhow&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Traveled to Stockholm and Berlin for work&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Received encouraging peer feedback highlighting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;eagerness to jump in and solve problems&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;deep Python expertise and ability to make complex topics accessible&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;pragmatic, non–yak-shaving engineering approach&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;initiative in connecting with researchers and identifying challenges&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2024"&gt;2024
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Survived painful, herniated disc and went through successful surgery&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Got back to lifting weights after 5 month break due to a herniated disc&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Down to the fourth belt hole on a belt I couldn’t wear a year ago&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Saw huge growth and adoption of feature store platform at Varo&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Landed amazing job at Spotify&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://tdhopper.com/talks" target="_blank" rel="noopener"
&gt;Spoke at fintech_devcon, my tech first conference in 5 years&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Saw my first &lt;a class="link" href="https://www.instagram.com/p/DEP8ZzFuXiF/" target="_blank" rel="noopener"
&gt;total eclipse&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://podcast.tdhopper.com" target="_blank" rel="noopener"
&gt;Recorded two interesting interviews on AI-assisted software development&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Back to blogging at &lt;a class="link" href="https://tdhopper.com" target="_blank" rel="noopener"
&gt;tdhopper.com&lt;/a&gt; a bit&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Redesigned &lt;a class="link" href="https://tdhopper.com" target="_blank" rel="noopener"
&gt;tdhopper.com&lt;/a&gt; with &lt;a class="link" href="https://gohugo.io" target="_blank" rel="noopener"
&gt;Hugo&lt;/a&gt; and &lt;a class="link" href="https://decapcms.org" target="_blank" rel="noopener"
&gt;Decap CMS&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2023"&gt;2023
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Had 4th kid and watched them all thrive (this was most of my year…)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;285 lbs bench press, 425 lbs deadlift, 405 lbs squat&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Most impactful year of work yet&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Started handbook on Python developer tooling (more soon, hopefully)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2022"&gt;2022
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Had 3rd kid and kept them all alive&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Moved with three small children&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Entered my first strongman competition (with a 10 day old baby)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Lead integration of Tecton&amp;rsquo;s feature store for my team (and learned a ton on the way)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://www.youtube.com/watch?v=6flt_3yMNb0" target="_blank" rel="noopener"
&gt;Spoke at normconf&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2021"&gt;2021
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://photos.tdhopper.com/2021-best-of" target="_blank" rel="noopener"
&gt;Took pictures I&amp;rsquo;m proud of&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://podcast.tdhopper.com" target="_blank" rel="noopener"
&gt;Recorded 3 great episodes of my podcast&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Survived Covid-19 in January&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Took over massive new responsibilities on my team after the departure of our infrastructure engineers&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Accepted new job as a staff software engineer&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://tdhopper.com/blog/organizing-research-data-with-intake/" target="_blank" rel="noopener"
&gt;Developed transformative tool for my team&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Helped underpaid colleague get a new job w/ a big raise&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://pypi.org/project/intake-pattern-catalog/" target="_blank" rel="noopener"
&gt;Released my company&amp;rsquo;s first ever open source library&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Deadlifted 405 lbs&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2020"&gt;2020
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Survived pandemic&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Left a disappointing job and found a great new one&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Completed challenging 1st year of 2nd child&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://podcast.tdhopper.com" target="_blank" rel="noopener"
&gt;Started a podcast&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Recruited my former coworker Brenton to work with me&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://photos.tdhopper.com/2020-best-of" target="_blank" rel="noopener"
&gt;Took pictures I&amp;rsquo;m proud of &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://resume.tdhopper.com" target="_blank" rel="noopener"
&gt;Built a new website&lt;/a&gt; for my resume (with automatically generated PDF version!)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Learned some new things (Go, Python dashboarding, xarray library, weather forecast data)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2019"&gt;2019
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Had a second son&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Moved my team completely off Python 2&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set a new record for length of time at a company&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Made &lt;a class="link" href="https://freespace.tdhopper.com" target="_blank" rel="noopener"
&gt;freespace.tdhopper.com&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/python/cpython/pull/11847" target="_blank" rel="noopener"
&gt;Had a PR merged into CPython&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Bench pressed 235 and deadlifted 375&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Took a long talked about trip to Yellowstone with my dad&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://www.nature.com/articles/d41586-019-02046-0" target="_blank" rel="noopener"
&gt;Had a tweet quoted in a Nature article&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://photos.tdhopper.com/2019-best-of" target="_blank" rel="noopener"
&gt;Took some photos I&amp;rsquo;m proud of&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2018"&gt;2018
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Made &lt;a class="link" href="http://photos.tdhopper.com" target="_blank" rel="noopener"
&gt;photos.tdhopper.com&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Spoke at Scipy, BSides Ashville, Pydata DC, Triangle Juypter Day, and the Southern Data Science Conference&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Bench-pressed 200 lbs and deadlifted 300 lbs for the first time&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Moved my static sites from AWS to Netlify&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Presented at 6 of my teams bi-weekly knowledge shares&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Sold my team on Pytest&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Learned a bunch about malware&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Learned more about network communications&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Started &lt;a class="link" href="http://instagram.com/doyouplayball" target="_blank" rel="noopener"
&gt;instagram.com/doyouplayball &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Published &lt;a class="link" href="https://tdhopper.com/blog/faq/" &gt;A Subjective and Anecdotal FAQ on Becoming a Data Scientist&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Bought my first house&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2017"&gt;2017
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Made &lt;a class="link" href="https://pythonplot.com" target="_blank" rel="noopener"
&gt;pythonplot.com&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;First baby&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Better understood Linux user account model&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Wrote &lt;a class="link" href="https://tdhopper.com/blog/some-reflections-on-being-turned-down-for-a-lot-of-data-science-jobs/" &gt;Some Reflections on Being Turned Down for a Lot of Data Science Jobs&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Second time on &lt;a class="link" href="https://adversariallearning.com" target="_blank" rel="noopener"
&gt;Adversarial Learning&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use Ansible effectively&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Two trips to Grayson Highlands State Park in Virginia&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Five semesters of linear algebra and all I do is solve Python dependency problems</title><link>https://tdhopper.com/talks/linear-algebra-python-dependencies/</link><pubDate>Thu, 15 Dec 2022 00:00:00 +0000</pubDate><guid>https://tdhopper.com/talks/linear-algebra-python-dependencies/</guid><description>&lt;img src="https://tdhopper.com/images/thinkido.png" alt="Featured image of post Five semesters of linear algebra and all I do is solve Python dependency problems" /&gt;&lt;div class="video-wrapper"&gt;
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/6flt_3yMNb0"
allowfullscreen
title="YouTube Video"
&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;This is a reflection I&amp;rsquo;ve carried around for years: the evolution of my career interests and how they relate (or don&amp;rsquo;t relate) to my core passion, which has always been math. It&amp;rsquo;s an overly introspective look at how my love for mathematics led me toward data science and machine learning, only to find myself 10 years later doing basically no pure math in my job.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re like me, and I know there are others out there, this one&amp;rsquo;s for you.&lt;/p&gt;
&lt;h3 id="the-gap-between-expectation-and-reality"&gt;The Gap Between Expectation and Reality
&lt;/h3&gt;&lt;p&gt;When I was a grad student studying machine learning 10 or 12 years ago, I thought my time would be spent choosing kernels for Support Vector Machines (SVMs). Today, I can confidently say I have &lt;em&gt;never&lt;/em&gt; needed to choose an SVM kernel.&lt;/p&gt;
&lt;p&gt;Of course, what I &lt;em&gt;want&lt;/em&gt; people on Twitter to think I do is work on deep learning and large language models. What I actually do as a Staff Software Engineer at Varo Bank is often search Stack Overflow for how to solve Python dependency issues, like installing TensorFlow on an M1 Mac. My biggest contributions, in fact, are often figuring out how to get pip to work the way we need it to.&lt;/p&gt;
&lt;p&gt;I help build and maintain our feature platform for the data science team at Varo Bank, which involves making machine learning features available for model training and inference. This system design work is great, but a large part of my career has been about trying to master the Python language and its ecosystem and using that knowledge to assist my teams. That means a lot of debugging people&amp;rsquo;s pip issues, Googling for exceptions, and encouraging better software engineering practices. This work is satisfying, but it&amp;rsquo;s certainly not where I expected to end up.&lt;/p&gt;
&lt;h3 id="following-the-math"&gt;Following the Math
&lt;/h3&gt;&lt;p&gt;I really do love math. I loved linear algebra from the get-go; it came pretty naturally to me. I’ve always loved algorithms, algorithmic thinking, and mathematical models. This goes back a long way. When I was eight years old, my mom wrote in a Christmas letter that &amp;ldquo;Math is entertainment for him&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;My passion for math led me to study it as an undergrad at Grove City College. I initially started as a Physics major, dreaming of becoming a professor, but quickly switched to Computer Science when I realized I enjoyed the math side of physics more than the lab work.&lt;/p&gt;
&lt;p&gt;However, even as a computer science student, I realized I preferred the abstract, theoretical side (like theory of computation class) over programming. Programming felt like a tool, not the main interest. By the end of my sophomore year, I realized math was truly for me and changed my major. I graduated in 2008 with highest honors in math, having taken my first two semesters of linear algebra plus numerical linear algebra.&lt;/p&gt;
&lt;p&gt;Around 2006, while an undergrad, I found a &lt;a class="link" href="https://www.bloomberg.com/news/articles/2006-01-22/math-will-rock-your-world" target="_blank" rel="noopener"
&gt;compelling article in &lt;em&gt;Business Week&lt;/em&gt;&lt;/a&gt;. I remember this quote from James R. Schatz, chief of mathematical research group at the NSA: &amp;ldquo;There has never been a better time to be a mathematician&amp;rdquo;. The article went on to describe what would later be called data science, discussing how companies needed skilled mathematicians and computer scientists to sort through their &amp;ldquo;swelling oceans of data&amp;rdquo;. To me, that sounded perfect: a job using the math I loved.&lt;/p&gt;
&lt;h3 id="the-academic-detours-and-the-realization"&gt;The Academic Detours and the Realization
&lt;/h3&gt;&lt;p&gt;My academic path continued. I entered the University of Virginia as a PhD student in the history of mathematics (a third semester of linear algebra was included in that program). But I quickly understood that the program was setting me up only for an increasingly unattractive career in academia.&lt;/p&gt;
&lt;p&gt;More critically, I started to realize that my math skills alone probably weren&amp;rsquo;t going to be valuable to anybody outside of instruction. I was unskilled despite having a math degree and being good at math, as almost no one was hiring for that directly.&lt;/p&gt;
&lt;p&gt;I found Operations Research (OR) next, which seemed like a perfect opportunity to merge math, real-world problems, and computation. I became a PhD student at North Carolina State University. This was 2010–2012, the era when data science was blowing up on Twitter, offering a clear career path using math and computation to solve real problems.&lt;/p&gt;
&lt;p&gt;During grad school, I interned at Kiva Systems (now Amazon Robotics) in the algorithms research group. I thought I had hit &amp;ldquo;the glory land&amp;rdquo; where I could focus on pure algorithms and optimization problems. However, I was deeply discouraged to find my boss—a PhD who studied vehicle routing problems—spending a lot of his time on the &lt;em&gt;lowly work&lt;/em&gt; of getting Java code ready for production. Right then, I committed myself: I would never fall down the route of just becoming a software developer; I was going to do math in my career.&lt;/p&gt;
&lt;h3 id="the-shift-to-ml-engineering-the-dirty-work"&gt;The Shift to ML Engineering (The Dirty Work)
&lt;/h3&gt;&lt;p&gt;After leaving grad school, I took a &amp;ldquo;perfect job&amp;rdquo; in 2015, working on a DARPA project implementing inference for Bayesian non-parametric models. It involved deriving equations, learning interesting statistics and math, and reading complex papers. But after nine months, the project ended abruptly.&lt;/p&gt;
&lt;p&gt;Back on the job market, I had a realization: there isn&amp;rsquo;t an enormous market for algorithm implementers who get to do only the pure work. A great implementation, like those in scikit-learn, PyTorch, or even decades-old Fortran implementations, can be used over and over again.&lt;/p&gt;
&lt;p&gt;I shifted back toward traditional data science roles, deciding that if I couldn&amp;rsquo;t be responsible for the underlying algorithms, I could focus on the math modeling side. But in many companies, especially smaller ones where I worked, the engineering barriers to using data science models were often momentous. The math modeling became one of the least important parts because of challenges like political tensions, organizational issues, and massive engineering deficits in getting anything into production.&lt;/p&gt;
&lt;p&gt;I got so frustrated with the engineering deficits that I decided to solve those problems first. I became what we now call a Machine Learning Engineer. My goal was to be the change I wanted to see: a data scientist who didn&amp;rsquo;t have to fight with Python dependency issues, who could instead spend time thinking about deep learning architectures or SVM kernels. I decided to do the engineering, the Dirty Work, to enable data scientists to focus on what I thought was the interesting work.&lt;/p&gt;
&lt;h3 id="finding-joy-in-logic-and-software"&gt;Finding Joy in Logic and Software
&lt;/h3&gt;&lt;p&gt;Here’s the unexpected twist: I started to learn that there is something quite enjoyable about writing great software and getting it ready for production. As a software developer, I still get to originate ideas in my head and write them out with my fingertips. Unlike pure math work, I get to see that work scale to millions of people, providing value for a company and its customers. It&amp;rsquo;s satisfying to do something useful.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t regret studying math at all. I followed my passions, even though I didn&amp;rsquo;t really have a plan. But reflecting on the 2006 quote that there was &amp;ldquo;never been a better time to be a mathematician,&amp;rdquo; I think that was misleading.&lt;/p&gt;
&lt;p&gt;There has never been a better time to be competent and good at math, but being a mathematician on its own is often a poor career path. Today, I tell most people that math skills without programming skills in 2022 are going to be all but worthless.&lt;/p&gt;
&lt;p&gt;While I still cherish the ideas from the three linear algebra texts I studied over the years, I rarely reference them in my day-to-day work. However, I&amp;rsquo;ve learned that software provides many of the same joys we find in mathematics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Problem solving&lt;/li&gt;
&lt;li&gt;Abstractions&lt;/li&gt;
&lt;li&gt;Worlds generated just by logic&lt;/li&gt;
&lt;li&gt;Reproducibility&lt;/li&gt;
&lt;li&gt;Endless areas of study&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For me, the beauty of software was often hidden behind the frustrations of things like pip resolution and impossible exceptions. But I’ve learned to uncover the joy of creating things with software, just as in math you can create things with ideas.&lt;/p&gt;
&lt;p&gt;My career has been a crazy, unpredictable road, and I suspect it will continue to be that way. I’ve spent far less time thinking about Support Vector Machine kernels and far more time solving Python dependency issues, but it’s turned out all right.&lt;/p&gt;</description></item><item><title>No Silver Bullet</title><link>https://tdhopper.com/blog/no-silver-bullet/</link><pubDate>Fri, 18 Nov 2022 20:15:17 +0000</pubDate><guid>https://tdhopper.com/blog/no-silver-bullet/</guid><description>&lt;img src="https://tdhopper.com/silver_bullet.png" alt="Featured image of post No Silver Bullet" /&gt;&lt;p&gt;In 1986, Fred Brooks published &amp;ldquo;No Silver Bullet—Essence and Accident in Software Engineering&amp;rdquo; where he argues that there is no silver bullet that &amp;ldquo;to make software costs drop as rapidly as computer hardware costs do&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Software progress doesn&amp;rsquo;t follow Moore&amp;rsquo;s Law.&lt;/p&gt;
&lt;p&gt;Managers characterize software projects as &amp;ldquo;usually innocent and straightforward, but&amp;hellip; capable of becoming a monster of missed schedules, blown budgets, and flawed products.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Ouch.&lt;/p&gt;
&lt;p&gt;The beginning of hope [in creating software], however, is realizing &amp;ldquo;that progress would be made stepwise, at great effort, and that a persistent, unremitting care.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Software&amp;rsquo;s difficulty has two aspects &amp;ldquo;essence, the difficulties inherent in the nature of software, and accidents, those difficulties that today attend its production but are not inherent.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;⚠️Brooks&amp;rsquo; central thesis:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I ʙᴇʟɪᴇᴠᴇ ᴛʜᴇ ʜᴀʀᴅ ᴘᴀʀᴛ ᴏғ ʙᴜɪʟᴅɪɴɢ sᴏғᴛᴡᴀʀᴇ to be the specification, design, and testing of this conceptual construct, not the labor of representing it and testing the fidelity of the representation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;▶ Essential challenges of software have 4 aspects: complexity, conformity, changeability, and invisibility.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Complexity: &amp;ldquo;Software entities are more complex for their size than perhaps any other human construct because no two parts are alike.&amp;rdquo; Because complexity is essential to software, in trying to abstract away complexity, you &amp;ldquo;often abstract away its essence.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Conformity: Our software is often forced to conform to other interfaces (perhaps because it comes after hardware, or perhaps simply because its &amp;ldquo;perceived as the most conformable&amp;rdquo;). &amp;ldquo;This complexity cannot be simplified out by any redesign of the software alone.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Changeability: &amp;ldquo;Aʟʟ sᴜᴄᴄᴇssғᴜʟ sᴏғᴛᴡᴀʀᴇ ɢᴇᴛs ᴄʜᴀɴɢᴇᴅ.&amp;rdquo; When software succeeds, people push the boundaries of what it was designed for. And the physical world around software changes, so software must adapted (e.g. a new model of hardware is released).&lt;/li&gt;
&lt;li&gt;Invisibility: &amp;ldquo;The reality of software is not inherently embedded in space.&amp;rdquo; Representations like directed graphs generally prove inadequate for fully representing software.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Progress has been made in removing 𝑎𝑐𝑐𝑖𝑑𝑒𝑛𝑡𝑎𝑙 difficulties of software:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;High-level languages&lt;/li&gt;
&lt;li&gt;Time sharing systems&lt;/li&gt;
&lt;li&gt;Unified programming environments&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Brooks saw other things coming that could improve software building, but he
was skeptical about how monumental they would be: artificial intelligence,
graphical programming, better code editors (e.g. language specific features),
faster workstations.&lt;/p&gt;
&lt;p&gt;Some means of attacking the 𝑒𝑠𝑠𝑒𝑛𝑡𝑖𝑎𝑙 complexity&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Buy vs. build: &amp;ldquo;The cost of software has always been development cost, not
replication cost.&amp;rdquo; Thus, if we 𝑐𝑎𝑛 buy it, we probably should.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Requirements refinement and rapid prototyping: &amp;ldquo;Tʜᴇ ʜᴀʀᴅᴇsᴛ sɪɴɢʟᴇ ᴘᴀʀᴛ ᴏғ
ʙᴜɪʟᴅɪɴɢ ᴀ sᴏғᴛᴡᴀʀᴇ sʏsᴛᴇᴍ ɪs ᴅᴇᴄɪᴅɪɴɢ ᴘʀᴇᴄɪsᴇʟʏ ᴡʜᴀᴛ ᴛᴏ ʙᴜɪʟᴅ.&amp;rdquo; &amp;ldquo;Therefore,
the most important function that the software builder performs for the client
is the 𝑖𝑡𝑒𝑟𝑎𝑡𝑖𝑣𝑒 𝑒𝑥𝑡𝑟𝑎𝑐𝑡𝑖𝑜𝑛 𝑎𝑛𝑑 𝑟𝑒𝑓𝑖𝑛𝑒𝑚𝑒𝑛𝑡 𝑜𝑓 𝑡ℎ𝑒 𝑝𝑟𝑜𝑑𝑢𝑐𝑡 𝑟𝑒𝑞𝑢𝑖𝑟𝑒𝑚𝑒𝑛𝑡𝑠.&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Brooks again:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I would go a step further and assert that it is really impossible for a
client, even working with a software engineer, to specify completely,
precisely, and correctly the exact requirements of a modern software product
before trying some versions of the product.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To serve iterative requirements gathering, a goal should be getting something
working as quickly as possible.&lt;/p&gt;
&lt;p&gt;(A side benefit of this is it improves morale for developers. &amp;ldquo;Enthusiasm
jumps when there is a running system, even a simple one.&amp;rdquo;)&lt;/p&gt;
&lt;p&gt;Great software design comes from great designers. Software is a creative
process. &amp;ldquo;Sound methodology can empower and liberate the creative mind; it
cannot inflame or inspire the drudge.&amp;rdquo; Organizations would do well to focus on
identifying great software designers and working hard to &amp;ldquo;grow great
designers&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Brooks:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Skepticism is not pessimism, however. &amp;hellip; A disciplined, consistent effort to
develop, propagate, and exploit these innovations should indeed yield an
order-of-magnitude improvement. There is no royal road, but there is a road.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a class="link" href="http://worrydream.com/refs/Brooks-NoSilverBullet.pdf" target="_blank" rel="noopener"
&gt;Read all of Brooks&amp;rsquo; &amp;ldquo;No Silver Bullet&amp;rdquo;
here&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;small&gt;&lt;a class="link" href="https://flickr.com/photos/eschipul/4160817135" title="silver bullet"
target="_blank" rel="noopener"
&gt;silver bullet&lt;/a&gt; flickr photo by &lt;a class="link" href="https://flickr.com/people/eschipul" target="_blank" rel="noopener"
&gt;eschipul&lt;/a&gt; shared under a &lt;a class="link" href="https://creativecommons.org/licenses/by-sa/2.0/" target="_blank" rel="noopener"
&gt;Creative Commons (BY-SA) license&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;</description></item><item><title>My first strongman competition</title><link>https://tdhopper.com/blog/my-first-strongman-competition/</link><pubDate>Fri, 20 May 2022 12:30:00 +0000</pubDate><guid>https://tdhopper.com/blog/my-first-strongman-competition/</guid><description>&lt;img src="https://tdhopper.com/images/strongman.png" alt="Featured image of post My first strongman competition" /&gt;&lt;p&gt;I started lifting weights for the first time in my life four years ago, as a 32-year-old. Over the last few years, I&amp;rsquo;ve gotten interested in Strongman as a sport through YouTube. Yesterday, I entered my first strongman competition.&lt;/p&gt;
&lt;p&gt;I competed not having touched a weight in 2.5 weeks and generally not having trained as I had hoped due to the event being two weeks after my third child&amp;rsquo;s birth. Nevertheless, I had a lot of fun and learned much about competing.&lt;/p&gt;
&lt;h2 id="events"&gt;Events
&lt;/h2&gt;&lt;h3 id="farmers-carry-medley"&gt;Farmer’s Carry Medley
&lt;/h3&gt;&lt;p&gt;There were three farmer’s implements, and each had to be carried 40 feet. They weighed 210, 230, and 260 lbs per hand. I finished in 33.93 seconds and took 6th place! Before the competition, I’d never picked up anything close to 520 lbs.&lt;/p&gt;
&lt;h3 id="axle-deadlift-ladder"&gt;Axle Deadlift Ladder
&lt;/h3&gt;&lt;p&gt;There were three axle bars on wagon wheels (an 18&amp;quot; deadlift) weighing 435, 485, and 535 lbs. The first two bars had to be lifted once, and the third bar could be lifted for reps within the 60-second window.&lt;/p&gt;
&lt;p&gt;I wasn’t able to lift the lightest bar. I had intended to do a lot of deadlift training in January and February, but it didn’t happen. After the farmer’s carry, my hamstrings were so tight that I could barely get down to the bar. I knew from trying to warm up I wouldn’t be able to budge the bar. However, I did manage to get it an inch or two off the ground (which is something).&lt;/p&gt;
&lt;h3 id="sandbag-race"&gt;Sandbag Race
&lt;/h3&gt;&lt;p&gt;There were two sandbags (200 and 225 lbs). Each had to be carried around a cone 40’ away and back to the start line.&lt;/p&gt;
&lt;p&gt;I’d never touched a bag weighing more than 150 lbs. I finished in 46.54 seconds and was got 10th place. I was pleased with my result, but I could have trimmed time off if I had more practice picking up big bags.&lt;/p&gt;
&lt;h3 id="viking-press"&gt;Viking Press
&lt;/h3&gt;&lt;p&gt;The Viking press was a natural log mounted on a pivot. It was supposed to be equivalent to a 225-lb overhead press.&lt;/p&gt;
&lt;p&gt;I was apprehensive because I had hurt my shoulder training for it, and I rarely do heavy overhead pressing. However, I was able to lift the trunk without shoulder pain.&lt;/p&gt;
&lt;p&gt;I got eight reps and was pleased with that. My final rep felt like my spine was about to pop, so I gave up.&lt;/p&gt;
&lt;h3 id="sandbag-battle"&gt;Sandbag Battle
&lt;/h3&gt;&lt;p&gt;The final event was loading increasingly heavy sandbags over a 4’ rail. After each competitor had a turn, the bag weight was increased by 25 lbs.&lt;/p&gt;
&lt;p&gt;It started with a 200-lb bag, which I had no trouble lifting. I also lifted the 225-lb bag fine. With the 250-lb bag, I struggled to get it to my lap, but I got it over the bar at the last second.&lt;/p&gt;
&lt;p&gt;I gave the 275-lb bag a good shot, but it couldn’t fit in my lap. Its weight is much higher than the 150-lb bag I trained with, but it is also a lot bigger, and I didn’t know how to get a grip on it. With some practice, I should be able to get to a 325-lb bag.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img src="https://tdhopper.com/images/1237244fd93e655ef52ce1dba00aa2ff.jpeg"&gt;&lt;figcaption&gt;
&lt;h4&gt;Sandbag race&lt;/h4&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/images/59f7ec02d3275ba63adf1b8dd06ed554.jpeg"&gt;&lt;figcaption&gt;
&lt;h4&gt;Farmer&amp;#39;s carry&lt;/h4&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description></item><item><title>Archives</title><link>https://tdhopper.com/archives/</link><pubDate>Sun, 06 Mar 2022 00:00:00 +0000</pubDate><guid>https://tdhopper.com/archives/</guid><description/></item><item><title>Apache Pulsar with Jowanza Joseph</title><link>https://tdhopper.com/blog/apache-pulsar-with-jowanza-joseph/</link><pubDate>Mon, 07 Feb 2022 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/apache-pulsar-with-jowanza-joseph/</guid><description>&lt;img src="https://tdhopper.com/images/podcast.png" alt="Featured image of post Apache Pulsar with Jowanza Joseph" /&gt;&lt;h2 id="listen"&gt;Listen
&lt;/h2&gt;&lt;iframe src="https://open.spotify.com/embed/episode/1bS5PPymcYjpTv5cDHYAMJ" width="100%" height="232" frameBorder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="links"&gt;Links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.youtube.com/results?search_query=jowanza&amp;#43;joseph&amp;#43;pulsar" target="_blank" rel="noopener"
&gt;Jowanza&amp;rsquo;s Pulsar talks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://twitter.com/jowanza" target="_blank" rel="noopener"
&gt;Jowanza&amp;rsquo;s Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://jowanza.com" target="_blank" rel="noopener"
&gt;Jowanza&amp;rsquo;s website &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://pulsar.apache.org/" target="_blank" rel="noopener"
&gt;Pulsar homepage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying" target="_blank" rel="noopener"
&gt;The Log: What every software engineer should know about real-time data&amp;rsquo;s unifying abstraction&lt;/a&gt; by Jay Kreps&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://streamnative.io/" target="_blank" rel="noopener"
&gt;StreamNative&lt;/a&gt; - Hosted Pulsar&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.datastax.com/products/astra-streaming" target="_blank" rel="noopener"
&gt;Datastax Astra Streaming&lt;/a&gt; - Hosted Pulsar&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.cncf.io/" target="_blank" rel="noopener"
&gt;Cloud Native Computing Foundation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://pulsar.apache.org/docs/en/functions-overview/" target="_blank" rel="noopener"
&gt;Pulsar Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://pulsar.apache.org/docs/en/2.3.0/io-overview/" target="_blank" rel="noopener"
&gt;Pulsar IO&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://pulsar.apache.org/docs/en/sql-overview/" target="_blank" rel="noopener"
&gt;Pulsar SQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://zookeeper.apache.org/" target="_blank" rel="noopener"
&gt;Apache ZooKeeper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://bookkeeper.apache.org/" target="_blank" rel="noopener"
&gt;Apache BookKeeper&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="subscribe"&gt;Subscribe
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/podcast/feed" target="_blank" rel="noopener"
&gt;RSS Feed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://podcasts.apple.com/us/podcast/into-the-hopper/id1499693201" target="_blank" rel="noopener"
&gt;Apple Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://open.spotify.com/show/63NrgKMVb0VTwkklGboIjy" target="_blank" rel="noopener"
&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://overcast.fm/itunes1499693201/into-the-hopper" target="_blank" rel="noopener"
&gt;Overcast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Code Review Guidelines for Data Science Teams</title><link>https://tdhopper.com/blog/code-review-guidelines/</link><pubDate>Fri, 03 Dec 2021 15:37:00 +0000</pubDate><guid>https://tdhopper.com/blog/code-review-guidelines/</guid><description>&lt;img src="https://tdhopper.com/images/code-review.png" alt="Featured image of post Code Review Guidelines for Data Science Teams" /&gt;&lt;p&gt;Over the last 6 years, I&amp;rsquo;ve been able to help the teams I have been part of
develop guidelines for code review. Many teams require &amp;ldquo;code review&amp;rdquo; without
putting any effort into establishing a common understanding of what that
means. This post is adapted from proposed guidelines I prepared for one of my
teams. Of course, you don&amp;rsquo;t have to adopt &lt;em&gt;my&lt;/em&gt; guidelines for review, but I
would encourage your team to set aside some time to make sure you all mean the
same thing by &amp;ldquo;code review&amp;rdquo;.&lt;/p&gt;
&lt;h2 id="what-is-a-code-review-for"&gt;What is a code review for?
&lt;/h2&gt;&lt;p&gt;Code review has multiple benefits and objectives including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Code correctness&lt;/strong&gt;: someone seeing your code with fresh eyes may help uncover bugs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Code familiarity&lt;/strong&gt;: reading one another&amp;rsquo;s code keeps everyone familiar with the codebase.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Design feedback&lt;/strong&gt;: a constantly evolving code base is a fight against complexity; reviewers can guide one another on keeping the codebase coherent and maintainable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Mutual learning&lt;/strong&gt;: the reviewer and author will inevitably learn from one another.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Regression protection&lt;/strong&gt;: future contributors to the code base have checks against breaking essential functionality; importantly, &lt;em&gt;this reduces fear of making necessary improvements to the code&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="what-code-reviews-arent"&gt;What code reviews aren&amp;rsquo;t
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;An opportunity for the reviewer to impose their idiosyncrasies.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An opportunity for the developer to push off responsibility (e.g. correctness) for their code to the reviewer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An opportunity to demand perfection (Per &lt;a class="link" href="https://google.github.io/eng-practices/review/reviewer/standard.html" target="_blank" rel="noopener"
&gt;Google’s Code Review Guidelines&lt;/a&gt;: &lt;em&gt;A key point here is that there is no such thing as &amp;ldquo;perfect&amp;rdquo; code—there is only better code&lt;/em&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="opening-pull-requests"&gt;Opening Pull Requests
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Take care to write informative commit messages. This helps your reviewer understand the decisions you made.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Consider your contribution in the broader context of the code base. Do you need to take extra steps to make the code healthier and manage complexity?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Keep pull requests short whenever possible. &lt;code&gt;git --shortstat origin/main&lt;/code&gt; will show you the size of your branch&amp;rsquo;s diff from &lt;code&gt;main&lt;/code&gt;; under 400 lines changed is a great goal.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Write a pull request description that sets your reviewer up for success by helping them understand what the PR intends to accomplish.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you have a particularly complex PR, consider doing a code walk-through with a reviewer first.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;New code should ordinarily come with new tests.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="reviewing-pull-requests"&gt;Reviewing Pull Requests
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Have a positive, constructive, helpful attitude.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Wait for continuous integration tasks to complete. Let the author resolve any test failures before beginning your review.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;As much as possible, configure your continuous integration to enforce your team&amp;rsquo;s style guidelines and look for line-level bugs. In Python, this might include running &lt;a class="link" href="https://mypy-lang.org/" target="_blank" rel="noopener"
&gt;mypy&lt;/a&gt; and &lt;a class="link" href="https://docs.astral.sh/ruff/" target="_blank" rel="noopener"
&gt;ruff&lt;/a&gt;. Automation like this has multiple benefits: they&amp;rsquo;re often better than humans at this task, they reduce cognitive load on the reviewer, and they reduce interpersonal tension that results from interviewers nitpicking code.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Things to evaluate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Does the code appear to do what it claims to do? (This requires you understanding what the code claims to do; you may need to ask the code author to write a better description.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Was the new code put in the right place?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Is the new code unnecessarily complex—or unnecessarily clever?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.&amp;rdquo; – Brian Kernighan&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Does the new code do all it can to avoid adding to the overall complexity of our codebase?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Did the author write tests for the new code?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clarify when a comment is minor or not essential for merging (for example, preface with &amp;ldquo;Nit:&amp;rdquo;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If a PR is too large for you to reasonably review, you can ask the author to split it into multiple PRs.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="additional-reading"&gt;Additional Reading
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://google.github.io/eng-practices/review/reviewer/standard.html" target="_blank" rel="noopener"
&gt;Google&amp;rsquo;s excellent guide to code review&lt;/a&gt; (Note: CL=change list=pull request)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://docs.gitlab.com/ee/development/code_review.html" target="_blank" rel="noopener"
&gt;Gitlab&amp;rsquo;s Code Review Guidelines&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://www.michaelagreiler.com/code-review-best-practices/" target="_blank" rel="noopener"
&gt;Proven Code Review Best Practices from Microsoft&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/thoughtbot/guides/tree/master/code-review" target="_blank" rel="noopener"
&gt;Thoughtbot Code Review Guidelines&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://slack.engineering/how-about-code-reviews/" target="_blank" rel="noopener"
&gt;Code reviews at Slack&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/joho/awesome-code-review/blob/master/readme.md" target="_blank" rel="noopener"
&gt;Curated list of articles about code review from John Barton&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Organizing research data with Intake</title><link>https://tdhopper.com/blog/organizing-research-data-with-intake/</link><pubDate>Tue, 02 Nov 2021 12:20:00 +0000</pubDate><guid>https://tdhopper.com/blog/organizing-research-data-with-intake/</guid><description>&lt;img src="https://tdhopper.com/images/shelves.png" alt="Featured image of post Organizing research data with Intake" /&gt;&lt;p&gt;As someone providing engineering and infrastructure support for a small data science team, I&amp;rsquo;ve seen firsthand how the right tool can dramatically improve team productivity. Our team recently discovered &lt;a class="link" href="https://intake.readthedocs.io/en/latest/" target="_blank" rel="noopener"
&gt;Intake&lt;/a&gt;, a data cataloging tool by Anaconda that transformed how we work with Python-based data science projects.&lt;/p&gt;
&lt;p&gt;Like many data science teams, we deal with data arriving through various channels, e.g., batch data in AWS S3, email-attached spreadsheets, and transformations of existing datasets. Before implementing &lt;a class="link" href="https://intake.readthedocs.io/en/latest/" target="_blank" rel="noopener"
&gt;Intake&lt;/a&gt;, our data scientists faced challenges in accessing datasets. They needed to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Know the dataset existed&lt;/li&gt;
&lt;li&gt;Locate its specific storage location&lt;/li&gt;
&lt;li&gt;Understand the download process&lt;/li&gt;
&lt;li&gt;Determine which Python library and arguments were needed to open it&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This cognitive overhead was taking valuable time away from actual data science work.&lt;/p&gt;
&lt;p&gt;Intake offered us an elegant solution through its lightweight interface and plugin system. We packaged it into a pip-installable library containing our data catalog, allowing data scientists to access datasets easily. Instead of juggling multiple parameters and commands, they could now load data with a clean, intuitive syntax:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;catalog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The beauty of this approach is that data scientists don&amp;rsquo;t need to know the underlying S3 infrastructure; they can focus purely on their analytical work.&lt;/p&gt;
&lt;p&gt;To make this system reliable, we implemented several key components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A YAML-based catalog definition system for organizing our data assets&lt;/li&gt;
&lt;li&gt;An internal Python package distributed through our private repository&lt;/li&gt;
&lt;li&gt;Nightly automated tests ensuring dataset accessibility&lt;/li&gt;
&lt;li&gt;A custom Intake plugin (which we open-sourced as &lt;a class="link" href="https://github.com/DTN-Public/intake-pattern-catalog" target="_blank" rel="noopener"
&gt;intake-pattern-catalog&lt;/a&gt;) for handling parameterized file paths and versioned files&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This seemingly simple change had profound effects on our workflow. It eliminated:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Friction in data access&lt;/li&gt;
&lt;li&gt;Redundant boilerplate code&lt;/li&gt;
&lt;li&gt;Reliance on unwritten institutional knowledge&lt;/li&gt;
&lt;li&gt;The constant need to search StackOverflow for boto3 solutions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most importantly, it shortened feedback loops - a critical factor in data science productivity - allowing our team to focus on model development rather than data access logistics.&lt;/p&gt;</description></item><item><title>Ocean Engineering to Data Science</title><link>https://tdhopper.com/blog/ocean-engineering-to-data-science/</link><pubDate>Wed, 15 Sep 2021 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/ocean-engineering-to-data-science/</guid><description>&lt;img src="https://tdhopper.com/images/podcast.png" alt="Featured image of post Ocean Engineering to Data Science" /&gt;&lt;h2 id="listen"&gt;Listen
&lt;/h2&gt;&lt;iframe src="https://open.spotify.com/embed/episode/39siLfJWH3VTJkyUhilVEd" width="100%" height="232" frameBorder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="links"&gt;Links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://twitter.com/brentonmallen?lang=en" target="_blank" rel="noopener"
&gt;Brenton&amp;rsquo;s Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://instagram.com/brentonmallen?lang=en" target="_blank" rel="noopener"
&gt;Brenton&amp;rsquo;s Instagram&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://brentonmallen.com" target="_blank" rel="noopener"
&gt;Brenton&amp;rsquo;s Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.ome.fau.edu/undergraduate/ocean/" target="_blank" rel="noopener"
&gt;Ocean Engineering at Florida Atlantic University&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="subscribe"&gt;Subscribe
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/podcast/feed" target="_blank" rel="noopener"
&gt;RSS Feed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://podcasts.apple.com/us/podcast/into-the-hopper/id1499693201" target="_blank" rel="noopener"
&gt;Apple Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://open.spotify.com/show/63NrgKMVb0VTwkklGboIjy" target="_blank" rel="noopener"
&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://overcast.fm/itunes1499693201/into-the-hopper" target="_blank" rel="noopener"
&gt;Overcast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Hiring Data Scientists with Roy Keyes</title><link>https://tdhopper.com/blog/hiring-data-scientists-with-roy-keyes/</link><pubDate>Wed, 30 Jun 2021 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/hiring-data-scientists-with-roy-keyes/</guid><description>&lt;img src="https://tdhopper.com/images/podcast.png" alt="Featured image of post Hiring Data Scientists with Roy Keyes" /&gt;&lt;h2 id="listen"&gt;Listen
&lt;/h2&gt;&lt;iframe src="https://open.spotify.com/embed/episode/2sKIYqYafX3xOAEeSMEezj" width="100%" height="232" frameBorder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="links"&gt;Links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://twitter.com/roycoding?lang=en" target="_blank" rel="noopener"
&gt;Roy&amp;rsquo;s Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://leanpub.com/dshiring" target="_blank" rel="noopener"
&gt;Roy&amp;rsquo;s Book&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.amazon.com/Data-Scientists-Work-Sebastian-Gutierrez/dp/1430265981" target="_blank" rel="noopener"
&gt;Data Scientists at Work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://hbr.org/2012/10/data-scientist-the-sexiest-job-of-the-21st-century" target="_blank" rel="noopener"
&gt;Data Scientist: The Sexiest Job of the 21st Century&lt;/a&gt; by Thomas H. Davenport and D.J. Patil&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.oreilly.com/library/view/building-data-science/BLDNGDST0001/" target="_blank" rel="noopener"
&gt;Building Data Science Teams&lt;/a&gt; by DJ Patil (2011)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://adversariallearning.com/episode-10-stories-of-degradation-and-humiliation.html" target="_blank" rel="noopener"
&gt;Adversarial Learning: Stories of Degradation and Humiliation&lt;/a&gt; — Podcast about bad interview experiences&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="subscribe"&gt;Subscribe
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/podcast/feed" target="_blank" rel="noopener"
&gt;RSS Feed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://podcasts.apple.com/us/podcast/into-the-hopper/id1499693201" target="_blank" rel="noopener"
&gt;Apple Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://open.spotify.com/show/63NrgKMVb0VTwkklGboIjy" target="_blank" rel="noopener"
&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://overcast.fm/itunes1499693201/into-the-hopper" target="_blank" rel="noopener"
&gt;Overcast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Weather Forecast Data Formats and Tools</title><link>https://tdhopper.com/blog/weather-forecast-data-formats-and-tools/</link><pubDate>Mon, 21 Jun 2021 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/weather-forecast-data-formats-and-tools/</guid><description>&lt;p&gt;Weather forecasts (like WRF, HRRR, etc.) are high-dimensional gridded data. They include projection information and other metadata, and there are several file formats used to store them. Here&amp;rsquo;s what I&amp;rsquo;ve learned about these formats and the tools for working with them.&lt;/p&gt;
&lt;h2 id="grib-gridded-binary-data"&gt;GRIB: GRIdded BInary Data
&lt;/h2&gt;&lt;p&gt;The World Meteorological Organization (WMO) Commission for Basic Systems (CBS) created the GRIB format in 1985. GRIB1 was released in 1994, and GRIB2 was released in 2003. Most GRIB data you&amp;rsquo;ll encounter today is GRIB2.&lt;/p&gt;
&lt;h2 id="netcdf"&gt;netCDF
&lt;/h2&gt;&lt;p&gt;NetCDF is a set of software libraries and self-describing, machine-independent data formats that support the creation, access, and sharing of array-oriented scientific data. It was developed and is maintained at Unidata.&lt;/p&gt;
&lt;p&gt;NetCDF 1.0 was released in 1998, and it&amp;rsquo;s now up to version 4.8. The format is strictly backwards compatible and almost entirely cross-compatible between language APIs. The canonical implementations of the spec are C and Java libraries.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll often hear that netCDF files are much bigger than GRIB2 files. NetCDF4 does support zlib compression, though it may not compress as well as GRIB2. NetCDF4 files can be (and typically are) stored in HDF5 format, so you can inspect them with either &lt;code&gt;ncinfo&lt;/code&gt; or &lt;code&gt;h5dump&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="the-data-model"&gt;The Data Model
&lt;/h3&gt;&lt;p&gt;A netCDF file has three key components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dimensions&lt;/strong&gt;: the shape of variables&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Variables&lt;/strong&gt;: the actual data&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Attributes&lt;/strong&gt;: metadata for the dataset or individual variables&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;NetCDF-4 also adds groups with named subgroups.&lt;/p&gt;
&lt;h3 id="ncdump-cli"&gt;&lt;code&gt;ncdump&lt;/code&gt; CLI
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;ncdump&lt;/code&gt; dumps a netCDF file as plain text. Useful flags:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-v&lt;/code&gt;: select a variable&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-h&lt;/code&gt;: header only&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-x&lt;/code&gt;: XML output&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="ncgen"&gt;&lt;code&gt;ncgen&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;ncgen&lt;/code&gt; turns Common Data Format (CDL) text into a netCDF binary file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ncgen -b example.cdl
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Both &lt;code&gt;ncgen&lt;/code&gt; and &lt;code&gt;ncdump&lt;/code&gt; are part of the core netCDF library.&lt;/p&gt;
&lt;h3 id="xarray"&gt;xarray
&lt;/h3&gt;&lt;p&gt;xarray is a Python library built on the netCDF data model. Data is always loaded lazily from netCDF files: you can manipulate, slice, and subset Dataset and DataArray objects without loading array values into memory until you perform actual computation.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;xarray&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;xr&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open_dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;example.nc&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_dataframe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;NetCDF is the standard way to serialize data from xarray.&lt;/p&gt;
&lt;h3 id="nco-netcdf-operators"&gt;NCO (NetCDF Operators)
&lt;/h3&gt;&lt;p&gt;NCO is a collection of command-line tools for working with netCDF files. The tools take netCDF, HDF, and/or DAP files as input and can derive new data, compute statistics, print content, hyperslab, manipulate metadata, and output results in text, binary, or netCDF formats.&lt;/p&gt;
&lt;h4 id="ncks-kitchen-sink"&gt;&lt;code&gt;ncks&lt;/code&gt; (Kitchen Sink)
&lt;/h4&gt;&lt;p&gt;&lt;code&gt;ncks&lt;/code&gt; combines most features of &lt;code&gt;ncdump&lt;/code&gt; and &lt;code&gt;nccopy&lt;/code&gt; with extra capabilities for extraction, hyperslabbing, subsetting, and translation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Copy a file (the copy includes history)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ncks example.nc copy.nc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Select a variable&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ncks -v WIND_6000maboveground wrf.nc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Print as CDL (like ncdump)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ncks --cdl example.nc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Compress with level 5 compression (-4 required for netCDF4)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ncks wrf.nc -4 -L5 wrf_compressed.nc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s also extensive functionality for concatenation, appending, summary statistics, and comparing files.&lt;/p&gt;
&lt;h4 id="other-nco-tools"&gt;Other NCO Tools
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ncrcat&lt;/code&gt;/&lt;code&gt;ncecat&lt;/code&gt;: concatenation (which one to use depends on whether you already have a record dimension like time)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ncra&lt;/code&gt;: average over time&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ncwa&lt;/code&gt;: weighted averages&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="converting-netcdf-to-grib"&gt;Converting netCDF to GRIB
&lt;/h3&gt;&lt;p&gt;The CDO (Climate Data Operators) library can convert between formats:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cdo -f grb2 copy wrf.nc wrf_from_nc.gr2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Fair warning: this doesn&amp;rsquo;t always work cleanly.&lt;/p&gt;
&lt;h2 id="takeaways"&gt;Takeaways
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Complicated datasets are complicated!&lt;/li&gt;
&lt;li&gt;There is more than one way to do everything.&lt;/li&gt;
&lt;li&gt;With some basic commands, you can inspect any of these file types.&lt;/li&gt;
&lt;li&gt;Converting between formats is often straightforward.&lt;/li&gt;
&lt;li&gt;All of these tools are available through Conda and conda-forge.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://code.mpimet.mpg.de/projects/cdo" target="_blank" rel="noopener"
&gt;CDO (Climate Data Operators)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://confluence.ecmwf.int/display/ECC/GRIB&amp;#43;tools&amp;#43;examples" target="_blank" rel="noopener"
&gt;GRIB tools examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://rda.ucar.edu/datasets/ds083.2/software/README_Formats.pdf" target="_blank" rel="noopener"
&gt;README on Formats (UCAR)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.ftp.cpc.ncep.noaa.gov/wd51we/wgrib2/intro_grib2.pdf" target="_blank" rel="noopener"
&gt;Intro to GRIB2 (PDF)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/" target="_blank" rel="noopener"
&gt;GRIB2 Spec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.unidata.ucar.edu/presentations/Rew/netcdf-intro-2014.pdf" target="_blank" rel="noopener"
&gt;NetCDF Intro (2014 Presentation)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://research.atmos.ucla.edu/csi/GROUP/tips/NCO_basics_N.Berg2013.pdf" target="_blank" rel="noopener"
&gt;NCO Basics (PDF)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>The Programming Book That Made My Career</title><link>https://tdhopper.com/blog/learning-python/</link><pubDate>Wed, 20 Jan 2021 13:58:16 +0000</pubDate><guid>https://tdhopper.com/blog/learning-python/</guid><description>&lt;img src="https://tdhopper.com/images/minimalist-landscape-with-mouse-and-snake.png" alt="Featured image of post The Programming Book That Made My Career" /&gt;&lt;p&gt;On January 7, 2011, &lt;a class="link" href="https://twitter.com/tdhopper/status/23515755127508993" target="_blank" rel="noopener"
&gt;I
tweeted&lt;/a&gt;, &amp;ldquo;Trying to
learn Python. We&amp;rsquo;ll see if this keeps up once classes start.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;On January 20, 2011, ten years ago today, I bought Mark Lutz&amp;rsquo;s &lt;a class="link" href="https://amzn.to/3sHDwXa" target="_blank" rel="noopener"
&gt;Learning Python&lt;/a&gt; from O&amp;rsquo;Reilly Books. Over the next month, I
read it on my Kindle on the Stairmaster at the school gym. It changed the
course of my adult life: since reading that book, I&amp;rsquo;ve written Python on more
days than I haven&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;Before discovering the data science Twitter community in late 2010, I&amp;rsquo;d only
heard of Python through another student researcher in my 2007
&lt;a class="link" href="https://www.nsf.gov/crssprgm/reu/" target="_blank" rel="noopener"
&gt;REU,&lt;/a&gt; who had used Python for some graphics
programming research. On Twitter, people like &lt;a class="link" href="https://twitter.com/hmason" target="_blank" rel="noopener"
&gt;Hilary
Mason&lt;/a&gt;, &lt;a class="link" href="https://twitter.com/JohnDCook" target="_blank" rel="noopener"
&gt;John
Cook&lt;/a&gt;, and &lt;a class="link" href="https://twitter.com/fonnesbeck" target="_blank" rel="noopener"
&gt;Chris
Fonnesbeck&lt;/a&gt; talked warmly about using Python
in their scientific work.&lt;/p&gt;
&lt;p&gt;In 2011, I was a first-year operations research student at North Carolina
State University, and I realized that I should pick up programming again to
improve my career prospects. I&amp;rsquo;d taught myself some PHP in high school (circa
2003). In undergrad, I did a computer science minor and learned a good bit of
C++ and did a lot of Mathematica scripting in my math coursework, but from
2008 to 2011, I basically didn&amp;rsquo;t program.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://twitter.com/tdhopper/status/132924915526533120" target="_blank" rel="noopener"
&gt;I struggled to use
Python&lt;/a&gt; in practice
for ML/scientific computing in those days before wheels (binary installs);
libraries like Scipy and Numpy required brittle compilation of C++ and Fortran
dependencies. During the summer of 2011, I gave up using Python (&lt;a class="link" href="https://twitter.com/tdhopper/status/73798291648811008" target="_blank" rel="noopener"
&gt;after
segfaulting&lt;/a&gt;
&lt;a class="link" href="https://orangedatamining.com/" target="_blank" rel="noopener"
&gt;Orange&lt;/a&gt;) for my internship at &lt;a class="link" href="https://en.wikipedia.org/wiki/Amazon_Robotics" target="_blank" rel="noopener"
&gt;Kiva Systems&lt;/a&gt; and dove into R, where
I could install packages more reliably and use
&lt;a class="link" href="https://ggplot2.tidyverse.org/" target="_blank" rel="noopener"
&gt;ggplot2&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For some reason (probably because of Twitter), I returned to Python a year
later and used it to write my research code in grad school (despite my
advisor&amp;rsquo;s wishes that I use C++). &lt;a class="link" href="https://github.com/fonnesbeck/ScipySuperpack" target="_blank" rel="noopener"
&gt;Scipy Superpack&lt;/a&gt; for installing the
Scipy stack was invaluable (thankfully now replaced by Wheels and Conda).&lt;/p&gt;
&lt;p&gt;My experience with Python was a big reason I got hired at &lt;a class="link" href="https://www.rti.org/" target="_blank" rel="noopener"
&gt;RTI International&lt;/a&gt; when I left my PhD program in October. They were looking to reduce their SAS dependency (and costs) and wanted
people experienced with open-source tools. I taught my colleagues a &amp;ldquo;Python for
statisticians&amp;rdquo; seminar soon after joining RTI.&lt;/p&gt;
&lt;p&gt;From RTI, I joined Parsely, which was like a Python boot camp working with &lt;a class="link" href="https://amontalenti.com/" target="_blank" rel="noopener"
&gt;Andrew Montalenti&lt;/a&gt; and others. Parsely uses Python across
their full stack, and it was an eye-opening and educational year for me.&lt;/p&gt;
&lt;p&gt;Since then, I&amp;rsquo;ve worked at a variety of companies where I&amp;rsquo;ve been able to use
Python for training machine learning models, building machine learning
platforms, &lt;a class="link" href="https://dp.tdhopper.com" target="_blank" rel="noopener"
&gt;writing Gibbs samplers for nonparametric Bayes&lt;/a&gt;, building data engineering pipelines, software
testing, etc.&lt;/p&gt;
&lt;p&gt;I use Python almost every single day for work and a lot of personal projects.
I&amp;rsquo;ve been able &lt;a class="link" href="https://tdhopper.com/talks" &gt;to speak&lt;/a&gt; at 3 PyData conferences,
a Scipy conference, and a number &lt;a class="link" href="https://www.meetup.com/tripython/" target="_blank" rel="noopener"
&gt;Triangle Python Users Groups&lt;/a&gt;. I had a &lt;a class="link" href="https://github.com/python/cpython/pull/11847" target="_blank" rel="noopener"
&gt;contribution merged into CPython in 2019&lt;/a&gt; and have
contributed to many other open-source projects.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m grateful to the countless people who have taught me (through tweets, code
reviews, conference talks, etc) about Python and the many who have built the
wonderful language with its incredible ecosystem of tools and packages that
enable me and others to do so many things.&lt;/p&gt;</description></item><item><title>Feature Stores with Willem Pienaar</title><link>https://tdhopper.com/blog/feature-stores-with-willem-pienaar/</link><pubDate>Sat, 16 Jan 2021 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/feature-stores-with-willem-pienaar/</guid><description>&lt;img src="https://tdhopper.com/images/podcast.png" alt="Featured image of post Feature Stores with Willem Pienaar" /&gt;&lt;h2 id="listen"&gt;Listen
&lt;/h2&gt;&lt;iframe src="https://open.spotify.com/embed/episode/2tVjErd1RD0t3BZbNXcKYH" width="100%" height="232" frameBorder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="links"&gt;Links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://twitter.com/willpienaar?lang=en" target="_blank" rel="noopener"
&gt;Willem&amp;rsquo;s Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.twitter.com/adamlaiacano" target="_blank" rel="noopener"
&gt;Adam&amp;rsquo;s Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.youtube.com/watch?v=DaNv-Wf1MBA" target="_blank" rel="noopener"
&gt;Feast: feature store for Machine Learning (2020 talk)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://feast.dev/" target="_blank" rel="noopener"
&gt;Feast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://tecton.ai/" target="_blank" rel="noopener"
&gt;Tecton&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://en.wikipedia.org/wiki/Gojek" target="_blank" rel="noopener"
&gt;GoJek on Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.featurestore.org/" target="_blank" rel="noopener"
&gt;featurestore.org&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="subscribe"&gt;Subscribe
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/podcast/feed" target="_blank" rel="noopener"
&gt;RSS Feed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://podcasts.apple.com/us/podcast/into-the-hopper/id1499693201" target="_blank" rel="noopener"
&gt;Apple Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://open.spotify.com/show/63NrgKMVb0VTwkklGboIjy" target="_blank" rel="noopener"
&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://overcast.fm/itunes1499693201/into-the-hopper" target="_blank" rel="noopener"
&gt;Overcast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Evolution of a Data Scientist with Adam Laiacano</title><link>https://tdhopper.com/blog/evolution-of-a-data-scientist-with-adam-laiacano/</link><pubDate>Sat, 10 Oct 2020 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/evolution-of-a-data-scientist-with-adam-laiacano/</guid><description>&lt;img src="https://tdhopper.com/images/podcast.png" alt="Featured image of post Evolution of a Data Scientist with Adam Laiacano" /&gt;&lt;h2 id="listen"&gt;Listen
&lt;/h2&gt;&lt;iframe src="https://open.spotify.com/embed/episode/32c5iOpFgFXKey2i2HYVFa" width="100%" height="232" frameBorder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="links"&gt;Links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.twitter.com/adamlaiacano" target="_blank" rel="noopener"
&gt;Adam&amp;rsquo;s Twitter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="subscribe"&gt;Subscribe
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/podcast/feed" target="_blank" rel="noopener"
&gt;RSS Feed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://podcasts.apple.com/us/podcast/into-the-hopper/id1499693201" target="_blank" rel="noopener"
&gt;Apple Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://open.spotify.com/show/63NrgKMVb0VTwkklGboIjy" target="_blank" rel="noopener"
&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://overcast.fm/itunes1499693201/into-the-hopper" target="_blank" rel="noopener"
&gt;Overcast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>git check-ignore</title><link>https://tdhopper.com/blog/git-check-ignore/</link><pubDate>Fri, 18 Sep 2020 13:10:00 +0000</pubDate><guid>https://tdhopper.com/blog/git-check-ignore/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post git check-ignore" /&gt;&lt;p&gt;&lt;a class="link" href="https://git-scm.com/docs/git-check-ignore" target="_blank" rel="noopener"
&gt;Git has&lt;/a&gt; a &lt;code&gt;git check-ignore -v &amp;lt;pathname&amp;gt;&lt;/code&gt; option which will explain to you why a given file is ignored by the repository. This is helpful for debugging &lt;code&gt;.gitignore&lt;/code&gt; and other exclude options.&lt;/p&gt;
&lt;p&gt;I hope you never need this, but you&amp;rsquo;ll be glad if you do.&lt;/p&gt;</description></item><item><title>Customer Focused Data Science with Mike Rogers</title><link>https://tdhopper.com/blog/customer-focused-data-science-with-mike-rogers/</link><pubDate>Thu, 23 Jul 2020 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/customer-focused-data-science-with-mike-rogers/</guid><description>&lt;img src="https://tdhopper.com/images/podcast.png" alt="Featured image of post Customer Focused Data Science with Mike Rogers" /&gt;&lt;h2 id="listen"&gt;Listen
&lt;/h2&gt;&lt;iframe src="https://open.spotify.com/embed/episode/6j7ixJgziQhF9U3zVmQoXf" width="100%" height="232" frameBorder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="links"&gt;Links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.talasecurity.io/" target="_blank" rel="noopener"
&gt;Tala Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://twitter.com/complicatedbull" target="_blank" rel="noopener"
&gt;Mike&amp;rsquo;s Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.linkedin.com/in/kfbr392/" target="_blank" rel="noopener"
&gt;Mike&amp;rsquo;s LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/blog/how-i-became-a-data-scientist" target="_blank" rel="noopener"
&gt;How I Became a Data Scientist Despite Being a Math Major&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="subscribe"&gt;Subscribe
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/podcast/feed" target="_blank" rel="noopener"
&gt;RSS Feed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://podcasts.apple.com/us/podcast/into-the-hopper/id1499693201" target="_blank" rel="noopener"
&gt;Apple Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://open.spotify.com/show/63NrgKMVb0VTwkklGboIjy" target="_blank" rel="noopener"
&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://overcast.fm/itunes1499693201/into-the-hopper" target="_blank" rel="noopener"
&gt;Overcast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Go from a Pythonista's Perspective</title><link>https://tdhopper.com/blog/go-from-a-pythonistas-perspective/</link><pubDate>Mon, 20 Jul 2020 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/go-from-a-pythonistas-perspective/</guid><description>&lt;p&gt;Recently, I&amp;rsquo;ve had my first opportunity to dive into the &lt;a class="link" href="https://golang.org" target="_blank" rel="noopener"
&gt;Go programming language&lt;/a&gt;. Most of my career has been as a Python developer, but given how frequently they are &lt;a class="link" href="https://getstream.io/blog/switched-python-go/" target="_blank" rel="noopener"
&gt;compared&lt;/a&gt;, Go seemed like a natural language to try.&lt;/p&gt;
&lt;p&gt;Before I move on to other things, I wanted to capture my reflections on Go, particularly as it compares to my experience with Python.&lt;/p&gt;
&lt;h2 id="the-good"&gt;The good
&lt;/h2&gt;&lt;h3 id="go-runs-really-quickly"&gt;Go runs really quickly
&lt;/h3&gt;&lt;p&gt;Core to the promises of the Go language is its ability to compile and run quickly. It lives up to these promises! It felt like the only time I ever spent waiting for something to happen was when Go was downloading dependencies.&lt;/p&gt;
&lt;h3 id="go-build-just-works"&gt;&lt;code&gt;go build&lt;/code&gt; just works
&lt;/h3&gt;&lt;p&gt;One of the notorious challenges of Python is with packaging and deployment. With Go, &lt;code&gt;go build&lt;/code&gt; downloads dependencies and generates a binary that can be run anywhere.&lt;/p&gt;
&lt;p&gt;Even more remarkably, Go allows you to &lt;a class="link" href="https://www.digitalocean.com/community/tutorials/building-go-applications-for-different-operating-systems-and-architectures" target="_blank" rel="noopener"
&gt;build binaries for multiple system architectures&lt;/a&gt; without running that architecture. For example, a Mac user can build a Go binary that will run natively on Windows. Python wheels, on the other hand, require access to a given architecture to build (non-universal) binaries.&lt;/p&gt;
&lt;h3 id="go-modules-is-a-big-improvement"&gt;Go Modules is a big improvement
&lt;/h3&gt;&lt;p&gt;When I first tried Go several years ago, I didn&amp;rsquo;t last long beyond being told that all my source code and dependencies had to live in my GOPATH directory.&lt;/p&gt;
&lt;p&gt;With the recent introduction of &lt;a class="link" href="https://blog.golang.org/using-go-modules" target="_blank" rel="noopener"
&gt;Go Modules&lt;/a&gt;, this absurd restriction has been lifted. Go projects can live anywhere on your computer, and the Go compiler automatically downloads and installs your dependencies.&lt;/p&gt;
&lt;h3 id="go-bans-unused-variables-and-imports"&gt;Go bans unused variables and imports
&lt;/h3&gt;&lt;p&gt;The Go compiler fails to build code with unused imports or variables. I replicate this behavior in Python projects by running flake8 as a pre-commit or continuous integration check. Having it built into the compiler itself was a nice surprise.&lt;/p&gt;
&lt;h3 id="vs-code-wins-again"&gt;VS Code wins again
&lt;/h3&gt;&lt;p&gt;The Go extension for VS Code, like the Python extension, is powerful, helpful, and free.&lt;/p&gt;
&lt;h3 id="garbage-collection-is-nice"&gt;Garbage collection is nice
&lt;/h3&gt;&lt;p&gt;I can&amp;rsquo;t comment on the performance of the Go garbage collector, however I&amp;rsquo;m glad it has one. Go is heavily inspired by C, but it&amp;rsquo;s better for almost everyone by not making us manage our own memory.&lt;/p&gt;
&lt;h3 id="json-marshaling-is-neat"&gt;JSON marshaling is neat
&lt;/h3&gt;&lt;p&gt;For better or worse, JSON data makes the world go &amp;lsquo;round. A delightful aspect of Go is its ability to automatically convert JSON to Go structs and Go structs to JSON. You decorate your struct fields with tags that map to JSON keys, and the standard library handles the rest. Coming from Python, where you&amp;rsquo;re often writing serialization boilerplate or reaching for a third-party library like marshmallow, this felt refreshingly clean.&lt;/p&gt;
&lt;h2 id="the-bad"&gt;The bad
&lt;/h2&gt;&lt;h3 id="tooling-is-rough-around-the-edges"&gt;Tooling is rough around the edges
&lt;/h3&gt;&lt;p&gt;Go&amp;rsquo;s tooling &lt;em&gt;mostly&lt;/em&gt; works well, but I found some rough edges. The &lt;code&gt;go get&lt;/code&gt; command conflates downloading a dependency with installing a binary, which is confusing. And while &lt;code&gt;gofmt&lt;/code&gt; is great in principle (I wish Python had a single blessed formatter&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;), the broader tooling ecosystem felt immature compared to what I&amp;rsquo;m used to in Python.&lt;/p&gt;
&lt;h3 id="simple-things-are-hard"&gt;Simple things are hard
&lt;/h3&gt;&lt;p&gt;In Python, if I want to filter a list, I write a list comprehension:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;evens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;In Go, this requires a manual for loop with an append:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;evens&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;evens&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This kind of thing comes up constantly. Operations that are one-liners in Python &amp;mdash; filtering, mapping, flattening &amp;mdash; all become multi-line loops in Go. I know Go values explicitness, but there&amp;rsquo;s a point where explicitness just means more code to read and more opportunities for off-by-one errors.&lt;/p&gt;
&lt;h3 id="standard-data-structures-are-limited"&gt;Standard data structures are limited
&lt;/h3&gt;&lt;p&gt;Go gives you slices and maps, and that&amp;rsquo;s about it. There&amp;rsquo;s no built-in set type, which surprised me. In Python, I use sets all the time for membership tests and deduplication. In Go, the idiomatic workaround is &lt;code&gt;map[string]bool&lt;/code&gt; or &lt;code&gt;map[string]struct{}&lt;/code&gt;, which works but reads like a hack.&lt;/p&gt;
&lt;p&gt;More broadly, the standard library doesn&amp;rsquo;t provide many of the collection types and utilities I take for granted in Python: no ordered dict, no default dict, no counter. You end up writing the same boilerplate data structure code from project to project.&lt;/p&gt;
&lt;h3 id="is-the-type-system-all-its-chalked-up-to-be"&gt;Is the type system all it&amp;rsquo;s chalked up to be?
&lt;/h3&gt;&lt;p&gt;Go&amp;rsquo;s &lt;a class="link" href="https://golang.org/doc" target="_blank" rel="noopener"
&gt;own documentation&lt;/a&gt; describes it as &amp;ldquo;a fast, statically typed, compiled language that feels like a dynamically typed, interpreted language.&amp;rdquo; I think they&amp;rsquo;re right that it &lt;em&gt;feels&lt;/em&gt; lightweight, but I&amp;rsquo;m not convinced Go&amp;rsquo;s type system catches substantially more bugs than Python with &lt;a class="link" href="http://mypy-lang.org/" target="_blank" rel="noopener"
&gt;mypy&lt;/a&gt; and a good linter.&lt;/p&gt;
&lt;p&gt;Go&amp;rsquo;s type system is simple, which is both its strength and weakness. You get basic type safety, but you don&amp;rsquo;t get sum types, pattern matching, or many of the features that make type systems in languages like Rust or Haskell genuinely powerful at preventing bugs. Meanwhile, mypy has gotten good enough that most of my Python type errors get caught before runtime anyway.&lt;/p&gt;
&lt;h3 id="lack-of-generics"&gt;Lack of generics
&lt;/h3&gt;&lt;p&gt;At the time of writing, Go has no generics.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; This means you can&amp;rsquo;t write a function that, say, finds the maximum element of any ordered type. Instead, you write the same function for &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;float64&lt;/code&gt;, &lt;code&gt;string&lt;/code&gt;, and so on &amp;mdash; or you fall back on &lt;code&gt;interface{}&lt;/code&gt; and lose the type safety that Go is supposed to provide.&lt;/p&gt;
&lt;p&gt;The lack of generics is the root cause of the &amp;ldquo;simple things are hard&amp;rdquo; problem. You can&amp;rsquo;t write a generic &lt;code&gt;filter&lt;/code&gt; or &lt;code&gt;map&lt;/code&gt; function, so you end up writing loops. It&amp;rsquo;s the most commonly cited frustration with Go, and for good reason.&lt;/p&gt;
&lt;h3 id="testing-feels-painful"&gt;Testing feels painful
&lt;/h3&gt;&lt;p&gt;Go&amp;rsquo;s built-in testing package is minimal to a fault. There&amp;rsquo;s no built-in assertion library, so instead of writing something like Python&amp;rsquo;s &lt;code&gt;self.assertEqual(got, expected)&lt;/code&gt;, you write:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;got&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;got %v, want %v&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;got&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;For every. Single. Test. Case. It&amp;rsquo;s tedious and error-prone. Python&amp;rsquo;s pytest, with its simple &lt;code&gt;assert&lt;/code&gt; statements and automatic diffs, is a world apart. I know third-party assertion libraries exist for Go, but the community seems to view them with suspicion.&lt;/p&gt;
&lt;p&gt;Table-driven tests are the idiomatic approach in Go, and they&amp;rsquo;re fine for simple cases. But the overall testing experience felt like a step backward from the Python ecosystem.&lt;/p&gt;
&lt;h3 id="no-exception-handling"&gt;No exception handling
&lt;/h3&gt;&lt;p&gt;Go replaces exceptions with multiple return values: functions return both a result and an error, and you check the error after every call. The result is code like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;You see this pattern &lt;em&gt;everywhere&lt;/em&gt;. It clutters the code and obscures the main logic. In Python, I can write the happy path clearly and handle exceptions where it makes sense. In Go, error handling is interleaved with every step of the business logic.&lt;/p&gt;
&lt;p&gt;I understand the argument for explicit error handling &amp;mdash; unhandled exceptions are a real source of bugs. But Go&amp;rsquo;s approach trades one problem for another: instead of accidentally swallowing exceptions, you accidentally ignore returned errors. And the visual noise of &lt;code&gt;if err != nil&lt;/code&gt; on every other line is real.&lt;/p&gt;
&lt;h3 id="havent-gotten-into-concurrency"&gt;Haven&amp;rsquo;t gotten into concurrency
&lt;/h3&gt;&lt;p&gt;Go&amp;rsquo;s goroutines and channels are supposedly the crown jewel of the language, and I believe it. Concurrency is the use case where Go&amp;rsquo;s design makes the most sense. Unfortunately, I haven&amp;rsquo;t had the chance to work on anything concurrency-heavy yet, so I can&amp;rsquo;t speak to this from experience. I suspect this is where Go would really shine compared to Python&amp;rsquo;s GIL-constrained threading.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Go is a fine language, and I can see why it&amp;rsquo;s popular for building networked services and infrastructure tools. The compilation speed, the deployment story, and the concurrency model are genuine strengths that Python can&amp;rsquo;t match.&lt;/p&gt;
&lt;p&gt;But for the kind of work I do &amp;mdash; data processing, scripting, exploratory analysis &amp;mdash; Python is still a better fit. The expressiveness of the language, the richness of the standard library, and the depth of the ecosystem (numpy, pandas, scikit-learn) make it hard to leave. Go felt like trading expressiveness for performance, and for many of my use cases, that&amp;rsquo;s not a trade I need to make.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re a Pythonista considering Go, I&amp;rsquo;d say: try it. You&amp;rsquo;ll appreciate some things about it, and it&amp;rsquo;ll give you a new perspective on your Python code. But don&amp;rsquo;t feel like you need to switch.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/psf/black" target="_blank" rel="noopener"
&gt;Black&lt;/a&gt; is close to this, but it&amp;rsquo;s not universally adopted the way &lt;code&gt;gofmt&lt;/code&gt; is for Go.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Go generics were &lt;a class="link" href="https://blog.golang.org/generics-next-step" target="_blank" rel="noopener"
&gt;proposed&lt;/a&gt; and have been a long time coming. I look forward to seeing how they change the language.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Fizz Buzz Book with Joel Grus</title><link>https://tdhopper.com/blog/fizz-buzz-book-with-joel-grus/</link><pubDate>Wed, 15 Jul 2020 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/fizz-buzz-book-with-joel-grus/</guid><description>&lt;img src="https://tdhopper.com/images/podcast.png" alt="Featured image of post Fizz Buzz Book with Joel Grus" /&gt;&lt;h2 id="listen"&gt;Listen
&lt;/h2&gt;&lt;iframe src="https://open.spotify.com/embed/episode/70eixpjzwzrccQlul6xAlL" width="100%" height="232" frameBorder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="links"&gt;Links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://joelgrus.com/2016/05/23/fizz-buzz-in-tensorflow/" target="_blank" rel="noopener"
&gt;Fizz Buzz in Tensorflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.youtube.com/watch?v=o64FV-ez6Gw" target="_blank" rel="noopener"
&gt;Livecoding Madness - Let&amp;rsquo;s Build a Deep Learning Library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.youtube.com/watch?v=7jiPeIFXb6U" target="_blank" rel="noopener"
&gt;I don&amp;rsquo;t like notebooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.youtube.com/watch?v=VPXXthwAdg8" target="_blank" rel="noopener"
&gt;Live Coding the Advent of Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://amzn.to/3j9SS1G" target="_blank" rel="noopener"
&gt;Data Science from Scratch Second Edition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://fizzbuzzbook.com/" target="_blank" rel="noopener"
&gt;Fizz Buzz Book: Meditations on Python, mathematics, science, engineering, and design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://twitter.com/joelgrus" target="_blank" rel="noopener"
&gt;Joel&amp;rsquo;s Twitter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="subscribe"&gt;Subscribe
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/podcast/feed" target="_blank" rel="noopener"
&gt;RSS Feed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://podcasts.apple.com/us/podcast/into-the-hopper/id1499693201" target="_blank" rel="noopener"
&gt;Apple Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://open.spotify.com/show/63NrgKMVb0VTwkklGboIjy" target="_blank" rel="noopener"
&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://overcast.fm/itunes1499693201/into-the-hopper" target="_blank" rel="noopener"
&gt;Overcast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Goodnight Zoom</title><link>https://tdhopper.com/blog/goodnight-zoom/</link><pubDate>Tue, 23 Jun 2020 11:30:43 +0000</pubDate><guid>https://tdhopper.com/blog/goodnight-zoom/</guid><description>&lt;img src="https://tdhopper.com/goodnight-zoom.png" alt="Featured image of post Goodnight Zoom" /&gt;&lt;p&gt;In the great #general room
There was idle chatter
And a red build
And a gif of-
The cow jumping over the moon&lt;/p&gt;
&lt;p&gt;And there were three little interns sitting with mentors
And two little commits
And a backlog of tickets
And a little blocker
And a fresh Docker
And a PM and a DM and a glass full of beer
And a loud young salesman who was typing @here&lt;/p&gt;
&lt;p&gt;Goodnight Slack room
Goodnight Zoom
Goodnight manager hopping on Zoom
Goodnight Jira
And the red build
Goodnight interns
Goodnight mentors
Goodnight AWS
And goodnight Bash&lt;/p&gt;
&lt;p&gt;Goodnight PMs
And goodnight DMs
Goodnight little blocker
And goodnight Docker
Goodnight commits
And goodnight tickets
Goodnight TypeError: null is not an object
Goodnight project
And a loud young salesman who was typing “@here”&lt;/p&gt;
&lt;p&gt;Goodnight Github stars
Goodnight asserts
Good night pager duty alerts&lt;/p&gt;</description></item><item><title>Tips for Working from Home</title><link>https://tdhopper.com/blog/tips-for-working-from-home/</link><pubDate>Mon, 16 Mar 2020 08:04:00 +0000</pubDate><guid>https://tdhopper.com/blog/tips-for-working-from-home/</guid><description>&lt;img src="https://tdhopper.com/images/office_ping_pong_game_illustration.png" alt="Featured image of post Tips for Working from Home" /&gt;&lt;p&gt;I&amp;rsquo;ve been working remotely for tech startups for six years, so I am sharing some tips for those working from home for the first time.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start Working Early&lt;/strong&gt;&lt;br&gt;
If you can&amp;rsquo;t find someone to chat with for 30 minutes while making coffee, consider starting your workday instead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simulate Office Noise&lt;/strong&gt;&lt;br&gt;
If you miss the background noise of your office, try listening to 8 hours of power drill noises &lt;a class="link" href="https://www.youtube.com/watch?v=mNu4cNgJ-Hs" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;. For a more realistic ambiance, you can overlay it with the most annoying laugh ever &lt;a class="link" href="https://youtube.com/watch?v=9WcSaIAa5Ac" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Afternoon Activities&lt;/strong&gt;&lt;br&gt;
Without a ping-pong table at home, you might find yourself with nothing to do but work in the afternoons.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Familiarize Yourself with Remote Tools&lt;/strong&gt;&lt;br&gt;
Programmers, take some time to get used to the essential tools for remote work, such as Jira, GitHub, Slack, and Zoom.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Engage in Local Politics&lt;/strong&gt;&lt;br&gt;
If the quarantine lasts longer than expected and you start to miss petty workplace politics, consider joining your local HOA.&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Ten Years of Data Science with Josh Wills and Oscar Boykin</title><link>https://tdhopper.com/blog/ten-years-of-data-science-with-josh-wills-and-oscar-boykin/</link><pubDate>Wed, 19 Feb 2020 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/ten-years-of-data-science-with-josh-wills-and-oscar-boykin/</guid><description>&lt;img src="https://tdhopper.com/images/podcast.png" alt="Featured image of post Ten Years of Data Science with Josh Wills and Oscar Boykin" /&gt;&lt;h2 id="listen"&gt;Listen
&lt;/h2&gt;&lt;iframe src="https://open.spotify.com/embed/episode/5ef9td3z5bvG6ydGfE0RE3" width="100%" height="232" frameBorder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;h2 id="links"&gt;Links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://twitter.com/josh_wills/" target="_blank" rel="noopener"
&gt;Josh&amp;rsquo;s twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://twitter.com/posco/" target="_blank" rel="noopener"
&gt;Oscar&amp;rsquo;s twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://softwareengineeringdaily.com/2020/01/10/slack-data-platform-with-josh-wills/" target="_blank" rel="noopener"
&gt;Josh on Software Engineering Daily&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/twitter/scalding" target="_blank" rel="noopener"
&gt;Scalding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/twitter/summingbird" target="_blank" rel="noopener"
&gt;Summingbird&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/apache/crunch" target="_blank" rel="noopener"
&gt;Apache Crunch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;Data Scientist (n.): Person who is better at statistics than any software engineer and better at software engineering than any statistician.&lt;/p&gt;&amp;mdash; Josh Wills (@josh_wills) &lt;a href="https://twitter.com/josh_wills/status/198093512149958656?ref_src=twsrc%5Etfw"&gt;May 3, 2012&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;h2 id="subscribe"&gt;Subscribe
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/podcast/feed" target="_blank" rel="noopener"
&gt;RSS Feed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://podcasts.apple.com/us/podcast/into-the-hopper/id1499693201" target="_blank" rel="noopener"
&gt;Apple Podcasts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://open.spotify.com/show/63NrgKMVb0VTwkklGboIjy" target="_blank" rel="noopener"
&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://overcast.fm/itunes1499693201/into-the-hopper" target="_blank" rel="noopener"
&gt;Overcast&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Safety first?</title><link>https://tdhopper.com/blog/safety-first/</link><pubDate>Wed, 09 Oct 2019 09:13:00 +0000</pubDate><guid>https://tdhopper.com/blog/safety-first/</guid><description>&lt;img src="https://tdhopper.com/images/safetyship.png" alt="Featured image of post Safety first?" /&gt;&lt;p&gt;I&amp;rsquo;m always annoyed when the pre-flight instructions inform us that the airline&amp;rsquo;s main priority is our safety.&lt;/p&gt;
&lt;p&gt;Turns out Thomas Aquinas observed this 700 years ago:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the highest aim of a captain were to preserve his ship, he would keep it in port for ever.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>My Tweet in BBC News</title><link>https://tdhopper.com/blog/tweets-in-the-news/</link><pubDate>Mon, 01 Jul 2019 13:40:00 +0000</pubDate><guid>https://tdhopper.com/blog/tweets-in-the-news/</guid><description>&lt;p&gt;A few weeks back, I was browsing Google Maps and noticed the word &amp;ldquo;DEMO&amp;rdquo;
written in faint green letters in rural South Sudan. A journalist came across
this tweet and &lt;a class="link" href="https://www.bbc.com/news/technology-48444703" target="_blank" rel="noopener"
&gt;wrote about it for
bbc.com&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="qme" dir="ltr"&gt;🤔 &lt;a href="https://t.co/TOJvHsxdD9"&gt;https://t.co/TOJvHsxdD9&lt;/a&gt; &lt;a href="https://t.co/UtHG6Jpgxz"&gt;pic.twitter.com/UtHG6Jpgxz&lt;/a&gt;&lt;/p&gt;&amp;mdash; Tim Hopper (@tdhopper) &lt;a href="https://twitter.com/tdhopper/status/1133413170909982720?ref_src=twsrc%5Etfw"&gt;May 28, 2019&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;</description></item><item><title>My Tweet in Nature</title><link>https://tdhopper.com/blog/nature-article-open-source/</link><pubDate>Mon, 01 Jul 2019 12:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/nature-article-open-source/</guid><description>&lt;img src="https://tdhopper.com/images/computer-fishing.png" alt="Featured image of post My Tweet in Nature" /&gt;&lt;p&gt;Way back in 2015, I tweeted:&lt;/p&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;Give a man a fish and you feed him for a day.&lt;br&gt;&lt;br&gt;Write a program to fish for him and you maintain it for a lifetime.&lt;/p&gt;&amp;mdash; Tim Hopper (@tdhopper) &lt;a href="https://twitter.com/tdhopper/status/661551195291574272?ref_src=twsrc%5Etfw"&gt;November 3, 2015&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;Today, that tweet appeared in &lt;a class="link" href="https://www.nature.com/articles/d41586-019-02046-0" target="_blank" rel="noopener"
&gt;a Nature article about the challenges of releasing and maintaining open source software&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The article opens with the story of how the first-ever image of a black hole was made possible by open-source software like Matplotlib, yet just five days after that historic announcement, the NSF rejected a grant proposal to support that very ecosystem, &amp;ldquo;saying that the software lacked sufficient impact.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Installing Python on Mohave with pyenv</title><link>https://tdhopper.com/blog/installing-python/</link><pubDate>Thu, 30 May 2019 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/installing-python/</guid><description>&lt;p&gt;I use &lt;a class="link" href="https://github.com/pyenv/pyenv" target="_blank" rel="noopener"
&gt;pyenv&lt;/a&gt; to manage Python versions on my Mac. I recently have gotten errors like&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;WARNING: The Python sqlite3 extension was not compiled. Missing the SQLite3 lib?
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;and&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;zipimport.ZipImportError: can&amp;#39;t decompress data; zlib not available
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The solution seems to be setting &lt;code&gt;LDFLAGS&lt;/code&gt; and &lt;code&gt;CPPFLAGS&lt;/code&gt; to point to the sqlite3 and zlib libraries, e.g.:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew install sqlite3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew install zlib
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;LDFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;-L/usr/local/opt/zlib/lib -L/usr/local/opt/sqlite/lib&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;CPPFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;-I/usr/local/opt/zlib/include -I/usr/local/opt/sqlite/include&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pyenv install 3.7.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Access a IPython repl from pdb</title><link>https://tdhopper.com/blog/ipython-pdb/</link><pubDate>Wed, 13 Feb 2019 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/ipython-pdb/</guid><description>&lt;p&gt;I&amp;rsquo;m a big fan of using &lt;a class="link" href="https://docs.python.org/3/library/pdb.html" target="_blank" rel="noopener"
&gt;pdb&lt;/a&gt;, the Python interactive debugger in conjunction with Pytest as I&amp;rsquo;m writing code.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; With the &lt;code&gt;--pdb&lt;/code&gt; flag, you can have Pytest drop into pdb when a test fails. With &lt;code&gt;pytest.set_trace()&lt;/code&gt;, you can selectively enter pdb while running your tests. (I use this where I might&amp;rsquo;ve just added print statements in the past.)&lt;/p&gt;
&lt;p&gt;A colleague uses IPython for most of his interactive development and asked me for help with some of the friction in his workflow. I recommended pdb, but he wanted to stick with the familiar IPython repl for much of his work.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; He figured how to &lt;a class="link" href="https://ipython.readthedocs.io/en/stable/interactive/reference.html#embedding" target="_blank" rel="noopener"
&gt;drop into an IPython repl&lt;/a&gt; from pdb with &lt;code&gt;from IPython import embed; embed()&lt;/code&gt;. You can&amp;rsquo;t move up and down the call stack until you exit the repl, but you have access to all the local state you have in pdb.&lt;sup id="fnref1:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;To simplify this, I recommended leveraging two little-known pdb features: &lt;code&gt;alias&lt;/code&gt; and &lt;code&gt;.pdbrc&lt;/code&gt;. &lt;a class="link" href="https://docs.python.org/3/library/pdb.html#debugger-aliases" target="_blank" rel="noopener"
&gt;&lt;code&gt;alias&lt;/code&gt;&lt;/a&gt; allows you to set alias for statements in pdb; in this case &lt;code&gt;ipy&lt;/code&gt; as an alias that drops into IPython:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;alias ipy from IPython import embed; embed()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;pdb also as the ability to load a config file from the user&amp;rsquo;s home directory or the current working directory. Each line in the file is just a pdb statement. Thus, if you can create &lt;code&gt;~/.pdbrc&lt;/code&gt; and add the alias statement above, the &lt;code&gt;ipy&lt;/code&gt; command becomes available in every pdb session for your user.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I highly recommend pip installing &lt;a class="link" href="https://pypi.org/project/pdbpp/" target="_blank" rel="noopener"
&gt;pdbpp&lt;/a&gt;, which replaces the default pdb with an enhanced debugger with syntax highlighting, tab completion, and more.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;There&amp;rsquo;s also an alternative debugger for Python based on IPython: &lt;a class="link" href="https://pypi.org/project/ipdb/" target="_blank" rel="noopener"
&gt;ipdb&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&amp;#160;&lt;a href="#fnref1:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Right Code, Right Place, Right Time</title><link>https://tdhopper.com/talks/right-code/</link><pubDate>Tue, 29 Jan 2019 00:00:00 +0000</pubDate><guid>https://tdhopper.com/talks/right-code/</guid><description>&lt;img src="https://tdhopper.com/images/code-review.png" alt="Featured image of post Right Code, Right Place, Right Time" /&gt;&lt;p&gt;I gave a talk at Pydata DC 2018 where I tried to articulate some reasons why companies building machine learning products under-invest in engineering and architecture.
I&amp;rsquo;m very interested in feedback, pointers to other resources on this topic, and a general discussion about how to make more effective ML products.&lt;/p&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vShM7kGP4WIWtr545IRMv2viQ_xpsn2ouAdYvo1yWuP5EmIWk3wYapKBWMjqua4bRwdeKHFHsz0qPSe/embed?start=false&amp;loop=false&amp;delayms=10000" frameborder="0" width="100%" height="400" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;small&gt;&lt;a class="link" href="https://docs.google.com/presentation/d/1CUfrWaET5cepR9KUXHMe9ChbenEaRAS-9KNyTxAb_HM/edit?usp=sharing" target="_blank" rel="noopener"
&gt;Open in Google Docs&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;And the video:&lt;/p&gt;
&lt;div class="video-wrapper"&gt;
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/BvqLgeNVg0Y"
allowfullscreen
title="YouTube Video"
&gt;
&lt;/iframe&gt;
&lt;/div&gt;</description></item><item><title>Problem of Programming</title><link>https://tdhopper.com/blog/problem-of-programming/</link><pubDate>Thu, 20 Dec 2018 12:30:00 +0000</pubDate><guid>https://tdhopper.com/blog/problem-of-programming/</guid><description>&lt;img src="https://tdhopper.com/images/computerweights.png" alt="Featured image of post Problem of Programming" /&gt;&lt;p&gt;Edsger W. Dijkstra in 1972:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As long as there were no machines, programming was no problem at all; when we had a few weak computers, programming became a mild problem, and now we have gigantic computers, programming has become an equally gigantic problem.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>"I Like Notebooks": a response to Joel Grus</title><link>https://tdhopper.com/blog/i-like-notebooks-a-response-to-joel-grus/</link><pubDate>Tue, 13 Nov 2018 12:30:00 +0000</pubDate><guid>https://tdhopper.com/blog/i-like-notebooks-a-response-to-joel-grus/</guid><description>&lt;img src="https://tdhopper.com/images/notebook.png" alt="Featured image of post "I Like Notebooks": a response to Joel Grus" /&gt;&lt;p&gt;At JuypterDay in the Triangle, I gave a response to &lt;a class="link" href="https://www.youtube.com/watch?v=7jiPeIFXb6U" target="_blank" rel="noopener"
&gt;Joel Grus&amp;rsquo;s memorable &amp;ldquo;I Don&amp;rsquo;t Like Notebooks&lt;/a&gt; presentation.&lt;/p&gt;
&lt;p&gt;It wasn&amp;rsquo;t recorded, but &lt;a class="link" href="https://docs.google.com/presentation/d/1XmbeH_sdOKqhi05_FbH2EdRw948i8IvBz1PdfJGbhf4/edit?usp=sharing" target="_blank" rel="noopener"
&gt;here are my slides&lt;/a&gt;:&lt;/p&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vT-rTqOaHhXN5ztzOzstVrRDRyf-wNhEfSyaCVYVJ277HrzTcM_qXReQNIyTEU9p_xffmNrEamtmNtd/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="100%" height="400" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;</description></item><item><title>Devops Empowered Data Science with Ansible</title><link>https://tdhopper.com/talks/ansible-talk/</link><pubDate>Thu, 12 Jul 2018 00:00:00 +0000</pubDate><guid>https://tdhopper.com/talks/ansible-talk/</guid><description>&lt;img src="https://tdhopper.com/preso.png" alt="Featured image of post Devops Empowered Data Science with Ansible" /&gt;&lt;p&gt;I gave a talk at Scipy 2018 loosely based on my &lt;a class="link" href="https://tdhopper.com/blog/automating-python-with-ansible/" &gt;Ansible tutorial&lt;/a&gt;. Here are my slides:&lt;/p&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vSHtiQKbpNvd902FzCqhzjZCbt4ldbPnolgSQfQbaPoQk0eweUng7Wkxjb5uDi942Ul0trl3s7C0yQ5/embed?start=false&amp;loop=false&amp;delayms=5000" frameborder="0" width="480" height="389" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;small&gt;&lt;a class="link" href="https://drive.google.com/open?id=1nsgG2YjfE5yWGdsxfzgqDj4SS1isyOIzBUjNa-r8ly0" target="_blank" rel="noopener"
&gt;Open in Google Docs&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;And the video:&lt;/p&gt;
&lt;div class="video-wrapper"&gt;
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/LKVtmGtT6qg"
allowfullscreen
title="YouTube Video"
&gt;
&lt;/iframe&gt;
&lt;/div&gt;</description></item><item><title>Challenges in Applying Machine Learning to Cybersecurity</title><link>https://tdhopper.com/talks/ml-for-security/</link><pubDate>Sat, 23 Jun 2018 00:00:00 +0000</pubDate><guid>https://tdhopper.com/talks/ml-for-security/</guid><description>&lt;img src="https://tdhopper.com/preso.png" alt="Featured image of post Challenges in Applying Machine Learning to Cybersecurity" /&gt;&lt;p&gt;I gave a talk called &lt;em&gt;Challenges in Applying Machine Learning to Cybersecurity&lt;/em&gt; at &lt;a class="link" href="http://bsidesasheville.com" target="_blank" rel="noopener"
&gt;BSides Asheville&lt;/a&gt;. Here are my slides:&lt;/p&gt;
&lt;div class="embed-responsive embed-responsive-16by9"&gt;
&lt;iframe class="embed-responsive-item" src="https://docs.google.com/presentation/d/e/2PACX-1vSxsBcas-WSaIchfsPU5uJI5mEiWW9oHxsGuW59H61P_XD-VGCNTaPro92fsWa1ovjyKCu2S4CluBi7/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="960" height="569" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;And the video:&lt;/p&gt;
&lt;div class="video-wrapper"&gt;
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/OS6j1Y2tJLY"
allowfullscreen
title="YouTube Video"
&gt;
&lt;/iframe&gt;
&lt;/div&gt;</description></item><item><title>Wrong Figures, Right Answers?</title><link>https://tdhopper.com/blog/wrong-figures/</link><pubDate>Tue, 29 May 2018 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/wrong-figures/</guid><description>&lt;blockquote&gt;
&lt;p&gt;On two occasions I have been asked, — &amp;lsquo;Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?&amp;rsquo; In one case a member of the Upper, and in the other a member of the Lower, House [of British Parliament] put this question.&lt;/p&gt;
&lt;p&gt;I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;— Charles Babbage, &lt;em&gt;Passages from the Life of a Philosopher&lt;/em&gt; (1864)&lt;/p&gt;</description></item><item><title>I Basically Can't Hire People Who Don't Know Git</title><link>https://tdhopper.com/blog/data-scientists-at-work/</link><pubDate>Tue, 22 May 2018 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/data-scientists-at-work/</guid><description>&lt;p&gt;In 2014, &lt;a class="link" href="https://twitter.com/seb_g" target="_blank" rel="noopener"
&gt;Sebastian Gutierrez&lt;/a&gt; published a collection of interviews entitled &lt;a class="link" href="https://amzn.to/2Lo0A7s" target="_blank" rel="noopener"
&gt;Data Scientists at Work&lt;/a&gt;. My friend and former boss Eric Jonas posted &lt;a class="link" href="http://ericjonas.com/datascientistsatwork.html" target="_blank" rel="noopener"
&gt;his interview on his website&lt;/a&gt;. It&amp;rsquo;s full of gems.&lt;/p&gt;
&lt;p&gt;On engineering skills required for data science work, Eric says,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;On the industry side, I think that the ability to do software engineering is something that is very important, but isn’t really taught. You don’t actually learn it as a computer science undergraduate, and you certainly don’t learn it as a graduate student. So for me it’s very important that someone has learned it somehow—either by themselves or from someone else. &lt;strong&gt;I basically can’t hire people who don’t know Git.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On someone trained in pure mathematics learning to analysis of real-world data, Eric says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;hellip;data analysis is so much messier than actual math. I have friends who work on these topology-based approaches, and I’m like, “You realize these manifolds totally evaporate when you actually throw noise into the system. How do you think this is really going to play out here?” So I would much rather someone be computationally skilled. I’m willing to trade off what their Putnam score was for how many open source GitHub projects they’ve committed to in the past.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I tried to argue this same point in &lt;a class="link" href="https://tdhopper.com/how/" &gt;an earlier post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On applying academic research, Eric observes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For example, when I evaluate machine learning papers, what I am looking to find out is whether the technique worked or not. &lt;strong&gt;This is something that the world needs to know—most papers don’t actually tell you whether the thing worked.&lt;/strong&gt; It’s really infuriating because most papers will show five dataset examples and then show that they’re slightly better on two different metrics when comparing against something from 20 years ago. In academia, it’s fine. In industry, it’s infuriating, because you need to know what actually works and what doesn’t.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have suggested before that we need a good website for sharing implementations of academic algorithms and providing a forum for discussion of whether or not the algorithm &lt;em&gt;actually works&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I highly recommend reading Eric&amp;rsquo;s &lt;a class="link" href="http://ericjonas.com/datascientistsatwork.html" target="_blank" rel="noopener"
&gt;full interview&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>George Washington's Struggle for Inbox Zero</title><link>https://tdhopper.com/blog/george-washington-inbox-zero/</link><pubDate>Tue, 06 Mar 2018 13:34:00 +0000</pubDate><guid>https://tdhopper.com/blog/george-washington-inbox-zero/</guid><description>&lt;img src="https://tdhopper.com/images/george-washington.png" alt="Featured image of post George Washington's Struggle for Inbox Zero" /&gt;&lt;blockquote&gt;
&lt;p&gt;Washington devoted far more time to the onerous task of draft letters than
leading men into battle. Running an embryonic government, he protested to
Congress that he and his aides &amp;ldquo;are confined from morn till eve, hearing and
answering the applications and letters of one and another,&amp;rdquo; leaving him with
&amp;ldquo;no hours for recreation.&amp;rdquo; He groaned at the huge stacks of correspondence
and felt besieged by supplicants for various favors. At times the enormous
quantity of paperwork must have seemed more daunting than British arms.&lt;/p&gt;&lt;span class="cite"&gt;&lt;span&gt;― &lt;/span&gt;&lt;span&gt;Ron Chernow, &lt;/span&gt;&lt;a href="https://amzn.to/3e3NppS"&gt;&lt;cite&gt;Washington: A Life&lt;/cite&gt;&lt;/a&gt;&lt;/span&gt;&lt;/blockquote&gt;</description></item><item><title>A Subjective and Anecdotal FAQ on Becoming a Data Scientist</title><link>https://tdhopper.com/blog/faq/</link><pubDate>Mon, 05 Mar 2018 16:59:00 +0000</pubDate><guid>https://tdhopper.com/blog/faq/</guid><description>&lt;img src="https://tdhopper.com/becoming.png" alt="Featured image of post A Subjective and Anecdotal FAQ on Becoming a Data Scientist" /&gt;&lt;p&gt;&lt;a class="link" href="https://tdhopper.com/blog/how-i-became-a-data-scientist/" &gt;Three years ago&lt;/a&gt;, I
wrote a post called &amp;ldquo;How I Became a Data Scientist Despite Being a Math
Major&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;When I wrote the post, I thought I was explaining that that was about all I
know about how someone can become a data scientist: that is, I shared my
subjective experience. I intended to communicate my uncertainty about the path
others should take. But, given the number of people who have read the post and
emailed asking for my advice on becoming a data scientist, that message wasn&amp;rsquo;t
clear. With most of these emails, I have felt bad because I simply don&amp;rsquo;t know
the answers to the questions people have. But because so many people seem to
have these questions, I decided to consolidate my uncertainty here. I hope you
find it helpful.&lt;/p&gt;
&lt;p&gt;A note before the Q&amp;amp;A: one reason it is so hard to advise someone on becoming
a &amp;ldquo;data scientist&amp;rdquo; is that &amp;ldquo;data scientist&amp;rdquo; is an ill-defined job title. At
some companies, data scientists are building machine learning models and
running them in a high performance production system. At other data companies,
data scientists are business analysts running SQL queries and visualizing the
results with Tableau. Some data scientist are doing complex experimental
design while others are mostly moving files around S3 buckets. Some data
science roles require deep domain expertise, others only need some programming
skill and knowledge of Scikit-Learn. As you shape your own path to data
science, take some time to think specifically about the kind of work you are
interested in doing; this will shape your preparation.&lt;/p&gt;
&lt;h2 id="should-i-get-a-masters-degree"&gt;Should I get a masters degree?
&lt;/h2&gt;&lt;p&gt;My sense is that a good masters degree in a technical field is a valuable
degree. It takes a fraction of the time a Ph.D. takes, yet you can learn a lot
in 3 or 4 semesters of coursework, and the degree is viewed favoribly by
employers. The quality and curriculum of masters programs varies wildly, so
you&amp;rsquo;ll want to do your due diligence before diving in.&lt;/p&gt;
&lt;p&gt;That said, I&amp;rsquo;m wary of people going into deep debt for a masters degree. If
you can get a teaching or research position that waives your tuition, do it.
Consider going a &lt;em&gt;good&lt;/em&gt; school on in-state tuition instead of a &lt;em&gt;great&lt;/em&gt; school
for $100,000. If you have publicly subsidized graduate education (as in many
European countries), go for it!&lt;/p&gt;
&lt;h2 id="will-a-masters-degree-help-me-becomebe-a-data-scientist"&gt;Will a masters degree help me become/be a data scientist?
&lt;/h2&gt;&lt;p&gt;I don&amp;rsquo;t know, but it has helped me. As I said in &lt;a class="link" href="https://tdhopper.com/blog/how-i-became-a-data-scientist/" &gt;my earlier
post&lt;/a&gt;, I learned a
lot about algorithms, probability models, math, and machine learning that has
been invaluable. Grad school also gave me the time and inspiration to learn R
and Python which have played important roles in my career?&lt;/p&gt;
&lt;h2 id="should-i-get-a-masters-degree-in-computer-science-will-it-help-me-become-a-data-scientist"&gt;Should I get a masters degree in computer science? Will it help me become a data scientist?
&lt;/h2&gt;&lt;p&gt;I think a masters degree in computer science will be likely to pay off in the
long run. It may not help you get a job as a data scientist, but it would
undoubtedly help you in a data science job.&lt;/p&gt;
&lt;p&gt;I have half of a computer science masters, and I sometimes wish I had finished
it.&lt;/p&gt;
&lt;h2 id="should-i-get-a-masters-degree-in-operations-research"&gt;Should I get a masters degree in operations research?
&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;m ambivalent about operations research. As a discipline, operations research
has been in an identity crisis. As a curriculum, many operations research
programs are full of content valuable for data scientists. As a signal to
potential employers, operations research is relatively unknown and won&amp;rsquo;t mean
as much as a degree in &amp;ldquo;machine learning&amp;rdquo; or similar.&lt;/p&gt;
&lt;h2 id="did-you-feel-that-operations-research-was-too-theoretical-and-that-you-had-to-hustle-outside-the-classroom-and-build-statml-skills-separately"&gt;Did you feel that operations research was too theoretical and that you had to hustle outside the classroom and build stat/ML skills separately?
&lt;/h2&gt;&lt;p&gt;Operations research programs have wildly different curriculums. Mine didn&amp;rsquo;t do
a great job preparing me for real-world applications, but that might be better
learned on the job anyway. My program allowed me to build statistics and ML
expertise only because I had flexibility in the courses I could select; I was
able to take a handful of stats/ML related classes. Other programs might not
offer that.&lt;/p&gt;
&lt;h2 id="should-i-get-a-masters-degree-in-statistics"&gt;Should I get a masters degree in statistics?
&lt;/h2&gt;&lt;p&gt;If you have strong programming/software engineering skills (or have another
means of building them), a statistics degree could be valuable; as with
anything, I&amp;rsquo;m sure the quality of statistics masters degrees varies greatly,
and it&amp;rsquo;s worth trying to find a good one.&lt;/p&gt;
&lt;h2 id="should-i-get-a-phd"&gt;Should I get a Ph.D.?
&lt;/h2&gt;&lt;p&gt;I don&amp;rsquo;t think it&amp;rsquo;s worth it for most
people. It&amp;rsquo;s also not necessary
for the vast majority of data science jobs. &lt;a class="link" href="https://tdhopper.com/phd" target="_blank" rel="noopener"
&gt;I have a free ebook to
help you answer this question.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There are a lot of people with Ph.D.&amp;rsquo;s in data science roles. It&amp;rsquo;s possible
that is more a result of the large difference in the number Ph.D.&amp;rsquo;s verses the
small number of permanent faculty positions in American universities. People
with Ph.D.&amp;rsquo;s need jobs; many have skills overlapping with data science; they
make their way from academia to data science.&lt;/p&gt;
&lt;h2 id="can-i-become-a-data-scientist-with-only-an-undergraduate-degree"&gt;Can I become a data scientist with only an undergraduate degree?
&lt;/h2&gt;&lt;p&gt;Many others have. I have known several who don&amp;rsquo;t even have college degrees.&lt;/p&gt;
&lt;h2 id="im-in-school-should-i-take-x-class"&gt;I&amp;rsquo;m in school should I take X class?
&lt;/h2&gt;&lt;p&gt;If it&amp;rsquo;s linear algebra, definitely. Otherwise, I&amp;rsquo;m not sure. Among other
reasons, I&amp;rsquo;ve been often surprised how classes I never expected to apply have
helped me years later; it&amp;rsquo;s hard for me to know what other classes would&amp;rsquo;ve
helped me had I taken them.&lt;/p&gt;
&lt;p&gt;Take the best professors you can (note I didn&amp;rsquo;t say &lt;em&gt;easiest&lt;/em&gt; ). Talk to older
students you admire about different classes and professors. Don&amp;rsquo;t let your
schooling interfere with your education.&lt;/p&gt;
&lt;h2 id="can-you-evaluate-my-qualifications-for-being-a-data-scientist"&gt;Can you evaluate my qualifications for being a data scientist?
&lt;/h2&gt;&lt;p&gt;Not very well. In fact, &lt;a class="link" href="https://tdhopper.com/blog/some-reflections-on-being-turned-down-for-a-lot-of-data-science-jobs/" &gt;I think it&amp;rsquo;s pretty challenging&lt;/a&gt; even for people who interview data science
candidates. &lt;a class="link" href="https://tdhopper.com/blog/how-i-became-a-data-scientist/" &gt;I have tried to share the things that helped qualify
me&lt;/a&gt;, and I imagine
those things would be valuable for you as well.&lt;/p&gt;
&lt;p&gt;I would suggest trying to evaluate your qualifications against specific jobs
(or job descriptions) you are interested in. Are you more interested in
&lt;a class="link" href="https://medium.com/@rchang/my-two-year-journey-as-a-data-scientist-at-twitter-f0c13298aee6" target="_blank" rel="noopener"
&gt;analysis or production systems&lt;/a&gt;? Are you interested
cybersecurity applications? Ad markets? Social good? Journalism? Finance?
Healthcare? Self-driving cars? Find job postings for roles and look at the
qualifications. Find people in these roles on Linkedin and look at their
qualifications and job history.&lt;/p&gt;
&lt;p&gt;One other note: just because you&amp;rsquo;re qualified, doesn&amp;rsquo;t mean you will get job
offers. Not getting an offer after interviewing &lt;em&gt;might&lt;/em&gt; reflect more on poor
interviewers than being a poor candidate.&lt;/p&gt;
&lt;h2 id="how-do-i-show-on-my-resumedemonstrate-to-employers-that-i-am-qualified-to-be-a-data-scientist"&gt;How do I show-on-my-resume/demonstrate-to-employers that I am qualified to be a data scientist?
&lt;/h2&gt;&lt;p&gt;My best advice is to work on interesting and relevant things and &lt;a class="link" href="https://youtu.be/uRul8QdYvqQ" target="_blank" rel="noopener"
&gt;tell people
about them&lt;/a&gt;. I don&amp;rsquo;t know how to be more
specific.&lt;/p&gt;
&lt;h2 id="how-can-i-get-a-job-where-i-can-do-more-applied-math"&gt;How can I get a job where I can do more applied math?
&lt;/h2&gt;&lt;p&gt;Even in data science jobs, a lot of the work is far removed from interesting
math. My hypothesis is that relatively few people get to spend a substantial
part of their job thinking about interesting math. I&amp;rsquo;ve
&lt;a class="link" href="https://github.com/datamicroscopes/lda/graphs/contributors" target="_blank" rel="noopener"
&gt;almost&lt;/a&gt; never
gotten to spend as much time doing math as &lt;a class="link" href="https://twitter.com/tdhopper/status/684380622639333376" target="_blank" rel="noopener"
&gt;I would
like&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="what-skills-would-you-recommend-i-develop-if-i-hope-to-become-a-data-scientist"&gt;What skills would you recommend I develop if I hope to become a data scientist?
&lt;/h2&gt;&lt;p&gt;You can never be a good enough writer, communicator, software engineer, linear
algebraist, or applied statistician. Tenacity is important too, though I&amp;rsquo;m not
sure how you develop it.&lt;/p&gt;
&lt;h2 id="am-i-doing-the-right-things-to-build-a-successful-career-in-data-science"&gt;Am I doing the right things to build a successful career in data science?
&lt;/h2&gt;&lt;p&gt;I don&amp;rsquo;t know the answer to that question. I have tried to share the things
that have been valuable for me in the preceding answer and &lt;a class="link" href="https://tdhopper.com/blog/how-i-became-a-data-scientist/" &gt;in my blog
post&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="im-in-a-career-as-a-teacherdeveloperanalystetc-can-you-advise-me-on-how-i-can-transition-to-be-a-data-scientist"&gt;I&amp;rsquo;m in a career as a teacher/developer/analyst/etc. Can you advise me on how I can transition to be a data scientist?
&lt;/h2&gt;&lt;p&gt;I have tried &lt;a class="link" href="https://tdhopper.com/blog/how-i-became-a-data-scientist/" &gt;to share the things that worked for
me&lt;/a&gt;; you might be
able to emulate them, but I can&amp;rsquo;t guarantee they&amp;rsquo;ll work for you. I would
encourage you to stay curious, keep learning, network (via the internet and
face to face), and keep applying for jobs.&lt;/p&gt;
&lt;h2 id="can-we-find-a-time-to-talk-on-the-phone-about-this"&gt;Can we find a time to talk on the phone about this?
&lt;/h2&gt;&lt;p&gt;Unfortunately, I don&amp;rsquo;t have the time and energy to do this.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Thanks to &lt;a class="link" href="https://mobile.twitter.com/roycoding" target="_blank" rel="noopener"
&gt;Roy Keyes&lt;/a&gt;, &lt;a class="link" href="http://www.vickiboykis.com" target="_blank" rel="noopener"
&gt;Vicki Boykis&lt;/a&gt;, and &lt;a class="link" href="https://twitter.com/databozo" target="_blank" rel="noopener"
&gt;Justin Bozonier&lt;/a&gt; for helpful feedback on a draft of
this post.&lt;/p&gt;</description></item><item><title>Optimism is essential to scientific success</title><link>https://tdhopper.com/blog/optimism/</link><pubDate>Tue, 13 Feb 2018 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/optimism/</guid><description>&lt;blockquote&gt;
&lt;p&gt;I have always believed that scientific research is another domain where a form of optimism is essential to success: I have yet to meet a successful scientist who lacks the ability to exaggerate the importance of what he or she is doing, and I believe that someone who lacks a delusional sense of significance will wilt in the face of repeated experiences of multiple small failures and rare successes, the fate of most researchers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;— Daniel Kahneman, [Thinking, Fast and Slow](Thinking Fast and Slow)&lt;/p&gt;</description></item><item><title>dothopper photography</title><link>https://tdhopper.com/blog/dothopper-photography/</link><pubDate>Fri, 09 Feb 2018 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/dothopper-photography/</guid><description>&lt;img src="https://tdhopper.com/images/camera.png" alt="Featured image of post dothopper photography" /&gt;&lt;p&gt;&lt;a class="link" href="https://photos.tdhopper.com" target="_blank" rel="noopener"
&gt;I created a new website&lt;/a&gt; devoted to my photography.&lt;/p&gt;
&lt;p&gt;I named it &lt;em&gt;dothopper photography&lt;/em&gt; in honor of my late grandmother, Dot Hopper, who fell in love with painting watercolors in retirement.&lt;/p&gt;</description></item><item><title>John Cook’s Ten Year Blogging Endeavour</title><link>https://tdhopper.com/blog/ten-year-endeavor/</link><pubDate>Wed, 10 Jan 2018 01:28:00 +0000</pubDate><guid>https://tdhopper.com/blog/ten-year-endeavor/</guid><description>&lt;p&gt;Ten years ago today, &lt;a class="link" href="https://www.johndcook.com" target="_blank" rel="noopener"
&gt;John Cook&lt;/a&gt; published his
first blog post entitled &lt;a class="link" href="https://www.johndcook.com/blog/2008/01/09/moores-law-and-software-bloat/" target="_blank" rel="noopener"
&gt;Moore’s law and software bloat&lt;/a&gt;, a brief observation on how &amp;ldquo;Software bloat has increased at roughly
the same rate as Moore’s law&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Since then, he’s written over 2,700 posts (nearly 1 per day) on math,
computing, software development, statistics, science, and more. His posts are
rarely long, but they always give me something to think about. Over the last
six years since I discovered his blog, John has encouraged me to&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2011/11/30/dead-authors/" target="_blank" rel="noopener"
&gt;engage with dead writers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2010/11/24/fairy-dust-on-the-diploma/" target="_blank" rel="noopener"
&gt;not look to diplomas for magic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2012/06/13/matrix-condition-number/" target="_blank" rel="noopener"
&gt;take care in inverting matrices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2013/08/20/why-are-differentiable-complex-functions-infinitely-differentiable/" target="_blank" rel="noopener"
&gt;cherish analytic functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2009/11/07/creativity-and-criticism/" target="_blank" rel="noopener"
&gt;be slow to say &amp;ldquo;they has too much time on their hands”&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2011/02/28/programmers-without-computers/" target="_blank" rel="noopener"
&gt;second guess the need to always be in front of a screen to be a programmer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2011/09/27/bayesian-amazon/" target="_blank" rel="noopener"
&gt;use Bayesian methods to better order search results of customer-rated items&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2011/11/21/career-advice-regarding-tools/" target="_blank" rel="noopener"
&gt;consider the benefits of portable, open-source tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2009/02/18/the-data-may-not-contain-the-answer/" target="_blank" rel="noopener"
&gt;have humility when analyzing data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2013/01/12/elementary-statistics-book/" target="_blank" rel="noopener"
&gt;be skeptical of “elementary” statistical books&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2008/02/23/everything-begins-with-p/" target="_blank" rel="noopener"
&gt;not feel dumb for being frustrated with statistics notation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2009/11/10/oftware-archeology/" target="_blank" rel="noopener"
&gt;think carefully about how &lt;em&gt;read&lt;/em&gt; code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2009/10/08/nasa-buggy-software/" target="_blank" rel="noopener"
&gt;reevaluate what it would take for software to be bug free&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2015/06/18/most-important-skill-in-software/" target="_blank" rel="noopener"
&gt;appreciate the massive importance of organization in software projects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2016/05/15/bring-out-your-equations/" target="_blank" rel="noopener"
&gt;understand the real challenges of effectively applying mathematics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2012/08/27/work-or-rest/" target="_blank" rel="noopener"
&gt;wonder if time-spent-at-desk is a good proxy for getting work done&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2009/06/04/software-challenges/" target="_blank" rel="noopener"
&gt;better understand why software projects fail&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2009/12/23/why-programmers-are-not-paid-in-proportion-to-their-productivity/" target="_blank" rel="noopener"
&gt;realize that it’s sometimes better not to produce more code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.johndcook.com/blog/2015/12/22/automate-to-save-mental-energy-not-time/" target="_blank" rel="noopener"
&gt;realize automating might help me more by saving &lt;em&gt;mental energy&lt;/em&gt; than by saving &lt;em&gt;time&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;John earned his Ph.D. in applied math at UT Austin in 1992 with a dissertation
on partial differential equations entitled &lt;a class="link" href="http://catalog.lib.utexas.edu/search~S2?/acook/acook/1%2C109%2C119%2CB/frameset&amp;amp;FF=acook&amp;#43;john&amp;#43;douglas&amp;#43;&amp;#43;&amp;#43;&amp;#43;&amp;#43;1966&amp;amp;1%2C1%2C" target="_blank" rel="noopener"
&gt;Diffusion models with
microstructure and secondary
flux&lt;/a&gt;.
In the years since, he has worked as a math professor at Vanderbilt, a
software developer, a research statistician at M.D. Anderson Cancer Center,
and, since 2013, an independent consultant in statistics, mathematics, and
&lt;a class="link" href="https://www.johndcook.com/blog/expert-hipaa-deidentification/" target="_blank" rel="noopener"
&gt;HIPAA expert determination&lt;/a&gt;. His breadth of career experience reflect his breadth of
interests that make his blog what it is.&lt;/p&gt;
&lt;p&gt;Though John has (wisely) avoided labeling himself a data scientist (preferring
&lt;a class="link" href="https://www.johndcook.com/veryappliedmath.html" target="_blank" rel="noopener"
&gt;very applied mathematician&lt;/a&gt;),
his writings have shaped every aspect of my trajectory from being a operations
research grad student to a practicing data scientist. John is an &lt;a class="link" href="https://www.johndcook.com/blog/bayesian-consulting/" target="_blank" rel="noopener"
&gt;expert in
Bayesian statistics&lt;/a&gt;, yet
has &lt;a class="link" href="https://www.johndcook.com/blog/2018/01/01/making-sense-of-a-probability-problem-in-the-wsj/" target="_blank" rel="noopener"
&gt;taught me much humility&lt;/a&gt; about the power of statistics. John is a&lt;a class="link" href="https://www.johndcook.com/blog/stand_alone_code/" target="_blank" rel="noopener"
&gt;
expert programmer&lt;/a&gt;, yet
&lt;a class="link" href="https://www.johndcook.com/blog/2014/10/02/the-mistakes-of-others/" target="_blank" rel="noopener"
&gt;points me back to the importance of soft
skills&lt;/a&gt; for
the success of software projects. John &lt;a class="link" href="https://www.johndcook.com/blog/2017/12/12/efficiency-is-not-associative-for-matrix-multiplication/" target="_blank" rel="noopener"
&gt;loves applications of mathematics&lt;/a&gt;, yet reminds me to step back and see
the &lt;a class="link" href="https://www.johndcook.com/blog/2018/01/04/new-prime-number-record-50th-mersenne-prime/" target="_blank" rel="noopener"
&gt;intrinsic beauty of mathematics&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The most common tags on his posts are
&lt;a class="link" href="https://www.johndcook.com/blog/tag/bayesian/" target="_blank" rel="noopener"
&gt;bayesian&lt;/a&gt;,
&lt;a class="link" href="https://www.johndcook.com/blog/tag/creativity/" target="_blank" rel="noopener"
&gt;creativity&lt;/a&gt;, &lt;a class="link" href="https://www.johndcook.com/blog/tag/differential-equations/" target="_blank" rel="noopener"
&gt;differential
equations&lt;/a&gt;,
&lt;a class="link" href="https://www.johndcook.com/blog/tag/education/" target="_blank" rel="noopener"
&gt;education&lt;/a&gt;,
&lt;a class="link" href="https://www.johndcook.com/blog/tag/history/" target="_blank" rel="noopener"
&gt;history&lt;/a&gt;,
&lt;a class="link" href="https://www.johndcook.com/blog/tag/latex/" target="_blank" rel="noopener"
&gt;latex&lt;/a&gt;,
&lt;a class="link" href="https://www.johndcook.com/blog/tag/math/" target="_blank" rel="noopener"
&gt;math&lt;/a&gt;,
&lt;a class="link" href="https://www.johndcook.com/blog/tag/networks/" target="_blank" rel="noopener"
&gt;networks&lt;/a&gt;, &lt;a class="link" href="https://www.johndcook.com/blog/tag/number-theory/" target="_blank" rel="noopener"
&gt;number
theory&lt;/a&gt;, &lt;a class="link" href="https://www.johndcook.com/blog/tag/probability-and-statistics/" target="_blank" rel="noopener"
&gt;probability and
statistics&lt;/a&gt;,
&lt;a class="link" href="https://www.johndcook.com/blog/tag/productivity/" target="_blank" rel="noopener"
&gt;productivity&lt;/a&gt;,
&lt;a class="link" href="https://www.johndcook.com/blog/tag/programming/" target="_blank" rel="noopener"
&gt;programming&lt;/a&gt;,
&lt;a class="link" href="https://www.johndcook.com/blog/tag/python/" target="_blank" rel="noopener"
&gt;python&lt;/a&gt;,
&lt;a class="link" href="https://www.johndcook.com/blog/tag/quotes/" target="_blank" rel="noopener"
&gt;quotes&lt;/a&gt;,
&lt;a class="link" href="https://www.johndcook.com/blog/tag/scipy/" target="_blank" rel="noopener"
&gt;scipy&lt;/a&gt;, and &lt;a class="link" href="https://www.johndcook.com/blog/tag/special-functions/" target="_blank" rel="noopener"
&gt;special
functions&lt;/a&gt;. He also
brings a unique approach in his posts on
&lt;a class="link" href="https://www.johndcook.com/blog/tag/music/" target="_blank" rel="noopener"
&gt;music&lt;/a&gt; and has a neat collection
of &lt;a class="link" href="https://www.johndcook.com/blog/tag/interview/" target="_blank" rel="noopener"
&gt;interviews&lt;/a&gt; he&amp;rsquo;s done with
&lt;a class="link" href="https://www.johndcook.com/blog/2010/05/26/fred-brooks-interview/" target="_blank" rel="noopener"
&gt;Fred Brooks&lt;/a&gt;, &lt;a class="link" href="https://www.johndcook.com/blog/2013/09/24/interview-with-sir-michael-atiyah/" target="_blank" rel="noopener"
&gt;Michael Atiyah&lt;/a&gt; and others.&lt;/p&gt;
&lt;p&gt;John’s blog is not his only educational outreach. He maintains &lt;a class="link" href="https://www.johndcook.com/blog/twitter_page/" target="_blank" rel="noopener"
&gt;17 (!) Twitter
accounts&lt;/a&gt; where he teaches about
technical topics in &lt;del&gt;140&lt;/del&gt; 280 characters or less. I&amp;rsquo;m always learning little
tidbits from &lt;a class="link" href="https://twitter.com/UnixToolTip" target="_blank" rel="noopener"
&gt;@UnixToolTip&lt;/a&gt; and find my
schooling refreshed by &lt;a class="link" href="https://twitter.com/AlgebraFact" target="_blank" rel="noopener"
&gt;@AlgebraFact&lt;/a&gt; and
&lt;a class="link" href="https://twitter.com/AnalysisFact" target="_blank" rel="noopener"
&gt;@AnalysisFact&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One of John’s areas of expertise is implementing numerical algorithms in code.
On his website, he has a &lt;a class="link" href="https://www.johndcook.com/blog/notes/" target="_blank" rel="noopener"
&gt;handful of technical
notes&lt;/a&gt; on this that many people have
found invaluable. Two years ago, I did &lt;a class="link" href="https://tdhopper.com/blog/mentions-of-john-cook-on-github/" &gt;a quick
survey&lt;/a&gt; of John’s
articles mentioned in Github repos. Hundreds of repos cited his &lt;a class="link" href="https://www.johndcook.com/blog/standard_deviation/" target="_blank" rel="noopener"
&gt;Accurately
computing running
variance&lt;/a&gt; article. No
doubt countless more have used his resources without citing him!&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t follow John&amp;rsquo;s blog, you should &lt;a class="link" href="https://www.johndcook.com/blog/ways-to-subscribe/" target="_blank" rel="noopener"
&gt;subscribe
today&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s to 10 more years, John!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Congrats to &lt;a class="link" href="https://twitter.com/JohnDCook?ref_src=twsrc%5Etfw" target="_blank" rel="noopener"
&gt;@JohnDCook&lt;/a&gt;
on 10 years of blogging!&lt;a class="link" href="https://t.co/DdMBjI89eV" target="_blank" rel="noopener"
&gt;https://t.co/DdMBjI89eV&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;— Tim Hopper 🆘 (@tdhopper) &lt;a class="link" href="https://twitter.com/tdhopper/status/950556877246615552?ref_src=twsrc%5Etfw" target="_blank" rel="noopener"
&gt;January 9,
2018&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>An Infosec Limerick</title><link>https://tdhopper.com/blog/an-infosec-limerick/</link><pubDate>Thu, 21 Dec 2017 12:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/an-infosec-limerick/</guid><description>&lt;img src="https://tdhopper.com/images/leakybucket.png" alt="Featured image of post An Infosec Limerick" /&gt;&lt;p&gt;There once was a man from Nantucket&lt;br&gt;
Who was careful with his data, where he stuck it&lt;br&gt;
His passwords always impress&lt;br&gt;
He checked for HTTPS&lt;br&gt;
But somebody still put his PII in a public S3 bucket&lt;/p&gt;</description></item><item><title>Neal Ford on Companies Ignoring Developer Costs</title><link>https://tdhopper.com/blog/neal-ford-on-companies-ignoring-developer-costs/</link><pubDate>Mon, 16 Oct 2017 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/neal-ford-on-companies-ignoring-developer-costs/</guid><description>&lt;blockquote&gt;
&lt;p&gt;The downside of this is you have to have two production stacks&amp;hellip; which is kind of expensive, but it&amp;rsquo;s dirt cheap compared to amount of money people spend on developers hacking away at things that are making them crazy. This is one of those things that drives me crazy about companies&amp;hellip; We will gladly spend a million dollars in overtime in developers to sit and work on our antiquated infrastructure, but we won&amp;rsquo;t spend a thousand dollars to buy a new machine to make them not have to do do that.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;— Neal Ford, &lt;a class="link" href="http://shop.oreilly.com/product/0636920020271.do" target="_blank" rel="noopener"
&gt;&amp;ldquo;Automation&amp;rdquo;, &lt;em&gt;Neal Ford on Agile Engineering Practices&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;</description></item><item><title>How I Became a Data Scientist Despite Having Been a Math Major: The Talk</title><link>https://tdhopper.com/talks/how-i-became-a-data-scientist-the-talk/</link><pubDate>Fri, 06 Oct 2017 00:00:00 +0000</pubDate><guid>https://tdhopper.com/talks/how-i-became-a-data-scientist-the-talk/</guid><description>&lt;img src="https://tdhopper.com/preso.png" alt="Featured image of post How I Became a Data Scientist Despite Having Been a Math Major: The Talk" /&gt;&lt;p&gt;A few years ago, I wrote a posted called &lt;a class="link" href="https://tdhopper.com/how/" &gt;How I Became a Data Scientist Despite Having Been a Math Major&lt;/a&gt;. I recently gave a talk on the same topic at the &lt;a class="link" href="https://www.thisismetis.com/demystifying-data-science" target="_blank" rel="noopener"
&gt;Demystify Data Science Conference&lt;/a&gt;.&lt;/p&gt;
&lt;div class="video-wrapper"&gt;
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/Q_UrZ4pmKmI"
allowfullscreen
title="YouTube Video"
&gt;
&lt;/iframe&gt;
&lt;/div&gt;</description></item><item><title>Zen of Git</title><link>https://tdhopper.com/blog/zen-of-git/</link><pubDate>Sat, 30 Sep 2017 00:26:00 +0000</pubDate><guid>https://tdhopper.com/blog/zen-of-git/</guid><description>&lt;img src="https://tdhopper.com/images/zen.png" alt="Featured image of post Zen of Git" /&gt;&lt;pre&gt;&lt;code&gt;$ git checkout this
Zen of Git
Ugly is better than beautiful.
Explicit is better than implicit.
Complex is better than simple.
Complicated is better than complex.
Flat is better than nested.
Readability is meaningless.
Special cases are everything.
Errors should never be comprehensible.
In the face of ambiguity, copy and paste from Stack Overflow.
There should be no obvious way to do it.
Although there may be endless non obvious ways to do it.
If the documentation is hard to understand, it's a great idea.
If the documentation is easy to understand, it's probably for another tool.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(With apologies to &lt;a class="link" href="https://www.python.org/dev/peps/pep-0020/" target="_blank" rel="noopener"
&gt;Tim Peters&lt;/a&gt;.)&lt;/p&gt;</description></item><item><title>Python Plotting for Exploratory Data Analysis</title><link>https://tdhopper.com/blog/python-plotting-for-exploratory-data-analysis/</link><pubDate>Mon, 26 Jun 2017 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/python-plotting-for-exploratory-data-analysis/</guid><description>&lt;p&gt;Plotting is an essential component of data analysis. As a data scientist, I spend a significant amount of my time making simple plots to understand complex data sets (exploratory data analysis) and help others understand them (presentations).&lt;/p&gt;
&lt;p&gt;In particular, I make a lot of bar charts (including histograms), line plots (including time series), scatter plots, and density plots from data in Pandas data frames. I often want to facet these on various categorical variables and layer them on a common grid.&lt;/p&gt;
&lt;p&gt;To that end, I made &lt;a class="link" href="http://pythonplot.com/" target="_blank" rel="noopener"
&gt;pythonplot.com&lt;/a&gt;, a brief introduction to Python plotting libraries and a &amp;ldquo;rosetta stone&amp;rdquo; comparing how to use them. I also included comparison to &lt;a class="link" href="https://en.wikipedia.org/wiki/Ggplot2" title="Wikipedia Entry: ggplot2 - Wikipedia"
target="_blank" rel="noopener"
&gt;ggplot2&lt;/a&gt;, the R plotting library that I and many others consider a gold standard.&lt;/p&gt;</description></item><item><title>Parallelizing a Python Function for the Extremely Lazy</title><link>https://tdhopper.com/blog/parallelizing-a-python-function-for-the-extremely-lazy/</link><pubDate>Wed, 07 Jun 2017 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/parallelizing-a-python-function-for-the-extremely-lazy/</guid><description>&lt;p&gt;Do you ever want to be able to run a Python function in parallel on a set of inputs? Have you ever gotten frustrated with the GIL, the multiprocessing library, or joblib?&lt;/p&gt;
&lt;p&gt;Try this:&lt;/p&gt;
&lt;h2 id="install-python-fire-to-run-your-command-from-the-command-line"&gt;Install Python Fire to run your command from the command line
&lt;/h2&gt;&lt;p&gt;Install &lt;a class="link" href="https://github.com/google/python-fire" target="_blank" rel="noopener"
&gt;Python Fire&lt;/a&gt; with &lt;code&gt;$ pip install fire&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Add this snippet to the bottom of your file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;if __name__ == &amp;#39;__main__&amp;#39;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; import fire
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; fire.Fire()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="install-gnu-parallel"&gt;Install GNU Parallel
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;$ brew install parallel&lt;/code&gt; or &lt;code&gt;$ sudo apt-get install parallel&lt;/code&gt; may work for you. Otherwise, see &lt;a class="link" href="https://www.gnu.org/software/parallel/" target="_blank" rel="noopener"
&gt;this&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="run-your-function-from-the-command-line"&gt;Run your function from the command line
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;$ parallel -j3 &amp;quot;python python_file.py function_name {1} &amp;quot; ::: input1 input2 input3 input4 input5&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;parallel&lt;/code&gt; is the command for GNU Parallel.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-j3&lt;/code&gt; tells Parallel to run at most 3 processes at once.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{1}&lt;/code&gt; fills in each item after the &lt;code&gt;:::&lt;/code&gt; as an argument to the &lt;code&gt;function_name&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="for-example"&gt;For example
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(lazy) ~ $ cat python_file.py
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;from time import sleep
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;def function_name(arg1):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; print(&amp;#34;Starting to run with&amp;#34;, arg1)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sleep(2)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; print(&amp;#34;Finishing to run with&amp;#34;, arg1)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;if __name__ == &amp;#39;__main__&amp;#39;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; import fire
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; fire.Fire()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(lazy) ~ $ parallel -j3 --lb &amp;#34;python -u python_file.py function_name {1} &amp;#34; ::: input1 input2 input3 input4 input5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Starting to run with input2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Starting to run with input1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Starting to run with input3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Finishing to run with input2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Finishing to run with input1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Finishing to run with input3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Starting to run with input4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Starting to run with input5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Finishing to run with input4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Finishing to run with input5
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;I added &lt;code&gt;--lb&lt;/code&gt; and &lt;code&gt;-u&lt;/code&gt; to keep Python and Parallel from buffering the output so you can see it being run in parallel.&lt;/p&gt;</description></item><item><title>CondaHTTPError: HTTP 401 UNAUTHORIZED for url</title><link>https://tdhopper.com/blog/condahttperror-http-401-unauthorized-for-url/</link><pubDate>Tue, 06 Jun 2017 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/condahttperror-http-401-unauthorized-for-url/</guid><description>&lt;p&gt;I was getting this message when I tried to install packages from &lt;a class="link" href="https://conda-forge.github.io/" title="conda-forge | community driven packaging for conda"
target="_blank" rel="noopener"
&gt;conda-forge&lt;/a&gt; with &lt;a class="link" href="https://conda.io/docs/intro.html" title="Intro to conda &amp;amp;mdash; Conda documentation"
target="_blank" rel="noopener"
&gt;Conda&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Fetching&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;CondaHTTPError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HTTP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;UNAUTHORIZED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="cp"&gt;//conda.anaconda.org/conda-forge/osx-64/repodata.json&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Elapsed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;00.920954&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;CF&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;RAY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="nx"&gt;ad7cbd5d1c23d8&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;IAD&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;remote&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;has&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;indicated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;invalid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;remote&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;anaconda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;org&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;follows&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Anaconda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;API&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;need&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;invalid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;system&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`anaconda logout`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;optionally&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;followed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;collecting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`anaconda login`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;or&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;provide&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;conda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;directly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Further&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;configuration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;help&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="cp"&gt;//conda.io/docs/config.html&amp;gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;I tried to do &lt;code&gt;$ anaconda logout&lt;/code&gt; but didn&amp;rsquo;t have a program called &lt;code&gt;anaconda&lt;/code&gt; installed.&lt;/p&gt;
&lt;p&gt;You can install the &lt;a class="link" href="https://docs.continuum.io/anaconda-cloud/user-guide/getting-started#cloud-install-client" target="_blank" rel="noopener"
&gt;Anaconda Cloud Client&lt;/a&gt; with &lt;code&gt;$ conda install anaconda-client&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After that, I was able to do &lt;code&gt;$ anaconda logout&lt;/code&gt; followed by &lt;code&gt;$ anaconda login&lt;/code&gt; where I used my old Binstar credentials (now anaconda.org).&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/conda-forge/ulmo-feedstock/issues/5" target="_blank" rel="noopener"
&gt;I&amp;rsquo;m not the only one having this problem.&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Choice of the Name Dynamic Programming</title><link>https://tdhopper.com/blog/choice-of-the-name-dynamic-programming/</link><pubDate>Fri, 26 May 2017 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/choice-of-the-name-dynamic-programming/</guid><description>&lt;p&gt;&lt;a class="link" href="http://smo.sogang.ac.kr/doc/dy_birth.pdf" target="_blank" rel="noopener"
&gt;Richard Bellman quoted by Stuart Dreyfus&lt;/a&gt; via &lt;a class="link" href="http://economics.gmu.edu/people/gjonesb" target="_blank" rel="noopener"
&gt;Garrett Jones&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I spent the Fall quarter (of 1950) at RAND. My first task was to find a name for multistage decision processes. “An interesting question is, ‘Where did the name, dynamic programming, come from?’ The 1950s were not good years for mathematical research.&lt;/p&gt;
&lt;p&gt;We had a very interesting gentleman in Washington named Wilson. He was Secretary of Defense, and he actually had a pathological fear and hatred of the word, research. I’m not using the term lightly; I’m using it precisely. His face would suffuse, he would turn red, and he would get violent if people used the term, research, in his presence. You can imagine how he felt, then, about the term, mathematical.&lt;/p&gt;
&lt;p&gt;The RAND Corporation was employed by the Air Force, and the Air Force had Wilson as its boss, essentially. Hence, I felt I had to do something to shield Wilson and the Air Force from the fact that I was really doing mathematics inside the RAND Corporation. What title, what name, could I choose? In the first place I was interested in planning, in decision making, in thinking. But planning, is not a good word for various reasons.&lt;/p&gt;
&lt;p&gt;I decided therefore to use the word, ‘programming.’ I wanted to get across the idea that this was dynamic, this was multistage, this was time-varying—I thought, let’s kill two birds with one stone. Let’s take a word that has an absolutely precise meaning, namely dynamic, in the classical physical sense. It also has a very interesting property as an adjective, and that is it’s impossible to use the word, dynamic, in a pejorative sense. Try thinking of some combination that will possibly give it a pejorative meaning. It’s impossible.
Thus, I thought dynamic programming was a good name. It was something not even a Congressman could object to. So I used it as an umbrella for my activities.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Adversarial Learning: Stories of Degradation and Humiliation</title><link>https://tdhopper.com/blog/stories-of-degradation-and-humiliation/</link><pubDate>Mon, 22 May 2017 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/stories-of-degradation-and-humiliation/</guid><description>&lt;img src="https://tdhopper.com/images/interview.png" alt="Featured image of post Adversarial Learning: Stories of Degradation and Humiliation" /&gt;&lt;p&gt;My friends &lt;a class="link" href="http://www.twitter.com/akm" target="_blank" rel="noopener"
&gt;Andrew&lt;/a&gt; and &lt;a class="link" href="http://www.twitter.com/joelgrus" target="_blank" rel="noopener"
&gt;Joel&lt;/a&gt; were kind enough to have me back on their podcast &lt;a class="link" href="http://adversariallearning.com/episode-10-stories-of-degradation-and-humiliation.html" target="_blank" rel="noopener"
&gt;Adversarial Learning&lt;/a&gt;. We shared our tales of bad data science interviews. Enjoy!&lt;/p&gt;
&lt;iframe style="border: none" src="//html5-player.libsyn.com/embed/episode/id/5378015/height/90/width/640/theme/custom/autonext/no/thumbnail/yes/autoplay/no/preload/no/no_addthis/no/direction/backward/render-playlist/no/custom-color/87A93A/" height="90" width="640" scrolling="no" allowfullscreen webkitallowfullscreen mozallowfullscreen oallowfullscreen msallowfullscreen&gt;&lt;/iframe&gt;</description></item><item><title>Build a Real Time Machine Learning System</title><link>https://tdhopper.com/talks/realtime-machine-learning/</link><pubDate>Wed, 17 May 2017 00:00:00 +0000</pubDate><guid>https://tdhopper.com/talks/realtime-machine-learning/</guid><description>&lt;img src="https://tdhopper.com/preso.png" alt="Featured image of post Build a Real Time Machine Learning System" /&gt;&lt;p&gt;I gave a talk at the Data Science Conference on on building a realtime machine learning system with Kafka, Streamparse, and Storm. You can see the video on &lt;a class="link" href="https://www.youtube.com/watch?v=inB33qWugMw" target="_blank" rel="noopener"
&gt;Youtube&lt;/a&gt;&lt;/p&gt;
&lt;div class="video-wrapper"&gt;
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/inB33qWugMw"
allowfullscreen
title="YouTube Video"
&gt;
&lt;/iframe&gt;
&lt;/div&gt;</description></item><item><title>Like most great mathematicians, he expects universal precision</title><link>https://tdhopper.com/blog/like-most-great-mathematicians-he-expects-universal-precision/</link><pubDate>Wed, 26 Apr 2017 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/like-most-great-mathematicians-he-expects-universal-precision/</guid><description>&lt;p&gt;From the &lt;a class="link" href="http://amzn.to/2qe0hRp" target="_blank" rel="noopener"
&gt;Autobiography of Benjamin Franklin&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a class="link" href="https://en.wikipedia.org/wiki/Thomas_Godfrey_%28inventor%29" target="_blank" rel="noopener"
&gt;Thomas Godfrey&lt;/a&gt;, a self-taught mathematician, great in his way, and afterward inventor of what is now called Hadley&amp;rsquo;s Quadrant. But he knew little out of his way, and was not a pleasing companion; as, like most great mathematicians I have met with, he expected universal precision in every-thing said, or was for ever denying or distinguishing upon trifles, to the disturbance of all conversation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;m a recovering Godfrey Precisionist.&lt;/p&gt;</description></item><item><title>Metawork is more interesting than work</title><link>https://tdhopper.com/blog/metawork-is-more-interesting-than-work/</link><pubDate>Fri, 14 Apr 2017 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/metawork-is-more-interesting-than-work/</guid><description>&lt;p&gt;&lt;a class="link" href="http://www.se-radio.net/2017/04/se-radio-episode-287-success-skills-for-architects-with-neil-ford/" target="_blank" rel="noopener"
&gt;This Software Engineering Radio interview&lt;/a&gt; with Neal Ford on Success Skills for Architects is full of gems about building effective software.&lt;/p&gt;
&lt;p&gt;He talks a lot about how coders love to solve problems, and that love can lead them to invent interesting, but unnecessary, problems to solve. This is true.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Metawork is more interesting than work. It&amp;rsquo;s so hard to get back to simplicity, because we love complicated little puzzles to solve, so we keep overengineering everything.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Anyone who&amp;rsquo;s developing software would benefit from listening.&lt;/p&gt;</description></item><item><title>Towards Reducing Distractions while Working</title><link>https://tdhopper.com/blog/towards-reducing-distractions-while-working/</link><pubDate>Thu, 13 Apr 2017 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/towards-reducing-distractions-while-working/</guid><description>&lt;p&gt;Staying focused while working in front of a computer and within reach of a smartphone is hard.&lt;/p&gt;
&lt;p&gt;In 2017, teaching people to focus is becoming a &lt;a class="link" href="https://thefocuscourse.com/" target="_blank" rel="noopener"
&gt;industry&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been trying to rethink distractions in my own life, particularly in my work environment. Here are some things that have helped:&lt;/p&gt;
&lt;h2 id="working-from-home"&gt;Working from Home
&lt;/h2&gt;&lt;p&gt;Working in an office, especially an open-floor plan office, is disastrous for staying focused. DeMarco and Lister wrote about this in &lt;a class="link" href="http://amzn.to/2px5PGj" target="_blank" rel="noopener"
&gt;Peopleware&lt;/a&gt; 30 years ago, and yet open offices are the norm for startups today.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m much more productive by working from home in my quiet office or on my back patio. I&amp;rsquo;m finally able to spend my time thinking about hard problems rather than ways of silencing Constant Throat Clearer or Perpetual Annoying Laugher.&lt;/p&gt;
&lt;h2 id="notifications"&gt;Notifications
&lt;/h2&gt;&lt;p&gt;Every app and website these days wants to send you notifications. I&amp;rsquo;m aggressive about &lt;a class="link" href="http://www.makeuseof.com/tag/take-control-ios-notifications-iphone-ipad/" target="_blank" rel="noopener"
&gt;reducing notifications&lt;/a&gt; down to those that I &lt;em&gt;need&lt;/em&gt; see, and I let almost nothing notify me with sound. I use Do Not Disturb mode on my phone and &lt;a class="link" href="https://support.apple.com/kb/PH18740?locale=en_US" target="_blank" rel="noopener"
&gt;Mac&lt;/a&gt; whenever I need to stop notifications altogether.&lt;/p&gt;
&lt;h2 id="slack"&gt;Slack
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://slack.com/" target="_blank" rel="noopener"
&gt;Slack&lt;/a&gt; has become the new normal for company communication. Some would say Slack itself is ruining our focus, but having it regularly available has been essential for my own work.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve come up with a few ways to take control of Slack:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Only show &amp;ldquo;My unread, along with everything I&amp;rsquo;ve starred&amp;rdquo; in the sidebar. See Michael Lopp&amp;rsquo;s &lt;a class="link" href="http://randsinrepose.com/archives/how-i-slack/" target="_blank" rel="noopener"
&gt;excellent post on Slack&lt;/a&gt; for more here.&lt;/li&gt;
&lt;li&gt;Enable notifications selectively.&lt;/li&gt;
&lt;li&gt;Sign out of distracting avocational Slacks.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="social-media"&gt;Social Media
&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;ve started using an app called &lt;a class="link" href="https://heyfocus.com/" target="_blank" rel="noopener"
&gt;Focus&lt;/a&gt; to block distracting websites (including Facebook and Twitter.com) and apps on my work computer from 9 AM to 5:30 PM. I use Focus&amp;rsquo;s scheduling feature so blocking isn&amp;rsquo;t optional for me.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve decided &lt;em&gt;not&lt;/em&gt; to block &lt;a class="link" href="https://tapbots.com/tweetbot/" target="_blank" rel="noopener"
&gt;Tweetbot&lt;/a&gt;. Though it can be distracting, Twitter is an invaluable way for me to learn from my professional colleagues, bounce ideas off of them, and have a good laugh.&lt;/p&gt;
&lt;p&gt;On my iPhone, iPad, and personal Laptop, I&amp;rsquo;ve started using &lt;a class="link" href="https://freedom.refersion.com/c/c6bad" target="_blank" rel="noopener"
&gt;Freedom&lt;/a&gt; to block all social media during the day. This has stopped me from instinctively checking Instagram every time I walk to the bathroom or get suck on a hard problem. I highly recommend it.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I also use Freedom to block social media for the first hour I&amp;rsquo;m up in the morning and before I go to bed.&lt;/p&gt;
&lt;h2 id="email"&gt;Email
&lt;/h2&gt;&lt;p&gt;I have two main tactics to keep email from being distracting.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I aggressively unsubscribe from mailing lists and ads.&lt;/li&gt;
&lt;li&gt;I use &lt;a class="link" href="https://sanebox.com/t/49nyh" target="_blank" rel="noopener"
&gt;Sanebox&lt;/a&gt; to filter low priority messages out of my inbox.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When emails only need a brief reply, I tend to write responses as soon as possible. At the moment, I&amp;rsquo;m trying to break people of the expectation that I&amp;rsquo;ll respond quickly. Using services like &lt;a class="link" href="http://www.boomeranggmail.com/" target="_blank" rel="noopener"
&gt;Boomerang&lt;/a&gt; which lets me write emails now and have them sent later helps here.&lt;/p&gt;
&lt;h2 id="reading"&gt;Reading
&lt;/h2&gt;&lt;p&gt;Long-form reading at the computer is terrible for comprehension. As &lt;a class="link" href="http://www.econtalk.org/archives/2016/11/doug_lemov_on_r.html" target="_blank" rel="noopener"
&gt;Doug Lemov has argued&lt;/a&gt;, you have to get away from your computer &lt;strong&gt;and&lt;/strong&gt; other devices to read deeply. I do this by &lt;a class="link" href="http://amzn.to/2oDATHj" target="_blank" rel="noopener"
&gt;printing&lt;/a&gt; articles or reading on my iPad with &lt;a class="link" href="https://freedom.refersion.com/c/c6bad" target="_blank" rel="noopener"
&gt;Freedom blocking enabled&lt;/a&gt;. I take my printouts or iPad and walk away from my desk to read.&lt;/p&gt;
&lt;h2 id="todo-items"&gt;Todo Items
&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;m a firm believer in the &lt;a class="link" href="http://amzn.to/2ovAgit" target="_blank" rel="noopener"
&gt;Getting Things Done&lt;/a&gt; principle of reducing the cognitive overhead of tracking to-do items in my head. I use Omnifocus for task management. &lt;a class="link" href="https://support.omnigroup.com/omnifocus-mail-drop/" target="_blank" rel="noopener"
&gt;Mail Drop&lt;/a&gt; and &lt;a class="link" href="http://bachyaproductions.com/better-omnifocus-task-entry/" target="_blank" rel="noopener"
&gt;this&lt;/a&gt; Alfred workflow help me to quickly add tasks to my Omnifocus inbox. When I think of something I need to take care of outside of work, I drop that thought into Omnifocus; this keeps those personal to-do items from distracting me while I&amp;rsquo;m working.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Staying focused is hard. I&amp;rsquo;m still learning how to do it well, and I&amp;rsquo;m sure I&amp;rsquo;m not the only one struggling to improve here. If you have any tips to share, I&amp;rsquo;d love to hear them!&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I can&amp;rsquo;t use Freedom on my work computer, because it acts as a VPN which conflicts with my work VPN.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Web Development and Design for the Backend Developer</title><link>https://tdhopper.com/blog/web-development-and-design-for-the-backend-developer/</link><pubDate>Fri, 31 Mar 2017 18:42:00 +0000</pubDate><guid>https://tdhopper.com/blog/web-development-and-design-for-the-backend-developer/</guid><description>&lt;p&gt;I&amp;rsquo;ve been tinkering with websites for nearly 20 years. My friend Hunter and I
were big into making terrible Angelfire sites as pre-teens. In high school, my
dad paid me to make him a webpage for his doctor&amp;rsquo;s office (I used Frontpage).
A year or two after that, I read Kevin Yank&amp;rsquo;s &amp;ldquo;Build Your Own Database Driven
Website Using PHP &amp;amp; MySQL&amp;rdquo; and hacked together a PHP back-end for a Lord of
the Rings fan site.&lt;/p&gt;
&lt;p&gt;In recent years, I&amp;rsquo;ve put together this blog,
&lt;a class="link" href="http://shouldigetaphd.com" target="_blank" rel="noopener"
&gt;shouldigetaphd.com&lt;/a&gt;, and a few other simple &lt;a class="link" href="https://shouldigetaphd.com/" target="_blank" rel="noopener"
&gt;web-based&lt;/a&gt; &lt;a class="link" href="https://pythonplot.com" target="_blank" rel="noopener"
&gt;side&lt;/a&gt;
&lt;a class="link" href="https://dp.tdhopper.com" target="_blank" rel="noopener"
&gt;projects&lt;/a&gt;. However, I haven&amp;rsquo;t kept up with modern
web development, and my projects have been hacked together from boilerplate or
templates. I&amp;rsquo;ve programmed professionally since 2011, I&amp;rsquo;ve spent very little
of that writing anything close to graphical user interfaces.&lt;/p&gt;
&lt;p&gt;I have a number of other side projects that I&amp;rsquo;d like to do at some point, and
most of them would require some sort of graphical interface. While I could
work on app development, I think web-based implementations would be a great
starting place.&lt;/p&gt;
&lt;p&gt;A few months back, I decided to stop watching Netflix on the treadmill and
instead use those 45 minutes each morning to learn; in particular, I&amp;rsquo;ve been
trying to learn more about modern(ish) web design and development. My work has
a subscription to &lt;a class="link" href="http://safaribooksonline.com" target="_blank" rel="noopener"
&gt;Safari Books Online&lt;/a&gt; which
gives me access to copious technical books and video tutorials.&lt;/p&gt;
&lt;p&gt;The number of resources available on Safari (along with YouTube, blog posts,
etc) is astounding. I started many video tutorials on Safari that I quickly
realized weren&amp;rsquo;t going to be useful. Yet there many gems to be found, which I
share here with you.&lt;/p&gt;
&lt;p&gt;What follows is an overview of the technologies I&amp;rsquo;ve realized I need to learn
more about and links to the resources I&amp;rsquo;ve found valuable in learning about
them. If you think there are gaps I haven&amp;rsquo;t yet filled or better resources
than I&amp;rsquo;ve listed below, I&amp;rsquo;d love your feedback.&lt;/p&gt;
&lt;h2 id="what-i-knew-going-in"&gt;What I Knew Going In
&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;ve been a professional software developer and data scientist since 2012. I
mostly write Python, but I&amp;rsquo;ve programmed in a number of different languages.&lt;/p&gt;
&lt;p&gt;I have a pretty good grasp on how HTML and CSS work. I&amp;rsquo;ve used enough
Javascript over the years to be dangerous; I understood how it runs in the
browsers. I understand what a DOM is and how it relates to the page source.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve used the &lt;a class="link" href="http://flask.pocoo.org/" target="_blank" rel="noopener"
&gt;Python Flask web framework&lt;/a&gt; for
several projects. I understand how to repond to HTTP requests with server-
generated content. I had some idea of how to run my own web server on AWS.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve used &lt;a class="link" href="https://jekyllrb.com/" target="_blank" rel="noopener"
&gt;Jekyll&lt;/a&gt;, &lt;a class="link" href="http://gohugo.io/" target="_blank" rel="noopener"
&gt;Hugo&lt;/a&gt;, and
&lt;a class="link" href="http://docs.getpelican.com/" target="_blank" rel="noopener"
&gt;Pelican&lt;/a&gt; to create statically generated sites.&lt;/p&gt;
&lt;p&gt;I understood DNS at a high level, but never really learned what all the
different DNS types were, and I didn&amp;rsquo;t understand why name server changes take
so long to propagate.&lt;/p&gt;
&lt;p&gt;I had some idea of what &lt;a class="link" href="https://nodejs.org/en/" title="Node.js"
target="_blank" rel="noopener"
&gt;node.js&lt;/a&gt; and
&lt;a class="link" href="https://www.npmjs.com/" title="npm"
target="_blank" rel="noopener"
&gt;npm&lt;/a&gt; are.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m a committed Sublime Text user.&lt;/p&gt;
&lt;h2 id="a-meta-tutorial-on-web-development"&gt;A Meta Tutorial on Web Development
&lt;/h2&gt;&lt;p&gt;A great place to start is Andrew Montalenti&amp;rsquo;s lengthy tutorial on using
Python, Flask, Bootstrap, and Mongo to rapidly prototype a website. The
tutorial is out of date, but the principles still stand.&lt;/p&gt;
&lt;p&gt;Another great resource is Cody Lindley&amp;rsquo;s free &lt;a class="link" href="https://frontendmasters.com/books/front-end-handbook/" target="_blank" rel="noopener"
&gt;Front-End Developer&amp;rsquo;s
Handbook&lt;/a&gt;. This is a
substantial list meta-resource that organizes links for learning all angles of
front-end development. &amp;ldquo;It is specifically written with the intention of being
a professional resource for potential and currently practicing front-end
developers to equip themselves with learning materials and development tools.&amp;rdquo;&lt;/p&gt;
&lt;h3 id="chrome-developer-tools"&gt;Chrome Developer Tools
&lt;/h3&gt;&lt;p&gt;One of the most important tools for me in learning more about web development
has been the &lt;a class="link" href="https://developer.chrome.com/devtools" target="_blank" rel="noopener"
&gt;Chrome Developer Tools&lt;/a&gt;.
You can live edit the DOM elements and style sheets and watch how a website
changes. I&amp;rsquo;ve mostly learned Developer Tools through exploring it myself, but
there are lots of tutorials for it on Youtube.&lt;/p&gt;
&lt;h3 id="html-css-and-bootstrap"&gt;HTML, CSS, and Bootstrap
&lt;/h3&gt;&lt;p&gt;Many modern websites are
&lt;a class="link" href="https://en.wikipedia.org/wiki/Responsive_web_design" target="_blank" rel="noopener"
&gt;responsive&lt;/a&gt;: they
automatically adapt to various size screens and devices, from phones to
desktops. Writing responsive websites from scratch requires deep knowledge of
HTML, CSS, Javascript, and browsers. Unless you&amp;rsquo;re doing this professionally,
you probably don&amp;rsquo;t want to write a responsive site from scratch.&lt;/p&gt;
&lt;p&gt;For several projects, I&amp;rsquo;ve used the lightweight
&lt;a class="link" href="http://getskeleton.com/" target="_blank" rel="noopener"
&gt;Skeleton&lt;/a&gt; project to create simple, responsive
pages.&lt;/p&gt;
&lt;p&gt;Recently, I decide to dive deep into the more robust &lt;a class="link" href="http://getbootstrap.com/" target="_blank" rel="noopener"
&gt;Bootstrap
framework&lt;/a&gt; originally developed at Twitter.&lt;/p&gt;
&lt;p&gt;I watched Brock Nunn&amp;rsquo;s &lt;a class="link" href="http://shop.oreilly.com/product/110000475.do" target="_blank" rel="noopener"
&gt;Building a Responsive Website with Bootstrap
&lt;/a&gt;
(&lt;a class="link" href="https://www.safaribooksonline.com/library/view/building-a-responsive/9781782164982/" target="_blank" rel="noopener"
&gt;Safari&lt;/a&gt;), a two hour tutorial on getting started with
Bootstrap. The &lt;a class="link" href="http://getbootstrap.com/getting-started/" target="_blank" rel="noopener"
&gt;documentation for Bootstrap&lt;/a&gt; is clear (if terse) and worth reading through.&lt;/p&gt;
&lt;p&gt;Once you have a basic idea of how Bootstrap works, the best thing you can do
is start playing with it. Since I was familiar with the Pelican static site
generator, I decided to switch this blog to Bootstrap theme starting with
&lt;a class="link" href="https://github.com/getpelican/pelican-themes/tree/master/pelican-bootstrap3" target="_blank" rel="noopener"
&gt;pelican-bootstrap3&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve worked with Bootstrap 3 until now. Bootstrap 4 is about to come out.
&lt;a class="link" href="https://v4-alpha.getbootstrap.com/migration/" target="_blank" rel="noopener"
&gt;Bootstrap 4 moves the style sheets from LESS to SASS and adds Flexbox
functionality&lt;/a&gt;. Unless you
understand what those mean (more below), you&amp;rsquo;d be fine using version 3.&lt;/p&gt;
&lt;p&gt;I wanted to get a better grasp on CSS Selectors, so I read Eric Meyer&amp;rsquo;s brief
&lt;a class="link" href="https://www.amazon.com/Selectors-Specificity-Cascade-Applying-Documents/dp/1449342493/ref=as_li_ss_il?ie=UTF8&amp;amp;qid=1490966101&amp;amp;sr=8-3&amp;amp;linkCode=li2&amp;amp;tag=dothopper-20&amp;amp;linkId=260c71eee0317cf531ad099db4c8a816" target="_blank" rel="noopener"
&gt;Selectors, Specificity, and the Cascade: Applying CSS3 to Documents&lt;/a&gt;
(&lt;a class="link" href="https://www.safaribooksonline.com/library/view/selectors-specificity-and/9781449358334/" target="_blank" rel="noopener"
&gt;Safari&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;I watched Marty Hall&amp;rsquo;s &lt;a class="link" href="http://www.coreservlets.com/javascript-jquery-tutorial/" target="_blank" rel="noopener"
&gt;JavaScript, jQuery, and jQuery
UI&lt;/a&gt; tutorial). I was
able to skip big chunks where I already understood certain parts, but it
helped me fill in lots of gaps.&lt;/p&gt;
&lt;h3 id="advanced-stylesheets-less-sass-and-flexbox"&gt;Advanced Stylesheets (LESS, SASS, and Flexbox)
&lt;/h3&gt;&lt;p&gt;There are several alternatives to writing raw CSS. Two popular ones are
&lt;a class="link" href="http://lesscss.org/" target="_blank" rel="noopener"
&gt;Less&lt;/a&gt; and &lt;a class="link" href="http://sass-lang.com/" target="_blank" rel="noopener"
&gt;SASS&lt;/a&gt;. These
&amp;ldquo;preprocessors&amp;rdquo; allow you to write CSS-like stylesheets but with constructs
such as variables, nesting, inheritance, and mathematical operators.&lt;/p&gt;
&lt;p&gt;I found this &lt;a class="link" href="http://shop.oreilly.com/product/0636920051213.do" target="_blank" rel="noopener"
&gt;brief tutorial on
Less&lt;/a&gt;
(&lt;a class="link" href="https://www.safaribooksonline.com/library/view/learning-less/9781783989867/" target="_blank" rel="noopener"
&gt;Safari&lt;/a&gt;) helpful, and I&amp;rsquo;ve enjoyed Less a lot. I haven&amp;rsquo;t used
SASS yet, but it&amp;rsquo;s very similar. I&amp;rsquo;ll probably switch to SASS when I start
using Bootstrap 4.&lt;/p&gt;
&lt;p&gt;Another modern innovation is the &lt;a class="link" href="https://en.wikipedia.org/wiki/CSS_Flex_Box_Layout" target="_blank" rel="noopener"
&gt;Flexbox layout
model&lt;/a&gt; for CSS. Stone River
Learning has a great &lt;a class="link" href="http://stoneriverelearning.com/p/css-flexbox" target="_blank" rel="noopener"
&gt;tutorial&lt;/a&gt;
on Flexbox (&lt;a class="link" href="https://www.safaribooksonline.com/library/view/css-flexbox/100000006A0444/" target="_blank" rel="noopener"
&gt;Safari&lt;/a&gt;). It seems that Flexbox is the future of CSS-based
layouts, and it&amp;rsquo;s worth learning about.&lt;/p&gt;
&lt;h3 id="advanced-javascript-elm-react-angular-backbone-ember"&gt;Advanced JavaScript (Elm, React, Angular, Backbone, Ember)
&lt;/h3&gt;&lt;p&gt;The JavaScript web framework space has exploded. Many of these are
implementations of the &lt;a class="link" href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller" title="Model-view-controller - Wikipedia"
target="_blank" rel="noopener"
&gt;Model, View, Controller
pattern&lt;/a&gt;, including React, Angular, and Ember.
These tools allow the creation of complex web apps (as well as mobile apps).&lt;/p&gt;
&lt;h3 id="web-server-operations-and-dns"&gt;Web Server Operations and DNS
&lt;/h3&gt;&lt;p&gt;I learned a ton form &lt;a class="link" href="http://shop.oreilly.com/product/0636920006066.do" target="_blank" rel="noopener"
&gt;Linux Web
Operations&lt;/a&gt;
(&lt;a class="link" href="https://www.safaribooksonline.com/library/view/linux-web-operations/9780133927757/" target="_blank" rel="noopener"
&gt;Safari&lt;/a&gt;) by Ben Whaley. &amp;ldquo;The videos discuss the
relationship between web and application servers, load balancers, and
databases and introduce configuration management, monitoring, containers,
cryptography, and DNS.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve struggled with DNS configuration over the years, so I watched Cricket
Liu&amp;rsquo;s &lt;a class="link" href="http://shop.oreilly.com/product/0636920040088.do" target="_blank" rel="noopener"
&gt;Learning DNS&lt;/a&gt; series
(&lt;a class="link" href="https://www.safaribooksonline.com/library/view/learning-dns/9781771373692/" target="_blank" rel="noopener"
&gt;Safari&lt;/a&gt;). I still wouldn&amp;rsquo;t want to be responsible for a company&amp;rsquo;s
complex DNS infrastructure, but I can now configure my own sites DNS with a
little more understanding.&lt;/p&gt;
&lt;h3 id="development-automation"&gt;Development Automation
&lt;/h3&gt;&lt;h4 id="package-managers"&gt;Package Managers
&lt;/h4&gt;&lt;p&gt;It&amp;rsquo;s likely that any modern web project will have some external Javascript
dependencies. Package managers (analogous to Pypi or Anaconda.org on Python)
have emerged to help support this. Node.js comes with the npm package manager,
but &lt;a class="link" href="https://bower.io/" target="_blank" rel="noopener"
&gt;Bower&lt;/a&gt; seems to make more sense for front-end
development.[^yarn] &lt;a class="link" href="http://codylindley.com/techpro/2013_04_12__package-managers-an-introducto/" target="_blank" rel="noopener"
&gt;Cody Lindley has a nice introduction to npm and Bower&lt;/a&gt;. Bower is &lt;a class="link" href="https://bower.io" target="_blank" rel="noopener"
&gt;well documented&lt;/a&gt; and easy to start
using. There is a nice &lt;a class="link" href="https://pypi.python.org/pypi/Flask-Bower/" target="_blank" rel="noopener"
&gt;Flask extension&lt;/a&gt; to help you integrate Bower with your Python project. (Update: since
writing this, &lt;a class="link" href="https://yarnpkg.com/en/" target="_blank" rel="noopener"
&gt;Yarn&lt;/a&gt; has come to dominate this
scene.)&lt;/p&gt;
&lt;h4 id="task-automation"&gt;Task Automation
&lt;/h4&gt;&lt;p&gt;Web development comes with lots of build-style tasks that have to happen
repeatedly. For example, before you can render a webpage in the browser, you
might need to convert the Less to CSS and start a local web server. Before
deploying to production, you might want to also run tests and minify your
Javascript.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a GUI application called &lt;a class="link" href="https://codekitapp.com/" target="_blank" rel="noopener"
&gt;Codekit&lt;/a&gt; that can
do a lot of these tasks. You can also do it through a Node.js program called
&lt;a class="link" href="https://gruntjs.com/" target="_blank" rel="noopener"
&gt;Grunt&lt;/a&gt;. I haven&amp;rsquo;t used it yet, but it looks like
following the documentation would be the best way to get started.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://gulpjs.com/" target="_blank" rel="noopener"
&gt;Gulp&lt;/a&gt; is a popular alternative to Grunt.&lt;/p&gt;
&lt;h3 id="design"&gt;Design
&lt;/h3&gt;&lt;h4 id="visual-design"&gt;Visual Design
&lt;/h4&gt;&lt;p&gt;Design has never been my strong point. One way to compensate for that is to
rely on the work of others. There are copious &lt;a class="link" href="https://www.google.com/search?q=bootstrap&amp;#43;theme" target="_blank" rel="noopener"
&gt;Bootstrap themes
available&lt;/a&gt;, and some are even
&lt;a class="link" href="https://bootswatch.com/" target="_blank" rel="noopener"
&gt;free&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I enjoyed &lt;a class="link" href="https://softwareengineeringdaily.com/2016/02/18/design-non-designers-tracy-osborn/" target="_blank" rel="noopener"
&gt;Software Engineering Daily&amp;rsquo;s interview with Tracy Osborn on Design for Non-designers&lt;/a&gt;. She has some &lt;a class="link" href="https://medium.com/@limedaring/design-for-non-designers-part-1-6559ed93ff91" target="_blank" rel="noopener"
&gt;blog posts&lt;/a&gt; on the topic. Tracy recommends
&lt;a class="link" href="http://www.colourlovers.com/" target="_blank" rel="noopener"
&gt;COLOURLovers&lt;/a&gt; for color ideas and &lt;a class="link" href="http://fontpair.co/" target="_blank" rel="noopener"
&gt;Font Pair&lt;/a&gt; for selecting fonts from &lt;a class="link" href="https://fonts.google.com/about" target="_blank" rel="noopener"
&gt;Google Fonts&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="user-experience-design"&gt;User Experience Design
&lt;/h4&gt;&lt;p&gt;On the topic of UX, I finally read Steve Krug&amp;rsquo;s classic &lt;a class="link" href="http://amzn.to/2opg3ZG" target="_blank" rel="noopener"
&gt;Don&amp;rsquo;t Make Me Think&lt;/a&gt;
(&lt;a class="link" href="https://www.safaribooksonline.com/library/view/dont-make-me/0321344758/" target="_blank" rel="noopener"
&gt;Safari&lt;/a&gt;); it&amp;rsquo;s great. Ginny Redish&amp;rsquo;s &lt;a class="link" href="http://amzn.to/2oDl0NY" target="_blank" rel="noopener"
&gt;Letting Go of
Words&lt;/a&gt;
(&lt;a class="link" href="https://www.safaribooksonline.com/library/view/letting-go-of/9780123859303/" target="_blank" rel="noopener"
&gt;Safari&lt;/a&gt;) is similarly excellent.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;ve learned a lot in the past few months. I&amp;rsquo;ve filled in some gaps about how
CSS works. I&amp;rsquo;ve gotten a better grasp on the Javascript prototype model. I&amp;rsquo;ve
learned that I can start with higher level tools (e.g. Bootstrap and JQuery)
to rapidly build my side projects with some amount of visual appeal. I&amp;rsquo;m
learning how to use available tools to reduce the boilerplate I have to write,
automate tedious tasks, and reduce my personal technical debt.&lt;/p&gt;
&lt;p&gt;I still have a lot of learning and a lot of practicing ahead of me, but I&amp;rsquo;m
starting to feel confident that I could make headway on some of my projects.
The modern &lt;a class="link" href="https://medium.com/@withinsight1/the-front-end-spectrum-c0f30998c9f0" target="_blank" rel="noopener"
&gt;frontend development landscape&lt;/a&gt; is massive, varied, and ever changing, but that
shouldn&amp;rsquo;t prohibit you from diving in if you want to.&lt;/p&gt;</description></item><item><title>Automating Python with Ansible</title><link>https://tdhopper.com/blog/automating-python-with-ansible/</link><pubDate>Thu, 23 Mar 2017 15:11:00 +0000</pubDate><guid>https://tdhopper.com/blog/automating-python-with-ansible/</guid><description>&lt;p&gt;I wrote &lt;a class="link" href="https://tdhopper.com/blog/data-scientists-need-more-automation/" &gt;a few months back&lt;/a&gt; about how data scientists need more automation. In
particular, I suggested that data scientists would be wise to learn more about
automated system configuration and automated deployments.&lt;/p&gt;
&lt;p&gt;In an attempt to take my own advice, I&amp;rsquo;ve finally been making myself learn
&lt;a class="link" href="https://www.ansible.com/" target="_blank" rel="noopener"
&gt;Ansible&lt;/a&gt;. It turns out that a great way to learn it
is to sit down and read through the docs, front to back; I commend that tactic
to you. I also put together this tutorial to walk through a practical example
of how a working data scientist might use this powerful tool.&lt;/p&gt;
&lt;p&gt;What follows is an Ansible guide that will take you from installing Ansible to
automatically deploying a long-running Python to a remote machine and running
it in a &lt;a class="link" href="https://conda.io/docs/using/envs.html" target="_blank" rel="noopener"
&gt;Conda environment&lt;/a&gt; using
&lt;a class="link" href="http://supervisord.org/" target="_blank" rel="noopener"
&gt;supervisord&lt;/a&gt;. It presumes your development machine
is on OS X and the remote machine is Debian-like; however, it shouldn&amp;rsquo;t
require too many changes to run it on other systems.&lt;/p&gt;
&lt;p&gt;I wrote this post in a Jupyter notebook with a Bash kernel. You can find the
notebook, Ansible files, and installation directions on &lt;a class="link" href="https://github.com/tdhopper/automating_python" target="_blank" rel="noopener"
&gt;my
Github&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="ansible"&gt;Ansible
&lt;/h2&gt;&lt;p&gt;Ansible provides &amp;ldquo;human readable automation&amp;rdquo; for &amp;ldquo;app deployment&amp;rdquo; and
&amp;ldquo;configuration management&amp;rdquo;. Unlike tools like Chef, it doesn&amp;rsquo;t require an
agent to be running on remote machines. In short, it translates declarative
YAML files into shell commands and runs them on your machines over SSH.&lt;/p&gt;
&lt;p&gt;Ansible is backed by Red Hat and has a great
&lt;a class="link" href="https://www.ansible.com/" target="_blank" rel="noopener"
&gt;website&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="installing-ansible-with-homebrew"&gt;Installing Ansible with Homebrew
&lt;/h2&gt;&lt;p&gt;First, you&amp;rsquo;ll need to &lt;a class="link" href="http://docs.ansible.com/ansible/intro_installation.html" target="_blank" rel="noopener"
&gt;install
Ansible&lt;/a&gt;. On a Mac, I
recommend doing this with &lt;a class="link" href="https://brew.sh/" target="_blank" rel="noopener"
&gt;Homebrew&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;brew install ansible
Warning: ansible-2.1.0.0 already installed
Warning: You are using OS X 10.12.
We do not provide support for this pre-release version.
You may encounter build failures or other breakages.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="quickstart"&gt;Quickstart
&lt;/h2&gt;&lt;p&gt;Soon, I&amp;rsquo;ll show you how to put write an Ansible YAML file. However, Ansible
also allows you specify tasks from the command line.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how we could use Ansible ping our local host:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ansible -i 'localhost,' -c local -m ping all
ansible -i 'localhost,' -c local -m ping all
localhost | SUCCESS =&amp;gt; {
&amp;quot;changed&amp;quot;: false,
&amp;quot;ping&amp;quot;: &amp;quot;pong&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command calls ansible and tells it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To use &lt;code&gt;localhost&lt;/code&gt; as it&amp;rsquo;s inventory (&lt;code&gt;-i&lt;/code&gt;). Inventory is Ansible speak for machine or machines you want to be able to run commands on.&lt;/li&gt;
&lt;li&gt;To connect (&lt;code&gt;-c&lt;/code&gt;) locally (&lt;code&gt;local&lt;/code&gt;) instead of over SSH.&lt;/li&gt;
&lt;li&gt;To run the &lt;a class="link" href="http://docs.ansible.com/ansible/ping_module.html" target="_blank" rel="noopener"
&gt;&lt;code&gt;ping&lt;/code&gt; module&lt;/a&gt; (&lt;code&gt;-m&lt;/code&gt;) to test the connection.&lt;/li&gt;
&lt;li&gt;To run the command on &lt;code&gt;all&lt;/code&gt; hosts in the inventory (in this case, our inventory is just the &lt;code&gt;localhost&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a class="link" href="https://web.archive.org/web/20170330023127/http://www.mechanicalfish.net/start-learning-ansible-with-one-line-and-no-files/" target="_blank" rel="noopener"
&gt;Michael Booth&lt;/a&gt; has a
&lt;a class="link" href="https://web.archive.org/web/20170330023127/http://www.mechanicalfish.net/start-learning-ansible-with-one-line-and-no-files/" target="_blank" rel="noopener"
&gt;post&lt;/a&gt; that goes into more detail about
this command.&lt;/p&gt;
&lt;p&gt;Behind the scenes, Ansible is turning this &lt;code&gt;-m ping&lt;/code&gt; command into shell
commands. (Try running with the &lt;code&gt;-vvv&lt;/code&gt; flag to see what&amp;rsquo;s happening behind the
scenes.) It can also execute arbitrary commands; by default, it&amp;rsquo;ll use the
Bourne shell &lt;code&gt;sh&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ansible all -i 'localhost, ' -c local -a &amp;quot;/bin/echo hello&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="setting-up-an-ansible-inventory"&gt;Setting up an Ansible Inventory
&lt;/h2&gt;&lt;p&gt;Instead of specifying our inventory with the &lt;code&gt;-i&lt;/code&gt; flag each time, we should
specify an Ansible inventory file. This file is a text file specifying
machines you have SSH access to; you can also group machines under bracketed
headings. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mail.example.com
[webservers]
foo.example.com
bar.example.com
[dbservers]
one.example.com
two.example.com
three.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ansible has to be able to connect to these machines over SSH, so you will
likely need to have relevant entries in your &lt;a class="link" href="http://nerderati.com/2011/03/17/simplify-your-life-with-an-ssh-config-file/" target="_blank" rel="noopener"
&gt;&lt;code&gt;.ssh/config&lt;/code&gt; file&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By default, the Ansible CLI will look for a system-wide Ansible inventory file
in &lt;code&gt;/etc/ansible/hosts&lt;/code&gt;. You can also specify an alternative path for an
intentory file with the &lt;code&gt;-i&lt;/code&gt; flag.&lt;/p&gt;
&lt;p&gt;For this tutorial, I&amp;rsquo;d like to have an inventory file specific to the project
directory without having to specify it each time we call Ansible. We can do
this by creating a file called &lt;code&gt;./ansible.cfg&lt;/code&gt; and set the name of our local
inventory file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat ./ansible.cfg
cat ./ansible.cfg
[defaults]
inventory = ./hosts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can check that Ansible is picking up your config file by running &lt;code&gt;ansible --version&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ansible --version
ansible --version
ansible 2.1.0.0
config file = /Users/tdhopper/repos/automating_python/ansible.cfg
configured module search path = Default w/o overrides
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For this example, I just have one host, a &lt;a class="link" href="https://www.digitalocean.com/" target="_blank" rel="noopener"
&gt;Digital Ocean
VPS&lt;/a&gt;. To run the examples below, you should
create a VPS instance on Digital Ocean, &lt;a class="link" href="https://amazonlightsail.com" target="_blank" rel="noopener"
&gt;Amazon&lt;/a&gt;,
or elsewhere; you&amp;rsquo;ll want to configure it for &lt;a class="link" href="https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys--2" target="_blank" rel="noopener"
&gt;passwordless authentication&lt;/a&gt;. I have an entry like this in my &lt;code&gt;~/.ssh/hosts&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Host digitalocean
HostName 45.55.395.23
User root
Port 22
IdentityFile /Users/tdhopper/.ssh/id_rsa
ForwardAgent yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and my intentory file (&lt;code&gt;~/hosts&lt;/code&gt;) is just&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;digitalocean
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before trying ansible, you should ensure that you can connect to this host:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh digitalocean echo 1
ssh digitalocean echo 1
1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I can verify that Ansible can connect to my machine by running the ping
command.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ansible all -m ping
ansible all -m ping
digitalocean | SUCCESS =&amp;gt; {
&amp;quot;changed&amp;quot;: false,
&amp;quot;ping&amp;quot;: &amp;quot;pong&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We told Ansible to run this command on &lt;code&gt;all&lt;/code&gt; specified hosts in the inventory.
It found our inventory by loading the &lt;code&gt;ansible.cfg&lt;/code&gt; which specified &lt;code&gt;./hosts&lt;/code&gt;
as the inventory file.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s possible that this will fail for you even if you can SSH into the
machine. If the error is something like &lt;code&gt;/bin/sh: 1: /usr/bin/python: not found&lt;/code&gt;, this is because your VPS doesn&amp;rsquo;t have Python installed on it. You can
&lt;a class="link" href="http://stackoverflow.com/questions/32429259/ansible-fails-with-bin-sh-1-usr-bin-python-not-found" target="_blank" rel="noopener"
&gt;install it with Ansible&lt;/a&gt;, but you may just want to
manually run &lt;code&gt;sudo apt-get -y install python&lt;/code&gt; on the VPS to get started.&lt;/p&gt;
&lt;h2 id="writing-our-first-playbook"&gt;Writing our first Playbook
&lt;/h2&gt;&lt;p&gt;While adhoc commands will often be useful, the real power of Ansible comes
from creating repeatable sets of instructions called
&lt;a class="link" href="http://docs.ansible.com/ansible/playbooks.html" target="_blank" rel="noopener"
&gt;Playbooks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A playbook contains a list of &amp;ldquo;plays&amp;rdquo;. Each play specifies a set of tasks to
be run and which hosts to run them on. A &amp;ldquo;task&amp;rdquo; is a call to an Ansible
module, like the &amp;ldquo;ping&amp;rdquo; module we&amp;rsquo;ve already seen. Ansible &lt;a class="link" href="http://docs.ansible.com/ansible/list_of_all_modules.html" target="_blank" rel="noopener"
&gt;comes packaged
with about 1000
modules&lt;/a&gt; for all
sorts of use cases. You can also extend it with your own
&lt;a class="link" href="http://docs.ansible.com/ansible/dev_guide/developing_modules.html" target="_blank" rel="noopener"
&gt;modules&lt;/a&gt;
and &lt;a class="link" href="http://docs.ansible.com/ansible/playbooks_roles.html#roles" target="_blank" rel="noopener"
&gt;roles&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our first playbook will just execute the ping module on all our hosts. It&amp;rsquo;s a
playbook with a single play comprised of a single task.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat ping.yml
cat ping.yml
---
- hosts: all
tasks:
- name: ping all hosts
ping:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can run our playbook with the &lt;code&gt;ansible-playbook&lt;/code&gt; command.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ansible-playbook ping.yml
ansible-playbook ping.yml
____________
&amp;lt; PLAY [all] &amp;gt;
------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
______________
&amp;lt; TASK [setup] &amp;gt;
--------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ok: [digitalocean]
_______________________
&amp;lt; TASK [ping all hosts] &amp;gt;
-----------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ok: [digitalocean]
____________
&amp;lt; PLAY RECAP &amp;gt;
------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
digitalocean : ok=2 changed=0 unreachable=0 failed=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might wonder why there are cows on your screen. You can find out
&lt;a class="link" href="https://michaelheap.com/cowsay-and-ansible/" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;. However, the important
thing is that our task was executed and returned successfully.&lt;/p&gt;
&lt;p&gt;We can override the hosts list for the play with the &lt;code&gt;-i&lt;/code&gt; flag to see what the
output looks like when Ansible fails to run the play because it can&amp;rsquo;t find the
host.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s work now on installing the dependencies for our Python project.&lt;/p&gt;
&lt;h2 id="installing-supervisord"&gt;Installing supervisord
&lt;/h2&gt;&lt;p&gt;&amp;ldquo;Supervisor is a client/server system that allows its users to monitor and
control a number of processes on UNIX-like operating systems.&amp;rdquo; We&amp;rsquo;ll use it to
run and monitor our Python process.&lt;/p&gt;
&lt;p&gt;On a Debian-like system, we can install it with APT. In the Ansible DSL that&amp;rsquo;s
just:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- name: Install supervisord
sudo: yes
apt:
name: supervisor
state: present
update_cache: yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can read more about the &lt;a class="link" href="http://docs.ansible.com/ansible/apt_module.html" target="_blank" rel="noopener"
&gt;apt module
here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once we have it installed, we can start it with this task:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- name: Start supervisord
sudo: yes
service:
name: &amp;quot;supervisor&amp;quot;
state: running
enabled: yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This uses the &lt;a class="link" href="http://docs.ansible.com/ansible/service_module.html" target="_blank" rel="noopener"
&gt;service&lt;/a&gt;
module.&lt;/p&gt;
&lt;p&gt;We could add these these tasks to a playbook file (like ping.yml), but what
maybe we will want to share it among multiple playbooks? For this, Ansible has
a construct called
&lt;a class="link" href="http://docs.ansible.com/ansible/playbooks_roles.html" target="_blank" rel="noopener"
&gt;Roles&lt;/a&gt;. A role is a
collection of &amp;ldquo;variable values, certain tasks, and certain handlers – or just
one or more of these things&amp;rdquo;. (You can learn more about variables and handlers
in the Ansible docs.)&lt;/p&gt;
&lt;p&gt;Roles are organized as subfolders of a folder called &amp;ldquo;Roles&amp;rdquo; in the working
directory. The rapid proliferation of folders in Ansible organization can be
overwhelming, but a very simple rule is just a file called &lt;code&gt;main.yml&lt;/code&gt; nestled
several folders deep. In our case, it&amp;rsquo;s in
&lt;code&gt;./roles/supervisor/tasks/main.yml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Check out &lt;a class="link" href="http://docs.ansible.com/ansible/playbooks_roles.html#roles" target="_blank" rel="noopener"
&gt;the
docs&lt;/a&gt; to learn
more about role organization.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what our role looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat ./roles/supervisor/tasks/main.yml
cat ./roles/supervisor/tasks/main.yml
---
- name: Install supervisord
become: true
apt:
name: supervisor
state: present
update_cache: yes
tags:
supervisor
- name: Start supervisord
become: true
service:
name: &amp;quot;supervisor&amp;quot;
state: running
enabled: yes
tags:
supervisor
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that I added &lt;code&gt;tags:&lt;/code&gt; to the task definitions.
&lt;a class="link" href="http://docs.ansible.com/ansible/playbooks_tags.html" target="_blank" rel="noopener"
&gt;Tags&lt;/a&gt; just allow you to
run a portion of a playbook instead of the whole thing with the &lt;code&gt;--tags&lt;/code&gt; flag
for &lt;code&gt;ansible-playbook&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now that we have the supervisor install encapsulated in a role, we can write a
simple playbook to run the role.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat supervisor.yml
cat supervisor.yml
---
- hosts: digitalocean
roles:
- role: supervisor
ansible-playbook supervisor.yml
ansible-playbook supervisor.yml
_____________________
&amp;lt; PLAY [digitalocean] &amp;gt;
---------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
______________
&amp;lt; TASK [setup] &amp;gt;
--------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ok: [digitalocean]
_________________________________________
&amp;lt; TASK [supervisor : Install supervisord] &amp;gt;
-----------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [digitalocean]
_______________________________________
&amp;lt; TASK [supervisor : Start supervisord] &amp;gt;
---------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
changed: [digitalocean]
____________
&amp;lt; PLAY RECAP &amp;gt;
------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
digitalocean : ok=3 changed=2 unreachable=0 failed=0
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="installing-conda-with-ansible-galaxy"&gt;Installing Conda with Ansible Galaxy
&lt;/h2&gt;&lt;p&gt;Next we want to ensure that Conda installed on our system. We could write our
own role to follow the &lt;a class="link" href="https://www.continuum.io/downloads" target="_blank" rel="noopener"
&gt;recommended
process&lt;/a&gt;. However, Ansible has a helpful
tool to help us avoid reinventing the wheel by allowing users to share roles;
this is called &lt;a class="link" href="https://galaxy.ansible.com/" target="_blank" rel="noopener"
&gt;Ansible Galaxy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can search the Galaxy website for
&lt;a class="link" href="https://galaxy.ansible.com/list#/roles?page=1&amp;amp;page_size=10&amp;amp;autocomplete=miniconda" target="_blank" rel="noopener"
&gt;miniconda&lt;/a&gt;
and see that a handful of roles for installing Miniconda exist. I liked &lt;a class="link" href="https://galaxy.ansible.com/andrewrothstein/miniconda/" target="_blank" rel="noopener"
&gt;this
one&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We can install the role locally using the &lt;code&gt;ansible-galaxy&lt;/code&gt; command line tool.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ansible-galaxy install -f andrewrothstein.miniconda
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can have the role installed wherever you want (run &lt;code&gt;ansible-galaxy install --help&lt;/code&gt; to see how, but by default they&amp;rsquo;ll go to
&lt;code&gt;/usr/local/etc/ansible/roles/&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ls -lh /usr/local/etc/ansible/roles/andrewrothstein.miniconda
ls -lh /usr/local/etc/ansible/roles/andrewrothstein.miniconda
total 32
-rw-rw-r-- 1 tdhopper admin 1.1K Jan 16 16:52 LICENSE
-rw-rw-r-- 1 tdhopper admin 666B Jan 16 16:52 README.md
-rw-rw-r-- 1 tdhopper admin 973B Jan 16 16:52 circle.yml
drwxrwxr-x 3 tdhopper admin 102B Mar 21 11:33 defaults
drwxrwxr-x 3 tdhopper admin 102B Mar 21 11:33 handlers
drwxrwxr-x 4 tdhopper admin 136B Mar 21 11:33 meta
drwxrwxr-x 3 tdhopper admin 102B Mar 21 11:33 tasks
drwxrwxr-x 3 tdhopper admin 102B Mar 21 11:33 templates
-rw-rw-r-- 1 tdhopper admin 57B Jan 16 16:52 test.yml
drwxrwxr-x 3 tdhopper admin 102B Mar 21 11:33 vars
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can look at the &lt;code&gt;tasks/main.yml&lt;/code&gt; to see the core logic of installing
Miniconda. It has tasks to download the installer, run the installer, delete
the installer, run &lt;code&gt;conda update conda&lt;/code&gt;, and make &lt;code&gt;conda&lt;/code&gt; the default system
Python.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat /usr/local/etc/ansible/roles/andrewrothstein.miniconda/tasks/main.yml
/main.ymllocal/etc/ansible/roles/andrewrothstein.miniconda/tasks
---
# tasks file for miniconda
- name: download installer...
become: yes
become_user: root
get_url:
url: '{{miniconda_installer_url}}'
dest: /tmp/{{miniconda_installer_sh}}
timeout: '{{miniconda_timeout_seconds}}'
checksum: '{{miniconda_checksum}}'
mode: '0755'
- name: installing....
become: yes
become_user: root
command: /tmp/{{miniconda_installer_sh}} -b -p {{miniconda_parent_dir}}/{{miniconda_name}}
args:
creates: '{{miniconda_parent_dir}}/{{miniconda_name}}'
- name: deleting installer...
become: yes
become_user: root
when: miniconda_cleanup
file:
path: /tmp/{{miniconda_installer_sh}}
state: absent
- name: link miniconda...
become: yes
become_user: root
file:
dest: '{{miniconda_parent_dir}}/miniconda'
src: '{{miniconda_parent_dir}}/{{miniconda_name}}'
state: link
- name: conda updates
become: yes
become_user: root
command: '{{miniconda_parent_dir}}/miniconda/bin/conda update -y --all'
- name: make system default python etc...
when: miniconda_make_sys_default
become: yes
become_user: root
with_items:
- etc/profile.d/miniconda.sh
template:
src: '{{item}}.j2'
dest: /{{item}}
mode: 0644
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="overriding-ansible-variables"&gt;Overriding Ansible Variables
&lt;/h3&gt;&lt;p&gt;Once a role is installed locally, you can add it to a play just like you can
with roles you wrote. Installing Miniconda is now as simple as:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; roles:
- role: andrewrothstein.miniconda
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before we add that to a playbook, I want to customize &lt;em&gt;where&lt;/em&gt; miniconda is
installed. If you look back at the &lt;code&gt;main.yml&lt;/code&gt; file above, you see a bunch of
things surrounded in double brackets. These are variables (in the &lt;a class="link" href="http://jinja.pocoo.org/docs/2.9/" target="_blank" rel="noopener"
&gt;Jinja2
template language&lt;/a&gt;). From the play, we can
see that Miniconda will be installed at
&lt;code&gt;{{miniconda_parent_dir}}/{{miniconda_name}}&lt;/code&gt;. The role defines these
variables in &lt;code&gt;/andrewrothstein.miniconda/defaults/main.yml&lt;/code&gt;. We can override
the default variables by specifying them in our play.&lt;/p&gt;
&lt;p&gt;A play to install miniconda could look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
- hosts: digitalocean
vars:
conda_folder_name: miniconda
conda_root: /root
roles:
- role: andrewrothstein.miniconda
miniconda_parent_dir: &amp;quot;{{ conda_root }}&amp;quot;
miniconda_name: &amp;quot;{{ conda_folder_name }}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I added this to
&lt;a class="link" href="https://github.com/tdhopper/automating_python/blob/master/playbook.yml" target="_blank" rel="noopener"
&gt;&lt;code&gt;playbook.yml&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We now know how to use Ansible to start and run supervisord and to install
Miniconda. Let&amp;rsquo;s see how to use it to deploy and start our application.&lt;/p&gt;
&lt;h2 id="deploy-python-application"&gt;Deploy Python Application
&lt;/h2&gt;&lt;p&gt;There are countless ways to deploy a Python application. We&amp;rsquo;re going to see
how to use Ansible to deploy from Github.&lt;/p&gt;
&lt;p&gt;I created a little project called
&lt;a class="link" href="https://github.com/tdhopper/long_running_python_process" target="_blank" rel="noopener"
&gt;long_running_python_application&lt;/a&gt;.
It has a
&lt;a class="link" href="https://github.com/tdhopper/long_running_python_process/blob/master/main.py" target="_blank" rel="noopener"
&gt;&lt;code&gt;main.py&lt;/code&gt;&lt;/a&gt;
that writes a log line to stdout every 30 seconds; that&amp;rsquo;s it. It also includes
a &lt;a class="link" href="https://github.com/tdhopper/long_running_python_process/blob/master/environment.yml" target="_blank" rel="noopener"
&gt;Conda environment
file&lt;/a&gt;
specifying the dependencies and &lt;a class="link" href="https://github.com/tdhopper/long_running_python_process/blob/master/run.sh" target="_blank" rel="noopener"
&gt;a shell
script&lt;/a&gt;
that activates the environment and runs the program.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re going to use Ansible to&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clone the repository into our remote machine.&lt;/li&gt;
&lt;li&gt;Create a Conda environment based on the environment.yml file.&lt;/li&gt;
&lt;li&gt;Create a supervisord file for running the program.&lt;/li&gt;
&lt;li&gt;Start the supervisord job.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="clone-the-repository"&gt;Clone the repository
&lt;/h3&gt;&lt;p&gt;Cloning a repository with Ansible is easy. We just use the &lt;a class="link" href="http://docs.ansible.com/ansible/git_module.html" target="_blank" rel="noopener"
&gt;&lt;code&gt;git&lt;/code&gt;
module&lt;/a&gt;. This play will clone
the repo into the specified directory. The &lt;code&gt;update: yes&lt;/code&gt; flag tells Ansible to
update the repository from the remote if it has already been cloned.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
- hosts: digitalocean
vars:
project_repo: git://github.com/tdhopper/long_running_python_process.git
project_location: /srv/long_running_python_process
tasks:
- name: Clone project code.
git:
repo: &amp;quot;{{ project_repo }}&amp;quot;
dest: &amp;quot;{{ project_location }}&amp;quot;
update: yes
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="creating-the-conda-environment"&gt;Creating the Conda Environment
&lt;/h3&gt;&lt;p&gt;Since we&amp;rsquo;ve now installed conda and cloned the repository with an
&lt;code&gt;environment.yml&lt;/code&gt; file, we just need to run &lt;code&gt;conda env update&lt;/code&gt; from the
directory containing the environment spec. Here&amp;rsquo;s a play to do that:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
- hosts: digitalocean
vars:
project_location: /srv/long_running_python_process
tasks:
- name: Create Conda environment from project environment file.
command: &amp;quot;{{conda_root}}/{{conda_folder_name}}/bin/conda env update&amp;quot;
args:
chdir: &amp;quot;{{ project_location }}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It uses the &lt;a class="link" href="http://docs.ansible.com/ansible/command_module.html" target="_blank" rel="noopener"
&gt;&lt;code&gt;command&lt;/code&gt;
module&lt;/a&gt; which just
executes a shell command in the desired directory.&lt;/p&gt;
&lt;h3 id="create-a-supervisord-file"&gt;Create a Supervisord File
&lt;/h3&gt;&lt;p&gt;By default, supervisord will &lt;a class="link" href="http://supervisord.org/configuration.html" target="_blank" rel="noopener"
&gt;look in &lt;code&gt;/etc/supervisor/conf.d/&lt;/code&gt; for
configuration&lt;/a&gt; on which programs to
run.&lt;/p&gt;
&lt;p&gt;We need to put a file in there that tells supervisord to run our &lt;code&gt;run.sh&lt;/code&gt;
script. Ansible has an integrated way of setting up templates which can be
placed on remote machines.&lt;/p&gt;
&lt;p&gt;I put a supervisord job template in the &lt;code&gt;./templates&lt;/code&gt; folder.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat ./templates/run_process.j2
cat ./templates/run_process.j2
[program:{{ program_name }}]
command=sh run.sh
autostart=true
directory={{ project_location }}
stderr_logfile=/var/log/{{ program_name }}.err.log
stdout_logfile=/var/log/{{ program_name }}.out.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a is normal INI-style config file, except it includes Jinja2
variables. We can use the Ansible &lt;a class="link" href="http://docs.ansible.com/ansible/template_module.html" target="_blank" rel="noopener"
&gt;&lt;code&gt;template&lt;/code&gt;
module&lt;/a&gt; to create a task
which fills in the variables with information about our program and copies it
into the &lt;code&gt;conf.d&lt;/code&gt; folder on the remote machine.&lt;/p&gt;
&lt;p&gt;The play for this would look like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- hosts: digitalocean
vars:
project_location: /srv/long_running_python_process
program_name: long_running_process
supervisord_configs_path: /etc/supervisor/conf.d
tasks:
- name: Copy supervisord job file to remote
template:
src: ./templates/run_process.j2
dest: &amp;quot;{{ supervisord_configs_path }}/run_process.conf&amp;quot;
owner: root
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="start-the-supevisord-job"&gt;Start the supevisord job
&lt;/h3&gt;&lt;p&gt;Finally, we just need to tell supervisord on our remote machine to start the
job described by &lt;code&gt;run_process.conf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Instead of issuing our own shell commands via Ansible, we can use the
&lt;a class="link" href="http://docs.ansible.com/ansible/supervisorctl_module.html" target="_blank" rel="noopener"
&gt;&lt;code&gt;supervisorctl&lt;/code&gt;
module&lt;/a&gt;. The task
is just:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; - name: Start job
supervisorctl:
name: &amp;quot;{{ program_name }}&amp;quot;
state: present
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;state: present&lt;/code&gt; ensures that Ansible calls &lt;code&gt;supervisorctl reread&lt;/code&gt; to load a
new config. Because our config has &lt;code&gt;autostart=true&lt;/code&gt;, supervisor will start it
as soon as the task is added.&lt;/p&gt;
&lt;h2 id="the-big-playbook"&gt;The Big Playbook!
&lt;/h2&gt;&lt;p&gt;We can take everything we&amp;rsquo;ve described above and put it in one playbook.&lt;/p&gt;
&lt;p&gt;This playbook will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install Miniconda using the role from Ansible Galaxy.&lt;/li&gt;
&lt;li&gt;Install and start Supervisor using the role we created.&lt;/li&gt;
&lt;li&gt;Clone the Github project we want to run.&lt;/li&gt;
&lt;li&gt;Create a Conda environment based on the environment.yml file.&lt;/li&gt;
&lt;li&gt;Create a supervisord file for running the program.&lt;/li&gt;
&lt;li&gt;Start the supervisord job.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of this will be done on the host we specify (&lt;code&gt;digitalocean&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat playbook.yml
cat playbook.yml
---
- hosts: digitalocean
vars:
project_repo: git://github.com/tdhopper/long_running_python_process.git
project_location: /srv/long_running_python_process
program_name: long_running_process
conda_folder_name: miniconda
conda_root: /root
supervisord_configs_path: /etc/supervisor/conf.d
roles:
- role: andrewrothstein.miniconda
miniconda_parent_dir: &amp;quot;{{ conda_root }}&amp;quot;
miniconda_name: &amp;quot;{{ conda_folder_name }}&amp;quot;
tags:
miniconda
- role: supervisor
tasks:
- name: Clone project code.
git:
repo: &amp;quot;{{ project_repo }}&amp;quot;
dest: &amp;quot;{{ project_location }}&amp;quot;
update: yes
tags:
git
- name: Create Conda environment from project environment file.
command: &amp;quot;{{conda_root}}/{{conda_folder_name}}/bin/conda env update&amp;quot;
args:
chdir: &amp;quot;{{ project_location }}&amp;quot;
tags:
conda
- name: Copy supervisord job file to remote
template:
src: ./templates/run_process.j2
dest: &amp;quot;{{ supervisord_configs_path }}/run_process.conf&amp;quot;
owner: root
tags:
conf
- name: Start job
supervisorctl:
name: &amp;quot;{{ program_name }}&amp;quot;
state: present
tags:
conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To configure our machine, we just have to run &lt;code&gt;ansible-playbook playbook.yml&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ANSIBLE_NOCOWS=1 ansible-playbook playbook.yml
ANSIBLE_NOCOWS=1 ansible-playbook playbook.yml
PLAY [digitalocean] ************************************************************
TASK [setup] *******************************************************************
ok: [digitalocean]
TASK [andrewrothstein.unarchive-deps : resolve platform specific vars] *********
TASK [andrewrothstein.unarchive-deps : install common pkgs...] *****************
changed: [digitalocean] =&amp;gt; (item=[u'tar', u'unzip', u'gzip', u'bzip2'])
TASK [andrewrothstein.bash : install bash] *************************************
ok: [digitalocean]
TASK [andrewrothstein.alpine-glibc-shim : fix alpine] **************************
skipping: [digitalocean]
TASK [andrewrothstein.miniconda : download installer...] ***********************
changed: [digitalocean]
TASK [andrewrothstein.miniconda : installing....] ******************************
changed: [digitalocean]
TASK [andrewrothstein.miniconda : deleting installer...] ***********************
skipping: [digitalocean]
TASK [andrewrothstein.miniconda : link miniconda...] ***************************
changed: [digitalocean]
TASK [andrewrothstein.miniconda : conda updates] *******************************
changed: [digitalocean]
TASK [andrewrothstein.miniconda : make system default python etc...] ***********
skipping: [digitalocean] =&amp;gt; (item=etc/profile.d/miniconda.sh)
TASK [supervisor : Install supervisord] ****************************************
ok: [digitalocean]
TASK [supervisor : Start supervisord] ******************************************
ok: [digitalocean]
TASK [Clone project code.] *****************************************************
changed: [digitalocean]
TASK [Create Conda environment from project environment file.] *****************
changed: [digitalocean]
TASK [Copy supervisord job file to remote] *************************************
changed: [digitalocean]
TASK [Start job] ***************************************************************
changed: [digitalocean]
PLAY RECAP *********************************************************************
digitalocean : ok=13 changed=9 unreachable=0 failed=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See that the &lt;code&gt;PLAY RECAP&lt;/code&gt; shows that everything was OK, no systems were
unreachable, and no tasks failed.&lt;/p&gt;
&lt;p&gt;We can verify that the program is running without error:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh digitalocean sudo supervisorctl status
ssh digitalocean sudo supervisorctl status
long_running_process RUNNING pid 4618, uptime 0:01:34
ssh digitalocean cat /var/log/long_running_process.out.log
ssh digitalocean cat /var/log/long_running_process.out.log
INFO:root:Process ran for the 1th time
INFO:root:Process ran for the 2th time
INFO:root:Process ran for the 3th time
INFO:root:Process ran for the 4th time
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If your lucky (i.e. your systems and networks were setup sufficiently similar
to mine), you can run this exact same command to configure and start a process
on your own system. Moreover, you could use this exact same command to start
this program on an arbitrary number of machines by simply adding more hosts to
your inventory and play spec!&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Ansible is a powerful, customizable tool. Unlike some similar tools, it
requires very little setup to start using it. As I&amp;rsquo;ve learned more about it,
I&amp;rsquo;ve seen more and more ways in which I could&amp;rsquo;ve used it in copious projects
in the past; I intend to make it a regular part of my toolkit. (Historically
I&amp;rsquo;ve done this kind of thing with hacky combinations of shell scripts and
&lt;a class="link" href="http://www.fabfile.org/" target="_blank" rel="noopener"
&gt;Fabric&lt;/a&gt;; Ansible would often be better.)&lt;/p&gt;
&lt;p&gt;This tutorial just scratches the surface of the Ansible functionality. If you
want to learn more, I again recommend reading through the
&lt;a class="link" href="http://docs.ansible.com/ansible/index.html" target="_blank" rel="noopener"
&gt;docs&lt;/a&gt;; they&amp;rsquo;re very good. Of
course, you should start writing and running your own playbooks as soon as
possible! I also liked &lt;a class="link" href="https://serversforhackers.com/an-ansible-tutorial" target="_blank" rel="noopener"
&gt;this tutorial from Server Admin for
Programmers&lt;/a&gt;. If you want
to compare Ansible to alternatives, the &lt;a class="link" href="https://valdhaus.co/books/taste-test-puppet-chef-salt-stack-ansible.html" target="_blank" rel="noopener"
&gt;Taste Test book&lt;/a&gt; by Matt Jaynes looks promising. For more on Supervisor,
&lt;a class="link" href="https://serversforhackers.com/monitoring-processes-with-supervisord" target="_blank" rel="noopener"
&gt;serversforhackers.com&lt;/a&gt; has a nice tutorial, and &lt;a class="link" href="http://supervisord.org/" target="_blank" rel="noopener"
&gt;its docs are
thorough&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I wrote a tutorial on using &lt;a class="link" href="https://twitter.com/ansible" target="_blank" rel="noopener"
&gt;@ansible&lt;/a&gt; and
supervisor to deploy a long running Python process to a
&lt;a class="link" href="https://twitter.com/digitalocean" target="_blank" rel="noopener"
&gt;@digitalocean&lt;/a&gt;
VPS.&lt;a class="link" href="https://t.co/uPC8bY5haD" target="_blank" rel="noopener"
&gt;https://t.co/uPC8bY5haD&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;— Tim Hopper 🔭 (@tdhopper) &lt;a class="link" href="https://twitter.com/tdhopper/status/845256769429483520" target="_blank" rel="noopener"
&gt;March 24,
2017&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Naive Bayes Meme</title><link>https://tdhopper.com/blog/naive-bayes/</link><pubDate>Tue, 14 Mar 2017 21:29:00 +0000</pubDate><guid>https://tdhopper.com/blog/naive-bayes/</guid><description>&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/naivebayes/nb1.jpg"&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/naivebayes/nb2.jpg"&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/naivebayes/nb3.jpg"&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/naivebayes/nb4.jpg"&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/naivebayes/nb5.jpg"&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/naivebayes/nb6.jpg"&gt;
&lt;/figure&gt;</description></item><item><title>Some Reflections on Being Turned Down for a Lot of Data Science Jobs</title><link>https://tdhopper.com/blog/some-reflections-on-being-turned-down-for-a-lot-of-data-science-jobs/</link><pubDate>Mon, 06 Mar 2017 17:02:00 +0000</pubDate><guid>https://tdhopper.com/blog/some-reflections-on-being-turned-down-for-a-lot-of-data-science-jobs/</guid><description>&lt;img src="https://tdhopper.com/rejected.png" alt="Featured image of post Some Reflections on Being Turned Down for a Lot of Data Science Jobs" /&gt;&lt;blockquote&gt;
&lt;p&gt;👉 The decision was close, but the team has decided to keep looking for
someone who might have more direct neural net experience.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;👉 Honestly, I think the way you communicated your thought process and
results was confusing for some people in the room.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;👉 He&amp;rsquo;s needing someone with an image analysis background for data scientist
we&amp;rsquo;re hiring now.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;👉 Quite honestly given your questions [about vacation policy] and the fact
that you are considering other options, [we] may not be the best choice for
you.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;These quotes above are some of the reasons I&amp;rsquo;ve been given for why I wasn&amp;rsquo;t
offered a data science job after interviewing. I&amp;rsquo;ve been told a variety of
other reasons as well: company decided against hiring remotes after
interviewing (I&amp;rsquo;ve heard this at least 3 times), company thought I changed
jobs too frequently, company decided it didn&amp;rsquo;t have necessary data
infrastructure in place for data science work. Multiple companies gave no
particular reason; some of these were at least kind enough to &lt;em&gt;notify me&lt;/em&gt; they
weren&amp;rsquo;t interested. One company hired someone with a Ph.D. from MIT soon after
turning me down.&lt;/p&gt;
&lt;p&gt;In the last five years, I&amp;rsquo;ve clearly interviewed for a lot of data science
jobs, and I&amp;rsquo;ve also been turned down for a lot of data science
jobs]. I&amp;rsquo;ve spent a good bit of time reflecting
on why I wasn&amp;rsquo;t offered this job or that. Several folks have asked me if I had
any advice to share on the experience, and I hope to offer that here.&lt;/p&gt;
&lt;h2 id="you-never-really-know"&gt;You never really know
&lt;/h2&gt;&lt;p&gt;I learned with graduate school applications years ago: you rarely truly know
why you were turned down. Maybe my GRE scores weren&amp;rsquo;t high enough, or maybe
the reviewer rushed through my application in the 5 minutes before lunch.
Maybe my statement of interest was too weak, or maybe the department needed to
accept an alumni&amp;rsquo;s child.&lt;/p&gt;
&lt;p&gt;The same goes for companies. I&amp;rsquo;m fairly skeptical that the reasons I have been
given for why I was passed by are the full story, and I suspect you will
rarely (if ever) know the real reasons why you weren&amp;rsquo;t offered a job. I try to
use the reasons I hear as a way to help me refine my skills and better present
myself, but I don&amp;rsquo;t put too much weight in them.&lt;/p&gt;
&lt;h2 id="some-advice-anyway"&gt;Some advice anyway
&lt;/h2&gt;&lt;p&gt;That said, here are a few takeaways from interviewing for probably 20 data
science jobs since 2012.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Companies often use interviews as a time to figure out what they&amp;rsquo;re really looking for.&lt;/strong&gt; I suspect this is rarely intentional. But actually interviewing candidates forces a team to talk through what they&amp;rsquo;re actually looking for, and they often realize they had differing perspectives prior to the interview.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Companies where &amp;ldquo;data science&amp;rdquo; is a new addition need your help in understanding what data science can do for them.&lt;/strong&gt; As much as possible, use the interview to sell your vision for what data science can offer at the company, how you&amp;rsquo;ll get it off the ground, and what the ROI might be.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Being the wrong fit for what a company needs is not ideal.&lt;/strong&gt; I&amp;rsquo;ve come to appreciate a company trying to ensure my abilities align with their needs. You&amp;rsquo;d hope this was &lt;em&gt;always&lt;/em&gt; the case, but I&amp;rsquo;ve been hired when it wasn&amp;rsquo;t. That said, I hesitate to say you should always look for this: if you need a job, and someone offers you a job, you should feel free to take it!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data infrastructure is important and many companies are lacking it.&lt;/strong&gt; Many data scientists can attest to being hired at a company only to discover the data they needed wasn&amp;rsquo;t available, and they spent months or years building the tools required for them to start their analysis. Many companies are naive about how much engineering effort is required for effective data science. Don&amp;rsquo;t assume that a company with a grand vision for data science necessarily knows what it will take to accomplish that vision.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Many companies are still uneasy about data science being done remotely.&lt;/strong&gt; I think this is silly, but I&amp;rsquo;m biased.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;There&amp;rsquo;s little consistency as to what you might be asked in a data science interview.&lt;/strong&gt; I&amp;rsquo;ve been asked about Java design patterns, how to solve combinatorics problems, to describe my favorite machine learning model, to explain the &lt;a class="link" href="https://en.wikipedia.org/wiki/Sequential_minimal_optimization" target="_blank" rel="noopener"
&gt;SMO algorithm&lt;/a&gt;, my opinions about the TensorFlow API, how I do software testing, to analyze a never-before-seen dataset and prepare a presentation in a 4 hour window, the list goes on. I spent a flight to the west coast reading up on the statistics of A/B testing only to be asked largely soft-skills type questions for an entire interview. I&amp;rsquo;ve largely given up attempting any special preparation for interviews.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Networking is still king.&lt;/strong&gt; Hiring is hard, and interviewing is hard; having a prior relationship with an applicant is attractive and reduces hiring uncertainty. In my own experience, my friendships and connections with the data science community on Twitter has &lt;a class="link" href="https://twitter.com/tdhopper/status/760109403383144448" target="_blank" rel="noopener"
&gt;shaped my career&lt;/a&gt;. Don&amp;rsquo;t downplay the benefits of networking.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;So how do you get a data science job? I don&amp;rsquo;t know.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been unbelievably fortunate to be continuously employed since college,
but I&amp;rsquo;m not sure how to tell you to repeat that. The best I have to offer is
to reiterate the conclusion of &lt;a class="link" href="https://tdhopper.com/blog/how-i-quit-my-ph.d.-and-learned-to-love-data-science/" &gt;my recent talk about data science as a career&lt;/a&gt;. Learn and know the hard stuff: linear algebra, probability,
statistics, machine learning, math modeling, data structures, algorithms,
distributed systems, etc. You probably won&amp;rsquo;t use this knowledge every day in
your job, but interviewers love to ask about it anyway.&lt;/p&gt;
&lt;p&gt;At the same time, don&amp;rsquo;t forget about the even harder skills: communication,
careful thought, prose writing skill, software writing skill, software
engineering, tenacity, Stack Overflow. You will use these every day in your
job, and they&amp;rsquo;ll help you present yourself well in an interview. (With the
exception of Stack Overflow. Using Stack Overflow in an interview is strangely
taboo.)&lt;/p&gt;
&lt;h2 id="further-reading"&gt;Further Reading
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="http://treycausey.com/data_science_interviews.html" target="_blank" rel="noopener"
&gt;Trey Causey: What it&amp;rsquo;s like to be on the data science job market&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://treycausey.com/hiring_data_scientists.html" target="_blank" rel="noopener"
&gt;Trey Causey: Hiring data scientists&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.erinshellman.com/crushed-it-landing-a-data-science-job/" target="_blank" rel="noopener"
&gt;Erin Shellman: Crushed it! Landing a data science job &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://joelgrus.com/2016/05/23/fizz-buzz-in-tensorflow/" target="_blank" rel="noopener"
&gt;Joel Grus: Fizz Buzz in Tensorflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Logistic Regression Rules Everything Around Me</title><link>https://tdhopper.com/blog/logistic-regression-rules-everything-around-me/</link><pubDate>Tue, 21 Feb 2017 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/logistic-regression-rules-everything-around-me/</guid><description>&lt;p&gt;&lt;a class="link" href="https://twitter.com/fredbenenson" target="_blank" rel="noopener"
&gt;Fred Benenson&lt;/a&gt; spent 6 years doing data science at Kickstarter. When he left last year, he wrote a &lt;a class="link" href="https://hackernoon.com/on-to-the-next-2-271-days-309d6ba672d7" target="_blank" rel="noopener"
&gt;fantastic recap&lt;/a&gt; of his experience.&lt;/p&gt;
&lt;p&gt;His &amp;ldquo;list of things I&amp;rsquo;ve discovered over the years&amp;rdquo; is particularly good. Here are a few of the things that resonated with me:&lt;/p&gt;
&lt;blockquote&gt;
&lt;li&gt; The more you can work with someone to help refine their question the easier it will be to answer
&lt;li&gt; Conducting a randomized controlled experiment via an A/B test is always better than analyzing historical data
&lt;li&gt; Metrics are crucial to the story a company tells itself; it is essential to honestly and rigorously define them
&lt;li&gt; Good experimental design is difficult; don't allow a great testing framework to let you get lazy with it
&lt;li&gt; Data science (A/B testing, etc.) can help you how to optimize for a particular outcome, but it will never tell you which particular outcome to optimize for
&lt;li&gt; Always seek to record and attain data in its rawest form, whether you're instrumenting something yourself or retrieving it from an API
&lt;/blockquote&gt;
&lt;p&gt;I highly recommend reading &lt;a class="link" href="https://hackernoon.com/on-to-the-next-2-271-days-309d6ba672d7" target="_blank" rel="noopener"
&gt;the whole post&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>D̶r̶. Hopper or: How I Quit My Ph.D. and Learned to Love Data Science</title><link>https://tdhopper.com/talks/dr-hopper-quit-phd-data-science/</link><pubDate>Thu, 02 Feb 2017 00:00:00 +0000</pubDate><guid>https://tdhopper.com/talks/dr-hopper-quit-phd-data-science/</guid><description>&lt;img src="https://tdhopper.com/preso.png" alt="Featured image of post D̶r̶. Hopper or: How I Quit My Ph.D. and Learned to Love Data Science" /&gt;&lt;p&gt;I recently gave to the &lt;a class="link" href="http://bigdata.duke.edu/" target="_blank" rel="noopener"
&gt;Duke Big Data Initiative&lt;/a&gt; entitled &lt;em&gt;&lt;s&gt;Dr.&lt;/s&gt; Hopper, or How I Quit My Ph.D. and Learned to Love Data Science&lt;/em&gt;. The talk was well received, and &lt;a class="link" href="https://twitter.com/tdhopper/status/827239362404433922/photo/1" target="_blank" rel="noopener"
&gt;my slides&lt;/a&gt; seemed to resonate in the Twitter data science community.&lt;/p&gt;
&lt;div class="embed-responsive embed-responsive-16by9"&gt;
&lt;iframe src="https://docs.google.com/presentation/d/1_wdSh2PFxiqBegt5PcatbEiQaganlgdb5bH7V2jHXZI/embed?start=false&amp;loop=false&amp;delayms=10000" frameborder="0" width="480" height="299" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Here&amp;rsquo;s my favorite section from the talk:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/talks/dr-hopper-quit-phd-data-science/dd1.png"
alt="What my wife thinks I do vs. what my mom thinks I do"&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/talks/dr-hopper-quit-phd-data-science/dd2.png"
alt="What my CEO thinks I do vs. what my manager thinks I do"&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/talks/dr-hopper-quit-phd-data-science/dd3.png"
alt="What I thought I would do vs. what I tell people on Twitter I do"&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/talks/dr-hopper-quit-phd-data-science/dd4.png"
alt="What I actually do"&gt;
&lt;/figure&gt;
&lt;p&gt;I conclude by listing things I learned in school but don&amp;rsquo;t use every day:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linear Algebra&lt;/li&gt;
&lt;li&gt;Probability&lt;/li&gt;
&lt;li&gt;Statistics&lt;/li&gt;
&lt;li&gt;Machine Learning&lt;/li&gt;
&lt;li&gt;Math Modeling&lt;/li&gt;
&lt;li&gt;Data Structures&lt;/li&gt;
&lt;li&gt;Algorithms&lt;/li&gt;
&lt;li&gt;Distributed Systems&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And things I wasn&amp;rsquo;t taught in school but use daily:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Communication&lt;/li&gt;
&lt;li&gt;Careful Thought&lt;/li&gt;
&lt;li&gt;Prose Writing Skill&lt;/li&gt;
&lt;li&gt;Software Writing Skill&lt;/li&gt;
&lt;li&gt;Software Engineering&lt;/li&gt;
&lt;li&gt;Tenacity&lt;/li&gt;
&lt;li&gt;Stack Overflow&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Your Old Tweets from This Day</title><link>https://tdhopper.com/blog/your-old-tweets-from-this-day/</link><pubDate>Tue, 24 Jan 2017 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/your-old-tweets-from-this-day/</guid><description>&lt;p&gt;A while ago, I &lt;a class="link" href="https://gist.github.com/tdhopper/fccc4ab00a07cb290f1812d8ac5cc5f3" target="_blank" rel="noopener"
&gt;published a Bash script&lt;/a&gt; that will open a Twitter search page to show your old tweets from this day of the year. I have enjoyed using it to &lt;a class="link" href="https://twitter.com/tdhopper/status/559005293808746497" target="_blank" rel="noopener"
&gt;see&lt;/a&gt; &lt;a class="link" href="https://twitter.com/tdhopper/status/426718234461872130" target="_blank" rel="noopener"
&gt;what&lt;/a&gt; I was &lt;a class="link" href="https://twitter.com/tdhopper/status/691088342499381250" target="_blank" rel="noopener"
&gt;thinking&lt;/a&gt; &lt;a class="link" href="https://twitter.com/tdhopper/status/426702495361269760" target="_blank" rel="noopener"
&gt;about&lt;/a&gt; in &lt;a class="link" href="https://twitter.com/tdhopper/status/294300532770738176" target="_blank" rel="noopener"
&gt;days&lt;/a&gt; &lt;a class="link" href="https://twitter.com/tdhopper/status/29641415810088961" target="_blank" rel="noopener"
&gt;gone&lt;/a&gt; by.&lt;/p&gt;
&lt;p&gt;So I turned this into a Twitter account.&lt;/p&gt;
&lt;blockquote class="twitter-tweet" data-lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;Follow me to have a link tweeted @ you each day that will show your old tweets from that day of the year. &lt;a href="https://twitter.com/hashtag/experimental?src=hash"&gt;#experimental&lt;/a&gt;&lt;/p&gt;&amp;mdash; On This Day (@your_old_tweets) &lt;a href="https://twitter.com/your_old_tweets/status/819196698044014592"&gt;January 11, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;If you follow &lt;a class="link" href="https://twitter.com/your_old_tweets" target="_blank" rel="noopener"
&gt;@your_old_tweets&lt;/a&gt;, it&amp;rsquo;ll tweet a link at you each day that will show you your old tweets from the day. It attempts to send it in the morning (assuming you have your &lt;a class="link" href="https://twitter.com/settings/account" target="_blank" rel="noopener"
&gt;timezone set&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;This runs on Amazon Lambda. The code is &lt;a class="link" href="https://github.com/tdhopper/your_old_tweets" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Compare RSA Key with Fingerprint in Github</title><link>https://tdhopper.com/blog/compare-rsa-key-with-fingerprint-in-github/</link><pubDate>Fri, 06 Jan 2017 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/compare-rsa-key-with-fingerprint-in-github/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Compare RSA Key with Fingerprint in Github" /&gt;&lt;p&gt;When you add an SSH key to your Github account, Github shows you the hexadecimal form of the MD5 hash of your public key.&lt;/p&gt;
&lt;p&gt;If you ever need to compare that against a key file on your computer, you can run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh-keygen -E md5 -lf ~/.ssh/id_rsa.pub
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;I learned this from &lt;a class="link" href="http://stackoverflow.com/a/32130465/982745" target="_blank" rel="noopener"
&gt;StackOverflow&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>The perils of thinkism</title><link>https://tdhopper.com/blog/the-perils-of-thinkism/</link><pubDate>Sat, 24 Dec 2016 15:01:00 +0000</pubDate><guid>https://tdhopper.com/blog/the-perils-of-thinkism/</guid><description>&lt;img src="https://tdhopper.com/images/reading.png" alt="Featured image of post The perils of thinkism" /&gt;&lt;p&gt;From &lt;a class="link" href="https://lemire.me/blog/2016/12/20/what-is-a-useful-theory/" target="_blank" rel="noopener"
&gt;Daniel Lemire&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I was an adept, as a teenager and a young adult, of thinkism. Thinkism is the idea that intelligence alone can solve problems. I thought I was smart so that I could just sit down and solve important problems. One after the other. Whatever contributions I ended up making had little to do with sitting down and thinking hard… and much more to do with getting dirty.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Tallest Data Scientist</title><link>https://tdhopper.com/blog/tallest-data-scientist/</link><pubDate>Thu, 08 Dec 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/tallest-data-scientist/</guid><description>&lt;img src="https://tdhopper.com/images/datascientist.png" alt="Featured image of post Tallest Data Scientist" /&gt;&lt;p&gt;I was honored to &lt;a class="link" href="http://adversariallearning.com/episode-2-the-tallest-data-scientist.html" target="_blank" rel="noopener"
&gt;join my friends Joel and Andrew on the Adversarial Learning&lt;/a&gt; podcast to talk about my career in data science (and what it&amp;rsquo;s like to be the tallest one).&lt;/p&gt;
&lt;iframe src="https://podcasters.spotify.com/pod/show/adversarial-learning/embed/episodes/Episode-Two-The-Tallest-Data-Scientist-eooh8a/a-a4acm9p" height="102px" width="100%" frameborder="0" scrolling="no"&gt;&lt;/iframe&gt;</description></item><item><title>Get Pykafka to work with rdkafka on Linux</title><link>https://tdhopper.com/blog/get-pykafka-to-work-with-rdkafka-on-linux/</link><pubDate>Fri, 18 Nov 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/get-pykafka-to-work-with-rdkafka-on-linux/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Get Pykafka to work with rdkafka on Linux" /&gt;&lt;p&gt;My former colleague&amp;rsquo;s from Parse.ly wrote the fantastic &lt;a class="link" href="https://github.com/Parsely/pykafka" target="_blank" rel="noopener"
&gt;pykafka&lt;/a&gt; library with an optional c-backed using &lt;a class="link" href="https://github.com/edenhill/librdkafka" target="_blank" rel="noopener"
&gt;rdkafka&lt;/a&gt;. I&amp;rsquo;ve had trouble getting it to work, and here are a few things I&amp;rsquo;ve learned:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The version of rdkafka installable with apt-get was out of data, and pykafka couldn&amp;rsquo;t find the headers it need. I instead used the simple &lt;a class="link" href="https://github.com/edenhill/librdkafka#building" target="_blank" rel="noopener"
&gt;build instructions&lt;/a&gt; in the rdkafka README to build it from head.&lt;/li&gt;
&lt;li&gt;I was getting the error &lt;code&gt;ImportError: librdkafka.so.1: cannot open shared object file: No such file or directory&lt;/code&gt; when trying to use rdkafka from Pykafka. It could be set in the short term by using &lt;code&gt;LD_LIBRARY_PATH=/usr/local/lib&lt;/code&gt;. However, I fixed it permanently by running &lt;a class="link" href="https://linux.die.net/man/8/ldconfig" target="_blank" rel="noopener"
&gt;&lt;code&gt;sudo ldconfig&lt;/code&gt;&lt;/a&gt; after building rdkafka.&lt;/li&gt;
&lt;li&gt;Pykafka has to be installed &lt;em&gt;after&lt;/em&gt; building rdkafka. At the moment, Pykafka tries to build a C-extension to connect to rdkafka, and if that fails, it will install without offering the rdkafka backend. Check the output of &lt;code&gt;pip install pykafka&lt;/code&gt; &lt;a class="link" href="https://github.com/Parsely/pykafka/blob/1b946fa288dd5eb278f397e858e887146e048e47/setup.py#L164-L172" target="_blank" rel="noopener"
&gt;to see if the rdkafka extension built&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Data Scientists Need More Automation</title><link>https://tdhopper.com/blog/data-scientists-need-more-automation/</link><pubDate>Thu, 17 Nov 2016 02:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/data-scientists-need-more-automation/</guid><description>&lt;p&gt;Many data scientists aren&amp;rsquo;t lazy enough.&lt;/p&gt;
&lt;p&gt;Whether we are managing production services or running computations on AWS
machines, many data scientists are working on computers besides their laptops.&lt;/p&gt;
&lt;p&gt;For me, this often takes the form of SSH-ing into remote boxes, manually
configuring the system with a combination of apt installs, &lt;a class="link" href="http://conda.pydata.org/docs/using/envs.html" title="Managing
environments — Conda documentation"
target="_blank" rel="noopener"
&gt;Conda
environments&lt;/a&gt;, and bash scripts.&lt;/p&gt;
&lt;p&gt;To run my service or scripts, I open a &lt;a class="link" href="https://tmux.github.io/" title="tmux"
target="_blank" rel="noopener"
&gt;tmux&lt;/a&gt;
window, activate my virtual environement, and start the process. (When I have
to do this on multiple machines, I&amp;rsquo;m occasionally clever enough to use tmux to
broadcast the commands to multiple terminal windows.)&lt;/p&gt;
&lt;p&gt;When I need to check my logs or see the output, I SSH back into each box,
reconnect to tmux (after I remember the name of my session), and tail my logs.
When running on multiple boxes, I repeat this process N times. If I need to
restart a process, I flip through my tmux tabs until I find the correct
process, kill it with a Ctrl-C, and use the up arrow to reload the last run
command.&lt;/p&gt;
&lt;p&gt;All of this works, of course. &lt;a class="link" href="https://xkcd.com/1319/" target="_blank" rel="noopener"
&gt;And as we all know&lt;/a&gt;, &lt;a class="link" href="http://xkcd.com/974/" target="_blank" rel="noopener"
&gt;a
simple solution that works&lt;/a&gt; &lt;a class="link" href="https://xkcd.com/1445/" target="_blank" rel="noopener"
&gt;can be
preferable&lt;/a&gt; to a fragile solution that requires
constant maintenance. That said, I suspect many of us aren&amp;rsquo;t lazy enough. We
don&amp;rsquo;t spend enough time automating tasks and processes. Even when we don&amp;rsquo;t
save time by doing it, we may save &lt;a class="link" href="http://www.johndcook.com/blog/2015/12/22/automate-to-save-mental-energy-not-time/" target="_blank" rel="noopener"
&gt;mental overhead&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I recently introduced several colleagues to some Python-based tools that can
help. &lt;a class="link" href="http://www.fabfile.org/" target="_blank" rel="noopener"
&gt;Fabric&lt;/a&gt; is a &amp;ldquo;library and command-line tool
for streamlining the use of SSH for application deployment or systems
administration tasks.&amp;rdquo; Fabric allows you to encapsulate sequences of commands
as you might with a Makefile. It&amp;rsquo;s killer feature is the ease with which it
lets you execute those commands on remote machines over SSH. With Fabric, you
could tail all the logs on all your nodes with a single command executed in
your local terminal. There are &lt;a class="link" href="https://www.youtube.com/results?search_query=python&amp;#43;fabric" target="_blank" rel="noopener"
&gt;a number of talks about Fabric on
Youtube&lt;/a&gt; if you
want to learn more. One of my colleagues reduced his daily workload by writing
his system management tasks into a Fabric file.&lt;/p&gt;
&lt;p&gt;Another great tool is &lt;a class="link" href="http://supervisord.org/" target="_blank" rel="noopener"
&gt;Supervisor&lt;/a&gt;. If you run long
running processes in tmux/screen/nohup, Supervisor might be for you. It allows
you to define the tasks you want to run in an INI file and &amp;ldquo;provides you with
one place to start, stop, and monitor your processes&amp;rdquo;. Supervisor will log the
stdout and stderr to a log location of your choice. It can be a little
confusing to set up, but will likely make your life easier in the longer run.&lt;/p&gt;
&lt;p&gt;A tool I want to learn but haven&amp;rsquo;t is &lt;a class="link" href="https://www.ansible.com/" target="_blank" rel="noopener"
&gt;Ansible&lt;/a&gt;, &amp;ldquo;a
free-software platform for configuring and managing computers which combines
multi-node software deployment, ad hoc task execution, and configuration
management&amp;rdquo;. Unlike Chef and Puppet, Ansible doesn&amp;rsquo;t require an agent on the
systems you need to configure; it does all the configuration over SSH. You can
use Ansible to configure your systems and install your dependencies, even
&lt;a class="link" href="https://github.com/zenoamaro/ansible-supervisord" target="_blank" rel="noopener"
&gt;Supervisor&lt;/a&gt;! Ansible is
written in Python and, mercifully, doesn&amp;rsquo;t require learning a Ruby-based DSL
(as does Chef).&lt;/p&gt;
&lt;p&gt;Recently I&amp;rsquo;ve been thinking that Fabric, Supervisor, and Ansible combined
become a powerful toolset for management and configuration of data science
systems. Each tool is also open source and can be installed in a few minutes.
Each tool is well documented and offers helpful tutorials on getting started;
however, learning to use them effectively may require some effort.&lt;/p&gt;
&lt;p&gt;I would love to see someone create training materials on these tools (and
others!) focused on how data scientists can take improve their system
management, configuration, and operations. A screencast series may be the
perfect thing. Someone please help data scientists be lazier, do less work,
and reduce the mental overhead of dealing with computers!&lt;/p&gt;</description></item><item><title>Filter by date in a Pandas MultiIndex</title><link>https://tdhopper.com/blog/filter-by-date-in-a-pandas-multiindex/</link><pubDate>Tue, 08 Nov 2016 22:17:00 +0000</pubDate><guid>https://tdhopper.com/blog/filter-by-date-in-a-pandas-multiindex/</guid><description>&lt;p&gt;I always forget how to do this.&lt;/p&gt;
&lt;p&gt;The pandas &lt;code&gt;DataFrame.loc&lt;/code&gt; method allows for &lt;em&gt;label&lt;/em&gt; -based filtering of data
frames. The &lt;a class="link" href="http://pandas.pydata.org/pandas-docs/stable/advanced.html#advanced-indexing-with-hierarchical-index" target="_blank" rel="noopener"
&gt;Pandas docs&lt;/a&gt; show how
it can be used to filter a &lt;code&gt;MultiIndex&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;In [39]: df
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Out[39]:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; A B C
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;first second
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bar one 0.895717 0.410835 -1.413681
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; two 0.805244 0.813850 1.607920
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;baz one -1.206412 0.132003 1.024180
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; two 2.565646 -0.827317 0.569605
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;foo one 1.431256 -0.076467 0.875906
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; two 1.340309 -1.187678 -2.211372
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;qux one -1.170299 1.130127 0.974466
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; two -0.226169 -1.436737 -2.006747
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;In [40]: df.loc[&amp;#39;bar&amp;#39;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Out[40]:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; A B C
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;second
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;one 0.895717 0.410835 -1.413681
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;two 0.805244 0.813850 1.607920
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;In [41]: df.loc[&amp;#39;bar&amp;#39;, &amp;#39;two&amp;#39;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Out[41]:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;A 0.805244
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;B 0.813850
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;C 1.607920
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Name: (bar, two), dtype: float64
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;It turns out you can easily use it to filter a &lt;code&gt;DateTimeIndex&lt;/code&gt; level by a
single date with &lt;code&gt;df['2016-11-07']&lt;/code&gt; or a range of dates with
&lt;code&gt;df['2016-11-07:2016-11-11']&lt;/code&gt;. This applies whether or not its a &lt;code&gt;MultiIndex&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you get an error like &lt;code&gt;KeyError: 'Key length (1) was greater than MultiIndex lexsort depth (0)'&lt;/code&gt;, it&amp;rsquo;s because &amp;ldquo;MultiIndex Slicing requires the
index to be fully lexsorted&amp;rdquo;. You may fix your problem by calling &lt;code&gt;df = df.sort_index()&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Speeding up PyMC3 NUTS Sampler</title><link>https://tdhopper.com/blog/speeding-up-pymc3-nuts-sampler/</link><pubDate>Tue, 08 Nov 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/speeding-up-pymc3-nuts-sampler/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Speeding up PyMC3 NUTS Sampler" /&gt;&lt;p&gt;I&amp;rsquo;m trying to use the &lt;a class="link" href="https://pymc-devs.github.io/pymc3/api.html?highlight=nuts#module-pymc3.step_methods.nuts" target="_blank" rel="noopener"
&gt;NUTS sampler&lt;/a&gt; in &lt;a class="link" href="https://github.com/pymc-devs/pymc3" title="GitHub - pymc-devs/pymc3: Probabilistic Programming in Python. Uses Theano as a backend, supports NUTS and ADVI."
target="_blank" rel="noopener"
&gt;PyMC3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;However, it was running at 2 iterations per second on my model, while the Metropolis Hastings sampler ran 450x faster.&lt;/p&gt;
&lt;p&gt;I showed my example to some of the PyMC3 devs on Twitter, and &lt;a class="link" href="https://de.linkedin.com/in/thomas-wiecki-46339244" title="Thomas Wiecki"
target="_blank" rel="noopener"
&gt;Thomas Wiecki&lt;/a&gt; showed me this trick:&lt;/p&gt;
&lt;blockquote class="twitter-tweet" data-lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;&lt;a href="https://twitter.com/tdhopper"&gt;@tdhopper&lt;/a&gt; &lt;a href="https://twitter.com/Springcoil"&gt;@Springcoil&lt;/a&gt; You need pm.NUTS(scaling=np.power(model.dict_to_array(v_params.stds), 2), is_cov=True) (terrible syntax, I know).&lt;/p&gt;&amp;mdash; Thomas Wiecki (@twiecki) &lt;a href="https://twitter.com/twiecki/status/796007019941462016"&gt;November 8, 2016&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;It resulted in a 25x speedup of the NUTS sampler. The code looks like this&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# SETUP MODEL HERE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;elbo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;variational&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;advi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NUTS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scaling&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dict_to_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sds&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;niter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;is_cov&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Understanding Probabilistic Topic Models By Simulation</title><link>https://tdhopper.com/talks/understanding-probabilistic-topic-models-by-simulation/</link><pubDate>Tue, 25 Oct 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/talks/understanding-probabilistic-topic-models-by-simulation/</guid><description>&lt;img src="https://tdhopper.com/preso.png" alt="Featured image of post Understanding Probabilistic Topic Models By Simulation" /&gt;&lt;p&gt;I gave a talk last week at Research Triangle Analysts on understanding probabilistic topic models (specificly LDA) by using Python for simulation. Here&amp;rsquo;s the description:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Latent Dirichlet Allocation and related topic models are often presented in the form of complicated equations and confusing diagrams. Tim Hopper presents LDA as a generative model through probabilistic simulation in simple Python. Simulation will help data scientists to understand the model assumptions and limitations and more effectively use black box LDA implementations.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can watch the video on &lt;a class="link" href="https://www.youtube.com/watch?v=Wy-XhT2sHgM&amp;amp;feature=youtu.be" target="_blank" rel="noopener"
&gt;Youtube&lt;/a&gt;:&lt;/p&gt;
&lt;div class="video-wrapper"&gt;
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/Wy-XhT2sHgM"
allowfullscreen
title="YouTube Video"
&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;I gave a &lt;a class="link" href="https://www.youtube.com/watch?v=_R66X_udxZQ" target="_blank" rel="noopener"
&gt;shorter version of the talk at PyData NYC 2015&lt;/a&gt;.&lt;/p&gt;
&lt;div class="video-wrapper"&gt;
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/_R66X_udxZQ"
allowfullscreen
title="YouTube Video"
&gt;
&lt;/iframe&gt;
&lt;/div&gt;</description></item><item><title>Undersampled Radio Interview</title><link>https://tdhopper.com/blog/undersampled-radio/</link><pubDate>Mon, 24 Oct 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/undersampled-radio/</guid><description>&lt;p&gt;I was flattered to be asked to be on a burgeoning data science podcast called Undersampled Radio. You can listen on &lt;a class="link" href="https://www.youtube.com/watch?v=q4e_hBUd6zI&amp;amp;feature=youtu.be" target="_blank" rel="noopener"
&gt;YouTube&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Sharing Your Side Projects Online</title><link>https://tdhopper.com/talks/sharing-side-projects/</link><pubDate>Wed, 05 Oct 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/talks/sharing-side-projects/</guid><description>&lt;img src="https://tdhopper.com/preso.png" alt="Featured image of post Sharing Your Side Projects Online" /&gt;&lt;div class="video-wrapper"&gt;
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/uRul8QdYvqQ"
allowfullscreen
title="YouTube Video"
&gt;
&lt;/iframe&gt;
&lt;/div&gt;</description></item><item><title>Setting Up supervisord</title><link>https://tdhopper.com/blog/setting-up-supervisord/</link><pubDate>Tue, 20 Sep 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/setting-up-supervisord/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Setting Up supervisord" /&gt;&lt;p&gt;First run &lt;code&gt;$ sudo pip install supervisor&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Create &lt;code&gt;/etc/supervisord.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;supervisor&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;unix_http_server&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;supervisor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;chmod&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0700&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;sockef&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt; &lt;span class="mi"&gt;0700&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;supervisord&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;logfile&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;supervisor&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;supervisord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;CWD&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;supervisord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pidfile&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;supervisord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;supervisord&lt;/span&gt; &lt;span class="n"&gt;pidfile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt; &lt;span class="n"&gt;supervisord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;childlogdir&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;supervisor&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;AUTO&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;TEMP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;below&lt;/span&gt; &lt;span class="n"&gt;section&lt;/span&gt; &lt;span class="n"&gt;must&lt;/span&gt; &lt;span class="n"&gt;remain&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;RPC&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;supervisorctl&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt; &lt;span class="n"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;work&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;additional&lt;/span&gt; &lt;span class="n"&gt;interfaces&lt;/span&gt; &lt;span class="n"&gt;may&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;added&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;defining&lt;/span&gt; &lt;span class="n"&gt;them&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;separate&lt;/span&gt; &lt;span class="n"&gt;rpcinterface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sections&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rpcinterface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;supervisor&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;supervisor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpcinterface_factory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;supervisor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rpcinterface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;make_main_rpcinterface&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;supervisorctl&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;serverurl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;///&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;supervisor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;URL&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;section&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="n"&gt;just&lt;/span&gt; &lt;span class="n"&gt;contain&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;files&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;This&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;setting&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="n"&gt;multiple&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;separated&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;whitespace&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;newlines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;It&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="n"&gt;also&lt;/span&gt; &lt;span class="n"&gt;contain&lt;/span&gt; &lt;span class="n"&gt;wildcards&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;filenames&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;interpreted&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;relative&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Included&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cannot&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;include&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="n"&gt;themselves&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;include&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;supervisor&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;/*.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;program&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;program1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;program1command&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;autostart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;stderr_logfile&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;program1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;stdout_logfile&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;program1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Now run &lt;code&gt;$ sudo supervisord -c /etc/supervisord.conf&lt;/code&gt;. See the status with &lt;code&gt;$ sudo supervisorctl status&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After modifying the config, run modify the config, &lt;code&gt;$ sudo supervisorctl reread &amp;amp; sudo supervisorctl update&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Entropy of a Discrete Probability Distribution</title><link>https://tdhopper.com/blog/entropy-of-a-discrete-probability-distribution/</link><pubDate>Mon, 05 Sep 2016 16:05:00 +0000</pubDate><guid>https://tdhopper.com/blog/entropy-of-a-discrete-probability-distribution/</guid><description>&lt;img src="https://tdhopper.com/entropy.png" alt="Featured image of post Entropy of a Discrete Probability Distribution" /&gt;&lt;p&gt;Suppose we have a discrete set of possible events \(1,\ldots, n\) that occur with probabilities \( (p_1, p_2, \ldots, p_n)\). Claude Shannon asked the question&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Can we find a measure of how much &amp;ldquo;choice&amp;rdquo; is involved in the selection of the event or of how uncertain we are of the outcome?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For example, suppose we have a coin that lands on heads with probability \(p\) and tails with probability \(1-p\). If \(p=1\), the coin always lands on heads. Since there is no uncertainty, we might want to say the uncertainty is 0. However, if the coin is fair and \(p=0.5\), we maximize our uncertainty: it&amp;rsquo;s a complete tossup whether the coin is heads or tails. We might want to say the uncertainty in this case is 1.&lt;/p&gt;
&lt;p&gt;In general, Shannon wanted to devise a function \(H(p_1, p_2, \ldots, p_n)\) describing the uncertainty of an arbitrary set of discrete events (i.e. a \(n\)-sided die). He thought that &amp;ldquo;it is reasonable&amp;rdquo; that \(H\) should have three properties:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;\(H\) should be a continuous function of each \(p_i\). A small change in a single probability should result in a similarly small entropy (uncertainty) change.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If each event is equally likely (\(p_i=\frac{1}{n}\)), \(H\) should increase as a function of \(n\): the more events there are, the more uncertain we are.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, entropy should be additive for independent events. Suppose we generate a random variable \(x\) by the following process: Flip a fair coin. If it is heads, \(x=0\). However, if the flip was tails, flip the coin again (an &lt;em&gt;independent&lt;/em&gt; event from the first flip). If the second flip is heads, \(x=1\), if tails \(x=2\). These three outcomes occur with probability $1/2$, $1/4$, and $1/4$, respectively.&lt;/p&gt;
&lt;p&gt;We can compute the entropy of $x$ as \(H(p_0=1/2, p_1=1/4, p_2=1/4)\). By the independence property, this relationship holds:&lt;/p&gt;
&lt;p&gt;\[H\left(\frac{1}{2}, \frac{1}{4}, \frac{1}{4}\right)=H\left(\frac{1}{2}, \frac{1}{2}\right) + \frac{1}{2} H\left(\frac{1}{2}, \frac{1}{2}\right).\]&lt;/p&gt;
&lt;p&gt;As &lt;a class="link" href="http://www.inference.phy.cam.ac.uk/itprnn/book.pdf" target="_blank" rel="noopener"
&gt;David MacKay explains&lt;/a&gt;, this is the general claim that&lt;/p&gt;
&lt;p&gt;\[ H(\mathbf{p})=H(p_1, 1-p_1)+(1-p_1)H\left(\frac{p_2}{1-p_1}, \frac{p_3}{1-p_1}, \ldots, \frac{p_n}{1-p_1}\right).\]&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Shannon showed that given these three assumptions, there is a unique form that \(H\) must take:&lt;/p&gt;
$$ H\propto -\sum_{i=1}^n p_i \log p_i=\sum_{i=1}^n p_i \log \frac{1}{p_i}. $$&lt;p&gt;He named this measure of uncertainty &lt;em&gt;entropy&lt;/em&gt;, because the form of \(H\) bears striking similarity to that of &lt;a class="link" href="https://en.wikipedia.org/wiki/Entropy_%28statistical_thermodynamics%29#Gibbs_Entropy_Formula" target="_blank" rel="noopener"
&gt;Gibbs Entropy&lt;/a&gt; in statistical thermodynamics.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Shannon observes that \(H\) has many other interesting properties:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Entropy \(H\) is 0 if and only if exactly one event has probability 1 and the rest have probability 0. (Uncertainty vanishes only when we are certain about the outcomes.)&lt;/li&gt;
&lt;li&gt;Entropy \(H\) is maximized when the \(p_i\) values are equal.&lt;/li&gt;
&lt;li&gt;The joint entropy of two events is less than or equal to the sum of the individual entropies. \(H(x, y)=H(x)+H(y)\) only when \(x\) and \(y\) are independent events.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can read more about this in Shannon&amp;rsquo;s seminal paper &lt;a class="link" href="http://ieeexplore.ieee.org/xpl/articleDetails.jsp?reload=true&amp;amp;arnumber=6773024" target="_blank" rel="noopener"
&gt;A Theory of Mathematical Communication&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Caianiello and Aizerman &lt;a class="link" href="https://books.google.com/books?id=ak6qCAAAQBAJ&amp;amp;lpg=PP1&amp;amp;ots=bzAoIDJk1K&amp;amp;dq=Topics%20in%20the%20General%20Theory%20of%20Structures&amp;amp;pg=PA200#v=onepage&amp;amp;q=cross%20entropy&amp;amp;f=false" target="_blank" rel="noopener"
&gt;say&lt;/a&gt; the name entropy is thanks to von Neumann who said&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You should call it entropy, for two reasons. In the first place your uncertainty function has been used in statistical mechanics under that name, so it already has a name. In the second place, and more important, nobody knows what entropy really is, so in a debate you will always have the advantage.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They argue that the name &amp;ldquo;uncertainty&amp;rdquo; would have been much more helpful since &amp;ldquo;Shannon entropy is simply and avowedly the &amp;lsquo;measure of the uncertainty inherient in a pre-assigned probability scheme.&amp;rsquo;&amp;rdquo;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Cross Entropy and KL Divergence</title><link>https://tdhopper.com/blog/cross-entropy-and-kl-divergence/</link><pubDate>Mon, 05 Sep 2016 14:12:00 +0000</pubDate><guid>https://tdhopper.com/blog/cross-entropy-and-kl-divergence/</guid><description>&lt;p&gt;&lt;a class="link" href="https://tdhopper.com/blog/entropy-of-a-discrete-probability-distribution/" target="_blank" rel="noopener"
&gt;As we saw in an earlier post&lt;/a&gt;, the entropy of a discrete probability distribution is defined to be&lt;/p&gt;
$$H(p)=H(p\_1,p\_2,\ldots,p\_n)=-\sum\_{i}p\_i \log p\_i.$$&lt;p&gt;Kullback and Leibler defined a similar measure now known as &lt;em&gt;KL divergence&lt;/em&gt;. This measure quantifies how similar a probability distribution $p$ is to a candidate distribution $q$.&lt;/p&gt;
$$D_{\text{KL}}(p\ | q)=\sum_i p_i \log \frac{p_i}{q_i}.$$&lt;p&gt;$D_\text{KL}$ is non-negative and zero if and only if $ p_i = q_i $ for all $i$. However, it is important to note that it is not in general symmetric:&lt;/p&gt;
$$ D_{\text{KL}}(p\| q) \neq D_{\text{KL}}(q\| p).$$&lt;p&gt;&lt;a class="link" href="http://arxiv.org/pdf/1404.2000v1.pdf" target="_blank" rel="noopener"
&gt;Jonathon Shlens explains&lt;/a&gt; that KL Divergence can be interpreted as measuring the likelihood that samples represented by the empirical distribution $p$ were generated by a fixed distribution $q$. If $D_{\text{KL}}(p\| q)=0$, we can guarantee that $p$ is generated by $q$. As $D_{\text{KL}}(p\| q)\rightarrow\infty$, we can say that it is increasingly unlikely that $p$ was generated by $q$.&lt;/p&gt;
&lt;p&gt;Algebraically, we can rewrite the definition as&lt;/p&gt;
$$ \begin{array}{rl} D_{\text{KL}}(p\| q) &amp;=\sum_i p_i \log \frac{p_i}{q_i} \\\\ &amp;=\sum_i \left ( - p_i \log q_i + p_i \log p_i \right)\\\\ &amp;=- \sum_i p_i \log q_i + \sum_i p_i \log p_i \\\\ &amp;=- \sum_i p_i \log q_i - \sum_i p_i \log \frac{1}{p_i} \\\\ &amp;=- \sum_i p_i \log q_i-H(p) \\\\ &amp;=\sum_i p_i \log \frac{1}{q_i}-H(p)\\\\ \end{array} $$&lt;p&gt;KL Divergence breaks down as something that looks similar to entropy (but combining $p$ and $q$) minus the entropy of $p$. This first term is often called &lt;em&gt;cross entropy&lt;/em&gt;:&lt;/p&gt;
$$H(p, q)=\sum_i p_i \log \frac{1}{q_i}.$$&lt;p&gt;We could alternatively use this relationship to define cross entropy as:&lt;/p&gt;
$$H(p, q)=H(p) + D_\text{KL}(p\| q).$$&lt;p&gt;Intuitively, the cross entropy is the uncertainty implicit in $H(p)$ plus the likelihood that $p$ could have be generated by $q$. If we consider $p$ to be a fixed distribution, $H(p, q)$ and $D_\text{KL}(p \| q)$ differ by a constant factor for all $q$.&lt;/p&gt;</description></item><item><title>Photos Featured on Smithsonian Magazine</title><link>https://tdhopper.com/blog/photos-featured-on-smithsonian-magazine/</link><pubDate>Mon, 25 Jul 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/photos-featured-on-smithsonian-magazine/</guid><description>&lt;p&gt;A few weeks ago, I introduced my wife to backpacking in the beautiful &lt;a class="link" href="http://www.dcr.virginia.gov/state-parks/grayson-highlands" title="Grayson Highlands State Park"
target="_blank" rel="noopener"
&gt;Grayson Highlands State Park&lt;/a&gt; in southestern Virginia. Part of my reason for picking this location was to see the herd of wild ponies that life at 5000&amp;rsquo; on the grassy balds.&lt;/p&gt;
&lt;p&gt;I shared some of my best pictures from the trip on Flicker under a Creative Commons license (CC BY-NC-ND 2.0). On Saturday, I stumbled acrosss &lt;a class="link" href="http://www.smithsonianmag.com/travel/more-than-100-ponies-roam-free-this-park-virginia-180959786/" target="_blank" rel="noopener"
&gt;an article about the Grayson Highlands ponies on the Smithsonian Magazine website&lt;/a&gt;. I was pleasantly surprised to see they selected two of my images for the story! I&amp;rsquo;ve been spending more time lately exploring my &lt;a class="link" href="https://twitter.com/tdhopper/status/43876534687248384" target="_blank" rel="noopener"
&gt;longtime interest in wildlife photography&lt;/a&gt;, and I&amp;rsquo;m thrilled to see others sharing my work.&lt;/p&gt;
&lt;p&gt;You can find more of my photography on &lt;a class="link" href="https://photos.tdhopper.com" target="_blank" rel="noopener"
&gt;photos.tdhopper.com&lt;/a&gt; or &lt;a class="link" href="https://www.instagram.com/tdhopper/" target="_blank" rel="noopener"
&gt;Instagram&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/grayson1.png"&gt;&lt;figcaption&gt;
&lt;h4&gt;Ponies at Grayson Highlands State Park&lt;/h4&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/grayson2.png"&gt;&lt;figcaption&gt;
&lt;h4&gt;Ponies at Grayon Highlands State Park&lt;/h4&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;</description></item><item><title>Find Files Modified in last N Minutes</title><link>https://tdhopper.com/blog/find-files-modified-in-last-n-minutes/</link><pubDate>Wed, 22 Jun 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/find-files-modified-in-last-n-minutes/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Find Files Modified in last N Minutes" /&gt;&lt;p&gt;I&amp;rsquo;ve been using Bash&amp;rsquo;s &lt;a class="link" href="http://www.freebsd.org/cgi/man.cgi?find%281%29" target="_blank" rel="noopener"
&gt;&lt;code&gt;find&lt;/code&gt;&lt;/a&gt; command a lot more regularly lately. I&amp;rsquo;ve always been scared off by its syntax, but it&amp;rsquo;s great once you&amp;rsquo;ve learned it.&lt;/p&gt;
&lt;p&gt;Today I learned how to filter the results by files changed in the last N minutes: the &lt;code&gt;cmin&lt;/code&gt; flag:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -cmin [-|+]n
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; True if the difference between the time of last change of file
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; status information and the time find was started, rounded up to
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; the next full minute, is more than n (+n), less than n (-n), or
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; exactly n minutes ago.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find . -cmin +5 # Files modified more than 5 minutes ago
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find . -cmin -5 # Files modified less than than 5 minutes ago
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find . -cmin 5 # Files modified exactly 5 minutes ago
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Configuring Impala Query Results with impalarc</title><link>https://tdhopper.com/blog/configuring-impala-query-results-with-impalarc/</link><pubDate>Wed, 15 Jun 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/configuring-impala-query-results-with-impalarc/</guid><description>&lt;p&gt;I routinely run Impala queries on a &lt;a class="link" href="http://tdhopper.com/notes/faster-ssh-access-to-remote-computers" target="_blank" rel="noopener"
&gt;remote machine&lt;/a&gt; and want the results to save to a CSV file on that machine.&lt;/p&gt;
&lt;p&gt;I recently realized that I should setup an Impala config file on that machine to configure the connection to the Impala cluster and the output file format.&lt;/p&gt;
&lt;p&gt;So I created a text file at &lt;code&gt;~/.impalarc&lt;/code&gt; with the following settings:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[impala]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;impalad=CLUSTER-ADDRESS:PORT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;output_delimiter=,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;verbose=true
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ignore_query_failure=false
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;default_db=prd
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;write_delimited=true
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;print_header=true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Now when I run a query with &lt;code&gt;impala-shell&lt;/code&gt;, I don&amp;rsquo;t have to specify the address of the cluster, or the various flags required to get a CSV with a header.&lt;/p&gt;
&lt;p&gt;Other impala-shell config options are specified &lt;a class="link" href="http://www.cloudera.com/documentation/archive/impala/2-x/2-0-x/topics/impala_shell_options.html" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Faster SSH Access to Remote Computers</title><link>https://tdhopper.com/blog/faster-ssh-access-to-remote-computers/</link><pubDate>Wed, 15 Jun 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/faster-ssh-access-to-remote-computers/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Faster SSH Access to Remote Computers" /&gt;&lt;p&gt;I routinely &lt;a class="link" href="https://en.wikipedia.org/wiki/SSH" title="Wikipedia Entry: SSH"
target="_blank" rel="noopener"
&gt;SSH&lt;/a&gt; into a company machine on Openstack to do work. Until yesterday, I&amp;rsquo;d use my Bash history to find the SSH command I needed to access it. I was reading &lt;a class="link" href="https://www.amazon.com/SSH-Mastery-OpenSSH-PuTTY-Tunnels/dp/1470069717" target="_blank" rel="noopener"
&gt;SSH Mastery&lt;/a&gt; in a plane yesterday and realized I&amp;rsquo;ve been foolish to neglect the power of &lt;code&gt;~/ssh/config&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I added this to my SSH config file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Host lab
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; HostName MACHINE.IP.ADDRESS
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; User tdhopper
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Port 22
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; IdentityFile ~/.ssh/id_rsa
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ForwardAgent yes
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; RemoteForward 52698 localhost:52698
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Now I can connect to the machine by typing &lt;code&gt;$ ssh lab&lt;/code&gt;. That&amp;rsquo;s it. I don&amp;rsquo;t have to provide the SSH key, username, or machine IP.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;RemoteForward&lt;/code&gt; bit also forwards a port to enable me to open files on the remote machine in my local SublimeText editor using &lt;a class="link" href="https://github.com/henrikpersson/rsub" target="_blank" rel="noopener"
&gt;rsub&lt;/a&gt;. This is great.&lt;/p&gt;</description></item><item><title>Using Conda with the Fish Shell</title><link>https://tdhopper.com/blog/using-conda-with-the-fish-shell/</link><pubDate>Wed, 04 May 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/using-conda-with-the-fish-shell/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Using Conda with the Fish Shell" /&gt;&lt;p&gt;I&amp;rsquo;m trying out the beautiful &lt;a class="link" href="https://fishshell.com/" target="_blank" rel="noopener"
&gt;Fish shell&lt;/a&gt;. I&amp;rsquo;ve been a Bash user up until now.&lt;/p&gt;
&lt;p&gt;I was bummed to see that &lt;code&gt;source activate ENV&lt;/code&gt; (for activating &lt;a class="link" href="https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html" target="_blank" rel="noopener"
&gt;Conda environments&lt;/a&gt; didn&amp;rsquo;t work natively in Fish.&lt;/p&gt;
&lt;p&gt;Apparently the &lt;a class="link" href="https://groups.google.com/a/continuum.io/d/msg/conda/8aaYESlLZI8/Ov1UcFhWCAAJ" target="_blank" rel="noopener"
&gt;Conda team is adding native support for Fish&lt;/a&gt;, but it&amp;rsquo;s not available as of 2016-05-04.&lt;/p&gt;
&lt;p&gt;There are several projects developed to help in the meantime:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="http://pypi.python.org/pypi/conda-workon/0.3.0" target="_blank" rel="noopener"
&gt;conda-workon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/fabioramponi/fish-conda-virtualenv" target="_blank" rel="noopener"
&gt;fish-conda-virtualenv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://gist.github.com/jiffyclub/9679788" target="_blank" rel="noopener"
&gt;condactivate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;m going to wait for the Conda update to be released.&lt;/p&gt;</description></item><item><title>Concatenate files with header row</title><link>https://tdhopper.com/blog/concatenate-files-with-header-row/</link><pubDate>Mon, 02 May 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/concatenate-files-with-header-row/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Concatenate files with header row" /&gt;&lt;p&gt;I needed to concatenate a bunch of CSV files while skipping the header row. There was a nice solution on &lt;a class="link" href="http://stackoverflow.com/questions/10103619/unix-merge-many-files-while-deleting-first-line-of-all-files" target="_blank" rel="noopener"
&gt;Stack Overflow&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find . -name &amp;#34;*.csv&amp;#34; | xargs -n 1 tail -n +2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;With the GNU version of &lt;code&gt;tail&lt;/code&gt; (sadly not the one installed on OS X by default), you can just use&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tail -q -n +2 *.csv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;or&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;awk &amp;#39;FNR != 1&amp;#39; *.csv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Searching the tweets of people you follow</title><link>https://tdhopper.com/blog/searching-the-tweets-of-people-you-follow/</link><pubDate>Mon, 02 May 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/searching-the-tweets-of-people-you-follow/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Searching the tweets of people you follow" /&gt;&lt;p&gt;This is more of a Today I Taught.&lt;/p&gt;
&lt;blockquote class="twitter-tweet" data-lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;Turns out, it is actually possible to search my timeline. Thanks &lt;a href="https://twitter.com/tdhopper"&gt;@tdhopper&lt;/a&gt; for the tip. &lt;a href="https://t.co/7IsD76qszD"&gt;pic.twitter.com/7IsD76qszD&lt;/a&gt;&lt;/p&gt;&amp;mdash; Ole Zorn (@olemoritz) &lt;a href="https://twitter.com/olemoritz/status/727148272675115008"&gt;May 2, 2016&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;I have a quick search in &lt;a class="link" href="https://www.alfredapp.com/" target="_blank" rel="noopener"
&gt;Alfred&lt;/a&gt; to search the tweets of people I follow. I launch Alfred with ⌘-Space and type &lt;code&gt;following {query}&lt;/code&gt;. Restricting a Twitter search to people you follow only requires adding the &lt;code&gt;s=follows&lt;/code&gt; query string to a search url:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://twitter.com/search?s=follows&amp;amp;q={query}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Testing whether a Python string contains an integer</title><link>https://tdhopper.com/blog/testing-whether-a-python-string-contains-an-integer/</link><pubDate>Fri, 29 Apr 2016 15:13:00 +0000</pubDate><guid>https://tdhopper.com/blog/testing-whether-a-python-string-contains-an-integer/</guid><description>&lt;p&gt;If you want to check whether a Python string is an integer, you can try
casting to an int with &lt;code&gt;int()&lt;/code&gt; and catching the &lt;code&gt;ValueError&lt;/code&gt; if it&amp;rsquo;s not an
integer:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;To check for &lt;em&gt;nonnegative&lt;/em&gt; integers, you can use the &lt;a class="link" href="https://docs.python.org/3.8/library/stdtypes.html#str.isdigit" target="_blank" rel="noopener"
&gt;&lt;code&gt;str.is_digit()&lt;/code&gt;
method&lt;/a&gt;. It
will &amp;ldquo;return true if all characters in the string are digits and there is at
least one character, false otherwise:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;123&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isdigit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;-123&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isdigit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Thanks to &lt;a class="link" href="https://twitter.com/trochee/status/726162607460114433" target="_blank" rel="noopener"
&gt;Jeremy Kahn for reminding
me&lt;/a&gt; that &lt;code&gt;isdigit&lt;/code&gt; only
detects positive integers.&lt;/p&gt;</description></item><item><title>Ultralight Backpacking for the Ultratall</title><link>https://tdhopper.com/blog/backpacking-for-the-very-tall/</link><pubDate>Fri, 29 Apr 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/backpacking-for-the-very-tall/</guid><description>&lt;p&gt;I created a single page website to collect notes on one of my other hobbies: &lt;a class="link" href="https://backpacking.tdhopper.com/" target="_blank" rel="noopener"
&gt;ultralight backpacking&lt;/a&gt;. In particular, notes on ultralight gear for the very tall.&lt;/p&gt;</description></item><item><title>Don't Buffer Python's stdout</title><link>https://tdhopper.com/blog/dont-buffer-pythons-stdout/</link><pubDate>Tue, 26 Apr 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/dont-buffer-pythons-stdout/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Don't Buffer Python's stdout" /&gt;&lt;p&gt;I was using &lt;code&gt;[tee](http://man7.org/linux/man-pages/man1/tee.1.html)&lt;/code&gt; with a long running Python process, but I wasn&amp;rsquo;t seeing any output. This is a result of Python buffering the stdout stream. You can run force Python to run in &lt;a class="link" href="https://docs.python.org/2/using/cmdline.html#cmdoption-u" target="_blank" rel="noopener"
&gt;unbuffered mode&lt;/a&gt; using the &lt;code&gt;-u&lt;/code&gt; flag at the command line.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Force stdin, stdout and stderr to be totally unbuffered. On systems where it matters, also put stdin, stdout and stderr in binary mode.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Econtalk</title><link>https://tdhopper.com/blog/econtalk/</link><pubDate>Wed, 13 Apr 2016 01:42:00 +0000</pubDate><guid>https://tdhopper.com/blog/econtalk/</guid><description>&lt;p&gt;Listening to Russ Roberts&amp;rsquo; &lt;a class="link" href="http://www.econtalk.org/" target="_blank" rel="noopener"
&gt;Econtalk&lt;/a&gt; podcast for
the last 5 years has given me a whole new perspective on the world. Roberts
has exposed me to a whole new way of economic thinking, refined my scientific
skepticism, and introduced me to copious topics and scholars.&lt;/p&gt;
&lt;p&gt;Here are episodes from over the years that I&amp;rsquo;ve particularly enjoyed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2006/06/the_economics_o_4.html" target="_blank" rel="noopener"
&gt;The Economics of Organ Donations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2006/07/hanushek_on_edu.html" target="_blank" rel="noopener"
&gt;Making Schools Better: A Conversation with Rick Hanushek&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2007/04/boudreaux_on_th.html" target="_blank" rel="noopener"
&gt;Boudreaux on the Economics of &amp;ldquo;Buy Local&amp;rdquo;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2011/01/deer_on_autism.html" target="_blank" rel="noopener"
&gt;Deer on Autism, Vaccination, and Scientific Fraud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2011/08/odonohoe_on_pot.html" target="_blank" rel="noopener"
&gt;O&amp;rsquo;Donohoe on Potato Chips and Salty Snacks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2012/02/david_owen_on_t.html" target="_blank" rel="noopener"
&gt;David Owen on the Environment, Unintended Consequences, and The Conundrum&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2012/04/cowen_on_food.html" target="_blank" rel="noopener"
&gt;Cowen on Food&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2012/12/lisa_turner_on.html" target="_blank" rel="noopener"
&gt;Lisa Turner on Organic Farming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2013/07/narlikar_on_fai.html" target="_blank" rel="noopener"
&gt;Narlikar on Fair Trade and Free Trade&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2014/03/velasquez-manof.html" target="_blank" rel="noopener"
&gt;Velasquez-Manoff on Autoimmune Disease, Parasites, and Complexity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2015/03/david_skarbek_o.html" target="_blank" rel="noopener"
&gt;David Skarbek on Prison Gangs and the Social Order of the Underworld&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2012/06/jonah_lehrer_on.html" target="_blank" rel="noopener"
&gt;Jonah Lehrer on Creativity and Imagine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2012/07/scott_atlas_on.html" target="_blank" rel="noopener"
&gt;Scott Atlas on American Health Care&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2014/08/terry_anderson.html" target="_blank" rel="noopener"
&gt;Terry Anderson on the Environment and Property Rights&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2015/04/leonard_wong_on.html" target="_blank" rel="noopener"
&gt;Leonard Wong on Honesty and Ethics in the Military&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2015/05/eric_topol_on_t.html" target="_blank" rel="noopener"
&gt;Eric Topol on the Power of Patients in a Digital World&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2015/06/adam_davidson_o_1.html" target="_blank" rel="noopener"
&gt;Adam Davidson on Hollywood and the Future of Work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2015/06/matt_ridley_on.html" target="_blank" rel="noopener"
&gt;Matt Ridley on Climate Change&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2015/07/lee_ohanian_arn.html" target="_blank" rel="noopener"
&gt;Lee Ohanian, Arnold Kling, and John Cochrane on the Future of Freedom, Democracy, and Prosperity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2015/08/rachel_laudan_o.html" target="_blank" rel="noopener"
&gt;Rachel Laudan on the History of Food and Cuisine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2015/09/william_macaski.html" target="_blank" rel="noopener"
&gt;William MacAskill on Effective Altruism and Doing Good Better&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2016/03/jayson_lusk_on.html" target="_blank" rel="noopener"
&gt;Jayson Lusk on Food, Technology, and Unnaturally Delicious&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One of my favorite guests is Duke economist Mike Munger. Here are some great
interviews with him:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2007/01/munger_on_price_1.html" target="_blank" rel="noopener"
&gt;Price Gouging&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2013/09/munger_on_milk.html" target="_blank" rel="noopener"
&gt;On Milk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2007/04/mike_munger_on.html" target="_blank" rel="noopener"
&gt;Division of Labor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.econtalk.org/archives/2008/10/munger_on_middl.html" target="_blank" rel="noopener"
&gt;Middlemen&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Incidentally, Priceonomics recently published a &lt;a class="link" href="http://priceonomics.com/russ-roberts-and-the-quest-to-make-economics/" target="_blank" rel="noopener"
&gt;great article&lt;/a&gt; on Roberts and Econtalk.&lt;/p&gt;</description></item><item><title>Column binding two Panda's Dataframes</title><link>https://tdhopper.com/blog/column-binding-two-pandas-dataframes/</link><pubDate>Mon, 11 Apr 2016 20:56:00 +0000</pubDate><guid>https://tdhopper.com/blog/column-binding-two-pandas-dataframes/</guid><description>&lt;p&gt;Joining two Pandas DataFrames with an equal number of rows is slightly harder
than it appears. In R, you just use the &lt;code&gt;cbind&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;As &lt;a class="link" href="http://stackoverflow.com/questions/33088010/pandas-column-bind-cbind-two-data-frames" target="_blank" rel="noopener"
&gt;this&lt;/a&gt; StackOverflow question shows, in Pandas it&amp;rsquo;s easy to
end up with something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;unique_id lacet_number latitude longitude
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;0 NaN NaN -93.193560 31.217029
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1 NaN NaN -93.948082 35.360874
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2 NaN NaN -103.131508 37.787609
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;15 5570613 TLA-0138365 NaN NaN
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;24 5025490 EMP-0138757 NaN NaN
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;36 4354431 DXN-0025343 NaN NaN
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This results from the indices not being identical. Frustratingly (to me) the
&lt;code&gt;ignore_index&lt;/code&gt; argument doesn&amp;rsquo;t give the 3-rowed DataFrame I&amp;rsquo;d hope it gives.&lt;/p&gt;
&lt;p&gt;As the &lt;a class="link" href="http://stackoverflow.com/a/33088410/982745" target="_blank" rel="noopener"
&gt;accepted answer&lt;/a&gt; on that
question shows, the thing to do is reset the indices on the DataFrames before
concatenating:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;df_a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reset_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;df_b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reset_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Printing Pandas Data Frames as HTML in Jupyter Notebooks</title><link>https://tdhopper.com/blog/printing-pandas-data-frames-as-html-in-jupyter-notebooks/</link><pubDate>Wed, 23 Mar 2016 14:18:00 +0000</pubDate><guid>https://tdhopper.com/blog/printing-pandas-data-frames-as-html-in-jupyter-notebooks/</guid><description>&lt;img src="https://tdhopper.com/panda.png" alt="Featured image of post Printing Pandas Data Frames as HTML in Jupyter Notebooks" /&gt;&lt;p&gt;Jupyter notebooks nicely render Pandas data frames if they&amp;rsquo;re the last line in
a cell. It renders the HTML version of the data frame returned by
&lt;code&gt;pandas.DataFrame.to_html()&lt;/code&gt;. However, if you call &lt;code&gt;print(df)&lt;/code&gt; in a cell, the
data frame is rendered in less readable text-based output.&lt;/p&gt;
&lt;p&gt;Despite using Notebooks regularly for years, I&amp;rsquo;d never bothered to figure out
a way around this. However, the solution is easy.&lt;/p&gt;
&lt;p&gt;Instead of &lt;code&gt;print(df)&lt;/code&gt; you use&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;IPython.display&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;display&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Impala Doesn't Have a histogram() function</title><link>https://tdhopper.com/blog/impala-doesnt-have-a-histogram-function/</link><pubDate>Wed, 23 Mar 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/impala-doesnt-have-a-histogram-function/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Impala Doesn't Have a histogram() function" /&gt;&lt;p&gt;&lt;a class="link" href="https://groups.google.com/a/cloudera.org/d/msg/impala-user/O8u5JFfKJPk/V0ZR1muNBAAJ" target="_blank" rel="noopener"
&gt;Impala Doesn&amp;rsquo;t Have a histogram() function&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Ibis&amp;rsquo;s histogram works by normalizing a column and rounding to integers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="k"&gt;table_name&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;t0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="k"&gt;column_name&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;min_1fe5be&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;max_1fe5be&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;min_1fe5be&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NUM_BINS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CROSS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="k"&gt;column_name&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;min_1fe5be&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="k"&gt;column_name&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;max_1fe5be&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Creating Impala Tables from Pandas Dataframes</title><link>https://tdhopper.com/blog/creating-impala-tables-from-pandas-dataframes/</link><pubDate>Tue, 15 Mar 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/creating-impala-tables-from-pandas-dataframes/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Creating Impala Tables from Pandas Dataframes" /&gt;&lt;p&gt;&lt;a class="link" href="http://wesmckinney.com/" title="Wes McKinney"
target="_blank" rel="noopener"
&gt;Wes Mckinney&lt;/a&gt;&amp;rsquo;s &lt;a class="link" href="http://docs.ibis-project.org/index.html" target="_blank" rel="noopener"
&gt;Ibis&lt;/a&gt;, a Pythonic interface to Impala, has functionality for creating &lt;a class="link" href="https://www.cloudera.com/products/apache-hadoop/impala.html" title="Apache Impala"
target="_blank" rel="noopener"
&gt;Impala&lt;/a&gt; tables from &lt;a class="link" href="http://pandas.pydata.org/" title="Python Data Analysis Library &amp;amp;mdash; pandas: Python Data Analysis Library"
target="_blank" rel="noopener"
&gt;Python Pandas&lt;/a&gt; dataframes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;pd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ibis&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;hdfs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ibis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hdfs_connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;webhdfs_host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;webhdfs_port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ibis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;impala&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;impala_host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;impala_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hdfs_client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;hdfs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ibis_testing&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pandas_table&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;a class="link" href="http://docs.ibis-project.org/impala.html#creating-tables" target="_blank" rel="noopener"
&gt;This functionality&lt;/a&gt;, added in Ibis 0.6.0, is &lt;em&gt;much&lt;/em&gt; easier that manually move data to HDFS and loading into Impala.&lt;/p&gt;</description></item><item><title>Galileo's Conflict was Science vs. Science</title><link>https://tdhopper.com/blog/galileos-conflict-was-science-vs.-science/</link><pubDate>Fri, 11 Mar 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/galileos-conflict-was-science-vs.-science/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Galileo's Conflict was Science vs. Science" /&gt;&lt;p&gt;Galileo&amp;rsquo;s conflict with the Church was not as it is often portrayed:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In January of 1616, the month before before the Roman Inquisition would infamously condemn the Copernican theory as being &amp;ldquo;foolish and absurd in philosophy&amp;rdquo;, Monsignor Francesco Ingoli addressed Galileo Galilei with an essay entitled &amp;ldquo;Disputation concerning the location and rest of Earth against the system of Copernicus&amp;rdquo;. &amp;hellip; The essay, upon which the Inquisition condemnation was likely based, lists mathematical, physical, and theological arguments against the Copernican theory. Ingoli asks Galileo to respond to those mathematical and physical arguments that are &amp;ldquo;more weighty&amp;rdquo;, and does not ask him to respond to the theological arguments at all. &amp;hellip; Ingoli&amp;rsquo;s emphasis on the scientific arguments of Brahe, and his lack of emphasis on theological arguments, raises the question of whether the condemnation of the Copernican theory was, in contrast to how it is usually viewed, essentially scientific in nature, following the ideas of Brahe.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;From &lt;a class="link" href="http://arxiv.org/abs/1211.4244" target="_blank" rel="noopener"
&gt;Francesco Ingoli&amp;rsquo;s essay to Galileo: Tycho Brahe and science in the Inquisition&amp;rsquo;s condemnation of the Copernican theory&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Using rbenv for Ruby Versions</title><link>https://tdhopper.com/blog/using-rbenv-for-ruby-versions/</link><pubDate>Fri, 11 Mar 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/using-rbenv-for-ruby-versions/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Using rbenv for Ruby Versions" /&gt;&lt;p&gt;I don&amp;rsquo;t use Ruby much, but I wanted to use Jekyll for this blog. I kept getting this error when running &lt;code&gt;gem install jekyll&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Fetching: colorator-0.1.gem (100%)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ERROR: While executing gem ... (Gem::FilePermissionError)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; You don&amp;#39;t have write permissions for the /Library/Ruby/Gems/2.0.0 directory.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;I installed rbenv with &lt;code&gt;brew install rbenv&lt;/code&gt; and added&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[[&lt;/span&gt; -s &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.rbenv/bin &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.rbenv/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;rbenv init -&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;to my &lt;code&gt;.bashrc&lt;/code&gt; file. Then I changed the default ruby interpreter from the system interpreter to one managed by rbenv with &lt;code&gt;rbenv install 2.2.3 &amp;amp;&amp;amp; rbenv global 2.2.3&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Stanford Encyclopedia of Philosophy Bot</title><link>https://tdhopper.com/blog/tweet-your-moon/</link><pubDate>Sun, 28 Feb 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/tweet-your-moon/</guid><description>&lt;p&gt;I enjoy the &lt;a class="link" href="" &gt;Stanford Encyclopedia of Philosophy&lt;/a&gt;, so I created a &lt;a class="link" href="" &gt;Twitter account&lt;/a&gt; that tweets links to random articles from it.&lt;/p&gt;</description></item><item><title>Mentions of John Cook on Github</title><link>https://tdhopper.com/blog/mentions-of-john-cook-on-github/</link><pubDate>Thu, 14 Jan 2016 02:59:00 +0000</pubDate><guid>https://tdhopper.com/blog/mentions-of-john-cook-on-github/</guid><description>&lt;p&gt;People mention &lt;a class="link" href="http://www.johndcook.com" target="_blank" rel="noopener"
&gt;John Cook&amp;rsquo;s blog&lt;/a&gt; a lot &lt;a class="link" href="https://github.com/search?q=johndcook.com&amp;amp;type=Code&amp;amp;utf8=%E2%9C%93" target="_blank" rel="noopener"
&gt;in Github
repos&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I scraped the Github search pages to try to figure out which of his pages are
most mentioned. His post &lt;a class="link" href="http://www.johndcook.com/blog/standard_deviation" target="_blank" rel="noopener"
&gt;Accurately computing running
variance&lt;/a&gt; gets many more
mentions than any other post. It provides C++ code for Knuth&amp;rsquo;s algorithm for
computing the mean, sample variance, and standard deviation for a stream of
data.&lt;/p&gt;
&lt;p&gt;Here are top 12 pages from his site most linked in Github:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="http://www.johndcook.com/blog/standard_deviation" target="_blank" rel="noopener"
&gt;Accurately computing running variance&lt;/a&gt; (377 mentions)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.johndcook.com/stand_alone_code.html" target="_blank" rel="noopener"
&gt;Stand-alone code for numerical computing&lt;/a&gt; (58 mentions)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.johndcook.com/blog/2011/09/27/bayesian-amazon" target="_blank" rel="noopener"
&gt;A Bayesian view of Amazon Resellers&lt;/a&gt; (52 mentions)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.johndcook.com/blog" target="_blank" rel="noopener"
&gt;johndcook.com/blog&lt;/a&gt; (47 mentions)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.johndcook.com/python_longitude_latitude.html" target="_blank" rel="noopener"
&gt;Computing the distance between two locations on Earth from coordinates&lt;/a&gt; (44 mentions)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.johndcook.com/math_h.html" target="_blank" rel="noopener"
&gt;Math.h in POSIX, ISO, and Visual Studio&lt;/a&gt; (38 mentions)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale" target="_blank" rel="noopener"
&gt;Three algorithms for converting color to grayscale&lt;/a&gt; (26 mentions)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.johndcook.com" target="_blank" rel="noopener"
&gt;johndcook.com&lt;/a&gt; (21 mentions)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.johndcook.com/blog/skewness_kurtosis" target="_blank" rel="noopener"
&gt;Computing skewness and kurtosis in one pass&lt;/a&gt; (20 mentions)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.johndcook.com/blog/2010/06/02/whats-so-hard-about-finding-a-hypotenuse" target="_blank" rel="noopener"
&gt;What’s so hard about finding a hypotenuse?&lt;/a&gt; (19 mentions)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.johndcook.com/simplerng.cpp" target="_blank" rel="noopener"
&gt;Random number generation in C++&lt;/a&gt; (19 mentions)&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.johndcook.com/r_language_for_programmers.html" target="_blank" rel="noopener"
&gt;R language for programmers&lt;/a&gt; (19 mentions)&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Quotes from Former Professors</title><link>https://tdhopper.com/blog/quotes-from-former-professors/</link><pubDate>Tue, 12 Jan 2016 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/quotes-from-former-professors/</guid><description>&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;I&amp;#39;ll never forget the great words of one of my best math professors:&amp;#10;&amp;#10;&amp;quot;If all I wanted was the answer, I sure as hell wouldn&amp;#39;t ask you.&amp;quot;&lt;/p&gt;&amp;mdash; Tim Hopper (@tdhopper) &lt;a href="https://twitter.com/tdhopper/status/410071644896382977"&gt;December 9, 2013&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;Another great former professor quote:&amp;#10;&amp;#10;&amp;quot;I&amp;#39;m not smarter than you. I can just recover from mistakes faster than you.&amp;quot;&lt;/p&gt;&amp;mdash; Tim Hopper (@tdhopper) &lt;a href="https://twitter.com/tdhopper/status/410084681443323904"&gt;December 9, 2013&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;</description></item><item><title>I Love Twitter</title><link>https://tdhopper.com/blog/i-love-twitter/</link><pubDate>Mon, 07 Dec 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/i-love-twitter/</guid><description>&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;&lt;a href="https://twitter.com/tdhopper"&gt;@tdhopper&lt;/a&gt; my father wrote his PhD on this question, and I believe his answer was &amp;quot;depends&amp;quot;&lt;/p&gt;&amp;mdash; Fred Benenson (@fredbenenson) &lt;a href="https://twitter.com/fredbenenson/status/673926014406434816"&gt;December 7, 2015&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;</description></item><item><title>My Python Environment Workflow with Conda</title><link>https://tdhopper.com/blog/my-python-environment-workflow-with-conda/</link><pubDate>Tue, 24 Nov 2015 14:41:00 +0000</pubDate><guid>https://tdhopper.com/blog/my-python-environment-workflow-with-conda/</guid><description>&lt;img src="https://tdhopper.com/images/anaconda.png" alt="Featured image of post My Python Environment Workflow with Conda" /&gt;&lt;p&gt;&lt;strong&gt;If you enjoy this post, check out my &lt;a class="link" href="https://pydevtools.com" target="_blank" rel="noopener"
&gt;Python Developer Tooling Handbook&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Many new Python programmers rely on their system install of Python to run
their scripts. There are several good reasons to stop using the system Python.
First, it&amp;rsquo;s probably an old version of Python. Secondly, if you install 3rd
party packages with &lt;a class="link" href="https://pypi.python.org/pypi/pip" target="_blank" rel="noopener"
&gt;pip&lt;/a&gt;, every package is
installed into the same globally accessible directory. While this may sound
convenient, it causes problems if you (1) install different packages with the
same name (2) need to use different versions of the same package (3) upgrade
your operating system (OS X will delete all the packages you have installed).&lt;/p&gt;
&lt;p&gt;For many years, best practice for Python developers was to use virtualenv to
create a sandbox-ed environment for each project. If you use virtualenv, each
project you work on can have its own version of Python with its own 3rd party
packages (hopefully specified in an &lt;code&gt;requirements.txt&lt;/code&gt; file). In my
experience, getting started with virtualenv is cumbersome and confusing; to
this day, I have to look up the command to create a Python 3 virtualenv.
virtualenv also provides no helping in actually managing Python versions. You
have to install each version yourself and then tell virtualenv to use it.&lt;/p&gt;
&lt;p&gt;In 2015, I have almost exclusively used Python installations provided through
&lt;a class="link" href="https://www.continuum.io/" title="Continuum"
target="_blank" rel="noopener"
&gt;Continuum Analytics&amp;rsquo;s&lt;/a&gt;
&lt;a class="link" href="http://conda.pydata.org/docs/intro.html" title="Intro to conda —
Conda documentation"
target="_blank" rel="noopener"
&gt;Conda/Anaconda&lt;/a&gt; platform. I have also switched from using virtualenvs
to using &lt;a class="link" href="http://conda.pydata.org/docs/using/envs.html" title="Managing environments — Conda documentation"
target="_blank" rel="noopener"
&gt;conda environments&lt;/a&gt;, and I am loving it.&lt;/p&gt;
&lt;p&gt;Before explaining my workflow, here&amp;rsquo;s a quick glossary of the similarly-named
products that Continuum offers.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="http://conda.pydata.org/docs/intro.html" target="_blank" rel="noopener"
&gt;conda&lt;/a&gt;: &amp;ldquo;Conda is an open source package management system and environment management system for installing multiple versions of software packages and their dependencies and switching easily between them. It works on Linux, OS X and Windows, and was &lt;em&gt;created for Python programs but can package and distribute any software.&lt;/em&gt; &amp;quot; A conda install provides a whole suite of command line tools for installing and managing packages and environments. Because conda works for any software, it can even install different versions of Python (unlike pip).&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.continuum.io/downloads" target="_blank" rel="noopener"
&gt;Anaconda&lt;/a&gt;: &amp;ldquo;Anaconda is a completely free Python distribution (including for commercial use and redistribution). It includes more than 300 of the most popular Python packages for science, math, engineering, and data analysis.&amp;rdquo; It is available across platforms and installable through a binary.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://anaconda.org" target="_blank" rel="noopener"
&gt;Anaconda Cloud&lt;/a&gt;: Also known as Anaconda.org and formerly known as Binstar, &amp;ldquo;Anaconda Cloud is a package management service where you can host software packages of all kinds.&amp;rdquo; Anaconda Cloud is a package repository analogous to PyPI. Packages are installed via the conda command line tool instead of Pip. By default, the &lt;code&gt;conda install&lt;/code&gt; command installs packages from a curated collection of packages (a superset of those in Anaconda). Continuum allows users to host their own packages on Anaconda Cloud; these packages can also be installed through &lt;code&gt;conda install&lt;/code&gt; using the &lt;code&gt;-n&lt;/code&gt; flag with the username.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Conda, Anaconda, and Anaconda cloud are distinct but interrelated tools;
keeping them straight can be hard, but is helpful.&lt;/p&gt;
&lt;p&gt;Conda (the package manager) can be installed in two ways. Through the
&lt;a class="link" href="http://conda.pydata.org/miniconda.html" target="_blank" rel="noopener"
&gt;Miniconda&lt;/a&gt; installer or the
&lt;a class="link" href="https://www.continuum.io/downloads" target="_blank" rel="noopener"
&gt;Anaconda&lt;/a&gt; installer. Both install the
package manager, but the latter also installs the 300+ packages for scientific
Python. (Installing Anaconda is equivalent to installing Miniconda and then
running &lt;code&gt;conda install anaconda&lt;/code&gt;.)&lt;/p&gt;
&lt;h2 id="conda-environment-files"&gt;Conda Environment Files
&lt;/h2&gt;&lt;p&gt;It has become standard for pip users to create a &lt;code&gt;requirements.txt&lt;/code&gt; file for
specifying dependencies for a particular project. Often, a developer working a
project will (1) create and activate a virtual environment (2) run &lt;code&gt;pip install -r requirements.txt&lt;/code&gt; to build an isolated development environment with
the needed packages.&lt;/p&gt;
&lt;p&gt;Conda provides an analogous (but more powerful) file: &lt;code&gt;environment.yml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A simple &lt;code&gt;environment.yml&lt;/code&gt; file might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: numpy-env
dependencies:
- python=3
- numpy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are in a directory containing this file, you can run &lt;code&gt;$ conda env create&lt;/code&gt; to create a Conda environment named &lt;code&gt;numpy-env&lt;/code&gt; that runs Python 3 and
has &lt;a class="link" href="http://www.numpy.org/" title="NumPy — Numpy"
target="_blank" rel="noopener"
&gt;numpy&lt;/a&gt; installed[^numpy]. Run &lt;code&gt;$ source activate numpy-env&lt;/code&gt; to activate this environment. Once activated,
running &lt;code&gt;$ python&lt;/code&gt; will run Python 3 from your environment instead of the
globally installed Python for your system. Moreover, you will be able to
&lt;code&gt;import numpy&lt;/code&gt; but not any of the 3rd party packages installed globally.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;environment.yml&lt;/code&gt; can also install packages via pip with this syntax:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: pip-env
dependencies:
- python
- pip
- pip:
- pypi-package-name
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I see &lt;code&gt;environment.yml&lt;/code&gt; files as a positive development from
&lt;code&gt;requirements.txt&lt;/code&gt; files for several reasons. Foremost, they allow you to
specify the version of Python you want to use. At Pydata NYC 2015, many
presenters provided their code in Github repositories without specifying
anywhere whether they were using Python 2 or 3. Because I &lt;a class="link" href="https://github.com/tdhopper/pydata-nyc-2015/blob/55b9d2892b18e1d191325fc1890740901723dcfd/environment.yml" target="_blank" rel="noopener"
&gt;included a YAML file&lt;/a&gt;,
attendees could see exactly what version I was using and quickly install it
with &lt;code&gt;conda env create&lt;/code&gt;. I also like being able to specify the name of the
environment in the file; this is particularly helpful when working with
others. Finally, because conda can install from PyPI via pip,
&lt;code&gt;environment.yml&lt;/code&gt; files provide no less functionality than a
&lt;code&gt;requirements.txt&lt;/code&gt; file provides.&lt;/p&gt;
&lt;h2 id="my-python-environment-workflow"&gt;My Python Environment Workflow
&lt;/h2&gt;&lt;p&gt;Lately, whenever I am working on a new project (however big or small), I
follow the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a project folder in the &lt;code&gt;~/repos/&lt;/code&gt; directory on my computer.&lt;/li&gt;
&lt;li&gt;Create an &lt;code&gt;environment.yml&lt;/code&gt; file in the directory. Typically the environment name will be the same as the folder name. At minimum, it will specify the version of Python I want to use; it will often include &lt;code&gt;anaconda&lt;/code&gt; as a dependency.&lt;/li&gt;
&lt;li&gt;Create the conda environment with &lt;code&gt;$ conda env create&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Activate the conda environment with &lt;code&gt;$ source activate ENV_NAME&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;.env&lt;/code&gt; file containing the line &lt;code&gt;source activate ENV_NAME&lt;/code&gt;. Because I have &lt;a class="link" href="https://github.com/kennethreitz/autoenv" target="_blank" rel="noopener"
&gt;autoenv&lt;/a&gt; installed, this file will be run every time I navigate to the project folder in the Terminal. Therefore, my conda environment will be activated as soon as I navigate to the folder.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;$ git init&lt;/code&gt; to make the folder a Git repository. I then run &lt;code&gt;$ git add environment.yml &amp;amp;&amp;amp; git commit -m 'initial commit'&lt;/code&gt; to add the YAML file to the repository.&lt;/li&gt;
&lt;li&gt;If I want to push the repository to Github, I use &lt;code&gt;$ git create&lt;/code&gt; using Github&amp;rsquo;s &lt;a class="link" href="https://github.com/github/hub" target="_blank" rel="noopener"
&gt;hub&lt;/a&gt; commands. I then push the master branch with &lt;code&gt;$ git push -u origin master&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As I add dependencies to my project, I try to be sure I add them to my
&lt;code&gt;environment.yml&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;A major benefit of all this is how easily reproducible a development
environment becomes. If a colleague or conference attendee wants to run my
code, they can setup the dependencies ( &lt;em&gt;including&lt;/em&gt; Python version) by (1)
cloning the repository, (2) running &lt;code&gt;$ conda env create&lt;/code&gt;, (3) running &lt;code&gt;$ source activate ENV_NAME&lt;/code&gt;. It&amp;rsquo;s easy enough for me to drop those instructions
and further instructions for running the code in a README file. If I&amp;rsquo;m feeling
especially helpful, I&amp;rsquo;ll create a
&lt;a class="link" href="http://mrbook.org/blog/tutorials/make/" title="Makefiles – Mrbook&amp;#39;s
Stuff"
target="_blank" rel="noopener"
&gt;Makefile&lt;/a&gt; or &lt;a class="link" href="http://www.fabfile.org/" title="Welcome to Fabric! — Fabric
documentation"
target="_blank" rel="noopener"
&gt;Fabfile&lt;/a&gt; to encapsulate commands for core functionality of the code.&lt;/p&gt;
&lt;p&gt;An even larger benefit is that I can return to a project after, days, months,
or years and quickly start developing without first having to hunt for &lt;code&gt;print&lt;/code&gt;
statements to figure out whether I was using Python 2 or 3.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve come to love &lt;code&gt;environment.yml&lt;/code&gt; files, and I think you might too.&lt;/p&gt;</description></item><item><title>Sequential Minimal Optimization Algorithm for Support Vector Machines</title><link>https://tdhopper.com/blog/sequential-minimal-optimization-algorithm-for-support-vector-machines/</link><pubDate>Sat, 21 Nov 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sequential-minimal-optimization-algorithm-for-support-vector-machines/</guid><description>&lt;p&gt;In my nonlinear optimization class in grad school at North Carolina State University, I wrote a paper on the famed SMO algorithm for support vector machines. In particular, I derive the Lagrangian dual of the classic formulation of the SVM optimization model and show how it can be solved using the stochastic gradient descent algorithm.&lt;/p&gt;
&lt;p&gt;You can &lt;a class="link" href="https://github.com/tdhopper/smo-svm/blob/master/OR706%20Support%20Vector%20Machines.pdf" target="_blank" rel="noopener"
&gt;find the paper here&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Knitting</title><link>https://tdhopper.com/blog/knitting/</link><pubDate>Fri, 20 Nov 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/knitting/</guid><description>&lt;p&gt;True story: I&amp;rsquo;m a closet knitter. I don&amp;rsquo;t have much time for it these days, but it helped keep me sane in grad school. Here are some things I&amp;rsquo;ve made over the years.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2015/11/knitting1.JPG"
loading="lazy"
alt="knitting1.JPG"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2015/11/knitting2.JPG"
loading="lazy"
alt="knitting2.JPG"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2015/11/knitting3.jpg"
loading="lazy"
alt="knitting3.jpg"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2015/11/knitting4.jpg"
loading="lazy"
alt="knitting4.jpg"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2015/11/knitting5.JPG"
loading="lazy"
alt="knitting5.JPG"
&gt;&lt;/p&gt;</description></item><item><title>Using Twitter Data to Gain Insights into E-cigarette Marketing and Locations of Use</title><link>https://tdhopper.com/blog/my-first-publication/</link><pubDate>Fri, 06 Nov 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/my-first-publication/</guid><description>&lt;img src="https://tdhopper.com/images/ecig.png" alt="Featured image of post Using Twitter Data to Gain Insights into E-cigarette Marketing and Locations of Use" /&gt;&lt;p&gt;When I worked at &lt;a class="link" href="http://www.rti.org/" title="RTI International"
target="_blank" rel="noopener"
&gt;RTI International&lt;/a&gt;, I worked on an exploratory analysis of Twitter discussion of electronic cigarettes. A paper on our work was just published in the &lt;a class="link" href="http://www.jmir.org/" title="JMIR-Journal of Medical Internet Research"
target="_blank" rel="noopener"
&gt;Journal of Internet Medical Research&lt;/a&gt;: &lt;a class="link" href="http://www.jmir.org/2015/11/e251/" target="_blank" rel="noopener"
&gt;Using Twitter Data to Gain Insights into E-cigarette Marketing and Locations of Use: An Infoveillance Study&lt;/a&gt;.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Marketing and use of electronic cigarettes (e-cigarettes) and other electronic nicotine delivery devices have increased exponentially in recent years fueled, in part, by marketing and word-of-mouth communications via social media platforms, such as Twitter. &amp;hellip; We identified approximately 1.7 million tweets about e-cigarettes between 2008 and 2013, with the majority of these tweets being advertising (93.43%, 1,559,508/1,669,123). Tweets about e-cigarettes increased more than tenfold between 2009 and 2010, suggesting a rapid increase in the popularity of e-cigarettes and marketing efforts. The Twitter handles tweeting most frequently about e-cigarettes were a mixture of e-cigarette brands, affiliate marketers, and resellers of e-cigarette products. Of the 471 e-cigarette tweets mentioning a specific place, most mentioned e-cigarette use in class (39.1%, 184/471) followed by home/room/bed (12.5%, 59/471), school (12.1%, 57/471), in public (8.7%, 41/471), the bathroom (5.7%, 27/471), and at work (4.5%, 21/471).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I have no idea what &amp;ldquo;Infoveillance&amp;rdquo; means.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Nonparametric Latent Dirichlet Allocation</title><link>https://tdhopper.com/blog/wrapping-up-on-nonparametric-bayes/</link><pubDate>Fri, 16 Oct 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/wrapping-up-on-nonparametric-bayes/</guid><description>&lt;p&gt;Today is my last day at &lt;a class="link" href="http://qadium.com" target="_blank" rel="noopener"
&gt;Qadium&lt;/a&gt;. Next week, I am joining the data science team at &lt;a class="link" href="http://distilnetworks.com" target="_blank" rel="noopener"
&gt;Distil Networks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been privileged to work with &lt;a class="link" href="http://ericjonas.com/" target="_blank" rel="noopener"
&gt;Eric Jonas&lt;/a&gt; on the &lt;a class="link" href="http://datamicroscopes.github.io" target="_blank" rel="noopener"
&gt;data microscopes&lt;/a&gt; project for the past 8 months. In particular, I contributed the implementation of &lt;a class="link" href="https://github.com/datamicroscopes/lda" target="_blank" rel="noopener"
&gt;Nonparametric Latent Dirichlet Allocation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I published a collection of &lt;a class="link" href="https://tdhopper.com/blog/notes-on-dirichlet-processes/" &gt;notes on nonparametric Bayesian methods and Latent Dirichlet Allocation&lt;/a&gt;. I hope this will be useful to other students and researchers of these methods.&lt;/p&gt;</description></item><item><title>Tweets I'm Proud Of (2)</title><link>https://tdhopper.com/blog/tweets-im-proud-of-2/</link><pubDate>Fri, 16 Oct 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/tweets-im-proud-of-2/</guid><description>&lt;p&gt;On MCMC:&lt;/p&gt;
&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;Any sufficiently advanced MCMC is indistinguishable from magic. &amp;#10;&amp;#10;Very slow magic.&lt;/p&gt;&amp;mdash; Tim Hopper (@tdhopper) &lt;a href="https://twitter.com/tdhopper/status/654023792667291648"&gt;October 13, 2015&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;On nonparametric Bayesian methods:&lt;/p&gt;
&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;Nonparametric methods are an advanced technique for replacing intuitive parameters with unintuitive hyperparameters &amp;amp; way more computation.&lt;/p&gt;&amp;mdash; Tim Hopper (@tdhopper) &lt;a href="https://twitter.com/tdhopper/status/649679350510424064"&gt;October 1, 2015&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;On code:&lt;/p&gt;
&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;The cost of a line of code is larger than the time it takes to write it.&lt;/p&gt;&amp;mdash; Tim Hopper (@tdhopper) &lt;a href="https://twitter.com/tdhopper/status/654386934672924673"&gt;October 14, 2015&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;On key pressers:&lt;/p&gt;
&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;&amp;quot;We call people like these two young programmers &amp;#39;key pressers.&amp;#39;&amp;quot;&amp;#10;&amp;#10;(from SEAL Team Six) &lt;a href="http://t.co/VFxZbafLlG"&gt;pic.twitter.com/VFxZbafLlG&lt;/a&gt;&lt;/p&gt;&amp;mdash; Tim Hopper (@tdhopper) &lt;a href="https://twitter.com/tdhopper/status/653902530406187008"&gt;October 13, 2015&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;</description></item><item><title>Sample from Antoniak Distribution with Python</title><link>https://tdhopper.com/blog/antoniak/</link><pubDate>Thu, 15 Oct 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/antoniak/</guid><description>&lt;img src="https://tdhopper.com/images/antoniak-mr-men.png" alt="Featured image of post Sample from Antoniak Distribution with Python" /&gt;&lt;p&gt;&lt;code&gt;rand_antoniak&lt;/code&gt; draws a sample from the distribution of tables created by a Chinese restaurant process with parameter &lt;code&gt;alpha&lt;/code&gt; after &lt;code&gt;n&lt;/code&gt; patrons are seated. Some notes on this distribution are &lt;a class="link" href="http://www.cs.cmu.edu/~tss/antoniak.pdf" target="_blank" rel="noopener"
&gt;available here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;np&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;numpy.random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stirling&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Bad input to stirling.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stirling&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;stirling&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;stirling&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;118124&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;stirling&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;118124&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;stirling&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;stirling&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;stirling&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;stirling&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;4536&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;normalized_stirling_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# * stirling(nn) Gives unsigned Stirling numbers of the first&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# * kind s(nn,*) in ss. ss[i] = s(nn,i). ss is normalized so that maximum&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# * value is 1. After Teh (npbayes).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stirling&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;max_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;max_val&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ss1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ss2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ss10&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt; &lt;span class="mf"&gt;3.09439754e-01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;8.75395242e-01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.00000000e+00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mf"&gt;6.17105824e-01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.29662318e-01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;5.39549757e-02&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mf"&gt;8.05832694e-03&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;7.41877718e-04&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.83729854e-05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mf"&gt;8.52733009e-07&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c1"&gt;# Verified with Yee Whye Teh&amp;#39;s code&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="n"&gt;normalized_stirling_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;ss1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.00001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="n"&gt;normalized_stirling_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;ss2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.00001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="n"&gt;normalized_stirling_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;ss10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.00001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rand_antoniak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Sample from Antoniak Distribution&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# cf http://www.cs.cmu.edu/~tss/antoniak.pdf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;normalized_stirling_numbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;aa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;aa&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;aa&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;rand_antoniak&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Collapsed Gibbs Sampling for Bayesian Mixture Models (with a Nonparametric Extension)</title><link>https://tdhopper.com/blog/collapsed-gibbs/</link><pubDate>Wed, 14 Oct 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/collapsed-gibbs/</guid><description>&lt;img src="https://tdhopper.com/images/collapsed-gibbs-mr-men.png" alt="Featured image of post Collapsed Gibbs Sampling for Bayesian Mixture Models (with a Nonparametric Extension)" /&gt;&lt;p&gt;&lt;a class="link" href="https://tdhopper.com/blog/mixture-model/" &gt;In an earlier notebook&lt;/a&gt;, I showed how we can fit the parameters of a Bayesian mixture model using a Gibbs sampler. The sampler defines a Markov chain that, in steady state, samples from the posterior distribution of the mixture model. To move the chain forward by one step we:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sample the cluster assignment $z_i$.&lt;/li&gt;
&lt;li&gt;Sample the mixture weights $\pi$&lt;/li&gt;
&lt;li&gt;Sample the cluster means $\mu_n$.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It turns out that we can derive a Gibbs sampler that &lt;em&gt;just&lt;/em&gt; samples the assignments instead of the mixture weights and cluster means. This is known as a &lt;em&gt;collapsed&lt;/em&gt; Gibbs sampler. If we integrate out the cluster means $\theta_k$ and mixture weights from the margial distribution of cluster assignment
&lt;/p&gt;
$$p(z_i=k \,|\,
z_{\neg i}, \pi,
\theta_1, \theta_2, \theta_3, \sigma, \mathbf{x}, \alpha
)$$&lt;p&gt; we are left with
&lt;/p&gt;
$$p(z_i\,|\, z_{\neg i}, \sigma, \mathbf{x}, \alpha).$$&lt;p&gt;By the conditional independence, we can factorize this marginal distribution
&lt;/p&gt;
$$
\begin{align}
p(z_i=k\,|\, z_{\neg i}, \sigma, \mathbf{x}, \alpha)
&amp;\propto
p(z_i=k\,|\, x_i, z_{\neg i}, \sigma, \mathbf{x}_{\neg i}, \alpha)\\
&amp;=
p(z_i=k\,|\, z_{\neg i}, \sigma, \mathbf{x}_{\neg i}, \alpha)
p(x_i \,|\, z, \sigma, \mathbf{x}_{\neg i}, \alpha)\\
&amp;=
p(z_i=k \,|\, z_{\neg i}, \alpha) p(x_i \,|\, z, \mathbf{x}_{\neg i}, \sigma)\\
&amp;=
p(z_i=k \,|\, z_{\neg i}, \alpha)p(x_i \,|\, z_i=k, z_{\neg i}, x_{\neg_i}, \sigma)\\
&amp;=
p(z_i=k \,|\, z_{\neg i}, \alpha)p(x_i \,|\, \left\{x_j \,|\, z_j=k, j\neq i\right\}, \sigma).
\end{align}
$$&lt;p&gt;The two terms have intuitive explanations. $p(z_i = k \,|\, z_{\neg i}, \alpha)$ is the probability point $x_i$ will be assigned to component $k$ given the current assignments. Because we are using a symmetric Dirichlet prior, this is the predictive likelihood of a Dirichlet-categorical distribution. This is given by:
&lt;/p&gt;
$$p(z_i=k \,|\, z_{\not i}, \alpha)=
\frac{N_k^{-i}+\alpha / K}{N-1+\alpha}$$&lt;p&gt;
where $N_k^{-i}=\sum_{j\neq i} \delta(z_j, k)$ is the number of observation assigned to $k$ (except $x_i$). We also need to define $\bar{x}_k^{-i}$ to be the mean of all observations assigned to component $k$ (except $x_i$).&lt;/p&gt;
&lt;p&gt;The second term is the predictive likelihood that point $x_i$ is distributed according to cluster $k$ (given the data currently in cluster $k$). For our example, we are assuming unknown cluster means are distributed according to a normal distribution with hyperparameter mean $\lambda_1$ and variance $\lambda_2^2$ and known cluster variance $\sigma^2$.&lt;/p&gt;
&lt;p&gt;Thus,
&lt;/p&gt;
$$
\begin{align}
p(x_i \,|\, \left\{x_j \,|\, z_j=k, j\neq i\right\}, \sigma)
&amp;= \mathcal{N}(x_i \,|\, \mu_k, \sigma_k^2+\sigma^2)
\end{align}
$$&lt;p&gt;
where
&lt;/p&gt;
$$\sigma_k^2 = \left( \frac{N_k^{-i}}{\sigma^2} + \frac{1}{\lambda_2^2} \right)^{-1}$$&lt;p&gt;
and
&lt;/p&gt;
$$\mu_k = \sigma_k^2 \left(
\frac{\lambda_1}{\lambda_2^2}+\frac{N_k^{-i}\cdot \bar{x}_k^{-i}}{\sigma^2}
\right).$$&lt;p&gt;
This is derived in Kevin Murphey&amp;rsquo;s fantastic article &lt;a class="link" href="http://www.cs.ubc.ca/~murphyk/Papers/bayesGauss.pdf" target="_blank" rel="noopener"
&gt;Conjugate Bayesian analysis of the Gaussian distribution&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At each step of the collapsed sampler, we sample each $z_i$ as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For each cluster $k$, compute
$$f_k(x_i) =p(x_i \,|\, \left\{x_j \,|\, x_j=k, j\neq i\right\}, \lambda).$$This is the predictive probability that $x_i$ is in cluster $k$ given the data currently assigned to that cluster.&lt;/li&gt;
&lt;li&gt;Sample
$$z_i\sim \frac{1}{Z_i}\sum_{k=1}^K(N_k^{-i}+\alpha/K)f_k(x_i)\delta(z_i,k)$$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;where the normalizing constant is $Z_i=\sum_{k=1}^K(N_k^{-i}+\alpha/K)f_k(x_i)$.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s write code for this Gibbs sampler!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;pd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;np&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;plt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;namedtuple&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;scipy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;First, load the same dataset we used &lt;a class="link" href="https://tdhopper.com/blog/mixture-model/" &gt;previously&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Series&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;clusters.csv&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_6_0.png"
width="377"
height="256"
srcset="https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_6_0_hu_746e691434352d36.png 480w, https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_6_0_hu_d70ff04bbc9236ad.png 1024w"
loading="lazy"
alt="Histogram of clustered data"
class="gallery-image"
data-flex-grow="147"
data-flex-basis="353px"
&gt;&lt;/p&gt;
&lt;p&gt;Again, we want to define a state object and a function for updating the sufficient statistics of the state.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;SuffStat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;namedtuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;SuffStat&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;theta N&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initial_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_clusters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cluster_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_clusters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;cluster_ids_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cluster_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;data_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;num_clusters_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;num_clusters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;cluster_variance_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;alpha_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;hyperparameters_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;mean&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;variance&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cluster_ids&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster_ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;pi&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;num_clusters&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cluster_ids&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;update_suffstats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_suffstats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iteritems&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;points_in_cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;cluster_id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;points_in_cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SuffStat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Next we define functions to compute the two terms of our marginal distribution over cluster assignments (as we derived above).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_predictive_likelihood&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Predictive likelihood of the data at data_id is generated
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; by cluster_id given the currenbt state.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; From Section 2.4 of
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; http://www.cs.ubc.ca/~murphyk/Papers/bayesGauss.pdf
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hp_mean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hyperparameters_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mean&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hp_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hyperparameters_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;variance&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;param_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_variance_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_log_predictive_likelihood&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hp_mean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hp_var&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param_var&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_log_predictive_likelihood&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hp_mean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hp_var&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param_var&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;posterior_sigma2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;param_var&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;1.&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;hp_var&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;predictive_mu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;posterior_sigma2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hp_mean&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;hp_var&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;theta&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;param_var&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;predictive_sigma2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;param_var&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;posterior_sigma2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;predictive_sd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;predictive_sigma2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;predictive_mu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;predictive_sd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logpdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_cluster_assign_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Log-likelihood that a new point generated will
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; be assigned to cluster_id given the current state.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;current_cluster_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;num_clusters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;num_clusters_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;alpha_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_cluster_size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;num_clusters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Given these two functions, we can compute the posterior probability distribution for assignment of a given datapoint. This is the core of our collapsed Gibbs sampler.&lt;/p&gt;
&lt;p&gt;To simplify the computation of things like $N_k^{-i}$ (where we remove point $i$ from the summary statistics), we create two simple functions to add and remove a point from the summary statistics for a given cluster.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cluster_assignment_distribution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Compute the marginal distribution of cluster assignment
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; for each cluster.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log_predictive_likelihood&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;log_cluster_assign_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iteritems&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;normalization&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;normalization&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iteritems&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_datapoint_to_suffstats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Add datapoint to sufficient stats for normal component
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;SuffStat&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;theta&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;remove_datapoint_from_suffstats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Remove datapoint from sufficient stats for normal component
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;SuffStat&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;theta&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Finally, we&amp;rsquo;re ready to create a function that takes a Gibbs step on the state. For each datapoint, it&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Removes the datapoint from its current cluster.&lt;/li&gt;
&lt;li&gt;Computes the posterior probability of the point being assigned to each cluster (given the other current assignments).&lt;/li&gt;
&lt;li&gt;Assigns the datapoint to a cluster sampled from this probability distribution.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;gibbs_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pairs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datapoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;remove_datapoint_from_suffstats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datapoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cluster_assignment_distribution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;add_datapoint_to_suffstats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s our old function to plot the assignments.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;plot_clusters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;gby&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hist_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gby&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;gby&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hist_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;bins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;histtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;stepfilled&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;.5&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Randomly assign the datapoints to a cluster to start.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initial_state&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plot_clusters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_18_0.png"
width="371"
height="256"
srcset="https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_18_0_hu_ac490161eb7ad43.png 480w, https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_18_0_hu_32c3566ebe1713ad.png 1024w"
loading="lazy"
alt="Random initial assignments"
class="gallery-image"
data-flex-grow="144"
data-flex-basis="347px"
&gt;&lt;/p&gt;
&lt;p&gt;Look what happens to the assignments after just one Gibbs step!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;gibbs_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plot_clusters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_20_0.png"
width="377"
height="256"
srcset="https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_20_0_hu_6efbddbaf79313b7.png 480w, https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_20_0_hu_85c1957a4df7c0f0.png 1024w"
loading="lazy"
alt="Assignments after one Gibbs step"
class="gallery-image"
data-flex-grow="147"
data-flex-basis="353px"
&gt;&lt;/p&gt;
&lt;p&gt;Two:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;gibbs_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plot_clusters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_22_0.png"
width="377"
height="256"
srcset="https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_22_0_hu_1d7298fc2dd80086.png 480w, https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_22_0_hu_9cd8ee64aad1305.png 1024w"
loading="lazy"
alt="Assignments after two Gibbs steps"
class="gallery-image"
data-flex-grow="147"
data-flex-basis="353px"
&gt;&lt;/p&gt;
&lt;p&gt;After just two steps, our assignments look &lt;em&gt;really&lt;/em&gt; good. We can run it a few more times and see the assignments again.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;gibbs_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plot_clusters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_24_0.png"
width="377"
height="256"
srcset="https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_24_0_hu_356ba6c275f133a4.png 480w, https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_24_0_hu_1163205c2de0b919.png 1024w"
loading="lazy"
alt="Assignments after 22 Gibbs steps"
class="gallery-image"
data-flex-grow="147"
data-flex-basis="353px"
&gt;&lt;/p&gt;
&lt;h3 id="nonparametric-mixture-models"&gt;Nonparametric Mixture Models!
&lt;/h3&gt;&lt;p&gt;It turns out, the collapsed Gibbs sampler for mixture models is almost identical in the context of a &lt;em&gt;nonparametric&lt;/em&gt; model. This model uses a &lt;em&gt;Dirichlet process prior&lt;/em&gt; instead of a &lt;em&gt;Dirichlet distribution prior&lt;/em&gt;. It doesn&amp;rsquo;t require us to specify how many clusters we are looking for in our data.&lt;/p&gt;
&lt;p&gt;The cluster assignment score changes slightly. It is proportional to $N_k^{-i}$ for each known cluster. We assign a datapoint to a &lt;em&gt;new&lt;/em&gt; cluster with probability proportional to $\alpha$ (which is now the DP dispersion parameter).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_cluster_assign_score_dp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Log-likelihood that a new point generated will
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; be assigned to cluster_id given the current state.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cluster_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;new&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;alpha_&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The predictive likelihood remains the same for known clusters. However, we need to know the likelihood of assigning a datapoint to a new cluster. In this case, we fall back on the hyperparameters to get:&lt;/p&gt;
$$
\begin{align}
p(x_i \,|\, z, x_{\neg_i}, \sigma)
&amp;= \mathcal{N}(x_i \,|\, \lambda_1, \lambda_2^2+\sigma^2)
\end{align}
$$&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_predictive_likelihood_dp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Predictive likelihood of the data at data_id is generated
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; by cluster_id given the currenbt state.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; From Section 2.4 of
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; http://www.cs.ubc.ca/~murphyk/Papers/bayesGauss.pdf
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cluster_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;new&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SuffStat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hp_mean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hyperparameters_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mean&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hp_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hyperparameters_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;variance&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;param_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_variance_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_log_predictive_likelihood&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hp_mean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hp_var&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param_var&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Given this, we can define the marginal distribution over cluster assignment. The only change is that the &amp;ldquo;&lt;code&gt;new&lt;/code&gt;&amp;rdquo; state enters in the distribution.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cluster_assignment_distribution_dp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Compute the marginal distribution of cluster assignment
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; for each cluster.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cluster_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;new&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cluster_ids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log_predictive_likelihood_dp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;log_cluster_assign_score_dp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iteritems&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;normalization&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;normalization&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iteritems&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;We also need to be able to create a new cluster when &amp;ldquo;&lt;code&gt;new&lt;/code&gt;&amp;rdquo; is drawn, and destroy a cluster if its emptied.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_cluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;num_clusters_&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cluster_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SuffStat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_ids_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cluster_id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;destroy_cluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;num_clusters_&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_ids_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;prune_clusters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_ids_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;destroy_cluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Finally, we can define the &lt;code&gt;gibbs_step_dp&lt;/code&gt; function. It&amp;rsquo;s nearly identical to the earlier &lt;code&gt;gibbs_step&lt;/code&gt; function except&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It uses &lt;code&gt;cluster_assignment_distribution_dp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;It creates a new cluster when the sampled assignment is &amp;ldquo;&lt;code&gt;new&lt;/code&gt;&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;It destroys a cluster any time it is emptied.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For clarity, I split out the code for sampling assignment to its own function.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sample_assignment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Sample new assignment from marginal distribution.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; If cluster is &amp;#34;`new`&amp;#34;, create a new cluster.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cluster_assignment_distribution_dp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;new&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;create_cluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;gibbs_step_dp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Collapsed Gibbs sampler for Dirichlet Process Mixture Model
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pairs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datapoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;remove_datapoint_from_suffstats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datapoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;prune_clusters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sample_assignment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;add_datapoint_to_suffstats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This time, we will start by randomly assigning our data to two clusters.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initial_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_clusters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plot_clusters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_36_0.png"
width="371"
height="256"
srcset="https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_36_0_hu_a526009d7c13a93c.png 480w, https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_36_0_hu_d7bdb91ce9801e81.png 1024w"
loading="lazy"
alt="Initial assignment to 2 clusters"
class="gallery-image"
data-flex-grow="144"
data-flex-basis="347px"
&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what happens when we run our Gibbs sampler once.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;gibbs_step_dp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plot_clusters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_38_0.png"
width="377"
height="256"
srcset="https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_38_0_hu_39fa5631907bb45f.png 480w, https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_38_0_hu_b258ecc9b572e576.png 1024w"
loading="lazy"
alt="After one DP Gibbs step: 4 clusters"
class="gallery-image"
data-flex-grow="147"
data-flex-basis="353px"
&gt;&lt;/p&gt;
&lt;p&gt;We went from 2 to 4 clusters!&lt;/p&gt;
&lt;p&gt;After 100 iterations:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;gibbs_step_dp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plot_clusters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_41_0.png"
width="377"
height="256"
srcset="https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_41_0_hu_b18683741734f2b3.png 480w, https://tdhopper.com/blog/collapsed-gibbs/2015-10-14-collapsed-gibbs-sampling-for-mixture-models_41_0_hu_f105d5ebc42b302d.png 1024w"
loading="lazy"
alt="After 100 DP Gibbs iterations: 3 clusters"
class="gallery-image"
data-flex-grow="147"
data-flex-basis="353px"
&gt;&lt;/p&gt;
&lt;p&gt;After 100 iterations, our assignment looks correct! We went back to 3 clusters.&lt;/p&gt;
&lt;p&gt;We can sample the mixture weights, if we need them, using the &amp;ldquo;Conditional Distribution of Mixture Weights&amp;rdquo; derived &lt;a class="link" href="https://tdhopper.com/blog/mixture-model/" &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;alpha_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;num_clusters_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_ids_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirichlet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;pre&gt;&lt;code&gt;array([ 0.21330625, 0.29838101, 0.48831275])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can also sample the cluster means using &lt;a class="link" href="https://tdhopper.com/blog/mixture-model/" &gt;the method we derived earlier&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cluster_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_ids_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cluster_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_variance_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hp_mean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hyperparameters_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mean&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hp_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hyperparameters_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;variance&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;numerator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hp_mean&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;hp_var&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;theta&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;cluster_var&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;denominator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;hp_var&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;cluster_var&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;posterior_mu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numerator&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;denominator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;posterior_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;denominator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;posterior_mu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;posterior_var&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;cluster_id:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;mean&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;pre&gt;&lt;code&gt;cluster_id: 1 mean -0.0176257860235
cluster_id: 2 mean -0.400581819532
cluster_id: 3 mean 0.600302879661
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Much thanks to Erik Sudderth&amp;rsquo;s excellent introduction to nonparametric Bayes in &lt;a class="link" href="http://cs.brown.edu/~sudderth/papers/sudderthPhD.pdf" target="_blank" rel="noopener"
&gt;Chapter 2 of his dissertation&lt;/a&gt;. Algorithms 2.2 and 2.3 in that piece are the clearest formulation of collapsed Gibbs sampling for mixture models that I have come across.&lt;/p&gt;</description></item><item><title>Nomenclature of Dirichlet Processes</title><link>https://tdhopper.com/blog/nomenclature/</link><pubDate>Tue, 13 Oct 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/nomenclature/</guid><description>&lt;img src="https://tdhopper.com/images/nomenclature-mr-men.png" alt="Featured image of post Nomenclature of Dirichlet Processes" /&gt;&lt;p&gt;Much of the literature on Dirichlet Processes makes assertions similar to the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;DP is the Dirichlet process, a distribution over distributions.&amp;rdquo; (&lt;a class="link" href="http://www.stat.columbia.edu/npbayes/papers/neal_sampling.pdf" target="_blank" rel="noopener"
&gt;Neal, 2000&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&amp;ldquo;[The Dirichlet process] is a distribution over distributions, i.e. each draw from a Dirichlet process is itself a distribution.&amp;rdquo; (&lt;a class="link" href="http://www.stats.ox.ac.uk/~teh/research/npbayes/Teh2010a.pdf" target="_blank" rel="noopener"
&gt;Teh, 2010&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&amp;ldquo;The Dirichlet process (DP) is a distribution over distributions.&amp;rdquo; (&lt;a class="link" href="http://web.mit.edu/sjgershm/www/GershmanBlei12.pdf" target="_blank" rel="noopener"
&gt;Gershman and Blei, 2012&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;The &amp;ldquo;Dirichlet process defines a distribution on random probability measures&amp;hellip;&amp;rdquo; (&lt;a class="link" href="http://cs.brown.edu/~sudderth/papers/sudderthPhD.pdf" target="_blank" rel="noopener"
&gt;Sudderth, 2006&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Dirichlet processes define a distribution over distributions&amp;hellip;&amp;rdquo; (&lt;a class="link" href="http://mlg.eng.cam.ac.uk/zoubin/talks/uai05tutorial-b.pdf" target="_blank" rel="noopener"
&gt;Ghahramani, 2005&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Michael Jordan makes an &lt;a class="link" href="http://www.cs.berkeley.edu/~jordan/courses/281B-spring04/lectures/dp1.pdf" target="_blank" rel="noopener"
&gt;equivalent statement&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Each of these sources makes the claim that a Dirichlet Process is a distribution over probability distributions. That is, given a base distribution $G_0$ and a parameter $\alpha$, $DP(\alpha, G_0)$ &lt;em&gt;is&lt;/em&gt; a Dirichlet process and &lt;em&gt;is&lt;/em&gt; (equivalently) a distribution over probability distributions. Therefore, a sample $G$ drawn from the Dirichlet process $DP(\alpha, G_0)$ is itself a probability distribution. $G$ (where $G\sim DP(\alpha, G_0)$) is a discrete probability distribution whose support is a discrete subset of the support of $G_0$.&lt;/p&gt;
&lt;p&gt;Confusingly, while many sources refer to the DP as a distribution over distributions, when using the phrase &amp;ldquo;sample from a Dirichlet process&amp;rdquo;, they mean a sample from $G$, not from $DP(\alpha, G_0)$. These authors appear to equivocate on the term &amp;ldquo;Dirichlet process&amp;rdquo;. It means both a distribution over distributions and a distribution sampled from this distribution over distributions.&lt;/p&gt;
&lt;p&gt;After being confused by this point for some time, I &lt;a class="link" href="https://tdhopper.com/blog/dirichlet-distribution/" &gt;prepared these notes&lt;/a&gt; arguging that the Dirichlet process &lt;em&gt;is&lt;/em&gt; a distribution over distributions. I argued that the term &lt;em&gt;sample from a Dirichlet process&lt;/em&gt; should refer to a distribution sampled from the DP, not to a point sampled from the support of $G_0$.&lt;/p&gt;
&lt;p&gt;In response to my notes, &lt;a class="link" href="http://danroy.org/marginalia/Nomenclature_for_stochastic_processes_and_Bayesian_nonparametric_statistics" target="_blank" rel="noopener"
&gt;Dan Roy&lt;/a&gt; &lt;a class="link" href="http://danroy.org/marginalia/Nomenclature_for_stochastic_processes_and_Bayesian_nonparametric_statistics" target="_blank" rel="noopener"
&gt;briefly argued&lt;/a&gt; that &amp;ldquo;The Dirichlet process is a distribution on the space of probability measures&amp;rdquo; is a misstatement. In fact, Roy argues that $DP(\alpha, G_0)$ is not a Dirichlet process at all! Instead $G$ (the distribution sampled from $DP(\alpha, G_0)$) is the Dirichlet process.&lt;/p&gt;
&lt;p&gt;Thomas Ferguson first defined the Dirichlet Process &lt;a class="link" href="http://www.cis.upenn.edu/~taskar/courses/cis700-sp08/papers/ferguson.pdf" target="_blank" rel="noopener"
&gt;in his 1973&lt;/a&gt; paper. Charles Antoniak (a student of Ferguson) repeats the definition &lt;a class="link" href="http://www.cis.upenn.edu/~taskar/courses/cis700-sp08/papers/antoniak.pdf" target="_blank" rel="noopener"
&gt;in his his 1974 paper&lt;/a&gt;. Antoniak&amp;rsquo;s definition is as follows:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Let $\Theta$ be a set, and $\mathcal{A}$ a $\sigma$-field of subsets of $\Theta$. Let $\beta$ be a finite, nonnull, nonnegative, finitely additive measure on $(\Theta, \mathcal{A})$. We say a random probability measure $P$ on $(\Theta, \mathcal{A})$ is a Dirichlet process on $(\Theta, \mathcal{A})$ with parameter $\beta$, denoted $P\in \mathcal{D}(\beta)$, if for every $k=1, 2, \ldots$ and measurable partition $B_1,\ldots,B_k$ of $\Theta$, the joint distribution of the random probabilities $(P(B_1),\ldots,P(B_k))$ is Dirichlet with parameters $(\beta(B_1),\ldots,\beta(B_k))$, denoted $(P(B_1),\ldots,P(B_k))\in \mathcal{D}(\beta(B_1),\ldots,\beta(B_k))$.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&amp;rsquo;s unpack this dense, measure theoretic definition.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Let $\Theta$ be a set, and $\mathcal{A}$ a $\sigma$-field of subsets of $\Theta$. Let $\beta$ be a finite, nonnull, nonnegative, finitely additive measure on $(\Theta, \mathcal{A})$.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;First, note that $\beta$ here is a finite measure, i.e. a not-necessarily-normalized probability distribution. Antoniak&amp;rsquo;s $\alpha$ is equivalent to $\alpha\cdot G_0$ in our notation. Essentially, this means we have a probability distribution over some set $\Theta$ with density $p(x) = \beta(x) / \beta(\Theta)$. (The business about $\sigma$-field of subsets just allows us to avoid things like the &lt;a class="link" href="https://en.wikipedia.org/wiki/Banach%E2%80%93Tarski_paradox" target="_blank" rel="noopener"
&gt;Banach-Tarski paradox&lt;/a&gt;.)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We say a random probability measure $P$ on $(\Theta, \mathcal{A})$ is a Dirichlet process on $(\Theta, \mathcal{A})$ with parameter $\beta$, denoted $P\in \mathcal{D}(\beta)$&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;hellip;if for every $k=1, 2, \ldots$ and measurable partition $B_1,\ldots,B_k$ of $\Theta$, the joint distribution of the random probabilities $(P(B_1),\ldots,P(B_k))$ is Dirichlet with parameters $(\beta(B_1),\ldots,\beta(B_k))$, denoted $(P(B_1),\ldots,P(B_k))\in \mathcal{D}(\beta(B_1),\ldots,\beta(B_k))$.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Econtalk Topics: Nonparametric LDA in Practice</title><link>https://tdhopper.com/blog/econtalk-topics/</link><pubDate>Wed, 07 Oct 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/econtalk-topics/</guid><description>&lt;img src="https://tdhopper.com/images/econtalk-topics-mr-men.png" alt="Featured image of post Econtalk Topics: Nonparametric LDA in Practice" /&gt;&lt;p&gt;&lt;em&gt;The interactive visualizations in this analysis require a live notebook. &lt;a class="link" href="http://nbviewer.ipython.org/github/tdhopper/notes-on-dirichlet-processes/blob/master/2015-10-07-econtalk-topics.ipynb" target="_blank" rel="noopener"
&gt;View the original notebook on NBViewer&lt;/a&gt; for the full interactive experience, including the pyLDAvis topic visualization.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="nonparametric-latent-dirichlet-allocation"&gt;Nonparametric Latent Dirichlet Allocation
&lt;/h2&gt;&lt;h3 id="analysis-of-the-topics-of-econtalk"&gt;Analysis of the topics of &lt;a class="link" href="http://www.econtalk.org/" target="_blank" rel="noopener"
&gt;Econtalk&lt;/a&gt;
&lt;/h3&gt;&lt;p&gt;In 2003, a groundbreaking statistical model called &amp;ldquo;&lt;a class="link" href="https://www.cs.princeton.edu/~blei/papers/BleiNgJordan2003.pdf" target="_blank" rel="noopener"
&gt;Latent Dirichlet Allocation&lt;/a&gt;&amp;rdquo; was presented by David Blei, Andrew Ng, and Michael Jordan.&lt;/p&gt;
&lt;p&gt;LDA provides a method for summarizing the topics discussed in a document. LDA defines topics to be discrete probability distrbutions over words. For an introduction to LDA, see &lt;a class="link" href="http://blog.echen.me/2011/08/22/introduction-to-latent-dirichlet-allocation/" target="_blank" rel="noopener"
&gt;Edwin Chen&amp;rsquo;s post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The original LDA model requires the number of topics in the document to be specfied as a known parameter of the model. In 2005, Yee Whye Teh and others published &lt;a class="link" href="http://www.cs.berkeley.edu/~jordan/papers/hdp.pdf" target="_blank" rel="noopener"
&gt;a &amp;ldquo;nonparametric&amp;rdquo; version of this model&lt;/a&gt; that doesn&amp;rsquo;t require the number of topics to be specified. This model uses a prior distribution over the topics called a hierarchical Dirichlet process. &lt;a class="link" href="https://tdhopper.com/blog/nonparametric-lda/" &gt;I wrote an introduction to this HDP-LDA model&lt;/a&gt; earlier this year.&lt;/p&gt;
&lt;p&gt;For the last six months, I have been developing a Python-based Gibbs sampler for the HDP-LDA model. This is part of a larger library of &amp;ldquo;robust, validated Bayesian nonparametric models for discovering structure in data&amp;rdquo; known as &lt;a class="link" href="http://datamicroscopes.github.io" target="_blank" rel="noopener"
&gt;Data Microscopes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This notebook demonstrates the functionality of this implementation.&lt;/p&gt;
&lt;p&gt;The Data Microscopes library is available on &lt;a class="link" href="https://anaconda.org/datamicroscopes/" target="_blank" rel="noopener"
&gt;anaconda.org&lt;/a&gt; for Linux and OS X. &lt;code&gt;microscopes-lda&lt;/code&gt; can be installed with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ conda install -c datamicroscopes -c distributions microscopes-lda
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Econtalk transcript data used in this analysis is &lt;a class="link" href="https://github.com/tdhopper/notes-on-dirichlet-processes/tree/master/econtalk-data" target="_blank" rel="noopener"
&gt;available on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pyLDAvis&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;cPickle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;microscopes.common.rng&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;microscopes.lda.definition&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;model_definition&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;microscopes.lda.model&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;initialize&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;microscopes.lda&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;utils&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;microscopes.lda&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;runner&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;genfromtxt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;linalg&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;dtm.csv&lt;/code&gt; contains a document-term matrix representation of the words used in Econtalk transcripts. The columns of the matrix correspond to the words in &lt;code&gt;vocab.txt&lt;/code&gt;. The rows in the matrix correspond to the show urls in &lt;code&gt;urls.txt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Our LDA implementation takes input data as a list of lists of hashable objects (typically words). We can use a utility function to convert the document-term matrix to the list of tokenized documents.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vocab&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;genfromtxt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./econtalk-data/vocab.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delimiter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;str&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dtm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;genfromtxt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./econtalk-data/dtm.csv&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delimiter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;docs_from_document_term_matrix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dtm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vocab&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;vocab&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./econtalk-data/urls.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readlines&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s set up our model. First we created a model definition describing the basic structure of our data. Next we initialize an MCMC state object using the model definition, documents, random number generator, and hyper-parameters.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vocab&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;defn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model_definition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;prng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;defn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;vocab_hp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dish_hps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;alpha&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;gamma&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;defn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;When we first create a state object, the words are randomly assigned to topics. Thus, our perplexity (model score) is quite high. After running 1000 iterations of the MCMC, the perplexity drops significantly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;randomly initialized model:
number of documents 454
vocabulary size 16445
perplexity: 16523.1820356 num topics: 9
after 1000 iterations:
perplexity: 2363.65138771 num topics: 11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can extract the term relevance for each topic. Here are the 10 most relevant words for each of the 11 discovered topics:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;topic 0 : banks fed bank money financial monetary debt inflation crisis rates
topic 1 : party republicans constitution vote democrats republican tax election president stalin
topic 2 : fat science diet eat insulin disease immune replication scientific eating
topic 3 : growth trade water cities china city development climate inequality oil
topic 4 : people think don just going like say things lot way
topic 5 : smith hayek moral economics society adam liberty coase theory rules
topic 6 : bitcoin internet software google technology store bitcoins computer machines company
topic 7 : prison health drug care drugs medicaid medical patients patient women
topic 8 : schools teachers school kids teacher education students parents teaching sports
topic 9 : bees honey pollination colony ants bee queen cheung ant colonies
topic 10 : museum museums art gallery galleries monet seating trustees admission director
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We could assign titles to each of these topics. For example, &lt;em&gt;Topic 5&lt;/em&gt; appears to be about the &lt;em&gt;foundations of classical liberalism&lt;/em&gt;. &lt;em&gt;Topic 6&lt;/em&gt; is obviously &lt;em&gt;Bitcoin and Software&lt;/em&gt;. &lt;em&gt;Topic 0&lt;/em&gt; is the &lt;em&gt;financial system and monetary policy&lt;/em&gt;. &lt;em&gt;Topic 4&lt;/em&gt; seems to be &lt;em&gt;generic words used in most episodes&lt;/em&gt;; unfortunately, the prevalence of &amp;ldquo;don&amp;rdquo; is a result of my preprocessing which splits up the contraction &amp;ldquo;don&amp;rsquo;t&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Topic 5 appears to be about the theory of classical liberalism. The 20 episodes with the highest proportion of words from that topic include discussions of the Theory of Moral Sentiments, foundations of liberty, and microeconomics:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The Economics of Organ Donations
Klein on The Theory of Moral Sentiments, Episode 2
Boudreaux on Law and Legislation
Klein on The Theory of Moral Sentiments, Episode 4
Klein on The Theory of Moral Sentiments, Episode 5
Wolfe on Liberalism
Boettke on Katrina and the Economics of Disaster
Richard Thaler on Libertarian Paternalism
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can also use the topic distributions as low-dimensional projections of the documents, allowing us to find episodes that are similar in content. For example, episodes similar to &amp;ldquo;Kling on Freddie and Fannie and the Recent History of the U.S. Housing Market&amp;rdquo; include many episodes about the financial crisis:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Irwin on the Great Depression and the Gold Standard
Rustici on Smoot-Hawley and the Great Depression
Reinhart on Financial Crises
Posner on the Financial Crisis
Sumner on Monetary Policy
Calomiris on the Financial Crisis
John Taylor on the Financial Crisis
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can also find the topics a given word is most likely to appear in. For example, the word &amp;ldquo;Munger&amp;rdquo; (as in &lt;a class="link" href="http://www.michaelmunger.com/" target="_blank" rel="noopener"
&gt;Mike Munger&lt;/a&gt;) appears most frequently in discussions of classical liberalism and microeconomics:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;topics_related_to_word&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;wd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rel&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word_dists&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;relevance&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wd&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;rel_words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;bars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;rel_words&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;pre&gt;&lt;code&gt;==== growth trade water cities china city development climate inequality oil
================== smith hayek moral economics society adam liberty coase theory rules
=== bitcoin internet software google technology store bitcoins computer machines company
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where does Munger come up? In discussing the moral foundations of classical liberalism and microeconomics!&lt;/p&gt;
&lt;p&gt;The word &amp;ldquo;lovely&amp;rdquo;&amp;mdash;which Russ Roberts uses often when talking about the &lt;em&gt;Theory of Moral Sentiments&lt;/em&gt;&amp;mdash;appears most in that topic as well:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;= smith hayek moral economics society adam liberty coase theory rules
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>A Joke</title><link>https://tdhopper.com/blog/a-joke/</link><pubDate>Thu, 24 Sep 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/a-joke/</guid><description>&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;Even though I&amp;#39;ve been coding for 12 years, I&amp;#39;d still just consider myself a grammer.&lt;/p&gt;&amp;mdash; Tim Hopper (@tdhopper) &lt;a href="https://twitter.com/tdhopper/status/531928033553563648"&gt;November 10, 2014&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;</description></item><item><title>Notes on Gibbs Sampling in Hierarchical Dirichlet Process Models</title><link>https://tdhopper.com/blog/hdp-lda/</link><pubDate>Mon, 21 Sep 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/hdp-lda/</guid><description>&lt;img src="https://tdhopper.com/images/hdp-lda-gibbs-mr-men.png" alt="Featured image of post Notes on Gibbs Sampling in Hierarchical Dirichlet Process Models" /&gt;&lt;p&gt;&lt;a class="link" href="http://www.cs.berkeley.edu/~jordan/papers/hdp.pdf" target="_blank" rel="noopener"
&gt;Yee Whye Teh et al&lt;/a&gt;&amp;rsquo;s 2005 paper &lt;em&gt;Hierarchical Dirichlet Processes&lt;/em&gt; describes a nonparametric prior for grouped clustering problems. For example, the HDP helps in generalizing the &lt;a class="link" href="https://en.wikipedia.org/wiki/Latent_Dirichlet_allocation" target="_blank" rel="noopener"
&gt;latent Dirichlet allocation&lt;/a&gt; model to the case the number of topics in the data are discovered by the inference algorithm instead of being specified as a parameter of the model.&lt;/p&gt;
&lt;p&gt;The authors describe three MCMC-based algorithms for fitting HDP based models. Unfortunately, the algorithms are described somewhat vaguely and in general terms. A fair bit of mathematical leg work is required before the HDP algorithms can be applied to the specific case of nonparametric latent Dirichlet allocation.&lt;/p&gt;
&lt;p&gt;Here are some notes I&amp;rsquo;ve compiled in my effort to understand these algorithms.&lt;/p&gt;
&lt;h2 id="hdp-lda-generative-model"&gt;HDP-LDA Generative Model
&lt;/h2&gt;&lt;p&gt;The generative model for Hierarchical Dirichlet Process Latent Dirichlet Allocation is as follows:&lt;/p&gt;
\begin{equation}
\begin{aligned}
H &amp; \sim \text{Dirichlet}(\beta) \\
G_0 \,|\, \gamma, H &amp; \sim \text{DP}(\gamma, H) \\
G_j \,|\, \alpha_0, G_0 &amp; \sim \text{DP}(\alpha_0, G_0) \\
\theta_{ji} \,|\, G_j &amp; \sim G_j \\
x_{ij} \,|\, \theta_{ji} &amp; \sim \text{Categorical}(\theta_{ji}) \\
\end{aligned}
\end{equation}&lt;ul&gt;
&lt;li&gt;$H$ is Dirichlet distribution whose dimension is the size of the vocabulary, i.e. it is distribution over an uncountably-number of term distributions (topics).&lt;/li&gt;
&lt;li&gt;$G_0$ is a distribution over a countably-infinite number of categorical term distributions, i.e. topics.&lt;/li&gt;
&lt;li&gt;For each document $j$, $G_j$ is a is a distribution over a countably-infinite number of categorical term distributions, i.e. topics.&lt;/li&gt;
&lt;li&gt;$\theta_{ji}$ is a categorical distribution over terms, i.e. a topic.&lt;/li&gt;
&lt;li&gt;$x_{ij}$ is a term.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To see code for sampling from this generative model, see &lt;a class="link" href="https://tdhopper.com/blog/nonparametric-lda/" &gt;this post&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="chinese-restaurant-franchise-approach"&gt;Chinese Restaurant Franchise Approach
&lt;/h3&gt;&lt;p&gt;Instead of the above Dirichlet process model, we can think of an identical &amp;ldquo;Chinese Restaurant Franchise&amp;rdquo; model.&lt;/p&gt;
&lt;p&gt;Each $\theta_{ji}$ is a customer in restaurant $j$. Each customer is sitting at a table, and each table has multiple customers.&lt;/p&gt;
&lt;p&gt;There is a global menu of $K$ dishes that the restaurants serve, $\phi_1,\ldots,\phi_K$.&lt;/p&gt;
&lt;p&gt;Some other definitions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\psi_{jt}$ is the dish served at table $t$ in restaurant $j$; i.e. each $\psi_{jt}$ corresponds to some $\phi_k$.&lt;/li&gt;
&lt;li&gt;$t_{ji}$ is the index of the $\psi_{jt}$ associated with $\theta_{ji}$.&lt;/li&gt;
&lt;li&gt;$k_{jt}$ is the index of $\phi_k$ associated with $\psi_{jt}$.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Customer $i$ in restaurant $j$ sits at table $t_{ji}$ while table $t$ in restaurant $j$ serves dish $k_{jt}$.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There are two arrays of count variables we will want to track:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$n_{jtk}$ is the number of customers in restaurant $j$ at table $t$ eating dish $k$.&lt;/li&gt;
&lt;li&gt;$m_{jk}$ is the number of tables in restaurant $j$ serving dish $k$ (multiple tables may serve the same dish).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To summarize:&lt;/p&gt;
&lt;p&gt;$x_{ij}$ are observed data (words). We assume $x_{ij}\sim F(\theta_{ij})$. Further, we assume $\theta_{ji}$ is associated with table $t_{ji}$, that is $\theta_{ji}=\psi_{jt_{ji}}$. Further, we assume the topic for table $j$ is indexed by $k_{jt}$, i.e. $\psi_{jt}=\phi_{k_{jt}}$. Thus, if we know $t_{ji}$ (the table assignment for $x_{ij}$) and $k_{jt}$ (the dish assignment for table $t$) for all $i, j, t$, we could determine the remaining parameters by sampling.&lt;/p&gt;
&lt;h2 id="gibbs-sampling"&gt;Gibbs Sampling
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="http://www.cs.berkeley.edu/~jordan/papers/hdp.pdf" target="_blank" rel="noopener"
&gt;Teh et al&lt;/a&gt; describe three Gibbs samplers for this model. The first and third are most applicable to the LDA application. The section helps with more complication applications of the LDA algorithm (e.g. the hidden Markov model).&lt;/p&gt;
&lt;h3 id="53-posterior-sampling-by-direct-assignment"&gt;5.3 Posterior sampling by direct assignment
&lt;/h3&gt;&lt;p&gt;Section 5.3 describes a direct assignment Gibbs sampler that directly assigns words to topics by augmenting the model with an assignment variable $z_{ji}$ that is equivalent to $k_{jt_{ji}}$. This also requires a count variable $m_{jk}$: the number of tables in document/franchise $j$ assigned to dish/topic $k$. This sampler requires less &amp;ldquo;bookkeeping&amp;rdquo; than the algorithm from 5.1, however it requires expensive simulation or computation of recursively computed Stirling numbers of the first kind.&lt;/p&gt;
&lt;p&gt;My notes below are derive the necessary equations for Gibbs sampling for the algorithm in section 5.1. However, they also provide most of the derivations needed for 5.3.&lt;/p&gt;
&lt;h3 id="51-posterior-sampling-in-the-chinese-restaurant-franchise"&gt;5.1 Posterior sampling in the Chinese restaurant franchise
&lt;/h3&gt;&lt;p&gt;Section 5.1 describes &amp;ldquo;Posterior sampling in the Chinese restaurant franchise&amp;rdquo;. Given observed data $\mathbf{x}$ (i.e. documents), we sample over the index variables $t_{ji}$ (associating tables with customers/words) and $k_{jt}$ (associating tables with dishes/topics). Given these variables, we can reconstruct the distribution over topics for each document and distribution over words for each topic.&lt;/p&gt;
&lt;h4 id="notes-on-implementing-algorithm-51"&gt;Notes on Implementing Algorithm 5.1
&lt;/h4&gt;&lt;p&gt;Teh et al&amp;rsquo;s original HDP paper is sparse on details with regard to applying these samplers to the specific case of nonparametric LDA. For example, both samplers require computing the conditional distribution of word $x_{ji}$ under topic $k$ given all data items except $x_{ji}$: $f_k^{x_{ji}}(x_{ji})$) (eq. 30).&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://shuyo.wordpress.com/2012/08/15/hdp-lda-updates/" target="_blank" rel="noopener"
&gt;A Blogger Formerly Known As Shuyo&lt;/a&gt; has a brief post where he states (with little-to-no derivation) the equations specific to the LDA case. Below I attempt provide some of those derivations in pedantic detail.&lt;/p&gt;
&lt;p&gt;As stated above, in the case of topic modeling, $H$ is a Dirichlet distribution over terms distributions and $F$ is a multinomial distribution over terms.&lt;/p&gt;
&lt;p&gt;By definition,&lt;/p&gt;
\begin{equation} h(\phi_k)=\frac{1}{Z}\prod_v[\phi_k]_v^{\beta-1} \end{equation}&lt;p&gt;and&lt;/p&gt;
\begin{equation}
f(x_{ji}=v \,|\, \phi_k)=\phi_{kv}.
\end{equation}&lt;h5 id="equation-30"&gt;Equation (30): $f_k^{x_{ji}}(x_{ji})$
&lt;/h5&gt;&lt;p&gt;For convenience, define $v=x_{ji}$ (word $i$ in document $j$), define $k=k_{jt_{ji}}$ (topic assignment for the table in document $j$ containing word $i$), and&lt;/p&gt;
\begin{equation}
n_{kv}^{-ji}=\left|\left\{
x_{mn} \,|\, k_{mt_{mn}}=k,\, x_{mn}=v,\, (m,n)\neq(j,i)
\right\}\right|
\end{equation}&lt;p&gt;(the number of times the term $x_{ji}$, besides $x_{ji}$ itself, is generated by the same topic as was $x_{ji}$).&lt;/p&gt;
&lt;p&gt;First look at the term (for fixed $k$):&lt;/p&gt;
\begin{equation}
\prod_{j'i'\neq ji, z_{j'i'=k}}
f(x_{j'i'} \,|\, \phi_k)=
\prod_{j'}
\prod_{i'\neq i, z_{j'i'}=k}
[\phi_{k}]_{x_{j'i'}}
\end{equation}&lt;p&gt;$[\phi_k]_v$ is the probability that term $v$ is generated by topic $k$. The double sums run over every word generated by topic $k$ in every document. Since $[\phi_{k}]_{x_{j'i'}}$ is fixed for a given word $w$, we could instead do a product over the each word of the vocabulary:&lt;/p&gt;
\begin{equation}
\prod_{j'i'\neq ji, z_{j'i'=k}}
f(x_{j'i'} \,|\, \phi_k)
=\prod_{w\in\mathcal{V}}[\phi_k]_w^{n_{kw}^{-ji}}
\end{equation}&lt;p&gt;We use this early on in the big derivation below.&lt;/p&gt;
&lt;p&gt;Also, note that&lt;/p&gt;
\begin{equation}
\int
\phi_{kv}^{n_{kv}^{-ji}+\beta}
\prod_{w\neq v}
\phi_{kw}^{n_{kw}^{-ji}+\beta-1}
d\phi_k \text{ and }
\int
\prod_w
\phi_{kw}^{n_{kw}^{-ji}+\beta-1}
d\phi_k
\end{equation}&lt;p&gt;are the normalizing coefficients for Dirichlet distributions.&lt;/p&gt;
&lt;p&gt;Equation (30) in Teh&amp;rsquo;s paper is:&lt;/p&gt;
\begin{equation}
\begin{aligned}
f_k^{-x_{ji}}(x_{ji})
&amp;=\frac{
\int
f(x_{ji} \,|\, \phi_k)
\left[
\prod_{j'i'\neq ji, z_{j'i'}=k}
f(x_{j'i'} \,|\, \phi_k)
\right]
h(\phi_k)
d\phi_k
}
{
\int
\left[
\prod_{j'i'\neq ji, z_{j'i'}=k}
f(x_{j'i'}|\phi_k)
\right]
h(\phi_k)d\phi_k
} \\
&amp;=\frac{
\int
f(x_{ji} \,|\, \phi_k)
\left[
\prod_{j'}
\prod_{i'\neq i, z_{j'i'}=k}
\phi_{kv}
\right]\cdot
h(\phi_k)
d\phi_k
}
{
\int
\left[
\prod_{j'i'\neq ji, z_{j'i'}=k}
f(x_{j'i'}|\phi_k)
\right]h(\phi_k)d\phi_k
} \\
&amp;\propto\frac{
\int
\phi_{kv}
\prod_w
\phi_{kw}^{n_{kw}^{-ji}}
\prod_{w}
\phi_{kw}^{\beta-1}
d\phi_k
}{
\int
\prod_w
\phi_{kw}^{n_{kw}^{-ji}}
\prod_w
\phi_{kw}^{\beta-1}
d\phi_k
}\\
&amp;=\frac{
\int
\phi_{kv}\cdot
\phi_{kv}^{n_{kv}^{-ji}}\cdot
\prod_{w\neq v}
\phi_{kw}^{n_{kw}^{-ji}}\cdot
\phi_{kv}^{\beta-1}\cdot
\prod_{w\neq v}
\phi_{kw}^{\beta-1}
d\phi_k
}{
\int
\prod_w
\phi_{kw}^{n_{kw}^{-ji}}
\prod_w
\phi_{kw}^{\beta-1}
d\phi_k
}\\
&amp;=
\int
\phi_{kv}^{n_{kv}^{-ji}+\beta}
\prod_{w\neq v}
\phi_{kw}^{n_{kw}^{-ji}+\beta-1}
d\phi_k
\cdot
\frac{
1
}{
\int
\prod_w
\phi_{kw}^{n_{kw}^{-ji}+\beta-1}
d\phi_k.
}
\end{aligned}
\end{equation}&lt;p&gt;Recognizing these integrals as those that occur in the Dirichlet distribution, we have,&lt;/p&gt;
\begin{equation}
\begin{aligned}
f_k^{-x_{ji}}(x_{ji})
&amp;=\frac{
\Gamma\left(\beta+n_{kv}^{-ji}+1\right)
\prod_{w\neq v} \Gamma\left(\beta+n_{kw}^{-ji}\right)
}{
\Gamma\left(
\sum_{w\neq v}
\left[
\beta+n_{kw}^{-ji}
\right]+
(\beta+n_{kv}^{-ji}+1)\right)
}\cdot
\frac{
1
}{
\int\prod_w
\left(\phi_{kw}\right)^{n_{kw}^{-ji}+\beta-1}
d\phi_k
}\\
&amp;=\frac{
\Gamma\left(\beta+n_{kv}^{-ji}+1\right)
\prod_{w\neq v} \Gamma\left(\beta+n_{kw}^{-ji}\right)
}{
\Gamma\left(
\sum_{w\in\mathcal{V}}
\left[
\beta+n_{kw}^{-ji}
\right]
+1\right)
}\cdot
\frac{
1
}{
\int\prod_w
\left(\phi_{kw}\right)^{n_{kw}^{-ji}+\beta-1}
d\phi_k
}\\
&amp;=\frac{
\Gamma\left(\beta+n_{kv}^{-ji}+1\right)
\prod_{w\neq v} \Gamma\left(\beta+n_{kw}^{-ji}\right)
}{
\Gamma\left(
\sum_{w\in\mathcal{V}}
\left[
\beta+n_{kw}^{-ji}
\right]
+1\right)
}\cdot
\frac{
\Gamma\left(
\sum_{w\in\mathcal{V}}
\left[
\beta+n_{kw}^{-ji}
\right]
\right)
}{
\prod_{w}\Gamma\left(\beta+n_{kw}^{-ji}\right)
}\\
&amp;=\frac{
\Gamma\left(\beta+n_{kv}^{-ji}+1\right)
\prod_{w\neq v} \Gamma\left(\beta+n_{kw}^{-ji}\right)
}{
\Gamma\left(V\beta+n_{k\cdot}^{-ji}+1\right)
}\cdot
\frac{
\Gamma\left(V\beta+n_{k\cdot}^{-ji}\right)
}{
\prod_{w}\Gamma\left(\beta+n_{kw}^{-ji}\right)
}\\
&amp;=\frac{
\Gamma\left(\beta+n_{kv}^{-ji}+1\right)
\Gamma\left(V\beta+n_{k\cdot}^{-ji}\right)
}{
\Gamma\left(V\beta+n_{k\cdot}^{-ji}+1\right)
}\cdot
\frac{
\prod_{w\neq v} \Gamma\left(\beta+n_{kw}^{-ji}\right)
}{
\prod_{w}\Gamma\left(\beta+n_{kw}^{-ji}\right)
}\\
&amp;=\frac{
\Gamma\left(\beta+n_{kn}^{-ji}+1\right)
\Gamma\left(V\beta+n_{k\cdot}^{-ji}\right)
}{
\Gamma\left(V\beta+n_{k\cdot}^{-ji} + 1\right)
\Gamma\left(\beta+n_{kv}^{-ji}\right)
}\\
&amp;=\frac{
\Gamma\left(\beta+n_{kn}^{-ji}+1\right)
}{
\Gamma\left(\beta+n_{kv}^{-ji}\right)
}\cdot
\frac{
\Gamma\left(V\beta+n_{k\cdot}^{-ji}\right)
}{
\Gamma\left(V\beta+n_{k\cdot}^{-ji} + 1\right)
}\\
&amp;=\frac{\beta+n_{kv}^{-ji}}{V\beta+n_{k\cdot}^{-ji}}
\end{aligned}
\end{equation}&lt;p&gt;This is validated in the &lt;a class="link" href="http://arxiv.org/pdf/1201.1657.pdf" target="_blank" rel="noopener"
&gt;appendix of this paper&lt;/a&gt;.&lt;/p&gt;
&lt;h5 id="equation"&gt;Equation: $f_{k^\text{new}}^{x_{ji}}(x_{ji})$
&lt;/h5&gt;&lt;p&gt;We also need the prior density of $x_{ji}$ to compute the likelihood that $x_{ji}$ will be seated at a new table.&lt;/p&gt;
\begin{equation}
\begin{aligned}
f_{k^{\text{new}}}^{-x_{ji}}(x_{ji})
&amp;=
\int
f(x_{ji} \,|\, \phi)
h(\phi)d\phi_k
\\
&amp;=\int
\phi_v \cdot
\frac{
\Gamma(V\beta)
}{
\prod_w \Gamma(\beta)
}
\prod_w \phi_w^{\beta-1}
d\phi \\
&amp;=\frac{
\Gamma(V\beta)
}{
\prod_w \Gamma(\beta)
}
\int
\phi_v \cdot
\prod_w \phi_w^{\beta-1}
d\phi\\
&amp;=\frac{
\Gamma(V\beta)
}{
\prod_w \Gamma(\beta)
}
\int
\phi_v^\beta \cdot
\prod_{w\neq v} \phi_w^{\beta-1}
d\phi\\
&amp;=\frac{
\Gamma(V\beta)
}{
\prod_w \Gamma(\beta)
}\cdot
\frac{
\Gamma(\beta+1)\prod_{w\neq v}\Gamma(\beta)
}{
\Gamma(V\beta+1)
}\\
&amp;=\frac{
\Gamma(V\beta)
}{
\Gamma(V \beta+1)
}\cdot
\frac{
\beta\prod_{w}\Gamma(\beta)
}{
\prod_w \Gamma(\beta)
}\\
&amp;=\frac{
1
}{
V \beta
}\cdot
\beta\\
&amp;= \frac{1}{V}
\end{aligned}
\end{equation}&lt;h5 id="equation-31"&gt;Equation (31): $p(x_{ji} \,|\, {\bf t}^{-ji}, t_{ji}=t^{new}, {\bf k})$
&lt;/h5&gt;&lt;p&gt;These last two derivations give us Equation (31), the likelihood that $t_{ji}=t^{new}$:&lt;/p&gt;
\begin{equation}
\begin{aligned}
p(x_{ji} \,|\, {\bf t}^{-ji}, t_{ji}=t^{new}, {\bf k})
&amp;=\sum_{k=1}^K \left[
\frac{
m_{\cdot k}
}{
m_{\cdot\cdot}+\gamma
}\cdot
\frac{\beta+n_{kv}^{-ji}}{V\beta+n_{k\cdot}^{-ji}}
\right] \\
&amp;\phantom{=}+ \frac{
\gamma
}{
m_{\cdot\cdot}+\gamma
}
\cdot \frac{1}{V}
\end{aligned}
\end{equation}&lt;h5 id="equation-32"&gt;Equation (32): $p(t_{ji}=t \,|\, {\bf t}^{-ji}, {\bf k})$
&lt;/h5&gt;&lt;p&gt;From this, we know the conditional distribution of $t_{ji}$ is:&lt;/p&gt;
\begin{equation}
p(t_{ji}=t \,|\, {\bf t}^{-ji}, {\bf k}) \propto
\begin{cases}
n_{jt\cdot}^{-ji} \cdot \frac{\beta+n_{k_{jt}v}^{-ji}}{V\beta+n_{k_{jt}\cdot}^{-ji}}
&amp; {\tiny \text{if } t \text{ previously used,}}\\
{\tiny
\alpha_0 \cdot
p(x_{ji} \,|\, {\bf t}^{-ji}, t_{ji}=t^{new}, {\bf k})}
&amp; {\tiny \text{if } t=t^{\text{new}}}.
\end{cases}
\end{equation}&lt;h5 id="equation-33"&gt;Equation (33): $p(k_{jt^\text{new}}=k \,|\, {\bf t}, {\bf k}^{-jt^\text{new}})$
&lt;/h5&gt;&lt;p&gt;If the sampled value of $t_{ji}$ is $t^{\text{new}}$, we sample a dish $k_{jt^\text{new}}$ for the table with:&lt;/p&gt;
\begin{equation}
p(k_{jt^\text{new}}=k \,|\, {\bf t}, {\bf k}^{-jt^\text{new}}) \propto
\begin{cases}
m_{\cdot k}\cdot\frac{\beta+n_{kv}^{-ji}}{V\beta+n_{k\cdot}^{-ji}}
&amp; {\tiny \text{if } k \text{ previously used,}}\\
\frac{
\gamma
}{V}
&amp; {\tiny \text{if } t=k^{\text{new}}}.
\end{cases}
\end{equation}&lt;h5 id="equation-34"&gt;Equation (34): $p(k_{jt}=k \,|\, {\bf t}, {\bf k}^{-jt})$
&lt;/h5&gt;&lt;p&gt;We need to sample $k_{jt}$ (the dish/topic for table $t$ in restaurant $j$):&lt;/p&gt;
\begin{equation}\displaystyle
p(k_{jt}=k \,|\, {\bf t}, {\bf k}^{-jt}) \propto
\begin{cases}
m_{\cdot k}^{-jt}\cdot f_k^{-{\bf x}_{jt}}({\bf x}_{jt})
&amp; {\tiny \text{if } k \text{ previously used,}}\\
\gamma\cdot f_{k^\text{new}}^{-{\bf x}_{jt}}({\bf x}_{jt})
&amp; {\tiny \text{if } t=k^{\text{new}}}.
\end{cases}
\end{equation}&lt;p&gt;where $f_k^{-{\bf x}_{jt}}({\bf x}_{jt})$ is the &amp;ldquo;conditional density of ${\bf x}_{jt}$ given all data items associated with mixture component $k$ leaving out ${\bf x}_{jt}$&amp;rdquo; (Teh, et al). (${\bf x}_{jt}$ is every customer in restaurant $j$ seated at table $t$). $m_{\cdot k}^{-jt}$ is the number of tables (in all franchises) serving dish $k$ when we remove table $jt$.&lt;/p&gt;
&lt;p&gt;This requires $f_k^{-{\bf x}_{jt}}({\bf x}_{jt})$; this is different from Equation (30), though they look quite similar.&lt;/p&gt;
\begin{equation}
\begin{aligned}
f_k^{-{\bf x}_{jt}}({\bf x}_{jt})
&amp;=\frac{\displaystyle
\int
{\prod_{x_{ji}\in {\bf x}_{jt}}}
f(x_{ji} \,|\, \phi_k)
\left[
\prod_{x_{j'i'}\not\in {\bf x}_{jt}, z_{j'i'}=k}
f(x_{j'i'} \,|\, \phi_k)
\right]
h(\phi_k)
d\phi_k
}
{\displaystyle
\int
\left[
\displaystyle\prod_{x_{j'i'}\not\in {\bf x}_{jt}, z_{j'i'}=k}
f(x_{j'i'}|\phi_k)
\right]
h(\phi_k)d\phi_k
} \\
&amp;=\frac{\displaystyle
\int
{\prod_{x_{ji}\in {\bf x}_{jt}}}
\phi_{k x_{ji}}
\left[
\prod_{x_{j'i'}\not\in {\bf x}_{jt}, z_{j'i'}=k}
\phi_{k x_{j'i'}}
\right]
\prod_{w}
\phi_{kw}^{\beta-1}
d\phi_k
}
{\displaystyle
\int
\left[
\displaystyle\prod_{x_{j'i'}\not\in {\bf x}_{jt}, z_{j'i'}=k}
f(x_{j'i'}|\phi_k)
\right]
\prod_{w}
\phi_{kw}^{\beta-1}
d\phi_k
} \\
&amp;=\frac{\displaystyle
\int
{\prod_{x_{ji}\in {\bf x}_{jt}}}
\phi_{k x_{ji}}
\left[
\prod_{x_{j'i'}\not\in {\bf x}_{jt}, z_{j'i'}=k}
\phi_{k x_{j'i'}}
\right]
\prod_{w}
\phi_{kw}^{\beta-1}
d\phi_k
}
{\displaystyle
\int
\left[
\displaystyle\prod_{x_{j'i'}\not\in {\bf x_{jt}}, z_{j'i'}=k}
\phi_{k x_{j'i'}}
\right]
\prod_{w}
\phi_{kw}^{\beta-1}
d\phi_k
}
\end{aligned}
\end{equation}&lt;p&gt;The denominator is&lt;/p&gt;
\begin{equation}
\begin{aligned}
\text{denominator}&amp;=
\int\left[
\prod_{x_{j'i'}\not\in {\bf x_{jt}}, z_{j'i'}=k}
\phi_{k x_{j'i'}}
\right]
\prod_{w}
\phi_{kw}^{\beta-1}
d\phi_k \\
&amp;=\int\left[
\prod_w \phi_{kw}^{n_{kw}^{-jt}} \prod_w \phi_{kw}^{\beta-1}
\right]
d\phi_k \\
&amp;=\int\left[
\prod_w \phi_{kw}^{n_{kw}^{-jt}+\beta-1}
\right]
d\phi_k \\
&amp;=\frac{
\prod_w \Gamma\left(
n_{kw}^{-jt} + \beta
\right)
}{
\Gamma\left( \sum_w
n_{kw}^{-jt}+\beta
\right)
} \\
&amp;=\frac{
\prod_w \Gamma\left(
n_{kw}^{-jt} + \beta
\right)
}{
\Gamma\left(
n_{k\cdot}^{-jt}+V\beta
\right)
}
\end{aligned}
\end{equation}&lt;p&gt;The numerator is&lt;/p&gt;
\begin{equation}
\begin{aligned}
\text{numerator}
&amp;=\int
{\prod_{x_{ji}\in {\bf x}_{jt}}}
\phi_{k x_{ji}}
\left[
\prod_{x_{j'i'}\not\in {\bf x}_{jt}, z_{j'i'}=k}
\phi_{k x_{j'i'}}
\right]
\prod_{w}
\phi_{kw}^{\beta-1}
d\phi_k \\
&amp;=\int
\prod_{w}
\phi_{kw}^{
n_{kw}^{-jt} +
n_{\cdot w}^{jt} +
\beta + 1
}
d\phi_k \\
&amp;=\frac{
\prod_w \Gamma\left(
n_{kw}^{-jt} + n_{\cdot w}^{jt} + \beta
\right)
}{
\Gamma \left(
\sum_w
n_{kw}^{-jt} + n_{\cdot w}^{jt} + \beta
\right)
}\\
&amp;=\frac{
\prod_w \Gamma\left(
n_{kw}^{-jt} + n_{\cdot w}^{jt} + \beta
\right)
}{
\Gamma \left(
n_{k\cdot}^{-jt} + n_{\cdot \cdot}^{jt} + \beta
\right)
}
\end{aligned}
\end{equation}&lt;p&gt;This gives us a closed form version of this conditional distribution:&lt;/p&gt;
\begin{equation}
\begin{aligned}
f_k^{-{\bf x_{jt}}}({\bf x}_{jt})
&amp;= \displaystyle\frac{
\prod_w \Gamma\left(
n_{kw}^{-jt} + n_{\cdot w}^{jt} + \beta
\right)
}{
\prod_w \Gamma\left(
n_{kw}^{-jt} + \beta
\right)
}
\frac{
\Gamma\left(
n_{k\cdot}^{-jt}+V\beta
\right)
}{
\Gamma \left(
n_{k\cdot}^{-jt} + n_{\cdot \cdot}^{jt} + \beta
\right)
}.
\end{aligned}
\end{equation}&lt;p&gt;We also need the conditional distribution of $k$ is a new dish: $f_{k^\text{new}}^{-{\bf x}_{jt}}({\bf x}_{jt})$. Shuyo provides without derivation:&lt;/p&gt;
\begin{equation}
\begin{aligned}
f_{k^\text{new}}^{-{\bf x}_{jt}}({\bf x}_{jt})
&amp;=\int
\left[
\prod_{x_{ji}\in \mathbf{x_{jt}}}
f(x_{ji} \,|\, \phi)
\right]
h(\phi)d\phi_k
\\
&amp;=\int
\prod_{x_{ji}\in \mathbf{x_{jt}}}
\phi_{x_{ji}}
\cdot
\frac{
\Gamma(V\beta)
}{
\prod_w \Gamma(\beta)
}
\prod_w \phi_w^{\beta-1}
d\phi \\
&amp;=\frac{
\Gamma(V\beta)
}{
\prod_w \Gamma(\beta)
}
\int
\prod_{x_{ji}\in \mathbf{x_{jt}}}
\phi_{x_{ji}}
\cdot
\prod_w \phi_w^{\beta-1}
d\phi\\
&amp;=\frac{
\Gamma(V\beta)
}{
\prod_w \Gamma(\beta)
}
\int
\prod_{x_{ji}\in \mathbf{x_{jt}}}
\phi_{x_{ji}}^{\left(\beta+1\right)-1}
\cdot
\prod_{x_{jt}\not\in \mathbf{x_{jt}}} \phi_{x_{jt}}^{\beta-1}
d\phi\\
&amp;=\frac{
\Gamma(V\beta)
}{
\prod_w \Gamma(\beta)
}\cdot
\frac{
\prod_{x_{ji}\in \mathbf{x_{jt}}}
\Gamma(\beta+1)
\prod_{x_{ji}\not\in \mathbf{x_{jt}}}\Gamma(\beta)
}{
\Gamma(V\beta+\sum_{x_{ji}\in \mathbf{x_{jt}}} 1)
}\\
&amp;=\frac{
\Gamma(V\beta)
\prod_w \Gamma(\beta+n_{\cdot w}^{jt})
}{
\Gamma(V\beta+n_{\cdot\cdot}^{jt})
\prod_w \Gamma(\beta)
}.
\end{aligned}
\end{equation}&lt;p&gt;Given these equations for $f_{k}^{-{\bf x}_{jt}}({\bf x}_{jt})$ and $f_{k^\text{new}}^{-{\bf x}_{jt}}({\bf x}_{jt})$, we can draw samples from $p(k_{jt}=k \,|\, {\bf t}, {\bf k}^{-jt})$ by enumeration over topics. We now have a complete Gibbs sampler for the &lt;a class="link" href="http://www.cs.berkeley.edu/~jordan/papers/hdp.pdf" target="_blank" rel="noopener"
&gt;Posterior sampling in the Chinese restaurant franchise in Teh, et al.&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Data Science and Agriculture</title><link>https://tdhopper.com/blog/data-science-and-agriculture/</link><pubDate>Thu, 17 Sep 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/data-science-and-agriculture/</guid><description>&lt;p&gt;I&amp;rsquo;m excited to see tools developed for the web being applied to offline domains like agriculture and health. I posed a question on Twitter yesterday:&lt;/p&gt;
&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;Who is hiring at the intersection data science and agriculture?&lt;/p&gt;&amp;mdash; Tim Hopper (@tdhopper) &lt;a href="https://twitter.com/tdhopper/status/644138076521021440"&gt;September 16, 2015&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;I got a number of replies. Companies in this space (not all hiring for DS) include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.climate.com/" target="_blank" rel="noopener"
&gt;The Climate Corporation&lt;/a&gt;: Known for their high profile acquisition by Monsanto, Climate Corp &amp;ldquo;combines hyper-local weather monitoring, agronomic modeling, and high-resolution weather simulations&amp;rdquo; &amp;ldquo;that help farmers improve profitability by making better informed operating and financing decisions&amp;rdquo;. They are based in San Fransisco.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://farmlogs.com/" target="_blank" rel="noopener"
&gt;FarmLogs&lt;/a&gt;: A YCombinator-backed startup in Ann Arbor, MI, FarmLogs is combining satellite maps, soil surveys, GPS data, and more to &amp;ldquo;expose critical operational data insights&amp;rdquo; to row crop farmers.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://dairymaster.ie/careers/data-scientists/" target="_blank" rel="noopener"
&gt;Dairymaster&lt;/a&gt;: In Ireland, Darymaster is hiring data scientists for their business of dairy equipment manufacturing.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.awhere.com/" target="_blank" rel="noopener"
&gt;aWhere&lt;/a&gt;: aWhere &amp;ldquo;delivers agricultural intelligence into the hands of farmers, commercial growers, and policymakers everywhere&amp;rdquo; by collecting detailed weather data from around the world. They are based in Denver.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.pulsepod.io/" target="_blank" rel="noopener"
&gt;pulsepod&lt;/a&gt;: This small startup is building hardware to help farmers &amp;ldquo;know when to plant, fertilize, irrigate, and harvest to achieve quality and yield goals using data from your own field&amp;rdquo;. They are based in Princeton, NJ.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.granular.ag/" target="_blank" rel="noopener"
&gt;Granular&lt;/a&gt;: &amp;ldquo;Granular, a new kind of farm management software and analytics platform, helps you improve efficiency, profit and yield so you are ready to farm more acres.&amp;rdquo; They are based in San Fransisco.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.precisionhawk.com/" target="_blank" rel="noopener"
&gt;PrecisionHawk&lt;/a&gt;: Based in my home of Raleigh, NC, PrecisionHawk is &amp;ldquo;an information delivery company that combines unmanned aerial systems, cutting-edge artificial intelligence and remote sensing technologies to improve business operations and day-to-day decision making.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.mavrx.com" target="_blank" rel="noopener"
&gt;mavrx&lt;/a&gt;: &amp;ldquo;We use aerial imagery to provide actionable insights to the global agriculture industry.&amp;rdquo; They are based in San Fransisco.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.agsmarts.com/" target="_blank" rel="noopener"
&gt;AgSmarts&lt;/a&gt;: Memphis-based Agsmarts &amp;ldquo;is a Precision Agriculture company that automates existing agricultural irrigation systems with our universal retrofit solution to optimize crop yield, save water and AgSmarts minimize input costs via mesh networks of IP-enabled controllers &amp;amp; environmental sensors.&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Classes Future Programmers Should Take</title><link>https://tdhopper.com/blog/classes-future-programmers-should-take/</link><pubDate>Sat, 12 Sep 2015 01:55:00 +0000</pubDate><guid>https://tdhopper.com/blog/classes-future-programmers-should-take/</guid><description>&lt;p&gt;I appreciated &lt;a class="link" href="https://twitter.com/dadgumjames" target="_blank" rel="noopener"
&gt;James Hague&lt;/a&gt;&amp;rsquo;s post on
&lt;a class="link" href="http://prog21.dadgum.com/210.html" target="_blank" rel="noopener"
&gt;Computer Science Courses that Don&amp;rsquo;t Exist, But
Should&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I really like &lt;a class="link" href="http://www.se-radio.net/2009/11/episode-148-software-archaeology-with-dave-thomas/" target="_blank" rel="noopener"
&gt;Dave Thomas&amp;rsquo;s idea of a Software Archeology class&lt;/a&gt;. I have spent a huge amount of time as a developer reading (vs.
writing) code. I wish I&amp;rsquo;d been taught how to read code effectively.&lt;/p&gt;
&lt;p&gt;Similarly, I have thought there should be a class (or series of classes) in
&amp;ldquo;interacting with others&amp;rsquo; code&amp;rdquo;. Topics could include inheriting a software
project, handing off a software project, writing code using 3rd party
libraries, using package repositories, and understanding software licenses.
These are such important skills in real world software development, but they
seem to be rarely taught in the classroom. Perhaps a follow-up class could be
&amp;ldquo;contributing to open source&amp;rdquo;.&lt;/p&gt;</description></item><item><title>Dealing with Outliers</title><link>https://tdhopper.com/blog/dealing-with-outliers/</link><pubDate>Tue, 08 Sep 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/dealing-with-outliers/</guid><description>&lt;p&gt;From &lt;a class="link" href="http://www.pnas.org/content/110/39/15746.full" target="_blank" rel="noopener"
&gt;Testicular volume is inversely correlated with nurturing-related brain activity in human fathers&lt;/a&gt; in PNAS:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;One participant&amp;rsquo;s testes volume measurement was excluded because his value was 2.8 SDs above the mean (mean = 38,064; SD = 11,183) and was more than 13,000 mm^3 larger than any recorded value found in the literature. Of the more than 1,500 healthy, age-matched men in these studies, the largest reported value was 56,000 mm^3, and this participant’s measurement was 69,736 mm^3.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Fitting a Mixture Model with Gibbs Sampling</title><link>https://tdhopper.com/blog/mixture-model/</link><pubDate>Wed, 02 Sep 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/mixture-model/</guid><description>&lt;img src="https://tdhopper.com/images/mixture-model-mr-men.png" alt="Featured image of post Fitting a Mixture Model with Gibbs Sampling" /&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;pd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;np&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;plt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;scipy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;namedtuple&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Suppose we receive some data that looks like the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Series&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;clusters.csv&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/mixture-model/2015-09-02-fitting-a-mixture-model_3_0.png"
width="377"
height="256"
srcset="https://tdhopper.com/blog/mixture-model/2015-09-02-fitting-a-mixture-model_3_0_hu_746e691434352d36.png 480w, https://tdhopper.com/blog/mixture-model/2015-09-02-fitting-a-mixture-model_3_0_hu_d70ff04bbc9236ad.png 1024w"
loading="lazy"
alt="Histogram of clustered data"
class="gallery-image"
data-flex-grow="147"
data-flex-basis="353px"
&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;pre&gt;&lt;code&gt;1000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It appears that these data exist in three separate clusters. We want to develop a method for finding these &lt;em&gt;latent&lt;/em&gt; clusters. One way to start developing a method is to attempt to describe the process that may have generated these data.&lt;/p&gt;
&lt;p&gt;For simplicity and sanity, let&amp;rsquo;s assume that each data point is generated independently of the other. Moreover, we will assume that within each cluster, the data points are identically distributed. In this case, we will assume each cluster is normally distributed and that each cluster has the same variance, $\sigma^2$.&lt;/p&gt;
&lt;p&gt;Given these assumptions, our data could have been generated by the following process. For each data point, randomly select 1 of 3 clusters from the distribution $\text{Discrete}(\pi_1, \pi_2, \pi_3)$. Each cluster $k$ corresponds to a parameter $\theta_k$ for that cluster, sample a data point from $\mathcal{N}(\theta_k, \sigma^2)$.&lt;/p&gt;
&lt;p&gt;Equivalently, we could consider these data to be generated from a probability distribution with this probability density function:&lt;/p&gt;
$$
p(x_i \,|\, \pi, \theta_1, \theta_2, \theta_3, \sigma)=
\sum_{k=1}^3 \pi_k\cdot
\frac{1}{\sigma\sqrt{2\pi}}
\text{exp}\left\{
\frac{-(x_i-\theta_k)^2}{2\sigma^2}
\right\}
$$&lt;p&gt;where $\pi$ is a 3-dimensional vector giving the &lt;em&gt;mixing proportions&lt;/em&gt;. In other words, $\pi_k$ describes the proportion of points that occur in cluster $k$.&lt;/p&gt;
&lt;p&gt;That is, &lt;em&gt;the probability distribution describing $x$ is a linear combination of normal distributions&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We want to use this &lt;em&gt;generative&lt;/em&gt; model to formulate an algorithm for determining the particular parameters that generated the dataset above. The $\pi$ vector is unknown to us, as is each cluster mean $\theta_k$.&lt;/p&gt;
&lt;p&gt;We would also like to know $z_i\in\{1, 2, 3\}$, the latent cluster for each point. It turns out that introducing $z_i$ into our model will help us solve for the other values.&lt;/p&gt;
&lt;p&gt;The joint distribution of our observed data (&lt;code&gt;data&lt;/code&gt;) along with the assignment variables is given by:&lt;/p&gt;
\begin{align}
p(\mathbf{x}, \mathbf{z} \,|\, \pi, \theta_1, \theta_2, \theta_3, \sigma)&amp;=
p(\mathbf{z} \,|\, \pi)
p(\mathbf{x} \,|\, \mathbf{z}, \theta_1, \theta_2, \theta_3, \sigma)\\
&amp;= \prod_{i=1}^N p(z_i \,|\, \pi)
\prod_{i=1}^N p(x_i \,|\, z_i, \theta_1, \theta_2, \theta_3, \sigma) \\
&amp;= \prod_{i=1}^N \pi_{z_i}
\prod_{i=1}^N
\frac{1}{\sigma\sqrt{2\pi}}
\text{exp}\left\{
\frac{-(x_i-\theta_{z_i})^2}{2\sigma^2}
\right\}\\
&amp;= \prod_{i=1}^N
\left(
\pi_{z_i}
\frac{1}{\sigma\sqrt{2\pi}}
\text{exp}\left\{
\frac{-(x_i-\theta_{z_i})^2}{2\sigma^2}
\right\}
\right)\\
&amp;=
\prod_i^n
\prod_k^K
\left(
\pi_k
\frac{1}{\sigma\sqrt{2\pi}}
\text{exp}\left\{
\frac{-(x_i-\theta_k)^2}{2\sigma^2}
\right\}
\right)^{\delta(z_i, k)}
\end{align}&lt;h3 id="keeping-everything-straight"&gt;Keeping Everything Straight
&lt;/h3&gt;&lt;p&gt;Before moving on, we need to devise a way to keep all our data and parameters straight. Following ideas suggested by &lt;a class="link" href="http://people.csail.mit.edu/bonawitz/Composable%20Probabilistic%20Inference%20with%20Blaise%20-%20Keith%20Bonawitz%20PhD%20Thesis.pdf" target="_blank" rel="noopener"
&gt;Keith Bonawitz&lt;/a&gt;, let&amp;rsquo;s define a &amp;ldquo;state&amp;rdquo; object to store all of this data.&lt;/p&gt;
&lt;p&gt;It won&amp;rsquo;t yet be clear why we are defining some components of &lt;code&gt;state&lt;/code&gt;, however we will use each part eventually! As an attempt at clarity, I am using a trailing underscore in the names of members that are fixed. We will update the other parameters as we try to fit the model.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;SuffStat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;namedtuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;SuffStat&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;theta N&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_suffstats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iteritems&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;points_in_cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;cluster_id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;points_in_cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SuffStat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initial_state&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;num_clusters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cluster_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_clusters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;cluster_ids_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cluster_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;data_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;num_clusters_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;num_clusters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;cluster_variance_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;alpha_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;hyperparameters_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;mean&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;variance&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster_ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;pi&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;num_clusters&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cluster_ids&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;cluster_means&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;update_suffstats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initial_state&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;pre&gt;&lt;code&gt;num_clusters_
suffstats
data_
cluster_means
cluster_variance_
cluster_ids_
assignment
pi
alpha_
hyperparameters_
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="gibbs-sampling"&gt;Gibbs Sampling
&lt;/h3&gt;&lt;p&gt;The &lt;a class="link" href="https://en.wikipedia.org/wiki/Gibbs_sampling" target="_blank" rel="noopener"
&gt;theory of Gibbs sampling&lt;/a&gt; tells us that given some data $\bf y$ and a probability distribution $p$ parameterized by $\gamma_1, \ldots, \gamma_d$, we can successively draw samples from the distribution by sampling from&lt;/p&gt;
$$\gamma_j^{(t)}\sim p(\gamma_j \,|\, \gamma_{\neg j}^{(t-1)})$$&lt;p&gt;where $\gamma_{\neg j}^{(t-1)}$ is all current values of $\gamma_i$ except for $\gamma_j$. If we sample long enough, these $\gamma_j$ values will be random samples from $p$.&lt;/p&gt;
&lt;p&gt;In deriving a Gibbs sampler, it is often helpful to observe that&lt;/p&gt;
$$
p(\gamma_j \,|\, \gamma_{\neg j})
= \frac{
p(\gamma_1,\ldots,\gamma_d)
}{
p(\gamma_{\neg j})
} \propto p(\gamma_1,\ldots,\gamma_d).
$$&lt;p&gt;The conditional distribution is proportional to the joint distribution. We will get a lot of mileage from this simple observation by dropping constant terms from the joint distribution (relative to the parameters we are conditioned on).&lt;/p&gt;
&lt;p&gt;The $\gamma$ values in our model are each of the $\theta_k$ values, the $z_i$ values, and the $\pi_k$ values. Thus, we need to derive the conditional distributions for each of these.&lt;/p&gt;
&lt;p&gt;Many derivation of Gibbs samplers that I have seen rely on a lot of handwaving and casual appeals to conjugacy. I have tried to add more mathematical details here. I would gladly accept feedback on how to more clearly present the derivations! I have also tried to make the derivations more concrete by immediately providing code to do the computations in this specific case.&lt;/p&gt;
&lt;h4 id="conditional-distribution-of-assignment"&gt;Conditional Distribution of Assignment
&lt;/h4&gt;&lt;p&gt;For berevity, we will use&lt;/p&gt;
$$
p(z_i=k \,|\, \cdot)=
p(z_i=k \,|\,
z_{\neg i}, \pi,
\theta_1, \theta_2, \theta_3, \sigma, \bf x
).
$$&lt;p&gt;Because cluster assignements are conditionally independent given the cluster weights and paramters,&lt;/p&gt;
\begin{align}
p(z_i=k \,|\, \cdot)
&amp;\propto
\prod_i^n
\prod_k^K
\left(
\pi_k
\frac{1}{\sigma\sqrt{2\pi}}
\text{exp}\left\{
\frac{-(x_i-\theta_k)^2}{2\sigma^2}
\right\}
\right)^{\delta(z_i, k)} \\
&amp;\propto
\pi_k \cdot
\frac{1}{\sigma\sqrt{2\pi}}
\text{exp}\left\{
\frac{-(x_i-\theta_k)^2}{2\sigma^2}
\right\}
\end{align}&lt;p&gt;This equation intuitively makes sense: point $i$ is more likely to be in cluster $k$ if $k$ is itself probable ($\pi_k\gg 0$) and $x_i$ is close to the mean of the cluster $\theta_k$.&lt;/p&gt;
&lt;p&gt;For each data point $i$, we can compute $p(z_i=k \,|\, \cdot)$ for each of cluster $k$. These values are the unnormalized parameters to a discrete distribution from which we can sample assignments.&lt;/p&gt;
&lt;p&gt;Below, we define functions for doing this sampling. &lt;code&gt;sample_assignment&lt;/code&gt; will generate a sample from the posterior assignment distribution for the specified data point. &lt;code&gt;update_assignment&lt;/code&gt; will sample from the posterior assignment for each data point and update the &lt;code&gt;state&lt;/code&gt; object.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_assignment_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;log p(z_i=k \,|\, \cdot)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; We compute these scores in log space for numerical stability.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;theta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_means&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_variance_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log_pi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pi&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;log_pi&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;norm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logpdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;theta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assigment_probs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;p(z_i=cid \,|\, \cdot) for cid in cluster_ids
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;log_assignment_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_ids_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sample_assignment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Sample cluster assignment for data_id given current state
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; cf Step 1 of Algorithm 2.1 in Sudderth 2006
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assigment_probs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_ids_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_assignment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Update cluster assignment for each data point given current state
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; cf Step 1 of Algorithm 2.1 in Sudderth 2006
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sample_assignment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;update_suffstats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="conditional-distribution-of-mixture-weights"&gt;Conditional Distribution of Mixture Weights
&lt;/h4&gt;&lt;p&gt;We can similarly derive the conditional distributions of mixture weights by an application of Bayes theorem. Instead of updating each component of $\pi$ separately, we update them together (this is called blocked Gibbs).&lt;/p&gt;
\begin{align}
p(\pi \,|\, \cdot)&amp;=
p(\pi \,|\,
\bf{z},
\theta_1, \theta_2, \theta_3,
\sigma, \mathbf{x}, \alpha
)\\
&amp;\propto
p(\pi \,|\,
\mathbf{x},
\theta_1, \theta_2, \theta_3,
\sigma, \alpha
)
p(\bf{z}\ \,|\,
\mathbf{x},
\theta_1, \theta_2, \theta_3,
\sigma, \pi, \alpha
)\\
&amp;=
p(\pi \,|\,
\alpha
)
p(\bf{z}\ \,|\,
\mathbf{x},
\theta_1, \theta_2, \theta_3,
\sigma, \pi, \alpha
)\\
&amp;=
\prod_{i=1}^K \pi_k^{\alpha/K - 1}
\prod_{i=1}^K \pi_k^{\sum_{i=1}^N \delta(z_i, k)} \\
&amp;=\prod_{k=1}^3 \pi_k^{\alpha/K+\sum_{i=1}^N \delta(z_i, k)-1}\\
&amp;\propto \text{Dir}\left(
\sum_{i=1}^N \delta(z_i, 1)+\alpha/K,
\sum_{i=1}^N \delta(z_i, 2)+\alpha/K,
\sum_{i=1}^N \delta(z_i, 3)+\alpha/K
\right)
\end{align}&lt;p&gt;Here are Python functions to sample from the mixture weights given the current &lt;code&gt;state&lt;/code&gt; and to update the mixture weights in the &lt;code&gt;state&lt;/code&gt; object.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sample_mixture_weights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Sample new mixture weights from current state according to
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; a Dirichlet distribution
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; cf Step 2 of Algorithm 2.1 in Sudderth 2006
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;alpha_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;num_clusters_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_ids_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirichlet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_mixture_weights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Update state with new mixture weights from current state
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; sampled according to a Dirichlet distribution
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; cf Step 2 of Algorithm 2.1 in Sudderth 2006
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pi&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sample_mixture_weights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="conditional-distribution-of-cluster-means"&gt;Conditional Distribution of Cluster Means
&lt;/h4&gt;&lt;p&gt;Finally, we need to compute the conditional distribution for the cluster means.&lt;/p&gt;
&lt;p&gt;We assume the unknown cluster means are distributed according to a normal distribution with hyperparameter mean $\lambda_1$ and variance $\lambda_2^2$. The final step in this derivation comes from the normal-normal conjugacy. For more information see &lt;a class="link" href="http://www.cs.ubc.ca/~murphyk/Papers/bayesGauss.pdf" target="_blank" rel="noopener"
&gt;section 2.3 of this&lt;/a&gt; and &lt;a class="link" href="https://web.archive.org/web/20160304125731/http://fisher.osu.edu/~schroeder.9/AMIS900/ech6.pdf" target="_blank" rel="noopener"
&gt;section 6.2 this&lt;/a&gt;.)&lt;/p&gt;
\begin{align}
p(\theta_k \,|\, \cdot)&amp;=
p(\theta_k \,|\,
\bf{z}, \pi,
\theta_{\neg k},
\sigma, \bf x, \lambda_1, \lambda_2
) \\
&amp;\propto p(\left\{x_i \,|\, z_i=k\right\} \,|\, \bf{z}, \pi,
\theta_1, \theta_2, \theta_3,
\sigma, \lambda_1, \lambda_2) \cdot\\
&amp;\phantom{==}p(\theta_k \,|\, \bf{z}, \pi,
\theta_{\neg k},
\sigma, \lambda_1, \lambda_2)\\
&amp;\propto p(\left\{x_i \,|\, z_i=k\right\} \,|\, \mathbf{z},
\theta_k, \sigma)
p(\theta_k \,|\, \lambda_1, \lambda_2)\\
&amp;= \mathcal{N}(\theta_k \,|\, \mu_n, \sigma_n)\\
\end{align}$$ \sigma_n^2 = \frac{1}{
\frac{1}{\lambda_2^2} + \frac{N_k}{\sigma^2}
} $$&lt;p&gt;and&lt;/p&gt;
$$\mu_n = \sigma_n^2
\left(
\frac{\lambda_1}{\lambda_2^2} +
\frac{n\bar{x_k}}{\sigma^2}
\right)
$$&lt;p&gt;Here is the code for sampling those means and for updating our state accordingly.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sample_cluster_mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cluster_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_variance_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hp_mean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hyperparameters_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mean&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hp_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hyperparameters_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;variance&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;suffstats&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cluster_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;numerator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hp_mean&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;hp_var&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;theta&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;cluster_var&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;denominator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;hp_var&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;cluster_var&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;posterior_mu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numerator&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;denominator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;posterior_var&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;denominator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;posterior_mu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;posterior_var&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_cluster_means&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_means&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sample_cluster_mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_ids_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Doing each of these three updates in sequence makes a complete &lt;em&gt;Gibbs step&lt;/em&gt; for our mixture model. Here is a function to do that:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;gibbs_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;update_assignment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;update_mixture_weights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;update_cluster_means&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Initially, we assigned each data point to a random cluster. We can see this by plotting a histogram of each cluster.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;plot_clusters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;gby&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;assignment&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hist_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gby&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;gby&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hist_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;bins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;histtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;stepfilled&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;.5&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plot_clusters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/mixture-model/2015-09-02-fitting-a-mixture-model_18_0.png"
width="371"
height="256"
srcset="https://tdhopper.com/blog/mixture-model/2015-09-02-fitting-a-mixture-model_18_0_hu_74aade54de2321.png 480w, https://tdhopper.com/blog/mixture-model/2015-09-02-fitting-a-mixture-model_18_0_hu_bad9282f49896216.png 1024w"
loading="lazy"
alt="Random initial cluster assignments"
class="gallery-image"
data-flex-grow="144"
data-flex-basis="347px"
&gt;&lt;/p&gt;
&lt;p&gt;Each time we run &lt;code&gt;gibbs_step&lt;/code&gt;, our &lt;code&gt;state&lt;/code&gt; is updated with newly sampled assignments. Look what happens to our histogram after 5 steps:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;gibbs_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plot_clusters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/mixture-model/2015-09-02-fitting-a-mixture-model_20_0.png"
width="377"
height="256"
srcset="https://tdhopper.com/blog/mixture-model/2015-09-02-fitting-a-mixture-model_20_0_hu_3b9ab8eb2b89b326.png 480w, https://tdhopper.com/blog/mixture-model/2015-09-02-fitting-a-mixture-model_20_0_hu_38438ae42e47a343.png 1024w"
loading="lazy"
alt="Cluster assignments after 5 Gibbs steps"
class="gallery-image"
data-flex-grow="147"
data-flex-basis="353px"
&gt;&lt;/p&gt;
&lt;p&gt;Suddenly, we are seeing clusters that appear very similar to what we would intuitively expect: three Gaussian clusters.&lt;/p&gt;
&lt;p&gt;Another way to see the progress made by the Gibbs sampler is to plot the change in the model&amp;rsquo;s log-likelihood after each step. The log likehlihood is given by:&lt;/p&gt;
$$
\log p(\mathbf{x} \,|\, \pi, \theta_1, \theta_2, \theta_3)
\propto \sum_x \log \left(
\sum_{k=1}^3 \pi_k \exp
\left\{
-(x-\theta_k)^2 / (2\sigma^2)
\right\}
\right)
$$&lt;p&gt;We can define this as a function of our &lt;code&gt;state&lt;/code&gt; object:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_likelihood&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Data log-likeliehood
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Equation 2.153 in Sudderth
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;data_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pi&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_means&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cluster_variance_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ll&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ll&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initial_state&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;log_likelihood&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;gibbs_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ll&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_likelihood&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/mixture-model/2015-09-02-fitting-a-mixture-model_23_1.png"
width="390"
height="256"
srcset="https://tdhopper.com/blog/mixture-model/2015-09-02-fitting-a-mixture-model_23_1_hu_119c8a58f507ae94.png 480w, https://tdhopper.com/blog/mixture-model/2015-09-02-fitting-a-mixture-model_23_1_hu_d07c747be6ccefc1.png 1024w"
loading="lazy"
alt="Log-likelihood over Gibbs sampling iterations"
class="gallery-image"
data-flex-grow="152"
data-flex-basis="365px"
&gt;&lt;/p&gt;
&lt;p&gt;See that the log likelihood improves with iterations of the Gibbs sampler. This is what we should expect: the Gibbs sampler finds state configurations that make the data we have seem &amp;ldquo;likely&amp;rdquo;. However, the likelihood isn&amp;rsquo;t strictly monotonic: it jitters up and down. Though it behaves similarly, the Gibbs sampler isn&amp;rsquo;t optimizing the likelihood function. In its steady state, it is sampling from the posterior distribution. The &lt;code&gt;state&lt;/code&gt; after each step of the Gibbs sampler is a sample from the posterior.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ylim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/mixture-model/2015-09-02-fitting-a-mixture-model_25_1.png"
width="384"
height="256"
srcset="https://tdhopper.com/blog/mixture-model/2015-09-02-fitting-a-mixture-model_25_1_hu_32e7829d8a57a400.png 480w, https://tdhopper.com/blog/mixture-model/2015-09-02-fitting-a-mixture-model_25_1_hu_939234e0f3886b43.png 1024w"
loading="lazy"
alt="Log-likelihood zoomed in"
class="gallery-image"
data-flex-grow="150"
data-flex-basis="360px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://tdhopper.com/blog/collapsed-gibbs/" &gt;In another post&lt;/a&gt;, I show how we can &amp;ldquo;collapse&amp;rdquo; the Gibbs sampler and sampling the assignment parameter without sampling the $\pi$ and $\theta$ values. This collapsed sampler can also be extended to the model with a Dirichet process prior that allows the number of clusters to be a parameter fit by the model.&lt;/p&gt;
&lt;h2 id="notation-helper"&gt;Notation Helper
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;$N_k$, &lt;code&gt;state['suffstat'][k].N&lt;/code&gt;: Number of points in cluster $k$.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;$\theta_k$, &lt;code&gt;state['suffstat'][k].theta&lt;/code&gt;: Mean of cluster $k$.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;$\lambda_1$, &lt;code&gt;state['hyperparameters_']['mean']&lt;/code&gt;: Mean of prior distribution over cluster means.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;$\lambda_2^2$, &lt;code&gt;state['hyperparameters_']['variance']&lt;/code&gt; Variance of prior distribution over cluster means.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;$\sigma^2$, &lt;code&gt;state[cluster_variance_]&lt;/code&gt;: Known, fixed variance of clusters.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The superscript $(t)$ on $\theta_k$, $pi_k$, and $z_i$ indicates the value of that variable at step $t$ of the Gibbs sampler.&lt;/p&gt;</description></item><item><title>Profile in Computational Imagination</title><link>https://tdhopper.com/blog/profile-in-computational-imagination/</link><pubDate>Tue, 01 Sep 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/profile-in-computational-imagination/</guid><description>&lt;p&gt;I recently had the honor of being interviewed by &lt;a class="link" href="http://computationalimagination.com" target="_blank" rel="noopener"
&gt;Michael Swenson&lt;/a&gt; for his interview series called &amp;ldquo;Profiles in Computational Imagination&amp;rdquo;. I talked a bit about my current work, my wandering road to data science, and my love for remote work. You can &lt;a class="link" href="https://web.archive.org/web/20160304120901/http://computationalimagination.com/interview_tim_hopper.php" target="_blank" rel="noopener"
&gt;read it here&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>On Showing Your Work</title><link>https://tdhopper.com/blog/on-showing-your-work/</link><pubDate>Wed, 12 Aug 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/on-showing-your-work/</guid><description>&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;I&amp;#39;ll never forget the great words of one of my best math professors:&amp;#10;&amp;#10;&amp;quot;If all I wanted was the answer, I sure as hell wouldn&amp;#39;t ask you.&amp;quot;&lt;/p&gt;&amp;mdash; Tim Hopper (@tdhopper) &lt;a href="https://twitter.com/tdhopper/status/410071644896382977"&gt;December 9, 2013&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;</description></item><item><title>A Programmer's Portfolio</title><link>https://tdhopper.com/blog/a-programmers-portfolio/</link><pubDate>Wed, 05 Aug 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/a-programmers-portfolio/</guid><description>&lt;p&gt;I am convinced that a programming student hoping to get a job in that field should be actively building a portfolio online. Turn those class projects, presentations, and reports into Github repositories or blog posts! I felt vindicated as I read this anecdote in &lt;a class="link" href="https://en.wikipedia.org/wiki/Peopleware:_Productive_Projects_and_Teams" target="_blank" rel="noopener"
&gt;Peopleware&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In the spring of 1979, while teaching together in western Canada,we got a call from a computer science professor at the local technical college. He proposed to stop by our hotel after class one evening and buy us beers in exchange for ideas. That&amp;rsquo;s the kind of offer we seldom turn down. What we learned from him that evening was almost certainly worth more than whatever he learned from us.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The teacher was candid about what he needed to be judged a success in his work: He needed his students to get good job offers and lots of them. &amp;ldquo;A Harvard diploma is worth something in and of itself, but our diploma isn&amp;rsquo;t worth squat. If this year&amp;rsquo;s graduates don&amp;rsquo;t get hired fast, there are no students next year and I&amp;rsquo;m out of a job.&amp;rdquo; So he had developed a formula to make his graduates optimally attractive to the job market. Of course he taught them modern techniques for system construction, including structured analysis and design, data-driven design, information hiding, structured coding, walk throughs, and metrics. He also had them work on real applications for nearby companies and agencies. But the center piece of his formula was the portfolio that all students put together to show samples of their work.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;He described how his students had been coached to show off their portfolios as part of each interview:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;I&amp;rsquo;ve brought along some samples of the kind of work I do. Here, for instance, is a subroutine in Pascal from one project and a set of COBOL paragraphs from another. As you can see in this portion, we use the loop-with-exit extension advocated by Knuth, but aside from that, it&amp;rsquo;s pure structured code, pretty much the sort of thing that your company standard calls for. And here is the design that this code was written from. The hierarchies and coupling analysis use Myers&amp;rsquo; notation. I designed all of this particular subsystem, and this one little section where we used some Orr methods because the data structure really imposed itself on the process structure. And these are the leveled data flow diagrams that makeup the guts of our specification, and the associated data dictionary. &amp;hellip;&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;In the years since, we&amp;rsquo;ve often heard more about that obscure technical college and those portfolios. We&amp;rsquo;ve met recruiters from as far away as Triangle Park, North Carolina, and Tampa, Florida,who regularly converge upon that distant Canadian campus for a shot at its graduates.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Of course, this was a clever scheme of the professor&amp;rsquo;s to give added allure to his graduates, but what struck us most that evening was the report that interviewers were always surprised by the portfolios. That meant they weren&amp;rsquo;t regularly requiring all candidates to arrive with portfolios. Yet why not? What could be more sensible than asking each candidate to bring along some samples of work to the interview?&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Nonparametric Latent Dirichlet Allocation</title><link>https://tdhopper.com/blog/nonparametric-lda/</link><pubDate>Mon, 03 Aug 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/nonparametric-lda/</guid><description>&lt;img src="https://tdhopper.com/images/nonparametric-lda-mr-men.png" alt="Featured image of post Nonparametric Latent Dirichlet Allocation" /&gt;&lt;p&gt;&lt;em&gt;Latent Dirichlet Allocation&lt;/em&gt; is a &lt;a class="link" href="https://en.wikipedia.org/wiki/Generative_model" target="_blank" rel="noopener"
&gt;generative&lt;/a&gt; model for topic modeling. Given a collection of documents, an LDA inference algorithm attempts to determined (in an unsupervised manner) the topics discussed in the documents. It makes the assumption that each document is generated by a probability model, and, when doing inference, we try to find the parameters that best fit the model (as well as unseen/latent variables generated by the model). If you are unfamiliar with LDA, Edwin Chen has a &lt;a class="link" href="http://blog.echen.me/2011/08/22/introduction-to-latent-dirichlet-allocation/" target="_blank" rel="noopener"
&gt;friendly introduction&lt;/a&gt; you should read.&lt;/p&gt;
&lt;p&gt;Because LDA is a &lt;em&gt;generative&lt;/em&gt; model, we can simulate the construction of documents by forward-sampling from the model. The generative algorithm is as follows (following &lt;a class="link" href="http://www.arbylon.net/publications/text-est.pdf" target="_blank" rel="noopener"
&gt;Heinrich&lt;/a&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;for each topic $k\in [1,K]$ do
&lt;ul&gt;
&lt;li&gt;sample term distribution for topic $\overrightarrow \phi_k \sim \text{Dir}(\overrightarrow \beta)$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;for each document $m\in [1, M]$ do
&lt;ul&gt;
&lt;li&gt;sample topic distribution for document $\overrightarrow\theta_m\sim \text{Dir}(\overrightarrow\alpha)$&lt;/li&gt;
&lt;li&gt;sample document length $N_m\sim\text{Pois}(\xi)$&lt;/li&gt;
&lt;li&gt;for all words $n\in [1, N_m]$ in document $m$ do
&lt;ul&gt;
&lt;li&gt;sample topic index $z_{m,n}\sim\text{Mult}(\overrightarrow\theta_m)$&lt;/li&gt;
&lt;li&gt;sample term for word $w_{m,n}\sim\text{Mult}(\overrightarrow\phi_{z_{m,n}})$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can implement this with &lt;a class="link" href="https://gist.github.com/tdhopper/521006b60e1311d45509" target="_blank" rel="noopener"
&gt;a little bit of code&lt;/a&gt; and start to simulate documents.&lt;/p&gt;
&lt;p&gt;In LDA, we assume each word in the document is generated by a two-step process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sample a topic from the topic distribution for the document.&lt;/li&gt;
&lt;li&gt;Sample a word from the term distribution from the topic.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When we fit the LDA model to a given text corpus with an inference algorithm, our primary objective is to find the set of topic distributions $\underline \Theta$, term distributions $\underline \Phi$ that generated the documents, and latent topic indices $z_{m,n}$ for each word.&lt;/p&gt;
&lt;p&gt;To run the generative model, we need to specify each of these parameters:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vocabulary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;see&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;spot&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;run&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;num_terms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vocabulary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;num_topics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="c1"&gt;# K&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;num_documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="c1"&gt;# M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;mean_document_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="c1"&gt;# xi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;term_dirichlet_parameter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# beta&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;topic_dirichlet_parameter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# alpha&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The term distribution vector $\underline\Phi$ is a collection of samples from a Dirichlet distribution. This describes how our 3 terms are distributed across each of the two topics.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;scipy.stats&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dirichlet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;poisson&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nb"&gt;round&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;stl_choice&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;term_dirichlet_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;num_terms&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;term_dirichlet_parameter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;term_distributions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dirichlet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;term_dirichlet_vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;num_topics&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;term_distributions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;pre&gt;&lt;code&gt;[[ 0.41 0.02 0.57]
[ 0.38 0.36 0.26]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each document corresponds to a categorical distribution across this distribution of topics (in this case, a 2-dimensional categorical distribution). This categorical distribution is a &lt;em&gt;distribution of distributions&lt;/em&gt;; we could look at it as a Dirichlet process!&lt;/p&gt;
&lt;p&gt;The base base distribution of our Dirichlet process is a uniform distribution of topics (remember, topics are term distributions).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;base_distribution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;stl_choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;term_distributions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# A sample from base_distribution is a distribution over terms&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Each of our two topics has equal probability&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_distribution&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;most_common&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;count:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;topic:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;prob&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;pre&gt;&lt;code&gt;count: 5066 topic: [0.40999999999999998, 0.02, 0.56999999999999995]
count: 4934 topic: [0.38, 0.35999999999999999, 0.26000000000000001]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Recall that a sample from a Dirichlet process is a distribution that approximates (but varies from) the base distribution. In this case, a sample from the Dirichlet process will be a distribution over topics that varies from the uniform distribution we provided as a base. If we use the stick-breaking metaphor, we are effectively breaking a stick one time and the size of each portion corresponds to the proportion of a topic in the document.&lt;/p&gt;
&lt;p&gt;To construct a sample from the DP, we need to &lt;a class="link" href="https://tdhopper.com/blog/dirichlet-distribution/" &gt;again define our DP class&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;scipy.stats&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;beta&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;numpy.random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DirichletProcessSample&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base_measure&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_stick_used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_stick_used&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DirichletProcessSample&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;roll_die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;stick_piece&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_stick_used&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;stick_piece&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stick_piece&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;new_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;new_value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@staticmethod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;roll_die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;For each document, we will draw a topic distribution from the Dirichlet process:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;topic_distribution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DirichletProcessSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;base_distribution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;topic_dirichlet_parameter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;A sample from this &lt;em&gt;topic&lt;/em&gt; distribution is a &lt;em&gt;distribution over terms&lt;/em&gt;. However, unlike our base distribution which returns each term distribution with equal probability, the topics will be unevenly weighted.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topic_distribution&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;most_common&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;count:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;topic:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;prob&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;pre&gt;&lt;code&gt;count: 9589 topic: [0.38, 0.35999999999999999, 0.26000000000000001]
count: 411 topic: [0.40999999999999998, 0.02, 0.56999999999999995]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To generate each word in the document, we draw a sample topic from the topic distribution, and then a term from the term distribution (topic).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;topic_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_documents&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;topic_distribution_rvs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DirichletProcessSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;base_distribution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;topic_dirichlet_parameter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;document_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;poisson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean_document_length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document_length&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;topic_distribution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;topic_distribution_rvs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;topic_index&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topic_distribution&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vocabulary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;topic_distribution&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Here are the documents we generated:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;pre&gt;&lt;code&gt;['see', 'run', 'see', 'spot', 'see', 'spot']
['see', 'run', 'see']
['see', 'run', 'see', 'see', 'run', 'spot', 'spot']
['run', 'run', 'run', 'spot', 'run']
['run', 'run', 'see', 'spot', 'run', 'run']
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can see how each topic (term-distribution) is distributed across the documents:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;term_dist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;most_common&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;term_dist&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;topic_index&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Doc:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;count:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;topic:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;prob&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;pre&gt;&lt;code&gt;Doc: 0
count: 6 topic: [0.38, 0.35999999999999999, 0.26000000000000001]
Doc: 1
count: 3 topic: [0.40999999999999998, 0.02, 0.56999999999999995]
Doc: 2
count: 5 topic: [0.40999999999999998, 0.02, 0.56999999999999995]
count: 2 topic: [0.38, 0.35999999999999999, 0.26000000000000001]
Doc: 3
count: 5 topic: [0.38, 0.35999999999999999, 0.26000000000000001]
Doc: 4
count: 5 topic: [0.40999999999999998, 0.02, 0.56999999999999995]
count: 1 topic: [0.38, 0.35999999999999999, 0.26000000000000001]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To recap: for each document we draw a &lt;em&gt;sample&lt;/em&gt; from a Dirichlet &lt;em&gt;Process&lt;/em&gt;. The base distribution for the Dirichlet process is a categorical distribution over term distributions; we can think of the base distribution as an $n$-sided die where $n$ is the number of topics and each side of the die is a distribution over terms for that topic. By sampling from the Dirichlet process, we are effectively reweighting the sides of the die (changing the distribution of the topics).&lt;/p&gt;
&lt;p&gt;For each word in the document, we draw a &lt;em&gt;sample&lt;/em&gt; (a term distribution) from the distribution (over term distributions) &lt;em&gt;sampled&lt;/em&gt; from the Dirichlet process (with a distribution over term distributions as its base measure). Each term distribution uniquely identifies the topic for the word. We can sample from this term distribution to get the word.&lt;/p&gt;
&lt;p&gt;Given this formulation, we might ask if we can roll an &lt;em&gt;infinite&lt;/em&gt; sided die to draw from an unbounded number of topics (term distributions). We can do exactly this with a &lt;em&gt;Hierarchical&lt;/em&gt; Dirichlet process. Instead of the base distribution of our Dirichlet process being a &lt;em&gt;finite&lt;/em&gt; distribution over topics (term distributions) we will instead make it an infinite Distribution over topics (term distributions) by using yet another Dirichlet process! This base Dirichlet process will have as its base distribution a Dirichlet &lt;em&gt;distribution&lt;/em&gt; over terms.&lt;/p&gt;
&lt;p&gt;We will again draw a &lt;em&gt;sample&lt;/em&gt; from a Dirichlet &lt;em&gt;Process&lt;/em&gt; for each document. The base distribution for the Dirichlet process is itself a Dirichlet process whose base distribution is a Dirichlet distribution over terms. (Try saying that 5-times fast.) We can think of this as a countably infinite die each side of the die is a distribution over terms for that topic. The sample we draw is a topic (distribution over terms).&lt;/p&gt;
&lt;p&gt;For each word in the document, we will draw a &lt;em&gt;sample&lt;/em&gt; (a term distribution) from the distribution (over term distributions) &lt;em&gt;sampled&lt;/em&gt; from the Dirichlet process (with a distribution over term distributions as its base measure). Each term distribution uniquely identifies the topic for the word. We can sample from this term distribution to get the word.&lt;/p&gt;
&lt;p&gt;These last few paragraphs are confusing! Let&amp;rsquo;s illustrate with code.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;term_dirichlet_vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;num_terms&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;term_dirichlet_parameter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;base_distribution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dirichlet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;term_dirichlet_vector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;base_dp_parameter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;base_dp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DirichletProcessSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_distribution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;base_dp_parameter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This sample from the base Dirichlet process is our infinite sided die. It is a probability distribution over a countable infinite number of topics.&lt;/p&gt;
&lt;p&gt;The fact that our die is countably infinite is important. The sampler &lt;code&gt;base_distribution&lt;/code&gt; draws topics (term-distributions) from an uncountable set. If we used this as the base distribution of the Dirichlet process below each document would be constructed from a &lt;em&gt;completely unique set of topics&lt;/em&gt;. By feeding &lt;code&gt;base_distribution&lt;/code&gt; into a Dirichlet Process (stochastic memoizer), we allow the topics to be shared across documents.&lt;/p&gt;
&lt;p&gt;In other words, &lt;code&gt;base_distribution&lt;/code&gt; will never return the same topic twice; however, every topic sampled from &lt;code&gt;base_dp&lt;/code&gt; would be sampled an infinite number of times (if we sampled from &lt;code&gt;base_dp&lt;/code&gt; forever). At the same time, &lt;code&gt;base_dp&lt;/code&gt; will also return an &lt;em&gt;infinite number&lt;/em&gt; of topics. In our formulation of the the LDA sampler above, our base distribution only ever returned a finite number of topics (&lt;code&gt;num_topics&lt;/code&gt;); there is no &lt;code&gt;num_topics&lt;/code&gt; parameter here.&lt;/p&gt;
&lt;p&gt;Given this setup, we can generate documents from the &lt;em&gt;hierarchical Dirichlet process&lt;/em&gt; with an algorithm that is essentially identical to that of the original &lt;em&gt;latent Dirichlet allocation&lt;/em&gt; generative sampler:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;nested_dp_parameter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;topic_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_documents&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;topic_distribution_rvs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DirichletProcessSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;base_dp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;nested_dp_parameter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;document_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;poisson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean_document_length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document_length&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;topic_distribution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;topic_distribution_rvs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;topic_index&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topic_distribution&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vocabulary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;topic_distribution&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Here are the documents we generated:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;pre&gt;&lt;code&gt;['spot', 'spot', 'spot', 'spot', 'run']
['spot', 'spot', 'see', 'spot']
['spot', 'spot', 'spot', 'see', 'spot', 'spot', 'spot']
['run', 'run', 'spot', 'spot', 'spot', 'spot', 'spot', 'spot']
['see', 'run', 'see', 'run', 'run', 'run']
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here are the latent topics used:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;term_dist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;most_common&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;term_dist&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;topic_index&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Doc:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;count:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;topic:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;prob&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;pre&gt;&lt;code&gt;Doc: 0
count: 2 topic: [0.17999999999999999, 0.79000000000000004, 0.02]
count: 1 topic: [0.23000000000000001, 0.58999999999999997, 0.17999999999999999]
count: 1 topic: [0.089999999999999997, 0.54000000000000004, 0.35999999999999999]
count: 1 topic: [0.22, 0.40000000000000002, 0.38]
Doc: 1
count: 2 topic: [0.23000000000000001, 0.58999999999999997, 0.17999999999999999]
count: 1 topic: [0.17999999999999999, 0.79000000000000004, 0.02]
count: 1 topic: [0.35999999999999999, 0.55000000000000004, 0.089999999999999997]
Doc: 2
count: 4 topic: [0.11, 0.65000000000000002, 0.23999999999999999]
count: 2 topic: [0.070000000000000007, 0.65000000000000002, 0.27000000000000002]
count: 1 topic: [0.28999999999999998, 0.65000000000000002, 0.070000000000000007]
Doc: 3
count: 2 topic: [0.17999999999999999, 0.79000000000000004, 0.02]
count: 2 topic: [0.25, 0.55000000000000004, 0.20000000000000001]
count: 2 topic: [0.28999999999999998, 0.65000000000000002, 0.070000000000000007]
count: 1 topic: [0.23000000000000001, 0.58999999999999997, 0.17999999999999999]
count: 1 topic: [0.089999999999999997, 0.54000000000000004, 0.35999999999999999]
Doc: 4
count: 3 topic: [0.40000000000000002, 0.23000000000000001, 0.37]
count: 2 topic: [0.42999999999999999, 0.17999999999999999, 0.40000000000000002]
count: 1 topic: [0.23000000000000001, 0.29999999999999999, 0.46000000000000002]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our documents were generated by an unspecified number of topics, and yet the topics were shared across the 5 documents. This is the power of the hierarchical Dirichlet process!&lt;/p&gt;
&lt;p&gt;This non-parametric formulation of Latent Dirichlet Allocation was first published by &lt;a class="link" href="http://www.cs.berkeley.edu/~jordan/papers/hdp.pdf" target="_blank" rel="noopener"
&gt;Yee Whye Teh et al&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Unfortunately, forward sampling is the easy part. Fitting the model on data requires &lt;a class="link" href="http://psiexp.ss.uci.edu/research/papers/sciencetopics.pdf" target="_blank" rel="noopener"
&gt;complex MCMC&lt;/a&gt; or &lt;a class="link" href="http://www.cs.princeton.edu/~chongw/papers/WangPaisleyBlei2011.pdf" target="_blank" rel="noopener"
&gt;variational inference&lt;/a&gt;. There are a &lt;a class="link" href="http://www.stats.ox.ac.uk/~teh/software.html" target="_blank" rel="noopener"
&gt;limited&lt;/a&gt; &lt;a class="link" href="https://github.com/shuyo/iir/blob/master/lda/hdplda2.py" target="_blank" rel="noopener"
&gt;number&lt;/a&gt; of &lt;a class="link" href="https://github.com/renaud/hdp-faster" target="_blank" rel="noopener"
&gt;implementations&lt;/a&gt; &lt;a class="link" href="http://www.arbylon.net/resources.html" target="_blank" rel="noopener"
&gt;of HDP-LDA&lt;/a&gt; available, and none of them are great.&lt;/p&gt;</description></item><item><title>High Quality Code at Quora</title><link>https://tdhopper.com/blog/high-quality-code-at-quora/</link><pubDate>Thu, 30 Jul 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/high-quality-code-at-quora/</guid><description>&lt;p&gt;I love &lt;a class="link" href="http://engineering.quora.com/Moving-Fast-With-High-Code-Quality" target="_blank" rel="noopener"
&gt;this new post&lt;/a&gt; on Quora&amp;rsquo;s engineering blog. The post states &amp;ldquo;high code quality is the long-term boost to development speed&amp;rdquo; and goes on to explain how they go about accomplishing this.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve inherited large code bases at each of my jobs out of grad school, and I&amp;rsquo;ve spent a lot of thinking about this question. At least on the surface, I love the solutions Quora has in place for ensuring quality code: thoughtful code review, careful testing, style guidelines, static checking, and intentional code cleanup.&lt;/p&gt;</description></item><item><title>Sampling from a Hierarchical Dirichlet Process</title><link>https://tdhopper.com/blog/hdp-sampling/</link><pubDate>Thu, 30 Jul 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/hdp-sampling/</guid><description>&lt;img src="https://tdhopper.com/images/hdp-sampling-mr-men.png" alt="Featured image of post Sampling from a Hierarchical Dirichlet Process" /&gt;&lt;p&gt;&lt;a class="link" href="https://tdhopper.com/blog/dirichlet-distribution/" &gt;As we saw earlier&lt;/a&gt; the Dirichlet process describes the &lt;em&gt;distribution&lt;/em&gt; of a random probability distribution. The Dirichlet process takes two parameters: a base distribution $H_0$ and a dispersion parameter $\alpha$. A sample from the Dirichlet process is itself a probability distribution that &lt;em&gt;looks like&lt;/em&gt; $H_0$. On average, the larger $\alpha$ is, the closer a sample from $\text{DP}(\alpha H_0)$ will be to $H_0$.&lt;/p&gt;
&lt;p&gt;Suppose we&amp;rsquo;re feeling masochistic and want to input a distribution sampled from a Dirichlet process as base distribution to a new Dirichlet process. (It will turn out that there are good reasons for this!) Conceptually this makes sense. But can we construct such a thing in practice? Said another way, can we build a sampler that will draw samples from a probability distribution drawn from these nested Dirichlet processes? We might initially try construct a sample (a probability distribution) from the first Dirichlet process before feeding it into the second.&lt;/p&gt;
&lt;p&gt;But recall that fully constructing a sample (a probability distribution!) from a Dirichlet process would require drawing a countably infinite number of samples from $H_0$ and from the beta distribution to generate the weights. This would take forever, even with Hadoop!&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://danroy.org/papers/RoyManGooTen-ICMLNPB-2008.pdf" target="_blank" rel="noopener"
&gt;Dan Roy, et al&lt;/a&gt; helpfully described a technique of using &lt;em&gt;stochastic memoization&lt;/em&gt; to construct a distribution sampled from a Dirichlet process in a just-in-time manner. This process provides us with the equivalent of the &lt;a class="link" href="http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.rv_continuous.rvs.html" target="_blank" rel="noopener"
&gt;Scipy &lt;code&gt;rvs&lt;/code&gt;&lt;/a&gt; method for the sampled distribution. Stochastic memoization is equivalent to the &lt;a class="link" href="http://www.cs.princeton.edu/courses/archive/fall07/cos597C/scribe/20070921.pdf" target="_blank" rel="noopener"
&gt;Chinese restaurant process&lt;/a&gt;: sometimes you get seated an an occupied table (i.e. sometimes you&amp;rsquo;re given a sample you&amp;rsquo;ve seen before) and sometimes you&amp;rsquo;re put at a new table (given a unique sample).&lt;/p&gt;
&lt;p&gt;Here is our memoization class again:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;numpy.random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;scipy.stats&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;beta&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DirichletProcessSample&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base_measure&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_stick_used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_stick_used&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DirichletProcessSample&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;roll_die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;stick_piece&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_stick_used&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;stick_piece&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stick_piece&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;new_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;new_value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@staticmethod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;roll_die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s illustrate again with a standard normal base measure. We can construct a function &lt;code&gt;base_measure&lt;/code&gt; that generates samples from it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;scipy.stats&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;norm&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Because the normal distribution has continuous support, we can generate samples from it forever and we will never see the same sample twice (in theory). We can illustrate this by drawing from the distribution ten thousand times and seeing that we get ten thousand unique values.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pandas&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Series&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ndraws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Number of unique samples after &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt; draws:&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ndraws&lt;/span&gt;&lt;span class="p"&gt;),)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;draws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Series&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ndraws&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;draws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;pre&gt;&lt;code&gt;Number of unique samples after 10000 draws: 10000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, when we feed the base measure through the stochastic memoization procedure and then sample, we get many duplicate samples. The number of unique samples goes down as $\alpha$ increases.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;norm_dp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DirichletProcessSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Number of unique samples after &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt; draws:&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ndraws&lt;/span&gt;&lt;span class="p"&gt;),)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dp_draws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Series&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;norm_dp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ndraws&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dp_draws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;pre&gt;&lt;code&gt;Number of unique samples after 10000 draws: 446
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, we have a function &lt;code&gt;dp_draws&lt;/code&gt; that returns samples from a probability distribution (specifically, a probability distribution sampled from $\text{DP}(\alpha H_0)$). We can use &lt;code&gt;dp_draws&lt;/code&gt; as a base distribution for another Dirichlet process!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;norm_hdp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DirichletProcessSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;norm_dp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;How do we interpret this? &lt;code&gt;norm_dp&lt;/code&gt; is a sampler from a probability distribution that looks like the standard normal distribution. &lt;code&gt;norm_hdp&lt;/code&gt; is a sampler from a probability distribution that &amp;ldquo;looks like&amp;rdquo; the distribution &lt;code&gt;norm_dp&lt;/code&gt; samples from.&lt;/p&gt;
&lt;p&gt;Here is a histogram of samples drawn from &lt;code&gt;norm_dp&lt;/code&gt;, our first sampler.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;plt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;norm_dp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Histogram of Samples from norm_dp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_12_0.png"
width="379"
height="266"
srcset="https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_12_0_hu_fa3a40d1ad4f513e.png 480w, https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_12_0_hu_b9841ac93f9f1ff7.png 1024w"
loading="lazy"
alt="Histogram of samples from norm_dp"
class="gallery-image"
data-flex-grow="142"
data-flex-basis="341px"
&gt;&lt;/p&gt;
&lt;p&gt;And here is a histogram for samples drawn from &lt;code&gt;norm_hdp&lt;/code&gt;, our second sampler.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;norm_hdp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Histogram of Samples from norm_hdp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_14_0.png"
width="380"
height="266"
srcset="https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_14_0_hu_b7c4a27709c0d78d.png 480w, https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_14_0_hu_1ce189f5227764ec.png 1024w"
loading="lazy"
alt="Histogram of samples from norm_hdp"
class="gallery-image"
data-flex-grow="142"
data-flex-basis="342px"
&gt;&lt;/p&gt;
&lt;p&gt;The second plot doesn&amp;rsquo;t look very much like the first! The level to which a sample from a Dirichlet process approximates the base distribution is a function of the dispersion parameter $\alpha$. Because I set $\alpha=10$ (which is relatively small), the approximation is fairly course. In terms of memoization, a small $\alpha$ value means the stochastic memoizer will more frequently reuse values already seen instead of drawing new ones.&lt;/p&gt;
&lt;p&gt;This nesting procedure, where a sample from one Dirichlet process is fed into another Dirichlet process as a base distribution, is more than just a curiousity. It is known as a &lt;a class="link" href="http://www.cs.berkeley.edu/~jordan/papers/hdp.pdf" target="_blank" rel="noopener"
&gt;Hierarchical Dirichlet Process, and it plays an important role in the study of Bayesian Nonparametrics&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Without the stochastic memoization framework, constructing a sampler for a hierarchical Dirichlet process is a daunting task. We want to be able to draw samples from a distribution drawn from the second level Dirichlet process. However, to be able to do that, we need to be able to draw samples from a distribution sampled from a &lt;em&gt;base distribution of the second-level Dirichlet process&lt;/em&gt;: this base distribution is a &lt;em&gt;distribution drawn from the first-level Dirichlet process&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Though it appeared that we would need to be able to fully construct the first level sample (by drawing a countably infinite number of samples from the first-level base distribution). However, stochastic memoization allows us to only construct the first distribution just-in-time as it is needed at the second-level.&lt;/p&gt;
&lt;p&gt;We can define a Python class to encapsulate the Hierarchical Dirichlet Process as a base class of the Dirichlet process.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HierarchicalDirichletProcessSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DirichletProcessSample&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;first_level_dp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DirichletProcessSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;second_level_dp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DirichletProcessSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first_level_dp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;second_level_dp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Since the Hierarchical DP is a Dirichlet Process inside of Dirichlet process, we must provide it with both a first and second level $\alpha$ value.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;norm_hdp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HierarchicalDirichletProcessSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;We can sample directly from the probability distribution drawn from the Hierarchical Dirichlet Process.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;norm_hdp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Histogram of samples from distribution drawn from Hierarchical DP&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_20_0.png"
width="420"
height="266"
srcset="https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_20_0_hu_16e78b7f63bd8051.png 480w, https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_20_0_hu_6d79c0e976bba9b0.png 1024w"
loading="lazy"
alt="Histogram of samples from distribution drawn from Hierarchical DP"
class="gallery-image"
data-flex-grow="157"
data-flex-basis="378px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;norm_hdp&lt;/code&gt; is not equivalent to the Hierarchical Dirichlet Process; it samples from a &lt;em&gt;single distribution&lt;/em&gt; sampled from this HDP. Each time we instantiate the &lt;code&gt;norm_hdp&lt;/code&gt; variable, we are getting a sampler for a unique distribution. Below we sample five times and get five different distributions.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;norm_hdp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HierarchicalDirichletProcessSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;norm_hdp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Histogram of samples from distribution drawn from Hierarchical DP&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_22_0.png"
width="420"
height="266"
srcset="https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_22_0_hu_221dae2742e6e55a.png 480w, https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_22_0_hu_a6c34d0c4cbf429d.png 1024w"
loading="lazy"
alt="Hierarchical DP sample 1"
class="gallery-image"
data-flex-grow="157"
data-flex-basis="378px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_22_1.png"
width="420"
height="266"
srcset="https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_22_1_hu_708891a9b9a679e9.png 480w, https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_22_1_hu_7aa26536164fbf21.png 1024w"
loading="lazy"
alt="Hierarchical DP sample 2"
class="gallery-image"
data-flex-grow="157"
data-flex-basis="378px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_22_2.png"
width="420"
height="266"
srcset="https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_22_2_hu_46ff23202fc2254f.png 480w, https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_22_2_hu_cd0b1569d438d869.png 1024w"
loading="lazy"
alt="Hierarchical DP sample 3"
class="gallery-image"
data-flex-grow="157"
data-flex-basis="378px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_22_3.png"
width="420"
height="266"
srcset="https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_22_3_hu_7d5e5b13ea24133.png 480w, https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_22_3_hu_732962871c7dda4d.png 1024w"
loading="lazy"
alt="Hierarchical DP sample 4"
class="gallery-image"
data-flex-grow="157"
data-flex-basis="378px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_22_4.png"
width="420"
height="266"
srcset="https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_22_4_hu_5f4328cf005d49d2.png 480w, https://tdhopper.com/blog/hdp-sampling/2015-07-30-sampling-from-a-hierarchical-dirichlet-process_22_4_hu_c8c60bc93d1c9bb1.png 1024w"
loading="lazy"
alt="Hierarchical DP sample 5"
class="gallery-image"
data-flex-grow="157"
data-flex-basis="378px"
&gt;&lt;/p&gt;
&lt;p&gt;In a later post, I will discuss how these tools are applied in the realm of Bayesian nonparametrics.&lt;/p&gt;</description></item><item><title>10x Engineering</title><link>https://tdhopper.com/blog/10x-engineering/</link><pubDate>Tue, 28 Jul 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/10x-engineering/</guid><description>&lt;blockquote class="twitter-tweet" lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;How to be a 10x engineer: Incur technical debt fast enough to appear 10x as productive as the ten engineers tasked with cleaning it up.&lt;/p&gt;&amp;mdash; Brian Degenhardt (@bmdhacks) &lt;a href="https://twitter.com/bmdhacks/status/560949130999365633"&gt;January 29, 2015&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;</description></item><item><title>Dirichlet Distribution and Dirichlet Processes</title><link>https://tdhopper.com/blog/dirichlet-distribution/</link><pubDate>Tue, 28 Jul 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/dirichlet-distribution/</guid><description>&lt;img src="https://tdhopper.com/images/dirichlet-distribution-mr-men.png" alt="Featured image of post Dirichlet Distribution and Dirichlet Processes" /&gt;&lt;h2 id="dirichlet-distribution"&gt;Dirichlet Distribution
&lt;/h2&gt;&lt;p&gt;The symmetric &lt;a class="link" href="https://en.wikipedia.org/wiki/Dirichlet_distribution" target="_blank" rel="noopener"
&gt;Dirichlet distribution&lt;/a&gt; (DD) can be considered a distribution of distributions. Each sample from the DD is a &lt;a class="link" href="https://en.wikipedia.org/wiki/Categorical_distribution" target="_blank" rel="noopener"
&gt;categorial distribution&lt;/a&gt; over $K$ categories. It is parameterized $G_0$, a distribution over $K$ categories and $\alpha$, a scale factor.&lt;/p&gt;
&lt;p&gt;The expected value of the DD is $G_0$. The variance of the DD is a function of the scale factor. When $\alpha$ is large, samples from $DD(\alpha\cdot G_0)$ will be very close to $G_0$. When $\alpha$ is small, samples will vary more widely.&lt;/p&gt;
&lt;p&gt;We demonstrate below by setting $G_0=[.2, .2, .6]$ and varying $\alpha$ from 0.1 to 1000. In each case, the mean of the samples is roughly $G_0$, but the standard deviation is decreases as $\alpha$ increases.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;np&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;scipy.stats&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dirichlet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_printoptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;precision&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scale_factor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;G0&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;.6&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;samples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dirichlet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scale_factor&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;G0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; alpha:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scale_factor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; element-wise mean:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;samples&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;element-wise standard deviation:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;samples&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;pre&gt;&lt;code&gt; alpha: 0.1
element-wise mean: [ 0.2 0.2 0.6]
element-wise standard deviation: [ 0.38 0.38 0.47]
alpha: 1
element-wise mean: [ 0.2 0.2 0.6]
element-wise standard deviation: [ 0.28 0.28 0.35]
alpha: 10
element-wise mean: [ 0.2 0.2 0.6]
element-wise standard deviation: [ 0.12 0.12 0.15]
alpha: 100
element-wise mean: [ 0.2 0.2 0.6]
element-wise standard deviation: [ 0.04 0.04 0.05]
alpha: 1000
element-wise mean: [ 0.2 0.2 0.6]
element-wise standard deviation: [ 0.01 0.01 0.02]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="dirichlet-process"&gt;Dirichlet Process
&lt;/h2&gt;&lt;p&gt;The &lt;a class="link" href="https://en.wikipedia.org/wiki/Dirichlet_process" target="_blank" rel="noopener"
&gt;Dirichlet Process&lt;/a&gt; can be considered a way to &lt;em&gt;generalize&lt;/em&gt; the Dirichlet distribution. While the Dirichlet distribution is parameterized by a discrete distribution $G_0$ and generates samples that are similar discrete distributions, the Dirichlet process is parameterized by a generic distribution $H_0$ and generates samples which are distributions similar to $H_0$. The Dirichlet process also has a parameter $\alpha$ that determines how similar how widely samples will vary from $H_0$.&lt;/p&gt;
&lt;p&gt;We can construct a sample $H$ (recall that $H$ is a probability distribution) from a Dirichlet process $\text{DP}(\alpha H_0)$ by drawing a countably infinite number of samples $\theta_k$ from $H_0$) and setting:&lt;/p&gt;
$$H=\sum_{k=1}^\infty \pi_k \cdot\delta(x-\theta_k)$$&lt;p&gt;where the $\pi_k$ are carefully chosen weights (more later) that sum to 1. ($\delta$ is the &lt;a class="link" href="https://en.wikipedia.org/wiki/Dirac_delta_function" target="_blank" rel="noopener"
&gt;Dirac delta function&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;$H$, a sample from $DP(\alpha H_0)$, is a &lt;em&gt;probability distribution&lt;/em&gt; that looks similar to $H_0$ (also a distribution). In particular, $H$ is a &lt;em&gt;discrete&lt;/em&gt; distribution that takes the value $\theta_k$ with probability $\pi_k$. This sampled distribution $H$ is a discrete distribution &lt;em&gt;even if $H_0$ has continuous support&lt;/em&gt;; the &lt;a class="link" href="http://www.statlect.com/glossary/support_of_a_random_variable.htm" target="_blank" rel="noopener"
&gt;support&lt;/a&gt; of $H$ is a countably infinite subset of the support $H_0$.&lt;/p&gt;
&lt;p&gt;The weights ($\pi_k$ values) of a Dirichlet process sample related the Dirichlet &lt;em&gt;process&lt;/em&gt; back to the Dirichlet &lt;em&gt;distribution&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.arbylon.net/publications/ilda.pdf" target="_blank" rel="noopener"
&gt;Gregor Heinrich&lt;/a&gt; writes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The defining property of the DP is that its samples have weights $\pi_k$ and locations $\theta_k$ distributed in such a way that when partitioning $S(H)$ into finitely many arbitrary disjoint subsets $S_1, \ldots, S_j$ $J&lt;\infty$, the sums of the weights $\pi_k$ in each of these $J$ subsets are distributed according to a Dirichlet distribution that is parameterized by $\alpha$ and a discrete base distribution (like $G_0$) whose weights are equal to the integrals of the base distribution $H_0$ over the subsets $S_n$.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As an example, Heinrich imagines a DP with a standard normal base measure $H_0\sim \mathcal{N}(0,1)$. Let $H$ be a sample from $DP(H_0)$ and partition the real line (the support of a normal distribution) as $S_1=(-\infty, -1]$, $S_2=(-1, 1]$, and $S_3=(1, \infty]$ then&lt;/p&gt;
$$H(S_1),H(S_2), H(S_3) \sim \text{Dir}\left(\alpha\,\text{erf}(-1), \alpha\,(\text{erf}(1) - \text{erf}(-1)), \alpha\,(1-\text{erf}(1))\right)$$&lt;p&gt;where $H(S_n)$ be the sum of the $\pi_k$ values whose $\theta_k$ lie in $S_n$.&lt;/p&gt;
&lt;p&gt;These $S_n$ subsets are chosen for convenience, however similar results would hold for &lt;em&gt;any&lt;/em&gt; choice of $S_n$. For any sample from a Dirichlet &lt;em&gt;process&lt;/em&gt;, we can construct a sample from a Dirichlet &lt;em&gt;distribution&lt;/em&gt; by partitioning the support of the sample into a finite number of bins.&lt;/p&gt;
&lt;p&gt;There are several equivalent ways to choose the $\pi_k$ so that this property is satisfied: the Chinese restaurant process, the stick-breaking process, and the Pólya urn scheme.&lt;/p&gt;
&lt;p&gt;To generate $\left\{\pi_k\right\}$ according to a stick-breaking process we define $\beta_k$ to be a sample from $\text{Beta}(1,\alpha)$. $\pi_1$ is equal to $\beta_1$. Successive values are defined recursively as&lt;/p&gt;
$$\pi_k=\beta_k \prod_{j=1}^{k-1}(1-\beta_j).$$&lt;p&gt;Thus, if we want to draw a sample from a Dirichlet process, we could, in theory, sample an infinite number of $\theta_k$ values from the base distribution $H_0$, an infinite number of $\beta_k$ values from the Beta distribution. Of course, sampling an infinite number of values is easier in theory than in practice.&lt;/p&gt;
&lt;p&gt;However, by noting that the $\pi_k$ values are &lt;em&gt;positive&lt;/em&gt; values summing to 1, we note that, in expectation, they must get increasingly small as $k\rightarrow\infty$. Thus, we can reasonably approximate a sample $H\sim DP(\alpha H_0)$ by drawing &lt;em&gt;enough&lt;/em&gt; samples such that $\sum_{k=1}^K \pi_k\approx 1$.&lt;/p&gt;
&lt;p&gt;We use this method below to draw approximate samples from several Dirichlet processes with a standard normal ($\mathcal{N}(0,1)$) base distribution but varying $\alpha$ values.&lt;/p&gt;
&lt;p&gt;Recall that a single sample from a Dirichlet process is a probability distribution over a countably infinite subset of the support of the base measure.&lt;/p&gt;
&lt;p&gt;The blue line is the PDF for a standard normal. The black lines represent the $\theta_k$ and $\pi_k$ values; $\theta_k$ is indicated by the position of the black line on the $x$-axis; $\pi_k$ is proportional to the height of each line.&lt;/p&gt;
&lt;p&gt;We generate enough $\pi_k$ values are generated so their sum is greater than 0.99. When $\alpha$ is small, very few $\theta_k$&amp;rsquo;s will have corresponding $\pi_k$ values larger than $0.01$. However, as $\alpha$ grows large, the sample becomes a more accurate (though still discrete) approximation of $\mathcal{N}(0,1)$.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;plt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;scipy.stats&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;norm&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dirichlet_sample_approximation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;betas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;betas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;betas&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;tol&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;betas&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;new_beta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;betas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_beta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_beta&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;thetas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pis&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thetas&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;plot_normal_dp_approximation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Dirichlet Process Sample with N(0,1) Base Measure&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;suptitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;alpha: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thetas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dirichlet_sample_approximation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pis&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;norm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;pis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vlines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thetas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linspace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;norm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plot_normal_dp_approximation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plot_normal_dp_approximation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plot_normal_dp_approximation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;plot_normal_dp_approximation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/dirichlet-distribution/2015-07-28-dirichlet-distribution-dirichlet-process_5_0.png"
width="376"
height="274"
srcset="https://tdhopper.com/blog/dirichlet-distribution/2015-07-28-dirichlet-distribution-dirichlet-process_5_0_hu_a75610b858e14bf9.png 480w, https://tdhopper.com/blog/dirichlet-distribution/2015-07-28-dirichlet-distribution-dirichlet-process_5_0_hu_bab3ccc13872cf62.png 1024w"
loading="lazy"
alt="Dirichlet Process sample with alpha=0.1"
class="gallery-image"
data-flex-grow="137"
data-flex-basis="329px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/blog/dirichlet-distribution/2015-07-28-dirichlet-distribution-dirichlet-process_5_1.png"
width="376"
height="274"
srcset="https://tdhopper.com/blog/dirichlet-distribution/2015-07-28-dirichlet-distribution-dirichlet-process_5_1_hu_2a51ea7464097484.png 480w, https://tdhopper.com/blog/dirichlet-distribution/2015-07-28-dirichlet-distribution-dirichlet-process_5_1_hu_4ef86ec0c9c34775.png 1024w"
loading="lazy"
alt="Dirichlet Process sample with alpha=1"
class="gallery-image"
data-flex-grow="137"
data-flex-basis="329px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/blog/dirichlet-distribution/2015-07-28-dirichlet-distribution-dirichlet-process_5_2.png"
width="376"
height="274"
srcset="https://tdhopper.com/blog/dirichlet-distribution/2015-07-28-dirichlet-distribution-dirichlet-process_5_2_hu_31c884ed34da6c44.png 480w, https://tdhopper.com/blog/dirichlet-distribution/2015-07-28-dirichlet-distribution-dirichlet-process_5_2_hu_396be73f32945635.png 1024w"
loading="lazy"
alt="Dirichlet Process sample with alpha=10"
class="gallery-image"
data-flex-grow="137"
data-flex-basis="329px"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/blog/dirichlet-distribution/2015-07-28-dirichlet-distribution-dirichlet-process_5_3.png"
width="376"
height="274"
srcset="https://tdhopper.com/blog/dirichlet-distribution/2015-07-28-dirichlet-distribution-dirichlet-process_5_3_hu_91ec0fc5cca07904.png 480w, https://tdhopper.com/blog/dirichlet-distribution/2015-07-28-dirichlet-distribution-dirichlet-process_5_3_hu_90761313b86e15ef.png 1024w"
loading="lazy"
alt="Dirichlet Process sample with alpha=1000"
class="gallery-image"
data-flex-grow="137"
data-flex-basis="329px"
&gt;&lt;/p&gt;
&lt;p&gt;Often we want to draw samples from a &lt;em&gt;distribution sampled from a Dirichlet process&lt;/em&gt; instead of from the Dirichlet process itself. Much of the literature on the topic unhelpful refers to this as sampling from a Dirichlet process.&lt;/p&gt;
&lt;p&gt;Fortunately, we don&amp;rsquo;t have to draw an infinite number of samples from the base distribution and stick breaking process to do this. Instead, we can draw these samples &lt;em&gt;as they are needed&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Suppose, for example, we know a finite number of the $\theta_k$ and $\pi_k$ values for a sample $H\sim \text{Dir}(\alpha H_0)$. For example, we know&lt;/p&gt;
$$\pi_1=0.5,\; \pi_2=0.3,\; \theta_1=0.1,\; \theta_2=-0.5.$$&lt;p&gt;To sample from $H$, we can generate a uniform random $u$ number between 0 and 1. If $u$ is less than 0.5, our sample is $0.1$. If $0.5&lt;=u&lt;0.8$, our sample is $-0.5$. If $u&gt;=0.8$, our sample (from $H$ will be a new sample $\theta_3$ from $H_0$. At the same time, we should also sample and store $\pi_3$. When we draw our next sample, we will again draw $u\sim\text{Uniform}(0,1)$ but will compare against $\pi_1, \pi_2$, AND $\pi_3$.&lt;/p&gt;
&lt;p&gt;The class below will take a base distribution $H_0$ and $\alpha$ as arguments to its constructor. The class instance can then be called to generate samples from $H\sim \text{DP}(\alpha H_0)$.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;numpy.random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DirichletProcessSample&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base_measure&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_stick_used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_stick_used&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DirichletProcessSample&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;roll_die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;stick_piece&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_stick_used&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;stick_piece&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stick_piece&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;new_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;new_value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nd"&gt;@staticmethod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;roll_die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This Dirichlet process class could be called &lt;em&gt;stochastic memoization&lt;/em&gt;. This idea was first articulated in somewhat abstruse terms by &lt;a class="link" href="http://danroy.org/papers/RoyManGooTen-ICMLNPB-2008.pdf" target="_blank" rel="noopener"
&gt;Daniel Roy, et al&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Below are histograms of 10000 samples drawn from &lt;em&gt;samples&lt;/em&gt; drawn from Dirichlet processes with standard normal base distribution and varying $\alpha$ values.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;pd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;norm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rvs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;n_samples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;samples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dirichlet_norm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DirichletProcessSample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;base_measure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Alpha: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dirichlet_norm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_samples&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="https://tdhopper.com/blog/dirichlet-distribution/2015-07-28-dirichlet-distribution-dirichlet-process_9_0.png"
width="380"
height="266"
srcset="https://tdhopper.com/blog/dirichlet-distribution/2015-07-28-dirichlet-distribution-dirichlet-process_9_0_hu_97d84cf08684a4f0.png 480w, https://tdhopper.com/blog/dirichlet-distribution/2015-07-28-dirichlet-distribution-dirichlet-process_9_0_hu_fa32d1a1f23d0dc1.png 1024w"
loading="lazy"
alt="Histograms of samples from Dirichlet processes with varying alpha"
class="gallery-image"
data-flex-grow="142"
data-flex-basis="342px"
&gt;&lt;/p&gt;
&lt;p&gt;Note that these histograms look very similar to the corresponding plots of sampled distributions above. However, these histograms are plotting &lt;em&gt;points sampled from a distribution sampled from a Dirichlet process&lt;/em&gt; while the plots above were showing approximate &lt;em&gt;distributions samples from the Dirichlet process&lt;/em&gt;. Of course, as the number of samples from each $H$ grows large, we would expect the histogram to be a very good empirical approximation of $H$.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://tdhopper.com/blog/hdp-sampling/" &gt;In a another post&lt;/a&gt;, I will look at how this &lt;code&gt;DirichletProcessSample&lt;/code&gt; class can be used to draw samples from a &lt;em&gt;hierarchical&lt;/em&gt; Dirichlet process.&lt;/p&gt;</description></item><item><title>Handy One-off Webpages</title><link>https://tdhopper.com/blog/handy-one-off-webpages/</link><pubDate>Mon, 27 Jul 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/handy-one-off-webpages/</guid><description>&lt;p&gt;I&amp;rsquo;m starting to love single-page informational websites. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="http://keepachangelog.com" target="_blank" rel="noopener"
&gt;Keep a CHANGELOG&lt;/a&gt;: &lt;a class="link" href="http://olivierlacan.com/" title="Olivier Lacan"
target="_blank" rel="noopener"
&gt;Olivier Lacan&lt;/a&gt;&amp;rsquo;s guide to writing a &lt;em&gt;CHANGELOG.md&lt;/em&gt; for open source projects.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://strftime.org" target="_blank" rel="noopener"
&gt;strftime&lt;/a&gt;: &lt;a class="link" href="https://twitter.com/mccutchen" title="Will McCutchen (@mccutchen) | Twitter"
target="_blank" rel="noopener"
&gt;Will McCutchen&lt;/a&gt;&amp;rsquo;s little page of the easily forgettable date formatting codes for Python programmers.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://pyformat.info" target="_blank" rel="noopener"
&gt;PyFormat&lt;/a&gt;: &lt;a class="link" href="https://twitter.com/ulope" title="Ulrich Petri (@ulope) | Twitter"
target="_blank" rel="noopener"
&gt;Ulrich Petri&lt;/a&gt; and &lt;a class="link" href="https://github.com/zerok" title="zerok (Horst Gutmann) Âˇ GitHub"
target="_blank" rel="noopener"
&gt;Horst Gutmann&lt;/a&gt;&amp;rsquo;s guide to new style (&lt;code&gt;.format()&lt;/code&gt;) string formatting in Python.&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://twofactorauth.org" target="_blank" rel="noopener"
&gt;Two Factor Auth&lt;/a&gt;: &amp;ldquo;List of websites and whether or not they support 2FA.&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My website &lt;a class="link" href="https://freespace.tdhopper.com" target="_blank" rel="noopener"
&gt;Free Space&lt;/a&gt; is in this same vein.&lt;/p&gt;
&lt;p&gt;Publishing sites like this is free with &lt;a class="link" href="https://netlify.com" target="_blank" rel="noopener"
&gt;Netlify&lt;/a&gt; and &lt;a class="link" href="https://pages.cloudflare.com" target="_blank" rel="noopener"
&gt;Cloudflare Pages&lt;/a&gt;. I would love to see more of them created!&lt;/p&gt;</description></item><item><title>Thinking at Work</title><link>https://tdhopper.com/blog/thinking-at-work/</link><pubDate>Wed, 22 Jul 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/thinking-at-work/</guid><description>&lt;p&gt;Having worked from home for the last few years, I have a hard time understanding how people get anything done in open-floor plan offices. I would be overwhelmed and frustrated by the noise and commotion.&lt;/p&gt;
&lt;p&gt;I assumed open-floor plans for software shops were a relatively new invention. However, I just started reading &lt;a class="link" href="https://en.wikipedia.org/wiki/Peopleware:_Productive_Projects_and_Teams" target="_blank" rel="noopener"
&gt;Peopleware: Productive Projects and Teams&lt;/a&gt;, first published in 1987, and discovered that the first third of the book rails against open floor plan offices. I particularly enjoyed this quote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In my years at Bell Labs, we worked in two-person offices. They were spacious, quiet, and the phones could be diverted. I shared my office with Wendl Thomis, who went on to build a small empire as an electric toy maker. In those days, he was working on the Electronic Switching System fault dictionary. The dictionary scheme relied upon the notion of n-space proximity, a concept that was hairy enough to challenge even Wendel&amp;rsquo;s powers of concentration. One afternoon, I was bent over a program listing while Wendl was staring into space, his feet propped up on his desk. Our boss came in and asked, &amp;ldquo;Wendl! What are you doing?&amp;rdquo; Wendl said, &amp;ldquo;I&amp;rsquo;m thinking.&amp;rdquo; And the boss said, &amp;ldquo;Can&amp;rsquo;t you do that at home?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The difference between that Bell Labs environment and a typical modern-day office plan is that in those quiet offices, one at least had the option of thinking on the job. In most of the office space we encounter today, there is enough noise and interruption to make any serious thinking virtually impossible. More is the shame: Your people bring their brains with them every morning. They could put them to work for you at no additional cost if only there were a small measure of peace and quiet in the workplace.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>How I Became a Data Scientist Despite Having Been a Math Major</title><link>https://tdhopper.com/blog/how-i-became-a-data-scientist/</link><pubDate>Mon, 11 May 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/how-i-became-a-data-scientist/</guid><description>&lt;img src="https://tdhopper.com/math-class.png" alt="Featured image of post How I Became a Data Scientist Despite Having Been a Math Major" /&gt;&lt;p&gt;Caution: the following post is laden with qualitative extrapolation of
anecdotes and impressions. Perhaps ironically (though perhaps not), it is not
a data driven approach to measuring the efficacy of math majors as data
scientists. If you have a differing opinion, I would greatly appreciate you to
carefully articulate it and share it with the world.&lt;/p&gt;
&lt;p&gt;I recently started my third &amp;ldquo;real&amp;rdquo; job since finishing school; at my first and
third jobs I have been a &amp;ldquo;data scientist&amp;rdquo;. I was a math major in college (and
pretty good at it) and spent a year in the math Ph.D. program at the
University of Virginia (and performed well there as well). These two facts
alone would not have equipped me for a career in data science. In fact, it
remains unclear to me that those two facts alone would have prepared me for
any career (with the possible exception of teaching) without significantly
more training.&lt;/p&gt;
&lt;h2 id="there-has-never-been-a-better-time-to-be-a-mathematician"&gt;&amp;ldquo;There has never been a better time to be a mathematician&amp;rdquo;?
&lt;/h2&gt;&lt;p&gt;When I was in college &lt;a class="link" href="https://web.archive.org/web/20160315082911/https://www.bloomberg.com/news/articles/2006-01-22/math-will-rock-your-world" target="_blank" rel="noopener"
&gt;Business Week published an article&lt;/a&gt; declaring &amp;ldquo;There has never been a better time to be a mathematician.&amp;rdquo;
At the time, I saw an enormous disconnect between the piece and what I was
being taught in math classes (and thus what I considered to be a
&amp;ldquo;mathematician&amp;rdquo;). I have come across other pieces lauding this as the age of
the mathematicians, and more often than not, I&amp;rsquo;ve wondered if the author knew
what students actually studied in math departments.&lt;/p&gt;
&lt;h3 id="background-on-me"&gt;Background on Me
&lt;/h3&gt;&lt;p&gt;The math courses I had as an undergraduate were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linear algebra&lt;/li&gt;
&lt;li&gt;Discrete math&lt;/li&gt;
&lt;li&gt;Differential equations (ODEs and numerical)&lt;/li&gt;
&lt;li&gt;Theory of statistics 1&lt;/li&gt;
&lt;li&gt;Numerical analysis 1 (numerical linear algebra) and 2 (quadrature)&lt;/li&gt;
&lt;li&gt;Abstract algebra&lt;/li&gt;
&lt;li&gt;Number theory&lt;/li&gt;
&lt;li&gt;Real analysis&lt;/li&gt;
&lt;li&gt;Complex analysis&lt;/li&gt;
&lt;li&gt;Intermediate analysis (point set topology)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My program also required a one semester intro to C++ and two semesters of
freshman physics. In my year as a math Ph.D. student, I took analysis,
algebra, and topology classes; had I stayed in the program, my future
coursework would have been similar: pure math where homework problems
consistent almost exclusively of proofs done with pen and paper (or in LaTeX).&lt;/p&gt;
&lt;h2 id="what-is-data-science"&gt;What is Data Science?
&lt;/h2&gt;&lt;p&gt;Though my current position occasionally requires mathematical proof, I suspect
that is rare among data scientist. While the &amp;ldquo;data science&amp;rdquo; demarcation
problem is challenging (and I will not seek to solve it here), it seems
evident that my curriculum lacked preparation in many essential areas of data
science. Chief among these are programming skill, knowledge of experimental
statistics, and experience with math modeling.&lt;/p&gt;
&lt;h3 id="data-science-requires-programming-and-engineering"&gt;Data Science Requires Programming and Engineering
&lt;/h3&gt;&lt;p&gt;Few would argue that programming ability is not a key skill of data science.
As Drew Conway &lt;a class="link" href="http://drewconway.com/zia/2013/3/26/the-data-science-venn-diagram" target="_blank" rel="noopener"
&gt;has argued&lt;/a&gt;, a data scientist need not have a degree in computer
science, but &amp;ldquo;Being able to manipulate text files at the command-line,
understanding vectorized operations, thinking algorithmically; these are the
hacking skills that make for a successful data hacker.&amp;rdquo; Many of my undergrad
peers, having briefly seen C++ freshman year and occasionally used Mathematica
to solve ODEs for homework assignments, would have been unaware that
manipulation of a file from the command-line was even possible, much less have
been able to write a simple sed script; there was little difference with my
grad school classmates.&lt;/p&gt;
&lt;p&gt;Many data science positions require even more than the ability to solve
problems with code. As &lt;a class="link" href="http://treycausey.com/software_dev_skills.html" target="_blank" rel="noopener"
&gt;Trey Causey has recently
explained&lt;/a&gt;, many positions
require understanding of &lt;em&gt;software engineering&lt;/em&gt; skills and tools such as
writing reusable code, using version control, software testing, and logging.
Though I gained a fair bit of programming skill in college, these skills, now
essential in my daily work, remained foreign to me until years later.&lt;/p&gt;
&lt;h3 id="data-science-requires-applied-statistics"&gt;Data Science Requires Applied Statistics
&lt;/h3&gt;&lt;p&gt;My math training had a lack of statistics courses. Though my brief exposure to
mathematical statistics has been valuable in picking up machine learning,
experimental statistics was missing altogether. Many data science teams are
interested in questions of causal inference and design and analysis of
experiments; some would make these &lt;em&gt;essential&lt;/em&gt; skills for a data scientist. I
learned nothing about these topics in math departments. Moreover, machine
learning, also a cornerstone of data science, is not a subject I could have
even defined until after I was finished with my math coursework; at the end of
college, I would have said artificial intelligence was mostly about rule-based
systems in Lisp and Prolog.&lt;/p&gt;
&lt;h3 id="data-science-involves-very-applied-math"&gt;Data Science Involves &lt;em&gt;Very Applied Math&lt;/em&gt;
&lt;/h3&gt;&lt;p&gt;Even if statistics had play a more prominent role in my coursework, those who
have studied statistics know there is often a gulf between understanding
textbook statistics and being able to effectively apply statistical models and
methods to real world problems. This is only an aspect of a bigger issue:
mathematical (including statistical) modeling is an extraordinarily
challenging problem, but instruction on effectively model real world problems
is absent from many math programs. To this day, defining my problem in
mathematical terms one of the hardest problems I face; I am certain that I am
not alone on this. Though I am now armed with a wide variety of mathematical
models, it is rarely clear exactly which model can or should be applied in a
given situation.&lt;/p&gt;
&lt;p&gt;I suspect that many people, even technical people, are uncertain as to what
academic math is beyond undergraduate calculus. Mathematicians mostly work in
the logical manipulation of abstractly defined structures. These structures
rarely bear any necessary relationship to physical entities or data sets
outside the abstractly defined domain of discourse. Though some might argue I
am speaking only of &amp;ldquo;pure&amp;rdquo; mathematics, this is often true of what is formally
known as &amp;ldquo;applied mathematics&amp;rdquo;. &lt;a class="link" href="http://www.johndcook.com/veryappliedmath.html" target="_blank" rel="noopener"
&gt;John D. Cook has made similar
observations&lt;/a&gt; about the
limitations of pure and applied math (as proper disciplines) in dubbing
himself a &amp;ldquo;very applied mathematician&amp;rdquo;. Very applied mathematics is &amp;ldquo;an
interest in the grubby work required to see the math actually used and a
willingness to carry it out. This involves not just math but also computing,
consulting, managing, marketing, etc.&amp;rdquo; These skills are conspicuously absent
from most math curricula I am familiar with.&lt;/p&gt;
&lt;h2 id="math--data-science"&gt;Math → Data Science
&lt;/h2&gt;&lt;p&gt;Given this description of how my schooling left me woefully unprepared for a
career in data science, one might ask how I have had two jobs with that title.
I can think of several (though probably not all) reasons.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First, the academic study of mathematics provides much of the theoretical
underpinnings of data science.&lt;/strong&gt; Mathematics underlies the study of machine
learning, statistics, optimization, data structures, analysis of algorithms,
computer architecture, and other important aspects of data science. Knowledge
of mathematics (potentially) allows the learner to more quickly grasp each of
these fields. For example, learning how &lt;a class="link" href="http://en.wikipedia.org/wiki/Principal_component_analysis" target="_blank" rel="noopener"
&gt;principle component
analysis&lt;/a&gt;—a math
model that can be applied and interpreted by someone without formal
mathematical training—works will be significantly easier for someone with
earlier exposure linear algebra. On a meta-level, training in mathematics
forces students to think carefully and solve hard problems; these skills are
valuable in many fields, including data science.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;My second reason is connect to the first: I unwittingly took a number of
courses that later played important roles in my data science toolkit.&lt;/strong&gt; For
example, my current work in Bayesian inference has been made possible by my
knowledge of linear algebra, numerical analysis, stochastic processes, measure
theory, and mathematical statistics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Third, I did a minor in computer science as an undergraduate.&lt;/strong&gt; That
provided a solid foundation for me when I decided to get serious about
building programming skill in 2010. Though my academic exposure to computer
science lacked any software engineer skills, I left college with a solid grasp
of basic data structures, analysis of algorithms, complexity theory, and a
handful of programming languages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fourth, I did a masters degree in operations research (after my year as a
math PhD student convinced me pure math wasn&amp;rsquo;t for me).&lt;/strong&gt; This provided me
with experience in math modeling, a broad knowledge of mathematical
optimization (central to machine learning), and the opportunity to take
graduate-level machine learning classes.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fifth, my insatiable curiosity in computers and problem solving has played a
key role in my career success.&lt;/strong&gt; Eager to learn something about computer
programming, I taught myself PHP and SQL as a high school student (to make
Tolkien fan sites, incidentally). Having been given small Mathematica-based
homework assignments in freshman differential equations, I bought and read a
book on programming Mathematica. Throughout college and grad school, I often
tried—and sometimes succeeded—to write programs to solve homework problems
that professors expected to be solved by hand. This curiosity has proven
valuable time and time again as I&amp;rsquo;ve been required to learn new skills and
solve technical problems of all varieties. I&amp;rsquo;m comfortable jumping in to solve
a new problem at work, because I&amp;rsquo;ve been doing that on my own time for fifteen
years.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sixth, I have been been fortunate enough to have employers who have
patiently taught me and given me the freedom to learn on my own.&lt;/strong&gt; I have
learned an enormous amount in my two and a half year professional career, and
I don&amp;rsquo;t anticipate slowing down any time soon. As &lt;a class="link" href="https://twitter.com/mat_kelcey/status/328179275893207042" target="_blank" rel="noopener"
&gt;Mat
Kelcey&lt;/a&gt; has said:
always be sure you&amp;rsquo;re not the smartest one in the room. I am very thankful for
three jobs where I&amp;rsquo;ve been surrounded by smart people who have taught me a
lot, and for supervisors who trust me enough to let me learn on my own.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Finally,&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; it would be hard for me to overvalue the four and a half years of
participation in the data science community on Twitter.&lt;/strong&gt; Through Twitter, I
have the ear of some of data science&amp;rsquo;s brightest minds (most of whom I&amp;rsquo;ve
never met in person), and I&amp;rsquo;ve built a peer network that has helped me find my
current and last job. However, I mostly want to emphasize the pedagogical
value of Twitter. Every day, I&amp;rsquo;m updated on the release of new software tools
for data science, the best new blog posts for our field, and the musings of of
some of my data science heros. Of course, I don&amp;rsquo;t read every blog post or
learn every software tool. But Twitter helps me to recognize which posts are
most worth my time, and because of Twitter, I know &lt;em&gt;something&lt;/em&gt; instead of
&lt;em&gt;nothing&lt;/em&gt; about Theano, Scalding, and dplyr.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id="conclusions"&gt;Conclusions
&lt;/h2&gt;&lt;p&gt;I don&amp;rsquo;t know to what extent my experience generalizes&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;, in either the
limitations of my education or my analysis of my success, but I am obviously
not going to let that stop me from drawing some general conclusions.&lt;/p&gt;
&lt;h3 id="hiring-data-scientists"&gt;Hiring Data Scientists
&lt;/h3&gt;&lt;p&gt;For those hiring data scientists, recognize that mathematics as taught might
not be the same mathematics you need from your team. Plenty of people with
PhDs in mathematics would be unable to define linear regression or bloom
filters. At the same time, recognize that math majors are taught to think well
and solve hard problems; these skills shouldn&amp;rsquo;t be undervalued. Math majors
are also experienced in reading and learning math! They may be able to read
academic papers and understand difficult (even if new) mathematical more
quickly than a computer scientist or social scientist. Given enough practice
and training, they would probably be excellent programmers.&lt;/p&gt;
&lt;h3 id="studying-math"&gt;Studying Math
&lt;/h3&gt;&lt;p&gt;For those studying math, recognize that the field you love, in its formal
sense, may be keeping you away from enjoyable and lucrative careers. Most of
your math professors have spent their adult lives solving math problems on
paper or on a chalkboard. They are inexperienced and, possibly,
unknowledgeable about &lt;em&gt;very applied mathematics&lt;/em&gt;. A successful career in pure
mathematics will be very hard and will require you to be very good. While
there seem to be lots of jobs in teaching, they will rarely pay well.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re still an student, you have a great opportunity to take control of
your career path. Consider taking computer science classes (e.g. data
structures, algorithms, software engineering, machine learning) and statistics
classes (e.g. experimental design, data analysis, data mining).&lt;/p&gt;
&lt;p&gt;For both students and graduates, recognize your math knowledge becomes very
marketable when combined skills such as programming and machine learning;
there are a wealth of good books, MOOCs, and blog posts that can help you
learn these things. More over, the barrier to entry for getting started with
production quality tools has never been lower. Don&amp;rsquo;t let your coursework be
the extent of your education. There is so much more to learn!&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update (Oct. 2017)&lt;/strong&gt; : &lt;a class="link" href="https://www.youtube.com/watch?v=Q_UrZ4pmKmI" target="_blank" rel="noopener"
&gt;I gave a talk based on this
post&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update (Mar. 2018)&lt;/strong&gt; : I get a lot of emails with questions about this post,
so I wrote &lt;a class="link" href="https://tdhopper.com/blog/faq/" &gt;FAQ&lt;/a&gt; post trying to answer some of
them.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;At the same time, my academic training in operations research failed me, in some aspects, for a successful career in operations research. For example, practical math modeling was not sufficiently emphasized and the skills of computer programming and software development were undervalued.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Of course, I have plenty of data science skills left to learn. My knowledge of experimental design is still pretty fuzzy. I still struggle with effective mathematical modeling. I haven&amp;rsquo;t deployed a large scale machine learning system to production. I suck at software logging. I have no idea how deep learning works.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;I have successfully answered more than one interview question by regurgitating knowledge gleaned from tweets.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;Among other reasons, I didn&amp;rsquo;t really plan to get where I am today. I changed majors no fewer than three times in college (physics, CS, and math) and essentially dropped out of two PhD programs!&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;For example, install &lt;a class="link" href="https://store.continuum.io/cshop/anaconda/" target="_blank" rel="noopener"
&gt;Anaconda&lt;/a&gt; and start playing with some of &lt;a class="link" href="https://github.com/ipython/ipython/wiki/A-gallery-of-interesting-IPython-Notebooks" target="_blank" rel="noopener"
&gt;these IPython notebooks&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Links for March 23, 2015</title><link>https://tdhopper.com/blog/sundry-links-for-march-23-2015/</link><pubDate>Mon, 23 Mar 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-march-23-2015/</guid><description>&lt;p&gt;&lt;a class="link" href="https://aws.amazon.com/blogs/aws/start-using-landsat-on-aws/" target="_blank" rel="noopener"
&gt;Start Using Landsat on AWS&lt;/a&gt;: &amp;ldquo;The Landsat program has been running since 1972 and is the longest ongoing project to collect such imagery. Landsat 8 is the newest Landsat satellite and it gathers data based on visible, infrared, near-infrared, and thermal-infrared light. … You can now access over 85,000 Landsat 8 scenes&amp;rdquo; on AWS.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.lurklurk.org/linkers/linkers.html" target="_blank" rel="noopener"
&gt;Beginner&amp;rsquo;s Guide to Linkers&lt;/a&gt;: I’m getting back into doing a little C++ programming. Having spent the last 5 years in scripting languages, this was a helpful refresher on compilation.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.labnol.org/internet/auto-forward-gmail-messages/20665/" target="_blank" rel="noopener"
&gt;How to Auto-Forward your Gmail Messages in Bulk&lt;/a&gt;: Use Google App Scripts to autoforward emails by simply adding a label. I use this to add things to my &lt;a class="link" href="https://support.omnigroup.com/omnifocus-mail-drop" target="_blank" rel="noopener"
&gt;Omnifocus task link&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://math.stackexchange.com/questions/2949/which-one-result-in-mathematics-has-surprised-you-the-most" target="_blank" rel="noopener"
&gt;Which one result in mathematics has surprised you the most?&lt;/a&gt;: On Mathematics Stack Exchange. It might have been &lt;a class="link" href="http://en.wikipedia.org/wiki/Huffman_coding" target="_blank" rel="noopener"
&gt;Huffman Coding&lt;/a&gt; for me.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://www.youtube.com/watch?v=3OHNYIg6N5Y&amp;amp;feature=youtube_gdata_player" target="_blank" rel="noopener"
&gt;Ruby Midwest 2013 The Most Important Optimization: Happiness&lt;/a&gt;: Ernie Miller explains why he doesn’t let his career trump his happiness.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://tonyfischetti.github.io/sake/" target="_blank" rel="noopener"
&gt;Sake by tonyfischetti&lt;/a&gt;: Something of a modern GNU Make: &amp;ldquo;Sake is a way to easily design, share, build, and visualize workflows with intricate interdependencies. Sake is self-documenting because the instructions for building a project also serve as the documentation of the project&amp;rsquo;s workflow.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/n1k0/SublimeHighlight" target="_blank" rel="noopener"
&gt;n1k0/SublimeHighlight&lt;/a&gt;: &amp;ldquo;An humble SublimeText package for exporting highlighted code as RTF or HTML.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Concatenate MP3 Files</title><link>https://tdhopper.com/blog/concatenate-mp3-files/</link><pubDate>Sun, 15 Mar 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/concatenate-mp3-files/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Concatenate MP3 Files" /&gt;&lt;blockquote class="twitter-tweet" data-lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;TIL `cat` will correctly join MP3 files &lt;a href="http://t.co/mmc4NN2Kmp"&gt;http://t.co/mmc4NN2Kmp&lt;/a&gt;&lt;/p&gt;&amp;mdash; Tim Hopper (@tdhopper) &lt;a href="https://twitter.com/tdhopper/status/577149891077619713"&gt;March 15, 2015&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;From Stackoverflow:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An MP3 file is nothing more than the raw MPEG2-Layer 3 (audio) stream data, there is no file level header structure with, for example, duration, original source, encoding info.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Thus&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cat file1.mp3 file2.mp3 &amp;gt; out.mp3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;can join MP3 files.&lt;/p&gt;</description></item><item><title>Links for March 13, 2015</title><link>https://tdhopper.com/blog/sundry-links-for-march-13-2015/</link><pubDate>Fri, 13 Mar 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-march-13-2015/</guid><description>&lt;p&gt;&lt;a class="link" href="http://stackoverflow.com/questions/21360361/how-to-dynamically-update-a-plot-in-a-loop-in-ipython-notebook-within-one-cell" target="_blank" rel="noopener"
&gt;Dynamically Update a Plot in IPython&lt;/a&gt;: One thing I miss about Mathematica is &lt;a class="link" href="http://reference.wolfram.com/language/ref/Animate.html" target="_blank" rel="noopener"
&gt;Animate&lt;/a&gt; and &lt;a class="link" href="http://reference.wolfram.com/language/ref/Manipulate.html" target="_blank" rel="noopener"
&gt;Manipulate&lt;/a&gt;. IPython is slowing getting similar functionality. Here’s how to dynamically update a plot.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/jiahao/ipython-profile" target="_blank" rel="noopener"
&gt;Jiahao&amp;rsquo;s IPython Notebook customizations&lt;/a&gt;: Drop this CSS file on your machine, and suddenly your IPython notebooks look quite beautiful!&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.duetdisplay.com/" target="_blank" rel="noopener"
&gt;Duet Display&lt;/a&gt;: I tried Air Display a few years ago, and it wasn’t worth the hassle. But Duet Display is a fantastic way to turn your iPad into an external display.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/jbmouret/matplotlib_for_papers" target="_blank" rel="noopener"
&gt;Creating publication-quality figures with Matplotlib&lt;/a&gt;: Plotting in Python frustrates me to no end. But here’s a nice tutorial on creating nice figures in with Matplotlib.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://pypi.python.org/pypi/retrying" target="_blank" rel="noopener"
&gt;retrying 1.3.3 : Python Package Index&lt;/a&gt;: Python decorators &amp;ldquo;to simplify the task of adding retry behavior to just about anything.&amp;rdquo; These work like a charm!&lt;/p&gt;</description></item><item><title>Introduction to PySpark</title><link>https://tdhopper.com/talks/intro-to-pyspark/</link><pubDate>Sat, 28 Feb 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/talks/intro-to-pyspark/</guid><description>&lt;img src="https://tdhopper.com/preso.png" alt="Featured image of post Introduction to PySpark" /&gt;&lt;p&gt;I gave a talk at the Research Triangle Analysts meetup about Pyspark. It wasn&amp;rsquo;t recorded, but you can &lt;a class="link" href="http://nbviewer.jupyter.org/format/slides/github/tdhopper/rta-pyspark-presentation/blob/master/slides.ipynb#/" target="_blank" rel="noopener"
&gt;see the IPython notebook I presented from&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Links for January 19, 2015</title><link>https://tdhopper.com/blog/sundry-links-for-january-19-2015/</link><pubDate>Mon, 19 Jan 2015 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-january-19-2015/</guid><description>&lt;p&gt;&lt;a class="link" href="http://www.mattblodgett.com/2015/01/but-where-do-people-work-in-this-office.html" target="_blank" rel="noopener"
&gt;Matt Blodgett: But Where Do People Work in This Office?&lt;/a&gt;: &amp;ldquo;After looking through tons of cool office photos of many of the hottest companies in the Valley, I started to play a fun game I made up called &amp;lsquo;spot the desks’. I’ll show you what I mean.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://blog.stackoverflow.com/2015/01/why-we-still-believe-in-private-offices/" target="_blank" rel="noopener"
&gt;Why We (Still) Believe in Private Offices&lt;/a&gt;: Joel Spolsky and Fog Creek Software have been relentless defenders of quite, private offices for developers. They continue that here.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.alfredforum.com/topic/4686-pandoctor-an-alfred-gui-for-pandoc/" target="_blank" rel="noopener"
&gt;Pandoctor: An Alfred GUI for Pandoc&lt;/a&gt;: If you use &lt;a class="link" href="http://johnmacfarlane.net/pandoc/" target="_blank" rel="noopener"
&gt;Pandoc&lt;/a&gt; and &lt;a class="link" href="http://alfredapp.com/" target="_blank" rel="noopener"
&gt;Alfred&lt;/a&gt;, this is worth trying.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/tdhopper/alfred-hop" target="_blank" rel="noopener"
&gt;Alfred Hop&lt;/a&gt;: I use a little bash tool called &lt;a class="link" href="https://github.com/Cue/hop" target="_blank" rel="noopener"
&gt;Hop&lt;/a&gt; to bookmark frequently used directories. I made this tool to give me quick access to my bookmarks from Alfred.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/realpython/discover-flask" target="_blank" rel="noopener"
&gt;Discover Flask&lt;/a&gt;: Flask, the lightweight Python framework, is a joy to use. Here’s a nice introduction to it.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://cran.rstudio.com/web/packages/dplyr/vignettes/introduction.html" target="_blank" rel="noopener"
&gt;Introduction to dplyr&lt;/a&gt;: I haven’t used R much since leaving my last job, but the ecosystem has been booming with great tools; dplyr is one of them.&lt;/p&gt;</description></item><item><title>Links for December 29, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-december-29-2014/</link><pubDate>Mon, 29 Dec 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-december-29-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="https://www.youtube.com/watch?v=gurZp2JoD-s" target="_blank" rel="noopener"
&gt;Sublime: Nice Features &amp;amp; Plugins&lt;/a&gt;: A brief talk introducing my favorite editor.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/spamwax/alfred-pinboard" target="_blank" rel="noopener"
&gt;Alfred Workflow for Pinboard&lt;/a&gt;: I&amp;rsquo;ve started using &lt;a class="link" href="http://pinboard.in/" target="_blank" rel="noopener"
&gt;Pinboard&lt;/a&gt; a bit for organizing links. Here&amp;rsquo;s something that has the chance of getting me much deeper into pinboard: a powerful Alfred Workflow for interacting with Pinboard from your Mac&amp;rsquo;s keyboard. HT &lt;a class="link" href="http://www.bachyaproductions.com/link-mash-december-23-2014/" target="_blank" rel="noopener"
&gt;Aaron Bachya&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/alvarotrigo/fullPage.js" target="_blank" rel="noopener"
&gt;fullPage.js&lt;/a&gt;: I&amp;rsquo;ve been using this jquery plugin in a forthcoming project. It makes it really easy to create slide-like single page websites.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://benkurtovic.com/2014/06/01/obfuscating-hello-world.html" target="_blank" rel="noopener"
&gt;Obfuscating &amp;ldquo;Hello world!&amp;rdquo;&lt;/a&gt;: The author attempts to write the worst &amp;lsquo;hello world&amp;rsquo; possibe in Python. He does a good job.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://ryanmo.co/2014/05/10/show-time-in-multiple-time-zones-with-textexpander/" target="_blank" rel="noopener"
&gt;Show Time in Multiple Time Zones with TextExpander&lt;/a&gt;: As I spend more time working with people in different time zones, tools like this help remove the cognitive challenge of translating time.&lt;/p&gt;</description></item><item><title>Links for December 22, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-december-22-2014/</link><pubDate>Mon, 22 Dec 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-december-22-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="http://unix4lyfe.org/time/?v=1" target="_blank" rel="noopener"
&gt;Time&lt;/a&gt;: Programmers all hate time, timezones, etc. Here are some helpful &amp;ldquo;notes about time&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://strftime.org/" target="_blank" rel="noopener"
&gt;Python strftime reference&lt;/a&gt;: Speaking of time: &amp;ldquo;A quick reference for Python&amp;rsquo;s strftime formatting directives.&amp;rdquo; I have to look this stuff each time I need it.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/earwig/git-repo-updater" target="_blank" rel="noopener"
&gt;gitup&lt;/a&gt;: &amp;ldquo;A console script that allows you to easily update multiple git repositories at once&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.slate.com/articles/podcasts/working/2014/12/google_software_engineer_nina_kang_talks_about_the_differences_between_writing.html" target="_blank" rel="noopener"
&gt;The “How Does a Google Coder Work?” Edition
&lt;/a&gt;: I enjoyed this interview. My favorite quote? &amp;ldquo;When you&amp;rsquo;re reading code is it as clear as reading English?&amp;rdquo; &amp;ldquo;If I&amp;rsquo;m reading C++ code, it&amp;rsquo;s clearer.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www-stat.wharton.upenn.edu/~steele/HoldingPen/SunsetSalvo.pdf" target="_blank" rel="noopener"
&gt;Sunset Salvo&lt;/a&gt;: John Turkey discusses practical data analysis and statistical humility.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://stat.duke.edu/bnp10/index.html%3Fpage_id=1.html" target="_blank" rel="noopener"
&gt;10th Conference on Bayesian Nonparametrics&lt;/a&gt;: This is coming up in my own back yard. I’m excited!&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://static.googleusercontent.com/media/research.google.com/en/us/pubs/archive/43146.pdf" target="_blank" rel="noopener"
&gt;Machine Learning: The High-Interest Credit Card of Technical Debt&lt;/a&gt;: I haven’t read this in detail, but the premise makes tons of sense to me: &amp;ldquo;It is remarkably easy to incur massive ongoing maintenance costs at the system level when applying machine learning.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Should I get a Ph.D.?</title><link>https://tdhopper.com/phd/</link><pubDate>Mon, 08 Dec 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/phd/</guid><description>&lt;img src="https://tdhopper.com/images/crossroads2.png" alt="Featured image of post Should I get a Ph.D.?" /&gt;&lt;p&gt;Last year, I published nine interviews with Internet friends about how an academically-minded, 22-year-old college senior should work on a Ph.D. Many people have told me the interviews have been helpful for them or that they&amp;rsquo;ve emailed them to others.&lt;/p&gt;
&lt;p&gt;I hope this continues to be a valuable resource. I&amp;rsquo;d encourage you to share this with anyone you know who is thinking through this question.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://s3.us-east-1.amazonaws.com/tdhopper.com/shouldigetaphd.pdf" target="_blank" rel="noopener"
&gt;Download PDF version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://s3.us-east-1.amazonaws.com/tdhopper.com/shouldigetaphd.epub" target="_blank" rel="noopener"
&gt;Download ePUB version&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="excerpts"&gt;Excerpts
&lt;/h2&gt;&lt;h3 id="dr-john-d-cookfreelance-consultant-and-blogger"&gt;DR. JOHN D. COOK, FREELANCE CONSULTANT AND BLOGGER
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;There are basically two reasons to get a Ph.D.: personal satisfaction, and credentials for a job requiring a Ph.D.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;If a professor has never worked outside of academia, I’d be skeptical of anything he or she says about “the real world.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="dr-paul-rubinprofessor-emeritus"&gt;DR. PAUL RUBIN, PROFESSOR EMERITUS
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;If you do not enjoy doing research, pursuing a Ph.D. will be difficult, unfulfilling and possibly pointless (since you will not want a job with research expectations).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Having both a masters and doctorate in mathematics is no better than having just a doctorate.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="dr-eric-jonaspostdoc-and-serial-entrepreneur"&gt;DR. ERIC JONAS, POSTDOC AND SERIAL ENTREPRENEUR
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;My best advice to an undergraduate curious about the “experience” of graduate school is: work in a lab while you’re an undergrad.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;A lot of people think of a PhD as being like an undergraduate degree in that you’ve “learned a lot of material”. This is false.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="mr-carl-vogeldata-scientist"&gt;MR. CARL VOGEL, DATA SCIENTIST
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;The world is full of miserable grad students.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Successful grad students aren’t like normal humans.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="dr-melissa-santosdata-scientist"&gt;DR. MELISSA SANTOS, DATA SCIENTIST
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;To some extent, the process of getting the Ph.D. helped me have the mindset of putting together methods and being creative in my approach to problems that I’m not sure I would have with just the masters degrees.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The only reason you HAVE to do a Ph.D. is to become a professor. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="dr-paul-harperprofessor-of-operations-research"&gt;DR. PAUL HARPER, PROFESSOR OF OPERATIONS RESEARCH
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;A very common misconception is that applicants can simply pick a supervisor of their choice, but this requires mutual consent.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Perhaps the best way to summarise the life of a Ph.D. student is to look at the awesome Ph.D. comics (&lt;a class="link" href="http://phdcomics.com/" target="_blank" rel="noopener"
&gt;phdcomics.com&lt;/a&gt;) by Jorge Cham, which are spot on&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="dr-laura-mclayprofessor-of-operations-research"&gt;DR. LAURA MCLAY, PROFESSOR OF OPERATIONS RESEARCH
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;It’s important to think about how a Ph.D. fits in with other life decisions. I definitely felt like it would be hard to go back to graduate school if I started another career&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Let me be clear: a Ph.D. is not a Masters degree plus a little more coursework and a small project. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="mr-mike-nuterecovering-actuary-and-phd-student"&gt;MR. MIKE NUTE, RECOVERING ACTUARY AND PH.D. STUDENT
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;More specifically, if you don’t think that getting the Ph.D. is going to be fun on its own, then there’s a strong chance you’ll be miserable and it will end badly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;This reinforces the last point above: the only real reason to do a Ph.D. program is for love of the subject.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="dr-oscar-boykinsoftware-engineer"&gt;DR. OSCAR BOYKIN, SOFTWARE ENGINEER
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;The number one question: does he or she have a burning desire to do a PhD?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;As a professor, you are running a startup that can never be profitable: you are always raising money and hiring.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Links for December 6, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-december-06-2014/</link><pubDate>Sat, 06 Dec 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-december-06-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="http://researcher.watson.ibm.com/researcher/files/us-dpwoodru/journal.pdf" target="_blank" rel="noopener"
&gt;Sketching as a Tool for Numerical Linear Algebra&lt;/a&gt;: A neat paper on sketching algorithms for linear algebra. No, not that kind of sketching. &amp;ldquo;One first compresses it to a much smaller matrix by multiplying it by a (usually) random matrix with certain properties. Much of the expensive computation can then be performed on the smaller matrix, thereby accelerating the solution for the original problem.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://www.coursera.org/course/maps" target="_blank" rel="noopener"
&gt;Maps and the Geospatial Revolution&lt;/a&gt;: Coursera is teaching a class in the spring on how geospatical technology has changed our world.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.gis.usu.edu/~chrisg/python/2009/" target="_blank" rel="noopener"
&gt;Geoprocessing with Python using Open Source GIS&lt;/a&gt;: Speaking of geospatial technology, here are some slides and problems from a class on &amp;ldquo;Geoprocessing with Python&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://blog.pluralsight.com/how-to-use-bash-command-line-history" target="_blank" rel="noopener"
&gt;How to use the bash command line history&lt;/a&gt;: Bash&amp;rsquo;s history can do more than I realized!&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.visiondummy.com/2014/04/geometric-interpretation-covariance-matrix/" target="_blank" rel="noopener"
&gt;A geometric interpretation of the covariance matrix&lt;/a&gt;: Superb little post explaning covariance matrices with pictures and geometry.&lt;/p&gt;</description></item><item><title>Links for December 4, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-december-04-2014/</link><pubDate>Thu, 04 Dec 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-december-04-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="http://mathematica.stackexchange.com/questions/66538/how-do-i-draw-a-pair-of-buttocks" target="_blank" rel="noopener"
&gt;How do I draw a pair of buttocks?&lt;/a&gt;: Have you ever wondered how to plot a pair of buttocks in Mathematica? Of course you have.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://arxiv.org/abs/1411.5018" target="_blank" rel="noopener"
&gt;Frequentism and Bayesianism: A Python-driven Primer&lt;/a&gt;: Jake Vanderplas wrote a &amp;ldquo;brief, semi-technical comparison&amp;rdquo; of frequentist and Bayesian statistical inference using examples in Python.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/EducationalTestingService/skll" target="_blank" rel="noopener"
&gt;skll&lt;/a&gt;: Dan Blanchard released version 1.0 of his very cool command line tool for doing experiments with scikit-learn.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://codeascraft.com/2014/11/17/personalized-recommendations-at-etsy/" target="_blank" rel="noopener"
&gt;Personalized Recommendations at Etsy&lt;/a&gt;: A fantastic post from Etsy&amp;rsquo;s engineering blog on building scalable, personalized recommendations using linear algebra and locally sensitive hashing. I like math.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.pixelmonkey.org/2014/11/02/clojonic" target="_blank" rel="noopener"
&gt;Pythonic Clojure&lt;/a&gt;: Andrew Montalenti wrote a post analyzing Clojure from a Python programmer&amp;rsquo;s perspective. It&amp;rsquo;s great.&lt;/p&gt;</description></item><item><title>Links for November 24, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-november-24-2014/</link><pubDate>Mon, 24 Nov 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-november-24-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="https://github.com/stripe/brushfire" target="_blank" rel="noopener"
&gt;brushfire&lt;/a&gt;: Avi Bryant has been building a &amp;lsquo;Brushfire is a framework for distributed supervised learning of decision tree ensemble models in Scala.&amp;rsquo; Fun stuff!&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://stackoverflow.com/questions/500607/what-are-the-lesser-known-but-useful-data-structures" target="_blank" rel="noopener"
&gt;What are the lesser known but useful data structures?&lt;/a&gt;: I always enjoy StackOverflow questions like this, &lt;strong&gt;but it is not considered a good, on-topic question for this site&lt;/strong&gt;, of course.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/vhf/free-programming-books/blob/master/free-programming-books.md" target="_blank" rel="noopener"
&gt;Free Programming Books&lt;/a&gt;: A huge, crowd-sourced list of free programming books by language and topic.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://www.ml.cmu.edu/research/phd-dissertations.html" target="_blank" rel="noopener"
&gt;PhD Dissertations-Machine Learning Department&lt;/a&gt;: Seven years of ML PhD dissertations from Carnegie Mellon University. I wish I had time to read &lt;a class="link" href="http://reports-archive.adm.cs.cmu.edu/anon/cald/CMU-CALD-05-107.pdf" target="_blank" rel="noopener"
&gt;Tools for Large Graph Mining&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Links for November 17, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-november-17-2014/</link><pubDate>Mon, 17 Nov 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-november-17-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="https://www.hackerschool.com/blog/14-there-is-no-magic-virtualenv-edition" target="_blank" rel="noopener"
&gt;There&amp;rsquo;s no magic: virtualenv edition&lt;/a&gt;: I didn&amp;rsquo;t really get virtualenvs until long after I started programming Python, though they&amp;rsquo;re now an essential part of my toolkit. This is a great post explaining how they work.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://python-notes.curiousefficiency.org/en/latest/python_concepts/import_traps.html" target="_blank" rel="noopener"
&gt;Traps for the Unwary in Python’s Import System&lt;/a&gt;: &amp;ldquo;Python’s import system is powerful, but also quite complicated.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/Psycojoker/pyfmt" target="_blank" rel="noopener"
&gt;pyfmt&lt;/a&gt;: I recently learned about &lt;a class="link" href="https://golang.org/cmd/gofmt/" target="_blank" rel="noopener"
&gt;gofmt&lt;/a&gt; for auto-formatting Go code. Here&amp;rsquo;s a similar tool for Python.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://groups.google.com/forum/#!msg/comp.lang.java/aSPAJO05LIU/ushhUIQQ-ogJ" target="_blank" rel="noopener"
&gt;Q: Setting User-Agent Field?&lt;/a&gt;: A 1996 question in comp.lang.java on how to set the user agent field for a Java crawler. The signature on the question? &lt;em&gt;Thanks, Larry Page&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/alecthomas/importmagic" target="_blank" rel="noopener"
&gt;alecthomas/importmagic&lt;/a&gt;: Python tool and Sublime extension for automatically adding imports.&lt;/p&gt;</description></item><item><title>Links for November 12, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-november-12-2014/</link><pubDate>Wed, 12 Nov 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-november-12-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="http://amazonpickingchallenge.org/" target="_blank" rel="noopener"
&gt;Amazon Picking Challenge&lt;/a&gt;: Kiva Systems (where I interned in 2011) is setting up a robotics challenging for picking items off warehouse shelves.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://pypi.python.org/pypi/contexttimer/0.3.1" target="_blank" rel="noopener"
&gt;contexttimer 0.3.1&lt;/a&gt;: A handy Python context manger and decorator for timing things.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://blog.cloudera.com/blog/2014/09/how-to-translate-from-mapreduce-to-apache-spark/" target="_blank" rel="noopener"
&gt;How-to: Translate from MapReduce to Apache Spark&lt;/a&gt;: This is a helpful bit from Cloudera on moving algorithms from Mapreduce to Spark.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://pypi.python.org/pypi/combinatorics/1.4.3" target="_blank" rel="noopener"
&gt;combinatorics 1.4.3&lt;/a&gt;: Here&amp;rsquo;s a Python module adding some combinatorial functions to the language.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://mail.python.org/pipermail/python-3000/2006-November/004643.html" target="_blank" rel="noopener"
&gt;Special methods and interface-based type system&lt;/a&gt;: Guido van Rossum explains (in 2006) why Python uses &lt;code&gt;len(x)&lt;/code&gt; instead of &lt;code&gt;x.len()&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Links for November 3, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-november-03-2014/</link><pubDate>Mon, 03 Nov 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-november-03-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="https://aws.amazon.com/datasets" target="_blank" rel="noopener"
&gt;Public Data Sets : Amazon Web Services&lt;/a&gt;: Amazon hosts a number of publicly datasets on AWS (including the common crawl corpus and the &amp;ldquo;Marvel Universe Social Graph&amp;rdquo;).&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://www.youtube.com/watch?v=muMRNYPmQxE" target="_blank" rel="noopener"
&gt;Rapid Web Prototyping with Lightweight Tools&lt;/a&gt;: I&amp;rsquo;ve shared this before, but my boss Andrew did a fantastic tutorial last year on Flask, Jinja2, MongoDB, and Twitter Bootstrap. Combined with Heroku, it&amp;rsquo;s surprisingly easy to get a website running these days.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/wichert/rest_toolkit" target="_blank" rel="noopener"
&gt;rest_toolkit&lt;/a&gt;: REST has been my obsession of late. Here&amp;rsquo;s a little Python package for quickly writing RESTful APIs.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.davar.net/PROGRAM/EXTRACTS/CRAFTJOY.HTM" target="_blank" rel="noopener"
&gt;The Joys of the Craft&lt;/a&gt;: A quote from Fred Brooks&amp;rsquo; &lt;em&gt;The Mythical Man-Month&lt;/em&gt; on why programming is fun.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://unix.stackexchange.com/questions/77077/how-do-i-use-pushd-and-popd-commands" target="_blank" rel="noopener"
&gt;How do I use pushd and popd commands?&lt;/a&gt;: I recently learned bash has push and popd commands for temporarily changing directories. This is very handy for scripting.&lt;/p&gt;</description></item><item><title>Links for November 1, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-november-01-2014/</link><pubDate>Sat, 01 Nov 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-november-01-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="http://www.joelonsoftware.com/articles/Unicode.html" target="_blank" rel="noopener"
&gt;The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)&lt;/a&gt;: I guess the title says it all. By Joel Spolsky.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://hyperpolyglot.org/unix-shells" target="_blank" rel="noopener"
&gt;Unix Shells - Hyperpolyglot&lt;/a&gt;: Very cool comparison of basic command syntax in Bash, Fish, Ksh, Tcsh, and Zsh.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://blog.sanctum.geek.nz/better-bash-history/" target="_blank" rel="noopener"
&gt;Better Bash history&lt;/a&gt;: I&amp;rsquo;m pretty stuck on Bash at the moment. Here&amp;rsquo;s a way to get a better history in Bash. (Other shells often improve on Bash&amp;rsquo;s history.)&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://usaddress.readthedocs.org/en/latest/" target="_blank" rel="noopener"
&gt;usaddress 0.1&lt;/a&gt;: I always love seeing a Python library for something I&amp;rsquo;ve tried to do poorly on my own: &amp;ldquo;usaddress is a python library for parsing unstructured address strings into address components, using advanced NLP methods.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://pythonhosted.org/more-itertools/api.html" target="_blank" rel="noopener"
&gt;more-itertools&lt;/a&gt;: A great extension to the helpful &lt;a class="link" href="https://docs.python.org/2/library/itertools.html" target="_blank" rel="noopener"
&gt;itertools&lt;/a&gt; module in Python. Some particularly helpful functions: &lt;code&gt;chunked&lt;/code&gt;, &lt;code&gt;first&lt;/code&gt;, &lt;code&gt;peekaboo&lt;/code&gt;, and &lt;code&gt;take&lt;/code&gt;. Unfortunately, it doesn&amp;rsquo;t have Python 3 support at the moment.&lt;/p&gt;</description></item><item><title>Pyspark's AggregateByKey Method</title><link>https://tdhopper.com/blog/pysparks-aggregatebykey-method/</link><pubDate>Fri, 03 Oct 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/pysparks-aggregatebykey-method/</guid><description>&lt;p&gt;The &lt;a class="link" href="https://spark.apache.org/docs/2.2.0/rdd-programming-guide.html" target="_blank" rel="noopener"
&gt;pyspark documentation&lt;/a&gt; doesn&amp;rsquo;t include an example for the aggregateByKey RDD method. I didn&amp;rsquo;t find any nice examples online, so I wrote my own.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what the documentation does say:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;aggregateByKey(self, zeroValue, seqFunc, combFunc, numPartitions=None)&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Aggregate the values of each key, using given combine functions and a neutral &amp;ldquo;zero value&amp;rdquo;. This function can return a different result type, U, than the type of the values in this RDD, V. Thus, we need one operation for merging a V into a U and one operation for merging two U&amp;rsquo;s, The former operation is used for merging values within a partition, and the latter is used for merging values between partitions. To avoid memory allocation, both of these functions are allowed to modify and return their first argument instead of creating a new U.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;reduceByKey&lt;/code&gt; and &lt;code&gt;aggregateByKey&lt;/code&gt; are much more efficient than &lt;code&gt;groupByKey&lt;/code&gt; and should be used for aggregations as much as possible.&lt;/p&gt;
&lt;p&gt;In the example below, I create an RDD that is a short list of characters. My functions will aggregate the functions together with concatenation. I added brackets to the two types of concatenation to help give you an idea of what &lt;code&gt;aggregateByKey&lt;/code&gt; is doing.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Welcome to
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ____ __
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; / __/__ ___ _____/ /__
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; _\ \/ _ \/ _ `/ __/ &amp;#39;_/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /__ / .__/\_,_/_/ /_/\_\ version 1.1.0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /_/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Using Python version 2.7.5 (default, Mar 9 2014 22:15:05)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SparkContext available as sc.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;In [1]: # Create rdd that is a list of characters
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;In [2]: sc.parallelize(list(&amp;#34;aaaaabbbbcccdd&amp;#34;)) \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ...: .map(lambda letter: (letter, {&amp;#34;value&amp;#34;: letter})) \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ...: .aggregateByKey(
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ...: # Value to start aggregation (passed as s to `lambda s, d`)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ...: &amp;#34;start&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ...: # Function to join final data type (string) and rdd data type
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ...: lambda s, d: &amp;#34;[ %s %s ]&amp;#34; % (s, d[&amp;#34;value&amp;#34;]),
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ...: # Function to join two final data types.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ...: lambda s1, s2: &amp;#34;{ %s %s }&amp;#34; % (s1, s2),
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ...: ) \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ...: .collect()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Out[2]:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[(&amp;#39;a&amp;#39;, &amp;#39;{ { [ start a ] [ [ start a ] a ] } [ [ start a ] a ] }&amp;#39;),
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (&amp;#39;c&amp;#39;, &amp;#39;{ [ start c ] [ [ start c ] c ] }&amp;#39;),
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (&amp;#39;b&amp;#39;, &amp;#39;{ { [ [ start b ] b ] [ start b ] } [ start b ] }&amp;#39;),
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (&amp;#39;d&amp;#39;, &amp;#39;[ [ start d ] d ]&amp;#39;)]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Links for September 30, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-september-30-2014/</link><pubDate>Tue, 30 Sep 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-september-30-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="https://github.com/kadirpekel/hammock/blob/master/README.rst" target="_blank" rel="noopener"
&gt;Hammock&lt;/a&gt;: A lightweight wrapper around the Python requests module to convert REST APIs into &amp;ldquo;dead simple programmatic APIs&amp;rdquo;. It&amp;rsquo;s a clever idea. I&amp;rsquo;ll have to play around with it before I can come up with a firm opinion.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/mitsuhiko/pipsi#readme" target="_blank" rel="noopener"
&gt;pipsi&lt;/a&gt;: Pipsi wraps pip and virtualenv to allow you to install Python command line utilities without polluting your global environment.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://nvie.com/posts/writing-a-cli-in-python-in-under-60-seconds/" target="_blank" rel="noopener"
&gt;Writing a Command-Line Tool in Python&lt;/a&gt;: Speaking of Python command line utilities, here&amp;rsquo;s a little post from Vincent Driessen on writing them.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://nvie.com/posts/iterators-vs-generators/" target="_blank" rel="noopener"
&gt;Iterables vs. Iterators vs. Generators&lt;/a&gt;: Vincent has been on a roll lately. He also wrote this &amp;ldquo;little pocket reference on iterables, iterators and generators&amp;rdquo; in Python.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://mcfunley.com/design-for-continuous-experimentation" target="_blank" rel="noopener"
&gt;Design for Continuous Experimentation: Talk and Slides&lt;/a&gt;: I didn&amp;rsquo;t watch the lecture, but Dan McKinley&amp;rsquo;s slides on web experimentation are excellent.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://blog.cloudera.com/blog/2014/03/apache-spark-a-delight-for-developers/" target="_blank" rel="noopener"
&gt;Apache Spark: A Delight for Developers&lt;/a&gt;: I&amp;rsquo;ve been playing with PySpark lately, and it really is fun.&lt;/p&gt;</description></item><item><title>Links for September 25, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-september-25-2014/</link><pubDate>Thu, 25 Sep 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-september-25-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="http://plato.stanford.edu/entries/statistics/" target="_blank" rel="noopener"
&gt;Philosophy of Statistics (Stanford Encyclopedia of Philosophy)&lt;/a&gt;: I suspect that a lot of the Bayesian vs Frequentist debates ignore the important epistemological underpinnings of statistics. I haven’t finished reading this yet, but I wonder if it might help.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://ifttt.com/sunlightfoundation" target="_blank" rel="noopener"
&gt;Connect Sunlight Foundation to anything&lt;/a&gt;: “The Sunlight Foundation is a nonpartisan non-profit organization that uses the power of the Internet to catalyze greater U.S. Government openness and transparency.” They now of an &lt;a class="link" href="https://ifttt.com" target="_blank" rel="noopener"
&gt;IFTTT&lt;/a&gt; channel. Get push notifications when the president signs a bill!&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://furbo.org/2014/09/03/the-terminal/" target="_blank" rel="noopener"
&gt;furbo.org · The Terminal&lt;/a&gt;: &lt;a class="link" href="http://www.twitter.com/chockenberry" target="_blank" rel="noopener"
&gt;Craig Hockenberry&lt;/a&gt; wrote a massive post on how he uses the Terminal on OS X for fun and profit. You will learn things.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://snapsnapsnap.photos/a-sneak-peek-at-camera-6-manual-controls-are-coming-soon-to-you/" target="_blank" rel="noopener"
&gt;A sneak peek at Camera+ 6… manual controls are coming soon to you! &lt;/a&gt;: I’ve been a Camera+ user on iOS for a long time. The new version coming out soon is very exciting.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://githut.info/" target="_blank" rel="noopener"
&gt;GitHut - Programming Languages and GitHub&lt;/a&gt;: A very clever visualization of various languages represented on Github and of the properties of their respective repositories.&lt;/p&gt;</description></item><item><title>Links for September 20, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-september-20-2014/</link><pubDate>Sat, 20 Sep 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-september-20-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="http://www.jeffknupp.com/blog/2013/08/16/open-sourcing-a-python-project-the-right-way/" target="_blank" rel="noopener"
&gt;Open Sourcing a Python Project the Right Way&lt;/a&gt;: Great stuff that should be taught in school: “Most Python developers have written at least one tool, script, library or framework that others would find useful. My goal in this article is to make the process of open-sourcing existing Python code as clear and painless as possible.”&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/elasticsearch/elasticsearch-dsl-py/blob/master/README.rst" target="_blank" rel="noopener"
&gt;elasticsearch/elasticsearch-dsl-py&lt;/a&gt;: Elasticsearch is an incredible datastore. Unfortunately, its JSON-based query language is tedious, at best. Here’s a nice higher-level Python DSL being developed for it. It’s great!&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.podcastinghandbook.co/podcasting-equipment-guide/" target="_blank" rel="noopener"
&gt;Equipment Guide — The Podcasting Handbook&lt;/a&gt;: Dan Benjamin of 5by5 podcasting fame is writing a book on podcasting. Here’s his brief equipment guide.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/bachya/pinpress" target="_blank" rel="noopener"
&gt;bachya/pinpress&lt;/a&gt;: Aaron Bach
put together a neat Ruby script that he uses to generate his link posts. This is similar to but better than my &lt;a class="link" href="https://github.com/tdhopper/sundry" target="_blank" rel="noopener"
&gt;sundry&lt;/a&gt; tool.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://there4development.com/markdown-resume/" target="_blank" rel="noopener"
&gt;Markdown Resume Builder&lt;/a&gt;: I haven’t tried this yet, but I like the idea: a Markdown based resume format that can be converted into HTML or PDF.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://git-scm.com/book/en/Git-Basics-Tips-and-Tricks#Auto-Completion" target="_blank" rel="noopener"
&gt;Git - Tips and Tricks&lt;/a&gt;: Enabling autocomplete in Git is something I should have done long ago.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://hortonworks.com/blog/apache-storm-design-pattern-micro-batching/" target="_blank" rel="noopener"
&gt;Apache Storm Design Pattern—Micro Batching&lt;/a&gt;: Micro batching is a valuable tool when doing stream processing. Horton Works put up a helpful post outlining three ways of doing it.&lt;/p&gt;</description></item><item><title>Quickly Converting Python Dict to JSON</title><link>https://tdhopper.com/blog/quickly-converting-python-dict-to-json/</link><pubDate>Thu, 18 Sep 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/quickly-converting-python-dict-to-json/</guid><description>&lt;p&gt;Recently, I&amp;rsquo;ve spent a lot of time going back and forth between Python dicts and JSON. For some reason, I decided last week that I&amp;rsquo;d be useful to be able to quickly convert a Python dict to pretty printed JSON.&lt;/p&gt;
&lt;p&gt;I created a &lt;a class="link" href="http://smilesoftware.com/TextExpander/index.html" target="_blank" rel="noopener"
&gt;TextExpander&lt;/a&gt; snippet that takes a Python dict from the clipboard, converts it to JSON, and pastes it.&lt;/p&gt;
&lt;p&gt;Here are the details:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2014/09/textexpander.png"&gt;
&lt;/figure&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="ch"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getClipboardData&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pbpaste&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;retcode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;cb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getClipboardData&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sort_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;separators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;: &amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>Links for September 10, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-september-10-2014/</link><pubDate>Wed, 10 Sep 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-september-10-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="http://textract.readthedocs.org/en/latest/" target="_blank" rel="noopener"
&gt;textract&lt;/a&gt;: textract is a Python module and a command line tool for text extraction from many file formats. It cleverly pulls together many libraries into a consistent API.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/semirook/flask-kit" target="_blank" rel="noopener"
&gt;Flask Kit&lt;/a&gt;: I&amp;rsquo;ve been reading a lot about Flask (the Python web server) lately. Flask Kit is a little tool to give some structure to new Flask projects.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/audreyr/cookiecutter" target="_blank" rel="noopener"
&gt;cookiecutter&lt;/a&gt;: I was looking for this recently, but it I couldn&amp;rsquo;t find it. &amp;ldquo;A command-line utility that creates projects from cookiecutters (project templates). E.g. Python package projects, jQuery plugin projects.&amp;rdquo; There&amp;rsquo;s even a &lt;a class="link" href="https://github.com/sloria/cookiecutter-flask" target="_blank" rel="noopener"
&gt;Flask template&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.sciencedaily.com/releases/2010/08/100830094930.htm" target="_blank" rel="noopener"
&gt;Over 50? You Probably Prefer Negative Stories About Young People&lt;/a&gt;: A research paper from a few years ago show that older people prefer to read negative news about young people. &amp;ldquo;In fact, older readers who chose to read negative stories about young individuals actually get a small boost in their self-esteem.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.npr.org/blogs/money/2014/08/29/344034815/episode-564-the-signature" target="_blank" rel="noopener"
&gt;Episode 564: The Signature&lt;/a&gt;: The fantastic Planet Money podcast explains why signatures are meaningless in a modern age. My scribbles have become even worse since listening to this.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/thieman/github-selfies" target="_blank" rel="noopener"
&gt;github-selfies&lt;/a&gt;: Here&amp;rsquo;s a Chrome and Firefox extension that allows you to quickly embed gif selfies in Github posts. Caution: &lt;a class="link" href="http://www.threechords.org/blog/how-animated-gif-selfies-fixed-our-teams-morale/" target="_blank" rel="noopener"
&gt;may lead to improved team morale&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Shortcut to Switch to Previous Git Branch</title><link>https://tdhopper.com/blog/shortcut-to-switch-to-previous-git-branch/</link><pubDate>Wed, 10 Sep 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/shortcut-to-switch-to-previous-git-branch/</guid><description>&lt;img src="https://tdhopper.com/images/til.png" alt="Featured image of post Shortcut to Switch to Previous Git Branch" /&gt;&lt;blockquote class="twitter-tweet" data-lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;TIL how to checkout the last branch you were on in Git (before the current):&lt;br&gt;&lt;br&gt;git checkout -&lt;/p&gt;&amp;mdash; Tim Hopper (@tdhopper) &lt;a href="https://twitter.com/tdhopper/status/509767391103172608"&gt;September 10, 2014&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;</description></item><item><title>Links for August 30, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-august-30-2014/</link><pubDate>Sat, 30 Aug 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-august-30-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="http://jimhester.github.io/ggplot2ToGgvis/" target="_blank" rel="noopener"
&gt;Ggplot2 To Ggvis&lt;/a&gt;: I&amp;rsquo;m a huge fan of ggplot2 for data visualization in R. Here&amp;rsquo;s a brief tutorial for ggplot2 users to learn ggvis for generating &lt;em&gt;interactive&lt;/em&gt; plots in R using the grammar of graphics.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://danielfrg.com/blog/2014/08/01/storm-sklearn/" target="_blank" rel="noopener"
&gt;From zero to storm cluster for scikit-learn classification | Daniel Rodriguez&lt;/a&gt;: This is a very cool, if brief, blog post on using &lt;a class="link" href="https://github.com/Parsely/streamparse" title="Parsely/streamparse Âˇ GitHub"
target="_blank" rel="noopener"
&gt;streamparse&lt;/a&gt;, my company&amp;rsquo;s open source wrapper for &lt;a class="link" href="https://storm.incubator.apache.org/" title="Storm, distributed and fault-tolerant realtime computation"
target="_blank" rel="noopener"
&gt;Apache Storm&lt;/a&gt;, and &lt;a class="link" href="http://scikit-learn.org/stable/" title="scikit-learn: machine learning in Python &amp;amp;mdash; scikit-learn 0.15.1 documentation"
target="_blank" rel="noopener"
&gt;scikit-learn&lt;/a&gt;, &lt;a class="link" href="https://www.youtube.com/watch?v=2kx19t8bNMU" title="An Introduction to Scikit-Learn - YouTube"
target="_blank" rel="noopener"
&gt;my favorite machine learning library&lt;/a&gt;, to do machine learning on data streams.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.pixelmonkey.org/2010/11/03/pythonic-means-idiomatic-and-tasteful" target="_blank" rel="noopener"
&gt;Pythonic means idiomatic and tasteful&lt;/a&gt;: My boss Andrew recently shared an old blogpost he wrote on what it means for code to be Pythonic; I think he&amp;rsquo;s right on track.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pythonic isn’t just idiomatic Python — it’s tasteful Python. It’s less an objective property of code, more a compliment bestowed onto especially nice Python code.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/deanishe/alfred-repos" target="_blank" rel="noopener"
&gt;git workflow&lt;/a&gt;: In my ever continuing attempt to be able to run my entire life from &lt;a class="link" href="http://www.alfredapp.com/" title="Alfred App - Productivity App for Mac OS X"
target="_blank" rel="noopener"
&gt;Alfred&lt;/a&gt;, I recently installed this workflow that makes git repositories on my computer easily searchable.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.deanishe.net/alfred-workflow/index.html" target="_blank" rel="noopener"
&gt;Alfred-Workflow&lt;/a&gt;: Speaking of Alfred, here&amp;rsquo;s a handy Python library that makes it easy to write your own (if you&amp;rsquo;re a Python programmer).&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html" target="_blank" rel="noopener"
&gt;Squashing commits with rebase&lt;/a&gt;: Turns out you can use git rebase to clean up your commits before you push them to a remote repository. This can be a great way to make the commits your team sees more meaningful; don&amp;rsquo;t abuse it.&lt;/p&gt;</description></item><item><title>Tracking Weight Loss with R, Hazel, Withings, and IFTTT</title><link>https://tdhopper.com/blog/tracking-weight-loss-with-r-hazel-withings-and-ifttt/</link><pubDate>Sat, 30 Aug 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/tracking-weight-loss-with-r-hazel-withings-and-ifttt/</guid><description>&lt;p&gt;As I have &lt;a class="link" href="https://tdhopper.com/blog/2013/Jul/26/noisy-series-and-body-weight/" &gt;noted&lt;/a&gt; &lt;a class="link" href="https://tdhopper.com/blog/2013/Nov/28/noisy-series-and-body-weight-take-2/" &gt;before&lt;/a&gt;, body weight is a noisy thing. Day to day, your weight will probably fluctuate by several pounds. If you&amp;rsquo;re trying to lose weight, this noise can cause unfounded frustration and premature excitement.&lt;/p&gt;
&lt;p&gt;When I started a serious weight loss plan a year and a half ago, I bought a wifi-enabled &lt;a class="link" href="http://www.withings.com" target="_blank" rel="noopener"
&gt;Withings Scale&lt;/a&gt;. The scale allows me to automatically sync my weight with &lt;a class="link" href="http://www.bustan.net/" target="_blank" rel="noopener"
&gt;Montior Your Weight&lt;/a&gt;, &lt;a class="link" href="http://www.myfitnesspal.com" target="_blank" rel="noopener"
&gt;MyFitnessPal&lt;/a&gt;, &lt;a class="link" href="http://runkeeper.com/home" title="RunKeeper"
target="_blank" rel="noopener"
&gt;RunKeeper&lt;/a&gt;, and other fitness apps on my phone. IFTTT also has &lt;a class="link" href="https://ifttt.com/withings" title="Withings Channel - IFTTT"
target="_blank" rel="noopener"
&gt;great Withings support&lt;/a&gt; allowing me to push my weight to various other web services.&lt;/p&gt;
&lt;p&gt;One IFTTT rule I have appends my weight to a text file in Dropbox. This file looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;263.86 August 21, 2014 at 05:56AM
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;264.62 August 22, 2014 at 08:27AM
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;264.56 August 23, 2014 at 09:41AM
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;263.99 August 24, 2014 at 08:02AM
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;265.64 August 25, 2014 at 08:08AM
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;267.4 August 26, 2014 at 08:16AM
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;265.25 August 27, 2014 at 09:08AM
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;264.17 August 28, 2014 at 07:21AM
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;264.03 August 29, 2014 at 08:43AM
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;262.71 August 30, 2014 at 08:47AM
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;For a few months, I have been experimenting with using this time series to give myself a less-noisy update on my weight, and I&amp;rsquo;ve come up with a decent solution.&lt;/p&gt;
&lt;p&gt;This &lt;a class="link" href="http://www.r-project.org/" target="_blank" rel="noopener"
&gt;R script&lt;/a&gt; will take my weight time series, resample it, smooth it with a rolling median over the last month, and write summary stats to a text file in my Dropbox. It&amp;rsquo;s not the prettiest script, but it gets the job done for now.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;span class="lnt"&gt;58
&lt;/span&gt;&lt;span class="lnt"&gt;59
&lt;/span&gt;&lt;span class="lnt"&gt;60
&lt;/span&gt;&lt;span class="lnt"&gt;61
&lt;/span&gt;&lt;span class="lnt"&gt;62
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-R" data-lang="R"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;INPUT_PATH&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;~/Dropbox/Text Notes/Weight.txt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;OUTPUT_PATH&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;~/Dropbox/Text Notes/Weight Stats.txt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;library&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lubridate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;library&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ggplot2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;library&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zoo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# READ FILE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;con&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INPUT_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;rt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;readLines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# PARSE INTO LISTS OF WEIGHTS AND DATES&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;parse.line&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;strsplit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;[[1]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;date.str&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;paste&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s[2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="n"&gt;][&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;is.na&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s[2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="n"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collapse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;mdy_hm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date.str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quiet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;as.numeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s[1]&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;names&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;weight&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;date&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;l&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;list.weight.date&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;lapply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parse.line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;lapply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list.weight.date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dates&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;lapply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list.weight.date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# BUILD DATA FRAME&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;data.frame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;unlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;do.call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;c&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# CREATE TIME SERIES AND RESAMPLE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;zoo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;24&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;days&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;na.approx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# FUNCTION TO GET WEIGHT N-DAYS AGO IF WEIGHT IS SMOOTHED BY ROLLING MEDIAN&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# OVER A GIVEN (smooth.n) NUMBER OF DAYS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;days.ago&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="kr"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;smooth.n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;head&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;smoothed&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;rollmedianr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;smooth.n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;as.numeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;smoothed[date]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# SMOOTH WEIGHT BY 29 DAYS AND GENERATE SOME SUMMARY STATS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;days&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;29&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;current.weight&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;days.ago&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current.weight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;current.weight&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;days.ago&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;current.weight&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;days.ago&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;current.weight&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;days.ago&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;365&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;current.weight&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;names&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;current&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;7days&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;30days&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;365days&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;max&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;fileConn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="nf"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;paste&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Weight (lbs):&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x[&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;current&amp;#34;&lt;/span&gt;&lt;span class="n"&gt;]&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;paste&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Total Δ:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x[&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;max&amp;#34;&lt;/span&gt;&lt;span class="n"&gt;]&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;paste&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;1 Week Δ:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x[&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;7days&amp;#34;&lt;/span&gt;&lt;span class="n"&gt;]&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;paste&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;1 Month Δ:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x[&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;30days&amp;#34;&lt;/span&gt;&lt;span class="n"&gt;]&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;paste&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;1 Year Δ:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x[&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;365days&amp;#34;&lt;/span&gt;&lt;span class="n"&gt;]&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;writeLines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;fileConn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileConn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The output looks something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Weight (lbs): 265.7
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Total Δ: -112
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1 Week Δ: -0.8
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1 Month Δ: -4.8
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1 Year Δ: -75
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;I want this script to be run every time my weight is updated, so I created a second IFTTT rule that will create a new file in my Dropbox, called &lt;em&gt;new_weight_measurement&lt;/em&gt;, every time I weigh in. On my Mac Mini, I have a &lt;a class="link" href="http://www.noodlesoft.com/hazel.php" title="Noodlesoft | Hazel"
target="_blank" rel="noopener"
&gt;Hazel&lt;/a&gt; rule to watch for a file of this name to be created. When Hazel sees the file, it runs my R script and deletes that file.&lt;/p&gt;
&lt;p&gt;My Hazel rule looks like this:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2014/08/hazel-weight-1.png"&gt;
&lt;/figure&gt;
&lt;p&gt;The &amp;rsquo;embedded script&amp;rsquo; that is run is the R script above; I just have to tell Hazel to use the &lt;code&gt;Rscript&lt;/code&gt; shell.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2014/08/r-script-from-hazel.png"&gt;
&lt;/figure&gt;
&lt;p&gt;At this point, every time I step on my scale, a text file with readable statistics about my smoothed weight appear in my Dropbox folder.&lt;/p&gt;
&lt;p&gt;Of course, I want this updated information to be pushed directly too me. Hazel is again the perfect tool for the job. I have a second Hazel rule that watches for &lt;em&gt;Weight Stats.txt&lt;/em&gt; to be created. Hazel can pass the path of the updated file into any script of your choice. You could, for example, use &lt;a class="link" href="http://www.mailgun.com/" title="Transactional Email API Service for Developers - Mailgun"
target="_blank" rel="noopener"
&gt;Mailgun&lt;/a&gt; to email it to yourself or &lt;a class="link" href="https://pushover.net/" title="Pushover: Simple Notifications for Android, iOS, and Desktop"
target="_blank" rel="noopener"
&gt;Pushover&lt;/a&gt; to push it to your mobile devices. Obviously, I want to tweet mine.&lt;/p&gt;
&lt;p&gt;I have a Twitter account called &lt;a class="link" href="https://twitter.com/hopsfitness" target="_blank" rel="noopener"
&gt;@hopsfitness&lt;/a&gt; where I&amp;rsquo;ve recently been tracking my fitness progress. On my Mac Mini, I have &lt;a class="link" href="https://github.com/sferik/t" title="t GitHub"
target="_blank" rel="noopener"
&gt;t&lt;/a&gt; configured to access @hopsfitness from the command line. Thus, tweeting my updated statistics is just a matter of a little shell script executed by Hazel:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2014/08/hazel-weight-2.png"&gt;
&lt;/figure&gt;
&lt;p&gt;Since this data goes to Twitter, I can get it painlessly pushed to my phone: Twitter still allows you subscribe to accounts via text message, which I&amp;rsquo;ve done with @hopsfitness. A minute or so after I step on my scale, I get a text with useful information about where I am and where I&amp;rsquo;m going; this is much preferable to the noisy weight I see on my scale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt; (2014-12-06): I replaced my R script with a Python/pandas script. It requires Python 3 (to render the delta characters).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dateutil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;pd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;os.path&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;expanduser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;join&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expanduser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;~&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Dropbox/Text Notes/Weight.txt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;r&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readlines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;weight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dateutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;weight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;parse_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;date&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;weight&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;date&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1D&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;how&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;mean&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;missing&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isnull&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interpolate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;linear&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;std&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dropna&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;noise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;missing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;normalvariate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;missing&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;noise&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;smoothed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ewma&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smoothed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;Weight (lbs): &lt;/span&gt;&lt;span class="si"&gt;%(weight).1f&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;Total Δ: &lt;/span&gt;&lt;span class="si"&gt;%(total).1f&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;1 Week Δ: &lt;/span&gt;&lt;span class="si"&gt;%(week).1f&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;1 Month Δ: &lt;/span&gt;&lt;span class="si"&gt;%(month).1f&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;1 Year Δ: &lt;/span&gt;&lt;span class="si"&gt;%(year).1f&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;weight&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;total&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;smoothed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;week&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;smoothed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;month&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;smoothed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;year&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;smoothed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;366&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Dropbox/Text Notes/Weight Stats.txt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;wb&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;UTF-8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;This assumes your input file is formatted like mine, but you could easily adjust the first part of the code for other formats.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;You can &lt;a class="link" href="http://www.r-project.org/" title="The R Project for Statistical Computing"
target="_blank" rel="noopener"
&gt;download R here&lt;/a&gt;; installing it should add &lt;code&gt;Rscript&lt;/code&gt; to your system path.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Keeping IPython Notebooks Running in the Background</title><link>https://tdhopper.com/blog/keeping-ipython-notebooks-running-in-the-background/</link><pubDate>Thu, 28 Aug 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/keeping-ipython-notebooks-running-in-the-background/</guid><description>&lt;p&gt;I spend a lot of time in &lt;a class="link" href="http://ipython.org/notebook.html" title="The IPython Notebook &amp;amp;mdash; IPython"
target="_blank" rel="noopener"
&gt;IPython Notebooks&lt;/a&gt; for work. One of the few annoyances of IPython Notebooks is that they require keeping a terminal window open to run the notebook server and kernel. I routinely launch a Notebook kernel in a directory where I keep my work related notebooks. Earlier this week, I started to wonder if there was a way for me to keep this kernel running all the time &lt;em&gt;without&lt;/em&gt; having to keep a terminal window open..&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve ever tried to do chron-like automation on OS X, you&amp;rsquo;ve surely come across &lt;a class="link" href="http://launchd.info/" target="_blank" rel="noopener"
&gt;launchd&lt;/a&gt;, &amp;ldquo;a unified, open-source service management framework for starting, stopping and managing daemons, applications, processes, and script&amp;rdquo;. You&amp;rsquo;ve probably also gotten frustated with launchd and given up.&lt;/p&gt;
&lt;p&gt;I recently started using &lt;a class="link" href="http://www.soma-zone.com/LaunchControl/" title="soma-zone: LaunchControl"
target="_blank" rel="noopener"
&gt;LaunchControl&lt;/a&gt; &amp;ldquo;a fully-featured launchd GUI&amp;rdquo; for launchd; it&amp;rsquo;s pretty nice and worth $10. It occurred to me that LaunchControl would be a good way to keep my Notebook kernel running in the background.&lt;/p&gt;
&lt;p&gt;I created a LaunchControl to run the following command.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/usr/local/bin/IPython notebook --matplotlib inline --port=9777 --browser=false
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This launches an IPython Notebook kernel accessible on port 9777; setting the browser flag to something other than an installed browser prevents a browser window from opening when the kernel is launch.&lt;/p&gt;
&lt;p&gt;I added three other launchd keys in LaunchControl:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;Working Directory&lt;/em&gt; key to tell LaunchControl to start my notebook in my desired folder.&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;Run At Load&lt;/em&gt; key to tell it to start my kernel as soon as I load the job.&lt;/li&gt;
&lt;li&gt;And a &lt;em&gt;Keep alive&lt;/em&gt; key to tell LaunchControl to restart my kernel should the process ever die.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s how it looks in LaunchControl:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2014/08/launchcontrol.png"&gt;
&lt;/figure&gt;
&lt;p&gt;After I created it, I just had to save and load, and I was off to the races; the IPython kernel starts and runs in the background. I can access my Notebooks by navigating to 127.0.0.1:9777 in my browser. Actually, I added &lt;code&gt;127.0.0.1 parsely.scratch&lt;/code&gt; to my hosts file so I can access my Notebooks at parsely.scratch:9777. This works nicely with Chrome&amp;rsquo;s autocomplete feature. I&amp;rsquo;m avoiding the temptation to run nginx and give it an even prettier url.&lt;/p&gt;</description></item><item><title>Links for August 28, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-august-28-2014/</link><pubDate>Thu, 28 Aug 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-august-28-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="http://stackoverflow.com/questions/2161406/how-do-i-generate-a-uniform-random-integer-partition" target="_blank" rel="noopener"
&gt;How do I generate a uniform random integer partition?&lt;/a&gt;: This week, I wanted to generate random partitions of integers. Unsurprisingly, stackoverflow pulled through with a Python snippet to do just that.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://support.alfredapp.com/kb:firefox-chrome-bookmarks" target="_blank" rel="noopener"
&gt;Firefox and Chrome Bookmarks&lt;/a&gt;: I love &lt;a class="link" href="http://www.alfredapp.org" target="_blank" rel="noopener"
&gt;Alfred&lt;/a&gt; as a launcher in OS X. I use it many, many times a day. I just found this helpful workflow for quickly searching and opening my Chrome bookmarks.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.youneedabudget.com/blog/2014/ynab-for-ipad-is-here/" target="_blank" rel="noopener"
&gt;YNAB for iPad is Here&lt;/a&gt;: YNAB has been the best thing to ever happen to my financial life. I use it to track all my finances. They just released a beautiful iPad app. Importantly, it brings the ability to modify a budget to mobile!&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://the-paper-trail.org/blog/distributed-systems-theory-for-the-distributed-systems-engineer/" target="_blank" rel="noopener"
&gt;Distributed systems theory for the distributed systems engineer&lt;/a&gt;: I work on distributed systems these days. I need to read some of these papers.&lt;/p&gt;</description></item><item><title>Links for August 25, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-august-25-2014/</link><pubDate>Mon, 25 Aug 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-august-25-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="http://stackoverflow.com/questions/352098/how-can-i-pretty-print-json/1920585#1920585" target="_blank" rel="noopener"
&gt;How can I pretty-print JSON at the command line?&lt;/a&gt;: I needed to pretty print some JSON at the command line earlier today. The easiest way might be to pipe it through &lt;code&gt;python -m json.tool&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://blog.iansinnott.com/integrating-alfred-and-keyboard-maestro/" target="_blank" rel="noopener"
&gt;Integrating Alfred &amp;amp; Keyboard Maestro&lt;/a&gt;: I love &lt;a class="link" href="http://www.keyboardmaestro.com/main/" target="_blank" rel="noopener"
&gt;Keyboard Maestro&lt;/a&gt; for automating all kinds of things on my Mac, but I&amp;rsquo;m reaching a limit of keyboard shortcuts I can remember. Here&amp;rsquo;s an Alfred workflow for launching macros instead.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/Parsely/streamparse/releases/tag/v1.0.0" target="_blank" rel="noopener"
&gt;streamparse 1.0.0&lt;/a&gt;: My team at &lt;a class="link" href="http://www.parsely.com" target="_blank" rel="noopener"
&gt;Parsely&lt;/a&gt; is building a tool for easily writing Storm topologies (for processing large volumes of streaming data) in Python. We just released 1.0.0!&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://brettterpstra.com/projects/te-tools/" target="_blank" rel="noopener"
&gt;TextExpander Tools&lt;/a&gt;: Brett Terpstra, the king of Mac hacks, has some really handy tools for TextExpander.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.gnu.org/software/parallel/" target="_blank" rel="noopener"
&gt;GNU Parallel&lt;/a&gt;: GNU parallel is a shell tool for executing jobs in parallel using one or more computers using xargs-like syntax. Pretty cool. HT &lt;a class="link" href="http://www.twitter.com/oceankidbilly" target="_blank" rel="noopener"
&gt;@oceankidbilly&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Links for August 23, 2014</title><link>https://tdhopper.com/blog/sundry-links-for-august-23-2014/</link><pubDate>Sat, 23 Aug 2014 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sundry-links-for-august-23-2014/</guid><description>&lt;p&gt;&lt;a class="link" href="http://crsmithdev.com/arrow/" target="_blank" rel="noopener"
&gt;Arrow: better dates and times for Python&lt;/a&gt;: Arrow is a slick Python library &amp;ldquo;that offers a sensible, human-friendly approach to creating, manipulating, formatting and converting dates, times, and timestamps&amp;rdquo;. It&amp;rsquo;s a friendly alternative to datetime.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://penandpants.com/2014/03/09/docker-via-homebrew/" target="_blank" rel="noopener"
&gt;Docker via Homebrew&lt;/a&gt;: I&amp;rsquo;m starting to use Docker (&amp;ldquo;Docker is an open platform for developers and sysadmins to build, ship, and run distributed applications&amp;rdquo;) on occasion. Here are easy install instructions for Mac users.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://www.coursera.org/course/mmds" target="_blank" rel="noopener"
&gt;Mining Massive Datasets MOOC&lt;/a&gt;: I&amp;rsquo;m terrible at completing MOOCs, but I&amp;rsquo;m &lt;em&gt;really&lt;/em&gt; interested in this new on on Mining Massive Datasets.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://chrome.google.com/webstore/detail/url-pinner/lchefjdnocignejmkklgakfmnjhiimjh" target="_blank" rel="noopener"
&gt;URL Pinner - Chrome Web Store&lt;/a&gt;: URL Pinner is one of my favorite Chrome Extensions. I use it to automatically pin my Gmail and Rdio windows (which I almost always have open).&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.seengee.co.uk/2012/09/08/using-multitail-for-monitoring-multiple-log-files/" target="_blank" rel="noopener"
&gt;Using multitail for monitoring multiple log files&lt;/a&gt;: If you work with distributed systems, you&amp;rsquo;re probably used to SSH-ing into multiple machines to access logs. Multitool might save you some time.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.smbc-comics.com/?id=3454" target="_blank" rel="noopener"
&gt;Saturday Morning Breakfast Cereal&lt;/a&gt;: SBMC shows how job interviews would go if we were more honest.&lt;/p&gt;</description></item><item><title>Noisy Series and Body Weight Part 2</title><link>https://tdhopper.com/blog/noisy-series-and-body-weight-take-2/</link><pubDate>Thu, 28 Nov 2013 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/noisy-series-and-body-weight-take-2/</guid><description>&lt;p&gt;Back in July, I posted some &lt;a class="link" href="https://tdhopper.com/blog/2013/Jul/26/noisy-series-and-body-weight/" &gt;analysis of my attempt at weight loss&lt;/a&gt;. Now that I&amp;rsquo;m four months further down the line, I thought I&amp;rsquo;d post a follow-up.&lt;/p&gt;
&lt;p&gt;I continue to be fascinated with how noisy my weight time series is. While I&amp;rsquo;ve continued to lose weight over time, my weight goes up two out of five mornings.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a plot of the time series of my change in weight. Note how often the change is positive, i.e. I appear to have gained weight:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2013/11/weight_change1.png"&gt;
&lt;/figure&gt;
&lt;p&gt;This volatility can hide the fact that I&amp;rsquo;m making progress! When I put a regression line through the points, you can see that the average change slightly below zero:&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2013/11/weight_change2.png"&gt;
&lt;/figure&gt;
&lt;p&gt;I have wondered recently if my average change in weight is correlated with the day of the week. My hypothesis is that my weight tends to go up over the weekends, so I created a boxplot of my change in weight categorized by day.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2013/11/weight_box.png"&gt;
&lt;/figure&gt;
&lt;p&gt;Indeed, on Sundays and Mondays (i.e. weight change from Saturday morning to Sunday morning and Sunday morning to Monday morning) my median weight change is slightly above zero. This makes sense to me: on Saturdays, I&amp;rsquo;m more likely to be doing things with friends, and thus I have less control over my meals.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I wish I had a good explanation for why the change on Friday is so dramatic, but I don&amp;rsquo;t. Any guesses?&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I mentioned this to my college roommate who is a financial planner. He noted how similar this is to investing; it&amp;rsquo;s a constant battle for him to convince his clients to look at &lt;em&gt;average&lt;/em&gt; behavior instead of daily changes.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Also, beer.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Tweeting Primes</title><link>https://tdhopper.com/blog/tweeting-primes/</link><pubDate>Fri, 08 Nov 2013 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/tweeting-primes/</guid><description>&lt;p&gt;I recently discovered the Twitter account &lt;a class="link" href="https://twitter.com/_primes_" target="_blank" rel="noopener"
&gt;@&lt;em&gt;primes&lt;/em&gt;&lt;/a&gt;. Every hour, they tweet the subsequent prime number. This made me wonder two things. First, what is the largest prime that you can tweet (in base-10 encoding in 140 characters).&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; Second, how long until they get there.&lt;/p&gt;
&lt;p&gt;Doing some quick calculations in Mathematica, I believe the largest 140 digit prime is the following:&lt;/p&gt;
&lt;p&gt;9999999999999999999999999999999999999999999999&lt;/br&gt;
9999999999999999999999999999999999999999999999&lt;/br&gt;
999999999999999999999999999999999999999999999997&lt;/p&gt;
&lt;p&gt;Wolfram Alpha &lt;a class="link" href="http://www.wolframalpha.com/input/?i=IsPrime%5B99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999997" target="_blank" rel="noopener"
&gt;confirms that this is prime&lt;/a&gt; and that the &lt;a class="link" href="http://www.wolframalpha.com/input/?i=Length%5BIntegerDigits%5BNextPrime%5B99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999997%5D%5D%5D" target="_blank" rel="noopener"
&gt;next prime is 141 characters&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As for how long it would take, recall that the number of primes less than $n$ &lt;a class="link" href="http://en.wikipedia.org/wiki/Prime_number_theorem" target="_blank" rel="noopener"
&gt;is approximately&lt;/a&gt; $\frac{n}{\ln n}$. The number of primes less than $10^{141}$ is approximately&lt;/p&gt;
$$\pi(10^{140}) = \frac{10^{140}}{140\cdot \ln 10} = 3.1\cdot 10^{137}.$$&lt;p&gt;That&amp;rsquo;s $3\cdot 10^{57}$ times the estimated number of atoms in the universe. Looks like &lt;a class="link" href="https://twitter.com/_primes_" target="_blank" rel="noopener"
&gt;@&lt;em&gt;primes&lt;/em&gt;&lt;/a&gt; should be able to tweet for a while.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;The &lt;a class="link" href="http://en.wikipedia.org/wiki/Mersenne_primes" target="_blank" rel="noopener"
&gt;largest known prime&lt;/a&gt; is $2^{57,885,161} − 1$ and has 17,425,170 digits.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Sublime Text and Markdown</title><link>https://tdhopper.com/blog/sublime-text-and-markdown/</link><pubDate>Sat, 19 Oct 2013 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/sublime-text-and-markdown/</guid><description>&lt;p&gt;I have largely moved from Textmate to Sublime Text 2 for text editing. Among other reasons, Sublime Text is cross platform, and I use Windows at work and a Mac at home. I have also started writing as much as I can in &lt;a class="link" href="http://daringfireball.net/projects/markdown/" target="_blank" rel="noopener"
&gt;Markdown&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I intended to write a blog post about using Sublime Text as a tool for writing Markdown. However, the inimitable Federico Viticci, of &lt;a class="link" href="http://www.macstories.net/" target="_blank" rel="noopener"
&gt;macstories.net&lt;/a&gt;, has &lt;a class="link" href="http://www.macstories.net/roundups/sublime-text-2-and-markdown-tips-tricks-and-links/" target="_blank" rel="noopener"
&gt;already written that post&lt;/a&gt;, so I will simply refer you there.&lt;/p&gt;</description></item><item><title>The Incessant Commentary on Being Tall</title><link>https://tdhopper.com/blog/the-incessant-commentary-on-being-tall/</link><pubDate>Wed, 02 Oct 2013 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/the-incessant-commentary-on-being-tall/</guid><description>&lt;p&gt;Ralph Keyes, &lt;a class="link" href="http://www.ralphkeyes.com/height/" target="_blank" rel="noopener"
&gt;The Height of Your Life&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I&amp;rsquo;ve heard this sort of thing repeatedly from tall men. It&amp;rsquo;s not the incessant commentary about their height that is so annoying, it&amp;rsquo;s the stupefying boredom of it all. Were anyone to say something original or witty or different in any way, the constant chatter thrown their way might at least be entertaining. But soon after reaching their full height, tall people realize to their horror that the lifetime&amp;rsquo;s commentary to which they&amp;rsquo;ve been sentenced comes mostly from those with least to say.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Noisy Series and Body Weight</title><link>https://tdhopper.com/blog/noisy-series-and-body-weight/</link><pubDate>Fri, 26 Jul 2013 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/noisy-series-and-body-weight/</guid><description>&lt;p&gt;I put on some weight during my time in grad school, and this spring, I decided to do something about it.
In April, I started using &lt;a class="link" href="https://www.myfitnesspal.com" target="_blank" rel="noopener"
&gt;MyFitnessPal&lt;/a&gt; to track my food intake and exercise, and I run a net calorie deficit every day. Thankfully this seems to be working.&lt;/p&gt;
&lt;p&gt;In May, I bought a &lt;a class="link" href="http://www.amazon.com/Withings-Wireless-Scale-WS-30-Black/dp/B00AXYL4M6/ref=sr_1_4?ie=UTF8&amp;amp;qid=1374864050&amp;amp;sr=8-4&amp;amp;keywords=withings" target="_blank" rel="noopener"
&gt;Withings WS-30&lt;/a&gt; wireless scale.
When I first heard about this wifi scales, I thought they sounded like a gimmick, however the Withings has become a helpful tool in the weight loss process.&lt;/p&gt;
&lt;p&gt;Every morning, I step on the scale and my weight is automatically broadcast to MyFitnessPal, &lt;a class="link" href="https://itunes.apple.com/us/app/monitor-your-weight/id413313086?mt=8" target="_blank" rel="noopener"
&gt;Monitor Your Weight&lt;/a&gt; on iOS, and a text file in my Dropbox folder (via &lt;a class="link" href="https://ifttt.com" target="_blank" rel="noopener"
&gt;IFTTT&lt;/a&gt; and Withings&amp;rsquo; API).
MyFitnessPal adjusts my daily calorie limit by my weight, Monitor Your Weight is a great tool for visualizing progressing, and I use the text file to import a ggplot time series of my weight into &lt;a class="link" href="https://dayoneapp.com" target="_blank" rel="noopener"
&gt;Day One&lt;/a&gt; each month.&lt;/p&gt;
&lt;p&gt;An interesting aspect of my weight time series is how noisy it is. (No doubt this is true for others as well.)
On many mornings, my weight is up from the day before (despite a fairly consistent net caloric deficit).
As you can see from the plot, my weight jumps up and down daily even though the overall trend is downward.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2013/07/weightseries.png"&gt;
&lt;/figure&gt;
&lt;p&gt;I have been wondering what percentage of days I actually lose weight, so I decided to find out.
The plot below is a histogram of my weight change from day to day.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2013/07/weighthist.png"&gt;
&lt;/figure&gt;
&lt;p&gt;The data appears nearly Gaussian around 0! (In fact, the p-value on the Shapiro-Wilk normality test is 0.11, arguably not small enough to reject the null hypothesis that the data are &lt;em&gt;not&lt;/em&gt; normally distributed.) Fortunately the mean of the differences is actually about -0.24 (pounds/day), and my progress is downward.&lt;/p&gt;
&lt;p&gt;In total, I lost weight on 48 days, gained on 33, and stayed the same on 4% of the days.
That means I&amp;rsquo;ve steadly lost weight while only moving down on 56% of days.
I guess I don&amp;rsquo;t need to be depressed every time my weight jumps up slightly&amp;hellip;.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;This isn&amp;rsquo;t 100% true. I&amp;rsquo;m hiding the fact that I missed weighing-in on some days.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Guide to Monte Carlo Methods?</title><link>https://tdhopper.com/blog/guide-to-monte-carlo-methods/</link><pubDate>Tue, 23 Jul 2013 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/guide-to-monte-carlo-methods/</guid><description>&lt;p&gt;I have started to realize that &lt;a class="link" href="http://en.wikipedia.org/wiki/Monte_Carlo_method" target="_blank" rel="noopener"
&gt;Monte Carlo methods&lt;/a&gt; of various kinds keep coming up in my work. Despite significant application of Monte Carlo in my grad school research, I think I only know enough to be dangerous. I&amp;rsquo;d like to get a better grasp on Monte Carlo methods (especially MCMC and simulation).&lt;/p&gt;
&lt;p&gt;I &lt;a class="link" href="https://twitter.com/tdhopper/status/359642533950787585" target="_blank" rel="noopener"
&gt;asked on Twitter&lt;/a&gt; if anyone had a recommended reference that was readable and practical. Despite my love of measure theory, what I want is &lt;em&gt;Monte Carlo Methods for the &lt;a class="link" href="http://www.johndcook.com/veryappliedmath.html" target="_blank" rel="noopener"
&gt;Very Applied Mathematician&lt;/a&gt;&lt;/em&gt;, not a theoretical text.&lt;/p&gt;
&lt;p&gt;I got several recommendations. I&amp;rsquo;m not sure that any are exactly what I&amp;rsquo;m looking for, but I am certainly going to look deeper into them. Interestingly, they are all Springer books.&lt;/p&gt;
&lt;p&gt;Several people recommended Glasserman&amp;rsquo;s &lt;a class="link" href="http://www.amazon.com/Financial-Engineering-Stochastic-Modelling-Probability/dp/0387004513" target="_blank" rel="noopener"
&gt;Monte Carlo Methods in Financial Engineering&lt;/a&gt;. I don&amp;rsquo;t work in the financial sector, so it&amp;rsquo;s hard for me to evaluate the table of contents to tell how well it generalizes.&lt;/p&gt;
&lt;p&gt;Someone else recommended both &lt;a class="link" href="http://www.amazon.com/dp/038787836X" target="_blank" rel="noopener"
&gt;Explorations in Monte Carlo Methods&lt;/a&gt; and &lt;a class="link" href="http://www.amazon.com/Handbook-Chapman-Handbooks-Statistical-Methods/dp/1420079417" target="_blank" rel="noopener"
&gt;Handbook of Markov Chain Monte Carlo&lt;/a&gt; for two levels of MCMC.&lt;/p&gt;
&lt;p&gt;Finally, I got a recommendation for &lt;a class="link" href="http://www.amazon.com/Introducing-Monte-Carlo-Methods-ebook/dp/B00440E0JO/ref=sr_1_1?s=books&amp;amp;ie=UTF8&amp;amp;qid=1374616614&amp;amp;sr=1-1&amp;amp;keywords=introducing&amp;#43;monte&amp;#43;carlo&amp;#43;methods&amp;#43;with&amp;#43;R" target="_blank" rel="noopener"
&gt;Introducing Monte Carlo Methods with R&lt;/a&gt;. This might be closest to what I&amp;rsquo;m looking for. It appears to cover a breadth of topics, and it includes lots of code.&lt;/p&gt;</description></item><item><title>Bandit Algorithms for Website Optimization</title><link>https://tdhopper.com/blog/bandit-algorithms-for-website-optimization/</link><pubDate>Tue, 29 Jan 2013 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/bandit-algorithms-for-website-optimization/</guid><description>&lt;img src="https://tdhopper.com/images/mabandit.png" alt="Featured image of post Bandit Algorithms for Website Optimization" /&gt;&lt;p&gt;I had the honor of being a technical reviewer for John Myles White&amp;rsquo;s outstanding book, &amp;ldquo;&lt;a class="link" href="https://amzn.to/4g8G1K8" target="_blank" rel="noopener"
&gt;Bandit Algorithms for Website Optimization: Developing, Deploying, and Debugging&lt;/a&gt;.&amp;rdquo; I strongly suggest it to anyone curious about the subject. This book is an excellent start to understanding the field and provides practical advice on applying these algorithms effectively.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://amzn.to/3SXD9a8" target="_blank" rel="noopener"
&gt;Check it out!&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Introduction to Scikit-Learn</title><link>https://tdhopper.com/talks/scikit/</link><pubDate>Mon, 21 Jan 2013 00:00:00 +0000</pubDate><guid>https://tdhopper.com/talks/scikit/</guid><description>&lt;img src="https://tdhopper.com/preso.png" alt="Featured image of post Introduction to Scikit-Learn" /&gt;&lt;p&gt;I gave a talk at a recent Research Triangle Analysts meetup on scikit-learn, the excellent machine learning library for Python. You can see the &lt;a class="link" href="http://nbviewer.jupyter.org/format/slides/github/tdhopper/Research-Triangle-Analysts--Intro-to-scikit-learn/blob/master/Intro%20to%20Scikit-Learn.ipynb" target="_blank" rel="noopener"
&gt;IPython notebook that I presented from&lt;/a&gt;.&lt;/p&gt;
&lt;div class="video-wrapper"&gt;
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/2kx19t8bNMU"
allowfullscreen
title="YouTube Video"
&gt;
&lt;/iframe&gt;
&lt;/div&gt;</description></item><item><title>Bringing Operations Research into the 21st Century with Online Video</title><link>https://tdhopper.com/talks/orvideo/</link><pubDate>Sat, 01 Dec 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/talks/orvideo/</guid><description>&lt;img src="https://tdhopper.com/preso.png" alt="Featured image of post Bringing Operations Research into the 21st Century with Online Video" /&gt;&lt;p&gt;I presented at INFORMS 2012 on &lt;a class="link" href="https://www.youtube.com/watch?v=0gfBH4mC_iU" target="_blank" rel="noopener"
&gt;Bringing Operations Research into the 21st Century with Online Video&lt;/a&gt;. You can see the recording on &lt;a class="link" href="https://www.youtube.com/watch?v=0gfBH4mC_iU" target="_blank" rel="noopener"
&gt;Youtube&lt;/a&gt;.&lt;/p&gt;
&lt;div class="video-wrapper"&gt;
&lt;iframe loading="lazy"
src="https://www.youtube.com/embed/0gfBH4mC_iU"
allowfullscreen
title="YouTube Video"
&gt;
&lt;/iframe&gt;
&lt;/div&gt;</description></item><item><title>Pickle and Redis</title><link>https://tdhopper.com/blog/pickle-and-redis/</link><pubDate>Mon, 22 Oct 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/pickle-and-redis/</guid><description>&lt;img src="https://tdhopper.com/images/pickle.png" alt="Featured image of post Pickle and Redis" /&gt;&lt;p&gt;I gave a talk at PyCarolinas 2012 about using Pickle and Redis to persist data with Python. It wasn&amp;rsquo;t recorded, but you can &lt;a class="link" href="http://nbviewer.jupyter.org/github/tdhopper/Pickle-and-Redis/blob/master/Pickle%20and%20Redis.ipynb" target="_blank" rel="noopener"
&gt;see the IPython notebook I presented from&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Operations Research and Computer Programming</title><link>https://tdhopper.com/blog/operations-research-and-computer-programming/</link><pubDate>Thu, 12 Jul 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/operations-research-and-computer-programming/</guid><description>&lt;p&gt;For part of my sophomore year of college, I was a computer science major. When I realized that I loved my CS theory courses while my classmates hated them, I decide to major in math instead. I enjoyed the programming classes enough, but programming is not what I wanted to spend my time doing.&lt;/p&gt;
&lt;p&gt;The summer after my junior year, I was accepted to a &lt;em&gt;math&lt;/em&gt; REU at Rochester Institute of Technology. The first thing my adviser &lt;a class="link" href="http://www.cs.rit.edu/~spr/" target="_blank" rel="noopener"
&gt;Stanislaw Radziszowski&lt;/a&gt; asked me was whether or not I could program! I spent the whole summer programming combinatorial graph theory-related algorithms in C&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Now I, like many of my operations research classmates, spend much of my time programming. Despite the importance of writing code for solving operations research problems, I am surprised how little programming is discussed. The &lt;a class="link" href="http://www.or.ncsu.edu/admission/" target="_blank" rel="noopener"
&gt;admissions page for my program&lt;/a&gt; says nothing about programming ability, but it is implicitly assumed that programming is a skill that students have.&lt;/p&gt;
&lt;p&gt;Moreover, I suspect the operations research-specific parts of the research behind many journal articles is only a fraction of the actual work done by the authors. Much of the required work is implementation and debugging of their algorithms. Yet, articles contain little-to-no discussion of the actual code. Even worse, the code is often not published or reviewed. I can only imagine how many coding errors underly the results of peer-reviewed papers.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://kuomarc.wordpress.com/2012/07/02/top-hackers-we-need-you-for-operations-research/" target="_blank" rel="noopener"
&gt;Marc Kuo recently blogged&lt;/a&gt; about how operations researchers need to get with the program (pun intended). His post kicked of tons of discussion in its &lt;a class="link" href="https://kuomarc.wordpress.com/2012/07/02/top-hackers-we-need-you-for-operations-research/#comments" target="_blank" rel="noopener"
&gt;comments&lt;/a&gt;, on &lt;a class="link" href="https://plus.google.com/110464871801965858778/posts/QDtHN7PcsCL" target="_blank" rel="noopener"
&gt;Google+&lt;/a&gt;, on &lt;a class="link" href="http://news.ycombinator.com/item?id=4191847" target="_blank" rel="noopener"
&gt;Hacker News&lt;/a&gt;, and on &lt;a class="link" href="http://www.or-exchange.com/questions/5783/which-programming-skillsexperience-are-essential-for-being-a-good-or-researcher-if-any?utm_medium=twitter&amp;amp;utm_source=twitterfeed" target="_blank" rel="noopener"
&gt;OR-Exchange&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This discussion came at a good time for me. I&amp;rsquo;m in the middle of my first big coding project of my PhD research. Despite completing a computer science minor and spending two summers doing nothing but coding, I never learned good software engineering practices. I decided at the beginning of the summer to force myself not to just write this code to get the job done but to write &lt;em&gt;good&lt;/em&gt; code.&lt;/p&gt;
&lt;p&gt;To start, I finally started using &lt;a class="link" href="http://git-scm.com/" target="_blank" rel="noopener"
&gt;git&lt;/a&gt; and &lt;a class="link" href="https://github.com/" target="_blank" rel="noopener"
&gt;github&lt;/a&gt; for version control. I have tried several times before, but I have always found it rather confusing&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;. &lt;a class="link" href="http://www.vogella.com/articles/Git/article.html" target="_blank" rel="noopener"
&gt;This git tutorial&lt;/a&gt; finally got me over the hump. Now I can easily branch my code into different versions, and I have the ability to go back to old versions when I screw something up.&lt;/p&gt;
&lt;p&gt;Second, I started teaching myself about &lt;a class="link" href="http://en.wikipedia.org/wiki/Unit_testing" target="_blank" rel="noopener"
&gt;unit testing&lt;/a&gt;. Code testing was never mentioned in any of my classes in college, and I never hear operations researchers talk about it. Again, I have no doubt that the code behind much published work is full of mistakes. Operations researchers need good testing practices?&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Third, I&amp;rsquo;m trying to write clean, object-oriented, well-commented code. My intention is to publish this code on &lt;a class="link" href="https://github.com/tdhopper" target="_blank" rel="noopener"
&gt;github&lt;/a&gt; when the corresponding paper is published. I want my results to be easily reproducible by others and open to scrutiny. I would also like my code to be reusable for future research. My design patterns might not be quite there yet, but I&amp;rsquo;m trying to move in that direction.&lt;/p&gt;
&lt;p&gt;I realized that I&amp;rsquo;ve used the word &lt;em&gt;I&lt;/em&gt; as much as Stephen Wolfram blog post. I have no desire to toot my own horn here; I&amp;rsquo;m just thankful this conversation is happening, and I want to continue it. Good software is crucial to good operations research (both in the academy and out), and yet academic operations researchers, in my experience, talk very little about good software engineering practices. We can do better.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I&amp;rsquo;m eternally indebted to my brilliant research partner Evan who taught me how to use bash, vim, and subversion, among other things.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;I feel vindicated by a &lt;a class="link" href="http://news.ycombinator.com/item?id=4199772" target="_blank" rel="noopener"
&gt;recent thread on Hacker News&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Incidentally, here&amp;rsquo;s a&lt;a class="link" href="http://www.quora.com/How-do-people-write-automated-tests-for-their-stochastic-algorithms" target="_blank" rel="noopener"
&gt;n interesting Quora thread&lt;/a&gt; about testing stochastic algorithms.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Notes on the Derivation of Least Squares Policy Iteration</title><link>https://tdhopper.com/blog/notes-on-the-derivation-of-least-squares-policy-iteration/</link><pubDate>Mon, 18 Jun 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/notes-on-the-derivation-of-least-squares-policy-iteration/</guid><description>&lt;img src="https://tdhopper.com/images/lspi.png" alt="Featured image of post Notes on the Derivation of Least Squares Policy Iteration" /&gt;&lt;p&gt;&lt;a class="link" href="https://tdhopper.com/uploads/lspi.pdf" &gt;Here are my notes&lt;/a&gt; on the derivation of the Least Squares Policy Iteration (LSPI) algorithm. The notes are based on the &lt;a class="link" href="https://users.cs.duke.edu/~parr/jmlr03.pdf" target="_blank" rel="noopener"
&gt;original paper by Lagoudakis and Parr&lt;/a&gt;.&lt;/p&gt;
&lt;script type="text/javascript" src= '/js/pdf-js/build/pdf.js'&gt;&lt;/script&gt;
&lt;style&gt;
#embed-pdf-container {
position: relative;
width: 100%;
height: auto;
min-height: 20vh;
}
.pdf-canvas {
border: 1px solid black;
direction: ltr;
width: 100%;
height: auto;
display: none;
}
#the-canvas {
border: 1px solid black;
direction: ltr;
width: 100%;
height: auto;
display: none;
}
.pdf-loadingWrapper {
display: none;
justify-content: center;
align-items: center;
width: 100%;
height: 350px;
}
.pdf-loading {
display: inline-block;
width: 50px;
height: 50px;
border: 3px solid #d2d0d0;;
border-radius: 50%;
border-top-color: #383838;
animation: spin 1s ease-in-out infinite;
-webkit-animation: spin 1s ease-in-out infinite;
}
#overlayText {
word-wrap: break-word;
display: grid;
justify-content: end;
}
#overlayText a {
position: relative;
top: 10px;
right: 4px;
color: #000;
margin: auto;
background-color: #eeeeee;
padding: 0.3em 1em;
border: solid 2px;
border-radius: 12px;
border-color: #00000030;
text-decoration: none;
}
#overlayText svg {
height: clamp(1em, 2vw, 1.4em);
width: clamp(1em, 2vw, 1.4em);
}
@keyframes spin {
to { -webkit-transform: rotate(360deg); }
}
@-webkit-keyframes spin {
to { -webkit-transform: rotate(360deg); }
}
&lt;/style&gt;&lt;div class="embed-pdf-container" id="embed-pdf-container-8750537f"&gt;
&lt;div class="pdf-loadingWrapper" id="pdf-loadingWrapper-8750537f"&gt;
&lt;div class="pdf-loading" id="pdf-loading-8750537f"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div id="overlayText"&gt;
&lt;a href="https://tdhopper.com/uploads/lspi.pdf" aria-label="Download" download&gt;
&lt;svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"&gt;
&lt;path d="M9 13c.3 0 .5-.1.7-.3L15.4 7 14 5.6l-4 4V1H8v8.6l-4-4L2.6 7l5.7 5.7c.2.2.4.3.7.3zm-7 2h14v2H2z" /&gt;
&lt;/svg&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;canvas class="pdf-canvas" id="pdf-canvas-8750537f"&gt;&lt;/canvas&gt;
&lt;/div&gt;
&lt;div class="pdf-paginator" id="pdf-paginator-8750537f"&gt;
&lt;button id="pdf-prev-8750537f"&gt;Previous&lt;/button&gt;
&lt;button id="pdf-next-8750537f"&gt;Next&lt;/button&gt; &amp;nbsp; &amp;nbsp;
&lt;span&gt;
&lt;span class="pdf-pagenum" id="pdf-pagenum-8750537f"&gt;&lt;/span&gt; / &lt;span class="pdf-pagecount" id="pdf-pagecount-8750537f"&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;a class="pdf-source" id="pdf-source-8750537f" href="https://tdhopper.com/uploads/lspi.pdf"&gt;[pdf]&lt;/a&gt;
&lt;/div&gt;
&lt;noscript&gt;
View the PDF file &lt;a class="pdf-source" id="pdf-source-noscript-8750537f" href="https://tdhopper.com/uploads/lspi.pdf"&gt;here&lt;/a&gt;.
&lt;/noscript&gt;
&lt;script type="text/javascript"&gt;
(function(){
var url = '\/uploads\/lspi.pdf';
var hidePaginator = "" === "true";
var hideLoader = "" === "true";
var selectedPageNum = parseInt("") || 1;
var pdfjsLib = window['pdfjs-dist/build/pdf'];
if (pdfjsLib.GlobalWorkerOptions.workerSrc == '')
pdfjsLib.GlobalWorkerOptions.workerSrc = "https:\/\/tdhopper.com\/" + '/js/pdf-js/build/pdf.worker.js';
var pdfDoc = null,
pageNum = selectedPageNum,
pageRendering = false,
pageNumPending = null,
scale = 3,
canvas = document.getElementById('pdf-canvas-8750537f'),
ctx = canvas.getContext('2d'),
paginator = document.getElementById("pdf-paginator-8750537f"),
loadingWrapper = document.getElementById('pdf-loadingWrapper-8750537f');
showPaginator();
showLoader();
function renderPage(num) {
pageRendering = true;
pdfDoc.getPage(num).then(function(page) {
var viewport = page.getViewport({scale: scale});
canvas.height = viewport.height;
canvas.width = viewport.width;
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
var renderTask = page.render(renderContext);
renderTask.promise.then(function() {
pageRendering = false;
showContent();
if (pageNumPending !== null) {
renderPage(pageNumPending);
pageNumPending = null;
}
});
});
document.getElementById('pdf-pagenum-8750537f').textContent = num;
}
function showContent() {
loadingWrapper.style.display = 'none';
canvas.style.display = 'block';
}
function showLoader() {
if(hideLoader) return
loadingWrapper.style.display = 'flex';
canvas.style.display = 'none';
}
function showPaginator() {
if(hidePaginator) return
paginator.style.display = 'block';
}
function queueRenderPage(num) {
if (pageRendering) {
pageNumPending = num;
} else {
renderPage(num);
}
}
function onPrevPage() {
if (pageNum &lt;= 1) {
return;
}
pageNum--;
queueRenderPage(pageNum);
}
document.getElementById('pdf-prev-8750537f').addEventListener('click', onPrevPage);
function onNextPage() {
if (pageNum &gt;= pdfDoc.numPages) {
return;
}
pageNum++;
queueRenderPage(pageNum);
}
document.getElementById('pdf-next-8750537f').addEventListener('click', onNextPage);
pdfjsLib.getDocument(url).promise.then(function(pdfDoc_) {
pdfDoc = pdfDoc_;
var numPages = pdfDoc.numPages;
document.getElementById('pdf-pagecount-8750537f').textContent = numPages;
if(pageNum &gt; numPages) {
pageNum = numPages
}
renderPage(pageNum);
});
})();
&lt;/script&gt;</description></item><item><title>Calculus Haikus and Limericks</title><link>https://tdhopper.com/blog/calculus-haikus-and-limericks/</link><pubDate>Tue, 08 May 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/calculus-haikus-and-limericks/</guid><description>&lt;img src="https://tdhopper.com/images/poems.png" alt="Featured image of post Calculus Haikus and Limericks" /&gt;&lt;p&gt;I offered my calculus students bonus points to write a limerick or haiku on their final exam. I got some great answers! And the students seem to enjoy it. My favorite limerick (on improper integrals):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An integral has bounds from zero to one&lt;br /&gt;
You finish the problem and think you are done&lt;br /&gt;
Then your mind double checks&lt;br /&gt;
The equation is one over x&lt;br &gt;
And the improper integral has a different sum&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;My favorite haiku gives the formula for integration by parts:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Int of mu dv:&lt;br /&gt;
Equal to mu v minus&lt;br /&gt;
Int of v d mu&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here are some other good ones:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Doing Integrals… &lt;br /&gt;
Oh How I shall miss thee, &lt;br /&gt;
Calc! Goodbye, It&amp;rsquo;s been real?&lt;/p&gt;
&lt;p&gt;Calculus is hard &lt;br /&gt;
Derivative and limit &lt;br /&gt;
¡Yo no se hombre!&lt;/p&gt;
&lt;p&gt;MacLaurin Series &lt;br /&gt;
is an infinite series &lt;br /&gt;
Centered at zero&lt;/p&gt;
&lt;p&gt;Calculus is great&lt;br /&gt;
I&amp;rsquo;m sure I&amp;rsquo;ll use this in life&lt;br /&gt;
Or maybe I won&amp;rsquo;t&lt;/p&gt;
&lt;p&gt;Studying &amp;rsquo;til 5&lt;br /&gt;
And almost sleeping thru test&lt;br /&gt;
Was a real bad call&lt;/p&gt;
&lt;p&gt;Finding integrals&lt;br /&gt;
Is used to find area&lt;br /&gt;
Underneath the curve&lt;/p&gt;
&lt;p&gt;Calc is fun to do&lt;br /&gt;
If you like to integrate&lt;br /&gt;
x from a to b&lt;/p&gt;
&lt;p&gt;Calculus two, sigh&lt;br /&gt;
Why must you torment me so?&lt;br /&gt;
I thought we were friends&lt;/p&gt;
&lt;p&gt;F of e to x&lt;br /&gt;
Derived is e to the x&lt;br /&gt;
Lone function like it&lt;/p&gt;
&lt;p&gt;I like calculus&lt;br /&gt;
The feeling is mutual&lt;br /&gt;
But not all the time&lt;/p&gt;
&lt;p&gt;Integrals are fun&lt;br /&gt;
u-sub can be tricky… yes!&lt;br /&gt;
Don&amp;rsquo;t forget plus C&lt;/p&gt;
&lt;p&gt;Calculus is hard&lt;br /&gt;
Eight A.M. is too early&lt;br /&gt;
Had a good time though&lt;/p&gt;
&lt;p&gt;The ratio test:&lt;br /&gt;
If the limit equals one&lt;br /&gt;
Use another test&lt;/p&gt;
&lt;p&gt;Does this series telescope?&lt;br /&gt;
For my sake, I truly hope&lt;br /&gt;
Comparisons are icky,&lt;br /&gt;
Integrals make me sickly,&lt;br /&gt;
Turns out the answer is nope. :-(&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here are a few from when I taught at UVA:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An integral is&lt;br /&gt;
A derivative reversed&lt;br /&gt;
Don’t forget constants!&lt;/p&gt;
&lt;p&gt;Thanks to L’Hopital&lt;br /&gt;
We can use derivatives&lt;br /&gt;
to find a limit!&lt;/p&gt;
&lt;p&gt;Find the area,&lt;br /&gt;
Between the two stated curves,&lt;br /&gt;
Using integrals.&lt;/p&gt;
&lt;p&gt;Natural log of Zero,&lt;br /&gt;
does not exist, but&lt;br /&gt;
Ln of 1 doesn&amp;rsquo;t&lt;/p&gt;
&lt;p&gt;Calculus is great&lt;br /&gt;
But only if taught by&lt;br /&gt;
Tim At U of V.A.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Teaching Students to Fail</title><link>https://tdhopper.com/blog/teaching-students-to-fail/</link><pubDate>Fri, 13 Apr 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/teaching-students-to-fail/</guid><description>&lt;img src="https://tdhopper.com/images/failing.png" alt="Featured image of post Teaching Students to Fail" /&gt;&lt;p&gt;I am teaching the dreaded calculus II this semester. I&amp;rsquo;ve known many students who flew through calc I in college (having taken calculus in high school) only to receive a reality check from calc II the next semester. In the US, calc II often involves a significant section on &amp;ldquo;techniques of integration&amp;rdquo; where students learn techniques such as partial fractions, trig substitutions, integration by parts. Unlike much of differential calculus, which is taught in calc I, and unlike much of the math taught before college, integration is harder to do algorithmically. That is, a calc II professor cannot simply outline surefire steps guaranteed to give an antiderivative for any function. The inimitable Robert Ghrist explains it this way in his &amp;ldquo;&lt;a class="link" href="http://www.math.upenn.edu/~ghrist/FLCT/" target="_blank" rel="noopener"
&gt;funny little calculus text&lt;/a&gt;&amp;rdquo;:&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://tdhopper.com/uploads/2012/01/Screen-Shot-2012-01-10-at-5.09.27-PM.png" &gt;&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2012/01/Screen-Shot-2012-01-10-at-5.09.27-PM.png"&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Of course, algorithms for computing many antiderivatives do exist (and are used in Maple, Mathematica, and Wolfram Alpha), but I&amp;rsquo;d be fired if I tried to take my undergrads through &lt;em&gt;Symbolic Integration I: Transcendental Functions&lt;/em&gt;..&lt;/p&gt;
&lt;p&gt;Instead, calculus II teachers teach a handful of methods and attempt to teach students intuition for where to use what technique. Even more important, I try to teach my students the skill of trying a method, seeing that the method does not work, then trying something else. Try-fail-try-fail-try again. I do not think that high school students learn that skill—a skill vital to success in calc II and every discipline requiring analytical problem-solving. Yesterday, my adviser and I were discussing the first big research problem that I&amp;rsquo;ll be tackling this summer. He noted that our first attempt at solving a massive problem would probably fail; they usually do.&lt;/p&gt;
&lt;p&gt;Fortunately, I&amp;rsquo;ve been failing at solving problems, at least since taking number theory with Dan Krider in 2003. I know what Edison meant by &amp;ldquo;I have not failed. I&amp;rsquo;ve just found 10,000 ways that won&amp;rsquo;t work.&amp;rdquo; I hope my students are learning how to fail and how to try again. However, I think that kids need to learn earlier. High school assignments should not be set up for students to succeed the first time, every time. Somehow, teachers need to allow students to take risks, learn from their mistakes, and rebound. I&amp;rsquo;d love to hear feedback from students who are learning these lessons and teachers trying to teach them.&lt;/p&gt;</description></item><item><title>Average Area of a Random Hull</title><link>https://tdhopper.com/blog/average-area-of-a-random-hull/</link><pubDate>Thu, 05 Apr 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/average-area-of-a-random-hull/</guid><description>&lt;p&gt;Yesterday, someone on MathOverflow asked&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Consider $n$ points generated randomly and uniformly on a unit square. What is the expected value of the area (as a function of $n$) enclosed by the convex hull of the set of points?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Someone quickly cited &lt;a class="link" href="http://www.math.kth.se/~johanph/area12.pdf" target="_blank" rel="noopener"
&gt;2004 paper&lt;/a&gt; provides an analytical result for the cases where $n=3$ and $n=4$:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For $n=3$ the expected value is $11/144$ and for $n=4$ it is $11/72$.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is certainly a nontrivial result. However, the value can be approximated by generating a large number of random points, finding the area of the convex hull, and averaging the areas. Of course, finding the convex hull and the area of the convex hull of a set of points requires a little work. Mathematica provides functions for generating random points and finding the area of the convex hull of a set of points quickly. As a result, I was able to perform a Monte Carlo simulation for the $n=3$ and $n=4$ case in a couple of lines of Mathematica code:&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://tdhopper.com/uploads/2012/04/Screen-Shot-2012-04-04-at-11.29.04-AM.png" &gt;&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2012/04/Screen-Shot-2012-04-04-at-11.29.04-AM.png"&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Sampling 5000 cases for each returned results fairly close to the predicted average.&lt;/p&gt;</description></item><item><title>My Email Analytics</title><link>https://tdhopper.com/blog/my-email-analytics/</link><pubDate>Thu, 05 Apr 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/my-email-analytics/</guid><description>&lt;p&gt;Last month, Stephen Wolfram did a blog post on the &lt;a class="link" href="http://blog.stephenwolfram.com/2012/03/the-personal-analytics-of-my-life/" target="_blank" rel="noopener"
&gt;Personal Analytics&lt;/a&gt; of his life. For years, he&amp;rsquo;s recorded every phone call, keyboard stroke, email, and step. He made beautiful graphs to show his activity over the years. A Wolfram Alpha developer just posted a &lt;a class="link" href="http://blog.wolfram.com/2012/04/05/analyzing-your-email-with-mathematica/" target="_blank" rel="noopener"
&gt;Mathematica notebook&lt;/a&gt; on the Wolfram blog allowing anyone to do the same email analysis that Wolfram did with any IMAP email account. Of course, I dropped what I was doing to try it out with my Gmail account. At first, it failed to finish processing my incoming email because the JVM ran out of memory. It took me a while to figure out how to tell JLink to let Java have more memory&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;. Here&amp;rsquo;s a plot of emails sent by from Gmail me over the last six years:&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2012/04/blog-post-email-code.png"&gt;
&lt;/figure&gt;
&lt;p&gt;I started using Gmail regularly after I graduated from college in 2008 (once my college Exchange-based email account was gone). My emailing was noticeably sparse from May 2008-May 2009. During that time I was a teacher, and I didn&amp;rsquo;t spend nearly as much time on a computer as I do now. You can also see a gap during the summer of 2011. I was working at Kiva Systems at the time and primarily used my company email. On the horizontal axis, you notice I&amp;rsquo;m pretty silent between 11 PM and 7 AM. I need my sleep, and I never work at night! My emailing is light from 6-9 PM too. Here&amp;rsquo;s a graph of my email received over the past six years. It comes in pretty heavy from 8 AM to midnight!&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2012/04/blog-post-email-code1.png"&gt;
&lt;/figure&gt;
&lt;p&gt;The thick line just under 6 AM is the Google Calendar email updates I used to get every morning. I stopped getting those once I got an iPod Touch this past Christmas. I can&amp;rsquo;t remember what email used to come at 3 AM for a few years. This next graphic shows the average number of emails I receive per day for each month. My amount of emailing ramped up once I started using email. Notice the downward trend on incoming emails recently: I&amp;rsquo;ve been unsubscribing from unnecessary mailing lists and circulars. My emailing had pretty serious peak last May right before I moved to Boston. Not sure why.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2012/04/Screen-Shot-2012-04-05-at-2.17.28-PM.png"&gt;
&lt;/figure&gt;
&lt;p&gt;Here we have a histogram of the time at which I send emails. Apparently I&amp;rsquo;m most likely to send an email just after 10 PM. I wouldn&amp;rsquo;t have guess that. Don&amp;rsquo;t expect to hear from me after midnight!&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2012/04/blog-post-email-code3.png"&gt;
&lt;/figure&gt;
&lt;p&gt;As a good operations researcher, I wondered if I received email according to a &lt;a class="link" href="http://en.wikipedia.org/wiki/Poisson_process" target="_blank" rel="noopener"
&gt;Poisson process&lt;/a&gt;. I pulled the email time stamp data into R. I get email pretty steadily between 8 AM and 10 PM. I looked at the emails that arrived in that interval since September 2011. The mean interarrival time is 0.53 hours. The standard deviation is 0.92. If it were a poisson process, interarrival times would be exponentially distributed, and the mean and standard deviation would be equal. Below is a histogram of the interarrival times of my emails. The red line is an exponential distribution with the rate set to 1 over the mean interarrival time of my emails. It&amp;rsquo;s not a terrible fit!&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; One reason the email arrival rate might not be exponential is that I frequently have back-and-forth email conversations with people, which skews the distribution towards short interarrival times. I might do some more statistics later, but I have homework to do.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2012/04/Screen-Shot-2012-04-05-at-4.01.35-PM.png"&gt;
&lt;/figure&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;ReinstallJava[CommandLine -&amp;gt; &amp;ldquo;java&amp;rdquo;, JVMArguments -&amp;gt; &amp;ldquo;-Xmx4024m&amp;rdquo;]&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;That&amp;rsquo;s not a official statistical statement!&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Operations Research, Machine Learning, and Optimization</title><link>https://tdhopper.com/blog/operations-research-machine-learning-and-optimization/</link><pubDate>Tue, 03 Apr 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/operations-research-machine-learning-and-optimization/</guid><description>&lt;p&gt;Over the past 18 months, I&amp;rsquo;ve been slowly learning some machine learning. One thing I&amp;rsquo;ve noticed is that most of the math in machine learning is optimization. Regression is typically minimization of some error term. Support vector machines are a quadratic optimization problem with linear constraints. Learning a neural network is &lt;em&gt;simply&lt;/em&gt; solving a nonconvex optimization problem. Clustering often takes the form of expectation-maximization. I&amp;rsquo;m currently learning Bayesian network structure learning which is an extremely difficult combinatorial optimization problem.&lt;/p&gt;
&lt;p&gt;Yesterday on Twitter, I commented that I am surprised at how little operations research people and machine learning people talk. Most of the math of OR is, like machine learning, optimization. All the same &lt;a class="link" href="http://en.wikipedia.org/wiki/Karush%E2%80%93Kuhn%E2%80%93Tucker_conditions" target="_blank" rel="noopener"
&gt;theorems&lt;/a&gt; apply, and we use many of the same &lt;a class="link" href="http://en.wikipedia.org/wiki/Newton%27s_method" target="_blank" rel="noopener"
&gt;algorithms&lt;/a&gt;; we just apply them in different ways. I got helpful feedback from the nerds that follow me. &lt;a class="link" href="https://twitter.com/#!/jefflinderoth" target="_blank" rel="noopener"
&gt;Jeff Linderoth&lt;/a&gt; pointed to the recent book &lt;a class="link" href="http://mitpress.mit.edu/catalog/item/default.asp?ttype=2&amp;amp;tid=12674" target="_blank" rel="noopener"
&gt;Optimization for Machine Learning&lt;/a&gt; by his colleague (et al) Stephen J. Wright at University of Wisconsin, Madison. From what I can tell, Wright is an OR guy in a computer scientists clothing. There&amp;rsquo;s a &lt;a class="link" href="http://videolectures.net/nips2010_wright_oaml/" target="_blank" rel="noopener"
&gt;two-hour lecture&lt;/a&gt; by Wright on the same topic that I look forward to watching. Jeff also pointed to the work of his colleague &lt;a class="link" href="http://pages.cs.wisc.edu/~brecht/publications.html" target="_blank" rel="noopener"
&gt;Ben Recht&lt;/a&gt; who&amp;rsquo;s looking at the optimization problems in ML from a theoretical standpoint. &lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://twitter.com/#!/pykpyk" target="_blank" rel="noopener"
&gt;Paul Kerl&lt;/a&gt; linked to &lt;a class="link" href="http://users.eecs.northwestern.edu/~nocedal/publications.html" target="_blank" rel="noopener"
&gt;Jorge Nocedal&lt;/a&gt;&amp;rsquo;s work at Northwestern. Nocedal and Recht seem to have feet in both worlds. &lt;a class="link" href="https://twitter.com/johnmyleswhite" target="_blank" rel="noopener"
&gt;John Myles White&lt;/a&gt; noted that the legendary optimizer Stephen Boyd came to the New York Academy of Science&amp;rsquo;s Machine Learning event last year. I also came across a 2006 paper on &lt;a class="link" href="http://jmlr.csail.mit.edu/papers/volume7/MLOPT-intro06a/MLOPT-intro06a.pdf" target="_blank" rel="noopener"
&gt;The Interplay of Optimization and Machine Learning Research&lt;/a&gt;. The authors note some difference between an OR and ML perspective on optimization:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We observe that the qualities of good optimization algorithms from the machine learning and optimization perspectives can be quite different. Mathematical programming puts a premium on accuracy, speed, and robustness. Since generalization is the bottom line in machine learning and training is normally done off-line, accuracy and small speed improvements are of little concern in machine learning. Machine learning prefers simpler algorithms that work in reasonable computational time for specific classes of problems&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A bigger question might be where optimization lies as a discipline. Since I&amp;rsquo;ve been in OR, I&amp;rsquo;ve always considered optimization as a subfield of OR. But as I read applied OR literature, I find it jarring to see the details of &lt;em&gt;solving&lt;/em&gt; a difficult optimization problem mixed with the application of the solution to a real world problem. Of course, both ML and OR require practitioners to understand how the algorithms work. Optimization problems are hard, and a black box solution rarely works for any of us. But perhaps optimization will become a field of its own that OR and ML can both feed from instead of the two working independently.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I haven&amp;rsquo;t read the whole paper (that&amp;rsquo;s from the abstract), but I&amp;rsquo;m not entirely convinced that is true. Modern machine learning often requires large scale problems to be solved quickly on-line, while optimizers often solve a problem  offline and speed is negotiable.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Stephen Wolfram's AMA</title><link>https://tdhopper.com/blog/stephen-wolframs-ama/</link><pubDate>Mon, 05 Mar 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/stephen-wolframs-ama/</guid><description>&lt;p&gt;Stephen Wolfram, of Wolfram Research and Mathematica fame, did a &lt;a class="link" href="http://www.reddit.com/r/IAmA/comments/qisot/im_stephen_wolfram_mathematica_nks_wolframalpha/" target="_blank" rel="noopener"
&gt;Q&amp;amp;A (i.e. AMA) on Reddit&lt;/a&gt; today. I just enjoyed reading through his answers. A few interesting answers stood out to me.&lt;/p&gt;
&lt;p&gt;Someone ask Wolfram&amp;rsquo;s opinion on P=NP. &lt;a class="link" href="http://www.reddit.com/r/IAmA/comments/qisot/im_stephen_wolfram_mathematica_nks_wolframalpha/c3xy53q" target="_blank" rel="noopener"
&gt;He thinks it&amp;rsquo;s undecidable&lt;/a&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Some smart aleck &lt;a class="link" href="http://www.reddit.com/r/IAmA/comments/qisot/im_stephen_wolfram_mathematica_nks_wolframalpha/c3xxmie" target="_blank" rel="noopener"
&gt;threw the Riemann hypothesis at him&lt;/a&gt;. Interestingly, Wolfram also suspects this is undecidable&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;One questioner asked about open sourcing old versions of Mathematica. &lt;a class="link" href="http://www.reddit.com/r/IAmA/comments/qisot/im_stephen_wolfram_mathematica_nks_wolframalpha/c3xz8by" target="_blank" rel="noopener"
&gt;Wolfram responded very winsomely&lt;/a&gt;, in my view. I didn&amp;rsquo;t know that they&amp;rsquo;ve thought about making the core language more freely available. I&amp;rsquo;d like to see that.&lt;/p&gt;
&lt;p&gt;His most interesting answer is &lt;a class="link" href="http://www.reddit.com/r/IAmA/comments/qisot/im_stephen_wolfram_mathematica_nks_wolframalpha/c3xxhvt" target="_blank" rel="noopener"
&gt;his opinion on Matlab&lt;/a&gt;. He argues that Matlab has remained matrix-centric when so much of contemporary mathematics goes beyond that. &amp;ldquo;In the complete web of algorithms in Mathematica, things that can reasonably be represented as numerical matrices are perhaps 5 or 10% of the total.&amp;rdquo; However, Wolfram believes that Mathematica isn&amp;rsquo;t outdone by Maple in the realm of matrices.&lt;/p&gt;
&lt;p&gt;Wolfram relays that a major goal of Mathematica is &amp;ldquo;to make a single coherent system in which one can work, and in which everything fits nicely together.&amp;rdquo; &lt;a class="link" href="http://www.tdhopper.com/blog/mathematica-a-love-story/" target="_blank" rel="noopener"
&gt;I argued&lt;/a&gt; that that&amp;rsquo;s one thing they&amp;rsquo;ve done quite well.&lt;/p&gt;
&lt;p&gt;I appreciate Wolfram doing this. I continue to be optimistic about Mathematica as a product, and I hope they have a bright future ahead of them.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;See the Wikipedia page on &lt;a class="link" href="http://en.wikipedia.org/wiki/Undecidable_problem" target="_blank" rel="noopener"
&gt;undecidability&lt;/a&gt; for more.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Both the Riemann hypothesis and P=NP have been around for many years and have a big bounty on solving them: http://www.claymath.org/millennium/.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>The Spelling Corrector that Got Me Interested in Machine Learning</title><link>https://tdhopper.com/blog/the-spelling-corrector-that-got-me-interested-in-machine-learning/</link><pubDate>Fri, 02 Mar 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/the-spelling-corrector-that-got-me-interested-in-machine-learning/</guid><description>&lt;p&gt;Although I was a computer science minor, I&amp;rsquo;d never heard of statistical machine learning until after college. Now I dabble in machine learning on the side. In the long run, I&amp;rsquo;m interested in studying the intersection of operations research and learning, i.e. intelligent optimization systems. Two years ago, I stumbled across Peter Norvig&amp;rsquo;s essay &lt;a class="link" href="http://norvig.com/spell-correct.html" target="_blank" rel="noopener"
&gt;How to Write a Spelling Corrector&lt;/a&gt;. Google is a notoriously good spelling corrector; just try googling &amp;ldquo;&lt;a class="link" href="http://www.google.com/#hl=en&amp;amp;sclient=psy-ab&amp;amp;q=spellign&amp;amp;pbx=1&amp;amp;oq=spellign&amp;amp;aq=f&amp;amp;aqi=g-s4&amp;amp;aql=&amp;amp;gs_sm=3&amp;amp;gs_upl=355l2969l0l3671l8l8l0l0l0l0l152l775l4.4l8l0&amp;amp;gs_l=hp.3..0i10l4.355l2969l0l3672l8l8l0l0l0l0l152l775l4j4l8l0&amp;amp;bav=on.2,or.r_gc.r_pw.r_qf.,cf.osb&amp;amp;fp=6274c5cd76393bc1&amp;amp;biw=1257&amp;amp;bih=802" target="_blank" rel="noopener"
&gt;spellign&lt;/a&gt;.&amp;rdquo; I find that Google knows what word I&amp;rsquo;m trying to spell even when an application&amp;rsquo;s built-in spell check fail. Norvig explains how to use Bayes theorem to write a pretty-good spelling corrector in 21 lines of Python. In college, I had good grounding in probability and computing, but I don&amp;rsquo;t recall having seen the two mixed so elegantly. I&amp;rsquo;m pleased to now know that Norvig was only scratching the surface! This week, I&amp;rsquo;ve been watching Sebastian Thurn&amp;rsquo;s lectures on &lt;a class="link" href="http://www.udacity.com/" target="_blank" rel="noopener"
&gt;Kalman Filters&lt;/a&gt; for AI. Probabilistic techniques for filtering noisy data can be used, for example, in a robot keeping track of objects moving around it.&lt;/p&gt;</description></item><item><title>Mathematica: A Love Story</title><link>https://tdhopper.com/blog/mathematica-a-love-story/</link><pubDate>Fri, 10 Feb 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/mathematica-a-love-story/</guid><description>&lt;p&gt;As an undergraduate, our math department used Wolfram Research&amp;rsquo;s &lt;a class="link" href="http://www.wolfram.com/mathematica/" target="_blank" rel="noopener"
&gt;Mathematica&lt;/a&gt; heavily for instruction in a number of classes. Initially, I found it perplexing and frustrating. While most of my peers remained in that state (and never used it again after those classes), I soon found myself ordering and reading &lt;a class="link" href="http://www.amazon.com/Introduction-Programming-Mathematica-Paul-Wellin/dp/0521846781/ref=sr_1_4?ie=UTF8&amp;amp;qid=1328824356&amp;amp;sr=8-4" target="_blank" rel="noopener"
&gt;An Introduction to Programming with Mathematica&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Seven years later, I find myself using Mathematica almost daily. As a student, it is one of the most helpful tools at my disposal, and it has saved me countless hours of tedious computation by hand. I’m not sure I can express all the ways in which I appreciate it, but I hope to share some.&lt;/p&gt;
&lt;p&gt;I admit that I primarily use Mathematica as a glorified calculator. Most of my code is single use code to help me with a homework assignment. I have written some longer code for class projects, but rarely more than a few hundred lines. However, for the work that I have had over the past seven years, it is exactly the right tool, and I don’t know of any other language which comprehensively offers all the features I need within its core language.&lt;/p&gt;
&lt;p&gt;One other note to the Redditors and cynics (but I repeat myself): I&amp;rsquo;m not recommending or encouraging programmers to jump ship from their main languages to Mathematica. I’m not suggesting that Mathematica doesn’t have any shortcomings. I’m not arguing that Mathematica is good for everything. I’m well aware that Mathematica is an expensive, closed platform. I’m well aware that Mathematic has the worst undo ever. I’m not writing an advertisement or getting paid by Wolfram. I’m simply shared the story of a program that has become an invaluable part of my schooling.&lt;/p&gt;
&lt;h2 id="1-powerful-symbolic-computations"&gt;1. Powerful Symbolic Computations
&lt;/h2&gt;&lt;p&gt;Perhaps the thing Mathematica is most well-known for is symbolic computations. The oldest Mathematica file I have on my computer is a single line of code that I apparently used on a differential equations quiz in 2005. In it, I did a partial fraction decomposition: the bane of calculus 2 students, but easy for a computer.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_1.gif"
loading="lazy"
alt="alovestory_1.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_2.gif"
loading="lazy"
alt="alovestory_2.gif"
&gt;&lt;/p&gt;
&lt;p&gt;One of the benefits of Mathematica, is the elegance of typesetting in both the input and the output. Wolfram has taken great care to make Mathematica an aesthetically excellent experience, and I’m grateful for that.&lt;/p&gt;
&lt;p&gt;These days, I avoid doing algebraic manipulations by hand at all costs. It’s not worth it to me to risk making errors that might trickle down into my work. I let computers handle such things for me. Thus, when I’m doing homework, I usually have a notebook open filled with one-off expressions like&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_3.gif"
loading="lazy"
alt="alovestory_3.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_4.gif"
loading="lazy"
alt="alovestory_4.gif"
&gt;&lt;/p&gt;
&lt;p&gt;Of course, it can solve much harder problems too. Integration is no problem. Here’s a triple integral I solved in my electricity and magnetism class sophomore year. (I wish I remembered what it all means.)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_5.gif"
loading="lazy"
alt="alovestory_5.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_6.gif"
loading="lazy"
alt="alovestory_6.gif"
&gt;&lt;/p&gt;
&lt;p&gt;The output is messy because Mathematica tried to solve the integral as generally as possible. We can get a more clear answer by clarifying some assumptions we’re making about the parameters.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_7.gif"
loading="lazy"
alt="alovestory_7.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_8.gif"
loading="lazy"
alt="alovestory_8.gif"
&gt;&lt;/p&gt;
&lt;h2 id="2-functional-programming"&gt;2. Functional Programming
&lt;/h2&gt;&lt;p&gt;Like R, Mathematica allows procedural programming.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_9.gif"
loading="lazy"
alt="alovestory_9.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_10.gif"
loading="lazy"
alt="alovestory_10.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_11.gif"
loading="lazy"
alt="alovestory_11.gif"
&gt;&lt;/p&gt;
&lt;p&gt;However, again like R, Mathematica is really built for functional programming. &lt;a class="link" href="http://reference.wolfram.com/mathematica/tutorial/FunctionalOperationsOverview.html" target="_blank" rel="noopener"
&gt;Wolfram has a great tutorial on the topic, but let me share a brief example from my own use. &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;On a homework assignment this week, I wanted to measure the total tardiness for various schedules in a single machine problem. Each of the four jobs had a total processing time, given by {2,4,6,8}, and a due date {4,14,10,16}. The tardiness of a job for a given schedule (i.e. ordering of the jobs) is 0 if the job is completed on time and how late it is otherwise. First I set processing time and due dates for the four jobs:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_12.gif"
loading="lazy"
alt="alovestory_12.gif"
&gt;&lt;/p&gt;
&lt;p&gt;Any permutation of the job indices {1,2,3,4} gives a valid schedule. Suppose we want to know the total lateness of the schedule x={1,4,3,2}. The ordering of processing times is given by p[[x]] so the time when each job is completed is a running total of the processing times:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_13.gif"
loading="lazy"
alt="alovestory_13.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_14.gif"
loading="lazy"
alt="alovestory_14.gif"
&gt;&lt;/p&gt;
&lt;p&gt;The lateness of each job is defined by the completion time minus the due date:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_15.gif"
loading="lazy"
alt="alovestory_15.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_16.gif"
loading="lazy"
alt="alovestory_16.gif"
&gt;&lt;/p&gt;
&lt;p&gt;To get the tardiness, we want the max of the lateness and zero. There are a number of ways to do this, but one is to apply a max function to each element of the list:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_17.gif"
loading="lazy"
alt="alovestory_17.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_18.gif"
loading="lazy"
alt="alovestory_18.gif"
&gt;&lt;/p&gt;
&lt;p&gt;(The &lt;em&gt;#&lt;/em&gt; and &lt;em&gt;&amp;amp;&lt;/em&gt; are part of Mathematica’s notation for &lt;a class="link" href="http://reference.wolfram.com/mathematica/tutorial/PureFunctions.html" target="_blank" rel="noopener"
&gt;pure functions&lt;/a&gt;.) Or, more succinctly,&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_19.gif"
loading="lazy"
alt="alovestory_19.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_20.gif"
loading="lazy"
alt="alovestory_20.gif"
&gt;&lt;/p&gt;
&lt;p&gt;In a non-functional language, this would have required a for-loop and several lines of code. In a functional language, it naturally fits into a single line. Getting the total tardiness adds no more complexity:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_21.gif"
loading="lazy"
alt="alovestory_21.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_22.gif"
loading="lazy"
alt="alovestory_22.gif"
&gt;&lt;/p&gt;
&lt;p&gt;since % returns the last line evaluated.&lt;/p&gt;
&lt;p&gt;This is only a simple example of a functional operation in Mathematica. Expressions can become much more complex. All my Mathematica code is littered with functional expressions, but rarely will you see a for-loop or a while-loop in my code. And I like it that way.&lt;/p&gt;
&lt;p&gt;Oh, and if you want to parallelize these operations: &lt;a class="link" href="https://reference.wolfram.com/language/guide/ParallelComputing.html" target="_blank" rel="noopener"
&gt;not a problem&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="3-optimization"&gt;3. Optimization
&lt;/h2&gt;&lt;p&gt;As a student of operations research, I spend a lot of time solving optimization problems. Solving optimization problems of many flavors is built right into Mathematica. Solving linear programs given the matrices is easy with the LinearProgramming function. Because most of the problems I’ve solved up to this point have been “toy” problems for class, I can’t attest to Mathematica’s ability to handle large-scale problems, but they &lt;a class="link" href="http://reference.wolfram.com/mathematica/tutorial/ConstrainedOptimizationLinearProgramming.html" target="_blank" rel="noopener"
&gt;claim&lt;/a&gt; to be able to handle large problems. Mathematica’s ExampleData function gives easy access to many data sets, including &lt;a class="link" href="http://www.netlib.org/lp/data/readme" target="_blank" rel="noopener"
&gt;NetLib.org&lt;/a&gt;’s LP problems. Mathematica could solve this problem with 6072 rows and 12230 columns in 60 seconds on my 11-inch Macbook Air.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_23.gif"
loading="lazy"
alt="alovestory_23.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_24.gif"
loading="lazy"
alt="alovestory_24.gif"
&gt;&lt;/p&gt;
&lt;p&gt;The built-in solver certainly isn’t as robust as CPLEX or other commercial solver, but it does, at least, provide &lt;a class="link" href="http://reference.wolfram.com/mathematica/tutorial/ConstrainedOptimizationLinearProgramming.html" target="_blank" rel="noopener"
&gt;several solution methods&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I most often find myself using the Minimize and Maximize functions with explicit constraints:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_25.gif"
loading="lazy"
alt="alovestory_25.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_26.gif"
loading="lazy"
alt="alovestory_26.gif"
&gt;&lt;/p&gt;
&lt;p&gt;In the future, I hope to do a post on using Mathematica as a pseudo-modeling language. You can see the &lt;a class="link" href="http://reference.wolfram.com/mathematica/guide/Optimization.html" target="_blank" rel="noopener"
&gt;documentation&lt;/a&gt; for a number of other optimization related functions.&lt;/p&gt;
&lt;p&gt;Recently, I’ve been working with stochastic dynamic programming problems (i.e. Markov decision processes). Mathematica offers &lt;a class="link" href="http://reference.wolfram.com/mathematica/tutorial/FunctionsThatRememberValuesTheyHaveFound.html" target="_blank" rel="noopener"
&gt;the easiest memoization I’ve ever seen in any language&lt;/a&gt;. Combined with functional aspects, I can solve dynamic programs with relatively little code.&lt;/p&gt;
&lt;h2 id="4-graphics"&gt;4. Graphics
&lt;/h2&gt;&lt;p&gt;When I am doing school work, I want to be able to do complicated computations and then visualize the results quickly. Because of how tightly knit the native Mathematica graphics are built into the core language, I don’t have to go out of my way to do this.&lt;/p&gt;
&lt;p&gt;Last semester, I wanted to demonstrate a Monte Carlo algorithm for navigating a maze. Over 200 iterations, a relatively simple solver (built, of course, in Mathematica) could find an optimal path through a 4 by 3 maze. for a report I was writing.&lt;/p&gt;
&lt;p&gt;Working straight from the output of the solver, in about twenty lines of code, I output a grid showing candidate solution every other iteration (the green cells indicate cells where the action is optimal). Followed by an Export function, the graphic was ready to be included in my &lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_27.gif"
loading="lazy"
alt="alovestory_27.gif"
&gt; file. All of this without having to open another program or import any graphics packages.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://tdhopper.com/uploads/2012/02/maze.png" &gt;&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/maze-249x300.png"&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="5-documentation"&gt;5. Documentation
&lt;/h2&gt;&lt;p&gt;Wolfram has been careful to write readable and thorough documentation for Mathematica. Though Mathematica is not free software, its 10,000+ pages of documentation are &lt;a class="link" href="http://reference.wolfram.com/mathematica/guide/Mathematica.html" target="_blank" rel="noopener"
&gt;available online&lt;/a&gt;. Not only does the documentation for every function (usually including bullet points with Basic Examples, Scope, Generalizations &amp;amp; Extensions, Applications, Properties &amp;amp; Relations, and Neat Examples), it’s full of tutorials on various aspects of the language. Of you read the help inside of Mathematica, the files are simply notebooks, so the code can be evaluated within the documentation. I think you’d be hard pressed to find a language with better documentation.&lt;/p&gt;
&lt;h2 id="6-naming-conventions"&gt;6. Naming Conventions
&lt;/h2&gt;&lt;p&gt;If Mathematica wins one debate hands down, its naming conventions. By their own &lt;a class="link" href="http://reference.wolfram.com/mathematica/tutorial/NamingConventions.html" target="_blank" rel="noopener"
&gt;standards&lt;/a&gt;, “As with most Mathematica functions, the names are usually complete English words, fully spelled out.” If you know the mathematical name for something, you can probably guess the Mathematica form.&lt;/p&gt;
&lt;p&gt;Stephen Wolfram wrote a &lt;a class="link" href="http://blog.wolfram.com/2008/01/10/ten-thousand-hours-of-design-reviews/" target="_blank" rel="noopener"
&gt;blog post&lt;/a&gt; a few years ago on his personal role in naming Mathematica functions. Perhaps not too different from his late friend Steve Jobs, Wolfram desires intense control of the finest details of his products.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I just realized that over the course of the decade during which were developing Mathematica 6—and accelerating greatly towards the end—I spent altogether about 10,000 hours doing what we call “design reviews” for Mathematica 6, trying to make all those new functions and pieces of functionality in Mathematica 6 be as clean and simple as possible, and all fit together.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think this has paid off.&lt;/p&gt;
&lt;p&gt;Some people would complain about a language such an enormous number of named expressions, but Wolfram (the man and the company) have been so careful in constructing it that it doesn’t feel bloated.&lt;/p&gt;
&lt;h2 id="7-interactivity"&gt;7. Interactivity
&lt;/h2&gt;&lt;p&gt;In version 7, Wolfram introduced interactivity into Mathematica. The Manipulate function is one I have found extremely valuable. It allows you to parametrize an expression and adjust the parameters while seeing results in real-time. For example, you could use Manipulate to adjust the region over which a function is plotted:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_28.gif"
loading="lazy"
alt="alovestory_28.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_29.gif"
loading="lazy"
alt="alovestory_29.gif"
&gt;&lt;/p&gt;
&lt;p&gt;A side benefit to the careful construction of the language is that functions with related behavior often have interchangeable expression lists. Manipulate can be replaced with Animate with no other changes.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_30.gif"
loading="lazy"
alt="alovestory_30.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/Manip.gif"
loading="lazy"
alt="alovestory_29.gif"
&gt;&lt;/p&gt;
&lt;p&gt;Or Table for that matter:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_32.gif"
loading="lazy"
alt="alovestory_32.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_33.gif"
loading="lazy"
alt="alovestory_33.gif"
&gt;&lt;/p&gt;
&lt;h2 id="8-continual-development"&gt;8. Continual Development
&lt;/h2&gt;&lt;p&gt;Thankfully, Wolfram hasn’t given up on Mathematica. It’s been in development now for nearly 24 years. Mathematica 7 (released in November 2008) introduced interactivity features, access to many data sets, and built-in parallel computing, among &lt;a class="link" href="http://www.wolfram.com/products/mathematica/newin7/" target="_blank" rel="noopener"
&gt;other things&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Mathematica 8, released in November 2010, brought integration with Wolfram Alpha and free form input. I find myself using this frequently when I’m teaching calculus. For example:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_34.gif"
loading="lazy"
alt="alovestory_34.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_35.gif"
loading="lazy"
alt="alovestory_35.gif"
&gt;&lt;/p&gt;
&lt;p&gt;Mathematica 8 also brought incredible probability computations. What’s the probability that a standard normal random variable is less than a Uniform(0,1) random variable? No problem.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_36.gif"
loading="lazy"
alt="alovestory_36.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_37.gif"
loading="lazy"
alt="alovestory_37.gif"
&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a class="link" href="http://www.wolfram.com/mathematica/new-in-8/" target="_blank" rel="noopener"
&gt;list of things new in version 8&lt;/a&gt; goes on.&lt;/p&gt;
&lt;h2 id="9-comprehensiveness"&gt;9. Comprehensiveness
&lt;/h2&gt;&lt;p&gt;A feature of Mathematica that is hard to articulate is the comprehensiveness of the features I’ve already mentioned plus many more. It’s a full featured programming language, but the core language also extends to the depths of applied and pure mathematics. Symbolic manipulation? Check. Numerical methods? Check. &lt;a class="link" href="http://www.wolfram.com/mathematica/new-in-8/group-theory-algorithms/" target="_blank" rel="noopener"
&gt;Abstract algebra&lt;/a&gt;? Check. &lt;a class="link" href="http://reference.wolfram.com/mathematica/guide/GraphsAndNetworks.html" target="_blank" rel="noopener"
&gt;Graph theory&lt;/a&gt;? &lt;a class="link" href="http://reference.wolfram.com/mathematica/tutorial/BasicStatistics.html" target="_blank" rel="noopener"
&gt;Statistics&lt;/a&gt;? &lt;a class="link" href="http://reference.wolfram.com/mathematica/howto/CustomizePlotsAndGraphics.html" target="_blank" rel="noopener"
&gt;Visualization&lt;/a&gt;? &lt;a class="link" href="http://reference.wolfram.com/mathematica/guide/Optimization.html" target="_blank" rel="noopener"
&gt;Optimization&lt;/a&gt;? &lt;a class="link" href="http://reference.wolfram.com/mathematica/guide/ProcessingTextualData.html" target="_blank" rel="noopener"
&gt;String Processing&lt;/a&gt;? &lt;a class="link" href="http://reference.wolfram.com/mathematica/guide/DifferentialEquations.html" target="_blank" rel="noopener"
&gt;Differential equations&lt;/a&gt;? &lt;a class="link" href="http://reference.wolfram.com/mathematica/ref/ChemicalData.html" target="_blank" rel="noopener"
&gt;Computational chemistry&lt;/a&gt;? &lt;a class="link" href="http://reference.wolfram.com/mathematica/guide/Calculus.html" target="_blank" rel="noopener"
&gt;Calculus&lt;/a&gt;? Check. Check. Check.&lt;/p&gt;
&lt;p&gt;The comprehensiveness of &lt;em&gt;Mathematica&lt;/em&gt;&amp;rsquo;s functionality along with dynamic typing and functional programming allows me to write code to do complicated tasks very quickly. I love it.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;No doubt, Mathematica has its limitations&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;: Worlds worst undo. Not object-oriented. Closed platform. Expensive. No autosave. No data frame structure.&lt;/p&gt;
&lt;p&gt;However, for me, it’s an invaluable tool. Last semester, I saw a less computer savvy fellow graduate student writing out a huge table by hand. I don’t recall the name of what he was doing, but it was something to do with measuring the distance between permutations. I told him I could do it for him in a single line of Mathematica.&lt;/p&gt;
&lt;p&gt;In just a few minutes I wrote him the following code. It ended up taking me more than one line, but I wrote the code much faster than he was generating it by hand. (His table was actually for the 4-permutation case, so it was 24x24 instead of 6x6.)&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/alovestory_38.gif"
loading="lazy"
alt="alovestory_38.gif"
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://tdhopper.com/uploads/2012/02/Screen-Shot-2012-02-10-at-10.55.19-AM.png"
loading="lazy"
alt="alovestory_35.gif"
&gt;&lt;/p&gt;
&lt;p&gt;Using Mathematica for little things like this. It allows me to spend my time and brain power on the things that computers can’t handle.&lt;/p&gt;
&lt;p&gt;I love Mathematica. And maybe you will too.&lt;/p&gt;
&lt;p&gt;(I wrote this post in Mathematica. You can check out the &lt;a class="link" href="https://github.com/tdhopper/Mathematica-A-Love-Story" target="_blank" rel="noopener"
&gt;notebook&lt;/a&gt; here.)&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I might follow with a post on that very point&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Mathematicians, Engineers, and Businessmen on NPC Problems</title><link>https://tdhopper.com/blog/mathematicians-engineers-and-businessmen-on-npc-problems/</link><pubDate>Thu, 02 Feb 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/mathematicians-engineers-and-businessmen-on-npc-problems/</guid><description>&lt;p&gt;I assume most people who are nerdy enough to read this blog are nerdy enough to know about the &lt;a class="link" href="http://www.claymath.org/millennium/P_vs_NP/" target="_blank" rel="noopener"
&gt;$\mathcal{P}$ vs $\mathcal{NP}$ problem&lt;/a&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;. I first learned about this problem taking computer science classes in college, and it all seemed very theoretical at the time. Now that I study operations research, the problem is very real. Operations researchers are often limited in their pursuits by the challenges of $\mathcal{NP}$-hard &lt;a class="link" href="http://en.wikipedia.org/wiki/Integer_program" target="_blank" rel="noopener"
&gt;problems&lt;/a&gt;, and many operations researchers spend their careers trying to solve hard problems. Last semester, I came across &lt;a class="link" href="http://www.core.ucl.ac.be/~wolsey/" target="_blank" rel="noopener"
&gt;Laurence Wolsey&lt;/a&gt;&amp;rsquo;s beautiful description of how various people might view this problem. (Of course, the inimitable Randall Munroe has offered &lt;a class="link" href="http://xkcd.com/664" target="_blank" rel="noopener"
&gt;a similar look&lt;/a&gt; at the issue.)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A &lt;em&gt;pessimist&lt;/em&gt; might say that as most problems appear to be hard (i.e., their decision version lies in $\mathcal{NPC}$), we have no hope of solving instances of large size (because in the worst case we cannot hope to do better than enumeration), and so we should give up. A &lt;em&gt;mathematician (optimist)&lt;/em&gt; might set out to become famous by proving that $\mathcal{P=NP}$. A &lt;em&gt;mathematician (pessimist)&lt;/em&gt; might set out to become famous by proving that $\mathcal{P\neq NP}$. A &lt;em&gt;mathematician (thoughtful)&lt;/em&gt; might decide to ask a different question: Can I find an algorithm that is guaranteed to find a solution &amp;ldquo;close to optimal&amp;rdquo; in polynomial time in all cases&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;. A &lt;em&gt;probabilist (thoughtful)&lt;/em&gt; might also ask a different question: Can I find an algorithm that runs in polynomial time with high probability and that is guaranteed to find an optimal or &amp;ldquo;close to optimal&amp;rdquo; solution with high probability? An &lt;em&gt;engineer&lt;/em&gt; would start looking for a heuristic algorithm that produces practically usable solutions. Your &lt;em&gt;boss&lt;/em&gt; might say: I don&amp;rsquo;t care a damn about integer programming theory. You just worry about our scheduling problem. Give me a feasible production schedule for tomorrow in which William Brown and Daughters&amp;rsquo; order is out of the door by 4 P.M. A &lt;em&gt;struggling professor&lt;/em&gt; might say: Great. Previously I was trying to develop one algorithm to solve all integer programs, and publishing one paper every two years explaining why I was not succeeding. Now I know that I might as well study each $\mathcal{NP}$ problem individually. As there are thousands of them, I should be able to write twenty papers a year. Needless to say they are all right. There is no easy and rapid solution, but the problems will not go away, and more and more fascinating and important practical problems are being formulated as integer programs. So in spite of the $\mathcal{NP}$-completeness theory, using an appropriate combination of theory, algorithms, experience, and intensive calculation, verifiably good solutions for large instances can and must be found&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;If that&amp;rsquo;s not the case, you might check out this page: &lt;a class="link" href="http://simple.wikipedia.org/wiki/P_versus_NP" target="_blank" rel="noopener"
&gt;http://simple.wikipedia.org/wiki/P_versus_NP&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;See &lt;a class="link" href="http://en.wikipedia.org/wiki/Polynomial_approximation_scheme" target="_blank" rel="noopener"
&gt;http://en.wikipedia.org/wiki/Polynomial_approximation_scheme&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;From &lt;a class="link" href="http://books.google.com/books/about/Integer_programming.html?id=x7RvQgAACAAJ" target="_blank" rel="noopener"
&gt;Integer Programming&lt;/a&gt;, Page 87.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>The Efficient Research Hypothesis</title><link>https://tdhopper.com/blog/the-efficient-research-hypothesis/</link><pubDate>Fri, 27 Jan 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/the-efficient-research-hypothesis/</guid><description>&lt;img src="https://tdhopper.com/images/money.png" alt="Featured image of post The Efficient Research Hypothesis" /&gt;&lt;p&gt;In economics, some have asserted the efficient market hypothesis. The idea is that market prices take into account all the information currently available. If the efficient market hypothesis holds, an investor couldn&amp;rsquo;t consistently beat the market because his knowledge about the markets is no better than anyone else&amp;rsquo;s. An accompanying &amp;ldquo;joke&amp;rdquo; is that an economist would never bend over to pick up a $20 bill on the street. If there was really $20 to be taken, someone would have taken it already.&lt;/p&gt;
&lt;p&gt;As I&amp;rsquo;m moving towards research and away from classes in my PhD program, I sometimes find myself believing the efficient research hypothesis: if an idea I have is good and correct, someone must have had it already. I have the same temptation outside of school. On occasion, I consider an idea for a website or computer program but then decide that if it were really a good idea, someone would have had it already. This can&amp;rsquo;t always be right. I don&amp;rsquo;t know if markets are efficient or not; I&amp;rsquo;m neither an economist nor an investor. But the progression of knowledge certainly is not efficient. New things are always available to be learned and studied, and these things aren&amp;rsquo;t always obvious from the information currently available. Research (and other projects) requires more than just thinking of a good idea; it requires sweat and elbow grease.&lt;/p&gt;</description></item><item><title>From Nuclear Weapons to Operations Research</title><link>https://tdhopper.com/blog/from-nuclear-weapons-to-operations-research/</link><pubDate>Wed, 25 Jan 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/from-nuclear-weapons-to-operations-research/</guid><description>&lt;img src="https://tdhopper.com/images/tanker.png" alt="Featured image of post From Nuclear Weapons to Operations Research" /&gt;&lt;p&gt;In 1953, the eminent mathematician John Von Neumann (as they say, &amp;ldquo;Most mathematicians prove what they can, von Neumann proves what he wants&amp;rdquo;) wrote a letter to T. V. Moore of Standard Oil. Apparently, Moore had written Von Neumann about an operations problem he had. Eighteen tankers would transport fuel from La Salina to Las Piedras to Aruba. Moore wanted to &amp;ldquo;determine the economic value of increasing the number of berths for the loading of these tankers in La Salina from 3 to 4 or to 5.&amp;rdquo;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;However, due to &amp;ldquo;weather, conditions in the ports of call, etc&amp;hellip; the comings and goings of the tankers are described in statistical terms only.&amp;rdquo; Significant uncertainty existed in the time between ports and in loading and unloading times. Because of the complexity added by these uncertainties, Von Neumann speculated that the system would be very difficult to describe analytically. A few years earlier, in 1946, Von Neumann and Stanislaw Ulam worked to solve the radiation shielding problem. And, &amp;ldquo;Despite having most of the necessary data&amp;hellip; the problem could not be solved with analytical calculations.&amp;rdquo;&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;In this similarly probabilistic system, Ulam and Von Neumann decided to approximate the results they needed instead by repeatedly generating possible outcomes based on statistical manipulations and a (pseudo) random number generator. Their code name for this project was &amp;ldquo;Monto Carlo.&amp;rdquo; Von Neumann suggested that Standard Oil could build a &lt;em&gt;probabilistic&lt;/em&gt; model of the tanker problem. For example, a trip from La Salina to Las Piedras might take 8 hours with a probability of .8 (good weather conditions), 10 hours with a probability of .15 (moderate weather), and 15 hours with a probability of 0.05 (poor weather). Based on this model and given a table of random numbers, he could simulate possible outcomes of a trip that (roughly) match reality.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The procedure would have to be somewhat like this: Represent each tanker by some form of record, e.g., by a punch card, showing its exit time from La Salina&amp;hellip;. Program calculations which will develop the further history of this tanker, always deriving those quantities which depend on chance&amp;hellip;, with the use of suitable tables of random numbers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Of course, Moore would not want to generate just one possible trip. But if the process was done repeatedly, the average benefit of having some number of berths in the simulation would be an approximation of the real benefit:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It is thus possible to trace the history of as many days of operation as desired (say, a few years). One can then work out the behavior of sufficiently large samples for any assumed number of berths and thereby get an evaluation of the economic significance of any particular arrangement (i.e. any particular number of berths).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The same method Ulam and Von Neumann used to understand radioactive particles (which behave probabilistically) could be used to model full shipments. Fortunately, Monte Carlo simulation now requires neither punch cards or random number tables. Von Neumann&amp;rsquo;s punch card method would quickly be replaced by computer programs and &lt;a class="link" href="http://www.codeproject.com/Articles/25172/Simple-Random-Number-Generation" target="_blank" rel="noopener"
&gt;pseudo-random number generation algorithms&lt;/a&gt;. This early application of Monte Carlo methods to operations problems is one of many.&lt;/p&gt;
&lt;p&gt;I have recently been looking at online stochastic scheduling problems. In these problems, a system is asked to fulfill some scheduling requests while there is uncertainty (perhaps uncertainty about what other requests will come or how long the job might take to complete). A common approach to these problems is to solve &lt;em&gt;deterministic&lt;/em&gt; scheduling problems based on Monte Carlo simulations of the future. The system somehow combines these various schedules to make a decision. There are many &lt;a class="link" href="http://en.wikipedia.org/wiki/Monte_Carlo_method#Applications" target="_blank" rel="noopener"
&gt;applications of Monte Carlo methods&lt;/a&gt; in other fields as well.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;John von Neumann (2005). Miklós Rédei. ed. John von Neumann: Selected letters. History of Mathematics. 27. American Mathematical Society. p. 123. ISBN 0-8218-3776-1.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;&lt;a class="link" href="http://en.wikipedia.org/wiki/Monte_Carlo_method#History" target="_blank" rel="noopener"
&gt;http://en.wikipedia.org/wiki/Monte_Carlo_method#History&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>What do all operations researchers know?</title><link>https://tdhopper.com/blog/what-do-all-or-folks-know/</link><pubDate>Mon, 16 Jan 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/what-do-all-or-folks-know/</guid><description>&lt;p&gt;My friend &lt;a class="link" href="http://www.johndcook.com/blog/" target="_blank" rel="noopener"
&gt;John Cook&lt;/a&gt; asked me an interesting question recently:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you had a room full of people with a graduate degree in [operations research], what things would nearly everyone in the room know?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Operations research is notoriously hard to define. According to the Institute for Operations Research and Management Science, “In a nutshell, operations research (O.R.) is the discipline of applying advanced analytical methods to help make better decisions.” I suspect graduate programs spend most of their time teaching those “analytical methods,” i.e. mathematical and computational techniques for modeling and solving problems related to decisions. Examples include Received exception: &lt;a class="link" href="http://en.wikipedia.org/wiki/Linear_programming" target="_blank" rel="noopener"
&gt;linear programming&lt;/a&gt;, &lt;a class="link" href="http://en.wikipedia.org/wiki/Nonlinear_programming" target="_blank" rel="noopener"
&gt;nonlinear programming&lt;/a&gt;, &lt;a class="link" href="http://en.wikipedia.org/wiki/Integer_programming" target="_blank" rel="noopener"
&gt;integer programming&lt;/a&gt;, &lt;a class="link" href="http://en.wikipedia.org/wiki/Dynamic_programming" target="_blank" rel="noopener"
&gt;dynamic programming&lt;/a&gt;, &lt;a class="link" href="http://en.wikipedia.org/wiki/Stochastic_programming" target="_blank" rel="noopener"
&gt;stochastic programming&lt;/a&gt;, &lt;a class="link" href="http://en.wikipedia.org/wiki/Stochastic_models" target="_blank" rel="noopener"
&gt;stochastic models&lt;/a&gt;, &lt;a class="link" href="http://en.wikipedia.org/wiki/Queueing_theory" target="_blank" rel="noopener"
&gt;queueing theory&lt;/a&gt;, &lt;a class="link" href="http://en.wikipedia.org/wiki/Game_theory" target="_blank" rel="noopener"
&gt;game theory&lt;/a&gt;, and &lt;a class="link" href="http://en.wikipedia.org/wiki/Simulation" target="_blank" rel="noopener"
&gt;simulation&lt;/a&gt;. The &lt;a class="link" href="http://www.or.ncsu.edu/academics/msc.php" target="_blank" rel="noopener"
&gt;course requirements&lt;/a&gt; for OR PhD students at my university provide an upper bound for this problem: the only courses everyone must take are linear programming, nonlinear programming, and stochastic modeling. Some topics are surprisingly optional; in particular: simulation, statistics, integer programming/combinatorial optimization. John suggests that statistics PhD programs are similar. Topics diverge rather quickly after first year courses. Are all graduate programs like this? Is this a necessary evil (or evil at all)?&lt;/p&gt;</description></item><item><title>Carrots, Oatmeal, Operations Research</title><link>https://tdhopper.com/blog/carrots-oatmeal-operations-research/</link><pubDate>Mon, 09 Jan 2012 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/carrots-oatmeal-operations-research/</guid><description>&lt;p&gt;A former grad school classmate of mine lived largely off of oatmeal and carrots. Suppose he wanted to be sure to get 2000 calories/day, 60 milligrams of vitamin C per day, and no more than 40 grams of fat per day. How should he balance his intake of oatmeal and carrots while minimizing his expense at the grocery store? Suppose he eats \(S\) pounds of carrots per day and \(Q\) pounds of dry oatmeal. A pound of carrots contains 172 calories; a pound of oatmeal contains 1742 calories&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;. The total number of calories he gets in a day is \(172\cdot C+1732\cdot Q.\) To be sure he gets the adequate number of calories, he needs &lt;/p&gt;
$$172\cdot C+1732\cdot Q\geq 2000.$$&lt;p&gt; A pound of carrots contains 0.839 grams of fat, and a pound of oatmeal contains 25 grams of fat. For the total amount of fat has to be less than 40: &lt;/p&gt;
$$0.839\cdot C+25\cdot Q\leq 40.$$&lt;p&gt; A pound of carrots contains 19 milligrams of vitamin C, and a pound of oatmeal contains 15 milligrams of vitamin C. Since the total amount of vitamin C must be at least 60: &lt;/p&gt;
$$19\cdot C+15\cdot Q\geq 60.$$&lt;p&gt; If  you graph these three inequalities, you get the blue region shown below. Any point in the blue region represents a combination of carrots and oatmeal that would provide sufficient nutrients without giving too much fat. What we want to know is which combination is least expensive.&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2012/01/Whats-a-Stigler-Diet-Feasible1.gif"&gt;
&lt;/figure&gt;
&lt;p&gt;A pound of carrots and a pound of oatmeal both cost about $0.50. So, we want &lt;em&gt;to minimize&lt;/em&gt; the function $0.5\cdot C+0.5\cdot Q$ while still staying inside the blue region. We can write all of these things in a form that operations researchers call a &amp;ldquo;linear program.&amp;rdquo;&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
$$
\begin{align}\text{minimize }\\;\\; &amp; 0.5\cdot C+0.5\cdot Q\\\\
\text{subject to }\\;\\; &amp;172\cdot C+1742\cdot Q\geq 2000\\\\
&amp;0.839\cdot C+25\cdot Q\leq 40\\\\
&amp;19\cdot C+15\cdot Q\geq 60\\\\
&amp;C\geq 0, \\;Q\geq 0\end{align}
$$&lt;p&gt;It turns out, the optimal solution is that my friend should eat 2.44 pounds of carrots per day and 0.91 pounds of oatmeal. With that combination, he&amp;rsquo;ll get all his nutrients, restrict his fat, and keep his grocery store bill as low as possible (about $1.73 per day!). Below, I show another graph of the possible carrot-oatmeal combinations. This time, the color of a point represents the cost of that diet. The optimal diet occurs at the black dot, i.e. where the graph is most red. The most expensive diet occurs at the bottom right corner, where the graph is most blue. That diet corresponds to eating nearly 48 pounds of carrots per day!&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://tdhopper.com/uploads/2012/01/Whats-a-Stigler-Diet-Temp.gif" &gt;&lt;figure&gt;&lt;img src="https://tdhopper.com/uploads/2012/01/Whats-a-Stigler-Diet-Temp.gif"&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Operations researchers use this sort of linear programming mathematical model to solve all kinds of problems. While I don&amp;rsquo;t know of anyone who uses linear programming to fix their diet, the economist George Stigler suggested many years ago it is possible. I will follow up with another post talking about &lt;a class="link" href="http://en.wikipedia.org/wiki/Stigler_diet" target="_blank" rel="noopener"
&gt;Stigler&amp;rsquo;s Diet problem&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;See &lt;a class="link" href="http://www.wolframalpha.com/input/?i=pound&amp;#43;of&amp;#43;carrots%2C&amp;#43;pound&amp;#43;of&amp;#43;oatmeal" target="_blank" rel="noopener"
&gt;http://www.wolframalpha.com/input/?i=pound+of+carrots%2C+pound+of+oatmeal&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;&lt;em&gt;Program&lt;/em&gt; doesn&amp;rsquo;t refer to computer programming. It actually goes back to an older use of the word related to planning something out.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>👋 Hey, I'm Tim Hopper!</title><link>https://tdhopper.com/blog/welcome/</link><pubDate>Fri, 01 Jan 2010 00:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/welcome/</guid><description>&lt;h1 id="-hey-im-tim-hopper"&gt;👋 Hey, I&amp;rsquo;m Tim Hopper!
&lt;/h1&gt;&lt;p&gt;I&amp;rsquo;m an experienced machine learning platform engineer and Python developer. You can check out my resume at &lt;a class="link" href="https://resume.tdhopper.com" target="_blank" rel="noopener"
&gt;resume.tdhopper.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For over 10 years, I&amp;rsquo;ve helped companies solve business problems with machine learning in domains such as audio information retrieval, banking, cybersecurity, environmental science, and
weather forecasting. I see my role as helping data scientists and researchers shorten feedback loops and spend time on their business problems (instead of fussing with cloud resources).&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m also excited about developer productivity, especially in Python development. I am the author of an &lt;a class="link" href="https://pydevtools.com" target="_blank" rel="noopener"
&gt;ebook on Python developer tooling&lt;/a&gt; and like to help teams use Python more effectively.&lt;/p&gt;
&lt;p&gt;✍🏻 &lt;strong&gt;Writings&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://tdhopper.com" target="_blank" rel="noopener"
&gt;tdhopper.com&lt;/a&gt; has been a place for my thoughts and writings since grad school. If you&amp;rsquo;re new here, start with these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/blog/some-reflections-on-being-turned-down-for-a-lot-of-data-science-jobs/" target="_blank" rel="noopener"
&gt;Some Reflections on Being Turned Down for a Lot of Data Science Jobs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/blog/how-i-became-a-data-scientist/" target="_blank" rel="noopener"
&gt;How I Became a Data Scientist Despite Having Been a Math Major&lt;/a&gt; and &lt;a class="link" href="https://tdhopper.com/blog/faq/" target="_blank" rel="noopener"
&gt;A Subjective and Anecdotal FAQ on Becoming a Data Scientist&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/blog/goodnight-zoom/" target="_blank" rel="noopener"
&gt;Goodnight Zoom&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://tdhopper.com/blog/entropy-of-a-discrete-probability-distribution/" target="_blank" rel="noopener"
&gt;Entropy of a Discrete Probability Distribution&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;🖥️ &lt;strong&gt;Personal Projects&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;I have an &lt;a class="link" href="https://podcast.tdhopper.com" target="_blank" rel="noopener"
&gt;occasional podcast&lt;/a&gt; in which I talk to friends about things they&amp;rsquo;re interested in. You can find it in your podcast directory or at &lt;a class="link" href="https://podcast.tdhopper.com" target="_blank" rel="noopener"
&gt;podcast.tdhopper.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Years ago, I created &lt;a class="link" href="https://tdhopper.com/phd" target="_blank" rel="noopener"
&gt;Should I Get a Phd?&lt;/a&gt; where I interviewed nine friends about whether a young, bright student should consider pursuing a PhD. This is the resource I wish I&amp;rsquo;d had before starting a PhD program, and it&amp;rsquo;s been useful to many.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://pythonplot.com" target="_blank" rel="noopener"
&gt;Python Plotting for Exploratory Data Analysis&lt;/a&gt; is a Rosetta Stone for Python plotting libraries, and it also compares them to the GOAT of plotting libraries: ggplot.&lt;/p&gt;
&lt;p&gt;I created &lt;a class="link" href="https://dp.tdhopper.com" target="_blank" rel="noopener"
&gt;Notes on Dirichlet Processes&lt;/a&gt; after working on a &lt;a class="link" href="https://www.darpa.mil" target="_blank" rel="noopener"
&gt;DARPA&lt;/a&gt;-funded open source project for developing &lt;a class="link" href="http://datamicroscopes.github.io" target="_blank" rel="noopener"
&gt;Bayesian nonparametric models in Python&lt;/a&gt;. I did a lot of work to understand Bayesian nonparametrics and derive the Gibbs sampler for Hierarchical Dirichlet Processes. &lt;a class="link" href="https://dp.tdhopper.com" target="_blank" rel="noopener"
&gt;Notes on Dirichlet Processes&lt;/a&gt; shares what I learned for the benefit of others.&lt;/p&gt;
&lt;p&gt;I enjoy wildlife and nature photography in my free time. &lt;a class="link" href="https://photos.tdhopper.com" target="_blank" rel="noopener"
&gt;dothopper photo&lt;/a&gt; is my gallery.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://freespace.tdhopper.com" target="_blank" rel="noopener"
&gt;Free Disk Space&lt;/a&gt; is a little site I maintain with commands for freeing up disk space on your computer.&lt;/p&gt;
&lt;p&gt;⌨️ &lt;strong&gt;Open Source&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;I love to contribute to open source as I&amp;rsquo;m able. I&amp;rsquo;ve contributed to libraries like
&lt;a class="link" href="https://github.com/python/cpython/commits?author=tdhopper" target="_blank" rel="noopener"
&gt;cpython&lt;/a&gt;,
&lt;a class="link" href="https://datamicroscopes.github.io/" target="_blank" rel="noopener"
&gt;datamicroscopes&lt;/a&gt;,
&lt;a class="link" href="https://github.com/parsely/streamparse/commits?author=tdhopper" target="_blank" rel="noopener"
&gt;Streamparse&lt;/a&gt;,
&lt;a class="link" href="https://github.com/conda/conda/commits?author=tdhopper" target="_blank" rel="noopener"
&gt;Conda&lt;/a&gt;,
&lt;a class="link" href="https://github.com/ariddell/lda/commits?author=tdhopper" target="_blank" rel="noopener"
&gt;lda&lt;/a&gt;,
and
&lt;a class="link" href="https://github.com/pandas-dev/pandas/commits?author=tdhopper" target="_blank" rel="noopener"
&gt;Pandas&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;👨🏻‍💻 &lt;strong&gt;Social Media&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;You can find me on &lt;a class="link" href="https://twitter.com/tdhopper" target="_blank" rel="noopener"
&gt;Twitter&lt;/a&gt; and &lt;a class="link" href="https://www.linkedin.com/in/tdhopper/" target="_blank" rel="noopener"
&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;🗣️ &lt;strong&gt;Talks&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been speaking at conferences and meetups for many years. I keep a list of my recorded talks &lt;a class="link" href="http://tdhopper.com/talks/" target="_blank" rel="noopener"
&gt;here&lt;/a&gt;. If you&amp;rsquo;d like to get a taste of my talks, start with &lt;a class="link" href="https://www.youtube.com/watch?v=6flt_3yMNb0" target="_blank" rel="noopener"
&gt;Five semesters of linear algebra and all I do is solve Python dependency problems&lt;/a&gt; or &lt;a class="link" href="https://www.youtube.com/watch?v=OS6j1Y2tJLY" target="_blank" rel="noopener"
&gt;Challenges in Applying Machine Learning to Cybersecurity&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>On Ramsey Numbers for Quadrilaterals in 3 and 4 Colors.</title><link>https://tdhopper.com/blog/on-ramsey-numbers-for-quadrilaterals-in-3-and-4-colors./</link><pubDate>Mon, 07 Jan 2008 21:00:00 +0000</pubDate><guid>https://tdhopper.com/blog/on-ramsey-numbers-for-quadrilaterals-in-3-and-4-colors./</guid><description>&lt;img src="https://tdhopper.com/images/graph.png" alt="Featured image of post On Ramsey Numbers for Quadrilaterals in 3 and 4 Colors." /&gt;&lt;p&gt;Last summer, I had the privilege of serving as a student researcher in the &lt;a class="link" href="https://www.rit.edu/science/school-mathematics-and-statistics" target="_blank" rel="noopener"
&gt;Rochester Institute of Technology&lt;/a&gt;&amp;rsquo;s Research Experience for Undergraduates alongside &lt;a class="link" href="https://en.wikipedia.org/wiki/Stanis%C5%82aw_Radziszowski" target="_blank" rel="noopener"
&gt;Dr. Stanislaw Radziszowski&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Our research focused on computational combinatorics, specifically &lt;a class="link" href="https://mathworld.wolfram.com/RamseyNumber.html" target="_blank" rel="noopener"
&gt;Ramsey Numbers&lt;/a&gt;. My work focused on the Ramsey Number \(R_4(C_4)\), which is the smallest number \(n\) where, if you color all the edges of a complete graph with \(n\) vertices (called \(K_n\)) using four colors, you&amp;rsquo;ll always get a monochromatic \(C_4\) (a 4-cycle all in the same color). Before our work, the number was known to be 18 or 19. I wrote fast, distributed programs to generate \(C_4\)-free 4-colorings of \(K_{17}\) with the hope that an 18th node could be added while preserving the cycle-free property. Had we discovered such a graph, we would have proven the Ramsey number to be 18.&lt;/p&gt;
&lt;p&gt;This week, my research partner Evan Heidtmann and I presented our research at the &lt;a class="link" href="https://jointmathematicsmeetings.org/meetings/national/jmm/2109_program_monday.html" target="_blank" rel="noopener"
&gt;AMS-MAA-SIAM Special Session on Research in Mathematics by Undergraduates&lt;/a&gt; in San Diego. Here is our abstract:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We discuss the two multicolor Ramsey numbers concerning 4-cycles in 3 and 4 colors. For 3 colors, we find that there are exactly 1000 nonisomorphic critical colorings of \(K_{10}\) for the Ramsey number \(R_3(C_4)\) = 11, verifying our results using two independent computations. One of these colorings contains the Petersen graph as one of the colors and is more symmetric than all published colorings for this Ramsey number. In 4 colors, we were not able to improve the currently best known bounds \(18 ≤ R_4 (C_4) ≤ 19\), but we gather extensive computational evidence and then conjecture that no 4-coloring of \(K_{18}\) can avoid monochromatic \(C_4\)’s. We generate more than 28,000 nonisomorphic \(C_4\)-free 4-colorings of \(K_{17}\) (only 2 of which were previously published), but none can be extended to successfully color K18. Several searches, both heuristic and deterministic, also failed to produce a desired coloring. An exhaustive search seems to be extremely difficult computationally, even with all known constraints. We conjecture that \(R_4(C_4) = 18\).&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>More from Tim</title><link>https://tdhopper.com/more-from-tim/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://tdhopper.com/more-from-tim/</guid><description/></item><item><title>Search</title><link>https://tdhopper.com/search/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://tdhopper.com/search/</guid><description/></item></channel></rss>