Skip to content

Commit

Permalink
Built site for gh-pages
Browse files Browse the repository at this point in the history
  • Loading branch information
Quarto GHA Workflow Runner committed Feb 3, 2024
1 parent 2b61dde commit fea6270
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 31 deletions.
2 changes: 1 addition & 1 deletion .nojekyll
Original file line number Diff line number Diff line change
@@ -1 +1 @@
954ae025
0a2287b1
4 changes: 2 additions & 2 deletions chapters/sec1/1-3-data-access.html
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,7 @@ <h3 class="anchored" data-anchor-id="step-3-build-a-shiny-app">Step 3: Build a s
<span id="cb13-26"><a href="#cb13-26" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> vals():</span>
<span id="cb13-27"><a href="#cb13-27" aria-hidden="true" tabindex="-1"></a> d <span class="op">=</span> {</span>
<span id="cb13-28"><a href="#cb13-28" aria-hidden="true" tabindex="-1"></a> <span class="st">"bill_length_mm"</span> : <span class="bu">input</span>.bill_length(),</span>
<span id="cb13-29"><a href="#cb13-29" aria-hidden="true" tabindex="-1"></a> <span class="st">"sex_Male"</span> : <span class="bu">input</span>.sex() <span class="op">==</span> <span class="st">"Male"</span>,</span>
<span id="cb13-29"><a href="#cb13-29" aria-hidden="true" tabindex="-1"></a> <span class="st">"sex_male"</span> : <span class="bu">input</span>.sex() <span class="op">==</span> <span class="st">"Male"</span>,</span>
<span id="cb13-30"><a href="#cb13-30" aria-hidden="true" tabindex="-1"></a> <span class="st">"species_Gentoo"</span> : <span class="bu">input</span>.species() <span class="op">==</span> <span class="st">"Gentoo"</span>, </span>
<span id="cb13-31"><a href="#cb13-31" aria-hidden="true" tabindex="-1"></a> <span class="st">"species_Chinstrap"</span> : <span class="bu">input</span>.species() <span class="op">==</span> <span class="st">"Chinstrap"</span></span>
<span id="cb13-32"><a href="#cb13-32" aria-hidden="true" tabindex="-1"></a></span>
Expand Down Expand Up @@ -862,7 +862,7 @@ <h3 class="anchored" data-anchor-id="step-3-build-a-shiny-app">Step 3: Build a s
<span id="cb14-57"><a href="#cb14-57" aria-hidden="true" tabindex="-1"></a> pred <span class="ot">&lt;-</span> <span class="fu">eventReactive</span>(</span>
<span id="cb14-58"><a href="#cb14-58" aria-hidden="true" tabindex="-1"></a> input<span class="sc">$</span>predict,</span>
<span id="cb14-59"><a href="#cb14-59" aria-hidden="true" tabindex="-1"></a> httr2<span class="sc">::</span><span class="fu">request</span>(api_url) <span class="sc">|&gt;</span></span>
<span id="cb14-60"><a href="#cb14-60" aria-hidden="true" tabindex="-1"></a> httr2<span class="sc">::</span><span class="fu">req_body_json</span>(<span class="fu">vals</span>()) <span class="sc">|&gt;</span></span>
<span id="cb14-60"><a href="#cb14-60" aria-hidden="true" tabindex="-1"></a> httr2<span class="sc">::</span><span class="fu">req_body_json</span>(<span class="fu">list</span>(<span class="fu">vals</span>())) <span class="sc">|&gt;</span></span>
<span id="cb14-61"><a href="#cb14-61" aria-hidden="true" tabindex="-1"></a> httr2<span class="sc">::</span><span class="fu">req_perform</span>() <span class="sc">|&gt;</span></span>
<span id="cb14-62"><a href="#cb14-62" aria-hidden="true" tabindex="-1"></a> httr2<span class="sc">::</span><span class="fu">resp_body_json</span>(),</span>
<span id="cb14-63"><a href="#cb14-63" aria-hidden="true" tabindex="-1"></a> <span class="at">ignoreInit =</span> <span class="cn">TRUE</span></span>
Expand Down
2 changes: 1 addition & 1 deletion search.json
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@
"href": "chapters/sec1/1-3-data-access.html#lab-use-a-database-and-an-api",
"title": "3  Databases and Data APIs",
"section": "Lab: Use a database and an API",
"text": "Lab: Use a database and an API\nIn this lab, we will build the data and the presentation layers for our penguin mass model exploration. We’re going to create an app to explore the model, which will look like this:\n\nLet’s start by moving the data into an actual data layer.\n\nStep 1: Put the data in DuckDB\nLet’s start by moving the data into a DuckDB database and use it from there for the modeling and EDA scripts.\nTo start, let’s load the data.\nHere’s what that looks like in R:\ncon &lt;- DBI::dbConnect(duckdb::duckdb(), dbdir = \"my-db.duckdb\")\nDBI::dbWriteTable(con, \"penguins\", palmerpenguins::penguins)\nDBI::dbDisconnect(con)\nOr equivalently, in Python:\nimport duckdb\nfrom palmerpenguins import penguins\n\ncon = duckdb.connect('my-db.duckdb')\ndf = penguins.load_penguins()\ncon.execute('CREATE TABLE penguins AS SELECT * FROM df')\ncon.close()\nNow that the data is loaded, let’s adjust our scripts to use the database.\nIn R, we will replace our data loading with connecting to the database. Leaving out all the parts that don’t change, it looks like\n\n\neda.qmd\n\ncon &lt;- DBI::dbConnect(\n duckdb::duckdb(), \n dbdir = \"my-db.duckdb\"\n )\ndf &lt;- dplyr::tbl(con, \"penguins\")\n\nWe also need to call to DBI::dbDisconnect(con) at the end of the script.\nWe don’t have to change anything because we wrote our data processing code in {dplyr}. Under the hood, {dplyr} can switch seamlessly to a database backend, which is really cool.\n\n\neda.qmd\n\ndf %&gt;%\n group_by(species, sex) %&gt;%\n summarise(\n across(\n ends_with(\"mm\") | ends_with(\"g\"),\n \\(x) mean(x, na.rm = TRUE)\n )\n ) %&gt;%\n dplyr::collect() %&gt;%\n knitr::kable()\n\nIt’s unnecessary, but I’ve added a call to dplyr::collect() in line 31. It will be implied if I don’t put it there manually, but it helps make it obvious that all the work before there has been pushed off to the database. It doesn’t matter for this small dataset, but it could benefit a larger dataset.\nIn Python, we’re just going to load the entire dataset into memory for modeling, so the line loading the dataset changes to\n\n\nmodel.qmd\n\ncon = duckdb.connect('my-db.duckdb')\ndf = con.execute(\"SELECT * FROM penguins\").fetchdf().dropna()\ncon.close()\n\nNow let’s switch to figuring out the connection we’ll need to our processing layer in the presentation layer.\n\n\nStep 2: Call the model API from code\nBefore you start, ensure the API is running on your machine from the last lab.\n\n\n\n\n\n\nNote\n\n\n\nI’m assuming it’s running on port 8080 in this lab. If you’ve put it somewhere else, change the 8080 in the code below to match the port on your machine.\n\n\nIf you want to call the model in code, you can use any http request library. In R you should use httr2 and in Python you should use requests.\nHere’s what it looks like to call the API in Python\nimport requests\n\nreq_data = {\n \"bill_length_mm\": 0,\n \"species_Chinstrap\": False,\n \"species_Gentoo\": False,\n \"sex_male\": False\n}\nreq = requests.post('http://127.0.0.1:8080/predict', json = [req_data])\nres = req.json().get('predict')[0]\nor equivalently in R\nreq &lt;- httr2::request(\"http://127.0.0.1:8080/predict\") |&gt;\n httr2::req_body_json(\n list(\n \"bill_length_mm\" = 0,\n \"species_Chinstrap\" = FALSE,\n \"species_Gentoo\" = FALSE,\n \"sex_male\" = FALSE\n )\n ) |&gt;\n httr2::req_perform()\nres &lt;- httr2::resp_body_json(r)$predict[[1]]\nNote that there’s no translation necessary to send the request. The {requests} and{httr2} packages automatically know what to do with the Python dictionary and the R list.\nGetting the result back takes more work to find the right spot in the JSON returned. This is quite common.\n\n\n\n\n\n\nNote\n\n\n\nThe {vetiver} package also includes the ability to auto-query a {vetiver} API. I’m not using it here to expose the details of calling an API.\n\n\nLet’s take this API-calling code and build the presentation layer around it.\n\n\nStep 3: Build a shiny app\nWe will use the {shiny} R and Python package for creating interactive web apps using just Python code. If you don’t know much about {shiny}, you can unthinkingly follow the examples here or spend time with the Mastering Shiny book to learn to use it yourself.\nEither way, an app that looks like the picture above would look like this in Python\n\n\napp.py\n\nfrom shiny import App, render, ui, reactive\nimport requests\n\napi_url = 'http://127.0.0.1:8080/predict'\n\napp_ui = ui.page_fluid(\n ui.panel_title(\"Penguin Mass Predictor\"), \n ui.layout_sidebar(\n ui.panel_sidebar(\n [ui.input_slider(\"bill_length\", \"Bill Length (mm)\", 30, 60, 45, step = 0.1),\n ui.input_select(\"sex\", \"Sex\", [\"Male\", \"Female\"]),\n ui.input_select(\"species\", \"Species\", [\"Adelie\", \"Chinstrap\", \"Gentoo\"]),\n ui.input_action_button(\"predict\", \"Predict\")]\n ),\n ui.panel_main(\n ui.h2(\"Penguin Parameters\"),\n ui.output_text_verbatim(\"vals_out\"),\n ui.h2(\"Predicted Penguin Mass (g)\"), \n ui.output_text(\"pred_out\")\n )\n ) \n)\n\ndef server(input, output, session):\n @reactive.Calc\n def vals():\n d = {\n \"bill_length_mm\" : input.bill_length(),\n \"sex_Male\" : input.sex() == \"Male\",\n \"species_Gentoo\" : input.species() == \"Gentoo\", \n \"species_Chinstrap\" : input.species() == \"Chinstrap\"\n\n }\n return d\n \n @reactive.Calc\n @reactive.event(input.predict)\n def pred():\n r = requests.post(api_url, json = [vals()])\n return r.json().get('predict')[0]\n\n @output\n @render.text\n def vals_out():\n return f\"{vals()}\"\n\n @output\n @render.text\n def pred_out():\n return f\"{round(pred())}\"\n\napp = App(app_ui, server)\n\nAnd like this in R\n\n\napp.R\n\nlibrary(shiny)\n\napi_url &lt;- \"http://127.0.0.1:8080/predict\"\n\nui &lt;- fluidPage(\n titlePanel(\"Penguin Mass Predictor\"),\n\n # Model input values\n sidebarLayout(\n sidebarPanel(\n sliderInput(\n \"bill_length\",\n \"Bill Length (mm)\",\n min = 30,\n max = 60,\n value = 45,\n step = 0.1\n ),\n selectInput(\n \"sex\",\n \"Sex\",\n c(\"Male\", \"Female\")\n ),\n selectInput(\n \"species\",\n \"Species\",\n c(\"Adelie\", \"Chinstrap\", \"Gentoo\")\n ),\n # Get model predictions\n actionButton(\n \"predict\",\n \"Predict\"\n )\n ),\n\n mainPanel(\n h2(\"Penguin Parameters\"),\n verbatimTextOutput(\"vals\"),\n h2(\"Predicted Penguin Mass (g)\"),\n textOutput(\"pred\")\n )\n )\n)\n\nserver &lt;- function(input, output) {\n # Input params\n vals &lt;- reactive(\n list(\n bill_length_mm = input$bill_length,\n species_Chinstrap = input$species == \"Chinstrap\",\n species_Gentoo = input$species == \"Gentoo\",\n sex_male = input$sex == \"Male\"\n )\n )\n\n # Fetch prediction from API\n pred &lt;- eventReactive(\n input$predict,\n httr2::request(api_url) |&gt;\n httr2::req_body_json(vals()) |&gt;\n httr2::req_perform() |&gt;\n httr2::resp_body_json(),\n ignoreInit = TRUE\n )\n\n # Render to UI\n output$pred &lt;- renderText(pred()$predict[[1]])\n output$vals &lt;- renderPrint(vals())\n}\n\n# Run the application\nshinyApp(ui = ui, server = server)\n\nOver the next few chapters, we will implement more architectural best practices for the app and eventually go to deployment."
"text": "Lab: Use a database and an API\nIn this lab, we will build the data and the presentation layers for our penguin mass model exploration. We’re going to create an app to explore the model, which will look like this:\n\nLet’s start by moving the data into an actual data layer.\n\nStep 1: Put the data in DuckDB\nLet’s start by moving the data into a DuckDB database and use it from there for the modeling and EDA scripts.\nTo start, let’s load the data.\nHere’s what that looks like in R:\ncon &lt;- DBI::dbConnect(duckdb::duckdb(), dbdir = \"my-db.duckdb\")\nDBI::dbWriteTable(con, \"penguins\", palmerpenguins::penguins)\nDBI::dbDisconnect(con)\nOr equivalently, in Python:\nimport duckdb\nfrom palmerpenguins import penguins\n\ncon = duckdb.connect('my-db.duckdb')\ndf = penguins.load_penguins()\ncon.execute('CREATE TABLE penguins AS SELECT * FROM df')\ncon.close()\nNow that the data is loaded, let’s adjust our scripts to use the database.\nIn R, we will replace our data loading with connecting to the database. Leaving out all the parts that don’t change, it looks like\n\n\neda.qmd\n\ncon &lt;- DBI::dbConnect(\n duckdb::duckdb(), \n dbdir = \"my-db.duckdb\"\n )\ndf &lt;- dplyr::tbl(con, \"penguins\")\n\nWe also need to call to DBI::dbDisconnect(con) at the end of the script.\nWe don’t have to change anything because we wrote our data processing code in {dplyr}. Under the hood, {dplyr} can switch seamlessly to a database backend, which is really cool.\n\n\neda.qmd\n\ndf %&gt;%\n group_by(species, sex) %&gt;%\n summarise(\n across(\n ends_with(\"mm\") | ends_with(\"g\"),\n \\(x) mean(x, na.rm = TRUE)\n )\n ) %&gt;%\n dplyr::collect() %&gt;%\n knitr::kable()\n\nIt’s unnecessary, but I’ve added a call to dplyr::collect() in line 31. It will be implied if I don’t put it there manually, but it helps make it obvious that all the work before there has been pushed off to the database. It doesn’t matter for this small dataset, but it could benefit a larger dataset.\nIn Python, we’re just going to load the entire dataset into memory for modeling, so the line loading the dataset changes to\n\n\nmodel.qmd\n\ncon = duckdb.connect('my-db.duckdb')\ndf = con.execute(\"SELECT * FROM penguins\").fetchdf().dropna()\ncon.close()\n\nNow let’s switch to figuring out the connection we’ll need to our processing layer in the presentation layer.\n\n\nStep 2: Call the model API from code\nBefore you start, ensure the API is running on your machine from the last lab.\n\n\n\n\n\n\nNote\n\n\n\nI’m assuming it’s running on port 8080 in this lab. If you’ve put it somewhere else, change the 8080 in the code below to match the port on your machine.\n\n\nIf you want to call the model in code, you can use any http request library. In R you should use httr2 and in Python you should use requests.\nHere’s what it looks like to call the API in Python\nimport requests\n\nreq_data = {\n \"bill_length_mm\": 0,\n \"species_Chinstrap\": False,\n \"species_Gentoo\": False,\n \"sex_male\": False\n}\nreq = requests.post('http://127.0.0.1:8080/predict', json = [req_data])\nres = req.json().get('predict')[0]\nor equivalently in R\nreq &lt;- httr2::request(\"http://127.0.0.1:8080/predict\") |&gt;\n httr2::req_body_json(\n list(\n \"bill_length_mm\" = 0,\n \"species_Chinstrap\" = FALSE,\n \"species_Gentoo\" = FALSE,\n \"sex_male\" = FALSE\n )\n ) |&gt;\n httr2::req_perform()\nres &lt;- httr2::resp_body_json(r)$predict[[1]]\nNote that there’s no translation necessary to send the request. The {requests} and{httr2} packages automatically know what to do with the Python dictionary and the R list.\nGetting the result back takes more work to find the right spot in the JSON returned. This is quite common.\n\n\n\n\n\n\nNote\n\n\n\nThe {vetiver} package also includes the ability to auto-query a {vetiver} API. I’m not using it here to expose the details of calling an API.\n\n\nLet’s take this API-calling code and build the presentation layer around it.\n\n\nStep 3: Build a shiny app\nWe will use the {shiny} R and Python package for creating interactive web apps using just Python code. If you don’t know much about {shiny}, you can unthinkingly follow the examples here or spend time with the Mastering Shiny book to learn to use it yourself.\nEither way, an app that looks like the picture above would look like this in Python\n\n\napp.py\n\nfrom shiny import App, render, ui, reactive\nimport requests\n\napi_url = 'http://127.0.0.1:8080/predict'\n\napp_ui = ui.page_fluid(\n ui.panel_title(\"Penguin Mass Predictor\"), \n ui.layout_sidebar(\n ui.panel_sidebar(\n [ui.input_slider(\"bill_length\", \"Bill Length (mm)\", 30, 60, 45, step = 0.1),\n ui.input_select(\"sex\", \"Sex\", [\"Male\", \"Female\"]),\n ui.input_select(\"species\", \"Species\", [\"Adelie\", \"Chinstrap\", \"Gentoo\"]),\n ui.input_action_button(\"predict\", \"Predict\")]\n ),\n ui.panel_main(\n ui.h2(\"Penguin Parameters\"),\n ui.output_text_verbatim(\"vals_out\"),\n ui.h2(\"Predicted Penguin Mass (g)\"), \n ui.output_text(\"pred_out\")\n )\n ) \n)\n\ndef server(input, output, session):\n @reactive.Calc\n def vals():\n d = {\n \"bill_length_mm\" : input.bill_length(),\n \"sex_male\" : input.sex() == \"Male\",\n \"species_Gentoo\" : input.species() == \"Gentoo\", \n \"species_Chinstrap\" : input.species() == \"Chinstrap\"\n\n }\n return d\n \n @reactive.Calc\n @reactive.event(input.predict)\n def pred():\n r = requests.post(api_url, json = [vals()])\n return r.json().get('predict')[0]\n\n @output\n @render.text\n def vals_out():\n return f\"{vals()}\"\n\n @output\n @render.text\n def pred_out():\n return f\"{round(pred())}\"\n\napp = App(app_ui, server)\n\nAnd like this in R\n\n\napp.R\n\nlibrary(shiny)\n\napi_url &lt;- \"http://127.0.0.1:8080/predict\"\n\nui &lt;- fluidPage(\n titlePanel(\"Penguin Mass Predictor\"),\n\n # Model input values\n sidebarLayout(\n sidebarPanel(\n sliderInput(\n \"bill_length\",\n \"Bill Length (mm)\",\n min = 30,\n max = 60,\n value = 45,\n step = 0.1\n ),\n selectInput(\n \"sex\",\n \"Sex\",\n c(\"Male\", \"Female\")\n ),\n selectInput(\n \"species\",\n \"Species\",\n c(\"Adelie\", \"Chinstrap\", \"Gentoo\")\n ),\n # Get model predictions\n actionButton(\n \"predict\",\n \"Predict\"\n )\n ),\n\n mainPanel(\n h2(\"Penguin Parameters\"),\n verbatimTextOutput(\"vals\"),\n h2(\"Predicted Penguin Mass (g)\"),\n textOutput(\"pred\")\n )\n )\n)\n\nserver &lt;- function(input, output) {\n # Input params\n vals &lt;- reactive(\n list(\n bill_length_mm = input$bill_length,\n species_Chinstrap = input$species == \"Chinstrap\",\n species_Gentoo = input$species == \"Gentoo\",\n sex_male = input$sex == \"Male\"\n )\n )\n\n # Fetch prediction from API\n pred &lt;- eventReactive(\n input$predict,\n httr2::request(api_url) |&gt;\n httr2::req_body_json(list(vals())) |&gt;\n httr2::req_perform() |&gt;\n httr2::resp_body_json(),\n ignoreInit = TRUE\n )\n\n # Render to UI\n output$pred &lt;- renderText(pred()$predict[[1]])\n output$vals &lt;- renderPrint(vals())\n}\n\n# Run the application\nshinyApp(ui = ui, server = server)\n\nOver the next few chapters, we will implement more architectural best practices for the app and eventually go to deployment."
},
{
"objectID": "chapters/sec1/1-3-data-access.html#footnotes",
Expand Down
Loading

0 comments on commit fea6270

Please sign in to comment.