Resources

[79] Create a Python Web App Using Shiny (Gordon Shotwell)

Join our Meetup group for more events! https://www.meetup.com/data-umbrella ## Resources - website for presentation: https://shiny.rstudio.com/py/ - https://shiny.rstudio.com/py/docs/reactive-mutable.html - https://www.shinyapps.io/ - https://huggingface.co/new-space - https://shiny.rstudio.com/py/docs/shinylive.html - https://shiny.rstudio.com/py/api/reactive.poll.html#shiny.reactive.poll ## About the Event Shiny makes it easy to build interactive web applications with the power of Python’s data and scientific stack. If you want to develop a python web application you usually need to choose between simple, limited frameworks like Streamlit and more extensible frameworks like Dash. This can cause a lot of problems if you get started with a simple framework but then discover that you need to refactor your application to accommodate the next user request. Shiny for Python differs from other frameworks because it has tremendous range. You can build a small application in a few minutes with the confidence that the framework can handle much more complex problems. In this workshop we will go through the core limitations of Streamlit, and build a Shiny app which avoids those limitations. ## Timestamps 00:00 Welcome 00:23 Reshama introduces Data Umbrella 03:45 Reshama introduces Gordon Shotwell 04:21 Gordon Shotwell begins 04:29 The motivation to develop Shiny for Python 06:05 The main strength of both the R and Python library 06:56 What Gordon Shotwell will build during his presentation 07:25 Shiny documentation website 08:01 QuickStart for R users showing differences between the R and Python libraries 08:44 All the function reference in Shiny 09:08 Demo starts 09:50 Virtual environment 10:36 How to start shiny app in the terminal 11:15 Install shiny extension in VS Code which makes it easier to preview the web app 11:36 How the output function works on the preview app to execute 12:22 Penguin dataset description for the demo 12:45 Modules/submodules shiny app is built on 13:04 How to add a sidebar layout (sidebar, panel sidebar and panel main) 13:43 How to read in the data and the output functions 14:31 How to define some server logic 14:59 The conventional shiny rule 16:30 Use of slide input 17:50 Where the reactive magic comes in 19:30 Important note on what can really slow down your shiny app 20:14 Importance of Python data copy method when using external dataset 21:01 Important note to avoid dependency inside the render function 21:30 Q&A 29:35 Adding a plot to the output: The UI sides 30:12 Adding a plot to the output: The render sides 32:16 The core principle of reactivity in which you do not want to repeat yourself 33:26 Reactivate calculation concept which allows you to store intermediate values in one place 37:24 Q&A 38:53 Reactive calculations and rendering functions 39:30 Side-effects or user effect. Another class of interactions 41:18 How to tell reactive effect what it should respond to or what events to watch before executing 41:53 How to update the data filter in the side-effect function 42:22 The second important pattern for shiny 43:00 One of the important things to pay attention to once you start learning/using shiny 44:45 Series of Q&A until the end of the video. Some response includes live demo 01:01:03 Gordon Shotwell ends his presentation 01:01:17 Reshama closes the session ## About the Speaker Gordon Shotwell is a Software Engineer at Posit. He's been using Shiny to solve business problems for the past ten years. - LinkedIn: https://www.linkedin.com/in/gshotwell/ ## Key Links - Transcript: https://github.com/data-umbrella/event-transcripts/blob/main/2023/78-gordon-shiny.md - Meetup Event: https://www.meetup.com/data-umbrella/events/292848290/ - Video: https://youtu.be/pXidQWYY14w #python #deployment

image: thumbnail.jpg

Transcript#

This transcript was generated automatically and may contain errors.

Hey everybody, welcome to Data Umbrella's webinar. I'm going to do a brief introduction. Gordon will do his presentation and if you have any questions, ask in the Q&A and we'll stop when it's a good time to stop per section and get the questions answered.

This webinar is being recorded and will be available on our YouTube within a day or so.

A little bit about Data Umbrella. We are a community for underrepresented persons in data science. We organize data science and open source events for the community and we are a non-profit organization.

This is our team. I'm in New York. We have Beryl, Christina, Sandra, and Sangam. Christina is in New York, but Beryl, Sandra, and Sangam are in three different countries, so we are truly global.

We have a code of conduct and we thank you for helping to make this a welcoming professional community for all. There are various ways to support Data Umbrella. The first and foremost is to follow our code of conduct. We would love it if you could share Data Umbrella events with others, either personally or on social media. There are ways to contribute to Data Umbrella and our open source projects.

We have video timestamps where people watch the video and give timestamps per topic and that helps other users find the videos. We have a new website as well which is hosted on GitHub and we have sprint websites for PyMC and Scikit-Learn. We also have a blog which is hosted on GitHub and we have a data events board. You can also make a donation to our non-profit. We are an open collective. If you work for a company that uses Benefity, we are able to receive company-matched donations as well.

Subscribe to our YouTube channel. All of our webinars for the past three years are there and we have a really nice library of various topics in data science, Python, R, and open source. One of our playlists is career advice. Other playlists are DataViz, Open Source, Scikit-Learn, NumPy series, a lot of great stuff to check out.

We also have a monthly newsletter at dataumbrella.substack.com. We send it out once a month. If you want information about our events, that's a great place to find out about them.

Our website, this part is being updated resources, so it's not quite ported over to the new website. We are on all social media platforms as Data Umbrella. Meetup is the place to join for upcoming events. YouTube is a place to go for past events and videos and I mentioned the blog newsletter and we're on Twitter and LinkedIn as well.

We use BigMarker for this platform that you're watching right now and so it has live captioning. So if you go to the top where it says CC, closed captioning, and you click on it, you can see a live transcript at the bottom.

We also have a feedback form which I will post in the chat. And if you have feedback on this event or if you experience any technical issues you want to let us know about or if you have suggestions for future event topics or if you would like to speak at a Data Umbrella event, let us know.

Our upcoming events on May 17th, which is two weeks from today, we're having Solving NLP Tasks Using Large Language Models. That's hot in the news these days. And in June, we're having an Introduction to Image.io, which is a Python imaging library. And on June 14th, we're going to have Intro to Julia and their libraries for machine learning and data science.

Today's talk is Shiny, a Powerful and Flexible Tool to Create a Python Web App. Gordon Shotwell is a software engineer at Posit. He has been using Shiny to solve business problems for the past 10 years. Wow. And you can find Gordon on LinkedIn and Twitter at gshotwell.

Okay. With that, I'm going to hand over the microphone and screen share to Gordon. And I will post the link to the app that he shared earlier in the chat.

Introduction to Shiny for Python

Hi, everyone. Thank you so much for having me. I'm thrilled to be here to talk about Shiny for Python.

And I just wanted to start by motivating, I guess, the project of why we went ahead and ported this really successful, longstanding R library over to Python. And for me, that came from my last role. I was a data scientist at a company called Socure that does identity verification for banks and credit cards. And when I started at Socure, it was an R shop. And then about halfway through my time there, they moved to standardize on Python. And so part of my job was figuring out how we were going to port over the various kind of R web applications that we had developed over to Python.

And what I discovered was there wasn't really anything in the Python landscape that could do what Shiny could do. And in particular, the missing feature was what I would call programmatic range or the ability to solve very, very simple problems quickly, but also scale up to solve complicated or customized problems.

So when I was looking at tools like Streamlet, Streamlet does a really wonderful job at the first sort of five minutes of building an application, turning a script into a web app. But over time, as your problem gets complicated, if you want to make your user interface a little bit more nuanced or, you know, customized, if you want to add a lot of expensive computations, Streamlet gets really painful. Similarly with Dash, you know, Dash has a great feature set around deploying things really efficiently. But when you're just starting out, there's a lot of boilerplate. And similarly, some limitations in terms of just how Dash makes you program your application, which is maybe not always how you want to do it.

So Shiny, I think the main strength of it, both in our library and the Python one, is that the same, you learn a few core concepts to build a simple application. And those concepts will carry you through your whole web application journey. So you can build websites which are really quite customized. You can customize them with CSS or JavaScript. But even just in the base Python implementation, you're able to build the dynamic user interfaces, complicated user interfaces, or manage really expensive computations like fitting or scoring a model or doing a large database manipulation.

So Shiny, I think the main strength of it, both in our library and the Python one, is that the same, you learn a few core concepts to build a simple application. And those concepts will carry you through your whole web application journey.

So what I'm going to do in this presentation is I'm going to kind of build a web application in Shiny. And as I go through that, I'm going to teach you these three or four concepts that are the core features of the Shiny language that you need to build both simple and complicated things. And hopefully that will motivate you to give it a try yourself.

Documentation and getting started

So before I get started, I just wanted to point you to a couple of parts of the documentation. This is the Shiny website. So we have a great set of documentation of just learning from the start how to build these applications. And we have these little WebAssembly examples, which I just love. So these run in browser. And you can make changes to them and have them run. And you don't need a server in the background to run these examples.

If you're somebody who is familiar with the R library, the Python library is extremely easy to learn. And we have this quick start for R users that just goes through all of the differences between the R and Python libraries and kind of give you a way of leveraging all the things you might already know about R with the Python one. So that's the first one is this how to get started thing.

We have a page of examples. Again, these are running in WebAssembly in your browser. So these are really great things to play around with and just get familiar and build up your intuition about how these applications work. And for a lot of people, they can kind of just go here and flick through these applications and figure out the pieces that they want to put together in their own app.

And then the last thing is just the function reference. So this is the reference for all the functions in Shiny. If you're wondering, is there a function to do this particular thing, going to this page and searching is a really good option. And then, of course, you can always just ask me on Twitter or LinkedIn, and I'm happy to also point you in the right direction.

Live coding: building a Shiny app

So just going to get started with some code then. And I'm going to try to go slowly here. But if you have questions, please ask in the chat, and I'll try to pay attention to slow down if things go too quickly. And we're just building something really simple.

So this is a sort of lean forward presentation where I'm hoping that you'll kind of learn some of the details of how this framework works and develop a little bit of an intuition about what to do, what not to do, and how you should go about structuring it. And I'm also probably going to make a lot of mistakes. So because live coding, of course, is fraught with mistakes. So that will just help everybody feel better about themselves.

So the first thing that I'm just going to emphasize with this is something that I think should be true for probably most Python projects. But especially when you're building a Python web application or something that you're planning to deploy to another environment. The first thing is to always try to use a virtual environment when you're developing. Whichever virtual environment system you like to use is up to you. But that will really make your life a lot easier.

Getting into the habit of using a virtual environment when you're doing Shiny app development or Streamlet or any type of app development will make your life a lot easier as you're moving forward. So I've already done that. Just in the interest of time, I have a virtual environment with the libraries installed that I'm going to be using. And so I have the Shiny library, which has a little nice command line interface. And so the first thing I'm going to do is I'm going to go Shiny create dot. And this is going to just create a little working web application or working Shiny app that I can investigate.

So this is what it looks like. You know, this is the simplest, as simple as it can be. So we have two main components. There's the UI, which is what determines how the user interface of the application. And then there's another function that's called server that creates the logic of what that application does.

And in VS Code, we have a Shiny extension, which I'm going to be using. You can install this in the extensions library if you use VS Code. And I'm doing that because it just makes it a little easier to preview the app. So we have this little app preview here. And you see, just like kind of expect, we have a little slider. And when I change that slider, that causes this output function to execute to generate this text.

So if I change this to n times three, this to change the number to three, and then I save it, it'll then change that.

So that's kind of the hello world thing. And I think it's good to start here to just give yourself confidence that you have something that can run. So even though I've been working with Shiny for many, many years, I still always start this way, where I just start with generating something so I know that all the brackets are in the right place and everything like that.

But we're not going to be building just a little sort of toy app here. We're going to be building something based on this Palmer Penguins data, which is some little nice, small, easy to think about data about penguins. They're adorable. And so I'm going to be doing basically a dashboard with a couple of user interface things and one table and two graphs is my goal.

So I'm just going to get started by just deleting what's up here. And the Shiny library is built around these kind of modules, submodules. So we have one that's render and one that's UI. So all of the UI components are in this UI module. And this is nice because it means that you can tab complete pretty well.

So the first thing I'm going to be doing is adding a sidebar layout. And this sidebar layout is going to give us just a little sidebar and a main panel.

And in this sidebar panel, the first thing that I want to... the first thing I want to do here is just make sure I can read in the data. So in my main panel here, I will do UI table output table. And so these are what are called output functions. And output functions just say like here in the user interface is where I want the output of my server functions to go.

So this is the ID of this table element. So the next thing we need to do is define some server logic that's just going to put something in that output place. And how we do that is... I'm going to do this a few times, so don't worry if this is at all confusing. So we have these two decorators. The first one is output, which just tells Shiny that this is an output. And the second is in the render module. And this just tells us, like, what is the type of output? So is that a plot, a table, an image, text, or a UI element?

The next thing we do, and this is just the convention with Shiny, so we kind of just need to learn this rule as you kind of work through this, is we have... we define a function. And that function has the same name as the ID. So if we have an ID, this UI element, summary, we're going to need a function that's also named summary. And this is what allows Shiny to connect whatever comes out of this function to this UI element.

And we see that in my application, I just have this table output of that data frame. So what's going on here is that when I read in this data, it goes and tries to render this summary thing. I was like, okay, to do that, get that output, I need to call this output function. Runs that function, reads in the data set, returns it.

So this is just, again, basically, like, developing confidence that, like, things work. You know, I'm calling things right. I'm kind of reminding myself of how the syntax works. But it's not terribly useful.

So we're going to be starting with a slider input. And the slider input, we're going to have a few things. So the first one is ID. It's going to be mass. It's going to be a slider to filter out... to filter the data frame for just, like, penguins bigger or penguins smaller than a particular size.

And when I look at my application now, you see I have this... I have two things. I have a data table output, and I have a little slider. And the slider kind of doesn't do anything right now. So we haven't connected it to anything. So it just is a little slider that we can fiddle with.

So the next step, and this is where the reactive magics comes in, is we're going to call this... refer to this new input in this server function, in this rendering function, to filter this data set to only include the penguins that we're interested in.

So the way we'll do that is we'll do... we'll use some pandas. CF lock. We want... is mass in grams. Bigger than... and this is where the... so this is the important bit here. So the inputs... all of the inputs are stored in this input object, and each of the labels... so this, like, mass here is a method on that input logic that you can call to get the current value. So right... so at the beginning, this will be 6,000, whatever the value is that we set. But as the user changes it, it will do two things. The first thing is it'll re-execute this function every time he changes... every time they change the mass, and then it'll pick up the new value.

So let's just make sure that's working. So we have 6,000. And you see as I move it, the table is changing. So it's re-rendering each time.

And just in the chat, can anyone think of a problem with what I've just done? So in particular, this step here, line 20, has the potential to be a real slowdown, right? Like, I told you, every time I change this slider, because I'm referring to the input here, it's going to re-execute this whole function. But this CSV function file is actually not changing for our application. Like, this data was collected a long time ago. It's not going to change. So I actually don't want to have this part inside the render function. I want to have it outside the render function so that it's not going to always re-execute.

And this is a kind of important Python kind of got you, which is that I want to make sure whenever I'm using an external data set that I want to change, that I'm not modifying it in place. So what I usually like to do is just call a copy method on that data. So that basically ensures that whatever happens down here is going to be not going to modify this, like, original data frame in place.

But doing this, nothing really changed except for my application became a little bit more efficient. So this is kind of an important little bit of intuition to develop. So this code up here inside this function, this runs one time whenever the user initially loads the application, whenever there's a new session. But this code in here inside the rendering function runs every time any of its inputs change. So any of the inputs that go into this render table function change, it'll rerun. So you want to try to, if something doesn't depend on those inputs, try to pull that logic outside of the function and have it be stored as a bit of state.

Q&A: virtual environments, Python vs R, and performance

So the first one is, can you show what the requirements are for the virtual environment? So the virtual environment, there's a number of ways you can start a virtual environment. So I'm using, I think, virtualenv is the one that I use. But you can also use Conda or Poetry. And there are not really any particular requirements for it. It's doing, like, installing that package, whichever one you want to use. Basically, the purpose of it is it gives you a local copy of the libraries.

Oh, just Shiny. So if you just have Shiny, that's it. So there's not any other, there's no system dependencies. So you can build a library with Shiny and then whatever logic you want in your application. So in this one, I'm going to be using pandas and plot9. But it just kind of depends on what you want your application to do. Shiny as a library is just a standalone library, and it will generate the user interface and anything like that. It just will be limited to base Python if you don't have other packages.

The next question is, if we have this in R, why use it in Python? What would be the significant advantages of it? So the main advantage is you want to use Python. So you can – so the R library is more mature, of course, just because it has a longer history. And I love it. It's great. It's not going anywhere. And so if you're working on, say, you're working with team members who are using Python, and you want to have something where everybody can work on the same application, and they might not know R or be interested in learning R, that's one benefit. If you're wrapping, if your application is mostly a wrapper around some Python code, like you're using a large language model or something like that, it'll usually be a little bit easier to manage those dependencies in a pure Python application using Shiny for Python than using something like Reticulate.

The next question is, my R Shiny apps tend to be slow once I start dealing with larger sets of data. Can I expect this to be improved when using Shiny for Python? Probably not. So the R data library and data manipulation, it's about the same. Like you have some fast libraries like data table, you have some slower ones like base R, dplyr is sort of in the middle. In Python, it's the same. So you have pandas, which is a little slower, pollers, which is a little faster. So I would sort of focus a little bit more on the library that you're using to do the data manipulation. And if you're running into speed problems in an R app, you might switch to something like DuckDB or Arrow, which is a little bit better at handling large data sets faster. You probably won't notice a big speed up just by switching the Shiny library.

The next question is, what are the benefits or drawbacks of using Shiny for Python versus other libraries in the same sort of field, such as Streamlit, Dash, Panel, and others? Yeah. So the main benefit for using, so I'll just talk about Streamlit first of all, because I think that's probably one of the most popular ones. There are kind of different reasons for the different applications. So Streamlit, the main one is that Streamlit re-renders everything from start to finish all the time. So if I had this in Streamlit and I changed this slider, the whole script re-executes from the beginning, every single thing. And you can use caching or some other tools to try to mitigate that problem. But in general, it's going to re-render everything.

Whereas with Shiny, when I make this change, the only thing that re-executes is this small bit of code. So it means that as your application grows, it kind of naturally stays efficient. Whereas with Streamlit, as your application grows, you need to start manually getting involved with caching to sort of make sure that it doesn't just get really, really slow or difficult. And I think that creates a lot of complexity for the developer, because they're sort of managing performance or what we call control flow in a manual way, whereas Shiny is kind of handling that for you.

For Dash, the main difference is that Shiny has server state. So this thing here of reading in the CSV, and I saw a question about, maybe I should put that up at the top of the script, and that's correct. You can put that up there as well, and that's totally fine. It'll be maybe about the same, but it'll work. This won't work in Dash. So in Dash, I would have to have each rendering function either read in the data itself or have some kind of way that they can share cache or some kind of external database to handle that. So Dash has this idea of stateless callbacks, they're called, and that just creates a lot of awkwardness, I think, for a developer, because most of the time when you're writing, it's natural to just have some sort of variable at a higher level of scope and have your functions scope up to find that one.

The next question, and I have an add-on question, is, what is the reason to add the file inside the function versus adding it to the top of the file, maybe similar to a global variable? Yeah, so you need to have this inside because otherwise it won't re-render when you change the input. So this has to be in this rendering function when you want it to react to a particular piece of input. So this could go higher, like at the beginning of the script up here. The main difference is that when you have it here, this will rerun whenever the new user logs on to the application, whereas if you had it up top, it would only rerun when the application was redeployed. For your case, it might be different. So you might want to have something where you're able to change the data dynamically without redeploying the application, and then it would be better to be in server. If it's actually really static like this, you might put it up here. It doesn't particularly matter because those re-renderings happen more or less the same. I put it here just because it's easier to see, basically.

Adding plots and reactive calculations

So right now this is still not that exciting. So I'm going to change this a little bit to do a count, just so it kind of makes this table a little bit smaller and also maybe more informative.

Now we have just the count, which makes it a little easier. And someone pointed out I made an error. I had this comparison going the wrong way, so this should have been min body mass, but I meant to do a max one. So this shows the different species and their size.

Okay, so next thing I want to do is I want to add a plot. And so this is kind of where, again, I'll be doing the same thing a few different times, so hopefully it'll kind of get a little bit clearer.

And when you kind of have these elements nested inside of some sort of container, they just kind of go underneath each other. But I haven't actually drawn the plot, so there's nothing down here except for blank space.

So down here, I'm, again, going to be using my output, have a render, and this time I'm going to pick a plot. And since I'm doing the same filter, I'm going to copy this. And then I'm going to call this plotDF just to make it a little bit clearer to see the difference between them. And then in the interest of time, I'm just going to go ahead and copy over the plotting code that I've written previously.

So the – I apologize if I was skipping over a little bit of the plotting stuff. The basic idea here is that I've inserted a plot, and this plot is responding to the sidebar changes in the same way, with the same filter as the table that we just did. So this kind of works the same. I'll just walk through it a little bit more. So we're doing the same thing we're doing here. We're taking a copy of the dataset. We're doing the filter, same filter as we did up here. So this is the input that we're referring to. So that's going to be the one that's causing this function to rerender when that input changes. And then we're drawing a plot. And this can be any plotting library for the most part, or not every one, but most of the ones that you use commonly. So like Matplotlib or Seaborn, various different things will work here.

But there's a problem. And this is a kind of core issue with a core principle of, I guess, reactivity, which is that with reactivity, you really don't want to repeat yourself. And there's two reasons for that. The first one is that you want to keep your code encapsulated in one place. But the second one is that it actually improves your application's performance if it's not repetitive.

So right now, because we have these two filterings that are happening, whenever I change this slider, it's actually doing the same filter of the dataset twice. It's doing it once here when it renders the table, and it's doing it once here when it renders the location. And we can see this with a little flowchart.

So this is what it's looking like right now. The slider changes. There's a filter happening. And then I'm generating the summary table. Slider change, filter happening. It's generating a histogram. And Shiny is clever with this graph. This is actually how Shiny does the computation, where it knows that if this changes, it needs to render everything that's downstream of that change. It needs to do everything, like, that's downstream of that change. And it'll kind of do it in order.

But it has a concept called reactive calculations, which allows you to store intermediate values and refer to them, write them in one place, and then have them only execute in one place and return the result of that execution to anything that's downstream. So what we're after is we're after a plot that looks like an application flow that looks like this, where the slider changes, there's one place the data filtering happens, and then that filter data is passed on to these two histograms or these two elements.

So to get that to work, we need to sort of add another module up here. So we'll add this reactive module. And what I'm going to do is I'm going to create a new similar to the other functions I have, but instead of decorating it with render table and output, I'm going to be decorating it with this reactive calc object. But otherwise, I still refer to this data. I can create a function with any name. I'm going to call it filter data.

And just like I did with the inputs, with this, like, input.mass, I'm calling that as a method. I'm going to call this filtered data function. And what this will do is it will return whatever is kind of resulting in here, and the reactivity, like, making sure that when this is determined that it needs to change, what will happen is – I'm just going back to my graph here. When there's a slider is going to change, that will cause this filter data calculation to re-execute. And that in turn will cause the summary to re-execute and the histogram to re-execute.

And yes, okay, so I wasn't expecting any change to my application. So what I'm doing is kind of bringing out that – I'm using the filter data reactive in the table. And then I can do the same thing with the histogram.

Okay, so what we've accomplished here is we have this single place that we're doing this intermediate calculation. And then we're passing that to a number of rendering functions. Or that is being called by a number of rendering functions. And this is, again, something that differentiates Shiny from Streamlit and Dash. Like, neither of these things is really possible in those languages.

And what's important about it is that this kind of works well for this sort of simple thing. But imagine if you had, like, 100 components or more. And you're in a large multi-page application. Shiny being able to keep track of this computation graph means that that application is going to be very efficient. Because it's only going to re-render the parts of that thing that are kind of downstream of that graph. So it's similar to a makefile or if you've used a pipelining tool. Where it's just only calculating the things that it absolutely needs to calculate in order to give the user the updated components.

Shiny being able to keep track of this computation graph means that that application is going to be very efficient. Because it's only going to re-render the parts of that thing that are kind of downstream of that graph.

Reactive effects and side effects

There's one question. I don't know if it's related to reactivity. Can we relate the scroll bar with the information in the table? For each location in the bar, we have the rows of a color that highlights? Yes, I think so. So the nice thing about this is that it's very generic. So if I had, say I'm generating this table. I have access to this number that comes out of input mass. And so I can generate any table that I want to as a result of that. So you can do formatting on this table using the pandas formatting framework.

The idea behind this library is that we're giving you the sort of bricks that we think you need to be able to build a wide variety of applications. And then for the most part, once you kind of get into it, since Shiny doesn't really care what's in this function, you can sort of do whatever you want to with it. So long as you have some Python code that can generate the output that you're looking for, for the most part, it'll work.

So this is one of the big things. It's just like reactive calculations and rendering functions. And in general, you want to use these whenever you want the value of a function. So here we're doing some calculations. And the thing that we're interested in is the value that comes out of that. The return value of the function. So this should be using reactive calculations or rendering functions.

But there's another class of interactions that you want to have with your application that are called side effects. So for example, if I wanted to update this slider or I want to download a file or, you know, send a query to a database or something like that. Sometimes you want to have a button that you're clicking. And that button is not really returning a value. It's like doing it's creating some sort of side effect. So there's another pattern in Shiny for that.

So we're going to add a UI action button. And this is going to be called ID reset. And reset filter. Okay, so we have this little button. And instead of using reactive calculation, we're going to use another decorator from the reactive module, which is reactive effect. This is what you want whenever you want to get a side effect of user action instead of the value. And you're still using a function definition here to decide to find what's going to happen there. But this can be this doesn't really connect to anything. So like here we were using filter data because we needed to call that function later. We're not really going to be calling this reactive effect. So by convention we use this sort of underscore.

And just to show you that this is actually working. This is a good practice whenever you're doing these types of reactive effects. Just to just check that in the terminal that things are working as you're expecting them to.

Oh, and see it's not working as I'm expecting it to. Because I actually didn't tell this reactive effect what I should respond to. And the way I do that is I have this reactive event. And this just basically says like what is the thing you want me to watch to decide when I should run.

So let's try this again. Okay, great. So now I can push this and I see that it's whenever I push that function. I'm getting a new pushed value there.

Okay, so we're going to be updating this filter. So the function that I used to do that. I have these bunch of different update functions that allow you to update other UI elements. So I'm going to need the ID of this function, this slider, which is mass. And then all you do is pass in the value that you want to update.

Okay, so let's just take a look at how this works. So now if I make this change and then I hit reset, it goes ahead and resets the filter. And then importantly, when the filter is reset, all the kind of downstream things get recalculated properly.

So this is the sort of second pattern. And one of the things that I think is really important that I've seen people get into trouble with before when they're first starting to learn Shiny. Is Shiny is a framework that rewards you for trusting that it's going to do the updating properly. So for the most part, your rule should be Shiny is going to handle updating things. And figuring out like when to update stuff and when to re-render things. All I need to do is follow these sort of patterns of making sure that I'm kind of linking inputs and outputs. And referring to the either reactive calculations or the input values. And then Shiny is going to handle how your application is going to flow and make sure that things are up to date.

So you don't really need to do those manually. The one case where you do kind of need to do that sort of manual, like when this happens, do this. Is when you're looking for a side effect. For the most part, if you're asking for the value of a function, the return value of a function. You don't really need to think about how things are re-rendering or executing. The only time you need to think about that is when you're pressing a button or doing something else where you're actually asking for a side effect.

And that's kind of one of the core things. I see a lot of people where they're trying to build a Shiny app basically by having a ton of reactive effects and reactive events. And manually updating this thing. So you would have a reactive effect that watches this input math and updates a bunch of values whenever that happens. And that's not how Shiny works. Shiny is a declarative framework where what you want to do is just sort of write your application. And assume that the framework is going to go ahead and do the right thing with all of your values.

Q&A: mutability, debugging, and deployment

Why do penguins.copy instead of df.penguins.loc? So the main reason is like so Python does something called like Python objects. Some Python objects can be modified in place. And what that means is that if I just referred to penguins or just df equals penguins and then ran this like filter. It would actually filter this object as well. So R has like what's called copy on modify for all values. Which means that when you do an assignment actually copies the data. So like filtering or changing the copy data won't change the original. But in Python that's not true.

We have a really good it will cause a lot of problems for Shiny because you're referring to this penguins data a number of different places with a number of different reactions. And you can run into problems if you didn't realize that you were modifying the original one when you're doing it. So this like calling a reactive will do that copying for you. So that's another good reason to use it. So whenever you're in a place where you are actually modifying something and it's a mutable object you should copy it.

So will Shiny work with GPUs? Yes. If you have Python code that works with GPUs Shiny should work with it. Same thing with multiprocessing and multithreading.

I think I'm pretty much kind of I had another thing I could talk about but I think I'm probably going to run out of time. So I think probably we should just leave it there and just I'll leave some time for other people to sort of ask anything they want to know about Shiny or Posit more generally.

So while we wait for questions I do have a question which is I see that there's a gallery. So how would you say the best way for people to begin is to like maybe take one of the gallery examples and edit them. Yeah. So there's the examples page which is a great place to start. But I don't know that you need to I would sort of start with a problem. That's how I usually go for it where it's like you have something where you have like a Python script or something you want to like work into an application. And start just very basically just in the same way that I did like start Shiny create. Get at something that's running making sure you can run the library. And then work from the problem. Like what type of interaction do you want to use.

And then looking through those examples on the website is a really good way of sort of getting a sense of like what's the scope of interactions that we can do. You know ask questions like tweet at me. I'll get help with some feedback. But the main I find it a lot more rewarding to start with something that you're actually interested in rather than just working through a bunch of examples. Because again Shiny does have a very smooth learning curve. So it's a really nice you don't necessarily need to do like a ton of education before you can produce something useful.

Deployment options

Oh yeah sharing Shiny that's a great question. Let me share my screen and I'll tell you a few different places where you can do that. So there's a there's a so there's a few deployment options. We have we have them on our website here. So there's obviously there's like a couple of there's the free ones and then there's the hosted ones.

So shinyapps.io this is the by far the easiest way to share your applications. So so if you're this is like similar to to stream the clouds or something like that where it's like a free hosted one click deployment to to this website where you can deploy something and and see it. If you're if you're an organization which uses so we have a free tier which gives you basically five applications and 25 active hours a month. The applications like automatically start and stop. So most people never get close to that 25 hours. So that's one place that you can share your code.

And if you're if you're using Shiny for R this then you maybe your application your organization has some place where they're deploying Shiny for R. Almost all of those will let you deploy Shiny for Python as well. So that's one option.

Second option is shinylive. So shinylive is the WebAssembly tool that we use to show you how to host our documentation. This is wonderful because you can host Shiny applications just on a static site like GitHub pages or Netlify or anything like that. Or you can even like if I go to this examples you can actually share right from here. So this is the shinylive website where you can edit a Shiny app add something upload something from your computer and then share it just with the hash thing and it runs in the browser. So this is another great option.

And then the one that just launched that I'm proud of was involved in is on Hugging Face which is a ML community. We have these templates for Shiny for R and Shiny for Python that lets you deploy them using Docker. So that's another option.

So one of the nice things about Posit as a company and Shiny as a product is that we have a long history of working with organizations deploying their Shiny apps using Shiny Server Pro, which is open Shiny Server, which is open source and Posit Connect, which is our one of our enterprise products. So if you're working in a highly regulated environment or have a lot of security concerns, you need an air gap place to put your super secret Shiny apps. We have tools for all of that. There's we have we're committed to open source. We have a lot of free stuff like this. This application is free, of course, while a lot of our hosting options are free. But if you are somewhere where you do want to have like I need to pay for something because I need to make sure my data is really secure. We have a tool for that.

So the next question I'm seeing here is what would be a proper way to debug when building Shiny? That is a great question. So there's a few different options. So one is print statements like I did with that pushed where you're just typing and print to making sure something is printing properly. The other one is this is right. This is in the. Yeah. So it's PDB set trace. So that so when you like import PDB, this is similar to the R browser function where it will basically just stop my execution and give me a terminal in that spot.

And then that way I can like actually call my reactives to see what the value is of that space, which for this application, not that helpful because it's so simple. But if your application is complicated or there's multiple inputs going into a rendering function or reactive, it can be really helpful to actually see like what the different values are and figure out like where your things where things have gotten confusing.

So generally what I do is I will have something where I'm rendering to the console, like sometimes you do text or a table like it initially just make things sure things are going properly. Printing stuff and then PDB is kind of the last option. And to get out of PDB, you have to exit it like you're exiting Python. So it's not the best, but it does work.

The one thing in terms of debugging and just overall advice is that it's important to like take things step by step. And one of the other good patterns that's helpful is to write a function that works outside of Shiny. So, for example, say I was writing this plotting function. I might instead of having this code in Shiny, I might have some other place where I'm just defining the plotting functions like and then just calling, you know, I might have a function that just was not a reactive function that was just took some data and generated this plot.

And the advantage of this is that it really simplifies like of course anytime you're running something in a reactive context. It's more complicated. Like it creates some complexity. And making sure that your plot drawing function is actually working properly. Then that's the way to like just sort of minimize the parts where like, oh, this is really a reactive problem. That's just like I didn't understand how to use pandas, which is like 90% of my Python issues.

So we don't personally like Posit doesn't do consulting. But we have a lot of partner organizations. So, Trudy, if you want sorry the question just to repeat was I need professional help. Can I engage your services? So Posit we don't do consulting organizations. But we have a number of partner organizations. If you wanted to send me an email just with the request, I can put you in touch with our partner coordinator. And she can find a consulting firm for you in your geography.

One more question about can this link up to a live database so that it's an up to date data set rather than a static one? Yeah. So there's a few different things that help with that. So firstly, so in general, like if you can do it with Python, the expectation is you should be able to do it with Shiny. And you should be able to do it reactively. So if you can connect to a snowflake with Python using the snowflake connector or any database, you should be able to connect to that through Shiny.

So for example, this penguins dataset, like if I had this as a database query instead of a CSV, like if the data was in flux, I might actually want to have that down here. So in that case, like having up to date data might be more important than like it's more important to have the correct data than to have the data that's being kind of like minimally re-rendered. We also have this function called we have some functions called reactive like reactive file reader and reactive poll. And that's basically a way of that's a way of like handling like building something that like only like you might have something where it's like I only want to run the expensive database. Like I have a cheap database query that just checks if there's new data. It's really fast and that's running all the time. But then instead of it's only running the like expensive actual query, if that's quick query is if I determine that there is new data in the database, I might check like what's the last time stamp? Is that, you know, is that too is that recent or not? And then run the full query once you determine that that's true.

So, yeah, so Bokeh, I actually haven't used that enough myself to give you a really strong answer. So the question is very similar to previous question, but I've only used Bokeh for similar apps. Can you compare the two? A few people seem to use Bokeh and I don't know why. So I haven't used Bokeh enough myself. I've used it as a plotting library, but in terms of how it's used by panel under the hood as an application framework, I don't have a great answer for you.

I would say that one of the main differences and benefits for Shiny is that when you're working off of this, this UI, like this is actually just an HTML site. And what that means is that if I have HTML assets like CSS files or JavaScript, I can apply those really easily. And that can be really powerful as your application grows, because oftentimes you may be working in an organization that has some skill in some of these web developments and you can ask other people to help customize things or you can move.

So it's kind of like a little bit more of a generic web as more generic web tools, which is really helpful for doing that stuff. So I think this is one of the reasons why panel is recently like changing how they're using Bokeh to not be the main kind of rendering engine because of like having it be a little bit limited in terms of customization.

So you can use Plotly with this as well. Plotly works great. So we've kind of tried to do some of the main ones. And on the website, there's some examples of a lot of those. Plotly works great. Anything that you can wrap in an IPI widget is pretty good, although we haven't tried every single IPI widget, so there might be bugs. But we've sort of tried to. One of the people who works on our team is very involved in Plotly. So that one is pretty good. And then Matplotlib. Anything that uses Matplotlib as a rendering engine.

Okay, so I think we're just about at time. So I just want to thank you all for joining me. It's been great to chat with you all. And if you have other questions, you can reach out to me on the social links that we've mentioned before.

Great. Thank you very much, Gordon. And we've gone through all the questions. And for everybody joining, thank you very much. And the video will be posted with the links as well will be posted in the video description.