Resources

James Goldie - Your journey from data science to animated web graphics

video
Oct 31, 2024
20:30

image: thumbnail.jpg

Transcript#

This transcript was generated automatically and may contain errors.

Hi everyone, my name is James Goldie. I'm the Data and Digital Storytelling Lead at 360info. Today I'm here to talk about my journey, and hopefully your journey as well, from data science to animated web graphics with JavaScript.

I'm sorry I can't be there in person today at PositConf, it sounds like it's really awesome. I am on the Discord, however, if you have any questions or ideas you want to throw at me, please feel free.

So I am a data journalist at 360info in Melbourne, Australia, where an open newswire supported by Monash University. We deliver verified and reliable information to as many publishing, broadcasting, and civic society outlets as possible. So I lead our data efforts, I publish open graphics and data sets under Creative Commons. I also used to be a climate and health researcher, and I'm going to talk a bit more about that transition as part of my talk.

If you're following along today, the slides are available on my website down on the bottom left there. The code is also available, although without these lovely backgrounds. And of course, I'm in the Discord.

The journey from data science to web graphics

So I said my career started off in research and data science, and I really moved across to communication and data journalism over that time. Through all that, I really looked for tools to help kind of bridge my existing technical skills. Quarto is a fantastic bridge, and today I really want to take you on this journey, going from R or Python or Julia through to JavaScript.

And I think it is a bit of a journey. You know, it's not something where we start in one, one day, and the next day we're using JavaScript at full time.

And that's partly because there are all these little points along the way that feel scary. And they don't have to be JavaScript. You know, one thing I really struggled with, you know, one thing I really struggled with early on was just adding a style sheet to a Quarto or R Markdown document. I think a lot of us want to add our own fonts and colors to things. But as soon as you hear CSS, and you're like, oh, I've got to learn a whole new language. No, no thanks.

It feels scary. And that happens in JavaScript as well. You know, plenty of us have written a Shiny app. And then we hear that, you know, in order to get something done, we need to add this little bit of JavaScript. And it feels like popping the hood on a car, you know? If you haven't learned anything about how it works, it feels like you're just kind of going down this whole layer of abstraction.

And that's scary. And the same thing happens in JavaScript. You know, D3.js is a very popular framework for making web graphics. But it's really intimidating. Same thing with the sorts of build tools that JavaScript frameworks use. They're often very confusing and difficult to learn if you're not a part of that world. But Quarto has everything we need to get started with web graphics and to start learning.

But Quarto has everything we need to get started with web graphics and to start learning.

Getting started with Observable JavaScript

Every journey starts with a single step. And our first step is going to be using observable JavaScript. Now, OJS is one of Quarto's superpowers, in my opinion. I think it's fantastic.

And we're going to use OJS and a framework that comes with Quarto called observable plot to replace the sort of graphic we might make with ggplot in R. And the nice thing about OJS is that you can use it right now. If you have Quarto installed, you don't need to download anything else. You don't need to add anything else. You don't need to configure anything else. If you have Quarto, you're ready to go. You can add observable JavaScript to your documents right now.

So here's what we're going to do. We're going to take a very basic sort of bar chart. And in R, we might use a chunk of R code in our Quarto document that looks like the one on the left. Starts with our back ticks and then our R in curly braces. And we're going to do OJS instead. So instead of R in those curly braces, we're going to have OJS.

So let's step into it. So let's say we have a very basic data set. It's a list of Australian cities and their populations in millions.

This is the sort of thing we might do in R. We're loading ggplot2, and then we are adding a mapping function, our AES, that says, here are the columns in my data, city and pop, and I want to map them to the x and y axes. And then we're adding layers, like our geom cold, so that we've got our bar plot. The rest of it is styling. Yeah, I've just stripped it back a bit. But I think this is very typical of like a sort of plot that someone might make with some light customization and then a title at the end.

Now, let's see how we do this in observable plot. So we've switched from an R chunk to an OJS chunk. We've got a title that is actually just, it's not even part of the plot. It's just on top because it's HTML and we can put text wherever we want. Let's get into it. From line six, we've got our plot function. Now, if you're looking at this, you're like, JavaScript looks very different. And syntactically, yeah, sure, it's a bit different. But I think if you look closely, you can see some similarities here. So we have the marks option in observable plot. And this is just like adding our geoms in ggplot. We're adding physical layers.

And you can see here, we've got a bar Y layer or mark, I should say. And inside it, as well as saying, here's the data I want to use with it. We're defining our mappings. This would be our AES call in ggplot. And here we're saying the city column is our X axis and the pop column is our Y axis. And then the rest of it, we're just doing a bit of styling to make it look pretty similar. Already, we have something that is very equivalent to something we could do in R. And we've barely started modifying one of our existing auto documents.

Now, how would we get that data into OJS? If you're already working in R or Python, it's very simple. As part of your R chunk, you would write OJS define with an underscore. And then you just give it a data frame or some other piece of data you want to send across. And it appears. That's it. You can also write out a CSV file or some other spreadsheet or other file. And then you can load it back in in OJS using the file attachment function. But either way is fine.

So we've got our awesome plot. But why would we want to do this? I mean, it is just our first step. But we can do more just static plots with OJS and the observable plot. For example, observable plot has built-in tooltips. So we can see here, I've added an extra layer here on top of my existing plot from before. So I have my plot.barY. And on top of that, I have plot.tip with all the same options. And now we're getting tooltips for free. And in fact, in a lot of plots, it's simpler than this. We can take our plot.barY and we just add tip true to it. And we've got tooltips. And we can customize those tooltips as well, of course.

But as well as that, we can actually add some accessibility options here. If we were just making a PNG file with ggplot, we could add a description to the image as a whole. But we couldn't describe any of the individual parts of it. With observable plot, we can actually add ARIA attributes, which inject meaning into our plot, to the individual bars. So we can make sure that if somebody is looking at this plot with a screen reader, they can see, yes, it's a bar plot. But if they zero in on any of the individual bars, they can see that Brisbane has a population of 2.28 million people. Or this is a bit old now. So that lets us pack some extra meaning and accessibility into our plots.

Reactivity and animation with OJS

So we've talked about some basic examples here, just getting a flavor for OJS. Now I want to take us into some more adventurous stuff. We're going to try out a few things, see a bit of what OJS can do.

So here we have another observable plot. This one's a map. We've got some cities labeled around the world. It's very similar to the previous observable plot graphic we did, in the sense that you can see, again, we have our list of marks, our list of layers. This time we have three. We have a plot.geo, which is our coastline in dark gray. Then we have a layer of dots. That would be a geom point in ggplot. And then we also have a text layer, which is for our labels. And you can see for each one, we've mapped an X channel and a Y channel. So our longitude is our X, and our latitude is our Y, just like we do with AES in ggplot2.

Now this is a fine start, but I think we could make this graphic a bit more interesting. So for starters, we could make it a globe. And in an observable plot, that's not too hard to do at all. We can add a projection here. We've added an orthographic one to make it into a globe. And it's centered on 50 longitude, which is going to be important in a second. And then we've added a couple of extra layers on there for our grid lines and our sphere around it. So it's not just land in space. We can see the outline of the globe.

But we've lost half our cities because half of them are on the other side of the world. It would be really nice if we could move this around.

So the really nice thing about observable JavaScript is that it's reactive. Now, if you've ever used Shiny, for example, you'll know what reactivity is about. If your plot includes something that is changing, your plot changes too. And you don't need to tell it how to do that. You don't need to say, you know, when now does this or when one thing does this, do all this logic. You just refer to a thing that's changing and it changes as well. So built into Quarto and OJS, we have the now object. And that's just a ticking clock. It's just number of milliseconds ticking upwards forever.

But we can turn this into an angle with a bit of maths. So we do a bit of dividing, a bit of modulus, some subtraction. And now we've got a number between negative 180 and 180. And you can see we don't need to do anything special here. We just refer to now and angle is changing on its own. So that's automatically updating. And now we're going to take it a step further and we're going to include angle in our plot. So where we had rotate 50 degrees longitude, now we have angle. And so every time now changes, angle changes. And every time angle changes, our plot gets redrawn just a little bit each time. And when you do that finely enough, you get this animation, this rotation effect as it moves around. As angle slowly increases, our globe rotates just a little bit more. And we get this really neat rotation effect. And we haven't had to do any of this logic to tie these things together. We just refer to them.

We can do a lot with this technique. And it doesn't just need to be animation in the sense of things changing over time. My colleague Andrew Bray just presented CloseRead yesterday. That's a Quarto extension we've been working on for scrollytelling in Quarto. With CloseRead, you can use cr-progress instead of now. And then you're animating on scroll. So as you move this page down, that globe is twisting.

But sometimes we might not want to animate something necessarily. We might just want to conditionally update things or change them as our circumstances change. Now, if you're using R or Python, you've probably seen an if-else expression. We say we give it a condition. And we say if the condition is true, give us this thing. And if the condition is false, give us that thing. And in OJS, we have that as well. The condition comes first. We've got a question mark, true thing, colon, false thing. But the difference is in OJS, it's reactive. So it doesn't just get evaluated once when the document opens. It's evaluating continuously. When the person opens the web page, whenever the condition changes, the thing changes as well.

And we can do a lot with this. So for example, we have a simple timer here at the top. We don't need to worry about the code box. We can just see that it's ticking between 0 and 1 every couple of seconds. And if we refer to this, we can now start to make some conditional content. So on the bottom left, we have some just text, markdown text. And you can see that's changing as the traffic light timer does. In the middle, we have graphics. So we've got markdown graphics. We'd normally put these in a Quarto document. And we can tick between two to make like a slideshow or that kind of thing. On the right, we have a really more complicated example where we're actually doing a drawing of some traffic lights using basic shapes. And you can actually see here that we had our condition embedded in here twice. So our traffic light is ticking between, for the first one, black and orange. And for the other one, green and black.

So we can do a whole bunch of different things here. We don't even have to use the same kind of content. We can mix things. So for example, we have our city population data set here. And we've added it as a table. A table you'd normally think of as being an output. But in this case, it's also an input because we can select rows. Observable, Quarto, and OJS also come with a library called Observable Inputs. And this gives us a whole bunch of user interface elements that you've seen before, like checkboxes, radio boxes, drop-down menus, and in this case, a table. And what we're saying is that this first line up here, if we have less than three cities selected, or I should say zero, one, or two cities selected, we're going to do our city plot with our bar plot. And if not, we're going to show an error message. So you can see here, we've got an error message at the moment. But if I select these, as soon as we hit three, it pops up. And you can see it updating still as the data changes. So this selected cities is a filtered view of our data set. We've sent in our original data set. And then selected cities is the filtered version based on our selection.

We can even make responsive graphics with this. As well as now, we also have the width object. And you can see that if the width is over 700, we get a horizontal bar plot. And if not, we'll get a vertical one, which is something I've prepared separately. And as you can see, as I make the window narrower, as it goes below 700, we pop to this vertical one. So now we've got something that we could use on a mobile to make this a bit more readable on a narrow layout.

Introducing Sverto for smooth transitions

And most of the time, this is kind of enough. Honestly, most of the graphics I build with 360 Info, we're either using an external charting tool, or if I'm building something bespoke, I'm using observable plot and observable inputs and OJS. And this works really well because you can very effectively and quickly iterate to find the form of your graphic that's working for your audience.

But there are a few times when it doesn't work so well. And for me, that's often when I want to transition from A to B. You know, we can kind of do simulated transitions, like the globe example, where we had something that's ticking up over time, but it's all got to be manually calculated. In OJS, when you replace one thing with another with reactivity, the first thing gets destroyed immediately. What I need is something that can see the start and the finish and smoothly transition between the two.

So OJS's reactivity is lovely, but I want to bypass it sometimes. And I want more control over what I draw. But I do like the declarative way I get to build graphics into ggplot2 and observable plot. And so the last part of our talk is talking about a tool I've developed, a Quarto extension that lets me use a mature web framework called Svelte in Quarto. It's called Sverto.

The funny thing about reactivity is it's actually really popular these days. It's just about everywhere. You've seen it in Shiny, but it's also in mobile app development, most Apple and Google's main frameworks are reactive these days. And in web development, we have React, Svelte, and countless others. And the thing they all have in common is that you make a component, you give it data or options that might change, and you tell it what you want to show. And then it takes care of doing the updates when things change.

If this sounds really familiar, I think reactivity is very philosophically similar to declarative graphics because they're both about deciding that we don't want to get bogged down in the details of how we want to make changes to things and updates to things. We just tell it what we want to focus on and it takes care of the rest.

I think reactivity is very philosophically similar to declarative graphics because they're both about deciding that we don't want to get bogged down in the details of how we want to make changes to things and updates to things. We just tell it what we want to focus on and it takes care of the rest.

So here's Sverto. This is bringing Svelte components, like charts, into Quarto. And those can be Svelte components that someone else has made, part of the library, or they can be ones you've written yourself. In this case, the Svelte component is not the whole page, it's just the chart. This is one of the really nice things about Sverto is these dropdown menus we have here on the left are actually just OJS. They're just the same observable inputs that I used previously in the presentation.

But when I change them, they are updating a dataset and then that dataset is going into the chart. And when the chart gets a change, like a changed data, it smoothly updates the things that it's drawing. And it does so knowing where things were before and where they will be next. So you get these smooth transitions. So that could be like the colors as we change from day to night, as we change from Melbourne to Brisbane, looking at historical weather, you can see that we're adding and removing points as we need to, fading them in and out, and we're moving them as well. And you can see when we go from hottest to coldest, we've also got some axes that we're using with D3.js. So yeah, we've got a bit of everything here.

The way Sverto works, it lets you write Svelte components like charts and maps and bring them into Quarto quickly and easily and then drive them with OJS. And once you've got it installed, it's just three easy steps. So use your document front matter to bring the Svelte file in. Then you tell Sverto where you wanna place it with a little bit of OJS. And then you update with OJS just by assigning to properties. So we've brought my chart in. It has a property called chart data, which is our dataset. And we just tied this to something that is changing. Like say we've got our full dataset and we're filtering it based on some menu or something of our selected year. Every time that menu changes, our filter statement runs again. And then we pipe that into our chart and it changes as well. And we get this smooth update.

Resources and closing thoughts

So Sverto is available on sverto.jamesgoldie.dev. I'd love for you to give it a try.

But I also wanna send you on your way with a bunch of other resources. No matter where you are on this journey. So if the first part of my talk appealed to you, you just wanna try out OJS for the first time, get your first bar plot in or whatever it is, check out the Quarto documentation or check out observable plot and observable inputs because they both have great documentation.

If the intermediate part interested you, you're starting to get a bit more adventurous with OJS, check out observables examples. But also at 360info on our GitHub, we have dozens of OJS graphics that are ready for you to use. They're open source. They come with analysis, the whole thing. So please check them.

And if you were interested in the last part with Sverto and Svelte, please do check out Sverto. I'd love for you to use it. I also highly recommend a blog post by Connor Rothschild called how's you quote unquote learn D3 in 2023. It really sold me on this approach. I can't recommend it enough.

I'm gonna leave it there. If you have any questions, I'm in the discord. I'm happy to chat. Thank you very much for coming along to my talk. Thank you Posit for accepting it and to Articulate for the fantastic speaker training. Andrew Bray, thank you for letting me use one of our close read examples. And Chepakut, these amazing fantasy backgrounds, they agreed to let me use them as part of my talk and I really appreciate it. Thank you very much. Oh, and we have some bonus slides if you're checking this out on my website. Thank you.