
James Blair || Getting Started with {plumbertableau} || RStudio
00:00 Introduction 01:19 Setting up the problem - capitalizing text with a custom function 02:18 Using Plumber to create an API for our function 04:08 Using Run API + Swagger from the RStudio IDE 05:44 Giving Tableau access to the function with PlumberTableau 09:16 Reviewing what we've done so far 09:47 Comparing results between Plumber and PlumberTableau 10:12 Overview of what PlumberTableau does 14:27 Centralized hosting with RStudio Connect 15:17 Looking at our API in RStudio Connect 18:14 How to access the deployed API from Tableau 21:03 Overview of RStudio Connect, Tableau, and PlumberTableau process 21:52 More in-depth example using sample sales data 22:36 Example with the Python equivalent of PlumberTableau, FastAPITableau 25:15 Overview of how these Tableau extension packages work 27:21 Setting up a connection between Tableau and RStudio Connect Read more about the plumbertableau package here: https://rstudio.github.io/plumbertableau/ And learn about the fastapitableau package here: https://rstudio.github.io/fastapitableau/ If you’re unfamiliar with Plumber, this Quickstart guide gives a good overview of the package: https://www.rplumber.io/articles/quickstart.html And you can learn more about Shiny here: https://shiny.rstudio.com/ Got questions? The RStudio Community site is a great place to get assistance: https://community.rstudio.com/ Content: James Blair (@Blair09M) Design and editing: Jesse Mostipak (@kierisi) Music: Borough by Blue Dot Sessions https://app.sessions.blue/browse/track/89821
image: thumbnail.jpg
Transcript#
This transcript was generated automatically and may contain errors.
So the kind of the goal here is to just walk through like a really simple use case of plumber tableau to highlight a couple of things one how it relates to plumber and how it kind of extends what plumber can already do and then the other piece here is to just kind of showcase like what what is plumber tableau trying to solve from the developer perspective so as an as an R user what what do I get out of this tool and then the other side it's like as a as a tableau developer or tableau user how do I leverage something that's been developed in plumber tableau what's the what's the workflow and what's the pattern for using this once it's been developed so that's that's kind of the goal here I'm all I think there might be a few assumptions about kind of prerequisite plumber knowledge but I'll try to kind of highlight some of the main ideas if there's some unfamiliarity there but just like I said the use case here is really simple but I think it will help us kind of ignore or not necessarily ignore but it'll give us something concrete that we can work with that's not overly complex so we can focus instead on how the the tool is working rather than what we're trying to accomplish with it.
Setting up the problem
So all that being said the idea is I want to capitalize some text super easy but and in an R I could do that with just like the to upper function that's already there but to make this a little bit easier to kind of think about we're going to write our own function called caps which takes some input text and then we're going to use to upper in here on our input text so again nothing crazy but the idea is we can give this some sort of string hello world and we'll get back the capitalized form of that. Now let's say that for some reason this functionality is super critical to some tableau document that I have right I have a bunch of text and it has to be capitalized for my executives and this is the way I'm going to do it now it's a stretch but that's the that's the scenario scenario we're going to work with.
So the idea is this function right now just lives in R and what I really want is first I want to kind of make this accessible to other tools generically and then we'll worry about tableau so the way one of the ways to do that is to use the plumber package and to turn this function that we've defined into an API.
Using plumber to create an API
So I'm going to bring in plumber now and then plumber works by using special comments to change existing R functions like what we have written here into API endpoints that can respond to common HTTP requests so it's it's nice in the sense that I don't necessarily have to write a bunch of new code to turn this into an API I just have to add some new comments to do that so we're going to come in and the special kind of signifier for plumber is the standard comment symbol followed by an asterisk and then there are these kind of decorators that begin with the at symbol that we can use to define different pieces and components of what we want plumber to do and how we want it to handle the functions we define.
So I'm going to create an API title here we'll call this simple capitalization example we'll give it a description capitalized some text okay so these are this is just a generic kind of title description for the API and collection of different endpoints in our case we're only building this one function this caps function so I'm going to create a little description for this this is capitalized input text and return the result and then here's kind of the significant part here's where I define okay we're going to listen for post requests at the capitalized path so here's here's kind of the significant part where I where I tell plumber look respond to these types of requests at this location and whenever a request comes in that matches that criteria this function will be executed with the contents of that request on a very kind of simplified way.
Running the API with RStudio
So I'm going to just go ahead and save this here so that we can use some of the RStudio nicety so now once I've saved this as a plumber file you'll see that RStudio gives me this run API option and if I select this here we get this cool little interface that pops up in our viewer pane and this is a UI user interface that's been generated dynamically by plumber in combination with swagger which is the rendering tool that's been used here and this shows us you know here's our here's our title here's our description and if I change the values right we see capitalize some text is is what we see over here and if I went back and changed that and reran this we'd see the change updated here and if I come down here to capitalize we can expand this out we see our input text here we see an info some information about the different types of responses we might get back and I can actually go ahead and try this out we can say hello world and we can hit execute and there we go we have our response back a capitalized form of what the input that we provided.
Awesome now at this point like we've done like 95 of the work necessary to get this to work inside of tableau and and in fact if we just take a step back and kind of ignore tableau for a second and think about what we've done kind of more broadly we've taken this function that we wrote that capitalizes text and we've kind of turned it into this service that can be used regardless of the tool that's using it right like I could somebody writing python or somebody writing go or somebody writing c++ or java or whatever could call out to this service and say hey here's some text to capitalize and then r would execute what it needs to do and return back the capitalized text so we've made this kind of very generic interface to our function which can be super super useful.
Giving Tableau access with plumbertableau
Now at this point if I want to get this to tableau I have into tableau there's kind of like a couple of routes I could go one is I could start figuring out how exactly tableau wants to communicate with external services tableau has a very specific way of sending requests and and a very specific way in which it expects responses and I could go through and I could build out an api that meets that criteria or meets the contract that tableau expects an api to be or I could use the plumbertableau package which does all of that work for me.
So I'm going to go ahead and bring in plumbertableau now which as the name suggests is a package that expands the functionality of plumber to make it easy to build apis essentially that that operate as analytics extensions inside of tableau so once I bring the plumbertableau package in there's kind of two things that I now need to do one is there's a couple of new annotations that I need to provide when I describe my function one is tableau arg and then one is tableau return and and as you can probably kind of guess from the naming tableau arg is where we define the arguments that we expect tableau to be providing in this case that's this input text argument and we might have multiple arguments we could have input text one and input text two or you know we could have different things maybe there's parameters again this is a very simple use case but perhaps I have a previously trained machine learning model and I'm expecting tableau to submit new data and then I'm going to run that new data through my model and generate predictions and and then send the prediction the predicted outcomes back to tableau for further analysis in which case I might have you know a dozen or more different inputs that I expect to come from tableau and each one of those can be accompanied by this tableau arg this tableau arg specification here that allows me to define some information about that argument.
So here we're going to say this is input text is the name of the argument and its type is if I could spell its type is character right and then here's a brief description this is the text we want capitalized okay easy enough. Tableau return is what is my extension sending back to tableau I first need to define what type of what data type I'm sending back so again I'm going to be sending back a character value and this is the capitalized input text okay so those two new things tableau arg and tableau return and the last piece here is I need to add this modifier down at the bottom and a modifier is simply a function that that makes changes to an existing plumber router so here the way to do that in this context is to use this plumber decorator and then we're going to say tableau extension and this is a function in fact we can just look at this really quickly this is a function that is exported from plumbertableau it's one of the only functions that the package actually exports and this and there's an example here of how you might use that in the way that we're using it here but essentially this function does all the necessary modification to the underlying api so that it matches and behaves the way that tableau wants it to and the advantage here is this means that as the as the r developer I don't need to necessarily worry about how tableau is communicating with my api I can trust that the api will communicate appropriately as long as I use this modifier once I've defined everything that I want to define within my api.
this means that as the as the r developer I don't need to necessarily worry about how tableau is communicating with my api I can trust that the api will communicate appropriately as long as I use this modifier once I've defined everything that I want to define within my api.
Reviewing the plumbertableau changes
So I'm going to save the file again just to review we brought in the plumbertableau package we added tableau arg and tableau return and we modified the api with this tableau extension function you might notice that I'm not actually executing the function and that's just kind of a semantic of plumber itself so here is is the function name or the function reference I'm not executing the function but I'm indicating call this function on the api that's previously defined and and allow that function to modify the api as it as it needs to.
With these changes in place if I run the api again I see basically the same page but there's a few kind of subtle differences one is these new links show up here these are automatically generated as part of this plumbertableau package and we won't spend time looking at them here but we will kind of in a moment once we transition to deployment but if I scroll down and open up my capitalized endpoint again now things look very different and the reason is is because one of the things that that plumbertableau does is it automatically will create and generate documentation about how tableau is going to interact with this particular extension as well as an example request of what tableau is going to submit so here this this request that we see here is in the format that tableau will be submitting requests tableau submits a json object with two kind of nested values script and data and so we see those referenced here and then the data object contains the actual data values that are being passed over from tableau.
If we inspect this we can see that that arg1 is the first value here and and if we if we look just above this this table here shows a mapping between what my function in r has defined and what tableau is going to submit so we can see the input text in my r function corresponds to arg1 in this tableau data object it's a character type value and here's a description of what that value is and it's not optional right it's a required field I can still try this out so I can click try it out I can come in here and change the value of arg1 to hello everyone execute this and if we scroll down we see the capitalized version of that text being returned back.
Now I'm going to kind of pause there and highlight a couple of things one is as the like r developer here I haven't touched tableau I haven't opened tableau I haven't needed to look at tableau I haven't needed to visit tableau's website but regardless of that I've been able to build an extension and verify that it will work with tableau because I have an example of what a tableau request will look like. Let me just open this up one more time right when I click try it out in here and run this request and say this this is a test this is me verifying yep this extension seems to be doing exactly what I expected to be doing and I have confidence that it will behave that way when it's connected to tableau so the advantage is I can do all this within a tool that I'm comfortable and familiar with in this case I'm using RStudio workbench I could I might be using RStudio on my desktop I might not be using RStudio I might be developing this in some other editor or some other form but regardless I have access to this interface either directly in RStudio or in a browser and I can test and verify the behavior of my extension without needing to either open tableau myself or rely on somebody that has tableau access.
And that was one of our goals there's you know if I if I take a step back and just look at some of the philosophy that went into designing plumbertableau part of this was to say we want to make things easy for both both um like populations here one is the developers how do we make things easy for them when we build and extend tools that they're already comfortable and familiar with which is why we chose to extend plumber uh and use that framework we also make it easy for them to test validate and confirm the behavior of things without needing to rely on other tools which is what we just observed the other side of this is how do we help the tableau user right they might not know anything about R they might not know anything about plumber or anything any of these tools they just are building tableau workbooks and dashboards and visualizations and they want access to the things that have been built here now in some organizations the R developer and the tableau developer might be the same person or might work together on the same team in other organizations they may have no idea who one another are right it might be two totally separate groups two totally separate individuals um and we wanted that to we wanted to support that type of distinction between these two roles.
Centralized hosting with RStudio Connect
Okay now at this point I have a fully functioning running tableau extension that I've built in R the problem is tableau cannot access it at this point right and now if I was running say for example I was running um RStudio desktop and I had tableau desktop I could stitch these things together like I could run the API inside of RStudio desktop I could open up tableau desktop I could configure them to speak to one another and that would be great but that's like a very limited utility because it's still just everything's still local to me right everything's still running on my desktop and I'm not gonna like pass my laptop around the department to give people access to my dashboard I need some sort of centralized hosting mechanism on the tableau side that's either going to be tableau online or tableau server on the R side the easiest way to distribute and share and provide access to this extension is through RStudio Connect and RStudio Connect is a publishing platform that supports publishing all kinds of different R and Python content including these tableau extensions like the one that we built here and in order to publish this to RStudio Connect I can select this little publish button here inside of the RStudio development environment I can quick publish a API and then select details about where I want to publish it the name of it things like that.
Looking at the API in RStudio Connect
Now I've already gone through and published this API or one very similar to it so I'm going to just open that now so I'm going to come into RStudio Connect let me navigate to this particular API and there there was no we'll notice there's a few subtle differences here in what we look at in terms of the title and description of the API but the behavior and everything like that is the same so this is this is the that same API that we were just working with now published on RStudio Connect and something that you'll immediately notice is this looks very different than what we saw before right when we ran this inside of RStudio we saw an interface that looked like this if you've done any sort of API development or API work before you're likely familiar with this type of interface it's a very common way of kind of exposing the functionality of an API as well as providing mechanisms for testing and validating the API and so this is a very kind of developer centric view of what I've built.
On the other side once we publish this to RStudio Connect we're now kind of switching gears and saying okay now we're not necessarily trying to give the developer tools anymore but rather we're trying to expose this in a way that's intuitive for the Tableau user to know what they need to do and they don't need to know how the API request is formed they don't need to know how the endpoint responds what they need to know is how do I use this extension from Tableau and so if we look at this document that's been pulled up here on RStudio Connect we can see that's exactly what this is trying to provide it gives the same you know title description that we saw previously and then it has this usage section here that defines it's a literal copy and paste here's how you use this inside of Tableau copy this script function into your Tableau workbook under a calculated field make a change to the input value to match what you're trying to provide from Tableau and you're good to go.
And if we scroll down a little bit further we see additional information about the arguments that we're expecting Tableau to provide as well as the return value that we're going to be giving back to Tableau and again all this is being powered by those additional annotations that we added when we introduced plumbertableau to our API. One thing that I'll note is that if we come back just for a moment to our development environment and take a look when we defined Tableau return we not only described what we're returning but we were very specific about the type of data that we're going to be returning and the reason for that is because if we come back now to RStudio Connect you'll see that the script function is strongly typed right this is a script function and then this str is a reference to the fact that we expect a string value to come back from the extension so because we because we provided information when we developed this that said hey we're going to return a character value we know when we generate this documentation that we should put script string as the script function using this particular extension and this again the idea is just convenience we want to make this as easy as possible for the Tableau user for them to come in copy and paste this into their Tableau workbook.
Accessing the deployed API from Tableau
So let's do that let's do this in action I'm going to grab this this script that we have here we're going to open up a new workbook on Tableau online I'm going to zoom in I've got to connect to a data source so we'll just connect to the default one I'm going to zoom in here a little bit just so we can see a little bit better first thing I'm going to do is just save this because in order to use analytics extensions we have to have a saved workbook go ahead and save that and then I'm going to now create a parameter we'll call this input text and this is this just gives us something that we can kind of in real time work with and see so we're going to say this is a string the current value is hello world go ahead and hit okay there and then let's actually show this parameter so over here on the right hand side we have this input text field and we can we can use this field to input anything and it's referenceable from our Tableau workbook.
And the significance now is we can now go through the process of creating a calculated field we'll call this caps and here's where we're going to paste in that script function that we've copied from RStudio Connect and you'll notice the first argument here is the location of this extension on RStudio Connect all right so this is the path to the extension as hosted on RStudio Connect and then the second and any subsequent arguments are the values are referenced to the values that we want to pass from Tableau to the extension so I'm going to replace string value with input text which is a reference to our parameter that we created and then hit okay and now we're going to bring this calculated field into our workbook so take a moment to load and then we're going to it automatically defaults to a color value but we don't want that we'll call this what changes to be a label and here we can see and there's a way to expand it and I never remember how but if we hover we can see the capitalized rendered text of hello world and just to showcase that it doesn't just do hello world we can come over and say this is an extension and if we hover again we see this is an analytics extension in in full capitalization.
So we've gone through the full process of we've developed this extension inside of RStudio or some other editor using plumber and plumbertableau we've published the extension to RStudio Connect where it can then be accessed and used by Tableau users who have to come in let me come back to RStudio Connect come in copy the script value paste it into their workbook under a calculated field and then supply the appropriate values to that to that extension function and in our case that appropriate value if I pull this up one more time an appropriate value is this input text typically right again very very kind of trivial example.
A more comprehensive example
So in in practice what we might see here is we might see something like you know multiple values being passed in from the underlying data source that my Tableau workbook is built on top of and then and then we get really nice functionality where if a user comes in and filters the data or changes the view or otherwise manipulates the underlying data that triggers this extension to to re-evaluate with new data so it's this is a very responsive real-time interaction between some third-party service in our case that third-party service is RStudio Connect that's hosting this this R script this R function that we're running and it's responding in real time to how users are interacting with the Tableau workbook.
Just to to kind of highlight that let me come back here so here's an example let me see if this will pull up and render it sometimes is a little bit finicky but we'll see if we can make something happen so here's an example of kind of something that's a little bit more involved right so we have once this pulls up here and we have two different actually two different extensions that are being hosted on RStudio one is an R extension that does real-time outlier detection giving us given a set of input data so we consider a this is a transactional data set that we're working with here so we consider sales and profit for a given customer and then based on sales and profit we determine which customers appear to be outliers given the totality of the data set that all happens in R then we have a another extension written in Python so there's a kind of a companion package to plumbertableau called FastAPI Tableau that does the same thing just on the Python side and so we have another extension written in Python in fact let me just pull that up while that's loading and this extension takes a previously trained model some input from Tableau and uses that input to generate new predicted values and that allows us to do something in the case of our workbook what we're doing is we're saying okay given what we know about this customer and a transactional history what amount of profit do we predict out of this customer service transaction and so we take that predicted profit value we compare it to the actual profit of the transaction and then we can identify under and over performers based on that metric of comparing predicted to realize profit.
So this this view is a little bit different than what we had seen previously and the reason for this is the the FastAPI Tableau package is has a slightly different interface when it when it comes to rendering the documentation that we generate for Tableau users and there will be an update that gets made to the plumbertableau package that brings a similar style interface there as well so there's a kind of more unified feeling between the two but we see the same behavior here we can come in and we can say use in Tableau workbooks same thing we can copy and paste this this value into our Tableau workbook we have a description of what we're doing and the and the values we're expecting.
So that's like at a very kind of like I said kind of simplistic level that's the idea behind plumbertableau and by extension FastAPI Tableau is to say look let's say that I have some business logic I have some complex arithmetic or machine learning or whatever the case is that I need to do as some sort of like real-time interaction with my Tableau workbook takes place and and whatever it is that I'm trying to do extends beyond what Tableau natively gives me in terms of capabilities right and that's often the case when it comes to machine learning there's some rudimentary machine learning capabilities in Tableau but if I'm looking to do something a little bit more complex or involved or something like outlier detection which is one of the examples that we provide in our in our online repository then this becomes a really compelling use case because I can essentially say like I'm going to take the data that I have in Tableau I'm going to send it off somewhere for something to be done to it and then I'm going to get the results back and in this case we've shown I can send that data off to RStudio Connect this extension can execute and then return the results back into Tableau and I can then further use those results to you know improve my analysis or or increase enhance my visualization or you know whatever I'm doing with the results once they come back is is up to me.
Caveats and Tableau configuration
There's a few caveats that I think are worth pointing out one is Tableau is kind of interesting um in in the sense that if I'm passing in you know this this is a pretty again trivial toy example but if I'm if I'm referencing a large underlying data set and I'm trying to pass that data set in bulk to my extension it depends on how I'm actually calculating my calculated field inside of Tableau because Tableau will either send one request with all the data or it will send one request per observation which can dramatically slow things down so imagine I've got a hundred thousand rows of data um I could and accidentally if I if I haven't set things up the right way I could accidentally end up sending 100,000 requests every time I try to use the extension and that's certainly going to be much less efficient than sending just one request with 100,000 observations and so there's we have some documentation around things to look into and how to kind of go about reasoning around how how to set up Tableau so that it sends a request along the appropriate dimension so that you end up working as efficiently as possible.
The one other thing that I'll mention that we haven't discussed so far is how do we set these things up from you know like how do we set up the connection between Tableau and RStudio Connect and that that's kind of a little bit beyond the scope of like plumbertableau itself but I think it's worth just kind of understanding how that works so if I come back to the home page of Tableau Online and come to settings as an admin in Tableau Online if I come over to extensions here's where I can do a couple things one I can enable analytics extensions so about halfway down the page there's analytics extensions I would choose to enable those from my site and then there there isn't anything pre-configured so I need to create a new connection from this from this option list I'll select analytics extensions API which is the final option and then in here we can say we can give it a name RStudio Connect provide the host name irstudioconnect.com and the port whatever that might be username is always rstudio-connect and then the password here is any valid RStudio Connect user API key.
And the thing to understand about this setup again two things one is this is this is managed and done by the Tableau administrator so individual users aren't managing connections at this level this is done the administrative level and then when you create a workbook as a user you can select which connection you want to actually use from that workbook so it's done by the administrator the other thing is this password is centrally used and what I mean by that is whatever API key is is input here by the Tableau admin is the key that will be used anytime an extension uses this connection so if this key is like my own personal key that means my personal RStudio Connect user has to be added to every extension that's published in order for it to be used from Tableau our recommended approach here is to create like a service account called Tableau or Tableau extension or really call it whatever you want but then use a an API key from that service account as the key view that you provide here in the setup on Tableau and then add that Tableau user again whatever you you happen to call that service account add that user to every extension that's published so that they have so the Tableau has the ability to call out to that extension.
Okay and I think like again trivial example in terms of what we're actually doing but but hopefully highlights the kind of the flexibility of this approach and and the way in which it can be used to extend the functionality of Tableau beyond just what's already there anyway but that's that's kind of plumbertableau in a nutshell.
