Remember back in math class when you'd take a test?
It wasn't enough to just write down the answer.
What's the limit of that infinite summation?
Pi over 2.
Yes, but how did you get to that number?
Some problems in programming are just like this.
We want to keep track of the computations done
and only add more steps to the results.
Heck, that's basically the entire premise of functional programming.
On this episode, you'll meet Christopher Ariza,
who created a project called Static Frame.
Think of it like pandas in NumPy,
but it never changes the computations it's already performed.
It just adds to them.
This is Talk Python to Me, episode 204, recorded February 7th, 2019.
Welcome to Talk Python to Me, a weekly podcast on Python,
the language, the libraries, the ecosystem, and the personalities.
This is your host, Michael Kennedy.
Follow me on Twitter, where I'm @mkennedy.
Keep up with the show and listen to past episodes at talkpython.fm,
and follow the show on Twitter via at Talk Python.
Chris, welcome to Talk Python.
Hi, Michael. Glad to be on the show.
Yeah, it's great to have you on the show.
You have a really interesting library that you've been working on,
and it's an interesting sort of data-safe take on the whole Pandas API,
which I think is going to be a lot of fun for all the data scientists
and other people working with pandas out there.
Great. I look forward to talking about it.
Absolutely. But before we do, let's get started on your story.
How did you get into programming in Python?
Sure. I started programming in Python in the year 2000.
I was a graduate student at NYU, and I was doing a lot of work in computer music
and algorithmic composition specifically.
And I was looking for a way to extend my capacity, you know,
with these very high-level synthesis languages that I was using.
So I decided to learn programming in more depth.
And I was a graduate student there, so I took a course in C programming.
And I had a graduate advisor who was supposed to oversee my work at the time.
And I had this great idea to build this system in C.
And so I sat with this advisor, and I was like,
I really want to do this thing in C.
And he said to me,
well, if you wanted to build a car, would you start by building every screw?
And I said, no.
And so he said, use Python.
And I was like, what's Python?
He said, it's this new great language.
Go try it out.
And so I walked over to Barnes & Noble in Astor Place in New York City,
back when there were bookstores at Barnes & Noble.
Yeah, they used to have a great computer section, right?
You could go and browse and see what was interesting.
That was a way you learned about stuff, and not so much these days, right?
Yeah, exactly.
Yeah, so you went over and got this book, yeah.
I got Learning Python, actually.
There was a version of Learning Python out in the year 2000,
and I picked it up and started Learning Python,
and started building a system I called Athena CL.
This was a tool for algorithmic composition,
closely tied to a synthesis language called C-sound,
and did a bunch of work in that, culminating in my dissertation.
Wow, that's really cool.
So your dissertation is music and algorithmic composition, basically?
That's right.
Okay, so cool.
So when you started working on this library,
and you were doing the programming around it,
was this to have the computer generate music,
or try to use the computer to understand music?
What was the goal there?
I was studying, I was getting a PhD in music composition and theory,
and so I was using these synthesis languages
that took text-based input of event data.
So C-sound is an ancient, well, it's not ancient,
but it's a very old synthesis language.
In computer terms.
Yeah, well, it actually comes from an ancient lineage.
It comes from the very first synthesis languages
that Max Matthews invented in Bell Labs in the 60s.
Wow.
Called Music 1 and Music 2 and up to Music 5.
Those happened at Bell Labs in the 60s and 70s.
And C-sound is the modern version of that.
And it takes basically a text input file
defining your events and your parameters.
And it became quite clear that you could do really cool things
if you could use code to generate these text input files.
And of course, it's quite straightforward to do in Python.
So I wasn't using Python to do synthesis.
I was using Python to generate control data
that I would then feed into C-sound.
Okay.
How interesting.
Have you seen some of the programmatic generated music
that people are doing with Python lately?
I'm not sure.
Is there something specific you're thinking of?
Yeah.
There's been a couple of presentations around...
Gosh, I wish I could remember the library.
But there's a couple of libraries that you can use
to basically live in the REPL program out songs
and interactions.
And yeah, it's pretty wild.
So maybe I'll find a link and throw it into the show notes
because I can't remember the name.
But there's been a few really good conference presentations
that are basically live musical performances
done by programming Python.
I think I saw one at PyCon last year.
I believe that one worked on top of the SuperCollider language.
So SuperCollider is a synthesis language
that's much more modern than C-sound
that has a synthesis server independent from the language.
And it's possible to use other languages to control the server.
So I haven't been following it too closely,
but I suspect that some people are using Python
to control the SuperCollider server,
which is a great idea.
Yeah.
It's definitely interesting to see.
I'll throw in the video for people
because if you haven't seen it, it's pretty creative.
All right.
So today you're not doing that much music theory, right?
You're working in a different discipline.
Tell us about what you do day to day.
I worked in academia for a while at a few places
and was continuing to do my work in algorithmic composition,
generative music, that sort of work.
But, you know, decided to look for something else
and found a job at a firm called Research Affiliates.
We're a finance firm.
We define and build strategies for investment
that we license to many parties around the world.
That's cool.
Is this like the so-called algorithmic trading type of stuff?
Essentially, our strategies are,
many of our strategies are what we call passive investment vehicles,
which means that there is an algorithm,
a specific procedure that's used to generate
the portfolio constituents and the weights.
Our strategies are fairly slow moving.
It's not high frequency trading.
It's not anything like that.
Yeah, you're not looking for sub-millisecond advantages.
You're looking for just applying algorithms to long-term investing.
That's right.
All right.
So if like Warren Buffett were a programmer,
he might be doing stuff like that.
Yeah, that's right.
I guess.
It's funny.
All right.
Cool.
So that brings us sort of full circle back to this idea of pandas
and your variation, your take on a slightly different library
that is pandas-like.
So pandas also comes from the whole finance space, right?
Like it's very popular in data science,
but of course, you know, it originated out of finance,
which is maybe one part of data science, I guess, right?
Maybe.
I see data science as,
and there's certainly discussions and presentations on this issue,
I see it as, in some ways, more speculative research into data.
As opposed to using these tools for the systematic application of an algorithm or a procedure.
Right.
So what you're thinking is more that like what you guys do day to day is more programming,
using these tools that maybe originated out of data science,
but you're deploying production systems that are running and doing stuff,
not so much coming up with graphs and inferring stuff with Jupyter.
Yeah, exactly.
Right.
Okay.
Yeah.
But at my firm, we have finance researchers who that's what they do.
They comb over data and study research and, you know, try to make observations from the data.
You know, that is closer to data science.
But by the time strategies come to us, they are well-defined.
So we are, you know, implementing the production strategy
and don't really have that sort of discovery exploration need.
Okay.
So let's maybe start from the beginning.
You told me the idea of building these strategies and you've created this library called Static Frame,
which is really interesting.
We're going to talk a lot about it.
But you started with Pandas, right?
You're like, let's use Pandas and other Python libraries to solve this problem
before you decided I'm going to replace Pandas with my own library, right?
Yeah, that's right.
Maybe talk about that journey.
Sure, sure.
Well, actually, when I started at Research Affiliates in the year 2012,
Pandas was still quite young at that point.
And my predecessor had created his own library to model data transformations
and basically storing data in a table and then efficiently being able to add new data to that table by column addition,
kind of an Excel-like data model, but implemented in Python.
And his implementation was very straightforward.
It was simply a dictionary that held rows where the rows themselves were a dictionary.
So kind of like a JSON representation of a table, if you will, a dictionary for the rows and then a dictionary for each row.
And there, of course, we weren't using NumPy as the back end, but it was actually reasonably efficient.
And we still use it in some places.
After about in 2013, we started to, I spent some time looking at Pandas and started to use it because, of course,
the underlying performance in large part due to NumPy and being able to use the vector operations of NumPy
gave a significant advantage over using our own table model.
Yeah.
There's such an advantage to using things like NumPy where you hand a little data off to the C layer and that layer can do all the computation.
There was a really interesting analogy or observation made by Alessandro Molina from the last couple episodes ago.
And he was talking about Python is one of these languages that is a little bit counterintuitive.
Like in C, if you want something to go really fast, you might make it go really fast by writing and implementing the details in C or some other language.
And so the more you can kind of control that, the more precise you can be.
Where Python gets faster, the more high level you try to treat it.
So if you tried to implement those algorithms in pure Python, they'd be slow.
But if you just call like a high level NumPy function, boom, it's fast, right?
And so it's like this sort of inverted understanding of like where the performance is in this language compared to others.
Yeah.
And, you know, I have to admit, I had looked at NumPy before.
I'd been using Python for a long time.
But the context of using it through pandas as a wrapper to NumPy really started me thinking, oh, when I want to scale a vector, I just multiply by the value.
And I scale the whole vector.
And this happens amazingly fast.
And there's no loops.
And you begin to take on that mindset that wherever you have a loop, you're doing something wrong.
You know, you want all your loops to be in the NumPy layer.
And it takes a bit of conceptual work to get there.
Yeah, that's such an interesting observation.
I definitely think that that's true.
And you want to let it do that for you.
But to think, oh, there's a loop.
Where are we missing the opportunity to make this work the way pandas and NumPy should?
Yeah, absolutely.
I mean, yeah, that's exactly in our team code reviews.
That's exactly one of the things that we sort of look out for.
We see, you know, I see some pandas code that somebody wrote.
And I see a couple loops or a loop in a loop.
And I'm like, oh, there's got to be a better way.
Quote Raymond.
Yeah, absolutely.
There's got to be a better way.
That's cool.
All right.
This transition over to NumPy and pandas, this was pretty successful, right?
Like you guys were able to replace that library and do more of your work in these libraries and these packages?
Yeah, that's right.
We didn't entirely replace it because the old table model worked reasonably well in a few cases.
But in implementing some new strategies, some new tools, I started working with pandas.
And it's funny because although you have a bunch of utilities on pandas, it's hard to figure out what to do with it or how to use it, really, I think.
But I already had a precedent.
The precedent I had from our old library was that you start with data in a table.
You load up initial data, initial observations about companies, for example.
And you may maybe have 40 columns on a table of 10,000 companies.
And that's your initial inputs.
And then you add new data by doing operations, applying functions on those rows or previous columns and add new columns.
And the previous library we used was actually very much aligned to that workflow.
So moving to pandas was actually quite smooth because pandas very easily supports growing a data frame by adding columns.
And those column additions can easily be performed by doing operations on columns that are already on the table or doing function application to rows already in the table.
That makes a lot of sense.
We discussed this a little bit before.
You talked then about how it was really important as your data flows down the pipeline that is doing all the calculations and eventually comes to a decision of invest in this, not in that, or this much in that area to keep a history and keep track of what's happening.
Right?
Yeah, exactly.
That paradigm was established before we ever moved to pandas.
And it was a large part of the approach and ethos of my company and how we do our work.
Our strategies are not black boxes.
We don't use esoteric machine learning to discover results.
We use very explicit approaches that we want to be transparent.
And we do human quality control on everything that we release.
So it's my obligation to expose as much of the internal calculations as possible.
A lot of the intermediates, values, groupings, labels, everything that is necessary for a human to understand.
The calculation we try to expose in our final output.
So the table becomes initial data first as 20 or 40 or more columns as your initial data.
And then numerous columns that are intermediate calculations, intermediate results, reducing the opportunity set through screens and some other processes.
And then finally getting to the actual result, which in our case is weights and constituents.
Yeah.
It sounds very inspired by what you almost might do in Excel or Google Sheets.
Right?
You have your data and then you create a formula here.
And then that female formula is based on that previous one.
But you would never go and like replace the original data and change it with a formula in like some weird iterative way.
It's always just kind of like to the right and down.
That would be disciplined use of Excel.
Unfortunately, there's no discipline inherently in Excel.
So you see all sorts of things.
Yeah, that's true.
That's true.
But, you know, I guess inspired at least by the proper use.
So that's pretty cool.
So you did all this in Pandas and that was working really well.
Why create a new library?
Like what were the pain points or what were you like, you know, if we redesign this to be Panda-like but not Pandas, what could you gain?
Right.
The initial inspiration was, you know, recognizing this workflow that we had where we would start with initial data and some columns and then add columns as we go.
That initial workflow, we found that it worked really good and it was relatively safe as long as you followed this sort of grow-only paradigm where the table only gets bigger.
Yeah, exactly.
Exactly.
Yeah.
So that's the discipline that we were doing implicitly.
But we started to speculate, well, you know, it would be really nice if we could actually enforce this grow-only paradigm and in doing so remove a lot of opportunities for error.
Now, by convention, we would never mid-process go and update in place a value we had already used.
But we were certainly sensitive to that danger and in particular for teaching our paradigm to new members of our team and the rest.
We had this strong desire, oh, it would be so great if we could sort of enforce in some way this grow-only paradigm.
And that led naturally to thinking, well, what if the frame data itself could be immutable?
More than just enforcing a grow-only paradigm, what if you had immutable frames?
There's places where we open up a – we use a table as a reference data set and we might bring that in as a data frame.
So I might bring in FX rates, currency conversion rates as a series indexed by currency code and the currency conversion value.
And that's a reference value that I'm using in many, many, many places.
And the opportunity for error if any of those values gets mutated is significant.
So we kept on coming back to this.
Like, it would be so great if we could freeze a series or freeze a table like we have frozen set, for example, and treat it as a immutable collection.
Yeah.
And, of course, it completely simplifies the whole debugging and validation, right?
Because you no longer have to look for these weird references where somebody still has a pointer to the data frame and they call a function that changes the values or, you know, does some other odd thing where they're off by a column index or something.
And, you know, it seems like debugging that would be really hard.
And, of course, making financial decisions on it might be really bad.
Yeah, that's right.
It reduces what I often say is it reduces the opportunity for error.
There's many ways that you can things can go wrong and you can get very confusing, unexpected results by mutating your inputs and your values as you go.
Yeah.
There's the safety side of things.
And that makes perfect sense.
That's probably primary.
Another thing that immutable data really opens up, I don't know if this matters at all to you guys, but anytime you have immutable data, you start to have incredible opportunities for parallelism, right?
Like if you're sharing it, you don't have to worry about, oh, I got to lock on this and make sure that's not changed.
You just riff on it because it's immutable.
It's not going to change.
Yeah, that's a really interesting potential that I haven't really explored.
The one way I have explored it with Static Frame is that our function application iterators expose an interface to multiprocessing or multithreading function application to columns and rows.
So I've experimented with a little bit, but there's definitely more opportunities to look out for that.
Yeah, it sounds like for sure.
And maybe you could even mix in some Cython in there so it releases the GIL for the threaded side of stories.
And it just seems like there's a lot of cool possibilities to dig into that.
Is performance at all something you care about?
Or is it like, it takes two minutes and we run it once a day or once a week, so it's fine?
Yeah, definitely.
Performance is a very significant concern.
And as I was doing this, as I started working on this in May of 2017, and I started very small with this speculation.
I wasn't sure if I could do this in native Python.
That was the thing is that we, for years prior, me and my team who shared these convictions and these goals, speculated on something like this.
And I always thought I was going to have to implement it in C.
It's time to build the screws and the nuts and everything.
Exactly, exactly.
Yeah.
So I was going to have to implement this in C.
And maybe I'd done some work in C++.
So, you know, I was like, maybe I can implement this in C++ and build off the STL vectors.
And then I realized, oh, man, I'm re-implementing NumPy.
I don't want to do that.
And it was after a PyCon, I think it was two years ago, yeah, because it was in May of 2017, something that the PyCon triggered for me that, you know, why don't I just try it in native Python?
And if I've hit bottlenecks, I can use Cython, but I should just see what I can do and use NumPy and just use Python.
And I set out on that goal, and I found that performance is very good.
I mean, I can get in for many operations I can do as well or better than Pandas.
Some operations I'm slower.
Some operations I'm better or significantly better.
The aggregate performance is very hard to measure.
It's very dependent on use cases.
There's some things that are definitely slower than Pandas.
But at this point, it's just pure Python, pure NumPy.
We haven't done anything in Cython or C extensions or Numba or anything like that.
This portion of Talk Python to me is brought to you by Linode.
Are you looking for hosting that's fast, simple, and incredibly affordable?
Well, look past that bookstore and check out Linode at talkpython.fm/Linode.
That's L-I-N-O-D-E.
Planes start at just $5 a month for a dedicated server with a gig of RAM.
They have 10 data centers across the globe.
So no matter where you are or where your users are, there's a data center for you.
Whether you want to run a Python web app, host a private Git server, or just a file server,
you'll get native SSDs on all the machines, a newly upgraded 200 gigabit network, 24-7 friendly
support, even on holidays, and a seven-day money-back guarantee.
Need a little help with your infrastructure?
They even offer professional services to help you with architecture, migrations, and more.
Do you want a dedicated server for free for the next four months?
Just visit talkpython.fm/Linode.
So you're getting great performance out of it already, and then there's all these low-hanging
fruit opportunities if needed.
Yeah, that's right.
Yeah.
So it's interesting you talk about the performance and could I do it this way?
And I think people, programmers, are really bad at judging what's going to be fast and what's
going to be slow.
You look at some code, you're like, oh, this is definitely the problem.
Maybe it's slower, but it's sub-millisecond and who cares?
Or it's actually not even that part.
It's something totally different.
Did you do profiling and stuff like that to really try to dial that in, or did it just work
out?
Early on, I started benchmarking against pandas for certain operations.
And so I don't think of my...
It's actually a huge debt to pandas that they've provided this great framework that does so much
and really sets the foundation.
Of course, it's descended from R.
So pandas inherited a bunch of things from R in terms of the concept of the data frame.
And I think compared to what I know of the R model, they refined the interface and unified
it in quite a nice way.
And in doing so, they've really defined a set of expectations for using libraries like this.
One example is the drop NA method on a series or frame, like the idea that given a series
or frame, there should be an easy way to remove missing values.
We have to do this kind of thing all the time.
So with that model in mind, I could start to implement those things and test them.
And the performance metric that is relevant to me is my ratio to pandas.
So that's what I know.
Like I know for this operation, oh, I'm 0.6.
I'm faster than pandas.
Or for this operation, I'm 10 times slower than pandas.
And I do it at this very granular level for one-to-one comparisons.
That's a really interesting metric to think about.
But I guess it makes sense because you're like, I would like to have this other model, this
other data model, this other programming model that's data frame-like that has the safety
immutability thing.
It used to be pandas.
Long as I don't wreck the performance too much.
And if there's a benefit, then hooray.
Like, we're good.
Yep.
That's exactly right.
Yeah, that's a cool way to think about it.
So let's talk about how static frame deviates from pandas.
You know, so the overall idea is this immutable data grow to the right sort of story.
But there's a lot of details here.
Do you want to maybe talk us through them?
The biggest insight is, I mean, one of the biggest changes really is the underlying numpy
arrays are made immutable.
This was one of the key observations that led me to start, you know, developing this and
realize I didn't have to write this thing in C or C++ myself in that I found there's a
flag on the num.
Each numpy array has a flag attribute.
And on that flag attribute are a number of properties.
One of them is writable.
And it's a Boolean.
And you can flip it.
And in doing so, the numpy, if you try to assign values into the numpy array, it gives you
an exception.
And numpy arrays, of course, are already fixed in size and shape.
They are, numpy arrays out of the box are mutable in terms of the values contained within
that size and shape.
But when I found this, I was amazed.
I was like, oh my God, this is what I've been looking for.
So with that insight, I began writing the core piece of the library, which is the internal
component called the type blocks, which manages the heterogeneous typed arrays and exposes a
unified interface to external clients.
So that first piece really of making all the internal arrays immutable and what I describe
as fully managing the array.
That is, if you create a static frame object with a numpy array, if that array happens to
be immutable, I can take it and I can use it and I don't have to make a copy.
But if static frame frame is given a mutable array, I make a copy and I make that copy immutable.
And from there on, we're safe.
Yeah, that's really cool.
So obviously, if you're given immutable data, problem solved, right?
But if you're not, then you want to take ownership of that data, take it inside of your library
and say, yeah, you gave me this.
I've read it.
Now we have a safe version of it.
It's really cool that you were able to just leverage that built-in feature of numpy because
that meant that whole layer down there.
You could just build on what numpy is doing and not have to go, we're starting from scratch
with nuts and bolts, right?
Yeah, exactly.
And I'm still quite curious why it's there because it's not really advertised anywhere
in the numpy docs.
I don't see information as to suggestions of using this or whatnot.
I found little bits of discussion here and there where I've seen other evidence of people
using it.
It is certainly documented in the flags as part of the flags for an array, but I'm actually
eager to find out more information of how it got there and how the numpy developers imagined
it would be used.
Yeah, maybe if someone's listening, they know they could come and put a comment on
the show.
Yeah, that'd be great.
On the show page.
That'd be cool.
We'll all learn from it.
Yeah.
Yeah, great.
So how much having it based on numpy was it, I guess, able to more or less stay the same
as before?
Like, did it make moving from pandas a lot easier?
Yeah, that's right.
Because, of course, pandas, at least in its present state, all data is stored in numpy arrays.
So basic expectations about how that data would work are the same.
Our goal, though, with static frame was to be closer to numpy.
And what that means is that every time that we do a calculation, like produce a standard
deviation or a mean or something else, we use numpy operations.
I feel like pandas is a bit ambivalent about this, and they have probably reasons probably
for performance for doing this.
But sometimes if you call the STD method on a series, you're not actually executing numpy
or you're executing numpy in an unexpected way.
I have a lot of respect for numpy's stability and over the years, over their versions.
And I trust numpy in terms of their approaches to doing these calculations, their defaults,
et cetera.
And I don't want to make those decisions.
So I'm happy to rely on numpy entirely for those sorts of calculations.
And then the numpy type system is something also that pandas has sort of struggled against
or is ambivalent about or actually increasingly seem to want to get away from.
Rather than try to create or my own type system or augment numpy's types, I took the efficient
approach for the resources for the project, which is just, OK, we'll just use numpy types.
Which means one very clear way this shows up is that if you create a series out of two
character codes, like FX currency codes, three character currency codes, you will get a series
of fixed offset Unicode, three Unicode characters, which is what numpy does by default.
So whereas pandas will convert that into an object type.
So I just let numpy use its types pretty much as it would naturally do and avoid getting involved
in that.
Yeah.
And of course, you carry over all the validation and testing to make sure that all the calculations
were done as accurately as possible.
And that's quite a matter as well.
Yeah, that's right.
So yeah.
So some other things that look like differences for static frame relative to pandas is one
is around unique indices.
Yeah.
So this so there was, you know, there's a couple of things that we as a team would constantly
be frustrated with in terms of pandas.
And one really obvious one is this ambivalence about whether an index should be unique or not.
This is when I think of an index, and maybe most people think of an index, they think of
it as a mapping similar to a Python dictionary where keys have to be unique.
And very often, that's how people use indices.
But in pandas, indices don't have to be unique.
And we would constantly be surprised when we found that a column in a table was set as the
index.
And without us realizing it, those values in that column were not unique.
And we ended up with a non unique index.
And if you try to select row from a non unique index using an LLC call, where you expect to
get a series representing a row, now suddenly you get a data frame representing two rows.
And that's very confusing and surprising.
Pandas has an option to enforce uniqueness when you create an index from a column with a amusingly
named parameter called verify integrity.
And verify integrity is by default set to false on pandas set index operation, which I understand
the desire to be accommodating, which I think is the motive here.
But I do not want to be accommodating.
I want to say that an index is a unique collection.
And if you try to create an index that of a non unique collection, you'll get an error.
Right, you should get zero or one things, not zero one or other numbers.
Yeah, interesting.
Another one is around dot access.
So basically, Dunder get item mapping over to like pulling items out of the by by index.
Yeah, and I think this was motivated by the ancestry of R.
So in the R language, the not exactly certain, but I believe that our data frame library exposed
columns through dot attribute like lookup.
And I suspected that in early versions of pandas, there was a big pull to try to move our people
over to Python and having that similar syntax, I assume was was desirable.
But of course, there's other attributes other than columns on the data frame object.
And so inevitably, there's some sort of naming collision that's going to come up with getting
columns from dot attribute.
So with static frame, we simply say the only way to get a column is by using the get item
syntax.
And there's no dot access.
And that's sort of the general theme of trying to have there be only one and one way to do
things.
And in terms of getting a column, we say, okay, you use the get item syntax.
Yeah, that's, I think that makes sense.
You know, you want to be like one of the overriding themes, it sounds like a static frame is sort
of safety predictability.
Right?
Not like, oh, we asked for the load.
And that's not the load on the system column.
That's the load data method or some weird thing that happens, right?
When you interact with it that way.
Yeah, exactly.
And, you know, of course, it's a huge benefit to have to have pandas.
And, you know, there's actually a family of data frame like interfaces around these days.
Not only is there the pandas data frame, but I think some other library, I think of Xarray.
Xarray has kind of a pandas like thing.
And there's a few other libraries out there.
So it's just a huge benefit to be able to look at, you know, the hard work of all of these
contributors over the years, be in the luxurious position of picking and choosing.
And so, you know, it's a great debt we have to those other packages and pandas in particular
to be able to look at those libraries and see, okay, I see why they made all those choices.
But we can consolidate all that into one thing and remove a bunch of ambiguity, remove a bunch
of opportunities for error.
Yeah.
Does the growth of Python in the data science data exploration space and the popularity of
pandas make this an easier sell at your company?
Like, do you feel like you don't have to cheerlead and like make the case for Python so much?
When you go and talk to people, the management or whatever, say, yeah, we're building it this
way.
Yeah.
Well, in terms of Python in general, the growth and popularity of Python, as I'm sure all of
your listeners know, has been extraordinary in the last five, 10 years.
And the role of Python in data science is probably largely due to pandas.
And I would even go further to say specifically pandas read CSV, which is just extraordinarily
fast, blows NumPy, blows everybody else out of the water and is such an awesome thing that
I think it's the gateway into Python for data science.
Your question specifically about using Python within our firm, it's been a gradual move.
My team was the first to use Python, but more and more, nearly every other area of the firm
that's doing something with software engineering is using Python.
And of course, everybody starts with pandas because that's what you see.
Because it's a load CSV.
Yeah.
And the idea with Static Frame was like, well, that's great for data exploration.
But if you're going to build something that you want to last and you want to reduce opportunities
for error, take what you know from that library and try it out with this thing and see what
you can do.
That's cool.
You said it was basically becoming increasingly popular.
What technologies was it displacing?
Like what else were you using to the extent you can say?
Sure.
Yeah.
I mean, within our firm, we were using SAS.
We were using R.
And those were the primary two languages, which are still quite common in finance firms
and the like.
And to a certain extent, people still use those.
But you can see the effort in the Python community, both SciPy, Pandas, NumPy, Matplotlib, many
others moving in the last five, 10 years to provide all of that functionality that R had or almost
all of it and many other platforms.
So it's quite easy transition.
Well, not easy, but it's a directed transition from those other languages.
Yeah, it's definitely it's not like going from that to C or something crazy.
Yeah, for sure.
So another difference has to do around iterating static frame, right?
And Pandas, when you iterate, you get the values.
And here, it's more dictionary-like, right?
Oh, OK.
Yes.
There's two elements to sort of the iteration thing.
The first has to do with the static frame series.
So the frame and the series and both Pandas and static frame are dictionary-like containers.
Both static frame and Pandas define a keys method, define an items method that work in a way that
we know well from Python dictionaries.
With static frame, the difference, though, has to do with the series.
When you iterate a Pandas series, it iterates over the values, which makes some sense if
you think of it as a wrapper around a NumPy array.
But if you call dot items on a Pandas series, you're going to get pairs of the index, the key,
and the value.
So again, in this effort to try to be consistent, if you iterate a static frame series, you are
going to iterate over the keys, just like you would with a Python dictionary.
So you actually get the index values.
And if you want to get the values, you have to use the dot values attribute.
Right.
That's one difference in terms of iteration.
The other is, while that dictionary-like interface, we try to be really consistent there, the other
place is recognizing that Pandas has a number of different approaches to iterating over columns
or rows in a frame and function application on those iterations.
So Pandas has an apply function, and it has various iteration functions.
Like iter rows or iter tuples.
And I saw an opportunity to unify all of those.
So the series and the frame all have different families of iterators.
And all of those iterators return objects that themselves have function application methods
on them.
So the same tool you use for iterating exposes an opportunity to do function application.
And that descends from the old library that we use, where function application across the
table was a really common move.
And so making that sort of a first-class element in the library was really important to us.
Yeah.
It sounds great.
Also, you talked about the sorting.
The default sorting is stable in static frame.
What's the story there?
This is very simple because, fortunately, NumPy did all the work here.
NumPy's sort method provides a number of options for which sorting algorithm to use.
And again, in the spirit of safety and repeatability and stability, the default sort method for static
frame is set to Panda's merge sort, which is indeed stable.
The default for Panda's sort is you can switch it to be merge sort.
But by default, I forget exactly what it is.
But it is not a stable sort.
Like quicksort or something like that.
Yeah.
I believe the default is quicksort.
Now, why they chose quicksort, I don't know if there was any reasoning behind it.
Maybe quicksort is faster in certain cases.
But merge sort is reasonably fast.
And if I can make a choice to ensure that the sort is stable to the order entering the
sort, that seems like a benefit to me.
Yeah.
It comes back to this predictability, safety, overriding theme, right?
Exactly.
Yeah.
So I guess maybe another area is how it's tied to the NumPy defaults for calculations.
And things like that.
Yeah.
So that gets back to the spirit of, you know, being close to NumPy.
And I have an example of this where, you know, you take the standard deviation of three values
without any arguments in with a Panda's series.
And you get a different value if you do the same thing with a NumPy array.
If you use NumPy's STD function, you get a different value.
And that's very confusing.
And it has to do with the DDOF, the delta degrees of freedom argument to the standard deviation.
Now, people that have played with standard deviations are well aware of this parameter.
But some people may not be.
And that's quite confusing.
And I just, I don't see a need for that heterogeneity.
I'm fine to stick with NumPy.
Yeah, that makes a lot of sense.
This portion of Talk Python to Me is brought to you by StellarS, the AI-powered talent agent for top tech talent.
Hate your job or feeling just kind of meh about it?
StellarS will help you find a new job you'll actually be excited to go to.
StellarS knows that a job is much more than just how it sounds in a job description.
So they built their AI-powered talent agent to help you find the ideal job.
StellarS does all the work and screening for you, scouting out the best companies and roles
and introducing you to opportunities outside your network that you wouldn't have otherwise found.
Combining deep AI matching with human support, StellarS pairs things down to a maximum of five opportunities
that tightly match your goals, like compensation, work-life balance, working on products you're passionate about, and team chemistry.
They then facilitate warm intros.
And there's never any pressure, just opportunities to explore what's out there.
To get started and find a job that's just right for you, visit talkpython.fm/StellarS.
That's talkpython.fm/S-T-E-L-L-A-R-E-S.
Or just click the link in your show notes in your podcast player.
Let's see, another one is discrete functions rather than branching parameters.
So like trying to, is that like breaking stuff apart so there's functions that are simpler to understand rather than taking a bunch of parameters?
Yeah.
We've tried to systematically design an interface that has functions that have orthogonal parameters.
So I think when all of us write functions, that should be our goal.
That is, the relevance of one parameter to a function shouldn't depend on another parameter.
That's quite confusing and can lead to mistakes.
What you get instead is more functions.
But the functions are more specific.
And I believe that leads to more clear code.
And it also aids in refactoring, actually.
One example of that that I think is nice is the set index method.
So on pandas, there's a set index method that if you give it one column as the argument, it will set that one column as an index.
If you give that argument a list of column names, it will give you a hierarchical index.
And all you did was change your input.
And now you have a very different structure coming out of this.
Right.
Not even necessarily keyword arguments, but you just change the type that you're passing.
Right?
Yes, yes, yes.
So there's many places in pandas where there is this sensitive dependency to the type of an argument that results in a different output, which is very problematic.
So in static frame, we have two methods.
We have set index, and we have another method called set index hierarchy.
And when you set index hierarchy, there you're expected to give an iterable of column.
And you can't give it a single column and vice versa.
So we split the functionality into two different functions.
And now it's completely clear to the reader what was intended.
And if later on you need to do some refactoring and you need to find all of the places where you created a hierarchical index, well, you just search for the function name.
You don't have to search for the function and then probe the type of that argument to know whether or not you're getting a hierarchical index.
Yeah, that's a tremendous difference.
And, you know, you go to your fancy ID, you right click, you say find usages, it'll say there are six.
They are here.
Yeah, exactly.
Exactly.
That's way better than they're here, but only sometimes.
Like that's a little sketchy for sure.
That's right.
All right.
So it sounds like there's a lot of maybe familiarity if you're coming from Pandas, but there's enough difference that this is really something on its own and special.
And there's good reasons to use it.
One of the key things from Pandas is the, well, I mean, in Pandas, it took Pandas to figure this out too, is that there's three types of selection.
When we're selecting data, there is the root get item selection, which in Pandas overwhelmingly is used for column selection, but in some rare cases can be used for row selection.
That's something we changed, but I'll get back to that.
So there's the get item, there's the .loc selection, which can take one argument for row selection, two arguments for row and column selection, and the iLock selection, which uses integers instead of the labels of the index.
So that family of those three selectors really gives you everything you need.
Now, Pandas at various times had other types of selectors.
There's this IX method, and there's a few other variants, but they seem to be getting rid of those.
Recognizing that there's these three types of selection really is one of the fundamental things to bridge the gap for people coming from Pandas to static frame.
Those are relatively the same.
One of the key differences we made in line with consistency and having only one way to do things is the root get item selection interface is only a column selector.
It is never a row selector, which is a shortcut you can do in Pandas, but again, it's undesirable, is not clear for readability, and is difficult for refactoring.
Yeah.
Interesting.
Okay, cool.
There's three types of selections, the root get item, the lock, and the iLock, and then we expose them in sub-interfaces, if you will.
So a relevant question is, if I have an immutable data frame, how do I do assignment?
Well, you don't.
But Pandas and also NumPy have these really powerful ways of doing an assignment.
I can do an assignment with Pandas.
I can do an assignment in a lock call, in an LLC.
And I can assign to an entire column.
I can assign to an entire row.
I can assign to a mixture of columns and rows by using the same syntax I use for selection.
That's an awesome feature.
I wanted to maintain that same expressive interface, but you can't do in-place mutation.
So how do you do it?
Well, on static frame, there's a .assign attribute.
And that .assign attribute exposes a root get item, a lock, and an iLock.
So under that assign attribute, you can do all of the same type of assignment moves you
used to do, only you get back a new frame, and you're not mutating the old frame in place.
That's a great feature.
I love it.
So let's talk about testing for a little bit.
I saw that you have unit tests for performance tests, unit tests, things like that, which is
great.
One of the things that really stood out to me when I was looking at it was that you actually
were using hypothesis, which is an interesting library.
I covered, I had Austin Bingham on the show long ago talking about hypothesis.
It's probably been three years.
But you want to just tell us roughly, really high level, what that is and why you decided
to use it?
I saw at last, I think it was last year's PyCon presentation on, maybe it wasn't specifically
hypothesis, but it was related to that.
Property-based testing in general.
Yeah.
And I just was so impressed.
I was like, oh man, all that time I spend trying to find corner cases and trying to make my
unit tests have sufficient coverage can be automated for me by using a tool that you control and you
shape the random generation of values to meet the expectations of finding these extreme corner
cases.
I took that away and was like, wow, I really want to do more of that.
One of my colleagues here at Research Affiliates who does some work in Haskell set off on trying
to use this a little bit more in depth.
And this whole idea of property testing, in fact, comes out of Haskell.
I forget the name of the library that originated it, but the whole library was published as one
page on the paper that introduced the concept.
It's really amusing, but the implementation of the original sort of property-based testing tool
is just one page of Haskell code.
But through his example, my colleagues' examples and starting to look at it, I'm like, man, this is
exactly what I need for static frame because, you know, you're trying to build a general purpose
library.
There's no way I'm going to be able to anticipate the things that people are going to want to put
into a series or a frame.
There's no idea that I can anticipate all the possible values someone is going to try to put
in an index.
So with property-based testing, with using hypothesis, you open the door to just defining
the properties that you expect to have.
You know, namely that if you create an index with 20 integers, the resultant index is going
to have 20 values.
Well, that's true unless you've duplicated any values.
Or that's true if it's not true if you duplicated values.
Or it's not true if something else went wrong in reading those values.
So I think of hypothesis in the context of static frame as a way of simulating my user.
It's, you know, the user.
It's thousands of users who are throwing everything into these containers.
And hypothesis really nicely gives you a way to model that and really changes the way you
think about testing.
Again, my same colleague, you know, was like, you know, I enjoy testing again.
I enjoy writing tests so much more when using this because it just forces you to think about
it in a different way.
And it's very refreshing compared to the task of writing unit tests.
Yeah, it's cool.
It's almost like writing a meta test, right?
Instead of going like, here are the seven cases.
Here's one where the value's in the middle.
Here's the edge of the array I'm trying to test.
Here's one that's out of the bounds.
You can just go, this is the general type of stuff that goes in.
These are the general types of things I want to verify.
Go make that happen and vary a bunch of stuff for me, right?
Yeah, yeah, that's right.
Yeah, it's pretty cool.
So I was really thrilled to see that you had put that in there for some of the testing stuff.
It's cool.
People can check it out in the GitHub repo.
Yeah, I have a lot more to do there.
But again, it's like you have to go into it.
What's really startling about it is you really have to be in a different mindset.
So you have to give yourself the time to get into mindset.
There's much more I need to do with that.
But it's a refreshing and pleasurable place to be in.
So yeah, I highly recommend it.
Yeah, I bet.
It seems super cool.
It definitely seems like you can't just bring your main way of thinking about testing.
Like, I'm going to test this one case and see if it works.
You've got to sort of step back a level.
Yeah, that's exactly right.
Yeah, nice.
Another thing I wanted to ask you about that I didn't before when we talked about Python
and finance and just we're coming up on 2020.
It's the death clock for Python 2 is ticking at pythonclock.org.
I think it is.
It's ticking down.
The time is getting short on it.
What is it like?
Is first of all, the static frame support for Python 3?
Oh, it built entirely in Python 3.
We're at 3.5 now.
No support for 2.
So that was a huge benefit of my predecessor here.
Research Affiliates.
He set out building our code base in Python 3 back in 2012 or even 2011, which some people
would have said, might have said was, you know, kind of questionable choice.
But at that point, we had NumPy and we had Pandas soon after that.
So given that foundation of Python 3, we've been using Python 3 entirely and have never looked
back.
Yeah, that's super.
And then what do you see that transition looking like in the finance space larger?
Not necessarily just for your firm, but the other folks you interact with as well.
In terms of moving to Python 3?
Yeah.
Like, do people just have their head in the ground and go, we're just not doing it?
Are they going like, oh my gosh, here it comes.
This is going to be like Y2K again.
Or do they, are they ambivalent?
What's the finance vibe around that?
I can't speak broadly.
I do know that there's a very large bank that employs a very large number of Python developers
who use a lot of extensive systems built entirely in Python 2.
And I don't know if they're even on 2.7 or 2.5.
Yeah, I think the bank that you were talking about, I think I know.
And I don't even think they're on 2.7.
Yeah, I think they're stuck on 2.5 is what I heard.
But it's going to be very hard, I would expect.
Maybe they've built their frameworks in such a way that maybe they're okay.
One of the things I've heard about that very large bank is that their Python tools, to some extent,
are enforcing immutability.
And for the same motivations that we have, they may have put constraints on the language
in a way to help reduce risk that they can keep for a little while.
But certainly, it's going to require a transition at some time, and that's going to be hard.
Yeah, I agree.
I guess two thoughts.
One, do you feel like maybe that is a failure of leadership, engineering leadership, to say,
we put ourselves in a corner, you guys, and we have to.
I know it's not building features or driving the investment engine, but we have to keep moving
forward if we get stuck, not just on 2.7, but on 2.5.
And all these libraries, they can't use anything NumPy is doing, or Pandas, or in the future,
right, as they're dropping, right?
You know, Pandas already announced they're dropping Python 2 support.
Right, I saw that.
Yeah, it's definitely a challenge.
And it's technical debt, right?
It's, as, you know, my own team, we're on 3.5, and we're in the process of jumping to 3.7.
You know, even that, for us, as a relatively small team with a decent but modest-sized code base,
you know, it takes work and it takes time.
And just as you say, it doesn't deliver immediate features.
It doesn't deliver obvious benefits.
It is a technical debt.
And it's often, it's very difficult to prioritize that work appropriately, and also to communicate
the value to upper management and others that are considering what your developers are doing.
But it's, I mean, the important thing is that it's called debt for a reason.
You have to pay it, or your survivors will pay it.
There is no debt forgiveness in technical debt, other than abandonment.
I mean, you can abandon the code and start over.
There's no too big to fail, sort of, really.
Yeah, so it's definitely something to pay attention to.
I mean, even with Pandas versions, we've struggled to keep up with Pandas updates.
We're still presently using Pandas 17.
We are transitioning to Pandas 23 or 24.
I think we're going to 24 now.
I think 25 just came out.
But even before we were on 17, we suffered and spent quite a bit of time accommodating the changes to the API
and changes downstream of Pandas changes.
So it's painful, but you just have to do it.
Yeah, in their defense, right?
That's a lot of money.
If you're rewriting the code significantly, that's touching money versus just driving the website or whatever.
I can understand the hesitation.
I want to mess with that.
But at some point, maybe it's not in 2020.
Maybe it's 2025.
At some point, it's going to be a problem.
People are going to go, I don't want to work there.
You mean really?
That version from that long ago with that few library support?
No, thank you, right?
Like, it's going to be a problem.
It's going to be like cobalt.
Yeah, yeah.
Cobalt.
I made a joke about cobalt the other day with some of my colleagues, and I was quickly corrected that there is –
apparently there still is quite a bit of cobalt in production.
Yes.
So I was like – I thought it was like a dinosaur, but I guess there's still a lot of cobalt in production.
But you can get away with it for so long.
But at a certain point, yeah, you're exactly right.
It's a huge detriment to recruiting.
We're a small firm located in Newport Beach.
Not exactly a tech hub, although Irvine's trying a little bit.
But for as long as we've been recruiting for this team, I've been – less so now, but a few years ago, I would say to people, yeah, and we're working in Python 3.
And they would say, oh, really?
You're working in Python 3?
I'm stuck in 2.7 or 2.5.
I'm so excited.
That would be awesome.
I am so excited.
So a few years ago, that we were entirely in Python 3 was explicitly a highly desirable feature for prospective candidates to our team.
A little bit less so now, but it's something that we always say up front.
Yeah.
Well, it's definitely a good thing.
I think only less so only because other people have started to make that path, go down that path, right?
Yeah, that's right.
I mean, when I was in – I went to a PyCon, I believe it was in 2013, and I believe it was at Guido's keynote.
Maybe it was somebody else.
But the question was asked to the general assembly, you know, when there's all thousands of – however many thousands of people are in that room.
And they asked a show of hands of how many people are using Python 3 in production.
And me and my colleague raise our hand and look around.
And there's just – I mean, it was far less than 10%.
Yeah.
But I think they did that exercise again at a recent PyCon, and it was – looks like it was more than half, you know?
Oh, yeah.
Yeah.
The community is definitely moving, and, you know, it's good to see.
It's great to see.
All right.
Well, I think we're getting short on time, so we're going to have to leave it there.
People should definitely check out Static Frame.
If Pandas is something that you're doing, maybe this will apply.
I guess maybe one final question I could ask for you, Chris, is how does somebody know that they have a problem that Static Frame will solve better than Pandas is solving?
Yeah.
I mean, often the advice will be like, hey, yeah, use Pandas, right?
Load CSV, all that kind of stuff.
But, like, when would you say, actually, you should consider this because it'll solve your problem better?
I would say there's a couple signs.
One might be that you keep on making mistakes.
You make mistakes because you reach for the wrong interface, or you get a surprising result because there's a type sensitivity to an argument, or you make a mistake because you accidentally mutated data you didn't intend to mistake, or you got a multi-index when you expected a unique index.
You know, those are the kinds of things that are the telltale signs that maybe the kind of work you're doing, you know, requires a different package with a different set of constraints.
Yeah, that's right.
That's a great description.
Thanks.
All right.
Now, before you get out of here, I've got the final two questions.
Sure.
If you're going to write some Python code or work on Static Frame, what editor do you use?
I am recently moved over to VS Code, as many people may have had some apprehension about Microsoft products for some time.
And now there's a Microsoft product that I use every day and really enjoy.
Prior to that, I used a few different editors, but I've been really happy with VS Code.
In large part, I don't really ask a lot for my IDE.
I really want it to get out of my way, and I don't debug in the IDE.
I don't lint in the IDE.
I prefer to do those things from the command line.
I just like my ID to be something close to like a Zen mode that gets everything out of the way, and I'm very aesthetically inclined,
so I'm very sensitive to my colors and whatnot.
So with VS Code, I was able to quickly, with a very low transition cost, get it to be visually, aesthetically, sort of ergonomically comfortable for me.
And in subsequent updates, it hasn't made it worse.
It's been good.
So I've been very happy with VS Code.
That's cool.
Yeah, they're doing great stuff with that, so I definitely hear that a lot.
All right.
And then notable PyPI package.
I'll go ahead and throw a static frame out there for you.
People can pip install that, right?
Yep.
It's there.
Ready to go.
All right.
Other ones that you're like, oh, I heard about this the other day.
Maybe you don't know about it, but it's really cool.
It solves this problem uniquely or whatever.
Any come to mind?
I should plug the project I worked at before I started at Research Affiliates, which is Music21.
Music21 is a Python package that I co-created and founded and did sort of initial three years of work on it at MIT with a former colleague of mine there,
which is a really fun tool for the other day.
Which is a really fun tool for examining what we call symbolic music.
So music represented as XML or music represented as MIDI files.
Music21 allows you to take in these musical representations and play with them as an object model and ask questions about them.
Like, for example, given all of Mozart's string quartets, how often does he use a modified pitch on the third beat or something like that?
Uh-huh.
Awesome.
So it's a really fun toolkit if you know anything about music and you want to start experimenting with generating or analyzing musical notation.
Okay.
That's a great recommendation.
That's very cool.
All right.
Final call to action.
People want to get started with static frame.
What do they do?
I did the essential thing recently.
I made a quick start guide.
So I started to write API documentation, and that was kind of tough.
And it's not a pleasurable read and not a good introduction.
So I fairly recently wrote a little quick start guide.
You can find it on GitHub in the readme.
You can find it in the documentation, which is a little tutorial using data available from a JSON endpoint that will walk you through some of the key features and main differences from Pandas.
And hopefully will be enough to get people excited about the package.
Yeah, very cool.
And you also gave a presentation at PyCon, which was recorded.
I'll link to that so people can check that out.
Final question.
Are you looking for open source contributors, people to jump on this project, or is it kind of baked?
What's the status there?
Oh, absolutely.
So while this tool is being used internally within my firm and its use will grow within our firm, we are absolutely looking for contributors and users and testers to give us some feedback.
I've been fortunate in the development of this in that I've had my team to constantly give me feedback and tell me I'm being too nice, as they like to do, to make our interfaces discreet and precise and get a lot of feedback and support from my team.
So I owe a huge debt to my team and the context of our work here to support that.
But we need more users.
We need more testers.
We need more feedback.
So at a basic level, people using the tool and giving us some feedback, they may not be ready to move it into it into their production systems.
And I certainly understand that.
But some good dabbling, starting to play with it would be really helpful for us in getting some feedback.
And of course, if I'm pretty happy with the code itself, I would encourage those to look at the code.
They see opportunities to add things and make things better.
That would be fantastic as well.
Yeah, super.
All right.
Well, thanks for giving us the whole story and history of Static Frame.
It looks like a really cool project.
Great.
Thank you for your time.
Happy to be on the show.
Yep.
Happy to have you.
Bye.
Bye-bye.
This has been another episode of Talk Python to Me.
Our guest on this episode was Christopher Ariza, and it's been brought to you by Linode and Stellarress.
Linode is your go-to hosting for whatever you're building with Python.
Get four months free at talkpython.fm/Linode.
That's L-I-N-O-D-E.
Find the right job for you with Stellarress, the AI-powered talent agent for the top tech talent.
Visit talkpython.fm/Stellarress to get started.
That's talkpython.fm/S-T-E-L-L-A-R-E-S.
Stellarress.
Want to level up your Python?
If you're just getting started, try my Python Jumpstart by Building 10 Apps course.
Or if you're looking for something more advanced, check out our new Async course that digs into all the different types of Async programming you can do in Python.
And of course, if you're interested in more than one of these, be sure to check out our Everything Bundle.
It's like a subscription that never expires.
Be sure to subscribe to the show.
Open your favorite podcatcher and search for Python.
We should be right at the top.
You can also find the iTunes feed at /itunes, the Google Play feed at /play, and the direct RSS feed at /rss on talkpython.fm.
This is your host, Michael Kennedy.
Thanks so much for listening.
I really appreciate it.
Now get out there and write some Python code.
I really appreciate it.