[{"content":"Listen Links Brenton Mallen on LinkedIn Tim Hopper\u0026rsquo;s website Should I Get a PhD? Python Developer Tooling Handbook Project Hail Mary by Andy Weir It Takes Two (video game) Grove City College RTI International Kiva Systems (now Amazon Robotics) NC State Operations Research Encarta The Chronicles of Narnia by C.S. Lewis The Lord of the Rings by J.R.R. Tolkien Subscribe RSS Feed Apple Podcasts Spotify Overcast Summary In this special episode of Into the Hopper, 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\u0026rsquo;t really know the full Tim Hopper story.\nWe 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\u0026rsquo;s home in Tennessee, two PhD programs I started but didn\u0026rsquo;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.\nTranscript Brenton: So we don\u0026rsquo;t waste it—take one! Hi, welcome to Into the Hopper. I\u0026rsquo;m Brenton Mallen, and I\u0026rsquo;m here with my special guest, Tim Hopper. Thanks for having me.\nTim: Thanks for having me.\nBrenton: Well, you\u0026rsquo;re welcome. How\u0026rsquo;d I get here?\nTim: Beats me. I don\u0026rsquo;t know how we got here. Actually, maybe we\u0026rsquo;ll talk about that. What are we doing today?\nBrenton: For context, I had messaged you—I don\u0026rsquo;t even remember, it\u0026rsquo;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.\nTim: Very good.\nBrenton: As we started talking about doing this and I wrote down some stuff to talk about, I realized a couple of things. One, I\u0026rsquo;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\u0026rsquo;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\u0026rsquo;ve known you, I don\u0026rsquo;t know, 11 years now? Ten? Over a decade.\nTim: Just over 10, because we started working together in October of 2015.\nBrenton: Right. Was that PyData where we met?\nTim: We were already working together for a few weeks, and then we went to PyData.\nBrenton: I think I met you for the first time in person at PyData, yeah, definitely.\nTim: In New York.\nBrenton: PyData 2015.\nTim: At Capital One or no—I don\u0026rsquo;t know—the World Trade Center.\nBrenton: I realized I\u0026rsquo;ve quote-unquote \u0026ldquo;known\u0026rdquo; you for over a decade, but we talk decently regularly and I don\u0026rsquo;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\u0026rsquo;ll have a conversation.\nTim: Everyone will know the real Tim Hopper after this.\nBrenton: We\u0026rsquo;ll see—could be good, could be bad. Who knows? But then again, your data\u0026rsquo;s out there already.\nTim: Usually it\u0026rsquo;s selectively shared, but today whatever you ask, I\u0026rsquo;m obligated to answer.\nBrenton: 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\u0026rsquo;ve become.\nTim: Maybe we should explain first—we worked together at two different companies, actually, from 2015 to 2017 and then 2020 during early COVID until \u0026lsquo;21, right?\nBrenton: I think so. Something like that. My memory is not what it used to be.\nTim: It\u0026rsquo;s all right. I\u0026rsquo;m pretty sure I\u0026rsquo;m right about that.\nBrenton: As you look at a resume. But yeah, so let\u0026rsquo;s start there, I guess, at the very beginning. You grew up in West Virginia, right?\nGrowing Up in West Virginia Tim: 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.\nBrenton: Did you move from West Virginia to North Carolina when you were at school?\nTim: 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.\nBrenton: So what was West Virginia like?\nTim: It was great. My mom and I were just talking about this the other day. She said when we were younger, people would say, \u0026ldquo;Oh, you shouldn\u0026rsquo;t raise your kids there. You need them in a place where there\u0026rsquo;s opportunity and different things.\u0026rdquo;\nI\u0026rsquo;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\u0026rsquo;s only been in the last 10 years or so that I\u0026rsquo;ve started to tell people I grew up in rural West Virginia, because the town I grew up in was 6,000 people—I think it\u0026rsquo;s the 13th biggest town in West Virginia. We had Walmart. It wasn\u0026rsquo;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.\nBut West Virginia gets real rural real fast. In comparison, I didn\u0026rsquo;t grow up in the hollers, way off the beaten path. It\u0026rsquo;s stunningly beautiful. It just has been snowing here in Indiana, and I\u0026rsquo;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.\nI think it was a very safe place to grow up. People were very nice and welcoming. I think I had sufficient opportunities. I didn\u0026rsquo;t have the opportunities that a lot of kids have in bigger areas, but I didn\u0026rsquo;t have real complaints. I was satisfied with my education. I\u0026rsquo;m very glad I grew up there.\nBrenton: You were an only child?\nTim: No, I\u0026rsquo;m the youngest of four. I have three older sisters.\nBrenton: Did I know that?\nTim: I don\u0026rsquo;t know.\nBrenton: I feel like we\u0026rsquo;ve talked more about Maggie\u0026rsquo;s siblings.\nTim: Maggie has four sisters. I have three. So we have a lot of aunties in our family, as I like to say.\nBrenton: What was that like? For context, I\u0026rsquo;m the middle of three boys, which I imagine is just really different.\nTim: 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\u0026rsquo;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\u0026rsquo;s just different from anything I had with my sisters. But I don\u0026rsquo;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\u0026rsquo;t have more recess during school, that kind of thing. But I still have a good relationship with my sisters to this day. I\u0026rsquo;m very thankful for my upbringing and for my whole family.\nSchool, Sports, and Early Interests Brenton: What about going through school—grade school, friends, hobbies? What do kids do in West Virginia?\nTim: 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.\nAs a kid, I loved to play soccer—not basketball, as some people might be surprised to hear.\nBrenton: Are you longer in the legs or longer in the torso? I forget.\nTim: I\u0026rsquo;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.\nI kind of lost interest in sports going on. I\u0026rsquo;m not that competitive.\nBrenton: Sounds like you were more sporty back then, and now you\u0026rsquo;re less so.\nTim: I was never that good. I\u0026rsquo;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\u0026rsquo;t. I didn\u0026rsquo;t really work hard for sports.\nI started to play some instruments—piano with a private teacher and then some instruments in school—but never really liked to practice. Same problem.\nI\u0026rsquo;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.\nBrenton: We had a set of those.\nTim: 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.\nWe 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, \u0026ldquo;All right, this isn\u0026rsquo;t worth it for me.\u0026rdquo;\nIn 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 Encarta on it.\nBrenton: Okay.\nTim: 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 \u0026ldquo;Mary Had a Little Lamb\u0026rdquo; or something, there\u0026rsquo;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.\nI 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.\nEarly Programming and Lord of the Rings Websites Brenton: You said you ran a website—I think like a GeoCities thing or something.\nTim: 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 Lord of the Rings website. Actually, my AngelFire website you can still find—I probably shouldn\u0026rsquo;t link to it—but I made a Princess Bride fan site around 2000, \u0026lsquo;99. I was just copying content from other websites. I learned HTML by—in those days you didn\u0026rsquo;t have all this complex stuff—so I could just go open other websites and copy and paste the HTML.\nThen I started doing Lord of the Rings 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\u0026rsquo;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\u0026rsquo;t in tune with what was available through the internet.\nI was trying to find books to learn programming and bought a book on PHP. That was really where I first started programming, to make Lord of the Rings websites. I did that through high school. It was like 2001 to 2003 when the Lord of the Rings movies came out. I was in high school.\nBrenton: It was the perfect time.\nTim: I had already read the books and was a big fan. The main site was like TheOneRing.net or something.\nTim: Yeah, I won a big competition on TheOneRing.net and won this big Weta gift that was like worth hundreds of dollars. I was active on the Lord of the Rings 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\u0026rsquo;s where I really cut my teeth.\nReading and Sci-Fi Brenton: When did you first read Lord of the Rings? And what do you like to read—more fantasy?\nTim: My cousin gave me his copies of Lord of the Rings. I have two male cousins who are like six years older than me that I idolized—shout out Justin and Joseph if you\u0026rsquo;re listening. Joseph gave me copies of Lord of the Rings. I was pretty young, maybe like nine or ten, and they sat on my shelf for a long time.\nBrenton: Did you start with The Hobbit?\nTim: That\u0026rsquo;s a good question. I don\u0026rsquo;t know the answer. I finally got to reading Lord of the Rings around late middle school, like 2000-ish. I\u0026rsquo;ve never actually been a huge fantasy reader and I\u0026rsquo;m still not that much.\nMy mom read us Narnia as kids, so those were in my repertoire. These days I read some fantasy stuff to my kids. We\u0026rsquo;ve been reading The Chronicles of Prydain—Lloyd Alexander—they\u0026rsquo;re really old, from like 50 years ago. We\u0026rsquo;ve read the Narnia series. Eventually we\u0026rsquo;ll read Lord of the Rings. We\u0026rsquo;ve read The Hobbit.\nI\u0026rsquo;ve not been a big fantasy person. I do enjoy sci-fi, usually either hard sci-fi or kind of fluff sci-fi that\u0026rsquo;s really easy. I like the hard sci-fi for the science part—like Andy Weir stuff.\nBrenton: That kind of stuff.\nTim: Oh yeah, I love Andy Weir. Project Hail Mary is one of my favorites.\nBrenton: I\u0026rsquo;ve read that like seven or eight times now.\nTim: I should read it again.\nBrenton: The audio book is really good with the way the alien talks.\nTim: 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\u0026rsquo;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\u0026rsquo;t have a lot of time for reading these days.\nCollege: From Computer Engineering to Math Brenton: So Lord of the Rings websites—that\u0026rsquo;s early 2000s, you\u0026rsquo;re graduating high school in 2004?\nTim: 2004, yeah.\nBrenton: I was 2005.\nTim: The day Ronald Reagan died—graduated from high school.\nBrenton: Was he giving the commencement speech?\nTim: He wasn\u0026rsquo;t.\nBrenton: So then school after that—did you know where you wanted to go? Did you know what you wanted to do?\nTim: I thought in high school that I wanted to study computer engineering. I don\u0026rsquo;t think I really knew what computer engineering was, and I didn\u0026rsquo;t really know how to learn what it was. I just thought computers were really cool.\nI don\u0026rsquo;t know that I even knew—I mean, I had heard of computer science, but I don\u0026rsquo;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\u0026rsquo;t seem that interesting to me. I didn\u0026rsquo;t love programming, even though it was something I wanted to learn and do.\nBrenton: Did your parents try to influence you into a certain direction?\nTim: That\u0026rsquo;s a good question.\nBrenton: Like, no one asked you to be a doctor?\nTim: No. My dad\u0026rsquo;s a doctor, my mom\u0026rsquo;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\u0026rsquo;t think they necessarily pressured me a lot.\nI just didn\u0026rsquo;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.\nI was looking at Virginia Tech, which was fairly near where I grew up and obviously a very reputable school. But I\u0026rsquo;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 Grove City College in western Pennsylvania. It\u0026rsquo;s a Christian college that happened to have computer engineering, which is pretty rare for smaller schools.\nI actually ended up visiting there, and without that much perspective, I was like, \u0026ldquo;Oh, this is great. I\u0026rsquo;ll go here.\u0026rdquo; 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\u0026rsquo;re supposed to go. So I applied early admission and then didn\u0026rsquo;t apply to any other colleges.\nIt 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.\nBut 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.\nIn hindsight, I enjoyed that because in high school math, besides geometry, math is so routine—you\u0026rsquo;re just learning the mechanics. In physics, you\u0026rsquo;re actually getting to solve problems.\nBrenton: It\u0026rsquo;s like applied math to a degree. It frames a problem you\u0026rsquo;re trying to solve or some observation.\nTim: 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, \u0026ldquo;I\u0026rsquo;ll become a physics professor, that makes sense.\u0026rdquo; I don\u0026rsquo;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, \u0026ldquo;Well, be careful about that.\u0026rdquo;\nBrenton: Which is the inspiration for Should You Get a PhD?\nTim: 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.\nTim: 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\u0026rsquo;re taking basic classes.\nTo 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\u0026rsquo;d finished calculus. My senior year, I took some college math classes at a nearby college.\nSo I was actually ahead—I had finished calculus basically before I got to college because I\u0026rsquo;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\u0026rsquo;t enjoy the labs and hands-on side of it. More and more, I was like, \u0026ldquo;I just really love math classes.\u0026rdquo;\nBut I was also enjoying—I\u0026rsquo;d taken a computer programming class my freshman year, a C++ class. Despite being very different from what I\u0026rsquo;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.\nMy sophomore year, I decided to change to computer science.\nBrenton: So you went in as a math major?\nTim: I applied as computer engineering, went in as physics, and then a year later changed to computer science.\nI continued to take some physics classes on the side and finished a physics minor. But sophomore year in computer science, you take theory of computing, which is basically a math class—you\u0026rsquo;re trying to prove things that are computable, doing Turing machines, NP problems, that kind of stuff.\nI 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\u0026rsquo;t just enthusiastic about programming or wanting to write programs necessarily, but I was interested in the problem-solving.\nSo as I was taking it and realizing it was a math class, I was like, \u0026ldquo;I should just really be a math major.\u0026rdquo;\nBrenton: Did you change again?\nTim: 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.\nBrenton: How\u0026rsquo;s your French now?\nTim: It\u0026rsquo;s horrible, and I still have nightmares about French class. I literally wake up in the night sweating that I\u0026rsquo;m missing a French test or something.\nLiberal Arts Education and History Brenton: 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?\nTim: Yeah. It\u0026rsquo;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.\nIt 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.\nBrenton: I feel like it\u0026rsquo;s something you gain an appreciation for over time.\nTim: Yeah, and I can\u0026rsquo;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.\nI 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\u0026rsquo;s normally just education majors. It helped me think a lot about the purpose of education.\nHis big thing was that the purpose of education is really \u0026ldquo;what\u0026rsquo;s the purpose of people?\u0026quot;—and our education should help shape people towards that.\nBrenton: Do you have anything you walked away from that you carry with you today?\nTim: Yeah, I think that point I just made. As you think about education—I have four children now—what you\u0026rsquo;re trying to get out of it\u0026hellip; 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\u0026rsquo;s much bigger than just \u0026ldquo;you go and work.\u0026rdquo;\nIt really helped me think of education—and thus parenting—as shaping humans. What\u0026rsquo;s the full picture of what we want our humans to be? For my kids\u0026rsquo; school, it\u0026rsquo;s not just about \u0026ldquo;is what they\u0026rsquo;re doing valuable?\u0026rdquo; The question is not necessarily \u0026ldquo;when are they ever going to use this?\u0026rdquo; but \u0026ldquo;is their mind being opened to be people who understand the world and are interested in the world and interested in people?\u0026rdquo; Hopefully having a bigger-orbed perspective on education.\nAnd recognizing that education doesn\u0026rsquo;t just mean schooling. I don\u0026rsquo;t know if it\u0026rsquo;s a real quote, but there\u0026rsquo;s an often-quoted Mark Twain line: \u0026ldquo;I don\u0026rsquo;t let my schooling interfere with my education.\u0026rdquo; The conversations you have with your kid for 20 minutes before bedtime can be just as much of their education as their math lessons.\nBrenton: In D\u0026amp;D, it\u0026rsquo;s like the difference between wisdom and intelligence.\nTim: Yeah, I think that\u0026rsquo;s a real thing.\nBrenton: We\u0026rsquo;ll come back to the kids and parenting—I\u0026rsquo;ve got some questions there I\u0026rsquo;d like to get more of your perspective on.\nTim: 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 \u0026ldquo;what labor produces value that makes money\u0026rdquo; but just ideas.\nSome 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.\nI 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.\nBrenton: Do you have a particular period of history that you\u0026rsquo;re most interested in or find yourself studying more?\nTim: People can fast forward like 10 minutes if they want to get back to the interesting part of the podcast.\nBrenton: I\u0026rsquo;m here to have a conversation with you, so\u0026hellip;\nTim: I\u0026rsquo;m really interested in the history of science and history of math. It\u0026rsquo;s been less of something I\u0026rsquo;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, \u0026ldquo;Okay, it\u0026rsquo;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\u0026rsquo;s going to give me a job.\u0026rdquo;\nIn 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 Von Neumann, 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.\nBrenton: I think you\u0026rsquo;re a fan of Claude Shannon too?\nTim: 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\u0026rsquo;s really fascinating. And Feynman—I used to read Feynman\u0026rsquo;s books back in the day. That era really fascinated me.\nBut also earlier stuff—all the greats: Gauss, Euler, Fermat, Fourier. All of that 18th, 19th century stuff is really intriguing too.\nThese days, my interest is much more in American religious history. I\u0026rsquo;m interested in my own religious tradition, which is Presbyterianism and Presbyterian history in particular. But I\u0026rsquo;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?\nMy weird nerd habit is I love to drive around—I\u0026rsquo;m going to have to get some new skills here in Indiana—but in North Carolina, I\u0026rsquo;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.\nThat\u0026rsquo;s kind of my guilty pleasure hobby: just thinking about those ideas.\nBrenton: 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?\nTim: It\u0026rsquo;s very intertwined. Both dimensions influence each other. You definitely have the theological thing, but the theological influences aren\u0026rsquo;t necessarily separated from the cultural or immigration patterns.\nIf 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.\nThe 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\u0026rsquo;s really fascinating to think about why and how that happened.\nThat\u0026rsquo;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.\nBrenton: Just in time to leave.\nTim: Yeah, well, I was like, \u0026ldquo;I\u0026rsquo;m moving. I can still do it if you want.\u0026rdquo; I\u0026rsquo;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\u0026rsquo;s been my task of late—nobody knows the password to log in.\nMy family is part Presbyterian for 500 years. So there\u0026rsquo;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\u0026rsquo;t know. Piecing together their stories with American history is fascinating to me.\nAnyway, that\u0026rsquo;s a topic of interest to a limited audience, but there you go.\nGraduating and the Math Prize Brenton: All right, well then we\u0026rsquo;ll come back to the mainstream. So you finally decided to switch to being a math major. Did you graduate as a math major?\nTim: 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.\nActually, there was a cash prize for the highest average grade math major, and I shared it with another guy. I wouldn\u0026rsquo;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.\nBrenton: Well, like most things, it\u0026rsquo;s best shared, right?\nTim: Probably.\nBrenton: Being a math major, do you have opinions on brands of chalk, or chalk versus whiteboard?\nTim: That\u0026rsquo;s an interesting question because I\u0026rsquo;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, \u0026ldquo;What are you going to do when you need to write a note when you\u0026rsquo;re older?\u0026rdquo; 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\u0026rsquo;s all we ever do.\nBrenton: That\u0026rsquo;s what we call a thought leader.\nTim: Again, it\u0026rsquo;s really laziness. I still hate handwriting. I just had to sign documents to move, and they\u0026rsquo;re like, \u0026ldquo;Oh, well, it\u0026rsquo;d be good if your signature kind of spelled your name.\u0026rdquo;\nI do like, in theory, writing on a board—I know mathematicians are big about chalk—and I love teaching math. But I\u0026rsquo;ve never been that great at it, and I find it very tiring. I\u0026rsquo;ve never been into ordering chalk from Japan or something.\nWhen 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 REU program—Research Experience for Undergrads, an NSF-funded research program.\nThat was meaningful partially because I got there wanting to do a math program, and my advisor\u0026rsquo;s first question when I got there was, \u0026ldquo;How good are you at programming?\u0026rdquo;\nAll 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.\nThen 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\u0026rsquo;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.\nBrenton: Just for the way it looked, handwriting-wise and composition?\nTim: Yeah, and just making sure it\u0026rsquo;s not sloping down to the side and that kind of thing. My handwriting\u0026rsquo;s not good, my spelling\u0026rsquo;s not good. It\u0026rsquo;s really not a good thing for me to be doing.\nGrad School: History of Math at UVA Brenton: So math major, then grad school. Was that something you wanted to do immediately, or were you convinced to do?\nTim: Ever since I thought \u0026ldquo;I\u0026rsquo;ll study physics in college and be a physics professor,\u0026rdquo; I was like, \u0026ldquo;Well, I\u0026rsquo;ll just go to grad school. I like school, I\u0026rsquo;m good at school.\u0026rdquo;\nBrenton: Was this still the mindset of going to be a professor?\nTim: Yeah, I think that\u0026rsquo;s really what was in my mind. For undergrads who really enjoy the experience—which I did—it\u0026rsquo;s very tempting to think your professors have the dream. They\u0026rsquo;re the pinnacle. So I was going to go to grad school for math.\nI considered a variety of things: math, logic in the philosophy department. I visited the University of Pittsburgh\u0026rsquo;s History and Philosophy of Math department. But then I applied and surprisingly got accepted to the University of Virginia\u0026rsquo;s History of Math program, which seems very esoteric. It\u0026rsquo;s only one professor and she\u0026rsquo;s very selective, so it was kind of surprising that I got selected. I probably shamed her by dropping out after a year.\nIt\u0026rsquo;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.\nI 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 children\u0026rsquo;s home in Tennessee, a Christian children\u0026rsquo;s home. We just went and did manual labor projects to help them—building trails and steps, cleaning out barns, that kind of stuff.\nI had a really good experience doing that. It\u0026rsquo;s a small children\u0026rsquo;s home for children in crisis situations, not of their own making—so not kids who have been particularly bad kids, but they\u0026rsquo;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.\nI 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\u0026rsquo;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\u0026rsquo;t necessarily grade level. So you\u0026rsquo;re just teaching them to what their ability is—math, history, English, reading.\nBrenton: That sounds really humbling.\nTim: It was, yeah. My uncle said a very helpful thing to me: \u0026ldquo;Tim, when you go to grad school, you\u0026rsquo;ll learn how to be a mathematician. But if you do this, I think you\u0026rsquo;ll learn how to be a man.\u0026rdquo;\nThere\u0026rsquo;s a lot to that. Even now being a parent, it was a really helpful experience—and very hard, very tiring. But I\u0026rsquo;m really glad I did that. It was in the Smoky Mountains in Tennessee, like a half mile from the Smoky Mountain National Park.\nBrenton: Do you keep in touch with any of them?\nTim: Some, off and on through the years. It\u0026rsquo;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.\nI\u0026rsquo;ve connected with a few of them on Facebook. They\u0026rsquo;re all like in their probably 30s now, which is kind of crazy. You freeze them in your mind as middle schoolers, but they\u0026rsquo;re grown-ups with kids and things.\nSo 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.\nBrenton: I was thinking like Latin or something.\nTim: Latin could be one of them. I wasn\u0026rsquo;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\u0026rsquo;t understand any spoken French. I know you\u0026rsquo;re speaking French, and that\u0026rsquo;s all I can tell.\nBrenton: My grad professor was from France.\nTim: 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.\nMy 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\u0026rsquo;t have the opportunity for at all. They just had more exposure to things.\nBut 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.\nBut it was still very hard. While I was there, I was kind of having something of an existential crisis around \u0026ldquo;what am I doing with my life?\u0026rdquo; and realizing I had just been going to grad school thinking \u0026ldquo;this is what I\u0026rsquo;m going to do.\u0026rdquo; I hadn\u0026rsquo;t really thought through the implications of it.\nI started to realize the history of math isn\u0026rsquo;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, \u0026ldquo;Okay, so I\u0026rsquo;m gonna do this and then maybe hopefully get to teach at some podunk university? Maybe that\u0026rsquo;s not the best use of my time.\u0026rdquo;\nDiscovering Operations Research Somewhere along—late in college—I discovered operations research as a discipline: using math and optimization models to solve business and industrial problems. I discovered that through Cornell\u0026rsquo;s program. While I was looking at grad schools, I found it and was like, \u0026ldquo;Oh, this sounds really interesting.\u0026rdquo; It matched a lot of how I think about things—optimizing things, efficiency, math, and computation all in one.\nI 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\u0026rsquo;s program at University of Tennessee—mostly just because I loved that area.\nFor 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.\nBrenton: Did you go straight into a PhD from undergrad, or did you do a master\u0026rsquo;s?\nTim: I didn\u0026rsquo;t do a master\u0026rsquo;s. In the US, a PhD program has you take the master\u0026rsquo;s coursework for the first two years, but I didn\u0026rsquo;t do an independent master\u0026rsquo;s ahead of that.\nSo I graduated from college in \u0026lsquo;08, went straight to work at the children\u0026rsquo;s home, one year later started at UVA as a PhD student. But for the first two years, you\u0026rsquo;re doing the coursework.\nTeaching Calculus Brenton: Was your teaching before you started the PhD program, or was that part of your deal there?\nTim: I was a TA my first year, but usually you don\u0026rsquo;t teach your first year—you\u0026rsquo;re an assistant to another grad student who\u0026rsquo;s teaching. I was a TA for calculus one and two, where you\u0026rsquo;re grading and having office hours but not actually lecturing.\nBut that summer, I was lecturing, not having ever done it before. Actually, there\u0026rsquo;s a funny story—to me anyway. I\u0026rsquo;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\u0026rsquo;s all we had. We didn\u0026rsquo;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\u0026rsquo;d never taken calc two, where you learn all these tricks for integration like trig substitution. I didn\u0026rsquo;t know any of that.\nBy the time I TA\u0026rsquo;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.\nBrenton: I think calculus did that.\nTim: But when I taught calc two, I learned. Teaching a week\u0026rsquo;s worth of material in a day is not only hard for the students—it\u0026rsquo;s hard for me.\nBrenton: Did you have to come up with the curriculum and lessons too?\nTim: Yeah, basically the grad students had all these PDFs of notes they would pass around to each other. I would get somebody\u0026rsquo;s notes, but you\u0026rsquo;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.\nBut I learned so much calculus standing at the board, writing in chalk, explaining it. I\u0026rsquo;m like, \u0026ldquo;Oh, that\u0026rsquo;s what that means\u0026rdquo;—quietly to myself so the kids don\u0026rsquo;t think I don\u0026rsquo;t know.\nBrenton: Quietly to yourself so the kids don\u0026rsquo;t think, you know\u0026hellip;\nTim: 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.\nNC State and Operations Research Tim: So I went to North Carolina State, started doing classes again. They didn\u0026rsquo;t really care that I had been in a PhD program for a year previously. I didn\u0026rsquo;t get a degree or anything from UVA.\nThe motivation for doing operations research was: okay, here\u0026rsquo;s interesting problem-solving but also something that seems like it has jobs related to it because it\u0026rsquo;s useful. I don\u0026rsquo;t know if that\u0026rsquo;s actually true for operations research, but at least I was learning useful skills.\nI 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\u0026rsquo;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\u0026rsquo;re not sitting in the waiting room for a long time?\nBrenton: Would that be from clinical trial data, trying to build out those models?\nTim: 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 simulation model of the oncology clinic.\nAnother 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.\nBrenton: Is that what brought about the PhD downfall with Six Sigma?\nTim: No, I ended up working on semi-theoretical problems. My advisor was an expert in something called stochastic integer linear programming.\nA big part of operations research is optimization problems where you have a mathematical formula you\u0026rsquo;re trying to optimize with mathematical constraints. You can describe business problems that way. But stochastic optimization says, \u0026ldquo;Oh, I\u0026rsquo;m not just optimizing these, but maybe my coefficients are random variables.\u0026rdquo; So I want to minimize the worst outcome or maximize the average or something—which is mathematically significantly more complicated.\nHe worked on a problem on patient scheduling. When you call your doctor and make an appointment, they\u0026rsquo;re not operating under certainty—they don\u0026rsquo;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 three patients. So if your clinic sees a total of three patients, you can get the optimal solution.\nBrenton: We call it the three-body problem.\nTim: So I worked on essentially an approximation solution using what\u0026rsquo;s now called reinforcement learning—computer scientists had always called it that, but in operations research they called it approximate dynamic programming. It\u0026rsquo;s essentially the same thing: you simulate possible outcomes and use that to figure out your optimization.\nWhich 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. Data science 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, \u0026ldquo;Oh, this is really interesting—they\u0026rsquo;re using data and computation and math to solve real problems.\u0026rdquo;\nThis machine learning stuff sounded really interesting. I didn\u0026rsquo;t have any exposure to that in college or before.\nBecause 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.\nThis 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.\nKiva Systems Internship Tim: That summer—after one year at NC State—I talked my way into an internship at Kiva Systems, which is now Amazon Robotics, doing warehouse automation. I read about it over Christmas break in Wired Magazine, 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.\nI cold-emailed him and was like, \u0026ldquo;Can I come work for you for the summer?\u0026rdquo; 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.\nThat gave me exposure to real-world applications.\nBrenton: Applications, yeah.\nTim: 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.\nSo 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\u0026rsquo;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.\nBrenton: That\u0026rsquo;s an accomplishment in and of itself.\nTim: Yeah. I\u0026rsquo;ve always been good at tests, which is a completely useless skill, but it helps in school.\nBrenton: I mean, some people say life is a test.\nTim: I don\u0026rsquo;t think taking math tests is a good life skill, but I am good at it.\nLeaving the PhD Program Tim: 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, \u0026ldquo;I\u0026rsquo;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.\u0026rdquo; He was going to have a guest appointment so he could continue to advise me.\nI was like, \u0026ldquo;Okay, that\u0026rsquo;s fine. I\u0026rsquo;ll do that.\u0026rdquo; 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\u0026rsquo;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.\nBy August, I was like, \u0026ldquo;He\u0026rsquo;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?\u0026rdquo; 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\u0026rsquo;m lucky. That\u0026rsquo;s just not a good situation.\nBrenton: You\u0026rsquo;re going to be there for all the learning and growing pains of trying to do that remotely.\nTim: 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\u0026rsquo;t have that many expenses, but that\u0026rsquo;s not a lot of money.\nSo 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\u0026rsquo;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 Bayesian networks class, like a machine learning class.\nAnother part of this story: over those two years of getting interested in data science stuff, I started teaching myself Python and R as well—which I learned about from Twitter. I did my research in Python, which my advisor didn\u0026rsquo;t know and he didn\u0026rsquo;t want me to do. He wanted me to do C++, which he knew. But I was like, \u0026ldquo;Oh, Python\u0026rsquo;s the up-and-coming thing, and it has this thing called scikit-learn where you can access all these machine learning models\u0026rdquo;—which we could use for our reinforcement learning. That was really good that I did that.\nBrenton: You\u0026rsquo;re very proactive.\nTim: I tried to be. I really think I just got lucky in so many ways.\nBrenton: Well, that\u0026rsquo;s not without effort, you know.\nTim: My effort was not always—I didn\u0026rsquo;t always know what I was doing. I don\u0026rsquo;t think it\u0026rsquo;s always good advice, like \u0026ldquo;just follow your interests and it\u0026rsquo;ll work out.\u0026rdquo; But for me, it did in a lot of ways.\nI 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\u0026rsquo;t offer me very much money.\nBrenton: But still more than grad school was paying.\nTim: Yeah. I also got offered a job at RTI International, 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.\nI think some executive had read an article in some business magazine about data science and thought, \u0026ldquo;We should hire a data scientist.\u0026rdquo; 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\u0026rsquo;t really know what they were hiring me for.\nThey 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.\nSo then I called my advisor. I was like, \u0026ldquo;Oh, I\u0026rsquo;m dropping out, by the way.\u0026rdquo;\nBrenton: How did he take that news?\nTim: I don\u0026rsquo;t think he was totally surprised. I think he was probably disappointed that I hadn\u0026rsquo;t led on to it anymore. But I just had to protect myself at that point.\nSo I finished up that semester, finished up the last class, and started working late October of 2012. I\u0026rsquo;ve been in industry ever since then.\nMeeting Maggie and Getting Married Brenton: What year was this?\nTim: 2012, yeah. October 2012.\nBrenton: When did you meet Maggie?\nTim: I didn\u0026rsquo;t meet Maggie until a few years later. We got married in August of 2015.\nBrenton: It was right before Distill then, right?\nTim: 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.\nBrenton: I didn\u0026rsquo;t realize those events were that close together.\nTim: 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, \u0026ldquo;The whole project is falling apart\u0026rdquo;—I worked on a DARPA project, government stuff.\nAt 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.\nBrenton: Well, you know, we all go through our ups and downs. So then—okay, Distill—and then I\u0026rsquo;m debating if we want to talk about your career, because\u0026hellip;\nTim: We can, it\u0026rsquo;s pretty public. It\u0026rsquo;s on my resume.\nBrenton: Sure. I\u0026rsquo;m more interested in terms of—at least for me, selfishly—I\u0026rsquo;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\u0026rsquo;s start there instead of going into career stuff. Life outside of work from that point.\nHobbies: Woodworking and Hiking Tim: One thing I discovered late in grad school: NC State has a place called the Craft Center. It\u0026rsquo;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 woodworking 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.\nMaybe my last semester I took a wood class. It\u0026rsquo;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.\nBrenton: What did you build there?\nTim: 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.\nI 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\u0026rsquo;s so precise but also—wood is very alive because of moisture things.\nBrenton: But it\u0026rsquo;s approachable.\nTim: Yeah. It makes you learn to appreciate the ability to press the undo button, because there\u0026rsquo;s no undo button in woodworking.\nI also got more into hiking in that era. I\u0026rsquo;d been in Boy Scouts, but I didn\u0026rsquo;t really love the outdoors when I was younger.\nBrenton: What didn\u0026rsquo;t you like about it back then?\nTim: That\u0026rsquo;s a good question. I just—it didn\u0026rsquo;t really\u0026hellip; 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\u0026rsquo;t need to blow off steam because I had a very stable home. School was very stable. I don\u0026rsquo;t think I needed to go blow off steam that much.\nBut 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.\nThat also became more of an opportunity as I had money to drive myself out to the mountains, stay in a hotel, and hike.\nBrenton: Speaking of driving, when did you get your Matrix?\nTim: I bought my Toyota Matrix 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, \u0026ldquo;I can\u0026rsquo;t afford to keep driving this.\u0026rdquo;\nSo I bought a Toyota Matrix with 107,000 miles on it that I still drive now with 260,000 miles on it. It\u0026rsquo;s my pride and joy. I recently joined a Toyota Matrix owner\u0026rsquo;s Facebook group.\nBrenton: 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\u0026rsquo;t drive it, so we just got something else.\nTim: Mine\u0026rsquo;s the automatic, and the transmission has been rock solid.\nBrenton: It\u0026rsquo;s such a good car. You can do all kinds of stuff—the seats go down and it\u0026rsquo;s got so much room.\nTim: I hauled 28 bags of mulch from Lowe\u0026rsquo;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\u0026rsquo;m going to keep driving it until I can\u0026rsquo;t.\nBrenton: 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.\nTim: Yeah. Also right as I was finishing grad school, I became more involved in my church. I became the treasurer 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.\nThose 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\u0026rsquo;t blame anyone for the bathroom or kitchen not being cleaned.\nI actually like to cook a lot too. I don\u0026rsquo;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.\nAnd 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.\nPhotography and Wildlife Tim: Photography also started interesting me more in that era too. My dad\u0026rsquo;s always been a big photographer, and I\u0026rsquo;d taken a photography class in high school but hadn\u0026rsquo;t really stuck.\nBrenton: Was it always nature photography in particular, or just photography in general?\nTim: 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.\nNorth 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, \u0026ldquo;How can I capture this?\u0026rdquo;\nBrenton: Neutral density filter?\nTim: Yeah, I started to learn more about the techniques and bought different cameras.\nIt 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, \u0026ldquo;You should get a nicer camera.\u0026rdquo;\nThat kind of kicked off my interest in wildlife photography, which is now—I don\u0026rsquo;t know, we haven\u0026rsquo;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.\nBut it\u0026rsquo;s still my love. I would love to do more wildlife stuff—even more than landscapes. I think wildlife is what I really love.\nBecoming a Parent Brenton: Speaking of wildlife and kids, let\u0026rsquo;s talk about becoming a parent. Did you always want to be a parent?\nTim: Yeah, I\u0026rsquo;ve always loved kids. In elementary school, my parents were foster parents. I didn\u0026rsquo;t have younger siblings, but my mom\u0026rsquo;s a pediatric nurse. So she was specifically a foster parent for babies with medical needs—that was her expertise.\nWe had, not for long term—they\u0026rsquo;re mostly short term—but we had 25 different foster kids in my elementary school years. Some as short as like a weekend, and some for maybe months at a time—but not years.\nMy mom was kind of the revolving door of child care for her friends—her friends\u0026rsquo; 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.\nBrenton: Do you think that might have influenced or motivated you to do the children\u0026rsquo;s home stuff?\nTim: Yeah, probably. Those were older kids—middle-school age—kind of a different demographic in a lot of ways, but I\u0026rsquo;m sure that was influential.\nI 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\u0026rsquo;s above average.\nMaggie and I got married in August of 2015.\nAnother kind of crazy story you probably don\u0026rsquo;t know: Maggie\u0026rsquo;s older sister had married one of my best friends from college two years earlier. That\u0026rsquo;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.\nWe 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.\nAnd 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\u0026rsquo;s crazy—you could not have planned it any better.\nIt\u0026rsquo;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\u0026rsquo;s very fun.\nBrenton: I\u0026rsquo;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?\nTim: I think one thing with my parents is I always knew that they loved and supported us and just had our backs. That\u0026rsquo;s a big thing we want our children to see—that we\u0026rsquo;re there for them.\nWe also want them to know we\u0026rsquo;re in charge. I\u0026rsquo;m not as much into some of the modern parenting that\u0026rsquo;s more like you\u0026rsquo;re chummy with your children. We really enjoy the time with them, but we are in charge. They hopefully really know that we\u0026rsquo;re supporting them.\nAs 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\u0026rsquo;re eight and six.\nWe\u0026rsquo;re trying to make education not just a really rote thing—\u0026ldquo;here you just have to follow these steps\u0026rdquo;—or just learning things that are useful in the sense of \u0026ldquo;you\u0026rsquo;re going to be able to get a job one day.\u0026rdquo; I think it\u0026rsquo;s good to be able to get a job—we\u0026rsquo;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.\nThere\u0026rsquo;s teaching your kids to just enjoy things. Liberal arts education—often that\u0026rsquo;s in literature and stuff, really enjoying it. For me, having studied math, I just intrinsically enjoy math. I think it\u0026rsquo;s really beautiful—people hear that and think \u0026ldquo;oh, arithmetic\u0026rdquo;—and arithmetic is in its own way—but math at a higher level is past arithmetic.\nI think math is intrinsically a beautiful thing, and we\u0026rsquo;re trying to teach the kids to embrace that beauty of things.\nBrenton: 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.\nTim: Yeah. Feynman said something like—to paraphrase—nothing\u0026rsquo;s boring enough if you go deep enough into it. I don\u0026rsquo;t know if that\u0026rsquo;s totally true, but there\u0026rsquo;s a lot of truth to that.\nYou and I both got into \u0026ldquo;data science\u0026rdquo; 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.\nI 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.\nWith all the hardships and evil in the world, there\u0026rsquo;s such a great world to live in. So many beautiful things.\nBrenton: There\u0026rsquo;s a Brandon Sanderson book, and there\u0026rsquo;s a quote I really liked. It\u0026rsquo;s something along the lines of: \u0026ldquo;It\u0026rsquo;s a difficult time to live. It doesn\u0026rsquo;t have to be a difficult time to love.\u0026rdquo;\nTim: Yeah. There\u0026rsquo;s a C.S. Lewis quote along the same lines—where he\u0026rsquo;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.\nI don\u0026rsquo;t know if that\u0026rsquo;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.\nWe both love our different hobbies. Hobbies are an opportunity to just enjoy good things—even if you\u0026rsquo;re not sharing it, even if nobody knows about it. You do all kinds of things in your own home that people don\u0026rsquo;t know about.\nBrenton: I think one of my favorite things about different hobbies is learning that things are complicated. Problems are easier when they\u0026rsquo;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.\nI 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\u0026rsquo;d say, \u0026ldquo;Okay, now you got to be brave and put this thing in.\u0026rdquo; I was laughing—what is this guy talking about, being brave while you\u0026rsquo;re painting? It\u0026rsquo;s just a painting.\nBut I\u0026rsquo;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.\nTim: Yeah. In my attempt to not just do things in life that I\u0026rsquo;m good at, I\u0026rsquo;ve learned as an adult to really work hard and push through things and just try things. That\u0026rsquo;s something I\u0026rsquo;m trying to instill in my kids.\nOne of them doesn\u0026rsquo;t like to do something if it\u0026rsquo;s not going to turn out well. I tell them about the Edison quote—\u0026ldquo;we\u0026rsquo;ve learned a thousand ways that don\u0026rsquo;t work.\u0026rdquo; That\u0026rsquo;s what I do in my job all day long—try things, see it doesn\u0026rsquo;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\u0026rsquo;m gonna get back to being lazy.\nBrenton: How do you think being a parent has impacted you?\nTim: I\u0026rsquo;d have to give that some reflection. One thing I\u0026rsquo;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\u0026rsquo;t watch an emotional scene in a movie the same way. Or any movie where a kid gets hurt—it\u0026rsquo;s just impossible to watch. You experience it differently when you don\u0026rsquo;t have kids.\nI think it keeps you young in some ways. Your kids are at the stage they\u0026rsquo;re at, and you can be an adult most of the time, but sometimes you can just sit down with a two-year-old.\nBrenton: You experience childhood vicariously, I guess.\nTim: Yeah. One of the cool things—all my kids have had this, and I\u0026rsquo;ve seen some people on Twitter mention it, I think it\u0026rsquo;s fairly universal but I don\u0026rsquo;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\u0026rsquo;s like, \u0026ldquo;Look, the moon, the moon!\u0026rdquo; Around two years and three months or so.\nThose kinds of things are such a good reminder—like, oh, this is crazy. There\u0026rsquo;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\u0026rsquo;s like, \u0026ldquo;Oh yeah, I should stop and appreciate that the moon is up there, appreciate the stars are up there.\u0026rdquo;\nBrenton: One of my favorite things about astronomy and astrophotography is it\u0026rsquo;s very humbling, I think.\nTim: 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.\nBrenton: Those have probably changed by now.\nTim: Well, I was hoping our new house would be perfect, but they recently installed some new street lights right behind us that I didn\u0026rsquo;t know were going to be there. It\u0026rsquo;s not as dark as I thought. But we\u0026rsquo;re kind of on the edge of Indianapolis, so I\u0026rsquo;m hoping we can get out a little bit more and start getting into some astronomy together.\nBrenton: Really fun, especially with the kids—everyone\u0026rsquo;s seen it for the first time.\nTim: Yeah. There are a lot of things that are just neat to—like reading to the kids. That\u0026rsquo;s been one of my favorite things as a parent. Some stuff I\u0026rsquo;ve been reading to them for the first time—that\u0026rsquo;s fun—but like getting to read The Chronicles of Narnia to them, which my mom read to us. Or reading The Hobbit to them.\nBrenton: Are you going to do Lord of the Rings and The Hobbit?\nTim: I\u0026rsquo;m a little torn on when to read The Lord of the Rings. There\u0026rsquo;s a Tolkien quote about how Lord of the Rings isn\u0026rsquo;t a children\u0026rsquo;s book, and you can only read it for the first time once, so don\u0026rsquo;t do it too soon. I would love to start it now, but I think I want to wait till they\u0026rsquo;re a little older.\nBut even then, just getting to think, \u0026ldquo;What other stuff can we read together?\u0026rdquo; My boys and I read most evenings before bed. Recently they\u0026rsquo;ve gotten into the Three Stooges—they sometimes beg me to watch the Three Stooges instead of reading to them. But that\u0026rsquo;s been really fun.\nAI and the Future Brenton: With the way things are—AI taking over the world for better or worse, the Pandora\u0026rsquo;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?\nTim: It\u0026rsquo;s a big question. I\u0026rsquo;ve thought about it some, and I really just don\u0026rsquo;t know the answers.\nThe 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\u0026rsquo;s usually like \u0026ldquo;make a baby riding a motorcycle on Mars\u0026rdquo; or something silly.\nWith my eight-year-old—he just has endless questions about things: animals, technology, TV shows. So we\u0026rsquo;ll ask the AI.\nBrenton: You had a World Book Encyclopedia to flip through.\nTim: Yeah, I should get him some—I try to get him a lot of reference-type books because he\u0026rsquo;s a lot like me and likes that kind of stuff.\nI\u0026rsquo;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 vibe code 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\u0026rsquo;m excited to show him something like that.\nBut at the same time, it seems to still be really valuable to learn the fundamentals. I don\u0026rsquo;t want to derail him from learning the fundamentals.\nBrenton: 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.\nTim: Yeah. I remember when I was teaching calculus, Wolfram Alpha had come out and was showing the steps on integration and derivation.\nI\u0026rsquo;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\u0026rsquo;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.\nWhich is awesome—I use these tools all day, every day, and it\u0026rsquo;s fantastic. But I\u0026rsquo;m so glad I was schooled in foundations in a lot of ways.\nBrenton: Similarly, I\u0026rsquo;m glad we grew up before social media and the internet kind of took over. I don\u0026rsquo;t know what it would be like to grow up with that just being in your face all the time.\nTim: Absolutely, yeah. We\u0026rsquo;re slow to introduce technology things to my kids.\nIt 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\u0026rsquo;re going to college. I don\u0026rsquo;t want to keep them from learning how to use those tools well.\nBut 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\u0026rsquo;m better at using them because of a lot of experience prior to using them—understanding Python really well and how things work.\nI 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.\nI honestly don\u0026rsquo;t know what all those answers look like. But it\u0026rsquo;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.\nBut it\u0026rsquo;s going to be harder and harder for that. Kids in public school are getting Chromebooks, and once you have Google Docs, you\u0026rsquo;re getting access to Gemini.\nBrenton: The cognitive offload is pretty real.\nTim: Absolutely. All of us are wrestling with that in our own jobs—what the implications are, what\u0026rsquo;s good and bad about it.\nEven just writing—I don\u0026rsquo;t think I\u0026rsquo;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.\nI honestly don\u0026rsquo;t know all the answers. I\u0026rsquo;ve been asking everybody on my podcasts about the future, and nobody really knows. I don\u0026rsquo;t know either.\nMoving to Indiana Brenton: 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?\nTim: We moved to Indiana to be closer to family—my wife\u0026rsquo;s family specifically. She grew up in the town we\u0026rsquo;re living in now.\nIn a lot of ways, we\u0026rsquo;re just trying to develop that. Neither of us have really lived that close to family as adults.\nBrenton: Especially being as close with family as you guys are.\nTim: With kids, our kids have not grown up spending a lot of time with family. Now they have a cousin who\u0026rsquo;s a mile away, which is a whole new experience. We\u0026rsquo;re hoping to lean into that and really make the most of it.\nMaggie 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.\nBrenton: I was going to say board games, but we know where that\u0026rsquo;s going to go.\nTim: I\u0026rsquo;m not a board game player. Our off-and-on activity has been playing Cat Quest on the Switch.\nBrenton: There\u0026rsquo;s a studio that makes exclusively two-player games—the most recent one was called Split Fiction, which was really good. And they have It Takes Two, which is like a relationship-sort-of game. It\u0026rsquo;s really good—I would recommend that for video games. I don\u0026rsquo;t know if it\u0026rsquo;s on the Switch though.\nTim: My buddy\u0026rsquo;s been playing that with his wife.\nBrenton: The most recent one is Split Fiction—more like a platformer puzzly kind of thing, but it does things in such a neat way.\nAnd then painting—I like painting quite a bit. Try to constrain how much you\u0026rsquo;re doing in a given time. I get these little four-by-six canvas blocks and just do something small.\nTim: My sister-in-law who lives a mile from us is a very successful professional painter.\nBrenton: Well, don\u0026rsquo;t invite them. Or get tips and then don\u0026rsquo;t tell Maggie—then you can show them off.\nTim: I like the idea of foraying into something I don\u0026rsquo;t have a lot of ability in.\nBrenton: We did pottery for a while—that\u0026rsquo;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.\nTim: At the place I did woodworking, we took one pottery class before we had kids. That was really enjoyable.\nBrenton: Anything like making or going through that creative process together is always fun.\nTim: Yeah. We\u0026rsquo;ve just spent the past eight years having kids—a very intense time of our life. We\u0026rsquo;re not planning to have more. Now we have a sort of clean slate. What kinds of things can we do?\nWe also have a big basement now, which is awesome. I have space to do stuff.\nBrenton: For the board game collection.\nTim: Yeah, you know me. Unfortunately, my kids are enjoying games. Unfortunately. I\u0026rsquo;m not going to get into that. All Maggie\u0026rsquo;s family here loves games too, so\u0026hellip; might have to\u0026hellip;\nBrenton: I\u0026rsquo;ll have the podcast part two—I\u0026rsquo;ll have to get some more understanding of your perspective there.\nTim: Part of it is I\u0026rsquo;m not competitive.\nBrenton: Okay, that makes sense.\nMaking Things and Knitting Tim: Part of it is—something we haven\u0026rsquo;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 Python Developer Tooling Handbook.\nBrenton: What about the basketball thing? Is that just a chain of tweets?\nTim: It was a Twitter and Instagram account. I\u0026rsquo;ve tried to turn it into a website—I need to use Claude Code now to get that going again. My \u0026ldquo;Do You Play Ball\u0026rdquo; site. And I do some religious history-related stuff online too.\nI really love making things. Even loving to read—that\u0026rsquo;s actually a tension for me in my hobbies. Reading is like you\u0026rsquo;re not making anything. When I sit down to do something, I love to just tinker. A website is a common outlet for that.\nEven photography, I think for me, is making something. Photographers talk about \u0026ldquo;making an image\u0026rdquo;—and I really do think that\u0026rsquo;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.\nBrenton: People delete pictures now?\nTim: I delete a lot. Quite a bit.\nBrenton: It sounds like one thing to add some variety would be the tangible kind of creation—something that\u0026rsquo;s not on the computer, like the woodworking kind of thing.\nTim: I also like to knit. Do you know about me knitting?\nBrenton: I do know about you knitting. I forgot to bring that up too.\nTim: That\u0026rsquo;s something Maggie and I do sometimes together. She crochets, and I knit as a hobby. We do that and watch movies or something.\nBrenton: And your rocking chairs.\nTim: Yeah, I wish I had a rocking chair.\nBrenton: I know someone who\u0026rsquo;s making a rocking chair.\nTim: My boys are actually trying to teach them how to crochet right now, which is fun.\nBrenton: That\u0026rsquo;s cool.\nTim: Knitting is a very slow way to make things, but it\u0026rsquo;s something I started back in college 20 years ago. I picked it up again recently.\nBrenton: You\u0026rsquo;re gonna need it for all the cold weather.\nTim: It has been unbelievably cold here. It was six below Fahrenheit this morning when I woke up. And now it\u0026rsquo;s 10 PM, it is four above zero. It has been so cold for the two weeks I\u0026rsquo;ve been here.\nHere\u0026rsquo;s a hat that I knit—I never actually really finished it, but\u0026hellip;\nBrenton: That\u0026rsquo;s really nice.\nTim: I\u0026rsquo;m actually a very good knitter. People get intimidated when they see it.\nBrenton: You could go to the knitting circle. Jolene\u0026rsquo;s been eyeing one at the local store—try and encourage her to go.\nTim: It\u0026rsquo;s all ladies, but it\u0026rsquo;s fun.\nAn interesting thing about knitting is that knitting patterns are written in a really abstract way. If you\u0026rsquo;re used to programming and math, they\u0026rsquo;re very unintimidating to read. They\u0026rsquo;re not even complicated logic—it\u0026rsquo;s just really concise so they can put it on one page. I think that\u0026rsquo;s why people find the actual mechanics of complicated things aren\u0026rsquo;t usually that hard—it\u0026rsquo;s just, are you willing to be patient with the abstract stuff? Which is a no-brainer to me.\nBrenton: There\u0026rsquo;s also the geometry aspect too, in terms of crochet—there\u0026rsquo;s like a\u0026hellip;\nTim: Knitting is less geometric in the sense that when you\u0026rsquo;re knitting, you basically only are in one dimension at a time. Crochet is sort of more dimensional because it\u0026rsquo;s less constrained. But knitting is so constrained that if you can understand the abstraction, it\u0026rsquo;s only a very limited scope at any given time. Because you\u0026rsquo;re in one place and you can\u0026rsquo;t be in other places—I mean, there are some exceptions.\nBrenton: The other thing that\u0026rsquo;s neat about crochet is there\u0026rsquo;s no machine that can crochet. So every crochet piece you find somewhere in a store—that\u0026rsquo;s been handmade. There\u0026rsquo;s something special there, I think.\nTim: Yeah. That\u0026rsquo;s what Jolene points out to me all the time. That\u0026rsquo;s cool. And she picked up knitting too, so she\u0026rsquo;s been doing a lot of that.\nYou can machine knit too, but to get really intricate stuff, usually you can\u0026rsquo;t machine knit it as well.\nBrenton: You like the Fair Isle stuff where you make the pictures and things?\nTim: Yeah, I enjoy Fair Isle as well.\nBrenton: Or the pixel art sort of stuff.\nTim: Yeah, it\u0026rsquo;s very fun.\nClosing Brenton: All right. Hey, buddy. It\u0026rsquo;s been lovely to talk to you. It\u0026rsquo;s been a while.\nTim: Do you feel like you know me better?\nBrenton: I feel like I know you better. I feel like there are things we can still talk about. I\u0026rsquo;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.\nWhere can people find you? Do you have anything you want to plug?\nTim: I mean, this podcast is at podcast.tdhopper.com, which is my main website, and that links to most of my stuff. I\u0026rsquo;m tdhopper on Twitter/X and Instagram. \u0026ldquo;tdhopper\u0026rdquo; is kind of my online identity;.\nBrenton: All right, well, send my love to Maggie and the kids. It\u0026rsquo;s been nice spending more time with you.\nTim: Yeah, thanks Brenton. Thank you. Bye.\n","date":"2026-02-02T00:00:00Z","image":"https://tdhopper.com/images/robot-assistant.png","permalink":"https://tdhopper.com/blog/getting-to-know-tim-hopper-with-brenton-mallen/","title":"Getting to Know Tim Hopper with Brenton Mallen"},{"content":"Listen Links Till\u0026rsquo;s LinkedIn Till\u0026rsquo;s Website MotherDuck DuckDB Hopsworks Till on Practical AI Podcast Spider Text-to-SQL Benchmark mviz - Observable Dashboard Skill by Jacob Matson Subscribe RSS Feed Apple Podcasts Spotify Overcast Summary In this episode of Into the Hopper, 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.\nWe 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\u0026rsquo;s role from SQL writing to question refinement.\nTranscript Tim: Welcome to Into the Hopper podcast. Today I\u0026rsquo;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.\nTill: Thanks for having me.\nTim: We\u0026rsquo;re going to dive into AI-assisted SQL generation, which is something I\u0026rsquo;ve explored a little bit but don\u0026rsquo;t know a lot about. We\u0026rsquo;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.\nTill: Great.\nAbout MotherDuck Tim: First of all, do you want to tell us about MotherDuck?\nTill: 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.\nComing 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.\nMy 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.\nResearch-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\u0026rsquo;s how my journey at MotherDuck started.\nMotherDuck 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\u0026rsquo;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.\nWhen the dataset gets really big, or when you\u0026rsquo;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\u0026rsquo;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.\nThe 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.\nThat\u0026rsquo;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\u0026rsquo;t have to go to the server for everything. It\u0026rsquo;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.\nBuilding AI Features at MotherDuck Till: 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\u0026rsquo;t at the point yet where they were useful enough to position prominently—this was end of 2023. We couldn\u0026rsquo;t confidently put that in the primary path and say, \u0026ldquo;Hey, when you come to the UI, you don\u0026rsquo;t have to write SQL anymore. Just give us your question and we\u0026rsquo;ll generate the SQL for you.\u0026rdquo;\nIt 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.\nFor example, fixing queries. That\u0026rsquo;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\u0026rsquo;s so obvious when the database says, \u0026ldquo;This table does not exist. Did you mean this other table?\u0026rdquo; And 99.9% of the time, yes, that\u0026rsquo;s exactly what I meant. Can you please just use it?\nThat\u0026rsquo;s basically what this feature we call \u0026ldquo;Fix It\u0026rdquo; 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\u0026rsquo;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.\nThe History of Text-to-SQL Tim: Do you know much about the world pre-2023 of automation in SQL generation?\nTill: I don\u0026rsquo;t think I\u0026rsquo;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.\nTim: Probably to the seventies.\nTill: I think it\u0026rsquo;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.\nBut 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.\nThere were these benchmarks—Spider and others—that\u0026rsquo;s what people were typically competing on. When you look at the benchmarks, they\u0026rsquo;re very simple: small databases with three to five tables, very well-known columns. They\u0026rsquo;re not really a reflection of enterprise database reality. So there was always this limitation because the results didn\u0026rsquo;t translate to the real world.\nThe State of Text-to-SQL Today Tim: I\u0026rsquo;m interested in what\u0026rsquo;s possible today from two angles. First, if I\u0026rsquo;m an analyst with a business problem I think I can describe, and I don\u0026rsquo;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?\nTill: I remember about four years ago, there was a Hacker News discussion about what\u0026rsquo;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.\nWhen 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.\nI 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.\nBut it\u0026rsquo;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\u0026rsquo;t aware of.\nMCP Servers and Documentation Tim: Say I\u0026rsquo;m a Postgres developer wanting to do analytics. What\u0026rsquo;s the best toolset out there? Can I just tell Claude Code I have access to psql, say \u0026ldquo;go learn my schemas,\u0026rdquo; and then give it my prompt? Or are there more specific agents doing this better?\nTill: 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\u0026rsquo;t have access to data content, it\u0026rsquo;s pretty certain it\u0026rsquo;ll hallucinate filter parameters.\nProviding that access nowadays is super simple. You give an agent the right tools. There\u0026rsquo;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\u0026rsquo;d expect that to work fairly reliably.\nI recently wrote a research paper about this—it\u0026rsquo;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.\nIt 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\u0026rsquo;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.\nSome MCP servers do that. It\u0026rsquo;s basically a RAG pipeline that chunks and indexes the documentation and makes it retrievable through natural language questions. If that\u0026rsquo;s exposed in the MCP server, the agent can call an \u0026ldquo;ask docs question\u0026rdquo; tool when it\u0026rsquo;s in doubt about functionality. It passes a natural language question—\u0026ldquo;How does this feature work?\u0026quot;—and gets an answer grounded in the documentation.\nTim: So your MCP can talk to the documentation as well as your own data?\nTill: Exactly. We\u0026rsquo;re actually using the exact same documentation chatbot for the MCP that we use for our human-facing documentation.\nThe 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 SELECT * FROM duckdb_functions() 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.\nIt\u0026rsquo;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.\nThe Challenge of Undocumented Data Tim: 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\u0026rsquo;s seeing?\nTill: If you give the agent infinite time to do data exploration—you prompt it saying \u0026ldquo;here\u0026rsquo;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\u0026rdquo;—I think that could work quite effectively.\nBut 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.\nIf 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\u0026rsquo;s expensive. You have to run queries and you\u0026rsquo;re wasting tokens. You don\u0026rsquo;t want to do this every single time.\nAnother 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\u0026rsquo;s also very good for the agent to dig into.\nI think this exploration of schema and query history and collection of relevant context shouldn\u0026rsquo;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.\nIf 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\u0026rsquo;ve sent a Slack message or added info to a Linear ticket. But nowadays, it\u0026rsquo;s probably also part of the job to document this in a form that agents can consume easily.\nI wish I could tell you there\u0026rsquo;s an established standard for how to do this. Different companies try different things, and we\u0026rsquo;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\u0026rsquo;s a bit underused, but we recommend people start with using those to add context.\nThere\u0026rsquo;s a gap in our product where I think there\u0026rsquo;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.\nOther 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.\nMy 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\u0026rsquo;t want the join path to use unclean data.\nBuilding Trust in Agent-Generated SQL Tim: 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\u0026rsquo;s somewhat easy to get syntactically correct SQL or something that looks like SQL, and then with an agent, something that runs.\nTwo questions. One: what are good practices to build confidence that agent-generated SQL is correct? As anyone who\u0026rsquo;s spent time with SQL knows, that\u0026rsquo;s also a challenge with human-generated SQL—testing SQL is a hard problem.\nTwo: do you have a concern that by not writing our SQL, we\u0026rsquo;re not thinking about our data enough?\nTill: I would say we\u0026rsquo;re thinking about it in different ways now.\nTim: So what practices can help us know that agent-generated queries are correct?\nTill: It really depends on the type of question you\u0026rsquo;re asking and what the stakes are. If I\u0026rsquo;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.\nBut an agent can be useful for that as well. If I would do it manually, I\u0026rsquo;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\u0026rsquo;s no reason for the agent to be intentionally deceiving. On average, the things the agent does are going in the right direction.\nMaybe it\u0026rsquo;s off on a few things because it\u0026rsquo;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\u0026rsquo;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?\nThis 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.\nI 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\u0026rsquo;t need to know the answers, but by double-checking and poking, I can find out whether what the agent said is correct.\nTim: An interesting thing with SQL is you can do the inverse. Generate the SQL and then in a different session with new context, say \u0026ldquo;given this SQL, tell me what it does\u0026rdquo; and see if that matches what you think.\nTill: Yeah.\nTim: That\u0026rsquo;s a neat feature of Cursor now—you can run multiple models and have multiple responses.\nTill: Oh, I didn\u0026rsquo;t know that.\nTim: 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.\nTill: 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\u0026rsquo;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.\nIt\u0026rsquo;s the same with handwriting you mentioned, or writing itself. It\u0026rsquo;s a way to train the mind, keep the mind sharp, think more clearly about what I\u0026rsquo;m writing. I think it\u0026rsquo;s true in some sense that at this level of abstraction, you\u0026rsquo;ll probably start to lose a little bit of intuition. But it frees up mental space for other things.\nFor analysts—I\u0026rsquo;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\u0026hellip; I\u0026rsquo;ve written a lot of SQL in my life and never gotten really good at it. I\u0026rsquo;m always dreading having to write a window function. I\u0026rsquo;d rather try to do the analysis in a slightly different way so I don\u0026rsquo;t have to write that stupid window function.\nWith 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\u0026rsquo;m through that, I have mental capacity left to think about what other data sources could be interesting to integrate—something I wouldn\u0026rsquo;t have had time for if I\u0026rsquo;d spent all my time just figuring out the SQL syntax.\nIt has the potential to reshape the scope of the analyst\u0026rsquo;s work. At the same time, I could start thinking more about the actual business question.\nWe 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, \u0026ldquo;I kind of want to look into that, but I don\u0026rsquo;t even know if we have data for that.\u0026rdquo; 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.\nTim: I\u0026rsquo;ve found in Claude Code you can use the Ask User Question tool to get it to help me think through what problem I\u0026rsquo;m trying to solve. It surfaces things I know I should be thinking about but don\u0026rsquo;t always think about. That\u0026rsquo;s a very helpful way to work.\nTill: Indeed.\nSkills and Agent Customization Tim: Let\u0026rsquo;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.\nTill: 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.\nWhen 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 \u0026ldquo;run this query\u0026rdquo; or \u0026ldquo;list all tables.\u0026rdquo; There\u0026rsquo;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.\nIn 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\u0026rsquo;t give you that option unless you fork it and change the system prompt.\nSkills, especially because they\u0026rsquo;re shareable in Claude Code—that\u0026rsquo;s what I really like. I feel like overall in the skill ecosystem, what\u0026rsquo;s missing is a general sharing mechanism. There\u0026rsquo;s an effort that Val did, there\u0026rsquo;s this NPX skill distribution mechanism. I also saw there\u0026rsquo;s something coming in the MCP spec, potentially a proposal for providing an MCP service to surface skills.\nCurrently the biggest hurdle is distribution of skills. But once that\u0026rsquo;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.\nIf the agent figures out the user has a specific intent and there\u0026rsquo;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\u0026rsquo;re plug and play.\nJacob, 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.\nMemory and the Future of Agents Tim: Another thing people are talking about more—we discussed it in my last podcast—is memory as a useful tool.\nTill: Yeah.\nTim: I\u0026rsquo;m not sure everybody agrees on what memory means.\nTill: Indeed. I almost think of skills as a very primitive form of memory. If the agent had a way to also author skills—it\u0026rsquo;s basically just markdown—that\u0026rsquo;s a very simple form of memory. Could be a markdown file that the agent writes and can read.\nI think memory is extremely interesting and will help a lot with making agents better at what they\u0026rsquo;re doing. The current frontier of developments in the AI space is not in the model space anymore—it\u0026rsquo;s more on the agent layer. Memory will be a very important part of the agent layer.\nIf you have to explain your agent over and over again how not to make the same stupid mistake, that\u0026rsquo;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\u0026rsquo;s still some manual effort. Memory, there are a lot of things still to be developed and explored.\nIt\u0026rsquo;s not only RAG anymore, or just saving a markdown file. I\u0026rsquo;m very curious where this is going. I don\u0026rsquo;t really have a good answer for how memory looks like today beyond markdown files. I\u0026rsquo;m sure there\u0026rsquo;s a lot of interesting research at labs and in industry on memory architectures. We\u0026rsquo;ll see it.\nLooking Forward Tim: That brings me to a great concluding question. If we look back two years, we didn\u0026rsquo;t have agent tools particularly—we didn\u0026rsquo;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?\nTill: 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.\nAs I said, I think short to midterm, these agent architectures and memory are probably a space where developments will happen.\nI wish context windows would become bigger. That could also solve a bit of the memory and retrieval problems.\nI also find interesting things like diffusion models or new architectures for LLMs. Faster inference—if you\u0026rsquo;re working with Claude Code, it\u0026rsquo;s great, but it can be a little bit slow.\nIf I get off this call, I\u0026rsquo;ll have 20 more things on my mind, but right now I\u0026rsquo;m blank.\nTim: The less you say now, the better you\u0026rsquo;ll look in two years.\nTill: I guess so. But thanks so much for having me. It was fun.\nTim: Any places people can find you online besides motherduck.com?\nTill: People can go to my LinkedIn. I\u0026rsquo;m not active on Twitter or X. LinkedIn is a great place to connect.\nTim: Thanks for joining me on Into the Hopper.\n","date":"2026-01-30T01:05:00-05:00","image":"https://tdhopper.com/images/podcast.png","permalink":"https://tdhopper.com/blog/ai-assisted-sql-generation-with-till-d%C3%B6hmen/","title":"AI-Assisted SQL Generation with Till Döhmen"},{"content":"Boris Cherny, who built Claude Code at Anthropic, recently shared advice he received from Anthropic co-founder Ben Mann:\nDon\u0026rsquo;t build for the model of today, build for the model 6 months from now.\nWhen Cherny first developed Claude Code, it wasn\u0026rsquo;t a great product. The models weren\u0026rsquo;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.\nSix months later, with the release of Claude 3.5 Sonnet and Opus, the models caught up to the tool\u0026rsquo;s design. Cherny\u0026rsquo;s usage jumped to 80-90%.\nThis is a useful mental model for anyone building AI-powered tools: if you only design for today\u0026rsquo;s capabilities, your product may be obsolete by the time it ships.\n","date":"2026-01-30T00:00:00Z","permalink":"https://tdhopper.com/blog/build-for-the-model-six-months-from-now/","title":"Build for the Model Six Months from Now"},{"content":"Listen Links Ben\u0026rsquo;s Twitter Ben’s Website WorkHelix What Are AI Agents - O\u0026rsquo;Reilly Book Managing Memory for AI Agents - O\u0026rsquo;Reilly Book Bead Development Tool Subscribe RSS Feed Apple Podcasts Spotify Overcast Summary In this episode of Into the Hopper, I sit down with Ben Labaschin, a Principal Machine Learning Engineer at WorkHelix and author of the O\u0026rsquo;Reilly books What Are AI Agents and Managing Memory for AI Agents.\nWe dive deep into the current state of AI engineering, moving beyond the hype to discuss the \u0026ldquo;brass tacks\u0026rdquo; of developer workflows. We cover how AI is reshaping personal projects, the shift from \u0026ldquo;coder\u0026rdquo; to \u0026ldquo;conductor,\u0026rdquo; and the specific tool stacks (like Beads and Spec-Driven Development) that Ben uses to manage agent memory effectively.\nTranscript Tim: Welcome to the Into the Hopper podcast. Today, I\u0026rsquo;m joined by Ben Labaschin, a principal machine learning engineer at WorkHelix, who has been instrumental in the company\u0026rsquo;s growth from seed stage to its $75 million Series A valuation.\nBen is the author of O\u0026rsquo;Reilly Books, What Are AI Agents and Managing Memory for AI Agents. 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.\nBeyond 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.\nBen: Hey, man. I appreciate it. Thanks for having me, Tim.\nTim: You and I have known each other a while through the internet, though I think this is really the first time we\u0026rsquo;ve kind of talked one-on-one.\nBen: Yeah. You know, the internet can be really easy to communicate, but it\u0026rsquo;s great to be able to actually speak to you in person.\nTim: Yeah. We\u0026rsquo;re both former NormConf speakers. Shout out to Vicki Boykis and speaking on our NormConf official Shure microphones, which sound good.\nBen: It\u0026rsquo;s one of the best things to come from the conference other than all the awesome talks.\nAI Workflows and Developer Experience Tim: This is an interesting podcast for me because I\u0026rsquo;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\u0026rsquo;s been a wild ride of 2025 and so many changes from what we were doing.\nI 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\u0026rsquo;s given up on that. But that\u0026rsquo;s what a lot of us were doing at least 18 months ago. And now, I think agents are taking over the world. You\u0026rsquo;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.\nBen: That\u0026rsquo;s right. I mean, I\u0026rsquo;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\u0026rsquo;s been a fascinating experience to see how companies were anticipating what was coming versus when it actually came.\nThe long and short of that is that companies really weren\u0026rsquo;t interested in being told, \u0026ldquo;Hey, there\u0026rsquo;s this new paradigm coming, you should prepare for it.\u0026rdquo; When it actually happened, they weren\u0026rsquo;t prepared for it. And now they\u0026rsquo;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.\nTim: What\u0026rsquo;s the TLDR of what WorkHelix provides for the companies?\nBen: Basically what we do is we do a deep dive into a company\u0026rsquo;s labor system and say, \u0026ldquo;Here\u0026rsquo;s where your workers are. Here are some opportunities to leverage AI. Oh, you\u0026rsquo;re already leveraging AI? Let\u0026rsquo;s take that work that you\u0026rsquo;re doing and measure quantifiably the impact that\u0026rsquo;s having in real dollars on your company.\u0026rdquo;\nBecause the philosophy is: if you can\u0026rsquo;t measure it, then you can\u0026rsquo;t really manage it. And we\u0026rsquo;re trying to help companies manage it because, quite frankly, I don\u0026rsquo;t think there\u0026rsquo;s a real grasp on how to manage using these tools, what\u0026rsquo;s effective, and what\u0026rsquo;s not.\nMeasuring AI Impact on Productivity Tim: Are AI agents making developers more efficient?\nBen: It\u0026rsquo;s a really loaded conversation. Constantly you\u0026rsquo;ll see published papers that seemingly contradict each other. One paper says, \u0026ldquo;Game\u0026rsquo;s over. All jobs are going to be replaced.\u0026rdquo; Another says, \u0026ldquo;We have evidence here that work can\u0026rsquo;t be replaced.\u0026rdquo; You can probably guess that it\u0026rsquo;s somewhere in the middle.\nIf you measure it as more lines of code, 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\u0026rsquo;re seeing more LLM-generated code being deployed into production where end users are actually using it.\nAt 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: How many personal projects am I actually finishing in a given year?\nTim: Yeah, I think that\u0026rsquo;s something a lot of us are seeing—the ability to work on personal projects.\nPersonal Projects and AI Tools Tim: My output on personal projects has gone down rapidly with my number of children. But two things are being powerful:\nCloud Code on the phone: The ability to code on the go is so cool. Codex/Agents grinding: 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. 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: \u0026ldquo;Get me a VPS, install Coolify on it, and put it behind my Tailscale network.\u0026rdquo;\nIt 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.\nBen: It is. And I think that there\u0026rsquo;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\u0026rsquo;s like, I can check my phone and it\u0026rsquo;s working and then I can get back to being present.\nHowever, 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\u0026hellip; but context switching and having all of these things you\u0026rsquo;re attending to in your mind is probably not super healthy.\nTim: The observation folks have had is that we\u0026rsquo;re becoming more like managers or conductors. I look at my manager\u0026rsquo;s calendar and she\u0026rsquo;s in meetings all day while in three different Slack discussions. That\u0026rsquo;s kind of more how we\u0026rsquo;re all operating now.\nBen: I think the expectations are slowly going to change for workers too. I haven\u0026rsquo;t felt it yet, but as a hard worker, if I can do seven things at once, suddenly people start to say, \u0026ldquo;Well, this is just the rate that Ben works.\u0026rdquo;\nThe rubber will meet the road when the quality of work starts going down or things start getting messed up. That\u0026rsquo;s when people start saying, \u0026ldquo;Okay, we got to make sure that there\u0026rsquo;s not too much being worked on simultaneously.\u0026rdquo;\nTim: I agree. One of the things we\u0026rsquo;re still emphasizing a lot is human review. I\u0026rsquo;ve been working on a Claude skill to review code. Using it to look for the things that I want to look for that are relevant to our team—it\u0026rsquo;s finding bugs that I never would have found.\nI’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.\nBen: Looking back at economic history is generally very helpful. I look back at the 18th and 19th centuries to understand technological shifts.\nI think the printing press is a good analogy. It was a democratization of information. I feel agents are parallel to that because it\u0026rsquo;s a democratization of being able to build things. I don\u0026rsquo;t know what the consequences are going to be in 20 years, but I think they\u0026rsquo;ll be seismic.\nSpecialization in the Age of AI Ben: 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\u0026rsquo;s being flattened. I\u0026rsquo;m not a front-end engineer, but I can now do front-end work because agents are getting better at it.\nHowever, I would argue that specialization is still going to be valuable. Just because you can generate art with Midjourney doesn\u0026rsquo;t mean the meaning of a human artist painting a picture isn\u0026rsquo;t valuable.\nTim: If you think about the \u0026ldquo;T-shaped engineer\u0026rdquo; idea—breadth and depth—you wonder, does this just enable us to have a bigger T?\nI just merged a front-end task today using the Claude front-end skill on a Next.js project. I don\u0026rsquo;t know JavaScript particularly well, and I definitely don\u0026rsquo;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.\nBen: 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\u0026rsquo;re not coding as much.\nBut 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.\nTim: 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!\nYou have to continue to be deliberate to learn. Yesterday I did some Kubernetes ops work. At the end, I asked my Cloud Code session: \u0026ldquo;Teach me about all the things that we just did. Walk me through the steps.\u0026rdquo;\nBen: I completely agree. The tactics you\u0026rsquo;re using to get that learning might look different, but you have to be deliberate.\nToolset Evolution Over Time Tim: Let\u0026rsquo;s step back and talk about what your toolset has been. How has that changed over the last year?\nBen: My toolset has certainly changed. I used to focus on prompts. Now, I have an agents.md or a claude.md that I symlink so they are the same for different models.\nIf there are three things I use, it\u0026rsquo;s:\nGitHub: For that local vs. remote tracking of memory. Beads: For consolidating how local memory is being used and breaking those down into atomic tasks. Spec-Driven Development: 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. Tim: 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 \u0026ldquo;Ask User Question\u0026rdquo; skill. It forces me to think through things.\nBen: I have a rule in my claude.md. I say:\nThere should be an architecture.md (diagrams, high-level goals). There should be a log that it returns to. Before we do anything, read the architecture, read the readme, read the log. Create Beads tasks from our next step in the architecture document. It tends to be very effective.\nTim: I keep coming back to Fred Brooks in The Mythical Man-Month:\n\u0026ldquo;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.\u0026rdquo;\nI’m spending more time articulating what I want than I\u0026rsquo;ve ever done in my entire career.\nBen: I just realized\u0026hellip; I would always say, \u0026ldquo;Being in the code helps me figure out what I\u0026rsquo;m going to do.\u0026rdquo; I\u0026rsquo;m so the opposite now. That\u0026rsquo;s crazy.\nTim: 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\u0026rsquo;t spend hours typing it.\nBen: That\u0026rsquo;s so astute. My ego is less invested in this code because I put less of myself into the code.\nResources \u0026amp; Closing Tim: This was great. Can you tell us a little bit about your books with O\u0026rsquo;Reilly?\nBen: I\u0026rsquo;ve written two publications:\nWhat Are AI Agents Managing Memory for AI Agents 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.\nTim: You can also find more about Ben at econoben.dev. Thanks for joining us!\n","date":"2026-01-09T16:41:00-05:00","image":"https://tdhopper.com/images/podcast.png","permalink":"https://tdhopper.com/blog/the-evolution-of-ai-agents-with-ben-labaschin/","title":"The Evolution of AI Agents with Ben Labaschin"},{"content":"I\u0026rsquo;ve been relying on Claude Code more and more since March 2025, not just for development but for all things computer automation. 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.\nHere are some practical patterns I\u0026rsquo;ve learned over the last year:\nLet It Drive Your Git I\u0026rsquo;ve embraced letting Claude handle many of my git operations. It\u0026rsquo;s great at finding the right changes to commit and opening PRs. It\u0026rsquo;s also amazing at more complex tasks like cherry-picks, rebases, splitting large features into logical PRs.\nI 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 saved mental energy.\nStart Fresh When Stuck When Claude starts giving consistently bad answers, don\u0026rsquo;t keep pushing it. Clear the context and start a new chat. A fixated agent won\u0026rsquo;t suddenly become unfixated. Starting fresh gives you better results faster than trying to course-correct an existing conversation.\nUse It for System Automation, Not Just Code Claude Code is a misnomer; it\u0026rsquo;s a computer automation tool. It\u0026rsquo;s far better at using CLI tools than I\u0026rsquo;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.\nBuild Skills and Slash Commands for Repeated Tasks I\u0026rsquo;ve developed a skill for opening pull requests with well-structured, useful messages. I also use a slash command for code reviews that focuses on correctness, performance impact, and unnecessary complexity. These tools make repeated workflows consistent and fast.\nFast Feedback is Essential The value of fast feedback can\u0026rsquo;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.\nI think of Claude\u0026rsquo;s capability the way a favorite math professor once described his advantage: \u0026ldquo;I\u0026rsquo;m not smarter than you. I can just recover from mistakes faster than you.\u0026rdquo; Agents recover from errors at speeds humans can\u0026rsquo;t match (and, usually, don\u0026rsquo;t get as frustrated); feedback enables recovery.\nUse It to Navigate Unfamiliar Code 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.\nDefine the Problem Before Starting The hardest part is still understanding what problem you\u0026rsquo;re actually solving. I\u0026rsquo;ve wasted plenty of agent time by not thinking through what I wanted before asking. Spending five minutes clarifying the goal (sometimes with the agent) saves thirty minutes of iterating on the wrong solution.\nPrototype Aggressively, Throw Away Freely I prototype more now than I ever did before. The cost of trying something dropped dramatically. I\u0026rsquo;ll have Claude generate a quick script or CLI to test an idea, knowing I\u0026rsquo;ll probably throw it away. This changes how I approach problems: I can test assumptions with real implementations.\n","date":"2026-01-08T08:00:00Z","image":"https://tdhopper.com/images/mr-men-claude-code.png","permalink":"https://tdhopper.com/blog/lessons-from-using-claude-code-effectively/","title":"Lessons from Using Claude Code Effectively"},{"content":"Chat I subscribe to ChatGPT Plus 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\u0026rsquo;s many questions (\u0026ldquo;what was the longest war in history\u0026rdquo;).\nAt work, I have access to ChatGPT Pro through work and occasionally use the 5.2 Pro for deeper thinking (e.g., I recently asked \u0026ldquo;What were the best in class ai models at the end of 2025\u0026rdquo;).\nImage Generation Nano Banana Pro has become my only tool for image generation. It\u0026rsquo;s just so good. I don\u0026rsquo;t subscribe to Gemini personally, but I find the free tier gives me enough for things I want to generate (usually silly).\nCode A year ago, I had not used a coding agent. I mostly relied on copying and pasting code into ChatGPT and auto complete in Github Copilot to write code for me.\nEarly in the year, I started using Cursor at home and at work. By mid year, I had almost exclusively shifted to Claude Code which I exclusively use in --dangerously-skip-permissions mode. Opus 4.5 has made that an even better experience, and I find it reliably helps me refine what I\u0026rsquo;m asking for and then provide top tier implementations. I live in Claude Code these days.\nA lesson I\u0026rsquo;ve learned over the past 6 months is that Claude Code isn\u0026rsquo;t just good for code automation but also computer automation. It\u0026rsquo;s far better at using CLI tools than I will ever be, and it\u0026rsquo;s ability to combine them enables automation of lots of work. For example, I rarely use git directly any more and let Claude handle it for me (even opening pull requests).\nI subscribe to the $20/month pro plan and have extra usage enabled so I can pay for tokens after hitting the Pro limits.\nWriting As Robert Ghrist told me last year, Claude has the best writing style. I often rely on it to help me refine things I\u0026rsquo;m saying.\nLearning More and more I\u0026rsquo;m using LLMs to help me learn things better from understanding research to code bases to random topics of interest.\nFor random topics, this often starts with a ChatGPT Thinking session with web search enabled.\nFor code, I have found Claude Code can do excellent breakdowns and patiently fields my questions in discovering corners I don\u0026rsquo;t understand.\nNotebookLM 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\u0026rsquo;m asking. I also love generating a podcast with a prompt that guides them in the direction I\u0026rsquo;m interested in so I can learn on the go.\nTranscription I\u0026rsquo;ve never been a bit voice to text person, because I mumble with a choppy cadence. However, I\u0026rsquo;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.\nI have been experimenting with Whispr Flow for personal uses; this has largely been using their iOS keyboard for vastly superior text-to-speech that the built in Apple Option.\nI have Whisper Memos 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 Drafts inbox.\nI just installed Sotto on my work machine this week which provides local model voice transcription. I\u0026rsquo;m interested in using this for interacting with Slack and Claude Code, but I haven\u0026rsquo;t made much progress yet.\n","date":"2025-12-31T12:00:00Z","image":"https://tdhopper.com/images/puppets.png","permalink":"https://tdhopper.com/blog/how-im-using-ai-at-the-end-of-2025/","title":"How I'm Using AI at the End of 2025"},{"content":"Years ago, I published a collection of interviews with people I respected about whether a young person should pursue a Ph.D. I still hear from people who found them helpful.\nThis is a slide deck version of the key questions from those interviews. If you\u0026rsquo;re wrestling with this decision, I hope it gives you a useful framework.\nWant to hear directly from Ph.D. holders about their experiences? Check out the full interview series at Should I Get a Ph.D.?\n","date":"2025-12-10T00:00:00Z","image":"https://tdhopper.com/blog/the-phd-blueprint/page-01_hu_c242ca4e437ce5e5.png","permalink":"https://tdhopper.com/blog/the-phd-blueprint/","title":"The Ph.D. Blueprint: Five Questions to Design Your Future"},{"content":"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\u0026rsquo;t directly access webcam RTSP streams, but they do support WebRTC.\nAfter some tinkering, I found a clean solution: ffmpeg captures the webcam and streams it via RTSP, while MediaMTX converts that RTSP feed to WebRTC that browsers can consume.\nInstallation 1 brew install ffmpeg mediamtx Setup You need to run two processes in parallel.\n1. Start MediaMTX server 1 mediamtx This starts the MediaMTX server which will handle the protocol conversion and serve the web interface.\n2. Stream webcam with ffmpeg In a separate terminal:\n1 2 3 ffmpeg -f avfoundation -framerate 30 -i \u0026#34;0\u0026#34; \\ -vcodec libx264 -preset ultrafast -tune zerolatency \\ -f rtsp rtsp://127.0.0.1:8554/webcam The -f avfoundation flag is macOS-specific for capturing video devices. On Linux, you\u0026rsquo;d use -f v4l2 instead. The \u0026quot;0\u0026quot; refers to the first video device (your default webcam). The ultrafast preset and zerolatency tuning minimize encoding overhead for real-time streaming.\nViewing the Stream Once both processes are running, open your browser and navigate to:\n1 http://127.0.0.1:8889/webcam/ The feed should appear in your browser. You can access this URL from any device on your local network by replacing 127.0.0.1 with your machine\u0026rsquo;s IP address.\nMediaMTX 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 WebRTC API you can integrate with.\n","date":"2025-11-10T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/streaming-webcam-to-browser-with-mediamtx/","title":"Streaming Webcam to Browser with MediaMTX"},{"content":"Bob Belderbos invited me on the Pybites podcast to talk about my career, the Python Developer Tooling Handbook, uv, photography, and more. Bob was a great interviewer and I hope you enjoy.\nListen on pybitespodcast.com Listen on Spotify Listen on Apple Podcasts Watch on Youtube ","date":"2025-07-30T09:18:00-04:00","image":"https://tdhopper.com/images/podcast-interview-min.png","permalink":"https://tdhopper.com/blog/interview-with-the-pybites-podcast/","title":"Interview with the Pybites podcast"},{"content":"I\u0026rsquo;m very interested in how people are using LLMs to augment their work, especially in software development.\nI did three interviews with friends who are using them in different ways.\nJowanza Joseph is building a startup where Cursor is doing much of the legwork. Ravi Mody primarily interacts with web-based chat clients like ChatGPT to write his code. Robert Ghrist \u0026ldquo;directed\u0026rdquo; Claude on how to write a beautiful linear algebra textbook in his style. I\u0026rsquo;ve enjoyed several other pieces lately on how others are using LLMs:\nHarper Reed posted a detailed account of his workflow to robustly generate new code bases. I\u0026rsquo;ve been playing with his provided prompts and am impressed. Simon Willison wrote about using LLMs to write code, especially enabling him to build things he wouldn\u0026rsquo;t bother with otherwise. He shows concrete examples with his LLM generated tools page. Andrej Karpathy published a 2-hour long video that is the best introduction I\u0026rsquo;ve seen to the current state of LLMs and what they can do. I\u0026rsquo;d love to hear about your use of LLMs. Please drop me a note in the form below.\n","date":"2025-03-11T12:12:00Z","image":"https://tdhopper.com/images/ai-agent-coder.jpg","permalink":"https://tdhopper.com/blog/how-developers-are-using-llms/","title":"How Developers are Using LLMs"},{"content":"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.\nListen Links Professor Robert Grist from the University of Pennsylvania. Professor Ghrist\u0026rsquo;s Twitter Professor G\u0026rsquo;s Twitter thread about composing a linear algebra book with the assistance of the Claude LLM. Funny Little Calculus Text (FLC) Elementary Applied Topology PDF of the new linear algebra book Order a print copy of the linear algebra book from Amazon Subscribe RSS Feed Apple Podcasts Spotify Overcast ","date":"2025-01-18T10:03:00Z","image":"https://tdhopper.com/images/podcast.png","permalink":"https://tdhopper.com/blog/writing-a-math-textbook-with-claude-with-professor-ghrist/","title":"Writing a Math Textbook with Claude with Professor Ghrist"},{"content":"Over the last 10 years, I\u0026rsquo;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).\nI\u0026rsquo;ve used a lot of different cameras over the years1, but the one that has consistently brought me the most joy and satisfaction is my Sony a7 III. I\u0026rsquo;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.\nWhen 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\u0026rsquo;t the camera I use the most since my iPhone de jour is always closest at hand, I\u0026rsquo;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\u0026rsquo;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.\nMy full-frame Sony mirrorless, on the other hand, rarely disappoints.\nFast and Responsive When I\u0026rsquo;m not photographing my kids, my other main photography interest is wildlife. Wildlife photography presents a unique set of challenges - conditions are rarely ideal, and subjects move unpredictably. As a wildlife photographer, I\u0026rsquo;m constantly pursuing what Jay Maisel calls Gesture, Light, and Color in his book. You have to stay ready, alert to fleeting opportunities.\nPhotography kids is exactly the same.\nHaving a fast and responsive camera isn\u0026rsquo;t everything - the tool doesn\u0026rsquo;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:\nFirst, 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.\nBeyond pure speed, the camera\u0026rsquo;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.\nAmazing Autofocus Another aspect of speed is the camera\u0026rsquo;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.\nThe 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.\nAutofocus is one area where cameras are improving every generation, and I look forward to benefiting from that next time I upgrade\nLarge Sensor Unlike all the other cameras I\u0026rsquo;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\u0026ndash;especially paired with a fast prime lens\u0026ndash;consistently produces sharp and beautiful images, even at ISO 12800.\nThe large sensor also allows for more subject separation (bokeh) than smaller sensors. When I\u0026rsquo;m photographing my kids, I often want to blur the background to make the subject stand out. The Sony a7 III does this beautifully.\nDurable and Reliable The Sony a7 III is a professional-grade camera. It\u0026rsquo;s built to last. While I try not to abuse it, it\u0026rsquo;s been banged around on hikes, sandy at the beach, and bounced around in my backpack. Over five years later, you\u0026rsquo;d have no idea other than some surface wear on the body.\nBecause 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\u0026rsquo;t have to worry about it.\nThe camera also has excellent battery life, unlike some compact and mirrorless cameras I\u0026rsquo;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.\nImage Quality 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\u0026rsquo;ve taken with my Sony.\nSilent Shooting A small benefit of the mirrorless design is the ability to shoot totally silently. This isn\u0026rsquo;t a feature I knew I wanted, but it has been valuable on many occasions. Sometimes when photographing kids, it\u0026rsquo;s helpful to be able to take a photo without them knowing.\nConclusion Capturing the fleeting moments of my children\u0026rsquo;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.\nCameras I\u0026rsquo;ve used since 2014: iPhones from 5 up to 16 Pro Max, Olympus Tough TG-3, Sony NEX-5T, Canon 7D II, Sony a6500, Sony RX100V, Fuji X100V, Sony a7 III\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2025-01-12T00:00:00Z","image":"https://tdhopper.com/blog/sonya7iii/20210215-DSC02383.jpg","permalink":"https://tdhopper.com/blog/sonya7iii/","title":"The Sony A7 iii after 5 years"},{"content":"I dumped all my posts from the last 14 years into Claude and asked it to make any conclusions about me from them. These are spot on:\nEvolution in Technical Identity: 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. Teaching as Processing: 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. Meta-Learning Focus: Many of your posts aren\u0026rsquo;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. Honesty About Failure: Unlike many technical bloggers, you\u0026rsquo;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. Long-Form Thinking: You maintain ideas and threads over very long periods. For example, you\u0026rsquo;ve written multiple follow-ups and updates to your \u0026ldquo;How I Became a Data Scientist\u0026rdquo; post over several years as your perspective evolved. Documentation as Service: 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. Cross-Domain Connection Making: You often draw interesting parallels between seemingly unrelated domains - connecting operations research to data science, or academic math to practical programming. ","date":"2024-12-31T10:54:00Z","image":"https://tdhopper.com/images/computer-phone.png","permalink":"https://tdhopper.com/blog/ai-interpretation-of-tdhopper.com/","title":"AI interpretation of tdhopper.com"},{"content":"Last year, I invested in a Synology NAS as a home server and backup drive. Like many Synology users, I\u0026rsquo;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.\nOn my NAS, I use Pihole for network-wide ad blocking. A Webdav server syncs my Devonthink databases to manage my research and documents. I back up my Apple Photos library on Synology Photos. I\u0026rsquo;m transitioning my home camera setup to Surveillance Station (so all my footage is now local). Using Synology\u0026rsquo;s Docker support, I run OpenAudible to manage my audiobook collection. I run Synology MailPlus to back up my email. And there\u0026rsquo;s more!\nOf course, now that I have all these services running, I want to access them from anywhere. Synology offers the QuickConnect service, but I wanted something more secure and independent.\nWhile browsing Synology\u0026rsquo;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.\nTo make my services more accessible, I purchased a domain name through Cloudflare. I created a subdomain with an A record pointing to my always-on Mac Mini\u0026rsquo;s Tailscale IP address for each of my services. This IP address is accessible from anywhere but only through my devices authenticated with Tailscale.\nI run a Caddy server 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\u0026rsquo;s Encrypt, giving me https access to my services.\nCaddy was painless to install with Homebrew and easy to configure with a Caddyfile. Here\u0026rsquo;s an example of my Caddyfile:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 cat Caddyfile { email \u0026lt;MY_EMAIL\u0026gt; } *.mydomain.com mydomain.com { tls { dns cloudflare \u0026lt;CLOUDFLARE_API_TOKEN\u0026gt; } } pihole.mydomain.com { reverse_proxy 192.168.68.21:8765 } webdav.mydomain.com { reverse_proxy https://192.168.68.21:5006 { transport http { tls_insecure_skip_verify } } } The last piece of the puzzle involved configuring the Synology to know these subdomains. In the DSM Control Panel, under the Login Portal\u0026rsquo;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\u0026rsquo;t need to remember IP addresses and ports. I can access all my services through simple, memorable domain names from anywhere in the world.\nThis setup has transformed how I interact with my home server, making it both more powerful and more convenient to use. The combination of Tailscale\u0026rsquo;s security, Cloudflare\u0026rsquo;s domain management, and Caddy\u0026rsquo;s reverse proxy capabilities has created a easy to configure and user-friendly system that serves all my home server needs.\n","date":"2024-12-20T11:47:00Z","image":"https://tdhopper.com/images/network.png","permalink":"https://tdhopper.com/blog/accessing-my-home-server-around-the-world-with-custom-domain-names/","title":"Accessing my home server around the world with custom domain names"},{"content":"Listen Links Jowanza\u0026rsquo;s Twitter Jowanza\u0026rsquo;s website Parakeet Financial AI Assisted Programming with Ravi Mody Cursor Editor Magic Patterns V0 by Vercel Crew AI GitHub Copilot Subscribe RSS Feed Apple Podcasts Spotify Overcast ","date":"2024-12-17T12:00:00Z","image":"https://tdhopper.com/images/podcast.png","permalink":"https://tdhopper.com/blog/building-a-startup-with-ai-with-jowanza-joseph/","title":"Building a Startup with AI with Jowanza Joseph"},{"content":"I recently joined the Spotify Audio Intelligence team as a machine learning infrastructure engineer. At our team offsite, we did lightning round introductions of ourselves, so I used ChatGPT and tad.ai to generate a song about my career history.\nStarted at RTI, solving problems with care,\nSocial media models and GIS in the air. Then Parsley called, real-time streams to refine, With Python and Storm, data aligned.\nFrom research to engineering, skills took flight, Building platforms through day and night. Each role a step, each tool a stage, A career evolving, page by page.\nAt Cylance, you scaled machine learning with ease, Serverless pipelines and malware’s disease. D.T.N. brought platforms, Airflow in play, Cloud tools for data, clearing the way. Varo’s features took center stage, With Tecton and Airflow, systems engaged.\nFraud detection, insights in view, Scaling platforms, something new. From data science to ML core, Building systems that teams adore.\nNow at Spotify, the journey goes on, Tim’s career—focused, strong. Each milestone builds the next design, A career in motion, sharp and aligned.\nTim’s journey tells a thoughtful tale, A builder of systems, a path to unveil.\n","date":"2024-12-05T10:18:00Z","image":"https://tdhopper.com/images/singing.jpeg","permalink":"https://tdhopper.com/blog/a-builder-of-systems-a-path-to-unveil/","title":"A builder of systems, a path to unveil"},{"content":"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.\nHere are some lessons from my experience:\nPoint-in-time correctness is harder than it looks 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\u0026rsquo;s balance \u0026ldquo;as of yesterday\u0026rdquo; needs yesterday\u0026rsquo;s knowledge, not today\u0026rsquo;s corrected ledger. Build point-in-time correctness into the platform from day one. Retrofitting is expensive and painful.\nEvent time vs processing time matters Teams need a common understanding of feature availability and event time versus processing time semantics. When you query for \u0026ldquo;the features as of timestamp T,\u0026rdquo; 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.\nReduce skew by reducing code duplication 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\u0026rsquo;re doing online and offline retrieval, do whatever you must to have single code paths for the features.\nUnified batch and streaming requires operational maturity 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\u0026rsquo;t underestimate the operational complexity of running both a batch system and a streaming system in production.\nFeature discoverability unlocks reuse A centralized feature store becomes valuable not just for machine learning but in rules engines and analytics 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.\nGovernance reins in complexity 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.\nMetadata and lineage are debugging lifelines Tracking where features come from and how they\u0026rsquo;re built is essential for debugging, compliance, and understanding downstream impacts. When (not if) something goes wrong, you need to be able to trace the problem back to its source quickly.\nMonitoring and alerting are essential 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.\nML platforms aren\u0026rsquo;t just data engineering 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\u0026rsquo;t simply treat ML data like any other data pipeline.\nTooling enforces what platforms guarantee There\u0026rsquo;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.\nEnforcement creates tension 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.\nCost management means thinking about serving 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.\nCustomer-centricity is non-negotiable 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.\n","date":"2024-11-14T12:00:00-05:00","image":"https://tdhopper.com/images/featureplat.png","permalink":"https://tdhopper.com/blog/lessons-from-developing-a-machine-learning-feature-platform/","title":"Lessons from Developing a Machine Learning Feature Platform"},{"content":"I started college as a physics major, but I switched to computer science my sophomore year when I realized I didn\u0026rsquo;t want to take more lab classes. After a year as a CS major, I realized I loved the theory classes but didn\u0026rsquo;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.\nDuring the winter of my junior year, I applied to a bunch of math Research Experience for Undergraduates programs at schools around the country. I was thrilled to be accepted to the program at Rochester Institute of Technology organized by Darren Narayan for the summer of 2007.\nI headed to Rochester starry eyed about spending the summer doing math research. On my first day, I found out I\u0026rsquo;d be working with the inimitable mathematician Stanisław P. Radziszowski on computational combinatorics with another student researcher, Evan Heidtmann. The first question Dr. Radziszowski had for us was \u0026ldquo;How are your programming skills?\u0026rdquo; This was not what I expected\u0026ndash;or wanted\u0026ndash;to hear at a math research program.\nWe spent the summer investigating the Ramsey number $R_4(C_4)$. This is the smallest number $n$ such that a complete graph with $n$ vertices where the edges are colored by four colors is guaranteed to have a monochromatic cycle with 4 vertices.\nAt the time, the number was known to be either 18 or 19. Due to combinatorial explosion, it\u0026rsquo;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.\nSince 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.\nBefore 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 nauty.\nI had also never used version control, but Evan was running a version control (I don\u0026rsquo;t recall which one) platform on his home server, so we were able to easily version and collaborate on our code.\nWe 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.\nWell, we failed to solve our problem. It turns out the solution was actually published by Chinese mathematicians while we were working on it. $R_4(C_4)=18$, so the graph we looked for didn\u0026rsquo;t even exist.\nNonetheless, 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.\nI didn\u0026rsquo;t write much code between 2007 and starting my operations research master\u0026rsquo;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\u0026rsquo;t have picked that research project had I been given a choice; I\u0026rsquo;m so glad I wasn\u0026rsquo;t asked.\n","date":"2024-09-19T13:20:00Z","image":"https://tdhopper.com/images/graphtheory.jpg","permalink":"https://tdhopper.com/blog/my-summer-doing-math-research/","title":"My summer doing math (?) research"},{"content":"Listen Links Ravi Mody on LinkedIn ChatGPT Claude.ai BBEdit Cursor Drew Conway\u0026rsquo;s Data Science Venn Diagram Subscribe RSS Feed Apple Podcasts Spotify Overcast Summary In this episode of Into the Hopper, 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.\nRavi 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\u0026rsquo;re both spending less time on Stack Overflow.\nTranscript Tim: Welcome to episode nine of the Into the Hopper podcast, back after maybe an 18-month hiatus. My name is Tim Hopper. I\u0026rsquo;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.\nRavi and I have been discussing how large language models are impacting how we program. We\u0026rsquo;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\u0026rsquo;m interested in how others are using these tools too. Sometimes it\u0026rsquo;s hard to know how broad the impact is, or if it\u0026rsquo;s just people on social media talking about it. Ravi\u0026rsquo;s own development has been really impacted, and I asked him to come chat about what that looks like day to day.\nRavi: Tim, thanks so much for having me. This is definitely a topic I think about a lot.\nTim: Do you want to expand on your intro anymore? I summarized your very long resume in one sentence.\nRavi: Yeah, I mean, when I talk about programming, I\u0026rsquo;m largely coming at it from the perspective of building machine learning systems. That\u0026rsquo;s my specialty—usually recommender systems specifically. So some of my use of LLM coding is probably very domain-specific.\nAnother piece of context: I\u0026rsquo;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\u0026rsquo;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.\nTim: That\u0026rsquo;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.\nRavi: 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\u0026rsquo;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.\nCurrent Toolset Tim: Why don\u0026rsquo;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\u0026rsquo;s your current setup?\nRavi: I have access to ChatGPT, Claude 3.5, and Cursor. I go back and forth between them. The honest truth is I\u0026rsquo;m actually very comfortable with a basic text editor. I\u0026rsquo;m not a backend engineer—I\u0026rsquo;m really building ML models. So I\u0026rsquo;ve always been most comfortable in almost a notepad-style of programming. I use BBEdit.\nTim: BBEdit\u0026rsquo;s your primary editor?\nRavi: Yeah, it has been for most of my career.\nTim: I don\u0026rsquo;t think you\u0026rsquo;ve ever mentioned that. That\u0026rsquo;s interesting.\nRavi: It\u0026rsquo;s kind of embarrassing, honestly. I don\u0026rsquo;t like to tell people. I think it makes you a different kind of programmer if you\u0026rsquo;re not using a real IDE.\nTim: A lot of people have really relied on it. So BBEdit doesn\u0026rsquo;t currently have any copilot or cursor-type integration in the editor—you\u0026rsquo;re going to the chat interfaces directly?\nRavi: Yeah. I\u0026rsquo;m honestly not even sure if it does. I\u0026rsquo;m not a power user of BBEdit. I literally just use it as a notepad with syntax highlighting.\nTim: That\u0026rsquo;s wild. I\u0026rsquo;m very interested to hear that. So on a day to day, what are you turning to AI tools to assist with?\nThinking at the Function Level Ravi: One thing this means is that I\u0026rsquo;m not really getting code completion. Sometimes I\u0026rsquo;ll use Cursor because there is a little bit of friction otherwise. But I\u0026rsquo;ve never found that the speed of my typing is what really slows me down on projects. It\u0026rsquo;s usually one level of abstraction higher—taking a more complex idea and working at the level of functions and classes.\nFor 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\u0026rsquo;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\u0026rsquo;m usually generating at the function level.\nHours become five to ten minutes now. That\u0026rsquo;s where I\u0026rsquo;ve been finding my productivity multiples. It\u0026rsquo;s not completing a little piece of syntax—it\u0026rsquo;s thinking at the function and class level.\nTim: That\u0026rsquo;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 \u0026ldquo;give me a DuckDB command that\u0026rsquo;s going to restructure this the way I want.\u0026rdquo;\nRavi: 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—\u0026ldquo;save this JSON file to this folder and then load it from that folder.\u0026rdquo; I\u0026rsquo;m trying to automate away the parts I just did not enjoy about the job.\nTim: It\u0026rsquo;s interesting historically how the ability to do that has been such a superpower. Going back to Drew Conway\u0026rsquo;s data science Venn diagram from 13 years ago—that hacking skill, being able to manipulate data. So many people just don\u0026rsquo;t have that paradigm. It\u0026rsquo;ll be interesting to see if this opens the door for more people to do that kind of thing.\nI 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\u0026rsquo;s impossible to work with, but DuckDB is perfect for this. In the past, I\u0026rsquo;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\u0026rsquo;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\u0026rsquo;t want to spend time on anymore.\nRavi: Exactly. If I\u0026rsquo;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\u0026rsquo;ve realized in my career is the higher level of abstraction you can think, the more productive you\u0026rsquo;ll be. 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\u0026rsquo;s been the biggest game changer for me.\nCode Completion and Reduced Mental Overhead Tim: I\u0026rsquo;m actually surprised you\u0026rsquo;re not using it more at the code completion level. That\u0026rsquo;s been the biggest change for me. I\u0026rsquo;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\u0026rsquo;s so good at recognizing patterns.\nIn Python, for example, there\u0026rsquo;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 \u0026ldquo;try to fix this error that this import doesn\u0026rsquo;t exist\u0026rdquo;—and it\u0026rsquo;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\u0026rsquo;t have to type out \u0026ldquo;from pandas import something.\u0026rdquo;\nRavi: There\u0026rsquo;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.\nTim: The other thing is if you\u0026rsquo;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\u0026rsquo;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\u0026rsquo;ve spent many hours writing regex to do that kind of thing. Now it just does it.\nRavi: One thing I\u0026rsquo;ve been really enjoying is—and this is partially because of the chat interface—I\u0026rsquo;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\u0026rsquo;ve sometimes done is have a skeleton of a function and throw it into Claude, saying \u0026ldquo;This is what I\u0026rsquo;m trying to get out of this. Give me any advice, but also just write all of this boilerplate stuff.\u0026rdquo;\nI think of that as code completion at the function level. I\u0026rsquo;m asking it to think cohesively about that function: \u0026ldquo;This is what the function needs to do, this is the context of why I\u0026rsquo;m doing it.\u0026rdquo; Something that would have taken me half an hour takes a minute now.\nTim: 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\u0026rsquo;m not writing big libraries used by lots of people—it\u0026rsquo;s little internal things. How much is it worth spending time on? But when it can do it for you, it\u0026rsquo;s pretty nice.\nImplications for Junior Developers Tim: You\u0026rsquo;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\u0026rsquo;s been a concern people have had.\nRavi: That\u0026rsquo;s a great question. This AI stuff, at least at this point where it\u0026rsquo;s still error-prone and can\u0026rsquo;t think about large systems cohesively—it\u0026rsquo;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.\nThis is particularly dangerous for people who don\u0026rsquo;t have the experience of understanding edge cases where things may fail, or understanding inefficiencies in the code.\nI 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\u0026rsquo;s going to do something where I\u0026rsquo;m like, \u0026ldquo;Hey, take a look at that.\u0026rdquo;\nWith more junior employees or people straight out of college who don\u0026rsquo;t have that experience, code review will still be important. As I start to hire people for whom this is a bigger risk, I\u0026rsquo;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.\nAt this moment in time, we cannot 100% trust LLMs, and we need to embrace that it\u0026rsquo;s not completely doing our job right now.\nTim: At the same time, there\u0026rsquo;s really no going back. We can\u0026rsquo;t pretend people aren\u0026rsquo;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.\nWe\u0026rsquo;ve had the same concerns over the years about people copying and pasting code snippets from Stack Overflow that they didn\u0026rsquo;t understand. I\u0026rsquo;ve done it many times. In some ways, we\u0026rsquo;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.\nRavi: That\u0026rsquo;s a great point—we\u0026rsquo;ve been doing this forever with Stack Overflow. We copy and paste code in. It\u0026rsquo;s always been best practice to understand the code, to even cite where you got it from.\nShadow LLM Use Tim: There\u0026rsquo;s an interesting issue of companies being hesitant about this. There are legitimate legal concerns and related issues, but at every company now there\u0026rsquo;s going to be this shadow LLM use where you can\u0026rsquo;t talk about it because maybe you\u0026rsquo;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\u0026rsquo;t do it on their work computers, they\u0026rsquo;re pulling up their phones and using ChatGPT.\nRavi: Absolutely. It\u0026rsquo;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\u0026rsquo;s not getting shared back inappropriately. But definitely there are companies just trying to ban it, and I think that\u0026rsquo;s a huge mistake. You\u0026rsquo;re going to make your employees less productive, plus they\u0026rsquo;re just going to go off and do it on the side anyway.\nTim: I know for a fact that\u0026rsquo;s happening. I talk to a lot of people doing that kind of thing. There\u0026rsquo;s basically no way to stop it unless you\u0026rsquo;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.\nI work at a bank where we have particular data privacy risks, but I\u0026rsquo;ve been very impressed—they\u0026rsquo;ve been making an effort. We explored Amazon\u0026rsquo;s CodeWhisperer offering, did a company-blessed trial, but it ended up not being very good. Now we\u0026rsquo;re doing a trial with Copilot, and I\u0026rsquo;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.\nImagining 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, \u0026ldquo;Can we use these tools?\u0026rdquo;\nRavi: As you said, there\u0026rsquo;s no going back. Increasingly it\u0026rsquo;s just going to become a normal part of the dev experience. It would be the equivalent of saying, \u0026ldquo;Hey, you can\u0026rsquo;t use an IDE to program.\u0026rdquo;\nWhere LLMs Fall Short Tim: Have you come across patterns or particular problems where the tools have fallen short? Things you know you aren\u0026rsquo;t going to be able to get answers for?\nRavi: All the time. I don\u0026rsquo;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\u0026rsquo;re not really designed to think at that system level. They\u0026rsquo;re still really good at thinking in terms of paragraphs instead of books, for example.\nI think this is changing quickly. I\u0026rsquo;m really curious about how chain of thought changes this. I\u0026rsquo;m sure there\u0026rsquo;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\u0026rsquo;d like to think in terms of abstractions and go up levels. I\u0026rsquo;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.\nTim: We probably should have opened this by explaining what models we\u0026rsquo;re on now for people listening in the future.\nRavi: Yeah, Claude 3.5 and ChatGPT-4 and O1.\nTim: The thing that\u0026rsquo;s been interesting to me recently—I don\u0026rsquo;t get to use Cursor in a work context, only in personal projects—but Cursor has this Composer element where you\u0026rsquo;re giving it free rein to understand the whole repository and make changes across multiple files.\nI have personal website projects I\u0026rsquo;d like to do, but I don\u0026rsquo;t have front-end knowledge and very limited JavaScript knowledge. I\u0026rsquo;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\u0026rsquo;ve done it, I\u0026rsquo;ve hit a wall where it starts cycling through wrong solutions to something.\nRavi: Yeah.\nTim: I haven\u0026rsquo;t spent much time on it—literally hours. But I\u0026rsquo;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\u0026rsquo;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.\nThe point is that it\u0026rsquo;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\u0026rsquo;t perfect at writing new code, just the ability to open a repository and have an LLM understand it and explain it to you—how many of us have spent so many hours coming into a company, trying to understand the codebase? I think it\u0026rsquo;s going to be really valuable to just open it up and ask your robot, \u0026ldquo;What is this codebase? What parts are important? How do I even run this?\u0026rdquo;\nRavi: I love it for that. One thing I\u0026rsquo;ll often do in a Claude session is load it up with multiple parts of my code and give a brief description: \u0026ldquo;I\u0026rsquo;m building a recommender system, here\u0026rsquo;s some context about it.\u0026rdquo; 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.\nBut there is a major danger zone in current LLMs, and that\u0026rsquo;s the context limit. What I call it is the LLM goes off the rails. Its error rate or intelligence starts going down at some point. It\u0026rsquo;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.\nAlso, 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, \u0026ldquo;Tell me some ways I can speed it up.\u0026rdquo; It started rewriting it. I could run the code and see the time—maybe it starts at 200 milliseconds, gets to 180, 150. It\u0026rsquo;s actually doing something.\nThen I keep telling it, \u0026ldquo;Can you make it faster?\u0026rdquo; And it\u0026rsquo;s always like, \u0026ldquo;Of course!\u0026rdquo; But then you see the time actually start going up—250, 300 milliseconds. There\u0026rsquo;s a certain overconfidence in these LLMs. It\u0026rsquo;s like, \u0026ldquo;Yeah, I can do whatever you ask me.\u0026rdquo;\nTim: That\u0026rsquo;s an area where it requires not checking out. Your brain has to keep working. It\u0026rsquo;s going to be interesting, and we\u0026rsquo;re at such an early stage.\nRavi: It\u0026rsquo;ll be interesting to listen to this in a few years and just be like, \u0026ldquo;Wow, I can\u0026rsquo;t believe how basic it was.\u0026rdquo;\nConcerns About Skill Atrophy Tim: Do you have any concerns about losing particular skills? You\u0026rsquo;re thinking about generating code at the function level—are you worried it\u0026rsquo;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?\nRavi: That\u0026rsquo;s a good question. I feel like I should be worried about it. But honestly, because I don\u0026rsquo;t think algorithms are getting worse, there are certain things that I\u0026rsquo;m slowly deciding I no longer need to know. I now trust it to abstract some things away from me.\nA 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 \u0026ldquo;look at the docs, read the docs, understand it\u0026rdquo; with any of the popular libraries.\nNow 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\u0026rsquo;m okay with being rusty at putting a visualization into code. Increasingly, these small three or four line pieces of code, I\u0026rsquo;m going to trust them. I can go back and read it and understand what I was doing, but I\u0026rsquo;m okay not knowing that syntax. I\u0026rsquo;m okay just looking at the visualization and saying \u0026ldquo;cool, it did what I wanted.\u0026rdquo;\nTim: The flip side is that you also have the ability to ask the LLM to go back and explain what you did.\nRavi: That\u0026rsquo;s a great point.\nTim: I\u0026rsquo;ve done that with SQL queries. It doesn\u0026rsquo;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 \u0026ldquo;can you add comments to this, help me explain it\u0026rdquo;—without changing anything—is really powerful.\nAgain, it\u0026rsquo;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\u0026rsquo;t punt too much on what you know. But at the same time, we\u0026rsquo;ve got these assets that are huge, and don\u0026rsquo;t ignore that either.\nRavi: I completely agree. I don\u0026rsquo;t want to sound like an old dude lecturing young people, but there\u0026rsquo;s a real danger that you get mid-level in your career and it turns out you don\u0026rsquo;t really know what you\u0026rsquo;re doing. I think that wasn\u0026rsquo;t really possible when we were starting off. That\u0026rsquo;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.\nThe Foundation of Math and Learning Tim: My background was in math. There\u0026rsquo;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.\nI imagine having studied math—it\u0026rsquo;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\u0026hellip; 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.\nI don\u0026rsquo;t really know what the solution is, but I\u0026rsquo;m glad I didn\u0026rsquo;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\u0026rsquo;s no reason you wouldn\u0026rsquo;t want to use that to advance problem solving. It\u0026rsquo;s an interesting balance we\u0026rsquo;re going to have to figure out.\nHave you looked at any of the AI code review tools yet?\nRavi: I probably should. What\u0026rsquo;s out there?\nTim: I haven\u0026rsquo;t used them either. I\u0026rsquo;ve only heard people talk about them. There are integrations with GitHub that go in and add comments. I don\u0026rsquo;t know how you tune those, but I think it\u0026rsquo;ll be interesting in the future.\nOne thing I\u0026rsquo;m really excited about—hopefully going to have another episode soon with a mutual friend of ours who\u0026rsquo;s running a small company and has been able to do way more because of LLM-assisted development. I think he\u0026rsquo;s only himself and one other engineer.\nI\u0026rsquo;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\u0026rsquo;t always need code review, but it\u0026rsquo;s easy to miss things.\nRavi: 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 \u0026ldquo;this function doesn\u0026rsquo;t look right\u0026rdquo; but it\u0026rsquo;s actually right, maybe you wasted 30 seconds. I can imagine it will be much more thorough than the typical PR review I\u0026rsquo;m used to—\u0026ldquo;looks good to me\u0026rdquo; or \u0026ldquo;hey, why\u0026rsquo;d you do it this way?\u0026rdquo;\nTim: 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 \u0026ldquo;maybe this is too complex and we could split it up\u0026rdquo; would be really cool. The next step beyond that is maybe automated suggested changes—open a PR into your PR with recommended changes.\nRavi: Just click through the changes. It\u0026rsquo;s interesting to see how we\u0026rsquo;re slowly iterating towards more of our code being written and reasoned about by AI. We\u0026rsquo;re clearly not there, but I think we\u0026rsquo;ll keep seeing iteratively the LLMs being able to take over more of it. I\u0026rsquo;m very curious if there\u0026rsquo;s an endpoint, or maybe 10 years from now they\u0026rsquo;re just doing our jobs for us. But we\u0026rsquo;re just in the early stages where it\u0026rsquo;s a huge boost to our productivity, and we\u0026rsquo;re still ultimately in charge of our code.\nLooking Forward Tim: 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?\nRavi: I think there\u0026rsquo;s probably a ton of low-hanging fruit specifically in our space. One thing I mentioned earlier is LLMs actually running code. I\u0026rsquo;m sure that\u0026rsquo;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.\nClearly the models are getting better. We\u0026rsquo;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\u0026rsquo;m sure we\u0026rsquo;ll hit limits with the current technology—I\u0026rsquo;m deeply skeptical that we\u0026rsquo;re going to hit general intelligence with what we have now. But for the last two years, we\u0026rsquo;ve seen consistent, huge jumps in performance. I believe very much in momentum. I think we\u0026rsquo;re on the path towards LLMs owning more of a codebase. We\u0026rsquo;re not there yet, but I think we\u0026rsquo;re moving in that direction.\nTim: It\u0026rsquo;ll be fun to see. It\u0026rsquo;s very interesting, and very hard to make predictions. There are a lot of crazy predictions out there.\nRavi: Again, I don\u0026rsquo;t want to come back and listen to this and be like, \u0026ldquo;Wow, I was so wrong.\u0026rdquo;\nTim: Could we have even imagined this? You\u0026rsquo;ve been doing machine learning a few years longer than me, but in 2010, could we have imagined this? It seems wild. A \u0026ldquo;language model\u0026rdquo; back then was a Markov chain model. They were so dumb. You\u0026rsquo;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.\nJust the ability to generate text that even looks sensible, much less actually is sensible, is wild. It\u0026rsquo;s going to be really cool to see what that turns into.\nRavi: 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\u0026rsquo;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.\nTim: Very cool. All right, well if you don\u0026rsquo;t have anything else to add, we\u0026rsquo;ll wrap up there. This has been a great discussion. I appreciate your perspective.\nRavi: Likewise. Thanks for having me.\n","date":"2024-09-18T00:00:00Z","image":"https://tdhopper.com/images/robot-assistant.png","permalink":"https://tdhopper.com/blog/ai-assisted-programming-with-ravi-mody/","title":"AI Assisted Programming with Ravi Mody"},{"content":"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\u0026rsquo;t come naturally to me, and it\u0026rsquo;s a skill I\u0026rsquo;ve developed over the past 15 years.\nHere are some of the things I do to be productive:\nWrite things down I loosely follow the Getting Things Done method developed by David Allen. The most important aspect to me has been capturing tasks into a single inbox. Capturing tasks means I don\u0026rsquo;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\u0026rsquo;t get lost in piles of Post-Its and napkins.\nI use the Drafts app on iOS and macOS, which lets me capture notes with zero friction. If it\u0026rsquo;s a task I\u0026rsquo;m writing down, I use a Drafts action to send it to my Omnifocus 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).\nUsing nagging reminders Tasks that must be done at a certain time aren\u0026rsquo;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 Due app on iOS, which will remind me repeatedly until the task is complete.\nTame email I keep my email tidy. A big part of this is using Sanebox to keep unimportant emails out of my inbox; Sanebox automatically detects, labels, and archives inbox clutter like receipts, marketing, and newsletters. These typically don\u0026rsquo;t require any action and can be triaged as necessary. I aggressively hit the \u0026ldquo;Unsubscribe\u0026rdquo; button on things I don\u0026rsquo;t need to be bothered with (including most marketing).\nWhen 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 Omnifocus inbox and make it a task.\nWhen this works well, my email inbox is normally empty.\nForget passwords 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.\nEvery password I use lives in my 1Password account. I also use it to manage 2-factor authentication pins, credit cards, WiFi passwords, and personal information (social security numbers, driver\u0026rsquo;s license scans, etc.). Using the 1Password keyboard shortcuts, I can quickly log in to any of my accounts.\nI know my 1Password master passwords and almost no other passwords to my accounts. They\u0026rsquo;re typically long, randomly generated mixtures of characters.\n1Password also lets me have a shared vault with my wife, which is invaluable for our shared services.\nKeep Slack under control Like many tech workers, much of my day is spent in Slack. A few years ago, I stumbled upon Michael Lopp\u0026rsquo;s guide to optimizing Slack. His approach greatly improved my experience with Slack and kept it from being a constant distraction. Go read his post.\nDon\u0026rsquo;t waste 15 minutes Like the next guy, I\u0026rsquo;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.\nWhen 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\u0026rsquo;m regularly surprised that daunting tasks can be finished in little time.\nDon\u0026rsquo;t waste idle brain cycles 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\u0026rsquo;m a strong believer in the power of noodling on ideas.\nWhen 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.\n","date":"2024-05-13T14:14:00Z","image":"https://tdhopper.com/images/todo.png","permalink":"https://tdhopper.com/blog/how-i-am-productive/","title":"How I am Productive"},{"content":" When someone says, “I want a programming language in which I need only say what I wish done,” give him a lollipop. (Alan Perlis, Epigrams in Programming)\nPeople love to talk trash about programming languages on Twitter. Every day there\u0026rsquo;s a new viral tweet about the inadequacies of Python.\nI have worked with many programming languages and studied others in school, but I\u0026rsquo;ve spent most of the last ten years deep into Python. Of course, Python has warts and wrinkles; I\u0026rsquo;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 Ruff).\nI\u0026rsquo;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\u0026rsquo;t miss the mark in his 1979 paper with Plauger:\n\u0026hellip;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.\nPeople 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.\nBjarne Stroustrup, creator of C++, says, \u0026ldquo;There are only two kinds of languages: the ones people complain about and the ones nobody uses.\u0026rdquo; He\u0026rsquo;s right, and I\u0026rsquo;m guessing we\u0026rsquo;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.\n","date":"2024-04-03T11:46:00Z","image":"https://tdhopper.com/images/complaining.png","permalink":"https://tdhopper.com/blog/good-programming-languages-are-nice-but-not-vital/","title":"Good programming languages are nice but not vital"},{"content":"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.\nI\u0026rsquo;m excited to announce that I\u0026rsquo;m writing the Python Developer Tooling Handbook, 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.\nThe book is currently a work in progress, and I\u0026rsquo;m excited to share it with you when it\u0026rsquo;s ready. If you want to be notified as soon as it\u0026rsquo;s released, please consider signing up for my mailing list here.\n","date":"2024-02-10T16:31:16.906Z","image":"https://tdhopper.com/images/pydevtools-announcement.png","permalink":"https://tdhopper.com/blog/announcing-the-python-developer-tooling-handbook/","title":"Announcing the Python Developer Tooling Handbook"},{"content":"Good ideas are hard to come by, but I find I\u0026rsquo;m often looking in the wrong places.\nWhen I\u0026rsquo;m trying to develop new ideas, sitting at my desk staring at my computer (and all the world\u0026rsquo;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.\nI\u0026rsquo;m inspired by stories of brilliant people coming up with ideas at surprising times:\nHirotugu Akaike said the idea for his eponymous Akaike Information Criterion while taking his seat on the bus. Grant Wood, most famous for painting American Gothic, once said \u0026ldquo;All the really good ideas I\u0026rsquo;ve ever had came to me while I was milking a cow.\u0026rdquo; Stanisław Ulam, a key physicist in the Manhattan Project, recounted how he thought of Monte Carlo methods while home sick and playing solitaire. (Monte Carlo methods essential in modern artificial intelligence model training!) Paul Dirac, a founder of quantum mechanics, said, \u0026ldquo;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.\u0026rdquo; Of course, this method can be abused. I had a classmate in grad school who used to use \u0026ldquo;you can\u0026rsquo;t force inspiration\u0026rdquo; as an excuse to never do any work. I try to balance these anecdotes with Stephen King\u0026rsquo;s proverb: \u0026ldquo;Amateurs sit and wait for inspiration, the rest of us just get up and go to work.\u0026rdquo;\n","date":"2024-02-05T23:48:54.527Z","image":"https://tdhopper.com/images/inspiration.png","permalink":"https://tdhopper.com/blog/on-inspiration/","title":"On Inspiration"},{"content":"In 1986, Fred Brooks published \u0026ldquo;No Silver Bullet—Essence and Accident in Software Engineering\u0026rdquo; where he argues that there is no silver bullet that \u0026ldquo;to make software costs drop as rapidly as computer hardware costs do\u0026rdquo;.\nSoftware progress doesn\u0026rsquo;t follow Moore\u0026rsquo;s Law.\nManagers characterize software projects as \u0026ldquo;usually innocent and straightforward, but\u0026hellip; capable of becoming a monster of missed schedules, blown budgets, and flawed products.\u0026rdquo;\nOuch.\nThe beginning of hope [in creating software], however, is realizing \u0026ldquo;that progress would be made stepwise, at great effort, and that a persistent, unremitting care.\u0026rdquo;\nSoftware\u0026rsquo;s difficulty has two aspects \u0026ldquo;essence, the difficulties inherent in the nature of software, and accidents, those difficulties that today attend its production but are not inherent.\u0026rdquo;\n⚠️Brooks\u0026rsquo; central thesis:\nI ʙᴇʟɪᴇᴠᴇ ᴛʜᴇ ʜᴀʀᴅ ᴘᴀʀᴛ ᴏғ ʙᴜɪʟᴅɪɴɢ 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.\n▶ Essential challenges of software have 4 aspects: complexity, conformity, changeability, and invisibility.\nComplexity: \u0026ldquo;Software entities are more complex for their size than perhaps any other human construct because no two parts are alike.\u0026rdquo; Because complexity is essential to software, in trying to abstract away complexity, you \u0026ldquo;often abstract away its essence.\u0026rdquo; Conformity: Our software is often forced to conform to other interfaces (perhaps because it comes after hardware, or perhaps simply because its \u0026ldquo;perceived as the most conformable\u0026rdquo;). \u0026ldquo;This complexity cannot be simplified out by any redesign of the software alone.\u0026rdquo; Changeability: \u0026ldquo;Aʟʟ sᴜᴄᴄᴇssғᴜʟ sᴏғᴛᴡᴀʀᴇ ɢᴇᴛs ᴄʜᴀɴɢᴇᴅ.\u0026rdquo; 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). Invisibility: \u0026ldquo;The reality of software is not inherently embedded in space.\u0026rdquo; Representations like directed graphs generally prove inadequate for fully representing software. Progress has been made in removing 𝑎𝑐𝑐𝑖𝑑𝑒𝑛𝑡𝑎𝑙 difficulties of software:\nHigh-level languages Time sharing systems Unified programming environments 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.\nSome means of attacking the 𝑒𝑠𝑠𝑒𝑛𝑡𝑖𝑎𝑙 complexity\nBuy vs. build: \u0026ldquo;The cost of software has always been development cost, not replication cost.\u0026rdquo; Thus, if we 𝑐𝑎𝑛 buy it, we probably should.\nRequirements refinement and rapid prototyping: \u0026ldquo;Tʜᴇ ʜᴀʀᴅᴇsᴛ sɪɴɢʟᴇ ᴘᴀʀᴛ ᴏғ ʙᴜɪʟᴅɪɴɢ ᴀ sᴏғᴛᴡᴀʀᴇ sʏsᴛᴇᴍ ɪs ᴅᴇᴄɪᴅɪɴɢ ᴘʀᴇᴄɪsᴇʟʏ ᴡʜᴀᴛ ᴛᴏ ʙᴜɪʟᴅ.\u0026rdquo; \u0026ldquo;Therefore, the most important function that the software builder performs for the client is the 𝑖𝑡𝑒𝑟𝑎𝑡𝑖𝑣𝑒 𝑒𝑥𝑡𝑟𝑎𝑐𝑡𝑖𝑜𝑛 𝑎𝑛𝑑 𝑟𝑒𝑓𝑖𝑛𝑒𝑚𝑒𝑛𝑡 𝑜𝑓 𝑡ℎ𝑒 𝑝𝑟𝑜𝑑𝑢𝑐𝑡 𝑟𝑒𝑞𝑢𝑖𝑟𝑒𝑚𝑒𝑛𝑡𝑠.\u0026rdquo;\nBrooks again:\nI 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.\nTo serve iterative requirements gathering, a goal should be getting something working as quickly as possible.\n(A side benefit of this is it improves morale for developers. \u0026ldquo;Enthusiasm jumps when there is a running system, even a simple one.\u0026rdquo;)\nGreat software design comes from great designers. Software is a creative process. \u0026ldquo;Sound methodology can empower and liberate the creative mind; it cannot inflame or inspire the drudge.\u0026rdquo; Organizations would do well to focus on identifying great software designers and working hard to \u0026ldquo;grow great designers\u0026rdquo;.\nBrooks:\nSkepticism is not pessimism, however. \u0026hellip; 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.\nRead all of Brooks\u0026rsquo; \u0026ldquo;No Silver Bullet\u0026rdquo; here.\nsilver bullet flickr photo by eschipul shared under a Creative Commons (BY-SA) license\n","date":"2022-11-18T20:15:17Z","image":"https://tdhopper.com/silver_bullet.png","permalink":"https://tdhopper.com/blog/no-silver-bullet/","title":"No Silver Bullet"},{"content":"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\u0026rsquo;ve gotten interested in Strongman as a sport through YouTube. Yesterday, I entered my first strongman competition.\nI 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\u0026rsquo;s birth. Nevertheless, I had a lot of fun and learned much about competing.\nEvents Farmer’s Carry Medley 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.\nAxle Deadlift Ladder There were three axle bars on wagon wheels (an 18\u0026quot; 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.\nI 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).\nSandbag Race There were two sandbags (200 and 225 lbs). Each had to be carried around a cone 40’ away and back to the start line.\nI’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.\nViking Press The Viking press was a natural log mounted on a pivot. It was supposed to be equivalent to a 225-lb overhead press.\nI 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.\nI got eight reps and was pleased with that. My final rep felt like my spine was about to pop, so I gave up.\nSandbag Battle 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.\nIt 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.\nI 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.\nSandbag race Farmer\u0026#39;s carry ","date":"2022-05-20T12:30:00Z","image":"https://tdhopper.com/images/strongman.png","permalink":"https://tdhopper.com/blog/my-first-strongman-competition/","title":"My first strongman competition"},{"content":"Listen Links Jowanza\u0026rsquo;s Pulsar talks Jowanza\u0026rsquo;s Twitter Jowanza\u0026rsquo;s website Pulsar homepage The Log: What every software engineer should know about real-time data\u0026rsquo;s unifying abstraction by Jay Kreps StreamNative - Hosted Pulsar Datastax Astra Streaming - Hosted Pulsar Cloud Native Computing Foundation Pulsar Functions Pulsar IO Pulsar SQL Apache ZooKeeper Apache BookKeeper Subscribe RSS Feed Apple Podcasts Spotify Overcast ","date":"2022-02-07T00:00:00Z","image":"https://tdhopper.com/images/podcast.png","permalink":"https://tdhopper.com/blog/apache-pulsar-with-jowanza-joseph/","title":"Apache Pulsar with Jowanza Joseph"},{"content":"Over the last 6 years, I\u0026rsquo;ve been able to help the teams I have been part of develop guidelines for code review. Many teams require \u0026ldquo;code review\u0026rdquo; 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\u0026rsquo;t have to adopt my guidelines for review, but I would encourage your team to set aside some time to make sure you all mean the same thing by \u0026ldquo;code review\u0026rdquo;.\nWhat is a code review for? Code review has multiple benefits and objectives including:\nCode correctness: someone seeing your code with fresh eyes may help uncover bugs.\nCode familiarity: reading one another\u0026rsquo;s code keeps everyone familiar with the codebase.\nDesign feedback: a constantly evolving code base is a fight against complexity; reviewers can guide one another on keeping the codebase coherent and maintainable.\nMutual learning: the reviewer and author will inevitably learn from one another.\nRegression protection: future contributors to the code base have checks against breaking essential functionality; importantly, this reduces fear of making necessary improvements to the code.\nWhat code reviews aren\u0026rsquo;t An opportunity for the reviewer to impose their idiosyncrasies.\nAn opportunity for the developer to push off responsibility (e.g. correctness) for their code to the reviewer.\nAn opportunity to demand perfection (Per Google’s Code Review Guidelines: A key point here is that there is no such thing as \u0026ldquo;perfect\u0026rdquo; code—there is only better code).\nOpening Pull Requests Take care to write informative commit messages. This helps your reviewer understand the decisions you made.\nConsider 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?\nKeep pull requests short whenever possible. git --shortstat origin/main will show you the size of your branch\u0026rsquo;s diff from main; under 400 lines changed is a great goal.\nWrite a pull request description that sets your reviewer up for success by helping them understand what the PR intends to accomplish.\nIf you have a particularly complex PR, consider doing a code walk-through with a reviewer first. New code should ordinarily come with new tests.\nReviewing Pull Requests Have a positive, constructive, helpful attitude.\nWait for continuous integration tasks to complete. Let the author resolve any test failures before beginning your review.\nAs much as possible, configure your continuous integration to enforce your team\u0026rsquo;s style guidelines and look for line-level bugs. In Python, this might include running mypy and ruff. Automation like this has multiple benefits: they\u0026rsquo;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. Things to evaluate:\nDoes 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.)\nWas the new code put in the right place?\nIs the new code unnecessarily complex—or unnecessarily clever?\n\u0026ldquo;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.\u0026rdquo; – Brian Kernighan Does the new code do all it can to avoid adding to the overall complexity of our codebase?\nDid the author write tests for the new code?\nClarify when a comment is minor or not essential for merging (for example, preface with \u0026ldquo;Nit:\u0026rdquo;).\nIf a PR is too large for you to reasonably review, you can ask the author to split it into multiple PRs.\nAdditional Reading Google\u0026rsquo;s excellent guide to code review (Note: CL=change list=pull request)\nGitlab\u0026rsquo;s Code Review Guidelines\nProven Code Review Best Practices from Microsoft\nThoughtbot Code Review Guidelines\nCode reviews at Slack\nCurated list of articles about code review from John Barton\n","date":"2021-12-03T15:37:00Z","image":"https://tdhopper.com/images/code-review.png","permalink":"https://tdhopper.com/blog/code-review-guidelines/","title":"Code Review Guidelines for Data Science Teams"},{"content":"As someone providing engineering and infrastructure support for a small data science team, I\u0026rsquo;ve seen firsthand how the right tool can dramatically improve team productivity. Our team recently discovered Intake, a data cataloging tool by Anaconda that transformed how we work with Python-based data science projects.\nLike 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 Intake, our data scientists faced challenges in accessing datasets. They needed to:\nKnow the dataset existed Locate its specific storage location Understand the download process Determine which Python library and arguments were needed to open it This cognitive overhead was taking valuable time away from actual data science work.\nIntake 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:\n1 catalog.category.dataset.read() The beauty of this approach is that data scientists don\u0026rsquo;t need to know the underlying S3 infrastructure; they can focus purely on their analytical work.\nTo make this system reliable, we implemented several key components:\nA YAML-based catalog definition system for organizing our data assets An internal Python package distributed through our private repository Nightly automated tests ensuring dataset accessibility A custom Intake plugin (which we open-sourced as intake-pattern-catalog) for handling parameterized file paths and versioned files This seemingly simple change had profound effects on our workflow. It eliminated:\nFriction in data access Redundant boilerplate code Reliance on unwritten institutional knowledge The constant need to search StackOverflow for boto3 solutions 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.\n","date":"2021-11-02T12:20:00Z","image":"https://tdhopper.com/images/shelves.png","permalink":"https://tdhopper.com/blog/organizing-research-data-with-intake/","title":"Organizing research data with Intake"},{"content":"Listen Links Brenton\u0026rsquo;s Twitter Brenton\u0026rsquo;s Instagram Brenton\u0026rsquo;s Website Ocean Engineering at Florida Atlantic University Subscribe RSS Feed Apple Podcasts Spotify Overcast ","date":"2021-09-15T00:00:00Z","image":"https://tdhopper.com/images/podcast.png","permalink":"https://tdhopper.com/blog/ocean-engineering-to-data-science/","title":"Ocean Engineering to Data Science"},{"content":"Listen Links Roy\u0026rsquo;s Twitter Roy\u0026rsquo;s Book Data Scientists at Work Data Scientist: The Sexiest Job of the 21st Century by Thomas H. Davenport and D.J. Patil Building Data Science Teams by DJ Patil (2011) Adversarial Learning: Stories of Degradation and Humiliation — Podcast about bad interview experiences Subscribe RSS Feed Apple Podcasts Spotify Overcast ","date":"2021-06-30T00:00:00Z","image":"https://tdhopper.com/images/podcast.png","permalink":"https://tdhopper.com/blog/hiring-data-scientists-with-roy-keyes/","title":"Hiring Data Scientists with Roy Keyes"},{"content":"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\u0026rsquo;s what I\u0026rsquo;ve learned about these formats and the tools for working with them.\nGRIB: GRIdded BInary Data 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\u0026rsquo;ll encounter today is GRIB2.\nnetCDF 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.\nNetCDF 1.0 was released in 1998, and it\u0026rsquo;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.\nYou\u0026rsquo;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 ncinfo or h5dump.\nThe Data Model A netCDF file has three key components:\nDimensions: the shape of variables Variables: the actual data Attributes: metadata for the dataset or individual variables NetCDF-4 also adds groups with named subgroups.\nncdump CLI ncdump dumps a netCDF file as plain text. Useful flags:\n-v: select a variable -h: header only -x: XML output ncgen ncgen turns Common Data Format (CDL) text into a netCDF binary file:\n1 ncgen -b example.cdl Both ncgen and ncdump are part of the core netCDF library.\nxarray 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.\n1 2 3 import xarray as xr ds = xr.open_dataset(\u0026#39;example.nc\u0026#39;) ds.to_dataframe() NetCDF is the standard way to serialize data from xarray.\nNCO (NetCDF Operators) 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.\nncks (Kitchen Sink) ncks combines most features of ncdump and nccopy with extra capabilities for extraction, hyperslabbing, subsetting, and translation:\n1 2 3 4 5 6 7 8 9 10 11 # Copy a file (the copy includes history) ncks example.nc copy.nc # Select a variable ncks -v WIND_6000maboveground wrf.nc # Print as CDL (like ncdump) ncks --cdl example.nc # Compress with level 5 compression (-4 required for netCDF4) ncks wrf.nc -4 -L5 wrf_compressed.nc There\u0026rsquo;s also extensive functionality for concatenation, appending, summary statistics, and comparing files.\nOther NCO Tools ncrcat/ncecat: concatenation (which one to use depends on whether you already have a record dimension like time) ncra: average over time ncwa: weighted averages Converting netCDF to GRIB The CDO (Climate Data Operators) library can convert between formats:\n1 cdo -f grb2 copy wrf.nc wrf_from_nc.gr2 Fair warning: this doesn\u0026rsquo;t always work cleanly.\nTakeaways Complicated datasets are complicated! There is more than one way to do everything. With some basic commands, you can inspect any of these file types. Converting between formats is often straightforward. All of these tools are available through Conda and conda-forge. Resources CDO (Climate Data Operators) GRIB tools examples README on Formats (UCAR) Intro to GRIB2 (PDF) GRIB2 Spec NetCDF Intro (2014 Presentation) NCO Basics (PDF) ","date":"2021-06-21T00:00:00Z","permalink":"https://tdhopper.com/blog/weather-forecast-data-formats-and-tools/","title":"Weather Forecast Data Formats and Tools"},{"content":"On January 7, 2011, I tweeted, \u0026ldquo;Trying to learn Python. We\u0026rsquo;ll see if this keeps up once classes start.\u0026rdquo;\nOn January 20, 2011, ten years ago today, I bought Mark Lutz\u0026rsquo;s Learning Python from O\u0026rsquo;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\u0026rsquo;ve written Python on more days than I haven\u0026rsquo;t.\nBefore discovering the data science Twitter community in late 2010, I\u0026rsquo;d only heard of Python through another student researcher in my 2007 REU, who had used Python for some graphics programming research. On Twitter, people like Hilary Mason, John Cook, and Chris Fonnesbeck talked warmly about using Python in their scientific work.\nIn 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\u0026rsquo;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\u0026rsquo;t program.\nI struggled to use Python 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 (after segfaulting Orange) for my internship at Kiva Systems and dove into R, where I could install packages more reliably and use ggplot2.\nFor 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\u0026rsquo;s wishes that I use C++). Scipy Superpack for installing the Scipy stack was invaluable (thankfully now replaced by Wheels and Conda).\nMy experience with Python was a big reason I got hired at RTI International 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 \u0026ldquo;Python for statisticians\u0026rdquo; seminar soon after joining RTI.\nFrom RTI, I joined Parsely, which was like a Python boot camp working with Andrew Montalenti and others. Parsely uses Python across their full stack, and it was an eye-opening and educational year for me.\nSince then, I\u0026rsquo;ve worked at a variety of companies where I\u0026rsquo;ve been able to use Python for training machine learning models, building machine learning platforms, writing Gibbs samplers for nonparametric Bayes, building data engineering pipelines, software testing, etc.\nI use Python almost every single day for work and a lot of personal projects. I\u0026rsquo;ve been able to speak at 3 PyData conferences, a Scipy conference, and a number Triangle Python Users Groups. I had a contribution merged into CPython in 2019 and have contributed to many other open-source projects.\nI\u0026rsquo;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.\n","date":"2021-01-20T13:58:16Z","image":"https://tdhopper.com/images/minimalist-landscape-with-mouse-and-snake.png","permalink":"https://tdhopper.com/blog/learning-python/","title":"The Programming Book That Made My Career"},{"content":"Listen Links Willem\u0026rsquo;s Twitter Adam\u0026rsquo;s Twitter Feast: feature store for Machine Learning (2020 talk) Feast Tecton GoJek on Wikipedia featurestore.org Subscribe RSS Feed Apple Podcasts Spotify Overcast ","date":"2021-01-16T00:00:00Z","image":"https://tdhopper.com/images/podcast.png","permalink":"https://tdhopper.com/blog/feature-stores-with-willem-pienaar/","title":"Feature Stores with Willem Pienaar"},{"content":"Listen Links Adam\u0026rsquo;s Twitter Subscribe RSS Feed Apple Podcasts Spotify Overcast ","date":"2020-10-10T00:00:00Z","image":"https://tdhopper.com/images/podcast.png","permalink":"https://tdhopper.com/blog/evolution-of-a-data-scientist-with-adam-laiacano/","title":"Evolution of a Data Scientist with Adam Laiacano"},{"content":"Git has a git check-ignore -v \u0026lt;pathname\u0026gt; option which will explain to you why a given file is ignored by the repository. This is helpful for debugging .gitignore and other exclude options.\nI hope you never need this, but you\u0026rsquo;ll be glad if you do.\n","date":"2020-09-18T13:10:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/git-check-ignore/","title":"git check-ignore"},{"content":"Listen Links Tala Security Mike\u0026rsquo;s Twitter Mike\u0026rsquo;s LinkedIn How I Became a Data Scientist Despite Being a Math Major Subscribe RSS Feed Apple Podcasts Spotify Overcast ","date":"2020-07-23T00:00:00Z","image":"https://tdhopper.com/images/podcast.png","permalink":"https://tdhopper.com/blog/customer-focused-data-science-with-mike-rogers/","title":"Customer Focused Data Science with Mike Rogers"},{"content":"Recently, I\u0026rsquo;ve had my first opportunity to dive into the Go programming language. Most of my career has been as a Python developer, but given how frequently they are compared, Go seemed like a natural language to try.\nBefore I move on to other things, I wanted to capture my reflections on Go, particularly as it compares to my experience with Python.\nThe good Go runs really quickly 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.\ngo build just works One of the notorious challenges of Python is with packaging and deployment. With Go, go build downloads dependencies and generates a binary that can be run anywhere.\nEven more remarkably, Go allows you to build binaries for multiple system architectures 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.\nGo Modules is a big improvement When I first tried Go several years ago, I didn\u0026rsquo;t last long beyond being told that all my source code and dependencies had to live in my GOPATH directory.\nWith the recent introduction of Go Modules, this absurd restriction has been lifted. Go projects can live anywhere on your computer, and the Go compiler automatically downloads and installs your dependencies.\nGo bans unused variables and imports 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.\nVS Code wins again The Go extension for VS Code, like the Python extension, is powerful, helpful, and free.\nGarbage collection is nice I can\u0026rsquo;t comment on the performance of the Go garbage collector, however I\u0026rsquo;m glad it has one. Go is heavily inspired by C, but it\u0026rsquo;s better for almost everyone by not making us manage our own memory.\nJSON marshaling is neat For better or worse, JSON data makes the world go \u0026lsquo;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\u0026rsquo;re often writing serialization boilerplate or reaching for a third-party library like marshmallow, this felt refreshingly clean.\nThe bad Tooling is rough around the edges Go\u0026rsquo;s tooling mostly works well, but I found some rough edges. The go get command conflates downloading a dependency with installing a binary, which is confusing. And while gofmt is great in principle (I wish Python had a single blessed formatter1), the broader tooling ecosystem felt immature compared to what I\u0026rsquo;m used to in Python.\nSimple things are hard In Python, if I want to filter a list, I write a list comprehension:\n1 evens = [x for x in numbers if x % 2 == 0] In Go, this requires a manual for loop with an append:\n1 2 3 4 5 6 var evens []int for _, x := range numbers { if x % 2 == 0 { evens = append(evens, x) } } This kind of thing comes up constantly. Operations that are one-liners in Python \u0026mdash; filtering, mapping, flattening \u0026mdash; all become multi-line loops in Go. I know Go values explicitness, but there\u0026rsquo;s a point where explicitness just means more code to read and more opportunities for off-by-one errors.\nStandard data structures are limited Go gives you slices and maps, and that\u0026rsquo;s about it. There\u0026rsquo;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 map[string]bool or map[string]struct{}, which works but reads like a hack.\nMore broadly, the standard library doesn\u0026rsquo;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.\nIs the type system all it\u0026rsquo;s chalked up to be? Go\u0026rsquo;s own documentation describes it as \u0026ldquo;a fast, statically typed, compiled language that feels like a dynamically typed, interpreted language.\u0026rdquo; I think they\u0026rsquo;re right that it feels lightweight, but I\u0026rsquo;m not convinced Go\u0026rsquo;s type system catches substantially more bugs than Python with mypy and a good linter.\nGo\u0026rsquo;s type system is simple, which is both its strength and weakness. You get basic type safety, but you don\u0026rsquo;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.\nLack of generics At the time of writing, Go has no generics.2 This means you can\u0026rsquo;t write a function that, say, finds the maximum element of any ordered type. Instead, you write the same function for int, float64, string, and so on \u0026mdash; or you fall back on interface{} and lose the type safety that Go is supposed to provide.\nThe lack of generics is the root cause of the \u0026ldquo;simple things are hard\u0026rdquo; problem. You can\u0026rsquo;t write a generic filter or map function, so you end up writing loops. It\u0026rsquo;s the most commonly cited frustration with Go, and for good reason.\nTesting feels painful Go\u0026rsquo;s built-in testing package is minimal to a fault. There\u0026rsquo;s no built-in assertion library, so instead of writing something like Python\u0026rsquo;s self.assertEqual(got, expected), you write:\n1 2 3 if got != expected { t.Errorf(\u0026#34;got %v, want %v\u0026#34;, got, expected) } For every. Single. Test. Case. It\u0026rsquo;s tedious and error-prone. Python\u0026rsquo;s pytest, with its simple assert 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.\nTable-driven tests are the idiomatic approach in Go, and they\u0026rsquo;re fine for simple cases. But the overall testing experience felt like a step backward from the Python ecosystem.\nNo exception handling 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:\n1 2 3 4 result, err := doSomething() if err != nil { return err } You see this pattern everywhere. 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.\nI understand the argument for explicit error handling \u0026mdash; unhandled exceptions are a real source of bugs. But Go\u0026rsquo;s approach trades one problem for another: instead of accidentally swallowing exceptions, you accidentally ignore returned errors. And the visual noise of if err != nil on every other line is real.\nHaven\u0026rsquo;t gotten into concurrency Go\u0026rsquo;s goroutines and channels are supposedly the crown jewel of the language, and I believe it. Concurrency is the use case where Go\u0026rsquo;s design makes the most sense. Unfortunately, I haven\u0026rsquo;t had the chance to work on anything concurrency-heavy yet, so I can\u0026rsquo;t speak to this from experience. I suspect this is where Go would really shine compared to Python\u0026rsquo;s GIL-constrained threading.\nConclusion Go is a fine language, and I can see why it\u0026rsquo;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\u0026rsquo;t match.\nBut for the kind of work I do \u0026mdash; data processing, scripting, exploratory analysis \u0026mdash; 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\u0026rsquo;s not a trade I need to make.\nIf you\u0026rsquo;re a Pythonista considering Go, I\u0026rsquo;d say: try it. You\u0026rsquo;ll appreciate some things about it, and it\u0026rsquo;ll give you a new perspective on your Python code. But don\u0026rsquo;t feel like you need to switch.\nBlack is close to this, but it\u0026rsquo;s not universally adopted the way gofmt is for Go.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nGo generics were proposed and have been a long time coming. I look forward to seeing how they change the language.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2020-07-20T00:00:00Z","permalink":"https://tdhopper.com/blog/go-from-a-pythonistas-perspective/","title":"Go from a Pythonista's Perspective"},{"content":"Listen Links Fizz Buzz in Tensorflow Livecoding Madness - Let\u0026rsquo;s Build a Deep Learning Library I don\u0026rsquo;t like notebooks Live Coding the Advent of Code Data Science from Scratch Second Edition Fizz Buzz Book: Meditations on Python, mathematics, science, engineering, and design Joel\u0026rsquo;s Twitter Subscribe RSS Feed Apple Podcasts Spotify Overcast ","date":"2020-07-15T00:00:00Z","image":"https://tdhopper.com/images/podcast.png","permalink":"https://tdhopper.com/blog/fizz-buzz-book-with-joel-grus/","title":"Fizz Buzz Book with Joel Grus"},{"content":"In the great #general room There was idle chatter And a red build And a gif of- The cow jumping over the moon\nAnd 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\nGoodnight Slack room Goodnight Zoom Goodnight manager hopping on Zoom Goodnight Jira And the red build Goodnight interns Goodnight mentors Goodnight AWS And goodnight Bash\nGoodnight 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”\nGoodnight Github stars Goodnight asserts Good night pager duty alerts\n","date":"2020-06-23T11:30:43Z","image":"https://tdhopper.com/goodnight-zoom.png","permalink":"https://tdhopper.com/blog/goodnight-zoom/","title":"Goodnight Zoom"},{"content":"I\u0026rsquo;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.\nStart Working Early\nIf you can\u0026rsquo;t find someone to chat with for 30 minutes while making coffee, consider starting your workday instead. Simulate Office Noise\nIf you miss the background noise of your office, try listening to 8 hours of power drill noises here. For a more realistic ambiance, you can overlay it with the most annoying laugh ever here. Afternoon Activities\nWithout a ping-pong table at home, you might find yourself with nothing to do but work in the afternoons. Familiarize Yourself with Remote Tools\nProgrammers, take some time to get used to the essential tools for remote work, such as Jira, GitHub, Slack, and Zoom. Engage in Local Politics\nIf the quarantine lasts longer than expected and you start to miss petty workplace politics, consider joining your local HOA. ","date":"2020-03-16T08:04:00Z","image":"https://tdhopper.com/images/office_ping_pong_game_illustration.png","permalink":"https://tdhopper.com/blog/tips-for-working-from-home/","title":"Tips for Working from Home"},{"content":"Listen Links Josh\u0026rsquo;s twitter Oscar\u0026rsquo;s twitter Josh on Software Engineering Daily Scalding Summingbird Apache Crunch Data Scientist (n.): Person who is better at statistics than any software engineer and better at software engineering than any statistician.\n\u0026mdash; Josh Wills (@josh_wills) May 3, 2012 Subscribe RSS Feed Apple Podcasts Spotify Overcast ","date":"2020-02-19T00:00:00Z","image":"https://tdhopper.com/images/podcast.png","permalink":"https://tdhopper.com/blog/ten-years-of-data-science-with-josh-wills-and-oscar-boykin/","title":"Ten Years of Data Science with Josh Wills and Oscar Boykin"},{"content":"I\u0026rsquo;m always annoyed when the pre-flight instructions inform us that the airline\u0026rsquo;s main priority is our safety.\nTurns out Thomas Aquinas observed this 700 years ago:\nIf the highest aim of a captain were to preserve his ship, he would keep it in port for ever.\n","date":"2019-10-09T09:13:00Z","image":"https://tdhopper.com/images/safetyship.png","permalink":"https://tdhopper.com/blog/safety-first/","title":"Safety first?"},{"content":"A few weeks back, I was browsing Google Maps and noticed the word \u0026ldquo;DEMO\u0026rdquo; written in faint green letters in rural South Sudan. A journalist came across this tweet and wrote about it for bbc.com.\n🤔 https://t.co/TOJvHsxdD9 pic.twitter.com/UtHG6Jpgxz\n\u0026mdash; Tim Hopper (@tdhopper) May 28, 2019 ","date":"2019-07-01T13:40:00Z","permalink":"https://tdhopper.com/blog/tweets-in-the-news/","title":"My Tweet in BBC News"},{"content":"Way back in 2015, I tweeted:\nGive a man a fish and you feed him for a day.\nWrite a program to fish for him and you maintain it for a lifetime.\n\u0026mdash; Tim Hopper (@tdhopper) November 3, 2015 Today, that tweet appeared in a Nature article about the challenges of releasing and maintaining open source software.\nThe 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, \u0026ldquo;saying that the software lacked sufficient impact.\u0026rdquo;\n","date":"2019-07-01T12:00:00Z","image":"https://tdhopper.com/images/computer-fishing.png","permalink":"https://tdhopper.com/blog/nature-article-open-source/","title":"My Tweet in Nature"},{"content":"I use pyenv to manage Python versions on my Mac. I recently have gotten errors like\n1 WARNING: The Python sqlite3 extension was not compiled. Missing the SQLite3 lib? and\n1 zipimport.ZipImportError: can\u0026#39;t decompress data; zlib not available The solution seems to be setting LDFLAGS and CPPFLAGS to point to the sqlite3 and zlib libraries, e.g.:\n1 2 3 4 5 6 brew install sqlite3 brew install zlib export LDFLAGS=\u0026#34;-L/usr/local/opt/zlib/lib -L/usr/local/opt/sqlite/lib\u0026#34; export CPPFLAGS=\u0026#34;-I/usr/local/opt/zlib/include -I/usr/local/opt/sqlite/include\u0026#34; pyenv install 3.7.0 ","date":"2019-05-30T00:00:00Z","permalink":"https://tdhopper.com/blog/installing-python/","title":"Installing Python on Mohave with pyenv"},{"content":"I\u0026rsquo;m a big fan of using pdb, the Python interactive debugger in conjunction with Pytest as I\u0026rsquo;m writing code.1 With the --pdb flag, you can have Pytest drop into pdb when a test fails. With pytest.set_trace(), you can selectively enter pdb while running your tests. (I use this where I might\u0026rsquo;ve just added print statements in the past.)\nA 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.2 He figured how to drop into an IPython repl from pdb with from IPython import embed; embed(). You can\u0026rsquo;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.2\nTo simplify this, I recommended leveraging two little-known pdb features: alias and .pdbrc. alias allows you to set alias for statements in pdb; in this case ipy as an alias that drops into IPython:\n1 alias ipy from IPython import embed; embed() pdb also as the ability to load a config file from the user\u0026rsquo;s home directory or the current working directory. Each line in the file is just a pdb statement. Thus, if you can create ~/.pdbrc and add the alias statement above, the ipy command becomes available in every pdb session for your user.\nI highly recommend pip installing pdbpp, which replaces the default pdb with an enhanced debugger with syntax highlighting, tab completion, and more.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nThere\u0026rsquo;s also an alternative debugger for Python based on IPython: ipdb.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2019-02-13T00:00:00Z","permalink":"https://tdhopper.com/blog/ipython-pdb/","title":"Access a IPython repl from pdb"},{"content":"Edsger W. Dijkstra in 1972:\nAs 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.\n","date":"2018-12-20T12:30:00Z","image":"https://tdhopper.com/images/computerweights.png","permalink":"https://tdhopper.com/blog/problem-of-programming/","title":"Problem of Programming"},{"content":"At JuypterDay in the Triangle, I gave a response to Joel Grus\u0026rsquo;s memorable \u0026ldquo;I Don\u0026rsquo;t Like Notebooks presentation.\nIt wasn\u0026rsquo;t recorded, but here are my slides:\n","date":"2018-11-13T12:30:00Z","image":"https://tdhopper.com/images/notebook.png","permalink":"https://tdhopper.com/blog/i-like-notebooks-a-response-to-joel-grus/","title":"\"I Like Notebooks\": a response to Joel Grus"},{"content":" On two occasions I have been asked, — \u0026lsquo;Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?\u0026rsquo; In one case a member of the Upper, and in the other a member of the Lower, House [of British Parliament] put this question.\nI am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question.\n— Charles Babbage, Passages from the Life of a Philosopher (1864)\n","date":"2018-05-29T00:00:00Z","permalink":"https://tdhopper.com/blog/wrong-figures/","title":"Wrong Figures, Right Answers?"},{"content":"In 2014, Sebastian Gutierrez published a collection of interviews entitled Data Scientists at Work. My friend and former boss Eric Jonas posted his interview on his website. It\u0026rsquo;s full of gems.\nOn engineering skills required for data science work, Eric says,\nOn 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. I basically can’t hire people who don’t know Git.\nOn someone trained in pure mathematics learning to analysis of real-world data, Eric says:\n\u0026hellip;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.\nI tried to argue this same point in an earlier post.\nOn applying academic research, Eric observes:\nFor example, when I evaluate machine learning papers, what I am looking to find out is whether the technique worked or not. This is something that the world needs to know—most papers don’t actually tell you whether the thing worked. 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.\nI 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 actually works.\nI highly recommend reading Eric\u0026rsquo;s full interview.\n","date":"2018-05-22T00:00:00Z","permalink":"https://tdhopper.com/blog/data-scientists-at-work/","title":"I Basically Can't Hire People Who Don't Know Git"},{"content":" 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 \u0026ldquo;are confined from morn till eve, hearing and answering the applications and letters of one and another,\u0026rdquo; leaving him with \u0026ldquo;no hours for recreation.\u0026rdquo; 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.\n― Ron Chernow, Washington: A Life ","date":"2018-03-06T13:34:00Z","image":"https://tdhopper.com/images/george-washington.png","permalink":"https://tdhopper.com/blog/george-washington-inbox-zero/","title":"George Washington's Struggle for Inbox Zero"},{"content":"Three years ago, I wrote a post called \u0026ldquo;How I Became a Data Scientist Despite Being a Math Major\u0026rdquo;.\nWhen 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\u0026rsquo;t clear. With most of these emails, I have felt bad because I simply don\u0026rsquo;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.\nA note before the Q\u0026amp;A: one reason it is so hard to advise someone on becoming a \u0026ldquo;data scientist\u0026rdquo; is that \u0026ldquo;data scientist\u0026rdquo; 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.\nShould I get a masters degree? 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\u0026rsquo;ll want to do your due diligence before diving in.\nThat said, I\u0026rsquo;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 good school on in-state tuition instead of a great school for $100,000. If you have publicly subsidized graduate education (as in many European countries), go for it!\nWill a masters degree help me become/be a data scientist? I don\u0026rsquo;t know, but it has helped me. As I said in my earlier post, 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?\nShould I get a masters degree in computer science? Will it help me become a data scientist? 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.\nI have half of a computer science masters, and I sometimes wish I had finished it.\nShould I get a masters degree in operations research? I\u0026rsquo;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\u0026rsquo;t mean as much as a degree in \u0026ldquo;machine learning\u0026rdquo; or similar.\nDid you feel that operations research was too theoretical and that you had to hustle outside the classroom and build stat/ML skills separately? Operations research programs have wildly different curriculums. Mine didn\u0026rsquo;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.\nShould I get a masters degree in statistics? 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\u0026rsquo;m sure the quality of statistics masters degrees varies greatly, and it\u0026rsquo;s worth trying to find a good one.\nShould I get a Ph.D.? I don\u0026rsquo;t think it\u0026rsquo;s worth it for most people. It\u0026rsquo;s also not necessary for the vast majority of data science jobs. I have a free ebook to help you answer this question.\nThere are a lot of people with Ph.D.\u0026rsquo;s in data science roles. It\u0026rsquo;s possible that is more a result of the large difference in the number Ph.D.\u0026rsquo;s verses the small number of permanent faculty positions in American universities. People with Ph.D.\u0026rsquo;s need jobs; many have skills overlapping with data science; they make their way from academia to data science.\nCan I become a data scientist with only an undergraduate degree? Many others have. I have known several who don\u0026rsquo;t even have college degrees.\nI\u0026rsquo;m in school should I take X class? If it\u0026rsquo;s linear algebra, definitely. Otherwise, I\u0026rsquo;m not sure. Among other reasons, I\u0026rsquo;ve been often surprised how classes I never expected to apply have helped me years later; it\u0026rsquo;s hard for me to know what other classes would\u0026rsquo;ve helped me had I taken them.\nTake the best professors you can (note I didn\u0026rsquo;t say easiest ). Talk to older students you admire about different classes and professors. Don\u0026rsquo;t let your schooling interfere with your education.\nCan you evaluate my qualifications for being a data scientist? Not very well. In fact, I think it\u0026rsquo;s pretty challenging even for people who interview data science candidates. I have tried to share the things that helped qualify me, and I imagine those things would be valuable for you as well.\nI would suggest trying to evaluate your qualifications against specific jobs (or job descriptions) you are interested in. Are you more interested in analysis or production systems? 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.\nOne other note: just because you\u0026rsquo;re qualified, doesn\u0026rsquo;t mean you will get job offers. Not getting an offer after interviewing might reflect more on poor interviewers than being a poor candidate.\nHow do I show-on-my-resume/demonstrate-to-employers that I am qualified to be a data scientist? My best advice is to work on interesting and relevant things and tell people about them. I don\u0026rsquo;t know how to be more specific.\nHow can I get a job where I can do more applied math? 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\u0026rsquo;ve almost never gotten to spend as much time doing math as I would like.\nWhat skills would you recommend I develop if I hope to become a data scientist? You can never be a good enough writer, communicator, software engineer, linear algebraist, or applied statistician. Tenacity is important too, though I\u0026rsquo;m not sure how you develop it.\nAm I doing the right things to build a successful career in data science? I don\u0026rsquo;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 in my blog post.\nI\u0026rsquo;m in a career as a teacher/developer/analyst/etc. Can you advise me on how I can transition to be a data scientist? I have tried to share the things that worked for me; you might be able to emulate them, but I can\u0026rsquo;t guarantee they\u0026rsquo;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.\nCan we find a time to talk on the phone about this? Unfortunately, I don\u0026rsquo;t have the time and energy to do this.\nThanks to Roy Keyes, Vicki Boykis, and Justin Bozonier for helpful feedback on a draft of this post.\n","date":"2018-03-05T16:59:00Z","image":"https://tdhopper.com/becoming.png","permalink":"https://tdhopper.com/blog/faq/","title":"A Subjective and Anecdotal FAQ on Becoming a Data Scientist"},{"content":" 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.\n— Daniel Kahneman, [Thinking, Fast and Slow](Thinking Fast and Slow)\n","date":"2018-02-13T00:00:00Z","permalink":"https://tdhopper.com/blog/optimism/","title":"Optimism is essential to scientific success"},{"content":"I created a new website devoted to my photography.\nI named it dothopper photography in honor of my late grandmother, Dot Hopper, who fell in love with painting watercolors in retirement.\n","date":"2018-02-09T00:00:00Z","image":"https://tdhopper.com/images/camera.png","permalink":"https://tdhopper.com/blog/dothopper-photography/","title":"dothopper photography"},{"content":"Ten years ago today, John Cook published his first blog post entitled Moore’s law and software bloat, a brief observation on how \u0026ldquo;Software bloat has increased at roughly the same rate as Moore’s law\u0026rdquo;.\nSince 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\nengage with dead writers not look to diplomas for magic take care in inverting matrices cherish analytic functions be slow to say \u0026ldquo;they has too much time on their hands” second guess the need to always be in front of a screen to be a programmer use Bayesian methods to better order search results of customer-rated items consider the benefits of portable, open-source tools have humility when analyzing data be skeptical of “elementary” statistical books not feel dumb for being frustrated with statistics notation think carefully about how read code reevaluate what it would take for software to be bug free appreciate the massive importance of organization in software projects understand the real challenges of effectively applying mathematics wonder if time-spent-at-desk is a good proxy for getting work done better understand why software projects fail realize that it’s sometimes better not to produce more code realize automating might help me more by saving mental energy than by saving time John earned his Ph.D. in applied math at UT Austin in 1992 with a dissertation on partial differential equations entitled Diffusion models with microstructure and secondary flux. 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 HIPAA expert determination. His breadth of career experience reflect his breadth of interests that make his blog what it is.\nThough John has (wisely) avoided labeling himself a data scientist (preferring very applied mathematician), his writings have shaped every aspect of my trajectory from being a operations research grad student to a practicing data scientist. John is an expert in Bayesian statistics, yet has taught me much humility about the power of statistics. John is a expert programmer, yet points me back to the importance of soft skills for the success of software projects. John loves applications of mathematics, yet reminds me to step back and see the intrinsic beauty of mathematics.\nThe most common tags on his posts are bayesian, creativity, differential equations, education, history, latex, math, networks, number theory, probability and statistics, productivity, programming, python, quotes, scipy, and special functions. He also brings a unique approach in his posts on music and has a neat collection of interviews he\u0026rsquo;s done with Fred Brooks, Michael Atiyah and others.\nJohn’s blog is not his only educational outreach. He maintains 17 (!) Twitter accounts where he teaches about technical topics in 140 280 characters or less. I\u0026rsquo;m always learning little tidbits from @UnixToolTip and find my schooling refreshed by @AlgebraFact and @AnalysisFact.\nOne of John’s areas of expertise is implementing numerical algorithms in code. On his website, he has a handful of technical notes on this that many people have found invaluable. Two years ago, I did a quick survey of John’s articles mentioned in Github repos. Hundreds of repos cited his Accurately computing running variance article. No doubt countless more have used his resources without citing him!\nIf you don\u0026rsquo;t follow John\u0026rsquo;s blog, you should subscribe today.\nHere\u0026rsquo;s to 10 more years, John!\nCongrats to @JohnDCook on 10 years of blogging!https://t.co/DdMBjI89eV\n— Tim Hopper 🆘 (@tdhopper) January 9, 2018\n","date":"2018-01-10T01:28:00Z","permalink":"https://tdhopper.com/blog/ten-year-endeavor/","title":"John Cook’s Ten Year Blogging Endeavour"},{"content":"There once was a man from Nantucket\nWho was careful with his data, where he stuck it\nHis passwords always impress\nHe checked for HTTPS\nBut somebody still put his PII in a public S3 bucket\n","date":"2017-12-21T12:00:00Z","image":"https://tdhopper.com/images/leakybucket.png","permalink":"https://tdhopper.com/blog/an-infosec-limerick/","title":"An Infosec Limerick"},{"content":" The downside of this is you have to have two production stacks\u0026hellip; which is kind of expensive, but it\u0026rsquo;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\u0026hellip; We will gladly spend a million dollars in overtime in developers to sit and work on our antiquated infrastructure, but we won\u0026rsquo;t spend a thousand dollars to buy a new machine to make them not have to do do that.\n— Neal Ford, \u0026ldquo;Automation\u0026rdquo;, Neal Ford on Agile Engineering Practices\n","date":"2017-10-16T00:00:00Z","permalink":"https://tdhopper.com/blog/neal-ford-on-companies-ignoring-developer-costs/","title":"Neal Ford on Companies Ignoring Developer Costs"},{"content":"$ 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. (With apologies to Tim Peters.)\n","date":"2017-09-30T00:26:00Z","image":"https://tdhopper.com/images/zen.png","permalink":"https://tdhopper.com/blog/zen-of-git/","title":"Zen of Git"},{"content":"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).\nIn 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.\nTo that end, I made pythonplot.com, a brief introduction to Python plotting libraries and a \u0026ldquo;rosetta stone\u0026rdquo; comparing how to use them. I also included comparison to ggplot2, the R plotting library that I and many others consider a gold standard.\n","date":"2017-06-26T00:00:00Z","permalink":"https://tdhopper.com/blog/python-plotting-for-exploratory-data-analysis/","title":"Python Plotting for Exploratory Data Analysis"},{"content":"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?\nTry this:\nInstall Python Fire to run your command from the command line Install Python Fire with $ pip install fire.\nAdd this snippet to the bottom of your file:\n1 2 3 if __name__ == \u0026#39;__main__\u0026#39;: import fire fire.Fire() Install GNU Parallel $ brew install parallel or $ sudo apt-get install parallel may work for you. Otherwise, see this.\nRun your function from the command line $ parallel -j3 \u0026quot;python python_file.py function_name {1} \u0026quot; ::: input1 input2 input3 input4 input5\nparallel is the command for GNU Parallel. -j3 tells Parallel to run at most 3 processes at once. {1} fills in each item after the ::: as an argument to the function_name. For example 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 (lazy) ~ $ cat python_file.py from time import sleep def function_name(arg1): print(\u0026#34;Starting to run with\u0026#34;, arg1) sleep(2) print(\u0026#34;Finishing to run with\u0026#34;, arg1) if __name__ == \u0026#39;__main__\u0026#39;: import fire fire.Fire() (lazy) ~ $ parallel -j3 --lb \u0026#34;python -u python_file.py function_name {1} \u0026#34; ::: input1 input2 input3 input4 input5 Starting to run with input2 Starting to run with input1 Starting to run with input3 Finishing to run with input2 Finishing to run with input1 Finishing to run with input3 Starting to run with input4 Starting to run with input5 Finishing to run with input4 Finishing to run with input5 I added --lb and -u to keep Python and Parallel from buffering the output so you can see it being run in parallel.\n","date":"2017-06-07T00:00:00Z","permalink":"https://tdhopper.com/blog/parallelizing-a-python-function-for-the-extremely-lazy/","title":"Parallelizing a Python Function for the Extremely Lazy"},{"content":"I was getting this message when I tried to install packages from conda-forge with Conda:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 Fetching package metadata ... CondaHTTPError: HTTP 401 UNAUTHORIZED for url \u0026lt;https://conda.anaconda.org/conda-forge/osx-64/repodata.json\u0026gt; Elapsed: 00:00.920954 CF-RAY: 36ad7cbd5d1c23d8-IAD The remote server has indicated you are using invalid credentials for this channel. If the remote site is anaconda.org or follows the Anaconda Server API, you will need to (a) remove the invalid token from your system with `anaconda logout`, optionally followed by collecting a new token with `anaconda login`, or (b) provide conda with a valid token directly. Further configuration help can be found at \u0026lt;https://conda.io/docs/config.html\u0026gt;. I tried to do $ anaconda logout but didn\u0026rsquo;t have a program called anaconda installed.\nYou can install the Anaconda Cloud Client with $ conda install anaconda-client.\nAfter that, I was able to do $ anaconda logout followed by $ anaconda login where I used my old Binstar credentials (now anaconda.org).\nI\u0026rsquo;m not the only one having this problem.\n","date":"2017-06-06T00:00:00Z","permalink":"https://tdhopper.com/blog/condahttperror-http-401-unauthorized-for-url/","title":"CondaHTTPError: HTTP 401 UNAUTHORIZED for url"},{"content":"Richard Bellman quoted by Stuart Dreyfus via Garrett Jones:\nI 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.\nWe 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.\nThe 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.\nI 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.\n","date":"2017-05-26T00:00:00Z","permalink":"https://tdhopper.com/blog/choice-of-the-name-dynamic-programming/","title":"Choice of the Name Dynamic Programming"},{"content":"My friends Andrew and Joel were kind enough to have me back on their podcast Adversarial Learning. We shared our tales of bad data science interviews. Enjoy!\n","date":"2017-05-22T00:00:00Z","image":"https://tdhopper.com/images/interview.png","permalink":"https://tdhopper.com/blog/stories-of-degradation-and-humiliation/","title":"Adversarial Learning: Stories of Degradation and Humiliation"},{"content":"From the Autobiography of Benjamin Franklin:\nThomas Godfrey, a self-taught mathematician, great in his way, and afterward inventor of what is now called Hadley\u0026rsquo;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.\nI\u0026rsquo;m a recovering Godfrey Precisionist.\n","date":"2017-04-26T00:00:00Z","permalink":"https://tdhopper.com/blog/like-most-great-mathematicians-he-expects-universal-precision/","title":"Like most great mathematicians, he expects universal precision"},{"content":"This Software Engineering Radio interview with Neal Ford on Success Skills for Architects is full of gems about building effective software.\nHe 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.\nMetawork is more interesting than work. It\u0026rsquo;s so hard to get back to simplicity, because we love complicated little puzzles to solve, so we keep overengineering everything.\nAnyone who\u0026rsquo;s developing software would benefit from listening.\n","date":"2017-04-14T00:00:00Z","permalink":"https://tdhopper.com/blog/metawork-is-more-interesting-than-work/","title":"Metawork is more interesting than work"},{"content":"Staying focused while working in front of a computer and within reach of a smartphone is hard.\nIn 2017, teaching people to focus is becoming a industry.\nI\u0026rsquo;ve been trying to rethink distractions in my own life, particularly in my work environment. Here are some things that have helped:\nWorking from Home Working in an office, especially an open-floor plan office, is disastrous for staying focused. DeMarco and Lister wrote about this in Peopleware 30 years ago, and yet open offices are the norm for startups today.\nI\u0026rsquo;m much more productive by working from home in my quiet office or on my back patio. I\u0026rsquo;m finally able to spend my time thinking about hard problems rather than ways of silencing Constant Throat Clearer or Perpetual Annoying Laugher.\nNotifications Every app and website these days wants to send you notifications. I\u0026rsquo;m aggressive about reducing notifications down to those that I need see, and I let almost nothing notify me with sound. I use Do Not Disturb mode on my phone and Mac whenever I need to stop notifications altogether.\nSlack Slack 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.\nI\u0026rsquo;ve come up with a few ways to take control of Slack:\nOnly show \u0026ldquo;My unread, along with everything I\u0026rsquo;ve starred\u0026rdquo; in the sidebar. See Michael Lopp\u0026rsquo;s excellent post on Slack for more here. Enable notifications selectively. Sign out of distracting avocational Slacks. Social Media I\u0026rsquo;ve started using an app called Focus 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\u0026rsquo;s scheduling feature so blocking isn\u0026rsquo;t optional for me.\nI\u0026rsquo;ve decided not to block Tweetbot. 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.\nOn my iPhone, iPad, and personal Laptop, I\u0026rsquo;ve started using Freedom 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.1\nI also use Freedom to block social media for the first hour I\u0026rsquo;m up in the morning and before I go to bed.\nEmail I have two main tactics to keep email from being distracting.\nI aggressively unsubscribe from mailing lists and ads. I use Sanebox to filter low priority messages out of my inbox. When emails only need a brief reply, I tend to write responses as soon as possible. At the moment, I\u0026rsquo;m trying to break people of the expectation that I\u0026rsquo;ll respond quickly. Using services like Boomerang which lets me write emails now and have them sent later helps here.\nReading Long-form reading at the computer is terrible for comprehension. As Doug Lemov has argued, you have to get away from your computer and other devices to read deeply. I do this by printing articles or reading on my iPad with Freedom blocking enabled. I take my printouts or iPad and walk away from my desk to read.\nTodo Items I\u0026rsquo;m a firm believer in the Getting Things Done principle of reducing the cognitive overhead of tracking to-do items in my head. I use Omnifocus for task management. Mail Drop and this 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\u0026rsquo;m working.\nStaying focused is hard. I\u0026rsquo;m still learning how to do it well, and I\u0026rsquo;m sure I\u0026rsquo;m not the only one struggling to improve here. If you have any tips to share, I\u0026rsquo;d love to hear them!\nI can\u0026rsquo;t use Freedom on my work computer, because it acts as a VPN which conflicts with my work VPN.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2017-04-13T00:00:00Z","permalink":"https://tdhopper.com/blog/towards-reducing-distractions-while-working/","title":"Towards Reducing Distractions while Working"},{"content":"I\u0026rsquo;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\u0026rsquo;s office (I used Frontpage). A year or two after that, I read Kevin Yank\u0026rsquo;s \u0026ldquo;Build Your Own Database Driven Website Using PHP \u0026amp; MySQL\u0026rdquo; and hacked together a PHP back-end for a Lord of the Rings fan site.\nIn recent years, I\u0026rsquo;ve put together this blog, shouldigetaphd.com, and a few other simple web-based side projects. However, I haven\u0026rsquo;t kept up with modern web development, and my projects have been hacked together from boilerplate or templates. I\u0026rsquo;ve programmed professionally since 2011, I\u0026rsquo;ve spent very little of that writing anything close to graphical user interfaces.\nI have a number of other side projects that I\u0026rsquo;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.\nA 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\u0026rsquo;ve been trying to learn more about modern(ish) web design and development. My work has a subscription to Safari Books Online which gives me access to copious technical books and video tutorials.\nThe 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\u0026rsquo;t going to be useful. Yet there many gems to be found, which I share here with you.\nWhat follows is an overview of the technologies I\u0026rsquo;ve realized I need to learn more about and links to the resources I\u0026rsquo;ve found valuable in learning about them. If you think there are gaps I haven\u0026rsquo;t yet filled or better resources than I\u0026rsquo;ve listed below, I\u0026rsquo;d love your feedback.\nWhat I Knew Going In I\u0026rsquo;ve been a professional software developer and data scientist since 2012. I mostly write Python, but I\u0026rsquo;ve programmed in a number of different languages.\nI have a pretty good grasp on how HTML and CSS work. I\u0026rsquo;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.\nI\u0026rsquo;ve used the Python Flask web framework 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.\nI\u0026rsquo;ve used Jekyll, Hugo, and Pelican to create statically generated sites.\nI understood DNS at a high level, but never really learned what all the different DNS types were, and I didn\u0026rsquo;t understand why name server changes take so long to propagate.\nI had some idea of what node.js and npm are.\nI\u0026rsquo;m a committed Sublime Text user.\nA Meta Tutorial on Web Development A great place to start is Andrew Montalenti\u0026rsquo;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.\nAnother great resource is Cody Lindley\u0026rsquo;s free Front-End Developer\u0026rsquo;s Handbook. This is a substantial list meta-resource that organizes links for learning all angles of front-end development. \u0026ldquo;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.\u0026rdquo;\nChrome Developer Tools One of the most important tools for me in learning more about web development has been the Chrome Developer Tools. You can live edit the DOM elements and style sheets and watch how a website changes. I\u0026rsquo;ve mostly learned Developer Tools through exploring it myself, but there are lots of tutorials for it on Youtube.\nHTML, CSS, and Bootstrap Many modern websites are responsive: 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\u0026rsquo;re doing this professionally, you probably don\u0026rsquo;t want to write a responsive site from scratch.\nFor several projects, I\u0026rsquo;ve used the lightweight Skeleton project to create simple, responsive pages.\nRecently, I decide to dive deep into the more robust Bootstrap framework originally developed at Twitter.\nI watched Brock Nunn\u0026rsquo;s Building a Responsive Website with Bootstrap (Safari), a two hour tutorial on getting started with Bootstrap. The documentation for Bootstrap is clear (if terse) and worth reading through.\nOnce 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 pelican-bootstrap3.\nI\u0026rsquo;ve worked with Bootstrap 3 until now. Bootstrap 4 is about to come out. Bootstrap 4 moves the style sheets from LESS to SASS and adds Flexbox functionality. Unless you understand what those mean (more below), you\u0026rsquo;d be fine using version 3.\nI wanted to get a better grasp on CSS Selectors, so I read Eric Meyer\u0026rsquo;s brief Selectors, Specificity, and the Cascade: Applying CSS3 to Documents (Safari)\nI watched Marty Hall\u0026rsquo;s JavaScript, jQuery, and jQuery UI tutorial). I was able to skip big chunks where I already understood certain parts, but it helped me fill in lots of gaps.\nAdvanced Stylesheets (LESS, SASS, and Flexbox) There are several alternatives to writing raw CSS. Two popular ones are Less and SASS. These \u0026ldquo;preprocessors\u0026rdquo; allow you to write CSS-like stylesheets but with constructs such as variables, nesting, inheritance, and mathematical operators.\nI found this brief tutorial on Less (Safari) helpful, and I\u0026rsquo;ve enjoyed Less a lot. I haven\u0026rsquo;t used SASS yet, but it\u0026rsquo;s very similar. I\u0026rsquo;ll probably switch to SASS when I start using Bootstrap 4.\nAnother modern innovation is the Flexbox layout model for CSS. Stone River Learning has a great tutorial on Flexbox (Safari). It seems that Flexbox is the future of CSS-based layouts, and it\u0026rsquo;s worth learning about.\nAdvanced JavaScript (Elm, React, Angular, Backbone, Ember) The JavaScript web framework space has exploded. Many of these are implementations of the Model, View, Controller pattern, including React, Angular, and Ember. These tools allow the creation of complex web apps (as well as mobile apps).\nWeb Server Operations and DNS I learned a ton form Linux Web Operations (Safari) by Ben Whaley. \u0026ldquo;The videos discuss the relationship between web and application servers, load balancers, and databases and introduce configuration management, monitoring, containers, cryptography, and DNS.\u0026rdquo;\nI\u0026rsquo;ve struggled with DNS configuration over the years, so I watched Cricket Liu\u0026rsquo;s Learning DNS series (Safari). I still wouldn\u0026rsquo;t want to be responsible for a company\u0026rsquo;s complex DNS infrastructure, but I can now configure my own sites DNS with a little more understanding.\nDevelopment Automation Package Managers It\u0026rsquo;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 Bower seems to make more sense for front-end development.[^yarn] Cody Lindley has a nice introduction to npm and Bower. Bower is well documented and easy to start using. There is a nice Flask extension to help you integrate Bower with your Python project. (Update: since writing this, Yarn has come to dominate this scene.)\nTask Automation 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.\nThere\u0026rsquo;s a GUI application called Codekit that can do a lot of these tasks. You can also do it through a Node.js program called Grunt. I haven\u0026rsquo;t used it yet, but it looks like following the documentation would be the best way to get started.\nGulp is a popular alternative to Grunt.\nDesign Visual Design Design has never been my strong point. One way to compensate for that is to rely on the work of others. There are copious Bootstrap themes available, and some are even free.\nI enjoyed Software Engineering Daily\u0026rsquo;s interview with Tracy Osborn on Design for Non-designers. She has some blog posts on the topic. Tracy recommends COLOURLovers for color ideas and Font Pair for selecting fonts from Google Fonts.\nUser Experience Design On the topic of UX, I finally read Steve Krug\u0026rsquo;s classic Don\u0026rsquo;t Make Me Think (Safari); it\u0026rsquo;s great. Ginny Redish\u0026rsquo;s Letting Go of Words (Safari) is similarly excellent.\nConclusion I\u0026rsquo;ve learned a lot in the past few months. I\u0026rsquo;ve filled in some gaps about how CSS works. I\u0026rsquo;ve gotten a better grasp on the Javascript prototype model. I\u0026rsquo;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\u0026rsquo;m learning how to use available tools to reduce the boilerplate I have to write, automate tedious tasks, and reduce my personal technical debt.\nI still have a lot of learning and a lot of practicing ahead of me, but I\u0026rsquo;m starting to feel confident that I could make headway on some of my projects. The modern frontend development landscape is massive, varied, and ever changing, but that shouldn\u0026rsquo;t prohibit you from diving in if you want to.\n","date":"2017-03-31T18:42:00Z","permalink":"https://tdhopper.com/blog/web-development-and-design-for-the-backend-developer/","title":"Web Development and Design for the Backend Developer"},{"content":"I wrote a few months back 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.\nIn an attempt to take my own advice, I\u0026rsquo;ve finally been making myself learn Ansible. 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.\nWhat 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 Conda environment using supervisord. It presumes your development machine is on OS X and the remote machine is Debian-like; however, it shouldn\u0026rsquo;t require too many changes to run it on other systems.\nI wrote this post in a Jupyter notebook with a Bash kernel. You can find the notebook, Ansible files, and installation directions on my Github.\nAnsible Ansible provides \u0026ldquo;human readable automation\u0026rdquo; for \u0026ldquo;app deployment\u0026rdquo; and \u0026ldquo;configuration management\u0026rdquo;. Unlike tools like Chef, it doesn\u0026rsquo;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.\nAnsible is backed by Red Hat and has a great website.\nInstalling Ansible with Homebrew First, you\u0026rsquo;ll need to install Ansible. On a Mac, I recommend doing this with Homebrew.\nbrew 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. Quickstart Soon, I\u0026rsquo;ll show you how to put write an Ansible YAML file. However, Ansible also allows you specify tasks from the command line.\nHere\u0026rsquo;s how we could use Ansible ping our local host:\nansible -i 'localhost,' -c local -m ping all ansible -i 'localhost,' -c local -m ping all localhost | SUCCESS =\u0026gt; { \u0026quot;changed\u0026quot;: false, \u0026quot;ping\u0026quot;: \u0026quot;pong\u0026quot; } This command calls ansible and tells it:\nTo use localhost as it\u0026rsquo;s inventory (-i). Inventory is Ansible speak for machine or machines you want to be able to run commands on. To connect (-c) locally (local) instead of over SSH. To run the ping module (-m) to test the connection. To run the command on all hosts in the inventory (in this case, our inventory is just the localhost). Michael Booth has a post that goes into more detail about this command.\nBehind the scenes, Ansible is turning this -m ping command into shell commands. (Try running with the -vvv flag to see what\u0026rsquo;s happening behind the scenes.) It can also execute arbitrary commands; by default, it\u0026rsquo;ll use the Bourne shell sh.\nansible all -i 'localhost, ' -c local -a \u0026quot;/bin/echo hello\u0026quot; Setting up an Ansible Inventory Instead of specifying our inventory with the -i 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:\nmail.example.com [webservers] foo.example.com bar.example.com [dbservers] one.example.com two.example.com three.example.com Ansible has to be able to connect to these machines over SSH, so you will likely need to have relevant entries in your .ssh/config file.\nBy default, the Ansible CLI will look for a system-wide Ansible inventory file in /etc/ansible/hosts. You can also specify an alternative path for an intentory file with the -i flag.\nFor this tutorial, I\u0026rsquo;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 ./ansible.cfg and set the name of our local inventory file:\ncat ./ansible.cfg cat ./ansible.cfg [defaults] inventory = ./hosts You can check that Ansible is picking up your config file by running ansible --version.\nansible --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 For this example, I just have one host, a Digital Ocean VPS. To run the examples below, you should create a VPS instance on Digital Ocean, Amazon, or elsewhere; you\u0026rsquo;ll want to configure it for passwordless authentication. I have an entry like this in my ~/.ssh/hosts file:\nHost digitalocean HostName 45.55.395.23 User root Port 22 IdentityFile /Users/tdhopper/.ssh/id_rsa ForwardAgent yes and my intentory file (~/hosts) is just\ndigitalocean Before trying ansible, you should ensure that you can connect to this host:\nssh digitalocean echo 1 ssh digitalocean echo 1 1 Now I can verify that Ansible can connect to my machine by running the ping command.\nansible all -m ping ansible all -m ping digitalocean | SUCCESS =\u0026gt; { \u0026quot;changed\u0026quot;: false, \u0026quot;ping\u0026quot;: \u0026quot;pong\u0026quot; } We told Ansible to run this command on all specified hosts in the inventory. It found our inventory by loading the ansible.cfg which specified ./hosts as the inventory file.\nIt\u0026rsquo;s possible that this will fail for you even if you can SSH into the machine. If the error is something like /bin/sh: 1: /usr/bin/python: not found, this is because your VPS doesn\u0026rsquo;t have Python installed on it. You can install it with Ansible, but you may just want to manually run sudo apt-get -y install python on the VPS to get started.\nWriting our first Playbook While adhoc commands will often be useful, the real power of Ansible comes from creating repeatable sets of instructions called Playbooks.\nA playbook contains a list of \u0026ldquo;plays\u0026rdquo;. Each play specifies a set of tasks to be run and which hosts to run them on. A \u0026ldquo;task\u0026rdquo; is a call to an Ansible module, like the \u0026ldquo;ping\u0026rdquo; module we\u0026rsquo;ve already seen. Ansible comes packaged with about 1000 modules for all sorts of use cases. You can also extend it with your own modules and roles.\nOur first playbook will just execute the ping module on all our hosts. It\u0026rsquo;s a playbook with a single play comprised of a single task.\ncat ping.yml cat ping.yml --- - hosts: all tasks: - name: ping all hosts ping: We can run our playbook with the ansible-playbook command.\nansible-playbook ping.yml ansible-playbook ping.yml ____________ \u0026lt; PLAY [all] \u0026gt; ------------ \\ ^__^ \\ (oo)\\_______ (__)\\ )\\/\\ ||----w | || || ______________ \u0026lt; TASK [setup] \u0026gt; -------------- \\ ^__^ \\ (oo)\\_______ (__)\\ )\\/\\ ||----w | || || ok: [digitalocean] _______________________ \u0026lt; TASK [ping all hosts] \u0026gt; ----------------------- \\ ^__^ \\ (oo)\\_______ (__)\\ )\\/\\ ||----w | || || ok: [digitalocean] ____________ \u0026lt; PLAY RECAP \u0026gt; ------------ \\ ^__^ \\ (oo)\\_______ (__)\\ )\\/\\ ||----w | || || digitalocean : ok=2 changed=0 unreachable=0 failed=0 You might wonder why there are cows on your screen. You can find out here. However, the important thing is that our task was executed and returned successfully.\nWe can override the hosts list for the play with the -i flag to see what the output looks like when Ansible fails to run the play because it can\u0026rsquo;t find the host.\nLet\u0026rsquo;s work now on installing the dependencies for our Python project.\nInstalling supervisord \u0026ldquo;Supervisor is a client/server system that allows its users to monitor and control a number of processes on UNIX-like operating systems.\u0026rdquo; We\u0026rsquo;ll use it to run and monitor our Python process.\nOn a Debian-like system, we can install it with APT. In the Ansible DSL that\u0026rsquo;s just:\n- name: Install supervisord sudo: yes apt: name: supervisor state: present update_cache: yes You can read more about the apt module here.\nOnce we have it installed, we can start it with this task:\n- name: Start supervisord sudo: yes service: name: \u0026quot;supervisor\u0026quot; state: running enabled: yes This uses the service module.\nWe 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 Roles. A role is a collection of \u0026ldquo;variable values, certain tasks, and certain handlers – or just one or more of these things\u0026rdquo;. (You can learn more about variables and handlers in the Ansible docs.)\nRoles are organized as subfolders of a folder called \u0026ldquo;Roles\u0026rdquo; 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 main.yml nestled several folders deep. In our case, it\u0026rsquo;s in ./roles/supervisor/tasks/main.yml.\nCheck out the docs to learn more about role organization.\nHere\u0026rsquo;s what our role looks like:\ncat ./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: \u0026quot;supervisor\u0026quot; state: running enabled: yes tags: supervisor Note that I added tags: to the task definitions. Tags just allow you to run a portion of a playbook instead of the whole thing with the --tags flag for ansible-playbook.\nNow that we have the supervisor install encapsulated in a role, we can write a simple playbook to run the role.\ncat supervisor.yml cat supervisor.yml --- - hosts: digitalocean roles: - role: supervisor ansible-playbook supervisor.yml ansible-playbook supervisor.yml _____________________ \u0026lt; PLAY [digitalocean] \u0026gt; --------------------- \\ ^__^ \\ (oo)\\_______ (__)\\ )\\/\\ ||----w | || || ______________ \u0026lt; TASK [setup] \u0026gt; -------------- \\ ^__^ \\ (oo)\\_______ (__)\\ )\\/\\ ||----w | || || ok: [digitalocean] _________________________________________ \u0026lt; TASK [supervisor : Install supervisord] \u0026gt; ----------------------------------------- \\ ^__^ \\ (oo)\\_______ (__)\\ )\\/\\ ||----w | || || changed: [digitalocean] _______________________________________ \u0026lt; TASK [supervisor : Start supervisord] \u0026gt; --------------------------------------- \\ ^__^ \\ (oo)\\_______ (__)\\ )\\/\\ ||----w | || || changed: [digitalocean] ____________ \u0026lt; PLAY RECAP \u0026gt; ------------ \\ ^__^ \\ (oo)\\_______ (__)\\ )\\/\\ ||----w | || || digitalocean : ok=3 changed=2 unreachable=0 failed=0 Installing Conda with Ansible Galaxy Next we want to ensure that Conda installed on our system. We could write our own role to follow the recommended process. However, Ansible has a helpful tool to help us avoid reinventing the wheel by allowing users to share roles; this is called Ansible Galaxy.\nYou can search the Galaxy website for miniconda and see that a handful of roles for installing Miniconda exist. I liked this one.\nWe can install the role locally using the ansible-galaxy command line tool.\nansible-galaxy install -f andrewrothstein.miniconda You can have the role installed wherever you want (run ansible-galaxy install --help to see how, but by default they\u0026rsquo;ll go to /usr/local/etc/ansible/roles/.\nls -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 You can look at the tasks/main.yml to see the core logic of installing Miniconda. It has tasks to download the installer, run the installer, delete the installer, run conda update conda, and make conda the default system Python.\ncat /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 Overriding Ansible Variables 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:\nroles: - role: andrewrothstein.miniconda Before we add that to a playbook, I want to customize where miniconda is installed. If you look back at the main.yml file above, you see a bunch of things surrounded in double brackets. These are variables (in the Jinja2 template language). From the play, we can see that Miniconda will be installed at {{miniconda_parent_dir}}/{{miniconda_name}}. The role defines these variables in /andrewrothstein.miniconda/defaults/main.yml. We can override the default variables by specifying them in our play.\nA play to install miniconda could look like this:\n--- - hosts: digitalocean vars: conda_folder_name: miniconda conda_root: /root roles: - role: andrewrothstein.miniconda miniconda_parent_dir: \u0026quot;{{ conda_root }}\u0026quot; miniconda_name: \u0026quot;{{ conda_folder_name }}\u0026quot; I added this to playbook.yml.\nWe now know how to use Ansible to start and run supervisord and to install Miniconda. Let\u0026rsquo;s see how to use it to deploy and start our application.\nDeploy Python Application There are countless ways to deploy a Python application. We\u0026rsquo;re going to see how to use Ansible to deploy from Github.\nI created a little project called long_running_python_application. It has a main.py that writes a log line to stdout every 30 seconds; that\u0026rsquo;s it. It also includes a Conda environment file specifying the dependencies and a shell script that activates the environment and runs the program.\nWe\u0026rsquo;re going to use Ansible to\nClone the repository into our remote machine. Create a Conda environment based on the environment.yml file. Create a supervisord file for running the program. Start the supervisord job. Clone the repository Cloning a repository with Ansible is easy. We just use the git module. This play will clone the repo into the specified directory. The update: yes flag tells Ansible to update the repository from the remote if it has already been cloned.\n--- - 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: \u0026quot;{{ project_repo }}\u0026quot; dest: \u0026quot;{{ project_location }}\u0026quot; update: yes Creating the Conda Environment Since we\u0026rsquo;ve now installed conda and cloned the repository with an environment.yml file, we just need to run conda env update from the directory containing the environment spec. Here\u0026rsquo;s a play to do that:\n--- - hosts: digitalocean vars: project_location: /srv/long_running_python_process tasks: - name: Create Conda environment from project environment file. command: \u0026quot;{{conda_root}}/{{conda_folder_name}}/bin/conda env update\u0026quot; args: chdir: \u0026quot;{{ project_location }}\u0026quot; It uses the command module which just executes a shell command in the desired directory.\nCreate a Supervisord File By default, supervisord will look in /etc/supervisor/conf.d/ for configuration on which programs to run.\nWe need to put a file in there that tells supervisord to run our run.sh script. Ansible has an integrated way of setting up templates which can be placed on remote machines.\nI put a supervisord job template in the ./templates folder.\ncat ./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 This is a is normal INI-style config file, except it includes Jinja2 variables. We can use the Ansible template module to create a task which fills in the variables with information about our program and copies it into the conf.d folder on the remote machine.\nThe play for this would look like:\n- 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: \u0026quot;{{ supervisord_configs_path }}/run_process.conf\u0026quot; owner: root Start the supevisord job Finally, we just need to tell supervisord on our remote machine to start the job described by run_process.conf.\nInstead of issuing our own shell commands via Ansible, we can use the supervisorctl module. The task is just:\n- name: Start job supervisorctl: name: \u0026quot;{{ program_name }}\u0026quot; state: present state: present ensures that Ansible calls supervisorctl reread to load a new config. Because our config has autostart=true, supervisor will start it as soon as the task is added.\nThe Big Playbook! We can take everything we\u0026rsquo;ve described above and put it in one playbook.\nThis playbook will:\nInstall Miniconda using the role from Ansible Galaxy. Install and start Supervisor using the role we created. Clone the Github project we want to run. Create a Conda environment based on the environment.yml file. Create a supervisord file for running the program. Start the supervisord job. All of this will be done on the host we specify (digitalocean).\ncat 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: \u0026quot;{{ conda_root }}\u0026quot; miniconda_name: \u0026quot;{{ conda_folder_name }}\u0026quot; tags: miniconda - role: supervisor tasks: - name: Clone project code. git: repo: \u0026quot;{{ project_repo }}\u0026quot; dest: \u0026quot;{{ project_location }}\u0026quot; update: yes tags: git - name: Create Conda environment from project environment file. command: \u0026quot;{{conda_root}}/{{conda_folder_name}}/bin/conda env update\u0026quot; args: chdir: \u0026quot;{{ project_location }}\u0026quot; tags: conda - name: Copy supervisord job file to remote template: src: ./templates/run_process.j2 dest: \u0026quot;{{ supervisord_configs_path }}/run_process.conf\u0026quot; owner: root tags: conf - name: Start job supervisorctl: name: \u0026quot;{{ program_name }}\u0026quot; state: present tags: conf To configure our machine, we just have to run ansible-playbook playbook.yml.\nANSIBLE_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] =\u0026gt; (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] =\u0026gt; (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 See that the PLAY RECAP shows that everything was OK, no systems were unreachable, and no tasks failed.\nWe can verify that the program is running without error:\nssh 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 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!\nConclusion Ansible is a powerful, customizable tool. Unlike some similar tools, it requires very little setup to start using it. As I\u0026rsquo;ve learned more about it, I\u0026rsquo;ve seen more and more ways in which I could\u0026rsquo;ve used it in copious projects in the past; I intend to make it a regular part of my toolkit. (Historically I\u0026rsquo;ve done this kind of thing with hacky combinations of shell scripts and Fabric; Ansible would often be better.)\nThis tutorial just scratches the surface of the Ansible functionality. If you want to learn more, I again recommend reading through the docs; they\u0026rsquo;re very good. Of course, you should start writing and running your own playbooks as soon as possible! I also liked this tutorial from Server Admin for Programmers. If you want to compare Ansible to alternatives, the Taste Test book by Matt Jaynes looks promising. For more on Supervisor, serversforhackers.com has a nice tutorial, and its docs are thorough.\nI wrote a tutorial on using @ansible and supervisor to deploy a long running Python process to a @digitalocean VPS.https://t.co/uPC8bY5haD\n— Tim Hopper 🔭 (@tdhopper) March 24, 2017\n","date":"2017-03-23T15:11:00Z","permalink":"https://tdhopper.com/blog/automating-python-with-ansible/","title":"Automating Python with Ansible"},{"content":" ","date":"2017-03-14T21:29:00Z","permalink":"https://tdhopper.com/blog/naive-bayes/","title":"Naive Bayes Meme"},{"content":" 👉 The decision was close, but the team has decided to keep looking for someone who might have more direct neural net experience.\n👉 Honestly, I think the way you communicated your thought process and results was confusing for some people in the room.\n👉 He\u0026rsquo;s needing someone with an image analysis background for data scientist we\u0026rsquo;re hiring now.\n👉 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.\nThese quotes above are some of the reasons I\u0026rsquo;ve been given for why I wasn\u0026rsquo;t offered a data science job after interviewing. I\u0026rsquo;ve been told a variety of other reasons as well: company decided against hiring remotes after interviewing (I\u0026rsquo;ve heard this at least 3 times), company thought I changed jobs too frequently, company decided it didn\u0026rsquo;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 notify me they weren\u0026rsquo;t interested. One company hired someone with a Ph.D. from MIT soon after turning me down.\nIn the last five years, I\u0026rsquo;ve clearly interviewed for a lot of data science jobs, and I\u0026rsquo;ve also been turned down for a lot of data science jobs]. I\u0026rsquo;ve spent a good bit of time reflecting on why I wasn\u0026rsquo;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.\nYou never really know I learned with graduate school applications years ago: you rarely truly know why you were turned down. Maybe my GRE scores weren\u0026rsquo;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\u0026rsquo;s child.\nThe same goes for companies. I\u0026rsquo;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\u0026rsquo;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\u0026rsquo;t put too much weight in them.\nSome advice anyway That said, here are a few takeaways from interviewing for probably 20 data science jobs since 2012.\nCompanies often use interviews as a time to figure out what they\u0026rsquo;re really looking for. I suspect this is rarely intentional. But actually interviewing candidates forces a team to talk through what they\u0026rsquo;re actually looking for, and they often realize they had differing perspectives prior to the interview. Companies where \u0026ldquo;data science\u0026rdquo; is a new addition need your help in understanding what data science can do for them. As much as possible, use the interview to sell your vision for what data science can offer at the company, how you\u0026rsquo;ll get it off the ground, and what the ROI might be. Being the wrong fit for what a company needs is not ideal. I\u0026rsquo;ve come to appreciate a company trying to ensure my abilities align with their needs. You\u0026rsquo;d hope this was always the case, but I\u0026rsquo;ve been hired when it wasn\u0026rsquo;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! Data infrastructure is important and many companies are lacking it. Many data scientists can attest to being hired at a company only to discover the data they needed wasn\u0026rsquo;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\u0026rsquo;t assume that a company with a grand vision for data science necessarily knows what it will take to accomplish that vision. Many companies are still uneasy about data science being done remotely. I think this is silly, but I\u0026rsquo;m biased. There\u0026rsquo;s little consistency as to what you might be asked in a data science interview. I\u0026rsquo;ve been asked about Java design patterns, how to solve combinatorics problems, to describe my favorite machine learning model, to explain the SMO algorithm, 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\u0026rsquo;ve largely given up attempting any special preparation for interviews. Networking is still king. 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 shaped my career. Don\u0026rsquo;t downplay the benefits of networking. Conclusion So how do you get a data science job? I don\u0026rsquo;t know.\nI\u0026rsquo;ve been unbelievably fortunate to be continuously employed since college, but I\u0026rsquo;m not sure how to tell you to repeat that. The best I have to offer is to reiterate the conclusion of my recent talk about data science as a career. Learn and know the hard stuff: linear algebra, probability, statistics, machine learning, math modeling, data structures, algorithms, distributed systems, etc. You probably won\u0026rsquo;t use this knowledge every day in your job, but interviewers love to ask about it anyway.\nAt the same time, don\u0026rsquo;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\u0026rsquo;ll help you present yourself well in an interview. (With the exception of Stack Overflow. Using Stack Overflow in an interview is strangely taboo.)\nFurther Reading Trey Causey: What it\u0026rsquo;s like to be on the data science job market Trey Causey: Hiring data scientists Erin Shellman: Crushed it! Landing a data science job Joel Grus: Fizz Buzz in Tensorflow ","date":"2017-03-06T17:02:00Z","image":"https://tdhopper.com/rejected.png","permalink":"https://tdhopper.com/blog/some-reflections-on-being-turned-down-for-a-lot-of-data-science-jobs/","title":"Some Reflections on Being Turned Down for a Lot of Data Science Jobs"},{"content":"Fred Benenson spent 6 years doing data science at Kickstarter. When he left last year, he wrote a fantastic recap of his experience.\nHis \u0026ldquo;list of things I\u0026rsquo;ve discovered over the years\u0026rdquo; is particularly good. Here are a few of the things that resonated with me:\nThe more you can work with someone to help refine their question the easier it will be to answer Conducting a randomized controlled experiment via an A/B test is always better than analyzing historical data Metrics are crucial to the story a company tells itself; it is essential to honestly and rigorously define them Good experimental design is difficult; don't allow a great testing framework to let you get lazy with it 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 Always seek to record and attain data in its rawest form, whether you're instrumenting something yourself or retrieving it from an API I highly recommend reading the whole post.\n","date":"2017-02-21T00:00:00Z","permalink":"https://tdhopper.com/blog/logistic-regression-rules-everything-around-me/","title":"Logistic Regression Rules Everything Around Me"},{"content":"A while ago, I published a Bash script that will open a Twitter search page to show your old tweets from this day of the year. I have enjoyed using it to see what I was thinking about in days gone by.\nSo I turned this into a Twitter account.\nFollow me to have a link tweeted @ you each day that will show your old tweets from that day of the year. #experimental\n\u0026mdash; On This Day (@your_old_tweets) January 11, 2017 If you follow @your_old_tweets, it\u0026rsquo;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 timezone set).\nThis runs on Amazon Lambda. The code is here.\n","date":"2017-01-24T00:00:00Z","permalink":"https://tdhopper.com/blog/your-old-tweets-from-this-day/","title":"Your Old Tweets from This Day"},{"content":"When you add an SSH key to your Github account, Github shows you the hexadecimal form of the MD5 hash of your public key.\nIf you ever need to compare that against a key file on your computer, you can run:\n1 ssh-keygen -E md5 -lf ~/.ssh/id_rsa.pub I learned this from StackOverflow.\n","date":"2017-01-06T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/compare-rsa-key-with-fingerprint-in-github/","title":"Compare RSA Key with Fingerprint in Github"},{"content":"From Daniel Lemire:\nI 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.\n","date":"2016-12-24T15:01:00Z","image":"https://tdhopper.com/images/reading.png","permalink":"https://tdhopper.com/blog/the-perils-of-thinkism/","title":"The perils of thinkism"},{"content":"I was honored to join my friends Joel and Andrew on the Adversarial Learning podcast to talk about my career in data science (and what it\u0026rsquo;s like to be the tallest one).\n","date":"2016-12-08T00:00:00Z","image":"https://tdhopper.com/images/datascientist.png","permalink":"https://tdhopper.com/blog/tallest-data-scientist/","title":"Tallest Data Scientist"},{"content":"My former colleague\u0026rsquo;s from Parse.ly wrote the fantastic pykafka library with an optional c-backed using rdkafka. I\u0026rsquo;ve had trouble getting it to work, and here are a few things I\u0026rsquo;ve learned:\nThe version of rdkafka installable with apt-get was out of data, and pykafka couldn\u0026rsquo;t find the headers it need. I instead used the simple build instructions in the rdkafka README to build it from head. I was getting the error ImportError: librdkafka.so.1: cannot open shared object file: No such file or directory when trying to use rdkafka from Pykafka. It could be set in the short term by using LD_LIBRARY_PATH=/usr/local/lib. However, I fixed it permanently by running sudo ldconfig after building rdkafka. Pykafka has to be installed after 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 pip install pykafka to see if the rdkafka extension built. ","date":"2016-11-18T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/get-pykafka-to-work-with-rdkafka-on-linux/","title":"Get Pykafka to work with rdkafka on Linux"},{"content":"Many data scientists aren\u0026rsquo;t lazy enough.\nWhether we are managing production services or running computations on AWS machines, many data scientists are working on computers besides their laptops.\nFor me, this often takes the form of SSH-ing into remote boxes, manually configuring the system with a combination of apt installs, Conda environments, and bash scripts.\nTo run my service or scripts, I open a tmux window, activate my virtual environement, and start the process. (When I have to do this on multiple machines, I\u0026rsquo;m occasionally clever enough to use tmux to broadcast the commands to multiple terminal windows.)\nWhen 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.\nAll of this works, of course. And as we all know, a simple solution that works can be preferable to a fragile solution that requires constant maintenance. That said, I suspect many of us aren\u0026rsquo;t lazy enough. We don\u0026rsquo;t spend enough time automating tasks and processes. Even when we don\u0026rsquo;t save time by doing it, we may save mental overhead.\nI recently introduced several colleagues to some Python-based tools that can help. Fabric is a \u0026ldquo;library and command-line tool for streamlining the use of SSH for application deployment or systems administration tasks.\u0026rdquo; Fabric allows you to encapsulate sequences of commands as you might with a Makefile. It\u0026rsquo;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 a number of talks about Fabric on Youtube if you want to learn more. One of my colleagues reduced his daily workload by writing his system management tasks into a Fabric file.\nAnother great tool is Supervisor. 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 \u0026ldquo;provides you with one place to start, stop, and monitor your processes\u0026rdquo;. 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.\nA tool I want to learn but haven\u0026rsquo;t is Ansible, \u0026ldquo;a free-software platform for configuring and managing computers which combines multi-node software deployment, ad hoc task execution, and configuration management\u0026rdquo;. Unlike Chef and Puppet, Ansible doesn\u0026rsquo;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 Supervisor! Ansible is written in Python and, mercifully, doesn\u0026rsquo;t require learning a Ruby-based DSL (as does Chef).\nRecently I\u0026rsquo;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.\nI 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!\n","date":"2016-11-17T02:00:00Z","permalink":"https://tdhopper.com/blog/data-scientists-need-more-automation/","title":"Data Scientists Need More Automation"},{"content":"I always forget how to do this.\nThe pandas DataFrame.loc method allows for label -based filtering of data frames. The Pandas docs show how it can be used to filter a MultiIndex:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 In [39]: df Out[39]: A B C first second bar one 0.895717 0.410835 -1.413681 two 0.805244 0.813850 1.607920 baz one -1.206412 0.132003 1.024180 two 2.565646 -0.827317 0.569605 foo one 1.431256 -0.076467 0.875906 two 1.340309 -1.187678 -2.211372 qux one -1.170299 1.130127 0.974466 two -0.226169 -1.436737 -2.006747 In [40]: df.loc[\u0026#39;bar\u0026#39;] Out[40]: A B C second one 0.895717 0.410835 -1.413681 two 0.805244 0.813850 1.607920 In [41]: df.loc[\u0026#39;bar\u0026#39;, \u0026#39;two\u0026#39;] Out[41]: A 0.805244 B 0.813850 C 1.607920 Name: (bar, two), dtype: float64 It turns out you can easily use it to filter a DateTimeIndex level by a single date with df['2016-11-07'] or a range of dates with df['2016-11-07:2016-11-11']. This applies whether or not its a MultiIndex.\nIf you get an error like KeyError: 'Key length (1) was greater than MultiIndex lexsort depth (0)', it\u0026rsquo;s because \u0026ldquo;MultiIndex Slicing requires the index to be fully lexsorted\u0026rdquo;. You may fix your problem by calling df = df.sort_index().\n","date":"2016-11-08T22:17:00Z","permalink":"https://tdhopper.com/blog/filter-by-date-in-a-pandas-multiindex/","title":"Filter by date in a Pandas MultiIndex"},{"content":"I\u0026rsquo;m trying to use the NUTS sampler in PyMC3\nHowever, it was running at 2 iterations per second on my model, while the Metropolis Hastings sampler ran 450x faster.\nI showed my example to some of the PyMC3 devs on Twitter, and Thomas Wiecki showed me this trick:\n@tdhopper @Springcoil You need pm.NUTS(scaling=np.power(model.dict_to_array(v_params.stds), 2), is_cov=True) (terrible syntax, I know).\n\u0026mdash; Thomas Wiecki (@twiecki) November 8, 2016 It resulted in a 25x speedup of the NUTS sampler. The code looks like this\n1 2 3 4 5 6 7 8 9 with pm.Model() as model: # SETUP MODEL HERE mu, sds, elbo = pm.variational.advi(n=200000) step = pm.NUTS(scaling=np.power(model.dict_to_array(sds), 2)) return pm.sample(niter, step=step, is_cov=True, start=mu, ) ","date":"2016-11-08T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/speeding-up-pymc3-nuts-sampler/","title":"Speeding up PyMC3 NUTS Sampler"},{"content":"I was flattered to be asked to be on a burgeoning data science podcast called Undersampled Radio. You can listen on YouTube.\n","date":"2016-10-24T00:00:00Z","permalink":"https://tdhopper.com/blog/undersampled-radio/","title":"Undersampled Radio Interview"},{"content":"First run $ sudo pip install supervisor.\nCreate /etc/supervisord.conf:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 ; supervisor config file [unix_http_server] file=/var/run/supervisor.sock ; (the path to the socket file) chmod=0700 ; sockef file mode (default 0700) [supervisord] logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log) pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid) childlogdir=/var/log/supervisor ; (\u0026#39;AUTO\u0026#39; child log dir, default $TEMP) ; the below section must remain in the config file for RPC ; (supervisorctl/web interface) to work, additional interfaces may be ; added by defining them in separate rpcinterface: sections [rpcinterface:supervisor] supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface [supervisorctl] serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket ; The [include] section can just contain the \u0026#34;files\u0026#34; setting. This ; setting can list multiple files (separated by whitespace or ; newlines). It can also contain wildcards. The filenames are ; interpreted as relative to this file. Included files *cannot* ; include files themselves. [include] files = /etc/supervisor/conf.d/*.conf [program:program1] command=program1command autostart=true stderr_logfile=/var/log/program1.err.log stdout_logfile=/var/log/program1.out.log Now run $ sudo supervisord -c /etc/supervisord.conf. See the status with $ sudo supervisorctl status.\nAfter modifying the config, run modify the config, $ sudo supervisorctl reread \u0026amp; sudo supervisorctl update.\n","date":"2016-09-20T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/setting-up-supervisord/","title":"Setting Up supervisord"},{"content":"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\nCan we find a measure of how much \u0026ldquo;choice\u0026rdquo; is involved in the selection of the event or of how uncertain we are of the outcome?\nFor 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\u0026rsquo;s a complete tossup whether the coin is heads or tails. We might want to say the uncertainty in this case is 1.\nIn 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 \u0026ldquo;it is reasonable\u0026rdquo; that \\(H\\) should have three properties:\n\\(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.\nIf 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.\nFinally, 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 independent 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.\nWe 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:\n\\[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).\\]\nAs David MacKay explains, this is the general claim that\n\\[ 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).\\]\nShannon showed that given these three assumptions, there is a unique form that \\(H\\) must take:\n$$ H\\propto -\\sum_{i=1}^n p_i \\log p_i=\\sum_{i=1}^n p_i \\log \\frac{1}{p_i}. $$He named this measure of uncertainty entropy, because the form of \\(H\\) bears striking similarity to that of Gibbs Entropy in statistical thermodynamics.1\nShannon observes that \\(H\\) has many other interesting properties:\nEntropy \\(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.) Entropy \\(H\\) is maximized when the \\(p_i\\) values are equal. 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. You can read more about this in Shannon\u0026rsquo;s seminal paper A Theory of Mathematical Communication.\nCaianiello and Aizerman say the name entropy is thanks to von Neumann who said\nYou 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.\nThey argue that the name \u0026ldquo;uncertainty\u0026rdquo; would have been much more helpful since \u0026ldquo;Shannon entropy is simply and avowedly the \u0026lsquo;measure of the uncertainty inherient in a pre-assigned probability scheme.\u0026rsquo;\u0026rdquo;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2016-09-05T16:05:00Z","image":"https://tdhopper.com/entropy.png","permalink":"https://tdhopper.com/blog/entropy-of-a-discrete-probability-distribution/","title":"Entropy of a Discrete Probability Distribution"},{"content":"As we saw in an earlier post, the entropy of a discrete probability distribution is defined to be\n$$H(p)=H(p\\_1,p\\_2,\\ldots,p\\_n)=-\\sum\\_{i}p\\_i \\log p\\_i.$$Kullback and Leibler defined a similar measure now known as KL divergence. This measure quantifies how similar a probability distribution $p$ is to a candidate distribution $q$.\n$$D_{\\text{KL}}(p\\ | q)=\\sum_i p_i \\log \\frac{p_i}{q_i}.$$$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:\n$$ D_{\\text{KL}}(p\\| q) \\neq D_{\\text{KL}}(q\\| p).$$Jonathon Shlens explains 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$.\nAlgebraically, we can rewrite the definition as\n$$ \\begin{array}{rl} D_{\\text{KL}}(p\\| q) \u0026=\\sum_i p_i \\log \\frac{p_i}{q_i} \\\\\\\\ \u0026=\\sum_i \\left ( - p_i \\log q_i + p_i \\log p_i \\right)\\\\\\\\ \u0026=- \\sum_i p_i \\log q_i + \\sum_i p_i \\log p_i \\\\\\\\ \u0026=- \\sum_i p_i \\log q_i - \\sum_i p_i \\log \\frac{1}{p_i} \\\\\\\\ \u0026=- \\sum_i p_i \\log q_i-H(p) \\\\\\\\ \u0026=\\sum_i p_i \\log \\frac{1}{q_i}-H(p)\\\\\\\\ \\end{array} $$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 cross entropy:\n$$H(p, q)=\\sum_i p_i \\log \\frac{1}{q_i}.$$We could alternatively use this relationship to define cross entropy as:\n$$H(p, q)=H(p) + D_\\text{KL}(p\\| q).$$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$.\n","date":"2016-09-05T14:12:00Z","permalink":"https://tdhopper.com/blog/cross-entropy-and-kl-divergence/","title":"Cross Entropy and KL Divergence"},{"content":"A few weeks ago, I introduced my wife to backpacking in the beautiful Grayson Highlands State Park in southestern Virginia. Part of my reason for picking this location was to see the herd of wild ponies that life at 5000\u0026rsquo; on the grassy balds.\nI 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 an article about the Grayson Highlands ponies on the Smithsonian Magazine website. I was pleasantly surprised to see they selected two of my images for the story! I\u0026rsquo;ve been spending more time lately exploring my longtime interest in wildlife photography, and I\u0026rsquo;m thrilled to see others sharing my work.\nYou can find more of my photography on photos.tdhopper.com or Instagram.\nPonies at Grayson Highlands State Park Ponies at Grayon Highlands State Park ","date":"2016-07-25T00:00:00Z","permalink":"https://tdhopper.com/blog/photos-featured-on-smithsonian-magazine/","title":"Photos Featured on Smithsonian Magazine"},{"content":"I\u0026rsquo;ve been using Bash\u0026rsquo;s find command a lot more regularly lately. I\u0026rsquo;ve always been scared off by its syntax, but it\u0026rsquo;s great once you\u0026rsquo;ve learned it.\nToday I learned how to filter the results by files changed in the last N minutes: the cmin flag:\n1 2 3 4 5 -cmin [-|+]n True if the difference between the time of last change of file status information and the time find was started, rounded up to the next full minute, is more than n (+n), less than n (-n), or exactly n minutes ago. For example:\n1 2 3 find . -cmin +5 # Files modified more than 5 minutes ago find . -cmin -5 # Files modified less than than 5 minutes ago find . -cmin 5 # Files modified exactly 5 minutes ago ","date":"2016-06-22T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/find-files-modified-in-last-n-minutes/","title":"Find Files Modified in last N Minutes"},{"content":"I routinely run Impala queries on a remote machine and want the results to save to a CSV file on that machine.\nI 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.\nSo I created a text file at ~/.impalarc with the following settings:\n1 2 3 4 5 6 7 8 [impala] impalad=CLUSTER-ADDRESS:PORT output_delimiter=, verbose=true ignore_query_failure=false default_db=prd write_delimited=true print_header=true Now when I run a query with impala-shell, I don\u0026rsquo;t have to specify the address of the cluster, or the various flags required to get a CSV with a header.\nOther impala-shell config options are specified here\n","date":"2016-06-15T00:00:00Z","permalink":"https://tdhopper.com/blog/configuring-impala-query-results-with-impalarc/","title":"Configuring Impala Query Results with impalarc"},{"content":"I routinely SSH into a company machine on Openstack to do work. Until yesterday, I\u0026rsquo;d use my Bash history to find the SSH command I needed to access it. I was reading SSH Mastery in a plane yesterday and realized I\u0026rsquo;ve been foolish to neglect the power of ~/ssh/config.\nI added this to my SSH config file:\n1 2 3 4 5 6 7 Host lab HostName MACHINE.IP.ADDRESS User tdhopper Port 22 IdentityFile ~/.ssh/id_rsa ForwardAgent yes RemoteForward 52698 localhost:52698 Now I can connect to the machine by typing $ ssh lab. That\u0026rsquo;s it. I don\u0026rsquo;t have to provide the SSH key, username, or machine IP.\nThe RemoteForward bit also forwards a port to enable me to open files on the remote machine in my local SublimeText editor using rsub. This is great.\n","date":"2016-06-15T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/faster-ssh-access-to-remote-computers/","title":"Faster SSH Access to Remote Computers"},{"content":"I\u0026rsquo;m trying out the beautiful Fish shell. I\u0026rsquo;ve been a Bash user up until now.\nI was bummed to see that source activate ENV (for activating Conda environments didn\u0026rsquo;t work natively in Fish.\nApparently the Conda team is adding native support for Fish, but it\u0026rsquo;s not available as of 2016-05-04.\nThere are several projects developed to help in the meantime:\nconda-workon fish-conda-virtualenv condactivate I\u0026rsquo;m going to wait for the Conda update to be released.\n","date":"2016-05-04T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/using-conda-with-the-fish-shell/","title":"Using Conda with the Fish Shell"},{"content":"I needed to concatenate a bunch of CSV files while skipping the header row. There was a nice solution on Stack Overflow:\n1 find . -name \u0026#34;*.csv\u0026#34; | xargs -n 1 tail -n +2 With the GNU version of tail (sadly not the one installed on OS X by default), you can just use\n1 tail -q -n +2 *.csv or\n1 awk \u0026#39;FNR != 1\u0026#39; *.csv ","date":"2016-05-02T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/concatenate-files-with-header-row/","title":"Concatenate files with header row"},{"content":"This is more of a Today I Taught.\nTurns out, it is actually possible to search my timeline. Thanks @tdhopper for the tip. pic.twitter.com/7IsD76qszD\n\u0026mdash; Ole Zorn (@olemoritz) May 2, 2016 I have a quick search in Alfred to search the tweets of people I follow. I launch Alfred with ⌘-Space and type following {query}. Restricting a Twitter search to people you follow only requires adding the s=follows query string to a search url:\n1 https://twitter.com/search?s=follows\u0026amp;q={query} ","date":"2016-05-02T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/searching-the-tweets-of-people-you-follow/","title":"Searching the tweets of people you follow"},{"content":"If you want to check whether a Python string is an integer, you can try casting to an int with int() and catching the ValueError if it\u0026rsquo;s not an integer:\n1 2 3 4 5 6 def is_integer(value: str, *, base: int=10) -\u0026gt; bool: try: int(value, base=base) return True except ValueError: return False To check for nonnegative integers, you can use the str.is_digit() method. It will \u0026ldquo;return true if all characters in the string are digits and there is at least one character, false otherwise:\n1 2 3 4 \u0026gt;\u0026gt;\u0026gt; \u0026#34;123\u0026#34;.isdigit() True \u0026gt;\u0026gt;\u0026gt; \u0026#34;-123\u0026#34;.isdigit() False Thanks to Jeremy Kahn for reminding me that isdigit only detects positive integers.\n","date":"2016-04-29T15:13:00Z","permalink":"https://tdhopper.com/blog/testing-whether-a-python-string-contains-an-integer/","title":"Testing whether a Python string contains an integer"},{"content":"I created a single page website to collect notes on one of my other hobbies: ultralight backpacking. In particular, notes on ultralight gear for the very tall.\n","date":"2016-04-29T00:00:00Z","permalink":"https://tdhopper.com/blog/backpacking-for-the-very-tall/","title":"Ultralight Backpacking for the Ultratall"},{"content":"I was using [tee](http://man7.org/linux/man-pages/man1/tee.1.html) with a long running Python process, but I wasn\u0026rsquo;t seeing any output. This is a result of Python buffering the stdout stream. You can run force Python to run in unbuffered mode using the -u flag at the command line.\nForce stdin, stdout and stderr to be totally unbuffered. On systems where it matters, also put stdin, stdout and stderr in binary mode.\n","date":"2016-04-26T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/dont-buffer-pythons-stdout/","title":"Don't Buffer Python's stdout"},{"content":"Listening to Russ Roberts\u0026rsquo; Econtalk 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.\nHere are episodes from over the years that I\u0026rsquo;ve particularly enjoyed.\nThe Economics of Organ Donations Making Schools Better: A Conversation with Rick Hanushek Boudreaux on the Economics of \u0026ldquo;Buy Local\u0026rdquo; Deer on Autism, Vaccination, and Scientific Fraud O\u0026rsquo;Donohoe on Potato Chips and Salty Snacks David Owen on the Environment, Unintended Consequences, and The Conundrum Cowen on Food Lisa Turner on Organic Farming Narlikar on Fair Trade and Free Trade Velasquez-Manoff on Autoimmune Disease, Parasites, and Complexity David Skarbek on Prison Gangs and the Social Order of the Underworld Jonah Lehrer on Creativity and Imagine Scott Atlas on American Health Care Terry Anderson on the Environment and Property Rights Leonard Wong on Honesty and Ethics in the Military Eric Topol on the Power of Patients in a Digital World Adam Davidson on Hollywood and the Future of Work Matt Ridley on Climate Change Lee Ohanian, Arnold Kling, and John Cochrane on the Future of Freedom, Democracy, and Prosperity Rachel Laudan on the History of Food and Cuisine William MacAskill on Effective Altruism and Doing Good Better Jayson Lusk on Food, Technology, and Unnaturally Delicious One of my favorite guests is Duke economist Mike Munger. Here are some great interviews with him:\nPrice Gouging On Milk Division of Labor Middlemen Incidentally, Priceonomics recently published a great article on Roberts and Econtalk.\n","date":"2016-04-13T01:42:00Z","permalink":"https://tdhopper.com/blog/econtalk/","title":"Econtalk"},{"content":"Joining two Pandas DataFrames with an equal number of rows is slightly harder than it appears. In R, you just use the cbind function.\nAs this StackOverflow question shows, in Pandas it\u0026rsquo;s easy to end up with something like this:\n1 2 3 4 5 6 7 unique_id lacet_number latitude longitude 0 NaN NaN -93.193560 31.217029 1 NaN NaN -93.948082 35.360874 2 NaN NaN -103.131508 37.787609 15 5570613 TLA-0138365 NaN NaN 24 5025490 EMP-0138757 NaN NaN 36 4354431 DXN-0025343 NaN NaN This results from the indices not being identical. Frustratingly (to me) the ignore_index argument doesn\u0026rsquo;t give the 3-rowed DataFrame I\u0026rsquo;d hope it gives.\nAs the accepted answer on that question shows, the thing to do is reset the indices on the DataFrames before concatenating:\n1 pd.concat([df_a.reset_index(drop=True), df_b.reset_index(drop=True)], axis=1) ","date":"2016-04-11T20:56:00Z","permalink":"https://tdhopper.com/blog/column-binding-two-pandas-dataframes/","title":"Column binding two Panda's Dataframes"},{"content":"Jupyter notebooks nicely render Pandas data frames if they\u0026rsquo;re the last line in a cell. It renders the HTML version of the data frame returned by pandas.DataFrame.to_html(). However, if you call print(df) in a cell, the data frame is rendered in less readable text-based output.\nDespite using Notebooks regularly for years, I\u0026rsquo;d never bothered to figure out a way around this. However, the solution is easy.\nInstead of print(df) you use\n1 2 3 from IPython.display import display display(df) ","date":"2016-03-23T14:18:00Z","image":"https://tdhopper.com/panda.png","permalink":"https://tdhopper.com/blog/printing-pandas-data-frames-as-html-in-jupyter-notebooks/","title":"Printing Pandas Data Frames as HTML in Jupyter Notebooks"},{"content":"Impala Doesn\u0026rsquo;t Have a histogram() function.\nIbis\u0026rsquo;s histogram works by normalizing a column and rounding to integers:\n1 2 3 4 5 6 7 8 9 WITH t0 AS ( SELECT * FROM db.`table_name`) SELECT floor((t0.`column_name` - (t1.`min_1fe5be` - 1e-13)) / ((t1.`max_1fe5be` - (t1.`min_1fe5be` - 1e-13)) / (NUM_BINS - 1))) AS `tmp` FROM t0 CROSS JOIN ( SELECT min(`column_name`) AS `min_1fe5be`, max(`column_name`) AS `max_1fe5be` FROM t0 ) t1 ","date":"2016-03-23T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/impala-doesnt-have-a-histogram-function/","title":"Impala Doesn't Have a histogram() function"},{"content":"Wes Mckinney\u0026rsquo;s Ibis, a Pythonic interface to Impala, has functionality for creating Impala tables from Python Pandas dataframes.\n1 2 3 4 5 6 7 8 9 import pandas as pd import ibis hdfs = ibis.hdfs_connect(host=webhdfs_host, port=webhdfs_port) client = ibis.impala.connect(host=impala_host, port=impala_port, hdfs_client=hdfs) db = c.database(\u0026#39;ibis_testing\u0026#39;) data = pd.DataFrame({\u0026#39;foo\u0026#39;: [1, 2, 3, 4], \u0026#39;bar\u0026#39;: [\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;, \u0026#39;c\u0026#39;, \u0026#39;d\u0026#39;]}) db.create_table(\u0026#39;pandas_table\u0026#39;, data) This functionality, added in Ibis 0.6.0, is much easier that manually move data to HDFS and loading into Impala.\n","date":"2016-03-15T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/creating-impala-tables-from-pandas-dataframes/","title":"Creating Impala Tables from Pandas Dataframes"},{"content":"Galileo\u0026rsquo;s conflict with the Church was not as it is often portrayed:\nIn January of 1616, the month before before the Roman Inquisition would infamously condemn the Copernican theory as being \u0026ldquo;foolish and absurd in philosophy\u0026rdquo;, Monsignor Francesco Ingoli addressed Galileo Galilei with an essay entitled \u0026ldquo;Disputation concerning the location and rest of Earth against the system of Copernicus\u0026rdquo;. \u0026hellip; 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 \u0026ldquo;more weighty\u0026rdquo;, and does not ask him to respond to the theological arguments at all. \u0026hellip; Ingoli\u0026rsquo;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.\nFrom Francesco Ingoli\u0026rsquo;s essay to Galileo: Tycho Brahe and science in the Inquisition\u0026rsquo;s condemnation of the Copernican theory.\n","date":"2016-03-11T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/galileos-conflict-was-science-vs.-science/","title":"Galileo's Conflict was Science vs. Science"},{"content":"I don\u0026rsquo;t use Ruby much, but I wanted to use Jekyll for this blog. I kept getting this error when running gem install jekyll:\n1 2 3 Fetching: colorator-0.1.gem (100%) ERROR: While executing gem ... (Gem::FilePermissionError) You don\u0026#39;t have write permissions for the /Library/Ruby/Gems/2.0.0 directory. I installed rbenv with brew install rbenv and added\n1 [[ -s $HOME/.rbenv/bin ]] \u0026amp;\u0026amp; export PATH=\u0026#34;$HOME/.rbenv/bin:$PATH\u0026#34; \u0026amp;\u0026amp; eval \u0026#34;$(rbenv init -)\u0026#34; to my .bashrc file. Then I changed the default ruby interpreter from the system interpreter to one managed by rbenv with rbenv install 2.2.3 \u0026amp;\u0026amp; rbenv global 2.2.3.\n","date":"2016-03-11T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/using-rbenv-for-ruby-versions/","title":"Using rbenv for Ruby Versions"},{"content":"I enjoy the Stanford Encyclopedia of Philosophy, so I created a Twitter account that tweets links to random articles from it.\n","date":"2016-02-28T00:00:00Z","permalink":"https://tdhopper.com/blog/tweet-your-moon/","title":"Stanford Encyclopedia of Philosophy Bot"},{"content":"People mention John Cook\u0026rsquo;s blog a lot in Github repos.\nI scraped the Github search pages to try to figure out which of his pages are most mentioned. His post Accurately computing running variance gets many more mentions than any other post. It provides C++ code for Knuth\u0026rsquo;s algorithm for computing the mean, sample variance, and standard deviation for a stream of data.\nHere are top 12 pages from his site most linked in Github:\nAccurately computing running variance (377 mentions) Stand-alone code for numerical computing (58 mentions) A Bayesian view of Amazon Resellers (52 mentions) johndcook.com/blog (47 mentions) Computing the distance between two locations on Earth from coordinates (44 mentions) Math.h in POSIX, ISO, and Visual Studio (38 mentions) Three algorithms for converting color to grayscale (26 mentions) johndcook.com (21 mentions) Computing skewness and kurtosis in one pass (20 mentions) What’s so hard about finding a hypotenuse? (19 mentions) Random number generation in C++ (19 mentions) R language for programmers (19 mentions) ","date":"2016-01-14T02:59:00Z","permalink":"https://tdhopper.com/blog/mentions-of-john-cook-on-github/","title":"Mentions of John Cook on Github"},{"content":"I\u0026#39;ll never forget the great words of one of my best math professors:\u0026#10;\u0026#10;\u0026quot;If all I wanted was the answer, I sure as hell wouldn\u0026#39;t ask you.\u0026quot;\n\u0026mdash; Tim Hopper (@tdhopper) December 9, 2013 Another great former professor quote:\u0026#10;\u0026#10;\u0026quot;I\u0026#39;m not smarter than you. I can just recover from mistakes faster than you.\u0026quot;\n\u0026mdash; Tim Hopper (@tdhopper) December 9, 2013 ","date":"2016-01-12T00:00:00Z","permalink":"https://tdhopper.com/blog/quotes-from-former-professors/","title":"Quotes from Former Professors"},{"content":"@tdhopper my father wrote his PhD on this question, and I believe his answer was \u0026quot;depends\u0026quot;\n\u0026mdash; Fred Benenson (@fredbenenson) December 7, 2015 ","date":"2015-12-07T00:00:00Z","permalink":"https://tdhopper.com/blog/i-love-twitter/","title":"I Love Twitter"},{"content":"If you enjoy this post, check out my Python Developer Tooling Handbook!\nMany 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\u0026rsquo;s probably an old version of Python. Secondly, if you install 3rd party packages with pip, 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).\nFor 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 requirements.txt 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.\nIn 2015, I have almost exclusively used Python installations provided through Continuum Analytics\u0026rsquo;s Conda/Anaconda platform. I have also switched from using virtualenvs to using conda environments, and I am loving it.\nBefore explaining my workflow, here\u0026rsquo;s a quick glossary of the similarly-named products that Continuum offers.\nconda: \u0026ldquo;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 created for Python programs but can package and distribute any software. \u0026quot; 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). Anaconda: \u0026ldquo;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.\u0026rdquo; It is available across platforms and installable through a binary. Anaconda Cloud: Also known as Anaconda.org and formerly known as Binstar, \u0026ldquo;Anaconda Cloud is a package management service where you can host software packages of all kinds.\u0026rdquo; Anaconda Cloud is a package repository analogous to PyPI. Packages are installed via the conda command line tool instead of Pip. By default, the conda install 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 conda install using the -n flag with the username. Conda, Anaconda, and Anaconda cloud are distinct but interrelated tools; keeping them straight can be hard, but is helpful.\nConda (the package manager) can be installed in two ways. Through the Miniconda installer or the Anaconda 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 conda install anaconda.)\nConda Environment Files It has become standard for pip users to create a requirements.txt file for specifying dependencies for a particular project. Often, a developer working a project will (1) create and activate a virtual environment (2) run pip install -r requirements.txt to build an isolated development environment with the needed packages.\nConda provides an analogous (but more powerful) file: environment.yml.\nA simple environment.yml file might look like this:\nname: numpy-env dependencies: - python=3 - numpy If you are in a directory containing this file, you can run $ conda env create to create a Conda environment named numpy-env that runs Python 3 and has numpy installed[^numpy]. Run $ source activate numpy-env to activate this environment. Once activated, running $ python will run Python 3 from your environment instead of the globally installed Python for your system. Moreover, you will be able to import numpy but not any of the 3rd party packages installed globally.\nenvironment.yml can also install packages via pip with this syntax:\nname: pip-env dependencies: - python - pip - pip: - pypi-package-name I see environment.yml files as a positive development from requirements.txt 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 included a YAML file, attendees could see exactly what version I was using and quickly install it with conda env create. 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, environment.yml files provide no less functionality than a requirements.txt file provides.\nMy Python Environment Workflow Lately, whenever I am working on a new project (however big or small), I follow the following steps:\nCreate a project folder in the ~/repos/ directory on my computer. Create an environment.yml 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 anaconda as a dependency. Create the conda environment with $ conda env create. Activate the conda environment with $ source activate ENV_NAME. Create a .env file containing the line source activate ENV_NAME. Because I have autoenv 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. Run $ git init to make the folder a Git repository. I then run $ git add environment.yml \u0026amp;\u0026amp; git commit -m 'initial commit' to add the YAML file to the repository. If I want to push the repository to Github, I use $ git create using Github\u0026rsquo;s hub commands. I then push the master branch with $ git push -u origin master. As I add dependencies to my project, I try to be sure I add them to my environment.yml file.\nA 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 ( including Python version) by (1) cloning the repository, (2) running $ conda env create, (3) running $ source activate ENV_NAME. It\u0026rsquo;s easy enough for me to drop those instructions and further instructions for running the code in a README file. If I\u0026rsquo;m feeling especially helpful, I\u0026rsquo;ll create a Makefile or Fabfile to encapsulate commands for core functionality of the code.\nAn 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 print statements to figure out whether I was using Python 2 or 3.\nI\u0026rsquo;ve come to love environment.yml files, and I think you might too.\n","date":"2015-11-24T14:41:00Z","image":"https://tdhopper.com/images/anaconda.png","permalink":"https://tdhopper.com/blog/my-python-environment-workflow-with-conda/","title":"My Python Environment Workflow with Conda"},{"content":"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.\nYou can find the paper here.\n","date":"2015-11-21T00:00:00Z","permalink":"https://tdhopper.com/blog/sequential-minimal-optimization-algorithm-for-support-vector-machines/","title":"Sequential Minimal Optimization Algorithm for Support Vector Machines"},{"content":"True story: I\u0026rsquo;m a closet knitter. I don\u0026rsquo;t have much time for it these days, but it helped keep me sane in grad school. Here are some things I\u0026rsquo;ve made over the years.\n","date":"2015-11-20T00:00:00Z","permalink":"https://tdhopper.com/blog/knitting/","title":"Knitting"},{"content":"When I worked at RTI International, I worked on an exploratory analysis of Twitter discussion of electronic cigarettes. A paper on our work was just published in the Journal of Internet Medical Research: Using Twitter Data to Gain Insights into E-cigarette Marketing and Locations of Use: An Infoveillance Study.1\nMarketing 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. \u0026hellip; 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).\nI have no idea what \u0026ldquo;Infoveillance\u0026rdquo; means.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2015-11-06T00:00:00Z","image":"https://tdhopper.com/images/ecig.png","permalink":"https://tdhopper.com/blog/my-first-publication/","title":"Using Twitter Data to Gain Insights into E-cigarette Marketing and Locations of Use"},{"content":"Today is my last day at Qadium. Next week, I am joining the data science team at Distil Networks.\nI\u0026rsquo;ve been privileged to work with Eric Jonas on the data microscopes project for the past 8 months. In particular, I contributed the implementation of Nonparametric Latent Dirichlet Allocation.\nI published a collection of notes on nonparametric Bayesian methods and Latent Dirichlet Allocation. I hope this will be useful to other students and researchers of these methods.\n","date":"2015-10-16T00:00:00Z","permalink":"https://tdhopper.com/blog/wrapping-up-on-nonparametric-bayes/","title":"Nonparametric Latent Dirichlet Allocation"},{"content":"On MCMC:\nAny sufficiently advanced MCMC is indistinguishable from magic. \u0026#10;\u0026#10;Very slow magic.\n\u0026mdash; Tim Hopper (@tdhopper) October 13, 2015 On nonparametric Bayesian methods:\nNonparametric methods are an advanced technique for replacing intuitive parameters with unintuitive hyperparameters \u0026amp; way more computation.\n\u0026mdash; Tim Hopper (@tdhopper) October 1, 2015 On code:\nThe cost of a line of code is larger than the time it takes to write it.\n\u0026mdash; Tim Hopper (@tdhopper) October 14, 2015 On key pressers:\n\u0026quot;We call people like these two young programmers \u0026#39;key pressers.\u0026#39;\u0026quot;\u0026#10;\u0026#10;(from SEAL Team Six) pic.twitter.com/VFxZbafLlG\n\u0026mdash; Tim Hopper (@tdhopper) October 13, 2015 ","date":"2015-10-16T00:00:00Z","permalink":"https://tdhopper.com/blog/tweets-im-proud-of-2/","title":"Tweets I'm Proud Of (2)"},{"content":"rand_antoniak draws a sample from the distribution of tables created by a Chinese restaurant process with parameter alpha after n patrons are seated. Some notes on this distribution are available here.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 import numpy as np from numpy.random import choice def stirling(N, m): if N \u0026lt; 0 or m \u0026lt; 0: raise Exception(\u0026#34;Bad input to stirling.\u0026#34;) if m == 0 and N \u0026gt; 0: return 0 elif (N, m) == (0, 0): return 1 elif N == 0 and m \u0026gt; 0: return m elif m \u0026gt; N: return 0 else: return stirling(N-1, m-1) + (N-1) * stirling(N-1, m) assert stirling(9, 3) == 118124 assert stirling(9, 3) == 118124 assert stirling(0, 0) == 1 assert stirling(1, 1) == 1 assert stirling(2, 9) == 0 assert stirling(9, 6) == 4536 def normalized_stirling_numbers(nn): # * stirling(nn) Gives unsigned Stirling numbers of the first # * kind s(nn,*) in ss. ss[i] = s(nn,i). ss is normalized so that maximum # * value is 1. After Teh (npbayes). ss = [stirling(nn, i) for i in range(1, nn + 1)] max_val = max(ss) return np.array(ss, dtype=float) / max_val ss1 = np.array([1]) ss2 = np.array([1, 1]) ss10 = np.array([ 3.09439754e-01, 8.75395242e-01, 1.00000000e+00, 6.17105824e-01, 2.29662318e-01, 5.39549757e-02, 8.05832694e-03, 7.41877718e-04, 3.83729854e-05, 8.52733009e-07]) # Verified with Yee Whye Teh\u0026#39;s code assert np.sqrt(((normalized_stirling_numbers(1) - ss1)**2).sum()) \u0026lt; 0.00001 assert np.sqrt(((normalized_stirling_numbers(2) - ss2)**2).sum()) \u0026lt; 0.00001 assert np.sqrt(((normalized_stirling_numbers(10) - ss10)**2).sum()) \u0026lt; 0.00001 def rand_antoniak(alpha, n): # Sample from Antoniak Distribution # cf http://www.cs.cmu.edu/~tss/antoniak.pdf p = normalized_stirling_numbers(n) aa = 1 for i, _ in enumerate(p): p[i] *= aa aa *= alpha p = np.array(p) / np.array(p).sum() return choice(range(1, n+1), p=p) rand_antoniak(.5, 10) ","date":"2015-10-15T00:00:00Z","image":"https://tdhopper.com/images/antoniak-mr-men.png","permalink":"https://tdhopper.com/blog/antoniak/","title":"Sample from Antoniak Distribution with Python"},{"content":"In an earlier notebook, 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:\nSample the cluster assignment $z_i$. Sample the mixture weights $\\pi$ Sample the cluster means $\\mu_n$. It turns out that we can derive a Gibbs sampler that just samples the assignments instead of the mixture weights and cluster means. This is known as a collapsed Gibbs sampler. If we integrate out the cluster means $\\theta_k$ and mixture weights from the margial distribution of cluster assignment $$p(z_i=k \\,|\\, z_{\\neg i}, \\pi, \\theta_1, \\theta_2, \\theta_3, \\sigma, \\mathbf{x}, \\alpha )$$ we are left with $$p(z_i\\,|\\, z_{\\neg i}, \\sigma, \\mathbf{x}, \\alpha).$$By the conditional independence, we can factorize this marginal distribution $$ \\begin{align} p(z_i=k\\,|\\, z_{\\neg i}, \\sigma, \\mathbf{x}, \\alpha) \u0026\\propto p(z_i=k\\,|\\, x_i, z_{\\neg i}, \\sigma, \\mathbf{x}_{\\neg i}, \\alpha)\\\\ \u0026= p(z_i=k\\,|\\, z_{\\neg i}, \\sigma, \\mathbf{x}_{\\neg i}, \\alpha) p(x_i \\,|\\, z, \\sigma, \\mathbf{x}_{\\neg i}, \\alpha)\\\\ \u0026= p(z_i=k \\,|\\, z_{\\neg i}, \\alpha) p(x_i \\,|\\, z, \\mathbf{x}_{\\neg i}, \\sigma)\\\\ \u0026= p(z_i=k \\,|\\, z_{\\neg i}, \\alpha)p(x_i \\,|\\, z_i=k, z_{\\neg i}, x_{\\neg_i}, \\sigma)\\\\ \u0026= p(z_i=k \\,|\\, z_{\\neg i}, \\alpha)p(x_i \\,|\\, \\left\\{x_j \\,|\\, z_j=k, j\\neq i\\right\\}, \\sigma). \\end{align} $$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: $$p(z_i=k \\,|\\, z_{\\not i}, \\alpha)= \\frac{N_k^{-i}+\\alpha / K}{N-1+\\alpha}$$ 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$).\nThe 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$.\nThus, $$ \\begin{align} p(x_i \\,|\\, \\left\\{x_j \\,|\\, z_j=k, j\\neq i\\right\\}, \\sigma) \u0026= \\mathcal{N}(x_i \\,|\\, \\mu_k, \\sigma_k^2+\\sigma^2) \\end{align} $$ where $$\\sigma_k^2 = \\left( \\frac{N_k^{-i}}{\\sigma^2} + \\frac{1}{\\lambda_2^2} \\right)^{-1}$$ and $$\\mu_k = \\sigma_k^2 \\left( \\frac{\\lambda_1}{\\lambda_2^2}+\\frac{N_k^{-i}\\cdot \\bar{x}_k^{-i}}{\\sigma^2} \\right).$$ This is derived in Kevin Murphey\u0026rsquo;s fantastic article Conjugate Bayesian analysis of the Gaussian distribution.\nAt each step of the collapsed sampler, we sample each $z_i$ as follows:\nFor 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. 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)$$ where the normalizing constant is $Z_i=\\sum_{k=1}^K(N_k^{-i}+\\alpha/K)f_k(x_i)$.\nLet\u0026rsquo;s write code for this Gibbs sampler!\n1 2 3 4 5 6 7 import pandas as pd import numpy as np import matplotlib.pyplot as plt from collections import namedtuple, Counter from scipy import stats from numpy import random 1 np.random.seed(12345) First, load the same dataset we used previously:\n1 2 data = pd.Series.from_csv(\u0026#34;clusters.csv\u0026#34;) _=data.hist(bins=20) Again, we want to define a state object and a function for updating the sufficient statistics of the state.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 SuffStat = namedtuple(\u0026#39;SuffStat\u0026#39;, \u0026#39;theta N\u0026#39;) def initial_state(num_clusters=3, alpha=1.0): cluster_ids = range(num_clusters) state = { \u0026#39;cluster_ids_\u0026#39;: cluster_ids, \u0026#39;data_\u0026#39;: data, \u0026#39;num_clusters_\u0026#39;: num_clusters, \u0026#39;cluster_variance_\u0026#39;: .01, \u0026#39;alpha_\u0026#39;: alpha, \u0026#39;hyperparameters_\u0026#39;: { \u0026#34;mean\u0026#34;: 0, \u0026#34;variance\u0026#34;: 1, }, \u0026#39;suffstats\u0026#39;: {cid: None for cid in cluster_ids}, \u0026#39;assignment\u0026#39;: [random.choice(cluster_ids) for _ in data], \u0026#39;pi\u0026#39;: {cid: alpha / num_clusters for cid in cluster_ids}, } update_suffstats(state) return state def update_suffstats(state): for cluster_id, N in Counter(state[\u0026#39;assignment\u0026#39;]).iteritems(): points_in_cluster = [x for x, cid in zip(state[\u0026#39;data_\u0026#39;], state[\u0026#39;assignment\u0026#39;]) if cid == cluster_id ] mean = np.array(points_in_cluster).mean() state[\u0026#39;suffstats\u0026#39;][cluster_id] = SuffStat(mean, N) Next we define functions to compute the two terms of our marginal distribution over cluster assignments (as we derived above).\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 def log_predictive_likelihood(data_id, cluster_id, state): \u0026#34;\u0026#34;\u0026#34;Predictive likelihood of the data at data_id is generated by cluster_id given the currenbt state. From Section 2.4 of http://www.cs.ubc.ca/~murphyk/Papers/bayesGauss.pdf \u0026#34;\u0026#34;\u0026#34; ss = state[\u0026#39;suffstats\u0026#39;][cluster_id] hp_mean = state[\u0026#39;hyperparameters_\u0026#39;][\u0026#39;mean\u0026#39;] hp_var = state[\u0026#39;hyperparameters_\u0026#39;][\u0026#39;variance\u0026#39;] param_var = state[\u0026#39;cluster_variance_\u0026#39;] x = state[\u0026#39;data_\u0026#39;][data_id] return _log_predictive_likelihood(ss, hp_mean, hp_var, param_var, x) def _log_predictive_likelihood(ss, hp_mean, hp_var, param_var, x): posterior_sigma2 = 1 / (ss.N * 1. / param_var + 1. / hp_var) predictive_mu = posterior_sigma2 * (hp_mean * 1. / hp_var + ss.N * ss.theta * 1. / param_var) predictive_sigma2 = param_var + posterior_sigma2 predictive_sd = np.sqrt(predictive_sigma2) return stats.norm(predictive_mu, predictive_sd).logpdf(x) def log_cluster_assign_score(cluster_id, state): \u0026#34;\u0026#34;\u0026#34;Log-likelihood that a new point generated will be assigned to cluster_id given the current state. \u0026#34;\u0026#34;\u0026#34; current_cluster_size = state[\u0026#39;suffstats\u0026#39;][cluster_id].N num_clusters = state[\u0026#39;num_clusters_\u0026#39;] alpha = state[\u0026#39;alpha_\u0026#39;] return np.log(current_cluster_size + alpha * 1. / num_clusters) 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.\nTo 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.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def cluster_assignment_distribution(data_id, state): \u0026#34;\u0026#34;\u0026#34;Compute the marginal distribution of cluster assignment for each cluster. \u0026#34;\u0026#34;\u0026#34; scores = {} for cid in state[\u0026#39;suffstats\u0026#39;].keys(): scores[cid] = log_predictive_likelihood(data_id, cid, state) scores[cid] += log_cluster_assign_score(cid, state) scores = {cid: np.exp(score) for cid, score in scores.iteritems()} normalization = 1.0/sum(scores.values()) scores = {cid: score*normalization for cid, score in scores.iteritems()} return scores def add_datapoint_to_suffstats(x, ss): \u0026#34;\u0026#34;\u0026#34;Add datapoint to sufficient stats for normal component \u0026#34;\u0026#34;\u0026#34; return SuffStat((ss.theta*(ss.N)+x)/(ss.N+1), ss.N+1) def remove_datapoint_from_suffstats(x, ss): \u0026#34;\u0026#34;\u0026#34;Remove datapoint from sufficient stats for normal component \u0026#34;\u0026#34;\u0026#34; return SuffStat((ss.theta*(ss.N)-x*1.0)/(ss.N-1), ss.N-1) Finally, we\u0026rsquo;re ready to create a function that takes a Gibbs step on the state. For each datapoint, it\nRemoves the datapoint from its current cluster. Computes the posterior probability of the point being assigned to each cluster (given the other current assignments). Assigns the datapoint to a cluster sampled from this probability distribution. 1 2 3 4 5 6 7 8 9 10 11 def gibbs_step(state): pairs = zip(state[\u0026#39;data_\u0026#39;], state[\u0026#39;assignment\u0026#39;]) for data_id, (datapoint, cid) in enumerate(pairs): state[\u0026#39;suffstats\u0026#39;][cid] = remove_datapoint_from_suffstats(datapoint, state[\u0026#39;suffstats\u0026#39;][cid]) scores = cluster_assignment_distribution(data_id, state).items() labels, scores = zip(*scores) cid = random.choice(labels, p=scores) state[\u0026#39;assignment\u0026#39;][data_id] = cid state[\u0026#39;suffstats\u0026#39;][cid] = add_datapoint_to_suffstats(state[\u0026#39;data_\u0026#39;][data_id], state[\u0026#39;suffstats\u0026#39;][cid]) Here\u0026rsquo;s our old function to plot the assignments.\n1 2 3 4 5 6 7 8 9 10 def plot_clusters(state): gby = pd.DataFrame({ \u0026#39;data\u0026#39;: state[\u0026#39;data_\u0026#39;], \u0026#39;assignment\u0026#39;: state[\u0026#39;assignment\u0026#39;]} ).groupby(by=\u0026#39;assignment\u0026#39;)[\u0026#39;data\u0026#39;] hist_data = [gby.get_group(cid).tolist() for cid in gby.groups.keys()] plt.hist(hist_data, bins=20, histtype=\u0026#39;stepfilled\u0026#39;, alpha=.5 ) Randomly assign the datapoints to a cluster to start.\n1 2 state = initial_state() plot_clusters(state) Look what happens to the assignments after just one Gibbs step!\n1 2 gibbs_step(state) plot_clusters(state) Two:\n1 2 gibbs_step(state) plot_clusters(state) After just two steps, our assignments look really good. We can run it a few more times and see the assignments again.\n1 2 for _ in range(20): gibbs_step(state) plot_clusters(state) Nonparametric Mixture Models! It turns out, the collapsed Gibbs sampler for mixture models is almost identical in the context of a nonparametric model. This model uses a Dirichlet process prior instead of a Dirichlet distribution prior. It doesn\u0026rsquo;t require us to specify how many clusters we are looking for in our data.\nThe cluster assignment score changes slightly. It is proportional to $N_k^{-i}$ for each known cluster. We assign a datapoint to a new cluster with probability proportional to $\\alpha$ (which is now the DP dispersion parameter).\n1 2 3 4 5 6 7 8 def log_cluster_assign_score_dp(cluster_id, state): \u0026#34;\u0026#34;\u0026#34;Log-likelihood that a new point generated will be assigned to cluster_id given the current state. \u0026#34;\u0026#34;\u0026#34; if cluster_id == \u0026#34;new\u0026#34;: return np.log(state[\u0026#34;alpha_\u0026#34;]) else: return np.log(state[\u0026#39;suffstats\u0026#39;][cluster_id].N) 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:\n$$ \\begin{align} p(x_i \\,|\\, z, x_{\\neg_i}, \\sigma) \u0026= \\mathcal{N}(x_i \\,|\\, \\lambda_1, \\lambda_2^2+\\sigma^2) \\end{align} $$ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def log_predictive_likelihood_dp(data_id, cluster_id, state): \u0026#34;\u0026#34;\u0026#34;Predictive likelihood of the data at data_id is generated by cluster_id given the currenbt state. From Section 2.4 of http://www.cs.ubc.ca/~murphyk/Papers/bayesGauss.pdf \u0026#34;\u0026#34;\u0026#34; if cluster_id == \u0026#34;new\u0026#34;: ss = SuffStat(0, 0) else: ss = state[\u0026#39;suffstats\u0026#39;][cluster_id] hp_mean = state[\u0026#39;hyperparameters_\u0026#39;][\u0026#39;mean\u0026#39;] hp_var = state[\u0026#39;hyperparameters_\u0026#39;][\u0026#39;variance\u0026#39;] param_var = state[\u0026#39;cluster_variance_\u0026#39;] x = state[\u0026#39;data_\u0026#39;][data_id] return _log_predictive_likelihood(ss, hp_mean, hp_var, param_var, x) Given this, we can define the marginal distribution over cluster assignment. The only change is that the \u0026ldquo;new\u0026rdquo; state enters in the distribution.\n1 2 3 4 5 6 7 8 9 10 11 12 13 def cluster_assignment_distribution_dp(data_id, state): \u0026#34;\u0026#34;\u0026#34;Compute the marginal distribution of cluster assignment for each cluster. \u0026#34;\u0026#34;\u0026#34; scores = {} cluster_ids = state[\u0026#39;suffstats\u0026#39;].keys() + [\u0026#39;new\u0026#39;] for cid in cluster_ids: scores[cid] = log_predictive_likelihood_dp(data_id, cid, state) scores[cid] += log_cluster_assign_score_dp(cid, state) scores = {cid: np.exp(score) for cid, score in scores.iteritems()} normalization = 1.0/sum(scores.values()) scores = {cid: score*normalization for cid, score in scores.iteritems()} return scores We also need to be able to create a new cluster when \u0026ldquo;new\u0026rdquo; is drawn, and destroy a cluster if its emptied.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def create_cluster(state): state[\u0026#34;num_clusters_\u0026#34;] += 1 cluster_id = max(state[\u0026#39;suffstats\u0026#39;].keys()) + 1 state[\u0026#39;suffstats\u0026#39;][cluster_id] = SuffStat(0, 0) state[\u0026#39;cluster_ids_\u0026#39;].append(cluster_id) return cluster_id def destroy_cluster(state, cluster_id): state[\u0026#34;num_clusters_\u0026#34;] = 1 del state[\u0026#39;suffstats\u0026#39;][cluster_id] state[\u0026#39;cluster_ids_\u0026#39;].remove(cluster_id) def prune_clusters(state): for cid in state[\u0026#39;cluster_ids_\u0026#39;]: if state[\u0026#39;suffstats\u0026#39;][cid].N == 0: destroy_cluster(state, cid) Finally, we can define the gibbs_step_dp function. It\u0026rsquo;s nearly identical to the earlier gibbs_step function except\nIt uses cluster_assignment_distribution_dp It creates a new cluster when the sampled assignment is \u0026ldquo;new\u0026rdquo;. It destroys a cluster any time it is emptied. For clarity, I split out the code for sampling assignment to its own function.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 def sample_assignment(data_id, state): \u0026#34;\u0026#34;\u0026#34;Sample new assignment from marginal distribution. If cluster is \u0026#34;`new`\u0026#34;, create a new cluster. \u0026#34;\u0026#34;\u0026#34; scores = cluster_assignment_distribution_dp(data_id, state).items() labels, scores = zip(*scores) cid = random.choice(labels, p=scores) if cid == \u0026#34;new\u0026#34;: return create_cluster(state) else: return int(cid) def gibbs_step_dp(state): \u0026#34;\u0026#34;\u0026#34;Collapsed Gibbs sampler for Dirichlet Process Mixture Model \u0026#34;\u0026#34;\u0026#34; pairs = zip(state[\u0026#39;data_\u0026#39;], state[\u0026#39;assignment\u0026#39;]) for data_id, (datapoint, cid) in enumerate(pairs): state[\u0026#39;suffstats\u0026#39;][cid] = remove_datapoint_from_suffstats(datapoint, state[\u0026#39;suffstats\u0026#39;][cid]) prune_clusters(state) cid = sample_assignment(data_id, state) state[\u0026#39;assignment\u0026#39;][data_id] = cid state[\u0026#39;suffstats\u0026#39;][cid] = add_datapoint_to_suffstats(state[\u0026#39;data_\u0026#39;][data_id], state[\u0026#39;suffstats\u0026#39;][cid]) This time, we will start by randomly assigning our data to two clusters.\n1 2 state = initial_state(num_clusters=2, alpha=0.1) plot_clusters(state) Here\u0026rsquo;s what happens when we run our Gibbs sampler once.\n1 2 gibbs_step_dp(state) plot_clusters(state) We went from 2 to 4 clusters!\nAfter 100 iterations:\n1 2 for _ in range(99): gibbs_step_dp(state) plot_clusters(state) After 100 iterations, our assignment looks correct! We went back to 3 clusters.\nWe can sample the mixture weights, if we need them, using the \u0026ldquo;Conditional Distribution of Mixture Weights\u0026rdquo; derived here.\n1 2 3 4 ss = state[\u0026#39;suffstats\u0026#39;] alpha = [ss[cid].N + state[\u0026#39;alpha_\u0026#39;] / state[\u0026#39;num_clusters_\u0026#39;] for cid in state[\u0026#39;cluster_ids_\u0026#39;]] stats.dirichlet(alpha).rvs(size=1).flatten() array([ 0.21330625, 0.29838101, 0.48831275]) We can also sample the cluster means using the method we derived earlier:\n1 2 3 4 5 6 7 8 9 10 11 12 13 for cluster_id in state[\u0026#39;cluster_ids_\u0026#39;]: cluster_var = state[\u0026#39;cluster_variance_\u0026#39;] hp_mean = state[\u0026#39;hyperparameters_\u0026#39;][\u0026#39;mean\u0026#39;] hp_var = state[\u0026#39;hyperparameters_\u0026#39;][\u0026#39;variance\u0026#39;] ss = state[\u0026#39;suffstats\u0026#39;][cluster_id] numerator = hp_mean / hp_var + ss.theta * ss.N / cluster_var denominator = (1.0 / hp_var + ss.N / cluster_var) posterior_mu = numerator / denominator posterior_var = 1.0 / denominator mean = stats.norm(posterior_mu, np.sqrt(posterior_var)).rvs() print(\u0026#34;cluster_id:\u0026#34;, cluster_id, \u0026#34;mean\u0026#34;, mean) cluster_id: 1 mean -0.0176257860235 cluster_id: 2 mean -0.400581819532 cluster_id: 3 mean 0.600302879661 Much thanks to Erik Sudderth\u0026rsquo;s excellent introduction to nonparametric Bayes in Chapter 2 of his dissertation. 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.\n","date":"2015-10-14T00:00:00Z","image":"https://tdhopper.com/images/collapsed-gibbs-mr-men.png","permalink":"https://tdhopper.com/blog/collapsed-gibbs/","title":"Collapsed Gibbs Sampling for Bayesian Mixture Models (with a Nonparametric Extension)"},{"content":"Much of the literature on Dirichlet Processes makes assertions similar to the following:\n\u0026ldquo;DP is the Dirichlet process, a distribution over distributions.\u0026rdquo; (Neal, 2000) \u0026ldquo;[The Dirichlet process] is a distribution over distributions, i.e. each draw from a Dirichlet process is itself a distribution.\u0026rdquo; (Teh, 2010) \u0026ldquo;The Dirichlet process (DP) is a distribution over distributions.\u0026rdquo; (Gershman and Blei, 2012) The \u0026ldquo;Dirichlet process defines a distribution on random probability measures\u0026hellip;\u0026rdquo; (Sudderth, 2006) \u0026ldquo;Dirichlet processes define a distribution over distributions\u0026hellip;\u0026rdquo; (Ghahramani, 2005) Michael Jordan makes an equivalent statement.\nEach 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)$ is a Dirichlet process and is (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$.\nConfusingly, while many sources refer to the DP as a distribution over distributions, when using the phrase \u0026ldquo;sample from a Dirichlet process\u0026rdquo;, they mean a sample from $G$, not from $DP(\\alpha, G_0)$. These authors appear to equivocate on the term \u0026ldquo;Dirichlet process\u0026rdquo;. It means both a distribution over distributions and a distribution sampled from this distribution over distributions.\nAfter being confused by this point for some time, I prepared these notes arguging that the Dirichlet process is a distribution over distributions. I argued that the term sample from a Dirichlet process should refer to a distribution sampled from the DP, not to a point sampled from the support of $G_0$.\nIn response to my notes, Dan Roy briefly argued that \u0026ldquo;The Dirichlet process is a distribution on the space of probability measures\u0026rdquo; 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.\nThomas Ferguson first defined the Dirichlet Process in his 1973 paper. Charles Antoniak (a student of Ferguson) repeats the definition in his his 1974 paper. Antoniak\u0026rsquo;s definition is as follows:\nLet $\\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))$.\nLet\u0026rsquo;s unpack this dense, measure theoretic definition.\nLet $\\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})$.\nFirst, note that $\\beta$ here is a finite measure, i.e. a not-necessarily-normalized probability distribution. Antoniak\u0026rsquo;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 Banach-Tarski paradox.)\nWe 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)$\n\u0026hellip;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))$.\n","date":"2015-10-13T00:00:00Z","image":"https://tdhopper.com/images/nomenclature-mr-men.png","permalink":"https://tdhopper.com/blog/nomenclature/","title":"Nomenclature of Dirichlet Processes"},{"content":"The interactive visualizations in this analysis require a live notebook. View the original notebook on NBViewer for the full interactive experience, including the pyLDAvis topic visualization.\nNonparametric Latent Dirichlet Allocation Analysis of the topics of Econtalk In 2003, a groundbreaking statistical model called \u0026ldquo;Latent Dirichlet Allocation\u0026rdquo; was presented by David Blei, Andrew Ng, and Michael Jordan.\nLDA 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 Edwin Chen\u0026rsquo;s post.\nThe 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 a \u0026ldquo;nonparametric\u0026rdquo; version of this model that doesn\u0026rsquo;t require the number of topics to be specified. This model uses a prior distribution over the topics called a hierarchical Dirichlet process. I wrote an introduction to this HDP-LDA model earlier this year.\nFor 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 \u0026ldquo;robust, validated Bayesian nonparametric models for discovering structure in data\u0026rdquo; known as Data Microscopes.\nThis notebook demonstrates the functionality of this implementation.\nThe Data Microscopes library is available on anaconda.org for Linux and OS X. microscopes-lda can be installed with:\n$ conda install -c datamicroscopes -c distributions microscopes-lda The Econtalk transcript data used in this analysis is available on GitHub.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 import pyLDAvis import json import sys import cPickle from microscopes.common.rng import rng from microscopes.lda.definition import model_definition from microscopes.lda.model import initialize from microscopes.lda import utils from microscopes.lda import model, runner from numpy import genfromtxt from numpy import linalg from numpy import array dtm.csv contains a document-term matrix representation of the words used in Econtalk transcripts. The columns of the matrix correspond to the words in vocab.txt. The rows in the matrix correspond to the show urls in urls.txt.\nOur 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.\n1 2 3 4 vocab = genfromtxt(\u0026#39;./econtalk-data/vocab.txt\u0026#39;, delimiter=\u0026#34;,\u0026#34;, dtype=\u0026#39;str\u0026#39;).tolist() dtm = genfromtxt(\u0026#39;./econtalk-data/dtm.csv\u0026#39;, delimiter=\u0026#34;,\u0026#34;, dtype=int) docs = utils.docs_from_document_term_matrix(dtm, vocab=vocab) urls = [s.strip() for s in open(\u0026#39;./econtalk-data/urls.txt\u0026#39;).readlines()] Let\u0026rsquo;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.\n1 2 3 4 5 6 7 N, V = len(docs), len(vocab) defn = model_definition(N, V) prng = rng(12345) state = initialize(defn, docs, prng, vocab_hp=1, dish_hps={\u0026#34;alpha\u0026#34;: .6, \u0026#34;gamma\u0026#34;: 2}) r = runner.runner(defn, docs, state, ) 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:\nrandomly initialized model: number of documents 454 vocabulary size 16445 perplexity: 16523.1820356 num topics: 9 after 1000 iterations: perplexity: 2363.65138771 num topics: 11 We can extract the term relevance for each topic. Here are the 10 most relevant words for each of the 11 discovered topics:\ntopic 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 We could assign titles to each of these topics. For example, Topic 5 appears to be about the foundations of classical liberalism. Topic 6 is obviously Bitcoin and Software. Topic 0 is the financial system and monetary policy. Topic 4 seems to be generic words used in most episodes; unfortunately, the prevalence of \u0026ldquo;don\u0026rdquo; is a result of my preprocessing which splits up the contraction \u0026ldquo;don\u0026rsquo;t\u0026rdquo;.\nTopic 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:\nThe 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 ... 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 \u0026ldquo;Kling on Freddie and Fannie and the Recent History of the U.S. Housing Market\u0026rdquo; include many episodes about the financial crisis:\nIrwin 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 ... We can also find the topics a given word is most likely to appear in. For example, the word \u0026ldquo;Munger\u0026rdquo; (as in Mike Munger) appears most frequently in discussions of classical liberalism and microeconomics:\n1 2 3 4 5 6 def topics_related_to_word(word, n=10): for wd, rel in zip(word_dists, relevance): score = wd[word] rel_words = \u0026#39; \u0026#39;.join([w for w, _ in rel][:n]) if bars(score): print bars(score), rel_words ==== 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 Where does Munger come up? In discussing the moral foundations of classical liberalism and microeconomics!\nThe word \u0026ldquo;lovely\u0026rdquo;\u0026mdash;which Russ Roberts uses often when talking about the Theory of Moral Sentiments\u0026mdash;appears most in that topic as well:\n= smith hayek moral economics society adam liberty coase theory rules ","date":"2015-10-07T00:00:00Z","image":"https://tdhopper.com/images/econtalk-topics-mr-men.png","permalink":"https://tdhopper.com/blog/econtalk-topics/","title":"Econtalk Topics: Nonparametric LDA in Practice"},{"content":"Even though I\u0026#39;ve been coding for 12 years, I\u0026#39;d still just consider myself a grammer.\n\u0026mdash; Tim Hopper (@tdhopper) November 10, 2014 ","date":"2015-09-24T00:00:00Z","permalink":"https://tdhopper.com/blog/a-joke/","title":"A Joke"},{"content":"Yee Whye Teh et al\u0026rsquo;s 2005 paper Hierarchical Dirichlet Processes describes a nonparametric prior for grouped clustering problems. For example, the HDP helps in generalizing the latent Dirichlet allocation 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.\nThe 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.\nHere are some notes I\u0026rsquo;ve compiled in my effort to understand these algorithms.\nHDP-LDA Generative Model The generative model for Hierarchical Dirichlet Process Latent Dirichlet Allocation is as follows:\n\\begin{equation} \\begin{aligned} H \u0026 \\sim \\text{Dirichlet}(\\beta) \\\\ G_0 \\,|\\, \\gamma, H \u0026 \\sim \\text{DP}(\\gamma, H) \\\\ G_j \\,|\\, \\alpha_0, G_0 \u0026 \\sim \\text{DP}(\\alpha_0, G_0) \\\\ \\theta_{ji} \\,|\\, G_j \u0026 \\sim G_j \\\\ x_{ij} \\,|\\, \\theta_{ji} \u0026 \\sim \\text{Categorical}(\\theta_{ji}) \\\\ \\end{aligned} \\end{equation} $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). $G_0$ is a distribution over a countably-infinite number of categorical term distributions, i.e. topics. For each document $j$, $G_j$ is a is a distribution over a countably-infinite number of categorical term distributions, i.e. topics. $\\theta_{ji}$ is a categorical distribution over terms, i.e. a topic. $x_{ij}$ is a term. To see code for sampling from this generative model, see this post.\nChinese Restaurant Franchise Approach Instead of the above Dirichlet process model, we can think of an identical \u0026ldquo;Chinese Restaurant Franchise\u0026rdquo; model.\nEach $\\theta_{ji}$ is a customer in restaurant $j$. Each customer is sitting at a table, and each table has multiple customers.\nThere is a global menu of $K$ dishes that the restaurants serve, $\\phi_1,\\ldots,\\phi_K$.\nSome other definitions:\n$\\psi_{jt}$ is the dish served at table $t$ in restaurant $j$; i.e. each $\\psi_{jt}$ corresponds to some $\\phi_k$. $t_{ji}$ is the index of the $\\psi_{jt}$ associated with $\\theta_{ji}$. $k_{jt}$ is the index of $\\phi_k$ associated with $\\psi_{jt}$. Customer $i$ in restaurant $j$ sits at table $t_{ji}$ while table $t$ in restaurant $j$ serves dish $k_{jt}$.\nThere are two arrays of count variables we will want to track:\n$n_{jtk}$ is the number of customers in restaurant $j$ at table $t$ eating dish $k$. $m_{jk}$ is the number of tables in restaurant $j$ serving dish $k$ (multiple tables may serve the same dish). To summarize:\n$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.\nGibbs Sampling Teh et al 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).\n5.3 Posterior sampling by direct assignment 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 \u0026ldquo;bookkeeping\u0026rdquo; than the algorithm from 5.1, however it requires expensive simulation or computation of recursively computed Stirling numbers of the first kind.\nMy 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.\n5.1 Posterior sampling in the Chinese restaurant franchise Section 5.1 describes \u0026ldquo;Posterior sampling in the Chinese restaurant franchise\u0026rdquo;. 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.\nNotes on Implementing Algorithm 5.1 Teh et al\u0026rsquo;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).\nA Blogger Formerly Known As Shuyo 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.\nAs stated above, in the case of topic modeling, $H$ is a Dirichlet distribution over terms distributions and $F$ is a multinomial distribution over terms.\nBy definition,\n\\begin{equation} h(\\phi_k)=\\frac{1}{Z}\\prod_v[\\phi_k]_v^{\\beta-1} \\end{equation}and\n\\begin{equation} f(x_{ji}=v \\,|\\, \\phi_k)=\\phi_{kv}. \\end{equation}Equation (30): $f_k^{x_{ji}}(x_{ji})$ 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\n\\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}(the number of times the term $x_{ji}$, besides $x_{ji}$ itself, is generated by the same topic as was $x_{ji}$).\nFirst look at the term (for fixed $k$):\n\\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}$[\\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:\n\\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}We use this early on in the big derivation below.\nAlso, note that\n\\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}are the normalizing coefficients for Dirichlet distributions.\nEquation (30) in Teh\u0026rsquo;s paper is:\n\\begin{equation} \\begin{aligned} f_k^{-x_{ji}}(x_{ji}) \u0026=\\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 } \\\\ \u0026=\\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 } \\\\ \u0026\\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 }\\\\ \u0026=\\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 }\\\\ \u0026= \\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}Recognizing these integrals as those that occur in the Dirichlet distribution, we have,\n\\begin{equation} \\begin{aligned} f_k^{-x_{ji}}(x_{ji}) \u0026=\\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 }\\\\ \u0026=\\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 }\\\\ \u0026=\\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) }\\\\ \u0026=\\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) }\\\\ \u0026=\\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) }\\\\ \u0026=\\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) }\\\\ \u0026=\\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) }\\\\ \u0026=\\frac{\\beta+n_{kv}^{-ji}}{V\\beta+n_{k\\cdot}^{-ji}} \\end{aligned} \\end{equation}This is validated in the appendix of this paper.\nEquation: $f_{k^\\text{new}}^{x_{ji}}(x_{ji})$ We also need the prior density of $x_{ji}$ to compute the likelihood that $x_{ji}$ will be seated at a new table.\n\\begin{equation} \\begin{aligned} f_{k^{\\text{new}}}^{-x_{ji}}(x_{ji}) \u0026= \\int f(x_{ji} \\,|\\, \\phi) h(\\phi)d\\phi_k \\\\ \u0026=\\int \\phi_v \\cdot \\frac{ \\Gamma(V\\beta) }{ \\prod_w \\Gamma(\\beta) } \\prod_w \\phi_w^{\\beta-1} d\\phi \\\\ \u0026=\\frac{ \\Gamma(V\\beta) }{ \\prod_w \\Gamma(\\beta) } \\int \\phi_v \\cdot \\prod_w \\phi_w^{\\beta-1} d\\phi\\\\ \u0026=\\frac{ \\Gamma(V\\beta) }{ \\prod_w \\Gamma(\\beta) } \\int \\phi_v^\\beta \\cdot \\prod_{w\\neq v} \\phi_w^{\\beta-1} d\\phi\\\\ \u0026=\\frac{ \\Gamma(V\\beta) }{ \\prod_w \\Gamma(\\beta) }\\cdot \\frac{ \\Gamma(\\beta+1)\\prod_{w\\neq v}\\Gamma(\\beta) }{ \\Gamma(V\\beta+1) }\\\\ \u0026=\\frac{ \\Gamma(V\\beta) }{ \\Gamma(V \\beta+1) }\\cdot \\frac{ \\beta\\prod_{w}\\Gamma(\\beta) }{ \\prod_w \\Gamma(\\beta) }\\\\ \u0026=\\frac{ 1 }{ V \\beta }\\cdot \\beta\\\\ \u0026= \\frac{1}{V} \\end{aligned} \\end{equation}Equation (31): $p(x_{ji} \\,|\\, {\\bf t}^{-ji}, t_{ji}=t^{new}, {\\bf k})$ These last two derivations give us Equation (31), the likelihood that $t_{ji}=t^{new}$:\n\\begin{equation} \\begin{aligned} p(x_{ji} \\,|\\, {\\bf t}^{-ji}, t_{ji}=t^{new}, {\\bf k}) \u0026=\\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] \\\\ \u0026\\phantom{=}+ \\frac{ \\gamma }{ m_{\\cdot\\cdot}+\\gamma } \\cdot \\frac{1}{V} \\end{aligned} \\end{equation}Equation (32): $p(t_{ji}=t \\,|\\, {\\bf t}^{-ji}, {\\bf k})$ From this, we know the conditional distribution of $t_{ji}$ is:\n\\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}} \u0026 {\\tiny \\text{if } t \\text{ previously used,}}\\\\ {\\tiny \\alpha_0 \\cdot p(x_{ji} \\,|\\, {\\bf t}^{-ji}, t_{ji}=t^{new}, {\\bf k})} \u0026 {\\tiny \\text{if } t=t^{\\text{new}}}. \\end{cases} \\end{equation}Equation (33): $p(k_{jt^\\text{new}}=k \\,|\\, {\\bf t}, {\\bf k}^{-jt^\\text{new}})$ If the sampled value of $t_{ji}$ is $t^{\\text{new}}$, we sample a dish $k_{jt^\\text{new}}$ for the table with:\n\\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}} \u0026 {\\tiny \\text{if } k \\text{ previously used,}}\\\\ \\frac{ \\gamma }{V} \u0026 {\\tiny \\text{if } t=k^{\\text{new}}}. \\end{cases} \\end{equation}Equation (34): $p(k_{jt}=k \\,|\\, {\\bf t}, {\\bf k}^{-jt})$ We need to sample $k_{jt}$ (the dish/topic for table $t$ in restaurant $j$):\n\\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}) \u0026 {\\tiny \\text{if } k \\text{ previously used,}}\\\\ \\gamma\\cdot f_{k^\\text{new}}^{-{\\bf x}_{jt}}({\\bf x}_{jt}) \u0026 {\\tiny \\text{if } t=k^{\\text{new}}}. \\end{cases} \\end{equation}where $f_k^{-{\\bf x}_{jt}}({\\bf x}_{jt})$ is the \u0026ldquo;conditional density of ${\\bf x}_{jt}$ given all data items associated with mixture component $k$ leaving out ${\\bf x}_{jt}$\u0026rdquo; (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$.\nThis requires $f_k^{-{\\bf x}_{jt}}({\\bf x}_{jt})$; this is different from Equation (30), though they look quite similar.\n\\begin{equation} \\begin{aligned} f_k^{-{\\bf x}_{jt}}({\\bf x}_{jt}) \u0026=\\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 } \\\\ \u0026=\\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 } \\\\ \u0026=\\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}The denominator is\n\\begin{equation} \\begin{aligned} \\text{denominator}\u0026= \\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 \\\\ \u0026=\\int\\left[ \\prod_w \\phi_{kw}^{n_{kw}^{-jt}} \\prod_w \\phi_{kw}^{\\beta-1} \\right] d\\phi_k \\\\ \u0026=\\int\\left[ \\prod_w \\phi_{kw}^{n_{kw}^{-jt}+\\beta-1} \\right] d\\phi_k \\\\ \u0026=\\frac{ \\prod_w \\Gamma\\left( n_{kw}^{-jt} + \\beta \\right) }{ \\Gamma\\left( \\sum_w n_{kw}^{-jt}+\\beta \\right) } \\\\ \u0026=\\frac{ \\prod_w \\Gamma\\left( n_{kw}^{-jt} + \\beta \\right) }{ \\Gamma\\left( n_{k\\cdot}^{-jt}+V\\beta \\right) } \\end{aligned} \\end{equation}The numerator is\n\\begin{equation} \\begin{aligned} \\text{numerator} \u0026=\\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 \\\\ \u0026=\\int \\prod_{w} \\phi_{kw}^{ n_{kw}^{-jt} + n_{\\cdot w}^{jt} + \\beta + 1 } d\\phi_k \\\\ \u0026=\\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) }\\\\ \u0026=\\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}This gives us a closed form version of this conditional distribution:\n\\begin{equation} \\begin{aligned} f_k^{-{\\bf x_{jt}}}({\\bf x}_{jt}) \u0026= \\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}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:\n\\begin{equation} \\begin{aligned} f_{k^\\text{new}}^{-{\\bf x}_{jt}}({\\bf x}_{jt}) \u0026=\\int \\left[ \\prod_{x_{ji}\\in \\mathbf{x_{jt}}} f(x_{ji} \\,|\\, \\phi) \\right] h(\\phi)d\\phi_k \\\\ \u0026=\\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 \\\\ \u0026=\\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\\\\ \u0026=\\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\\\\ \u0026=\\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) }\\\\ \u0026=\\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}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 Posterior sampling in the Chinese restaurant franchise in Teh, et al.\n","date":"2015-09-21T00:00:00Z","image":"https://tdhopper.com/images/hdp-lda-gibbs-mr-men.png","permalink":"https://tdhopper.com/blog/hdp-lda/","title":"Notes on Gibbs Sampling in Hierarchical Dirichlet Process Models"},{"content":"I\u0026rsquo;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:\nWho is hiring at the intersection data science and agriculture?\n\u0026mdash; Tim Hopper (@tdhopper) September 16, 2015 I got a number of replies. Companies in this space (not all hiring for DS) include:\nThe Climate Corporation: Known for their high profile acquisition by Monsanto, Climate Corp \u0026ldquo;combines hyper-local weather monitoring, agronomic modeling, and high-resolution weather simulations\u0026rdquo; \u0026ldquo;that help farmers improve profitability by making better informed operating and financing decisions\u0026rdquo;. They are based in San Fransisco. FarmLogs: A YCombinator-backed startup in Ann Arbor, MI, FarmLogs is combining satellite maps, soil surveys, GPS data, and more to \u0026ldquo;expose critical operational data insights\u0026rdquo; to row crop farmers. Dairymaster: In Ireland, Darymaster is hiring data scientists for their business of dairy equipment manufacturing. aWhere: aWhere \u0026ldquo;delivers agricultural intelligence into the hands of farmers, commercial growers, and policymakers everywhere\u0026rdquo; by collecting detailed weather data from around the world. They are based in Denver. pulsepod: This small startup is building hardware to help farmers \u0026ldquo;know when to plant, fertilize, irrigate, and harvest to achieve quality and yield goals using data from your own field\u0026rdquo;. They are based in Princeton, NJ. Granular: \u0026ldquo;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.\u0026rdquo; They are based in San Fransisco. PrecisionHawk: Based in my home of Raleigh, NC, PrecisionHawk is \u0026ldquo;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.\u0026rdquo; mavrx: \u0026ldquo;We use aerial imagery to provide actionable insights to the global agriculture industry.\u0026rdquo; They are based in San Fransisco. AgSmarts: Memphis-based Agsmarts \u0026ldquo;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 \u0026amp; environmental sensors.\u0026rdquo; ","date":"2015-09-17T00:00:00Z","permalink":"https://tdhopper.com/blog/data-science-and-agriculture/","title":"Data Science and Agriculture"},{"content":"I appreciated James Hague\u0026rsquo;s post on Computer Science Courses that Don\u0026rsquo;t Exist, But Should.\nI really like Dave Thomas\u0026rsquo;s idea of a Software Archeology class. I have spent a huge amount of time as a developer reading (vs. writing) code. I wish I\u0026rsquo;d been taught how to read code effectively.\nSimilarly, I have thought there should be a class (or series of classes) in \u0026ldquo;interacting with others\u0026rsquo; code\u0026rdquo;. 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 \u0026ldquo;contributing to open source\u0026rdquo;.\n","date":"2015-09-12T01:55:00Z","permalink":"https://tdhopper.com/blog/classes-future-programmers-should-take/","title":"Classes Future Programmers Should Take"},{"content":"From Testicular volume is inversely correlated with nurturing-related brain activity in human fathers in PNAS:\nOne participant\u0026rsquo;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.\n","date":"2015-09-08T00:00:00Z","permalink":"https://tdhopper.com/blog/dealing-with-outliers/","title":"Dealing with Outliers"},{"content":" 1 2 3 4 5 6 7 import pandas as pd import numpy as np import random import matplotlib.pyplot as plt from scipy import stats from collections import namedtuple, Counter Suppose we receive some data that looks like the following:\n1 2 data = pd.Series.from_csv(\u0026#34;clusters.csv\u0026#34;) _=data.hist(bins=20) 1 data.size 1000 It appears that these data exist in three separate clusters. We want to develop a method for finding these latent clusters. One way to start developing a method is to attempt to describe the process that may have generated these data.\nFor simplicity and sanity, let\u0026rsquo;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$.\nGiven 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)$.\nEquivalently, we could consider these data to be generated from a probability distribution with this probability density function:\n$$ 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\\} $$where $\\pi$ is a 3-dimensional vector giving the mixing proportions. In other words, $\\pi_k$ describes the proportion of points that occur in cluster $k$.\nThat is, the probability distribution describing $x$ is a linear combination of normal distributions.\nWe want to use this generative 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$.\nWe 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.\nThe joint distribution of our observed data (data) along with the assignment variables is given by:\n\\begin{align} p(\\mathbf{x}, \\mathbf{z} \\,|\\, \\pi, \\theta_1, \\theta_2, \\theta_3, \\sigma)\u0026= p(\\mathbf{z} \\,|\\, \\pi) p(\\mathbf{x} \\,|\\, \\mathbf{z}, \\theta_1, \\theta_2, \\theta_3, \\sigma)\\\\ \u0026= \\prod_{i=1}^N p(z_i \\,|\\, \\pi) \\prod_{i=1}^N p(x_i \\,|\\, z_i, \\theta_1, \\theta_2, \\theta_3, \\sigma) \\\\ \u0026= \\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\\}\\\\ \u0026= \\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)\\\\ \u0026= \\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}Keeping Everything Straight Before moving on, we need to devise a way to keep all our data and parameters straight. Following ideas suggested by Keith Bonawitz, let\u0026rsquo;s define a \u0026ldquo;state\u0026rdquo; object to store all of this data.\nIt won\u0026rsquo;t yet be clear why we are defining some components of state, 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.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 SuffStat = namedtuple(\u0026#39;SuffStat\u0026#39;, \u0026#39;theta N\u0026#39;) def update_suffstats(state): for cluster_id, N in Counter(state[\u0026#39;assignment\u0026#39;]).iteritems(): points_in_cluster = [x for x, cid in zip(state[\u0026#39;data_\u0026#39;], state[\u0026#39;assignment\u0026#39;]) if cid == cluster_id ] mean = np.array(points_in_cluster).mean() state[\u0026#39;suffstats\u0026#39;][cluster_id] = SuffStat(mean, N) def initial_state(): num_clusters = 3 alpha = 1.0 cluster_ids = range(num_clusters) state = { \u0026#39;cluster_ids_\u0026#39;: cluster_ids, \u0026#39;data_\u0026#39;: data, \u0026#39;num_clusters_\u0026#39;: num_clusters, \u0026#39;cluster_variance_\u0026#39;: .01, \u0026#39;alpha_\u0026#39;: alpha, \u0026#39;hyperparameters_\u0026#39;: { \u0026#34;mean\u0026#34;: 0, \u0026#34;variance\u0026#34;: 1, }, \u0026#39;suffstats\u0026#39;: [None, None, None], \u0026#39;assignment\u0026#39;: [random.choice(cluster_ids) for _ in data], \u0026#39;pi\u0026#39;: [alpha / num_clusters for _ in cluster_ids], \u0026#39;cluster_means\u0026#39;: [-1, 0, 1] } update_suffstats(state) return state state = initial_state() 1 2 for k, v in state.items(): print(k) num_clusters_ suffstats data_ cluster_means cluster_variance_ cluster_ids_ assignment pi alpha_ hyperparameters_ Gibbs Sampling The theory of Gibbs sampling 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\n$$\\gamma_j^{(t)}\\sim p(\\gamma_j \\,|\\, \\gamma_{\\neg j}^{(t-1)})$$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$.\nIn deriving a Gibbs sampler, it is often helpful to observe that\n$$ p(\\gamma_j \\,|\\, \\gamma_{\\neg j}) = \\frac{ p(\\gamma_1,\\ldots,\\gamma_d) }{ p(\\gamma_{\\neg j}) } \\propto p(\\gamma_1,\\ldots,\\gamma_d). $$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).\nThe $\\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.\nMany 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.\nConditional Distribution of Assignment For berevity, we will use\n$$ p(z_i=k \\,|\\, \\cdot)= p(z_i=k \\,|\\, z_{\\neg i}, \\pi, \\theta_1, \\theta_2, \\theta_3, \\sigma, \\bf x ). $$Because cluster assignements are conditionally independent given the cluster weights and paramters,\n\\begin{align} p(z_i=k \\,|\\, \\cdot) \u0026\\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)} \\\\ \u0026\\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}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$.\nFor 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.\nBelow, we define functions for doing this sampling. sample_assignment will generate a sample from the posterior assignment distribution for the specified data point. update_assignment will sample from the posterior assignment for each data point and update the state object.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 def log_assignment_score(data_id, cluster_id, state): \u0026#34;\u0026#34;\u0026#34;log p(z_i=k \\,|\\, \\cdot) We compute these scores in log space for numerical stability. \u0026#34;\u0026#34;\u0026#34; x = state[\u0026#39;data_\u0026#39;][data_id] theta = state[\u0026#39;cluster_means\u0026#39;][cluster_id] var = state[\u0026#39;cluster_variance_\u0026#39;] log_pi = np.log(state[\u0026#39;pi\u0026#39;][cluster_id]) return log_pi + stats.norm.logpdf(x, theta, var) def assigment_probs(data_id, state): \u0026#34;\u0026#34;\u0026#34;p(z_i=cid \\,|\\, \\cdot) for cid in cluster_ids \u0026#34;\u0026#34;\u0026#34; scores = [log_assignment_score(data_id, cid, state) for cid in state[\u0026#39;cluster_ids_\u0026#39;]] scores = np.exp(np.array(scores)) return scores / scores.sum() def sample_assignment(data_id, state): \u0026#34;\u0026#34;\u0026#34;Sample cluster assignment for data_id given current state cf Step 1 of Algorithm 2.1 in Sudderth 2006 \u0026#34;\u0026#34;\u0026#34; p = assigment_probs(data_id, state) return np.random.choice(state[\u0026#39;cluster_ids_\u0026#39;], p=p) def update_assignment(state): \u0026#34;\u0026#34;\u0026#34;Update cluster assignment for each data point given current state cf Step 1 of Algorithm 2.1 in Sudderth 2006 \u0026#34;\u0026#34;\u0026#34; for data_id, x in enumerate(state[\u0026#39;data_\u0026#39;]): state[\u0026#39;assignment\u0026#39;][data_id] = sample_assignment(data_id, state) update_suffstats(state) Conditional Distribution of Mixture Weights 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).\n\\begin{align} p(\\pi \\,|\\, \\cdot)\u0026= p(\\pi \\,|\\, \\bf{z}, \\theta_1, \\theta_2, \\theta_3, \\sigma, \\mathbf{x}, \\alpha )\\\\ \u0026\\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 )\\\\ \u0026= p(\\pi \\,|\\, \\alpha ) p(\\bf{z}\\ \\,|\\, \\mathbf{x}, \\theta_1, \\theta_2, \\theta_3, \\sigma, \\pi, \\alpha )\\\\ \u0026= \\prod_{i=1}^K \\pi_k^{\\alpha/K - 1} \\prod_{i=1}^K \\pi_k^{\\sum_{i=1}^N \\delta(z_i, k)} \\\\ \u0026=\\prod_{k=1}^3 \\pi_k^{\\alpha/K+\\sum_{i=1}^N \\delta(z_i, k)-1}\\\\ \u0026\\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}Here are Python functions to sample from the mixture weights given the current state and to update the mixture weights in the state object.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 def sample_mixture_weights(state): \u0026#34;\u0026#34;\u0026#34;Sample new mixture weights from current state according to a Dirichlet distribution cf Step 2 of Algorithm 2.1 in Sudderth 2006 \u0026#34;\u0026#34;\u0026#34; ss = state[\u0026#39;suffstats\u0026#39;] alpha = [ss[cid].N + state[\u0026#39;alpha_\u0026#39;] / state[\u0026#39;num_clusters_\u0026#39;] for cid in state[\u0026#39;cluster_ids_\u0026#39;]] return stats.dirichlet(alpha).rvs(size=1).flatten() def update_mixture_weights(state): \u0026#34;\u0026#34;\u0026#34;Update state with new mixture weights from current state sampled according to a Dirichlet distribution cf Step 2 of Algorithm 2.1 in Sudderth 2006 \u0026#34;\u0026#34;\u0026#34; state[\u0026#39;pi\u0026#39;] = sample_mixture_weights(state) Conditional Distribution of Cluster Means Finally, we need to compute the conditional distribution for the cluster means.\nWe 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 section 2.3 of this and section 6.2 this.)\n\\begin{align} p(\\theta_k \\,|\\, \\cdot)\u0026= p(\\theta_k \\,|\\, \\bf{z}, \\pi, \\theta_{\\neg k}, \\sigma, \\bf x, \\lambda_1, \\lambda_2 ) \\\\ \u0026\\propto p(\\left\\{x_i \\,|\\, z_i=k\\right\\} \\,|\\, \\bf{z}, \\pi, \\theta_1, \\theta_2, \\theta_3, \\sigma, \\lambda_1, \\lambda_2) \\cdot\\\\ \u0026\\phantom{==}p(\\theta_k \\,|\\, \\bf{z}, \\pi, \\theta_{\\neg k}, \\sigma, \\lambda_1, \\lambda_2)\\\\ \u0026\\propto p(\\left\\{x_i \\,|\\, z_i=k\\right\\} \\,|\\, \\mathbf{z}, \\theta_k, \\sigma) p(\\theta_k \\,|\\, \\lambda_1, \\lambda_2)\\\\ \u0026= \\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} } $$and\n$$\\mu_n = \\sigma_n^2 \\left( \\frac{\\lambda_1}{\\lambda_2^2} + \\frac{n\\bar{x_k}}{\\sigma^2} \\right) $$Here is the code for sampling those means and for updating our state accordingly.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def sample_cluster_mean(cluster_id, state): cluster_var = state[\u0026#39;cluster_variance_\u0026#39;] hp_mean = state[\u0026#39;hyperparameters_\u0026#39;][\u0026#39;mean\u0026#39;] hp_var = state[\u0026#39;hyperparameters_\u0026#39;][\u0026#39;variance\u0026#39;] ss = state[\u0026#39;suffstats\u0026#39;][cluster_id] numerator = hp_mean / hp_var + ss.theta * ss.N / cluster_var denominator = (1.0 / hp_var + ss.N / cluster_var) posterior_mu = numerator / denominator posterior_var = 1.0 / denominator return stats.norm(posterior_mu, np.sqrt(posterior_var)).rvs() def update_cluster_means(state): state[\u0026#39;cluster_means\u0026#39;] = [sample_cluster_mean(cid, state) for cid in state[\u0026#39;cluster_ids_\u0026#39;]] Doing each of these three updates in sequence makes a complete Gibbs step for our mixture model. Here is a function to do that:\n1 2 3 4 def gibbs_step(state): update_assignment(state) update_mixture_weights(state) update_cluster_means(state) Initially, we assigned each data point to a random cluster. We can see this by plotting a histogram of each cluster.\n1 2 3 4 5 6 7 8 9 10 11 12 def plot_clusters(state): gby = pd.DataFrame({ \u0026#39;data\u0026#39;: state[\u0026#39;data_\u0026#39;], \u0026#39;assignment\u0026#39;: state[\u0026#39;assignment\u0026#39;]} ).groupby(by=\u0026#39;assignment\u0026#39;)[\u0026#39;data\u0026#39;] hist_data = [gby.get_group(cid).tolist() for cid in gby.groups.keys()] plt.hist(hist_data, bins=20, histtype=\u0026#39;stepfilled\u0026#39;, alpha=.5 ) plot_clusters(state) Each time we run gibbs_step, our state is updated with newly sampled assignments. Look what happens to our histogram after 5 steps:\n1 2 3 for _ in range(5): gibbs_step(state) plot_clusters(state) Suddenly, we are seeing clusters that appear very similar to what we would intuitively expect: three Gaussian clusters.\nAnother way to see the progress made by the Gibbs sampler is to plot the change in the model\u0026rsquo;s log-likelihood after each step. The log likehlihood is given by:\n$$ \\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) $$We can define this as a function of our state object:\n1 2 3 4 5 6 7 8 9 10 11 12 13 def log_likelihood(state): \u0026#34;\u0026#34;\u0026#34;Data log-likeliehood Equation 2.153 in Sudderth \u0026#34;\u0026#34;\u0026#34; ll = 0 for x in state[\u0026#39;data_\u0026#39;]: pi = state[\u0026#39;pi\u0026#39;] mean = state[\u0026#39;cluster_means\u0026#39;] sd = np.sqrt(state[\u0026#39;cluster_variance_\u0026#39;]) ll += np.log(np.dot(pi, stats.norm(mean, sd).pdf(x))) return ll 1 2 3 4 5 6 state = initial_state() ll = [log_likelihood(state)] for _ in range(20): gibbs_step(state) ll.append(log_likelihood(state)) pd.Series(ll).plot() 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 \u0026ldquo;likely\u0026rdquo;. However, the likelihood isn\u0026rsquo;t strictly monotonic: it jitters up and down. Though it behaves similarly, the Gibbs sampler isn\u0026rsquo;t optimizing the likelihood function. In its steady state, it is sampling from the posterior distribution. The state after each step of the Gibbs sampler is a sample from the posterior.\n1 pd.Series(ll).plot(ylim=[-150, -100]) In another post, I show how we can \u0026ldquo;collapse\u0026rdquo; 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.\nNotation Helper $N_k$, state['suffstat'][k].N: Number of points in cluster $k$.\n$\\theta_k$, state['suffstat'][k].theta: Mean of cluster $k$.\n$\\lambda_1$, state['hyperparameters_']['mean']: Mean of prior distribution over cluster means.\n$\\lambda_2^2$, state['hyperparameters_']['variance'] Variance of prior distribution over cluster means.\n$\\sigma^2$, state[cluster_variance_]: Known, fixed variance of clusters.\nThe superscript $(t)$ on $\\theta_k$, $pi_k$, and $z_i$ indicates the value of that variable at step $t$ of the Gibbs sampler.\n","date":"2015-09-02T00:00:00Z","image":"https://tdhopper.com/images/mixture-model-mr-men.png","permalink":"https://tdhopper.com/blog/mixture-model/","title":"Fitting a Mixture Model with Gibbs Sampling"},{"content":"I recently had the honor of being interviewed by Michael Swenson for his interview series called \u0026ldquo;Profiles in Computational Imagination\u0026rdquo;. I talked a bit about my current work, my wandering road to data science, and my love for remote work. You can read it here.\n","date":"2015-09-01T00:00:00Z","permalink":"https://tdhopper.com/blog/profile-in-computational-imagination/","title":"Profile in Computational Imagination"},{"content":"I\u0026#39;ll never forget the great words of one of my best math professors:\u0026#10;\u0026#10;\u0026quot;If all I wanted was the answer, I sure as hell wouldn\u0026#39;t ask you.\u0026quot;\n\u0026mdash; Tim Hopper (@tdhopper) December 9, 2013 ","date":"2015-08-12T00:00:00Z","permalink":"https://tdhopper.com/blog/on-showing-your-work/","title":"On Showing Your Work"},{"content":"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 Peopleware:\nIn 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\u0026rsquo;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.\nThe 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. \u0026ldquo;A Harvard diploma is worth something in and of itself, but our diploma isn\u0026rsquo;t worth squat. If this year\u0026rsquo;s graduates don\u0026rsquo;t get hired fast, there are no students next year and I\u0026rsquo;m out of a job.\u0026rdquo; 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.\nHe described how his students had been coached to show off their portfolios as part of each interview:\n\u0026ldquo;I\u0026rsquo;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\u0026rsquo;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\u0026rsquo; 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. \u0026hellip;\u0026rdquo;\nIn the years since, we\u0026rsquo;ve often heard more about that obscure technical college and those portfolios. We\u0026rsquo;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.\nOf course, this was a clever scheme of the professor\u0026rsquo;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\u0026rsquo;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?\n","date":"2015-08-05T00:00:00Z","permalink":"https://tdhopper.com/blog/a-programmers-portfolio/","title":"A Programmer's Portfolio"},{"content":"Latent Dirichlet Allocation is a generative 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 friendly introduction you should read.\nBecause LDA is a generative model, we can simulate the construction of documents by forward-sampling from the model. The generative algorithm is as follows (following Heinrich):\nfor each topic $k\\in [1,K]$ do sample term distribution for topic $\\overrightarrow \\phi_k \\sim \\text{Dir}(\\overrightarrow \\beta)$ for each document $m\\in [1, M]$ do sample topic distribution for document $\\overrightarrow\\theta_m\\sim \\text{Dir}(\\overrightarrow\\alpha)$ sample document length $N_m\\sim\\text{Pois}(\\xi)$ for all words $n\\in [1, N_m]$ in document $m$ do sample topic index $z_{m,n}\\sim\\text{Mult}(\\overrightarrow\\theta_m)$ sample term for word $w_{m,n}\\sim\\text{Mult}(\\overrightarrow\\phi_{z_{m,n}})$ You can implement this with a little bit of code and start to simulate documents.\nIn LDA, we assume each word in the document is generated by a two-step process:\nSample a topic from the topic distribution for the document. Sample a word from the term distribution from the topic. 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.\nTo run the generative model, we need to specify each of these parameters:\n1 2 3 4 5 6 7 vocabulary = [\u0026#39;see\u0026#39;, \u0026#39;spot\u0026#39;, \u0026#39;run\u0026#39;] num_terms = len(vocabulary) num_topics = 2 # K num_documents = 5 # M mean_document_length = 5 # xi term_dirichlet_parameter = 1 # beta topic_dirichlet_parameter = 1 # alpha 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.\n1 2 3 4 from scipy.stats import dirichlet, poisson from numpy import round from collections import defaultdict from random import choice as stl_choice 1 2 3 term_dirichlet_vector = num_terms * [term_dirichlet_parameter] term_distributions = dirichlet(term_dirichlet_vector, 2).rvs(size=num_topics) print(term_distributions) [[ 0.41 0.02 0.57] [ 0.38 0.36 0.26]] 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 distribution of distributions; we could look at it as a Dirichlet process!\nThe base base distribution of our Dirichlet process is a uniform distribution of topics (remember, topics are term distributions).\n1 2 3 4 5 6 base_distribution = lambda: stl_choice(term_distributions) # A sample from base_distribution is a distribution over terms # Each of our two topics has equal probability from collections import Counter for topic, count in Counter([tuple(base_distribution()) for _ in range(10000)]).most_common(): print(\u0026#34;count:\u0026#34;, count, \u0026#34;topic:\u0026#34;, [round(prob, 2) for prob in topic]) count: 5066 topic: [0.40999999999999998, 0.02, 0.56999999999999995] count: 4934 topic: [0.38, 0.35999999999999999, 0.26000000000000001] 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.\nTo construct a sample from the DP, we need to again define our DP class:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 from scipy.stats import beta from numpy.random import choice class DirichletProcessSample(): def __init__(self, base_measure, alpha): self.base_measure = base_measure self.alpha = alpha self.cache = [] self.weights = [] self.total_stick_used = 0. def __call__(self): remaining = 1.0 - self.total_stick_used i = DirichletProcessSample.roll_die(self.weights + [remaining]) if i is not None and i \u0026lt; len(self.weights) : return self.cache[i] else: stick_piece = beta(1, self.alpha).rvs() * remaining self.total_stick_used += stick_piece self.weights.append(stick_piece) new_value = self.base_measure() self.cache.append(new_value) return new_value @staticmethod def roll_die(weights): if weights: return choice(range(len(weights)), p=weights) else: return None For each document, we will draw a topic distribution from the Dirichlet process:\n1 2 topic_distribution = DirichletProcessSample(base_measure=base_distribution, alpha=topic_dirichlet_parameter) A sample from this topic distribution is a distribution over terms. However, unlike our base distribution which returns each term distribution with equal probability, the topics will be unevenly weighted.\n1 2 for topic, count in Counter([tuple(topic_distribution()) for _ in range(10000)]).most_common(): print(\u0026#34;count:\u0026#34;, count, \u0026#34;topic:\u0026#34;, [round(prob, 2) for prob in topic]) count: 9589 topic: [0.38, 0.35999999999999999, 0.26000000000000001] count: 411 topic: [0.40999999999999998, 0.02, 0.56999999999999995] 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).\n1 2 3 4 5 6 7 8 9 10 11 topic_index = defaultdict(list) documents = defaultdict(list) for doc in range(num_documents): topic_distribution_rvs = DirichletProcessSample(base_measure=base_distribution, alpha=topic_dirichlet_parameter) document_length = poisson(mean_document_length).rvs() for word in range(document_length): topic_distribution = topic_distribution_rvs() topic_index[doc].append(tuple(topic_distribution)) documents[doc].append(choice(vocabulary, p=topic_distribution)) Here are the documents we generated:\n1 2 for doc in documents.values(): print(doc) ['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'] We can see how each topic (term-distribution) is distributed across the documents:\n1 2 3 4 for i, doc in enumerate(Counter(term_dist).most_common() for term_dist in topic_index.values()): print(\u0026#34;Doc:\u0026#34;, i) for topic, count in doc: print(5*\u0026#34; \u0026#34;, \u0026#34;count:\u0026#34;, count, \u0026#34;topic:\u0026#34;, [round(prob, 2) for prob in topic]) 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] To recap: for each document we draw a sample from a Dirichlet Process. 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).\nFor each word in the document, we draw a sample (a term distribution) from the distribution (over term distributions) sampled 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.\nGiven this formulation, we might ask if we can roll an infinite sided die to draw from an unbounded number of topics (term distributions). We can do exactly this with a Hierarchical Dirichlet process. Instead of the base distribution of our Dirichlet process being a finite 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 distribution over terms.\nWe will again draw a sample from a Dirichlet Process 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).\nFor each word in the document, we will draw a sample (a term distribution) from the distribution (over term distributions) sampled 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.\nThese last few paragraphs are confusing! Let\u0026rsquo;s illustrate with code.\n1 2 3 4 5 term_dirichlet_vector = num_terms * [term_dirichlet_parameter] base_distribution = lambda: dirichlet(term_dirichlet_vector).rvs(size=1)[0] base_dp_parameter = 10 base_dp = DirichletProcessSample(base_distribution, alpha=base_dp_parameter) This sample from the base Dirichlet process is our infinite sided die. It is a probability distribution over a countable infinite number of topics.\nThe fact that our die is countably infinite is important. The sampler base_distribution 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 completely unique set of topics. By feeding base_distribution into a Dirichlet Process (stochastic memoizer), we allow the topics to be shared across documents.\nIn other words, base_distribution will never return the same topic twice; however, every topic sampled from base_dp would be sampled an infinite number of times (if we sampled from base_dp forever). At the same time, base_dp will also return an infinite number of topics. In our formulation of the the LDA sampler above, our base distribution only ever returned a finite number of topics (num_topics); there is no num_topics parameter here.\nGiven this setup, we can generate documents from the hierarchical Dirichlet process with an algorithm that is essentially identical to that of the original latent Dirichlet allocation generative sampler:\n1 2 3 4 5 6 7 8 9 10 11 12 13 nested_dp_parameter = 10 topic_index = defaultdict(list) documents = defaultdict(list) for doc in range(num_documents): topic_distribution_rvs = DirichletProcessSample(base_measure=base_dp, alpha=nested_dp_parameter) document_length = poisson(mean_document_length).rvs() for word in range(document_length): topic_distribution = topic_distribution_rvs() topic_index[doc].append(tuple(topic_distribution)) documents[doc].append(choice(vocabulary, p=topic_distribution)) Here are the documents we generated:\n1 2 for doc in documents.values(): print(doc) ['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'] And here are the latent topics used:\n1 2 3 4 for i, doc in enumerate(Counter(term_dist).most_common() for term_dist in topic_index.values()): print(\u0026#34;Doc:\u0026#34;, i) for topic, count in doc: print(5*\u0026#34; \u0026#34;, \u0026#34;count:\u0026#34;, count, \u0026#34;topic:\u0026#34;, [round(prob, 2) for prob in topic]) 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] 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!\nThis non-parametric formulation of Latent Dirichlet Allocation was first published by Yee Whye Teh et al.\nUnfortunately, forward sampling is the easy part. Fitting the model on data requires complex MCMC or variational inference. There are a limited number of implementations of HDP-LDA available, and none of them are great.\n","date":"2015-08-03T00:00:00Z","image":"https://tdhopper.com/images/nonparametric-lda-mr-men.png","permalink":"https://tdhopper.com/blog/nonparametric-lda/","title":"Nonparametric Latent Dirichlet Allocation"},{"content":"I love this new post on Quora\u0026rsquo;s engineering blog. The post states \u0026ldquo;high code quality is the long-term boost to development speed\u0026rdquo; and goes on to explain how they go about accomplishing this.\nI\u0026rsquo;ve inherited large code bases at each of my jobs out of grad school, and I\u0026rsquo;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.\n","date":"2015-07-30T00:00:00Z","permalink":"https://tdhopper.com/blog/high-quality-code-at-quora/","title":"High Quality Code at Quora"},{"content":"As we saw earlier the Dirichlet process describes the distribution 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 looks like $H_0$. On average, the larger $\\alpha$ is, the closer a sample from $\\text{DP}(\\alpha H_0)$ will be to $H_0$.\nSuppose we\u0026rsquo;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.\nBut 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!\nDan Roy, et al helpfully described a technique of using stochastic memoization to construct a distribution sampled from a Dirichlet process in a just-in-time manner. This process provides us with the equivalent of the Scipy rvs method for the sampled distribution. Stochastic memoization is equivalent to the Chinese restaurant process: sometimes you get seated an an occupied table (i.e. sometimes you\u0026rsquo;re given a sample you\u0026rsquo;ve seen before) and sometimes you\u0026rsquo;re put at a new table (given a unique sample).\nHere is our memoization class again:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 from numpy.random import choice from scipy.stats import beta class DirichletProcessSample(): def __init__(self, base_measure, alpha): self.base_measure = base_measure self.alpha = alpha self.cache = [] self.weights = [] self.total_stick_used = 0. def __call__(self): remaining = 1.0 - self.total_stick_used i = DirichletProcessSample.roll_die(self.weights + [remaining]) if i is not None and i \u0026lt; len(self.weights) : return self.cache[i] else: stick_piece = beta(1, self.alpha).rvs() * remaining self.total_stick_used += stick_piece self.weights.append(stick_piece) new_value = self.base_measure() self.cache.append(new_value) return new_value @staticmethod def roll_die(weights): if weights: return choice(range(len(weights)), p=weights) else: return None Let\u0026rsquo;s illustrate again with a standard normal base measure. We can construct a function base_measure that generates samples from it.\n1 2 3 from scipy.stats import norm base_measure = lambda: norm().rvs() 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.\n1 2 3 4 5 6 from pandas import Series ndraws = 10000 print(\u0026#34;Number of unique samples after {} draws:\u0026#34;.format(ndraws),) draws = Series([base_measure() for _ in range(ndraws)]) print(draws.unique().size) Number of unique samples after 10000 draws: 10000 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.\n1 2 3 4 5 norm_dp = DirichletProcessSample(base_measure, alpha=100) print(\u0026#34;Number of unique samples after {} draws:\u0026#34;.format(ndraws),) dp_draws = Series([norm_dp() for _ in range(ndraws)]) print(dp_draws.unique().size) Number of unique samples after 10000 draws: 446 At this point, we have a function dp_draws that returns samples from a probability distribution (specifically, a probability distribution sampled from $\\text{DP}(\\alpha H_0)$). We can use dp_draws as a base distribution for another Dirichlet process!\n1 norm_hdp = DirichletProcessSample(norm_dp, alpha=10) How do we interpret this? norm_dp is a sampler from a probability distribution that looks like the standard normal distribution. norm_hdp is a sampler from a probability distribution that \u0026ldquo;looks like\u0026rdquo; the distribution norm_dp samples from.\nHere is a histogram of samples drawn from norm_dp, our first sampler.\n1 2 3 import matplotlib.pyplot as plt Series(norm_dp() for _ in range(10000)).hist() _=plt.title(\u0026#34;Histogram of Samples from norm_dp\u0026#34;) And here is a histogram for samples drawn from norm_hdp, our second sampler.\n1 2 Series(norm_hdp() for _ in range(10000)).hist() _=plt.title(\u0026#34;Histogram of Samples from norm_hdp\u0026#34;) The second plot doesn\u0026rsquo;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.\nThis 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 Hierarchical Dirichlet Process, and it plays an important role in the study of Bayesian Nonparametrics.\nWithout 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 base distribution of the second-level Dirichlet process: this base distribution is a distribution drawn from the first-level Dirichlet process.\nThough 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.\nWe can define a Python class to encapsulate the Hierarchical Dirichlet Process as a base class of the Dirichlet process.\n1 2 3 4 5 6 7 class HierarchicalDirichletProcessSample(DirichletProcessSample): def __init__(self, base_measure, alpha1, alpha2): first_level_dp = DirichletProcessSample(base_measure, alpha1) self.second_level_dp = DirichletProcessSample(first_level_dp, alpha2) def __call__(self): return self.second_level_dp() 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.\n1 norm_hdp = HierarchicalDirichletProcessSample(base_measure, alpha1=10, alpha2=20) We can sample directly from the probability distribution drawn from the Hierarchical Dirichlet Process.\n1 2 Series(norm_hdp() for _ in range(10000)).hist() _=plt.title(\u0026#34;Histogram of samples from distribution drawn from Hierarchical DP\u0026#34;) norm_hdp is not equivalent to the Hierarchical Dirichlet Process; it samples from a single distribution sampled from this HDP. Each time we instantiate the norm_hdp variable, we are getting a sampler for a unique distribution. Below we sample five times and get five different distributions.\n1 2 3 4 5 for i in range(5): norm_hdp = HierarchicalDirichletProcessSample(base_measure, alpha1=10, alpha2=10) _=Series(norm_hdp() for _ in range(100)).hist() _=plt.title(\u0026#34;Histogram of samples from distribution drawn from Hierarchical DP\u0026#34;) _=plt.figure() In a later post, I will discuss how these tools are applied in the realm of Bayesian nonparametrics.\n","date":"2015-07-30T00:00:00Z","image":"https://tdhopper.com/images/hdp-sampling-mr-men.png","permalink":"https://tdhopper.com/blog/hdp-sampling/","title":"Sampling from a Hierarchical Dirichlet Process"},{"content":"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.\n\u0026mdash; Brian Degenhardt (@bmdhacks) January 29, 2015 ","date":"2015-07-28T00:00:00Z","permalink":"https://tdhopper.com/blog/10x-engineering/","title":"10x Engineering"},{"content":"Dirichlet Distribution The symmetric Dirichlet distribution (DD) can be considered a distribution of distributions. Each sample from the DD is a categorial distribution over $K$ categories. It is parameterized $G_0$, a distribution over $K$ categories and $\\alpha$, a scale factor.\nThe 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.\nWe 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.\n1 2 3 4 5 6 7 8 9 10 11 12 13 import numpy as np from scipy.stats import dirichlet np.set_printoptions(precision=2) def stats(scale_factor, G0=[.2, .2, .6], N=10000): samples = dirichlet(alpha = scale_factor * np.array(G0)).rvs(N) print(\u0026#34; alpha:\u0026#34;, scale_factor) print(\u0026#34; element-wise mean:\u0026#34;, samples.mean(axis=0)) print(\u0026#34;element-wise standard deviation:\u0026#34;, samples.std(axis=0)) print() for scale in [0.1, 1, 10, 100, 1000]: stats(scale) 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] Dirichlet Process The Dirichlet Process can be considered a way to generalize 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$.\nWe 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:\n$$H=\\sum_{k=1}^\\infty \\pi_k \\cdot\\delta(x-\\theta_k)$$where the $\\pi_k$ are carefully chosen weights (more later) that sum to 1. ($\\delta$ is the Dirac delta function.)\n$H$, a sample from $DP(\\alpha H_0)$, is a probability distribution that looks similar to $H_0$ (also a distribution). In particular, $H$ is a discrete distribution that takes the value $\\theta_k$ with probability $\\pi_k$. This sampled distribution $H$ is a discrete distribution even if $H_0$ has continuous support; the support of $H$ is a countably infinite subset of the support $H_0$.\nThe weights ($\\pi_k$ values) of a Dirichlet process sample related the Dirichlet process back to the Dirichlet distribution.\nGregor Heinrich writes:\nThe 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\u003c\\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$.\nAs 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\n$$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)$$where $H(S_n)$ be the sum of the $\\pi_k$ values whose $\\theta_k$ lie in $S_n$.\nThese $S_n$ subsets are chosen for convenience, however similar results would hold for any choice of $S_n$. For any sample from a Dirichlet process, we can construct a sample from a Dirichlet distribution by partitioning the support of the sample into a finite number of bins.\nThere 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.\nTo 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\n$$\\pi_k=\\beta_k \\prod_{j=1}^{k-1}(1-\\beta_j).$$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.\nHowever, by noting that the $\\pi_k$ values are positive 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 enough samples such that $\\sum_{k=1}^K \\pi_k\\approx 1$.\nWe 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.\nRecall that a single sample from a Dirichlet process is a probability distribution over a countably infinite subset of the support of the base measure.\nThe 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.\nWe generate enough $\\pi_k$ values are generated so their sum is greater than 0.99. When $\\alpha$ is small, very few $\\theta_k$\u0026rsquo;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)$.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import matplotlib.pyplot as plt from scipy.stats import beta, norm def dirichlet_sample_approximation(base_measure, alpha, tol=0.01): betas = [] pis = [] betas.append(beta(1, alpha).rvs()) pis.append(betas[0]) while sum(pis) \u0026lt; (1.-tol): s = np.sum([np.log(1 - b) for b in betas]) new_beta = beta(1, alpha).rvs() betas.append(new_beta) pis.append(new_beta * np.exp(s)) pis = np.array(pis) thetas = np.array([base_measure() for _ in pis]) return pis, thetas def plot_normal_dp_approximation(alpha): plt.figure() plt.title(\u0026#34;Dirichlet Process Sample with N(0,1) Base Measure\u0026#34;) plt.suptitle(\u0026#34;alpha: %s\u0026#34; % alpha) pis, thetas = dirichlet_sample_approximation(lambda: norm().rvs(), alpha) pis = pis * (norm.pdf(0) / pis.max()) plt.vlines(thetas, 0, pis, ) X = np.linspace(-4,4,100) plt.plot(X, norm.pdf(X)) plot_normal_dp_approximation(.1) plot_normal_dp_approximation(1) plot_normal_dp_approximation(10) plot_normal_dp_approximation(1000) Often we want to draw samples from a distribution sampled from a Dirichlet process instead of from the Dirichlet process itself. Much of the literature on the topic unhelpful refers to this as sampling from a Dirichlet process.\nFortunately, we don\u0026rsquo;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 as they are needed.\nSuppose, 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\n$$\\pi_1=0.5,\\; \\pi_2=0.3,\\; \\theta_1=0.1,\\; \\theta_2=-0.5.$$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\u003c=u\u003c0.8$, our sample is $-0.5$. If $u\u003e=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$.\nThe 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)$.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from numpy.random import choice class DirichletProcessSample(): def __init__(self, base_measure, alpha): self.base_measure = base_measure self.alpha = alpha self.cache = [] self.weights = [] self.total_stick_used = 0. def __call__(self): remaining = 1.0 - self.total_stick_used i = DirichletProcessSample.roll_die(self.weights + [remaining]) if i is not None and i \u0026lt; len(self.weights) : return self.cache[i] else: stick_piece = beta(1, self.alpha).rvs() * remaining self.total_stick_used += stick_piece self.weights.append(stick_piece) new_value = self.base_measure() self.cache.append(new_value) return new_value @staticmethod def roll_die(weights): if weights: return choice(range(len(weights)), p=weights) else: return None This Dirichlet process class could be called stochastic memoization. This idea was first articulated in somewhat abstruse terms by Daniel Roy, et al.\nBelow are histograms of 10000 samples drawn from samples drawn from Dirichlet processes with standard normal base distribution and varying $\\alpha$ values.\n1 2 3 4 5 6 7 8 9 10 import pandas as pd base_measure = lambda: norm().rvs() n_samples = 10000 samples = {} for alpha in [1, 10, 100, 1000]: dirichlet_norm = DirichletProcessSample(base_measure=base_measure, alpha=alpha) samples[\u0026#34;Alpha: %s\u0026#34; % alpha] = [dirichlet_norm() for _ in range(n_samples)] _ = pd.DataFrame(samples).hist() Note that these histograms look very similar to the corresponding plots of sampled distributions above. However, these histograms are plotting points sampled from a distribution sampled from a Dirichlet process while the plots above were showing approximate distributions samples from the Dirichlet process. 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$.\nIn a another post, I will look at how this DirichletProcessSample class can be used to draw samples from a hierarchical Dirichlet process.\n","date":"2015-07-28T00:00:00Z","image":"https://tdhopper.com/images/dirichlet-distribution-mr-men.png","permalink":"https://tdhopper.com/blog/dirichlet-distribution/","title":"Dirichlet Distribution and Dirichlet Processes"},{"content":"I\u0026rsquo;m starting to love single-page informational websites. For example:\nKeep a CHANGELOG: Olivier Lacan\u0026rsquo;s guide to writing a CHANGELOG.md for open source projects. strftime: Will McCutchen\u0026rsquo;s little page of the easily forgettable date formatting codes for Python programmers. PyFormat: Ulrich Petri and Horst Gutmann\u0026rsquo;s guide to new style (.format()) string formatting in Python. Two Factor Auth: \u0026ldquo;List of websites and whether or not they support 2FA.\u0026rdquo; My website Free Space is in this same vein.\nPublishing sites like this is free with Netlify and Cloudflare Pages. I would love to see more of them created!\n","date":"2015-07-27T00:00:00Z","permalink":"https://tdhopper.com/blog/handy-one-off-webpages/","title":"Handy One-off Webpages"},{"content":"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.\nI assumed open-floor plans for software shops were a relatively new invention. However, I just started reading Peopleware: Productive Projects and Teams, first published in 1987, and discovered that the first third of the book rails against open floor plan offices. I particularly enjoyed this quote:\nIn 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\u0026rsquo;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, \u0026ldquo;Wendl! What are you doing?\u0026rdquo; Wendl said, \u0026ldquo;I\u0026rsquo;m thinking.\u0026rdquo; And the boss said, \u0026ldquo;Can\u0026rsquo;t you do that at home?\u0026rdquo;\nThe 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.\n","date":"2015-07-22T00:00:00Z","permalink":"https://tdhopper.com/blog/thinking-at-work/","title":"Thinking at Work"},{"content":"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.\nI recently started my third \u0026ldquo;real\u0026rdquo; job since finishing school; at my first and third jobs I have been a \u0026ldquo;data scientist\u0026rdquo;. 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.\n\u0026ldquo;There has never been a better time to be a mathematician\u0026rdquo;? When I was in college Business Week published an article declaring \u0026ldquo;There has never been a better time to be a mathematician.\u0026rdquo; 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 \u0026ldquo;mathematician\u0026rdquo;). I have come across other pieces lauding this as the age of the mathematicians, and more often than not, I\u0026rsquo;ve wondered if the author knew what students actually studied in math departments.\nBackground on Me The math courses I had as an undergraduate were:\nLinear algebra Discrete math Differential equations (ODEs and numerical) Theory of statistics 1 Numerical analysis 1 (numerical linear algebra) and 2 (quadrature) Abstract algebra Number theory Real analysis Complex analysis Intermediate analysis (point set topology) 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).\nWhat is Data Science? Though my current position occasionally requires mathematical proof, I suspect that is rare among data scientist. While the \u0026ldquo;data science\u0026rdquo; 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.\nData Science Requires Programming and Engineering Few would argue that programming ability is not a key skill of data science. As Drew Conway has argued, a data scientist need not have a degree in computer science, but \u0026ldquo;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.\u0026rdquo; 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.\nMany data science positions require even more than the ability to solve problems with code. As Trey Causey has recently explained, many positions require understanding of software engineering 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.\nData Science Requires Applied Statistics 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 essential 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.\nData Science Involves Very Applied Math 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.\nI 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 \u0026ldquo;pure\u0026rdquo; mathematics, this is often true of what is formally known as \u0026ldquo;applied mathematics\u0026rdquo;. John D. Cook has made similar observations about the limitations of pure and applied math (as proper disciplines) in dubbing himself a \u0026ldquo;very applied mathematician\u0026rdquo;. Very applied mathematics is \u0026ldquo;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.\u0026rdquo; These skills are conspicuously absent from most math curricula I am familiar with.\nMath → Data Science 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.\nFirst, the academic study of mathematics provides much of the theoretical underpinnings of data science. 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 principle component analysis—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.\nMy second reason is connect to the first: I unwittingly took a number of courses that later played important roles in my data science toolkit. 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.\nThird, I did a minor in computer science as an undergraduate. 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.\nFourth, I did a masters degree in operations research (after my year as a math PhD student convinced me pure math wasn\u0026rsquo;t for me). 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.1\nFifth, my insatiable curiosity in computers and problem solving has played a key role in my career success. 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\u0026rsquo;ve been required to learn new skills and solve technical problems of all varieties. I\u0026rsquo;m comfortable jumping in to solve a new problem at work, because I\u0026rsquo;ve been doing that on my own time for fifteen years.\nSixth, I have been been fortunate enough to have employers who have patiently taught me and given me the freedom to learn on my own. I have learned an enormous amount in my two and a half year professional career, and I don\u0026rsquo;t anticipate slowing down any time soon. As Mat Kelcey has said: always be sure you\u0026rsquo;re not the smartest one in the room. I am very thankful for three jobs where I\u0026rsquo;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.\nFinally,2 it would be hard for me to overvalue the four and a half years of participation in the data science community on Twitter. Through Twitter, I have the ear of some of data science\u0026rsquo;s brightest minds (most of whom I\u0026rsquo;ve never met in person), and I\u0026rsquo;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\u0026rsquo;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\u0026rsquo;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 something instead of nothing about Theano, Scalding, and dplyr.3\nConclusions I don\u0026rsquo;t know to what extent my experience generalizes4, 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.\nHiring Data Scientists 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\u0026rsquo;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.\nStudying Math 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 very applied mathematics. 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.\nIf you\u0026rsquo;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).\nFor 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\u0026rsquo;t let your coursework be the extent of your education. There is so much more to learn!5\nUpdate (Oct. 2017) : I gave a talk based on this post\nUpdate (Mar. 2018) : I get a lot of emails with questions about this post, so I wrote FAQ post trying to answer some of them.\nAt 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.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nOf 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\u0026rsquo;t deployed a large scale machine learning system to production. I suck at software logging. I have no idea how deep learning works.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nI have successfully answered more than one interview question by regurgitating knowledge gleaned from tweets.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nAmong other reasons, I didn\u0026rsquo;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!\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nFor example, install Anaconda and start playing with some of these IPython notebooks.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2015-05-11T00:00:00Z","image":"https://tdhopper.com/math-class.png","permalink":"https://tdhopper.com/blog/how-i-became-a-data-scientist/","title":"How I Became a Data Scientist Despite Having Been a Math Major"},{"content":"Start Using Landsat on AWS: \u0026ldquo;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\u0026rdquo; on AWS.\nBeginner\u0026rsquo;s Guide to Linkers: 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.\nHow to Auto-Forward your Gmail Messages in Bulk: Use Google App Scripts to autoforward emails by simply adding a label. I use this to add things to my Omnifocus task link.\nWhich one result in mathematics has surprised you the most?: On Mathematics Stack Exchange. It might have been Huffman Coding for me.\nRuby Midwest 2013 The Most Important Optimization: Happiness: Ernie Miller explains why he doesn’t let his career trump his happiness.\nSake by tonyfischetti: Something of a modern GNU Make: \u0026ldquo;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\u0026rsquo;s workflow.\u0026rdquo;\nn1k0/SublimeHighlight: \u0026ldquo;An humble SublimeText package for exporting highlighted code as RTF or HTML.\u0026rdquo;\n","date":"2015-03-23T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-march-23-2015/","title":"Links for March 23, 2015"},{"content":"TIL `cat` will correctly join MP3 files http://t.co/mmc4NN2Kmp\n\u0026mdash; Tim Hopper (@tdhopper) March 15, 2015 From Stackoverflow:\nAn 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.\nThus\n1 cat file1.mp3 file2.mp3 \u0026gt; out.mp3 can join MP3 files.\n","date":"2015-03-15T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/concatenate-mp3-files/","title":"Concatenate MP3 Files"},{"content":"Dynamically Update a Plot in IPython: One thing I miss about Mathematica is Animate and Manipulate. IPython is slowing getting similar functionality. Here’s how to dynamically update a plot.\nJiahao\u0026rsquo;s IPython Notebook customizations: Drop this CSS file on your machine, and suddenly your IPython notebooks look quite beautiful!\nDuet Display: 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.\nCreating publication-quality figures with Matplotlib: Plotting in Python frustrates me to no end. But here’s a nice tutorial on creating nice figures in with Matplotlib.\nretrying 1.3.3 : Python Package Index: Python decorators \u0026ldquo;to simplify the task of adding retry behavior to just about anything.\u0026rdquo; These work like a charm!\n","date":"2015-03-13T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-march-13-2015/","title":"Links for March 13, 2015"},{"content":"Matt Blodgett: But Where Do People Work in This Office?: \u0026ldquo;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 \u0026lsquo;spot the desks’. I’ll show you what I mean.\u0026rdquo;\nWhy We (Still) Believe in Private Offices: Joel Spolsky and Fog Creek Software have been relentless defenders of quite, private offices for developers. They continue that here.\nPandoctor: An Alfred GUI for Pandoc: If you use Pandoc and Alfred, this is worth trying.\nAlfred Hop: I use a little bash tool called Hop to bookmark frequently used directories. I made this tool to give me quick access to my bookmarks from Alfred.\nDiscover Flask: Flask, the lightweight Python framework, is a joy to use. Here’s a nice introduction to it.\nIntroduction to dplyr: 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.\n","date":"2015-01-19T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-january-19-2015/","title":"Links for January 19, 2015"},{"content":"Sublime: Nice Features \u0026amp; Plugins: A brief talk introducing my favorite editor.\nAlfred Workflow for Pinboard: I\u0026rsquo;ve started using Pinboard a bit for organizing links. Here\u0026rsquo;s something that has the chance of getting me much deeper into pinboard: a powerful Alfred Workflow for interacting with Pinboard from your Mac\u0026rsquo;s keyboard. HT Aaron Bachya\nfullPage.js: I\u0026rsquo;ve been using this jquery plugin in a forthcoming project. It makes it really easy to create slide-like single page websites.\nObfuscating \u0026ldquo;Hello world!\u0026rdquo;: The author attempts to write the worst \u0026lsquo;hello world\u0026rsquo; possibe in Python. He does a good job.\nShow Time in Multiple Time Zones with TextExpander: As I spend more time working with people in different time zones, tools like this help remove the cognitive challenge of translating time.\n","date":"2014-12-29T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-december-29-2014/","title":"Links for December 29, 2014"},{"content":"Time: Programmers all hate time, timezones, etc. Here are some helpful \u0026ldquo;notes about time\u0026rdquo;.\nPython strftime reference: Speaking of time: \u0026ldquo;A quick reference for Python\u0026rsquo;s strftime formatting directives.\u0026rdquo; I have to look this stuff each time I need it.\ngitup: \u0026ldquo;A console script that allows you to easily update multiple git repositories at once\u0026rdquo;\nThe “How Does a Google Coder Work?” Edition : I enjoyed this interview. My favorite quote? \u0026ldquo;When you\u0026rsquo;re reading code is it as clear as reading English?\u0026rdquo; \u0026ldquo;If I\u0026rsquo;m reading C++ code, it\u0026rsquo;s clearer.\u0026rdquo;\nSunset Salvo: John Turkey discusses practical data analysis and statistical humility.\n10th Conference on Bayesian Nonparametrics: This is coming up in my own back yard. I’m excited!\nMachine Learning: The High-Interest Credit Card of Technical Debt: I haven’t read this in detail, but the premise makes tons of sense to me: \u0026ldquo;It is remarkably easy to incur massive ongoing maintenance costs at the system level when applying machine learning.\u0026rdquo;\n","date":"2014-12-22T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-december-22-2014/","title":"Links for December 22, 2014"},{"content":"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\u0026rsquo;ve emailed them to others.\nI hope this continues to be a valuable resource. I\u0026rsquo;d encourage you to share this with anyone you know who is thinking through this question.\nDownload PDF version Download ePUB version Excerpts DR. JOHN D. COOK, FREELANCE CONSULTANT AND BLOGGER There are basically two reasons to get a Ph.D.: personal satisfaction, and credentials for a job requiring a Ph.D.\nIf a professor has never worked outside of academia, I’d be skeptical of anything he or she says about “the real world.”\nDR. PAUL RUBIN, PROFESSOR EMERITUS 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).\nHaving both a masters and doctorate in mathematics is no better than having just a doctorate.\nDR. ERIC JONAS, POSTDOC AND SERIAL ENTREPRENEUR My best advice to an undergraduate curious about the “experience” of graduate school is: work in a lab while you’re an undergrad.\nA 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.\nMR. CARL VOGEL, DATA SCIENTIST The world is full of miserable grad students.\nSuccessful grad students aren’t like normal humans.\nDR. MELISSA SANTOS, DATA SCIENTIST 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.\nThe only reason you HAVE to do a Ph.D. is to become a professor. DR. PAUL HARPER, PROFESSOR OF OPERATIONS RESEARCH A very common misconception is that applicants can simply pick a supervisor of their choice, but this requires mutual consent.\nPerhaps the best way to summarise the life of a Ph.D. student is to look at the awesome Ph.D. comics (phdcomics.com) by Jorge Cham, which are spot on\nDR. LAURA MCLAY, PROFESSOR OF OPERATIONS RESEARCH 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\nLet me be clear: a Ph.D. is not a Masters degree plus a little more coursework and a small project. MR. MIKE NUTE, RECOVERING ACTUARY AND PH.D. STUDENT 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.\nThis reinforces the last point above: the only real reason to do a Ph.D. program is for love of the subject.\nDR. OSCAR BOYKIN, SOFTWARE ENGINEER The number one question: does he or she have a burning desire to do a PhD?\nAs a professor, you are running a startup that can never be profitable: you are always raising money and hiring.\n","date":"2014-12-08T00:00:00Z","image":"https://tdhopper.com/images/crossroads2.png","permalink":"https://tdhopper.com/phd/","title":"Should I get a Ph.D.?"},{"content":"Sketching as a Tool for Numerical Linear Algebra: A neat paper on sketching algorithms for linear algebra. No, not that kind of sketching. \u0026ldquo;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.\u0026rdquo;\nMaps and the Geospatial Revolution: Coursera is teaching a class in the spring on how geospatical technology has changed our world.\nGeoprocessing with Python using Open Source GIS: Speaking of geospatial technology, here are some slides and problems from a class on \u0026ldquo;Geoprocessing with Python\u0026rdquo;.\nHow to use the bash command line history: Bash\u0026rsquo;s history can do more than I realized!\nA geometric interpretation of the covariance matrix: Superb little post explaning covariance matrices with pictures and geometry.\n","date":"2014-12-06T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-december-06-2014/","title":"Links for December 6, 2014"},{"content":"How do I draw a pair of buttocks?: Have you ever wondered how to plot a pair of buttocks in Mathematica? Of course you have.\nFrequentism and Bayesianism: A Python-driven Primer: Jake Vanderplas wrote a \u0026ldquo;brief, semi-technical comparison\u0026rdquo; of frequentist and Bayesian statistical inference using examples in Python.\nskll: Dan Blanchard released version 1.0 of his very cool command line tool for doing experiments with scikit-learn.\nPersonalized Recommendations at Etsy: A fantastic post from Etsy\u0026rsquo;s engineering blog on building scalable, personalized recommendations using linear algebra and locally sensitive hashing. I like math.\nPythonic Clojure: Andrew Montalenti wrote a post analyzing Clojure from a Python programmer\u0026rsquo;s perspective. It\u0026rsquo;s great.\n","date":"2014-12-04T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-december-04-2014/","title":"Links for December 4, 2014"},{"content":"brushfire: Avi Bryant has been building a \u0026lsquo;Brushfire is a framework for distributed supervised learning of decision tree ensemble models in Scala.\u0026rsquo; Fun stuff!\nWhat are the lesser known but useful data structures?: I always enjoy StackOverflow questions like this, but it is not considered a good, on-topic question for this site, of course.\nFree Programming Books: A huge, crowd-sourced list of free programming books by language and topic.\nPhD Dissertations-Machine Learning Department: Seven years of ML PhD dissertations from Carnegie Mellon University. I wish I had time to read Tools for Large Graph Mining.\n","date":"2014-11-24T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-november-24-2014/","title":"Links for November 24, 2014"},{"content":"There\u0026rsquo;s no magic: virtualenv edition: I didn\u0026rsquo;t really get virtualenvs until long after I started programming Python, though they\u0026rsquo;re now an essential part of my toolkit. This is a great post explaining how they work.\nTraps for the Unwary in Python’s Import System: \u0026ldquo;Python’s import system is powerful, but also quite complicated.\u0026rdquo;\npyfmt: I recently learned about gofmt for auto-formatting Go code. Here\u0026rsquo;s a similar tool for Python.\nQ: Setting User-Agent Field?: A 1996 question in comp.lang.java on how to set the user agent field for a Java crawler. The signature on the question? Thanks, Larry Page\nalecthomas/importmagic: Python tool and Sublime extension for automatically adding imports.\n","date":"2014-11-17T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-november-17-2014/","title":"Links for November 17, 2014"},{"content":"Amazon Picking Challenge: Kiva Systems (where I interned in 2011) is setting up a robotics challenging for picking items off warehouse shelves.\ncontexttimer 0.3.1: A handy Python context manger and decorator for timing things.\nHow-to: Translate from MapReduce to Apache Spark: This is a helpful bit from Cloudera on moving algorithms from Mapreduce to Spark.\ncombinatorics 1.4.3: Here\u0026rsquo;s a Python module adding some combinatorial functions to the language.\nSpecial methods and interface-based type system: Guido van Rossum explains (in 2006) why Python uses len(x) instead of x.len().\n","date":"2014-11-12T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-november-12-2014/","title":"Links for November 12, 2014"},{"content":"Public Data Sets : Amazon Web Services: Amazon hosts a number of publicly datasets on AWS (including the common crawl corpus and the \u0026ldquo;Marvel Universe Social Graph\u0026rdquo;).\nRapid Web Prototyping with Lightweight Tools: I\u0026rsquo;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\u0026rsquo;s surprisingly easy to get a website running these days.\nrest_toolkit: REST has been my obsession of late. Here\u0026rsquo;s a little Python package for quickly writing RESTful APIs.\nThe Joys of the Craft: A quote from Fred Brooks\u0026rsquo; The Mythical Man-Month on why programming is fun.\nHow do I use pushd and popd commands?: I recently learned bash has push and popd commands for temporarily changing directories. This is very handy for scripting.\n","date":"2014-11-03T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-november-03-2014/","title":"Links for November 3, 2014"},{"content":"The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!): I guess the title says it all. By Joel Spolsky.\nUnix Shells - Hyperpolyglot: Very cool comparison of basic command syntax in Bash, Fish, Ksh, Tcsh, and Zsh.\nBetter Bash history: I\u0026rsquo;m pretty stuck on Bash at the moment. Here\u0026rsquo;s a way to get a better history in Bash. (Other shells often improve on Bash\u0026rsquo;s history.)\nusaddress 0.1: I always love seeing a Python library for something I\u0026rsquo;ve tried to do poorly on my own: \u0026ldquo;usaddress is a python library for parsing unstructured address strings into address components, using advanced NLP methods.\u0026rdquo;\nmore-itertools: A great extension to the helpful itertools module in Python. Some particularly helpful functions: chunked, first, peekaboo, and take. Unfortunately, it doesn\u0026rsquo;t have Python 3 support at the moment.\n","date":"2014-11-01T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-november-01-2014/","title":"Links for November 1, 2014"},{"content":"The pyspark documentation doesn\u0026rsquo;t include an example for the aggregateByKey RDD method. I didn\u0026rsquo;t find any nice examples online, so I wrote my own.\nHere\u0026rsquo;s what the documentation does say:\naggregateByKey(self, zeroValue, seqFunc, combFunc, numPartitions=None)\nAggregate the values of each key, using given combine functions and a neutral \u0026ldquo;zero value\u0026rdquo;. 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\u0026rsquo;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.\nreduceByKey and aggregateByKey are much more efficient than groupByKey and should be used for aggregations as much as possible.\nIn 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 aggregateByKey is doing.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 Welcome to ____ __ / __/__ ___ _____/ /__ _\\ \\/ _ \\/ _ `/ __/ \u0026#39;_/ /__ / .__/\\_,_/_/ /_/\\_\\ version 1.1.0 /_/ Using Python version 2.7.5 (default, Mar 9 2014 22:15:05) SparkContext available as sc. In [1]: # Create rdd that is a list of characters In [2]: sc.parallelize(list(\u0026#34;aaaaabbbbcccdd\u0026#34;)) \\ ...: .map(lambda letter: (letter, {\u0026#34;value\u0026#34;: letter})) \\ ...: .aggregateByKey( ...: # Value to start aggregation (passed as s to `lambda s, d`) ...: \u0026#34;start\u0026#34;, ...: # Function to join final data type (string) and rdd data type ...: lambda s, d: \u0026#34;[ %s %s ]\u0026#34; % (s, d[\u0026#34;value\u0026#34;]), ...: # Function to join two final data types. ...: lambda s1, s2: \u0026#34;{ %s %s }\u0026#34; % (s1, s2), ...: ) \\ ...: .collect() Out[2]: [(\u0026#39;a\u0026#39;, \u0026#39;{ { [ start a ] [ [ start a ] a ] } [ [ start a ] a ] }\u0026#39;), (\u0026#39;c\u0026#39;, \u0026#39;{ [ start c ] [ [ start c ] c ] }\u0026#39;), (\u0026#39;b\u0026#39;, \u0026#39;{ { [ [ start b ] b ] [ start b ] } [ start b ] }\u0026#39;), (\u0026#39;d\u0026#39;, \u0026#39;[ [ start d ] d ]\u0026#39;)] ","date":"2014-10-03T00:00:00Z","permalink":"https://tdhopper.com/blog/pysparks-aggregatebykey-method/","title":"Pyspark's AggregateByKey Method"},{"content":"Hammock: A lightweight wrapper around the Python requests module to convert REST APIs into \u0026ldquo;dead simple programmatic APIs\u0026rdquo;. It\u0026rsquo;s a clever idea. I\u0026rsquo;ll have to play around with it before I can come up with a firm opinion.\npipsi: Pipsi wraps pip and virtualenv to allow you to install Python command line utilities without polluting your global environment.\nWriting a Command-Line Tool in Python: Speaking of Python command line utilities, here\u0026rsquo;s a little post from Vincent Driessen on writing them.\nIterables vs. Iterators vs. Generators: Vincent has been on a roll lately. He also wrote this \u0026ldquo;little pocket reference on iterables, iterators and generators\u0026rdquo; in Python.\nDesign for Continuous Experimentation: Talk and Slides: I didn\u0026rsquo;t watch the lecture, but Dan McKinley\u0026rsquo;s slides on web experimentation are excellent.\nApache Spark: A Delight for Developers: I\u0026rsquo;ve been playing with PySpark lately, and it really is fun.\n","date":"2014-09-30T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-september-30-2014/","title":"Links for September 30, 2014"},{"content":"Philosophy of Statistics (Stanford Encyclopedia of Philosophy): 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.\nConnect Sunlight Foundation to anything: “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 IFTTT channel. Get push notifications when the president signs a bill!\nfurbo.org · The Terminal: Craig Hockenberry wrote a massive post on how he uses the Terminal on OS X for fun and profit. You will learn things.\nA sneak peek at Camera+ 6… manual controls are coming soon to you! : I’ve been a Camera+ user on iOS for a long time. The new version coming out soon is very exciting.\nGitHut - Programming Languages and GitHub: A very clever visualization of various languages represented on Github and of the properties of their respective repositories.\n","date":"2014-09-25T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-september-25-2014/","title":"Links for September 25, 2014"},{"content":"Open Sourcing a Python Project the Right Way: 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.”\nelasticsearch/elasticsearch-dsl-py: 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!\nEquipment Guide — The Podcasting Handbook: Dan Benjamin of 5by5 podcasting fame is writing a book on podcasting. Here’s his brief equipment guide.\nbachya/pinpress: Aaron Bach put together a neat Ruby script that he uses to generate his link posts. This is similar to but better than my sundry tool.\nMarkdown Resume Builder: I haven’t tried this yet, but I like the idea: a Markdown based resume format that can be converted into HTML or PDF.\nGit - Tips and Tricks: Enabling autocomplete in Git is something I should have done long ago.\nApache Storm Design Pattern—Micro Batching: Micro batching is a valuable tool when doing stream processing. Horton Works put up a helpful post outlining three ways of doing it.\n","date":"2014-09-20T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-september-20-2014/","title":"Links for September 20, 2014"},{"content":"Recently, I\u0026rsquo;ve spent a lot of time going back and forth between Python dicts and JSON. For some reason, I decided last week that I\u0026rsquo;d be useful to be able to quickly convert a Python dict to pretty printed JSON.\nI created a TextExpander snippet that takes a Python dict from the clipboard, converts it to JSON, and pastes it.\nHere are the details:\n1 2 3 4 5 6 7 8 9 10 11 12 13 #!/usr/bin/env python import os, json import subprocess def getClipboardData(): p = subprocess.Popen([\u0026#39;pbpaste\u0026#39;], stdout=subprocess.PIPE) retcode = p.wait() data = p.stdout.read() return data cb = eval(getClipboardData()) print json.dumps(cb, sort_keys=True, indent=4, separators=(\u0026#39;,\u0026#39;, \u0026#39;: \u0026#39;)) ","date":"2014-09-18T00:00:00Z","permalink":"https://tdhopper.com/blog/quickly-converting-python-dict-to-json/","title":"Quickly Converting Python Dict to JSON"},{"content":"textract: 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.\nFlask Kit: I\u0026rsquo;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.\ncookiecutter: I was looking for this recently, but it I couldn\u0026rsquo;t find it. \u0026ldquo;A command-line utility that creates projects from cookiecutters (project templates). E.g. Python package projects, jQuery plugin projects.\u0026rdquo; There\u0026rsquo;s even a Flask template!\nOver 50? You Probably Prefer Negative Stories About Young People: A research paper from a few years ago show that older people prefer to read negative news about young people. \u0026ldquo;In fact, older readers who chose to read negative stories about young individuals actually get a small boost in their self-esteem.\u0026rdquo;\nEpisode 564: The Signature: The fantastic Planet Money podcast explains why signatures are meaningless in a modern age. My scribbles have become even worse since listening to this.\ngithub-selfies: Here\u0026rsquo;s a Chrome and Firefox extension that allows you to quickly embed gif selfies in Github posts. Caution: may lead to improved team morale.\n","date":"2014-09-10T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-september-10-2014/","title":"Links for September 10, 2014"},{"content":"TIL how to checkout the last branch you were on in Git (before the current):\ngit checkout -\n\u0026mdash; Tim Hopper (@tdhopper) September 10, 2014 ","date":"2014-09-10T00:00:00Z","image":"https://tdhopper.com/images/til.png","permalink":"https://tdhopper.com/blog/shortcut-to-switch-to-previous-git-branch/","title":"Shortcut to Switch to Previous Git Branch"},{"content":"Ggplot2 To Ggvis: I\u0026rsquo;m a huge fan of ggplot2 for data visualization in R. Here\u0026rsquo;s a brief tutorial for ggplot2 users to learn ggvis for generating interactive plots in R using the grammar of graphics.\nFrom zero to storm cluster for scikit-learn classification | Daniel Rodriguez: This is a very cool, if brief, blog post on using streamparse, my company\u0026rsquo;s open source wrapper for Apache Storm, and scikit-learn, my favorite machine learning library, to do machine learning on data streams.\nPythonic means idiomatic and tasteful: My boss Andrew recently shared an old blogpost he wrote on what it means for code to be Pythonic; I think he\u0026rsquo;s right on track.\nPythonic 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.\ngit workflow: In my ever continuing attempt to be able to run my entire life from Alfred, I recently installed this workflow that makes git repositories on my computer easily searchable.\nAlfred-Workflow: Speaking of Alfred, here\u0026rsquo;s a handy Python library that makes it easy to write your own (if you\u0026rsquo;re a Python programmer).\nSquashing commits with rebase: 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\u0026rsquo;t abuse it.\n","date":"2014-08-30T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-august-30-2014/","title":"Links for August 30, 2014"},{"content":"As I have noted before, body weight is a noisy thing. Day to day, your weight will probably fluctuate by several pounds. If you\u0026rsquo;re trying to lose weight, this noise can cause unfounded frustration and premature excitement.\nWhen I started a serious weight loss plan a year and a half ago, I bought a wifi-enabled Withings Scale. The scale allows me to automatically sync my weight with Montior Your Weight, MyFitnessPal, RunKeeper, and other fitness apps on my phone. IFTTT also has great Withings support allowing me to push my weight to various other web services.\nOne IFTTT rule I have appends my weight to a text file in Dropbox. This file looks like this:\n1 2 3 4 5 6 7 8 9 10 263.86 August 21, 2014 at 05:56AM 264.62 August 22, 2014 at 08:27AM 264.56 August 23, 2014 at 09:41AM 263.99 August 24, 2014 at 08:02AM 265.64 August 25, 2014 at 08:08AM 267.4 August 26, 2014 at 08:16AM 265.25 August 27, 2014 at 09:08AM 264.17 August 28, 2014 at 07:21AM 264.03 August 29, 2014 at 08:43AM 262.71 August 30, 2014 at 08:47AM 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\u0026rsquo;ve come up with a decent solution.\nThis R script 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\u0026rsquo;s not the prettiest script, but it gets the job done for now.1\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 INPUT_PATH \u0026lt;- \u0026#34;~/Dropbox/Text Notes/Weight.txt\u0026#34; OUTPUT_PATH \u0026lt;- \u0026#34;~/Dropbox/Text Notes/Weight Stats.txt\u0026#34; library(lubridate) library(ggplot2) library(zoo) # READ FILE con \u0026lt;- file(INPUT_PATH, \u0026#34;rt\u0026#34;) lines \u0026lt;- readLines(con) close(con) # PARSE INTO LISTS OF WEIGHTS AND DATES parse.line \u0026lt;- function(line) { s \u0026lt;- strsplit(line, split=\u0026#34; \u0026#34;)[[1]] date.str \u0026lt;- paste(s[2:10][!is.na(s[2:10])], collapse=\u0026#34; \u0026#34;) date \u0026lt;- mdy_hm(date.str, quiet=TRUE) l \u0026lt;- list(as.numeric(s[1]), date) names(l) \u0026lt;- c(\u0026#34;weight\u0026#34;, \u0026#34;date\u0026#34;) l } list.weight.date \u0026lt;- lapply(lines, parse.line) weights \u0026lt;- lapply(list.weight.date, function(X) X$weight) dates \u0026lt;- lapply(list.weight.date, function(X) X$date) # BUILD DATA FRAME df \u0026lt;- data.frame(weight = unlist(weights), date = do.call(\u0026#34;c\u0026#34;, dates) ) # CREATE TIME SERIES AND RESAMPLE ts \u0026lt;- zoo(c(df$weight), df$date) ts \u0026lt;- aggregate(ts, time(ts), tail, 1) g \u0026lt;- round(seq(start(ts), end(ts), 60 * 60 * 24), \u0026#34;days\u0026#34;) ts \u0026lt;- na.approx(ts, xout = g) # FUNCTION TO GET WEIGHT N-DAYS AGO IF WEIGHT IS SMOOTHED BY ROLLING MEDIAN # OVER A GIVEN (smooth.n) NUMBER OF DAYS days.ago \u0026lt;- function(days, smooth.n) { date \u0026lt;- head(tail(index(ts),days + 1),1) smoothed \u0026lt;- rollmedianr(ts, smooth.n) as.numeric(smoothed[date]) } # SMOOTH WEIGHT BY 29 DAYS AND GENERATE SOME SUMMARY STATS days = 29 current.weight \u0026lt;- days.ago(0, days) x \u0026lt;- c(current.weight, current.weight-days.ago(7, days), current.weight-days.ago(30, days), current.weight-days.ago(365, days), current.weight-max(ts)) x = round(x, 1) names(x) = c(\u0026#34;current\u0026#34;, \u0026#34;7days\u0026#34;, \u0026#34;30days\u0026#34;, \u0026#34;365days\u0026#34;, \u0026#34;max\u0026#34;) fileConn\u0026lt;-file(OUTPUT_PATH) w \u0026lt;- c(paste(\u0026#34;Weight (lbs):\u0026#34;, x[\u0026#34;current\u0026#34;]), paste(\u0026#34;Total Δ:\u0026#34;, x[\u0026#34;max\u0026#34;]), paste(\u0026#34;1 Week Δ:\u0026#34;, x[\u0026#34;7days\u0026#34;]), paste(\u0026#34;1 Month Δ:\u0026#34;, x[\u0026#34;30days\u0026#34;]), paste(\u0026#34;1 Year Δ:\u0026#34;, x[\u0026#34;365days\u0026#34;])) writeLines(w,fileConn) close(fileConn) The output looks something like this:\n1 2 3 4 5 Weight (lbs): 265.7 Total Δ: -112 1 Week Δ: -0.8 1 Month Δ: -4.8 1 Year Δ: -75 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 new_weight_measurement, every time I weigh in. On my Mac Mini, I have a Hazel 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.\nMy Hazel rule looks like this:\nThe \u0026rsquo;embedded script\u0026rsquo; that is run is the R script above; I just have to tell Hazel to use the Rscript shell.2\nAt this point, every time I step on my scale, a text file with readable statistics about my smoothed weight appear in my Dropbox folder.\nOf 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 Weight Stats.txt to be created. Hazel can pass the path of the updated file into any script of your choice. You could, for example, use Mailgun to email it to yourself or Pushover to push it to your mobile devices. Obviously, I want to tweet mine.\nI have a Twitter account called @hopsfitness where I\u0026rsquo;ve recently been tracking my fitness progress. On my Mac Mini, I have t 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:\nSince 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\u0026rsquo;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\u0026rsquo;m going; this is much preferable to the noisy weight I see on my scale.\nUpdate (2014-12-06): I replaced my R script with a Python/pandas script. It requires Python 3 (to render the delta characters).\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import dateutil import pandas as pd import random from os.path import expanduser, join home = expanduser(\u0026#34;~\u0026#34;) with open(join(home, \u0026#34;Dropbox/Text Notes/Weight.txt\u0026#34;), \u0026#34;r\u0026#34;) as f: lines = f.readlines() def parse_line(line): s = line.split(\u0026#34; \u0026#34;) weight = float(s[0]) date = dateutil.parser.parse(\u0026#39; \u0026#39;.join(s[1:4])) return date, weight weight = pd.DataFrame([parse_line(l) for l in lines], columns=[\u0026#34;date\u0026#34;, \u0026#34;weight\u0026#34;]) \\ .set_index(\u0026#34;date\u0026#34;) \\ .resample(\u0026#34;1D\u0026#34;, how=\u0026#34;mean\u0026#34;) weight[\u0026#34;missing\u0026#34;] = weight.weight.isnull() weight.weight = weight.weight.interpolate(method=\u0026#34;linear\u0026#34;) std = weight.weight.diff().dropna().std() noise = weight.missing.map(lambda missing: random.normalvariate(0, std) if missing else 0) weight.weight = weight.weight + noise smoothed = pd.ewma(weight.weight, span=30) current = smoothed[-1] stats = \u0026#34;\u0026#34;\u0026#34; Weight (lbs): %(weight).1f Total Δ: %(total).1f 1 Week Δ: %(week).1f 1 Month Δ: %(month).1f 1 Year Δ: %(year).1f \u0026#34;\u0026#34;\u0026#34;.strip() % {\u0026#34;weight\u0026#34;: current, \u0026#34;total\u0026#34;: current - smoothed[0], \u0026#34;week\u0026#34;: current - smoothed[-8], \u0026#34;month\u0026#34;: current - smoothed[-32], \u0026#34;year\u0026#34;: current - smoothed[-366], } with open(join(home, \u0026#34;Dropbox/Text Notes/Weight Stats.txt\u0026#34;), \u0026#34;wb\u0026#34;) as f: f.write(bytes(stats, \u0026#39;UTF-8\u0026#39;)) This assumes your input file is formatted like mine, but you could easily adjust the first part of the code for other formats.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nYou can download R here; installing it should add Rscript to your system path.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2014-08-30T00:00:00Z","permalink":"https://tdhopper.com/blog/tracking-weight-loss-with-r-hazel-withings-and-ifttt/","title":"Tracking Weight Loss with R, Hazel, Withings, and IFTTT"},{"content":"I spend a lot of time in IPython Notebooks 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 without having to keep a terminal window open..\nIf you\u0026rsquo;ve ever tried to do chron-like automation on OS X, you\u0026rsquo;ve surely come across launchd, \u0026ldquo;a unified, open-source service management framework for starting, stopping and managing daemons, applications, processes, and script\u0026rdquo;. You\u0026rsquo;ve probably also gotten frustated with launchd and given up.\nI recently started using LaunchControl \u0026ldquo;a fully-featured launchd GUI\u0026rdquo; for launchd; it\u0026rsquo;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.\nI created a LaunchControl to run the following command.\n1 /usr/local/bin/IPython notebook --matplotlib inline --port=9777 --browser=false 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.\nI added three other launchd keys in LaunchControl:\nA Working Directory key to tell LaunchControl to start my notebook in my desired folder. A Run At Load key to tell it to start my kernel as soon as I load the job. And a Keep alive key to tell LaunchControl to restart my kernel should the process ever die. Here\u0026rsquo;s how it looks in LaunchControl:\nAfter 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 127.0.0.1 parsely.scratch to my hosts file so I can access my Notebooks at parsely.scratch:9777. This works nicely with Chrome\u0026rsquo;s autocomplete feature. I\u0026rsquo;m avoiding the temptation to run nginx and give it an even prettier url.\n","date":"2014-08-28T00:00:00Z","permalink":"https://tdhopper.com/blog/keeping-ipython-notebooks-running-in-the-background/","title":"Keeping IPython Notebooks Running in the Background"},{"content":"How do I generate a uniform random integer partition?: This week, I wanted to generate random partitions of integers. Unsurprisingly, stackoverflow pulled through with a Python snippet to do just that.\nFirefox and Chrome Bookmarks: I love Alfred 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.\nYNAB for iPad is Here: 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!\nDistributed systems theory for the distributed systems engineer: I work on distributed systems these days. I need to read some of these papers.\n","date":"2014-08-28T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-august-28-2014/","title":"Links for August 28, 2014"},{"content":"How can I pretty-print JSON at the command line?: I needed to pretty print some JSON at the command line earlier today. The easiest way might be to pipe it through python -m json.tool.\nIntegrating Alfred \u0026amp; Keyboard Maestro: I love Keyboard Maestro for automating all kinds of things on my Mac, but I\u0026rsquo;m reaching a limit of keyboard shortcuts I can remember. Here\u0026rsquo;s an Alfred workflow for launching macros instead.\nstreamparse 1.0.0: My team at Parsely is building a tool for easily writing Storm topologies (for processing large volumes of streaming data) in Python. We just released 1.0.0!\nTextExpander Tools: Brett Terpstra, the king of Mac hacks, has some really handy tools for TextExpander.\nGNU Parallel: GNU parallel is a shell tool for executing jobs in parallel using one or more computers using xargs-like syntax. Pretty cool. HT @oceankidbilly.\n","date":"2014-08-25T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-august-25-2014/","title":"Links for August 25, 2014"},{"content":"Arrow: better dates and times for Python: Arrow is a slick Python library \u0026ldquo;that offers a sensible, human-friendly approach to creating, manipulating, formatting and converting dates, times, and timestamps\u0026rdquo;. It\u0026rsquo;s a friendly alternative to datetime.\nDocker via Homebrew: I\u0026rsquo;m starting to use Docker (\u0026ldquo;Docker is an open platform for developers and sysadmins to build, ship, and run distributed applications\u0026rdquo;) on occasion. Here are easy install instructions for Mac users.\nMining Massive Datasets MOOC: I\u0026rsquo;m terrible at completing MOOCs, but I\u0026rsquo;m really interested in this new on on Mining Massive Datasets.\nURL Pinner - Chrome Web Store: 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).\nUsing multitail for monitoring multiple log files: If you work with distributed systems, you\u0026rsquo;re probably used to SSH-ing into multiple machines to access logs. Multitool might save you some time.\nSaturday Morning Breakfast Cereal: SBMC shows how job interviews would go if we were more honest.\n","date":"2014-08-23T00:00:00Z","permalink":"https://tdhopper.com/blog/sundry-links-for-august-23-2014/","title":"Links for August 23, 2014"},{"content":"Back in July, I posted some analysis of my attempt at weight loss. Now that I\u0026rsquo;m four months further down the line, I thought I\u0026rsquo;d post a follow-up.\nI continue to be fascinated with how noisy my weight time series is. While I\u0026rsquo;ve continued to lose weight over time, my weight goes up two out of five mornings.\nHere\u0026rsquo;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:\nThis volatility can hide the fact that I\u0026rsquo;m making progress! When I put a regression line through the points, you can see that the average change slightly below zero:1\nI 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.\nIndeed, 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\u0026rsquo;m more likely to be doing things with friends, and thus I have less control over my meals.2\nI wish I had a good explanation for why the change on Friday is so dramatic, but I don\u0026rsquo;t. Any guesses?\nI mentioned this to my college roommate who is a financial planner. He noted how similar this is to investing; it\u0026rsquo;s a constant battle for him to convince his clients to look at average behavior instead of daily changes.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nAlso, beer.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2013-11-28T00:00:00Z","permalink":"https://tdhopper.com/blog/noisy-series-and-body-weight-take-2/","title":"Noisy Series and Body Weight Part 2"},{"content":"I recently discovered the Twitter account @primes. 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).1 Second, how long until they get there.\nDoing some quick calculations in Mathematica, I believe the largest 140 digit prime is the following:\n9999999999999999999999999999999999999999999999 9999999999999999999999999999999999999999999999 999999999999999999999999999999999999999999999997\nWolfram Alpha confirms that this is prime and that the next prime is 141 characters.\nAs for how long it would take, recall that the number of primes less than $n$ is approximately $\\frac{n}{\\ln n}$. The number of primes less than $10^{141}$ is approximately\n$$\\pi(10^{140}) = \\frac{10^{140}}{140\\cdot \\ln 10} = 3.1\\cdot 10^{137}.$$That\u0026rsquo;s $3\\cdot 10^{57}$ times the estimated number of atoms in the universe. Looks like @primes should be able to tweet for a while.\nThe largest known prime is $2^{57,885,161} − 1$ and has 17,425,170 digits.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2013-11-08T00:00:00Z","permalink":"https://tdhopper.com/blog/tweeting-primes/","title":"Tweeting Primes"},{"content":"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 Markdown.\nI intended to write a blog post about using Sublime Text as a tool for writing Markdown. However, the inimitable Federico Viticci, of macstories.net, has already written that post, so I will simply refer you there.\n","date":"2013-10-19T00:00:00Z","permalink":"https://tdhopper.com/blog/sublime-text-and-markdown/","title":"Sublime Text and Markdown"},{"content":"Ralph Keyes, The Height of Your Life:\nI\u0026rsquo;ve heard this sort of thing repeatedly from tall men. It\u0026rsquo;s not the incessant commentary about their height that is so annoying, it\u0026rsquo;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\u0026rsquo;s commentary to which they\u0026rsquo;ve been sentenced comes mostly from those with least to say.\n","date":"2013-10-02T00:00:00Z","permalink":"https://tdhopper.com/blog/the-incessant-commentary-on-being-tall/","title":"The Incessant Commentary on Being Tall"},{"content":"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 MyFitnessPal to track my food intake and exercise, and I run a net calorie deficit every day. Thankfully this seems to be working.\nIn May, I bought a Withings WS-30 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.\nEvery morning, I step on the scale and my weight is automatically broadcast to MyFitnessPal, Monitor Your Weight on iOS, and a text file in my Dropbox folder (via IFTTT and Withings\u0026rsquo; 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 Day One each month.\nAn 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.\nI 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.1\nThe 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 not normally distributed.) Fortunately the mean of the differences is actually about -0.24 (pounds/day), and my progress is downward.\nIn total, I lost weight on 48 days, gained on 33, and stayed the same on 4% of the days. That means I\u0026rsquo;ve steadly lost weight while only moving down on 56% of days. I guess I don\u0026rsquo;t need to be depressed every time my weight jumps up slightly\u0026hellip;.\nThis isn\u0026rsquo;t 100% true. I\u0026rsquo;m hiding the fact that I missed weighing-in on some days.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2013-07-26T00:00:00Z","permalink":"https://tdhopper.com/blog/noisy-series-and-body-weight/","title":"Noisy Series and Body Weight"},{"content":"I have started to realize that Monte Carlo methods 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\u0026rsquo;d like to get a better grasp on Monte Carlo methods (especially MCMC and simulation).\nI asked on Twitter if anyone had a recommended reference that was readable and practical. Despite my love of measure theory, what I want is Monte Carlo Methods for the Very Applied Mathematician, not a theoretical text.\nI got several recommendations. I\u0026rsquo;m not sure that any are exactly what I\u0026rsquo;m looking for, but I am certainly going to look deeper into them. Interestingly, they are all Springer books.\nSeveral people recommended Glasserman\u0026rsquo;s Monte Carlo Methods in Financial Engineering. I don\u0026rsquo;t work in the financial sector, so it\u0026rsquo;s hard for me to evaluate the table of contents to tell how well it generalizes.\nSomeone else recommended both Explorations in Monte Carlo Methods and Handbook of Markov Chain Monte Carlo for two levels of MCMC.\nFinally, I got a recommendation for Introducing Monte Carlo Methods with R. This might be closest to what I\u0026rsquo;m looking for. It appears to cover a breadth of topics, and it includes lots of code.\n","date":"2013-07-23T00:00:00Z","permalink":"https://tdhopper.com/blog/guide-to-monte-carlo-methods/","title":"Guide to Monte Carlo Methods?"},{"content":"I had the honor of being a technical reviewer for John Myles White\u0026rsquo;s outstanding book, \u0026ldquo;Bandit Algorithms for Website Optimization: Developing, Deploying, and Debugging.\u0026rdquo; 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.\nCheck it out!\n","date":"2013-01-29T00:00:00Z","image":"https://tdhopper.com/images/mabandit.png","permalink":"https://tdhopper.com/blog/bandit-algorithms-for-website-optimization/","title":"Bandit Algorithms for Website Optimization"},{"content":"I gave a talk at PyCarolinas 2012 about using Pickle and Redis to persist data with Python. It wasn\u0026rsquo;t recorded, but you can see the IPython notebook I presented from.\n","date":"2012-10-22T00:00:00Z","image":"https://tdhopper.com/images/pickle.png","permalink":"https://tdhopper.com/blog/pickle-and-redis/","title":"Pickle and Redis"},{"content":"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.\nThe summer after my junior year, I was accepted to a math REU at Rochester Institute of Technology. The first thing my adviser Stanislaw Radziszowski asked me was whether or not I could program! I spent the whole summer programming combinatorial graph theory-related algorithms in C1.\nNow 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 admissions page for my program says nothing about programming ability, but it is implicitly assumed that programming is a skill that students have.\nMoreover, 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.\nMarc Kuo recently blogged about how operations researchers need to get with the program (pun intended). His post kicked of tons of discussion in its comments, on Google+, on Hacker News, and on OR-Exchange.\nThis discussion came at a good time for me. I\u0026rsquo;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 good code.\nTo start, I finally started using git and github for version control. I have tried several times before, but I have always found it rather confusing2. This git tutorial 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.\nSecond, I started teaching myself about unit testing. 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?3\nThird, I\u0026rsquo;m trying to write clean, object-oriented, well-commented code. My intention is to publish this code on github 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\u0026rsquo;m trying to move in that direction.\nI realized that I\u0026rsquo;ve used the word I as much as Stephen Wolfram blog post. I have no desire to toot my own horn here; I\u0026rsquo;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.\nI\u0026rsquo;m eternally indebted to my brilliant research partner Evan who taught me how to use bash, vim, and subversion, among other things.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nI feel vindicated by a recent thread on Hacker News.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nIncidentally, here\u0026rsquo;s an interesting Quora thread about testing stochastic algorithms.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2012-07-12T00:00:00Z","permalink":"https://tdhopper.com/blog/operations-research-and-computer-programming/","title":"Operations Research and Computer Programming"},{"content":"Here are my notes on the derivation of the Least Squares Policy Iteration (LSPI) algorithm. The notes are based on the original paper by Lagoudakis and Parr.\nPrevious Next \u0026nbsp; \u0026nbsp; / [pdf] View the PDF file here. ","date":"2012-06-18T00:00:00Z","image":"https://tdhopper.com/images/lspi.png","permalink":"https://tdhopper.com/blog/notes-on-the-derivation-of-least-squares-policy-iteration/","title":"Notes on the Derivation of Least Squares Policy Iteration"},{"content":"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):\nAn integral has bounds from zero to one\nYou finish the problem and think you are done\nThen your mind double checks\nThe equation is one over x And the improper integral has a different sum\nMy favorite haiku gives the formula for integration by parts:\nInt of mu dv:\nEqual to mu v minus\nInt of v d mu\nHere are some other good ones:\nDoing Integrals… Oh How I shall miss thee, Calc! Goodbye, It\u0026rsquo;s been real?\nCalculus is hard Derivative and limit ¡Yo no se hombre!\nMacLaurin Series is an infinite series Centered at zero\nCalculus is great\nI\u0026rsquo;m sure I\u0026rsquo;ll use this in life\nOr maybe I won\u0026rsquo;t\nStudying \u0026rsquo;til 5\nAnd almost sleeping thru test\nWas a real bad call\nFinding integrals\nIs used to find area\nUnderneath the curve\nCalc is fun to do\nIf you like to integrate\nx from a to b\nCalculus two, sigh\nWhy must you torment me so?\nI thought we were friends\nF of e to x\nDerived is e to the x\nLone function like it\nI like calculus\nThe feeling is mutual\nBut not all the time\nIntegrals are fun\nu-sub can be tricky… yes!\nDon\u0026rsquo;t forget plus C\nCalculus is hard\nEight A.M. is too early\nHad a good time though\nThe ratio test:\nIf the limit equals one\nUse another test\nDoes this series telescope?\nFor my sake, I truly hope\nComparisons are icky,\nIntegrals make me sickly,\nTurns out the answer is nope. :-(\nHere are a few from when I taught at UVA:\nAn integral is\nA derivative reversed\nDon’t forget constants!\nThanks to L’Hopital\nWe can use derivatives\nto find a limit!\nFind the area,\nBetween the two stated curves,\nUsing integrals.\nNatural log of Zero,\ndoes not exist, but\nLn of 1 doesn\u0026rsquo;t\nCalculus is great\nBut only if taught by\nTim At U of V.A.\n","date":"2012-05-08T00:00:00Z","image":"https://tdhopper.com/images/poems.png","permalink":"https://tdhopper.com/blog/calculus-haikus-and-limericks/","title":"Calculus Haikus and Limericks"},{"content":"I am teaching the dreaded calculus II this semester. I\u0026rsquo;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 \u0026ldquo;techniques of integration\u0026rdquo; 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 \u0026ldquo;funny little calculus text\u0026rdquo;:\nOf course, algorithms for computing many antiderivatives do exist (and are used in Maple, Mathematica, and Wolfram Alpha), but I\u0026rsquo;d be fired if I tried to take my undergrads through Symbolic Integration I: Transcendental Functions..\nInstead, 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\u0026rsquo;ll be tackling this summer. He noted that our first attempt at solving a massive problem would probably fail; they usually do.\nFortunately, I\u0026rsquo;ve been failing at solving problems, at least since taking number theory with Dan Krider in 2003. I know what Edison meant by \u0026ldquo;I have not failed. I\u0026rsquo;ve just found 10,000 ways that won\u0026rsquo;t work.\u0026rdquo; 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\u0026rsquo;d love to hear feedback from students who are learning these lessons and teachers trying to teach them.\n","date":"2012-04-13T00:00:00Z","image":"https://tdhopper.com/images/failing.png","permalink":"https://tdhopper.com/blog/teaching-students-to-fail/","title":"Teaching Students to Fail"},{"content":"Yesterday, someone on MathOverflow asked\nConsider $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?\nSomeone quickly cited 2004 paper provides an analytical result for the cases where $n=3$ and $n=4$:\nFor $n=3$ the expected value is $11/144$ and for $n=4$ it is $11/72$.\nThis 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:\nSampling 5000 cases for each returned results fairly close to the predicted average.\n","date":"2012-04-05T00:00:00Z","permalink":"https://tdhopper.com/blog/average-area-of-a-random-hull/","title":"Average Area of a Random Hull"},{"content":"Last month, Stephen Wolfram did a blog post on the Personal Analytics of his life. For years, he\u0026rsquo;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 Mathematica notebook 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 memory1. Here\u0026rsquo;s a plot of emails sent by from Gmail me over the last six years:\nI 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\u0026rsquo;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\u0026rsquo;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\u0026rsquo;s a graph of my email received over the past six years. It comes in pretty heavy from 8 AM to midnight!\nThe 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\u0026rsquo;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\u0026rsquo;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.\nHere we have a histogram of the time at which I send emails. Apparently I\u0026rsquo;m most likely to send an email just after 10 PM. I wouldn\u0026rsquo;t have guess that. Don\u0026rsquo;t expect to hear from me after midnight!\nAs a good operations researcher, I wondered if I received email according to a Poisson process. 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\u0026rsquo;s not a terrible fit!2 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.\nReinstallJava[CommandLine -\u0026gt; \u0026ldquo;java\u0026rdquo;, JVMArguments -\u0026gt; \u0026ldquo;-Xmx4024m\u0026rdquo;]\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nThat\u0026rsquo;s not a official statistical statement!\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2012-04-05T00:00:00Z","permalink":"https://tdhopper.com/blog/my-email-analytics/","title":"My Email Analytics"},{"content":"Over the past 18 months, I\u0026rsquo;ve been slowly learning some machine learning. One thing I\u0026rsquo;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 simply solving a nonconvex optimization problem. Clustering often takes the form of expectation-maximization. I\u0026rsquo;m currently learning Bayesian network structure learning which is an extremely difficult combinatorial optimization problem.\nYesterday 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 theorems apply, and we use many of the same algorithms; we just apply them in different ways. I got helpful feedback from the nerds that follow me. Jeff Linderoth pointed to the recent book Optimization for Machine Learning 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\u0026rsquo;s a two-hour lecture by Wright on the same topic that I look forward to watching. Jeff also pointed to the work of his colleague Ben Recht who\u0026rsquo;s looking at the optimization problems in ML from a theoretical standpoint. Paul Kerl linked to Jorge Nocedal\u0026rsquo;s work at Northwestern. Nocedal and Recht seem to have feet in both worlds. John Myles White noted that the legendary optimizer Stephen Boyd came to the New York Academy of Science\u0026rsquo;s Machine Learning event last year. I also came across a 2006 paper on The Interplay of Optimization and Machine Learning Research. The authors note some difference between an OR and ML perspective on optimization:\nWe 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 problems1.\nA bigger question might be where optimization lies as a discipline. Since I\u0026rsquo;ve been in OR, I\u0026rsquo;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 solving 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.\nI haven\u0026rsquo;t read the whole paper (that\u0026rsquo;s from the abstract), but I\u0026rsquo;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.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2012-04-03T00:00:00Z","permalink":"https://tdhopper.com/blog/operations-research-machine-learning-and-optimization/","title":"Operations Research, Machine Learning, and Optimization"},{"content":"Stephen Wolfram, of Wolfram Research and Mathematica fame, did a Q\u0026amp;A (i.e. AMA) on Reddit today. I just enjoyed reading through his answers. A few interesting answers stood out to me.\nSomeone ask Wolfram\u0026rsquo;s opinion on P=NP. He thinks it\u0026rsquo;s undecidable1.\nSome smart aleck threw the Riemann hypothesis at him. Interestingly, Wolfram also suspects this is undecidable2.\nOne questioner asked about open sourcing old versions of Mathematica. Wolfram responded very winsomely, in my view. I didn\u0026rsquo;t know that they\u0026rsquo;ve thought about making the core language more freely available. I\u0026rsquo;d like to see that.\nHis most interesting answer is his opinion on Matlab. He argues that Matlab has remained matrix-centric when so much of contemporary mathematics goes beyond that. \u0026ldquo;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.\u0026rdquo; However, Wolfram believes that Mathematica isn\u0026rsquo;t outdone by Maple in the realm of matrices.\nWolfram relays that a major goal of Mathematica is \u0026ldquo;to make a single coherent system in which one can work, and in which everything fits nicely together.\u0026rdquo; I argued that that\u0026rsquo;s one thing they\u0026rsquo;ve done quite well.\nI 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.\nSee the Wikipedia page on undecidability for more.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nBoth 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/.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2012-03-05T00:00:00Z","permalink":"https://tdhopper.com/blog/stephen-wolframs-ama/","title":"Stephen Wolfram's AMA"},{"content":"Although I was a computer science minor, I\u0026rsquo;d never heard of statistical machine learning until after college. Now I dabble in machine learning on the side. In the long run, I\u0026rsquo;m interested in studying the intersection of operations research and learning, i.e. intelligent optimization systems. Two years ago, I stumbled across Peter Norvig\u0026rsquo;s essay How to Write a Spelling Corrector. Google is a notoriously good spelling corrector; just try googling \u0026ldquo;spellign.\u0026rdquo; I find that Google knows what word I\u0026rsquo;m trying to spell even when an application\u0026rsquo;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\u0026rsquo;t recall having seen the two mixed so elegantly. I\u0026rsquo;m pleased to now know that Norvig was only scratching the surface! This week, I\u0026rsquo;ve been watching Sebastian Thurn\u0026rsquo;s lectures on Kalman Filters for AI. Probabilistic techniques for filtering noisy data can be used, for example, in a robot keeping track of objects moving around it.\n","date":"2012-03-02T00:00:00Z","permalink":"https://tdhopper.com/blog/the-spelling-corrector-that-got-me-interested-in-machine-learning/","title":"The Spelling Corrector that Got Me Interested in Machine Learning"},{"content":"As an undergraduate, our math department used Wolfram Research\u0026rsquo;s Mathematica 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 An Introduction to Programming with Mathematica.\nSeven 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.\nI 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.\nOne other note to the Redditors and cynics (but I repeat myself): I\u0026rsquo;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.\n1. Powerful Symbolic Computations 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.\nOne 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.\nThese 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\nOf 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.)\nThe 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.\n2. Functional Programming Like R, Mathematica allows procedural programming.\nHowever, again like R, Mathematica is really built for functional programming. Wolfram has a great tutorial on the topic, but let me share a brief example from my own use. 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:\nAny 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:\nThe lateness of each job is defined by the completion time minus the due date:\nTo 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:\n(The # and \u0026amp; are part of Mathematica’s notation for pure functions.) Or, more succinctly,\nIn 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:\nsince % returns the last line evaluated.\nThis 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.\nOh, and if you want to parallelize these operations: not a problem.\n3. Optimization 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 claim to be able to handle large problems. Mathematica’s ExampleData function gives easy access to many data sets, including NetLib.org’s LP problems. Mathematica could solve this problem with 6072 rows and 12230 columns in 60 seconds on my 11-inch Macbook Air.\nThe built-in solver certainly isn’t as robust as CPLEX or other commercial solver, but it does, at least, provide several solution methods.\nI most often find myself using the Minimize and Maximize functions with explicit constraints:\nIn the future, I hope to do a post on using Mathematica as a pseudo-modeling language. You can see the documentation for a number of other optimization related functions.\nRecently, I’ve been working with stochastic dynamic programming problems (i.e. Markov decision processes). Mathematica offers the easiest memoization I’ve ever seen in any language. Combined with functional aspects, I can solve dynamic programs with relatively little code.\n4. Graphics 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.\nLast 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.\nWorking 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 file. All of this without having to open another program or import any graphics packages.\n5. Documentation 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 available online. Not only does the documentation for every function (usually including bullet points with Basic Examples, Scope, Generalizations \u0026amp; Extensions, Applications, Properties \u0026amp; 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.\n6. Naming Conventions If Mathematica wins one debate hands down, its naming conventions. By their own standards, “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.\nStephen Wolfram wrote a blog post 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.\nI 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.\nI think this has paid off.\nSome 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.\n7. Interactivity 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:\nA 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.\nOr Table for that matter:\n8. Continual Development 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 other things.\nMathematica 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:\nMathematica 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.\nThe list of things new in version 8 goes on.\n9. Comprehensiveness 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. Abstract algebra? Check. Graph theory? Statistics? Visualization? Optimization? String Processing? Differential equations? Computational chemistry? Calculus? Check. Check. Check.\nThe comprehensiveness of Mathematica\u0026rsquo;s functionality along with dynamic typing and functional programming allows me to write code to do complicated tasks very quickly. I love it.\nConclusion No doubt, Mathematica has its limitations1: Worlds worst undo. Not object-oriented. Closed platform. Expensive. No autosave. No data frame structure.\nHowever, 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.\nIn 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.)\nUsing Mathematica for little things like this. It allows me to spend my time and brain power on the things that computers can’t handle.\nI love Mathematica. And maybe you will too.\n(I wrote this post in Mathematica. You can check out the notebook here.)\nI might follow with a post on that very point\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2012-02-10T00:00:00Z","permalink":"https://tdhopper.com/blog/mathematica-a-love-story/","title":"Mathematica: A Love Story"},{"content":"I assume most people who are nerdy enough to read this blog are nerdy enough to know about the $\\mathcal{P}$ vs $\\mathcal{NP}$ problem1. 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 problems, and many operations researchers spend their careers trying to solve hard problems. Last semester, I came across Laurence Wolsey\u0026rsquo;s beautiful description of how various people might view this problem. (Of course, the inimitable Randall Munroe has offered a similar look at the issue.)\nA pessimist 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 mathematician (optimist) might set out to become famous by proving that $\\mathcal{P=NP}$. A mathematician (pessimist) might set out to become famous by proving that $\\mathcal{P\\neq NP}$. A mathematician (thoughtful) might decide to ask a different question: Can I find an algorithm that is guaranteed to find a solution \u0026ldquo;close to optimal\u0026rdquo; in polynomial time in all cases2. A probabilist (thoughtful) 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 \u0026ldquo;close to optimal\u0026rdquo; solution with high probability? An engineer would start looking for a heuristic algorithm that produces practically usable solutions. Your boss might say: I don\u0026rsquo;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\u0026rsquo; order is out of the door by 4 P.M. A struggling professor 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 found3.\nIf that\u0026rsquo;s not the case, you might check out this page: http://simple.wikipedia.org/wiki/P_versus_NP.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nSee http://en.wikipedia.org/wiki/Polynomial_approximation_scheme.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nFrom Integer Programming, Page 87.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2012-02-02T00:00:00Z","permalink":"https://tdhopper.com/blog/mathematicians-engineers-and-businessmen-on-npc-problems/","title":"Mathematicians, Engineers, and Businessmen on NPC Problems"},{"content":"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\u0026rsquo;t consistently beat the market because his knowledge about the markets is no better than anyone else\u0026rsquo;s. An accompanying \u0026ldquo;joke\u0026rdquo; 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.\nAs I\u0026rsquo;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\u0026rsquo;t always be right. I don\u0026rsquo;t know if markets are efficient or not; I\u0026rsquo;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\u0026rsquo;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.\n","date":"2012-01-27T00:00:00Z","image":"https://tdhopper.com/images/money.png","permalink":"https://tdhopper.com/blog/the-efficient-research-hypothesis/","title":"The Efficient Research Hypothesis"},{"content":"In 1953, the eminent mathematician John Von Neumann (as they say, \u0026ldquo;Most mathematicians prove what they can, von Neumann proves what he wants\u0026rdquo;) 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 \u0026ldquo;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.\u0026rdquo;1\nHowever, due to \u0026ldquo;weather, conditions in the ports of call, etc\u0026hellip; the comings and goings of the tankers are described in statistical terms only.\u0026rdquo; 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, \u0026ldquo;Despite having most of the necessary data\u0026hellip; the problem could not be solved with analytical calculations.\u0026rdquo;2\nIn 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 \u0026ldquo;Monto Carlo.\u0026rdquo; Von Neumann suggested that Standard Oil could build a probabilistic 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.\nThe 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\u0026hellip;. Program calculations which will develop the further history of this tanker, always deriving those quantities which depend on chance\u0026hellip;, with the use of suitable tables of random numbers.\nOf 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:\nIt 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).\nThe 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\u0026rsquo;s punch card method would quickly be replaced by computer programs and pseudo-random number generation algorithms. This early application of Monte Carlo methods to operations problems is one of many.\nI 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 deterministic scheduling problems based on Monte Carlo simulations of the future. The system somehow combines these various schedules to make a decision. There are many applications of Monte Carlo methods in other fields as well.\nJohn 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.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nhttp://en.wikipedia.org/wiki/Monte_Carlo_method#History\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2012-01-25T00:00:00Z","image":"https://tdhopper.com/images/tanker.png","permalink":"https://tdhopper.com/blog/from-nuclear-weapons-to-operations-research/","title":"From Nuclear Weapons to Operations Research"},{"content":"My friend John Cook asked me an interesting question recently:\nIf you had a room full of people with a graduate degree in [operations research], what things would nearly everyone in the room know?\nOperations 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: linear programming, nonlinear programming, integer programming, dynamic programming, stochastic programming, stochastic models, queueing theory, game theory, and simulation. The course requirements 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)?\n","date":"2012-01-16T00:00:00Z","permalink":"https://tdhopper.com/blog/what-do-all-or-folks-know/","title":"What do all operations researchers know?"},{"content":"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 calories1. 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 $$172\\cdot C+1732\\cdot Q\\geq 2000.$$ 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: $$0.839\\cdot C+25\\cdot Q\\leq 40.$$ 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: $$19\\cdot C+15\\cdot Q\\geq 60.$$ 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.\nA pound of carrots and a pound of oatmeal both cost about $0.50. So, we want to minimize 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 \u0026ldquo;linear program.\u0026rdquo;2\n$$ \\begin{align}\\text{minimize }\\\\;\\\\; \u0026 0.5\\cdot C+0.5\\cdot Q\\\\\\\\ \\text{subject to }\\\\;\\\\; \u0026172\\cdot C+1742\\cdot Q\\geq 2000\\\\\\\\ \u00260.839\\cdot C+25\\cdot Q\\leq 40\\\\\\\\ \u002619\\cdot C+15\\cdot Q\\geq 60\\\\\\\\ \u0026C\\geq 0, \\\\;Q\\geq 0\\end{align} $$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\u0026rsquo;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!\nOperations researchers use this sort of linear programming mathematical model to solve all kinds of problems. While I don\u0026rsquo;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 Stigler\u0026rsquo;s Diet problem.\nSee http://www.wolframalpha.com/input/?i=pound+of+carrots%2C+pound+of+oatmeal\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nProgram doesn\u0026rsquo;t refer to computer programming. It actually goes back to an older use of the word related to planning something out.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2012-01-09T00:00:00Z","permalink":"https://tdhopper.com/blog/carrots-oatmeal-operations-research/","title":"Carrots, Oatmeal, Operations Research"},{"content":"👋 Hey, I\u0026rsquo;m Tim Hopper! I\u0026rsquo;m an experienced machine learning platform engineer and Python developer. You can check out my resume at resume.tdhopper.com.\nFor over 10 years, I\u0026rsquo;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).\nI\u0026rsquo;m also excited about developer productivity, especially in Python development. I am the author of an ebook on Python developer tooling and like to help teams use Python more effectively.\n✍🏻 Writings:\ntdhopper.com has been a place for my thoughts and writings since grad school. If you\u0026rsquo;re new here, start with these:\nSome Reflections on Being Turned Down for a Lot of Data Science Jobs How I Became a Data Scientist Despite Having Been a Math Major and A Subjective and Anecdotal FAQ on Becoming a Data Scientist Goodnight Zoom Entropy of a Discrete Probability Distribution 🖥️ Personal Projects:\nI have an occasional podcast in which I talk to friends about things they\u0026rsquo;re interested in. You can find it in your podcast directory or at podcast.tdhopper.com.\nYears ago, I created Should I Get a Phd? where I interviewed nine friends about whether a young, bright student should consider pursuing a PhD. This is the resource I wish I\u0026rsquo;d had before starting a PhD program, and it\u0026rsquo;s been useful to many.\nPython Plotting for Exploratory Data Analysis is a Rosetta Stone for Python plotting libraries, and it also compares them to the GOAT of plotting libraries: ggplot.\nI created Notes on Dirichlet Processes after working on a DARPA-funded open source project for developing Bayesian nonparametric models in Python. I did a lot of work to understand Bayesian nonparametrics and derive the Gibbs sampler for Hierarchical Dirichlet Processes. Notes on Dirichlet Processes shares what I learned for the benefit of others.\nI enjoy wildlife and nature photography in my free time. dothopper photo is my gallery.\nFree Disk Space is a little site I maintain with commands for freeing up disk space on your computer.\n⌨️ Open Source:\nI love to contribute to open source as I\u0026rsquo;m able. I\u0026rsquo;ve contributed to libraries like cpython, datamicroscopes, Streamparse, Conda, lda, and Pandas.\n👨🏻‍💻 Social Media:\nYou can find me on Twitter and LinkedIn.\n🗣️ Talks:\nI\u0026rsquo;ve been speaking at conferences and meetups for many years. I keep a list of my recorded talks here. If you\u0026rsquo;d like to get a taste of my talks, start with Five semesters of linear algebra and all I do is solve Python dependency problems or Challenges in Applying Machine Learning to Cybersecurity.\n","date":"2010-01-01T00:00:00Z","permalink":"https://tdhopper.com/blog/welcome/","title":"👋 Hey, I'm Tim Hopper!"},{"content":"Last summer, I had the privilege of serving as a student researcher in the Rochester Institute of Technology\u0026rsquo;s Research Experience for Undergraduates alongside Dr. Stanislaw Radziszowski.\nOur research focused on computational combinatorics, specifically Ramsey Numbers. 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\u0026rsquo;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.\nThis week, my research partner Evan Heidtmann and I presented our research at the AMS-MAA-SIAM Special Session on Research in Mathematics by Undergraduates in San Diego. Here is our abstract:\nWe 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\\).\n","date":"2008-01-07T21:00:00Z","image":"https://tdhopper.com/images/graph.png","permalink":"https://tdhopper.com/blog/on-ramsey-numbers-for-quadrilaterals-in-3-and-4-colors./","title":"On Ramsey Numbers for Quadrilaterals in 3 and 4 Colors."}]