<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Felix Analytix]]></title><description><![CDATA[Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get all the R scripts of the tutorials.]]></description><link>https://www.felixanalytix.com</link><image><url>https://substackcdn.com/image/fetch/$s_!xTVm!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F06df93bf-1cd7-4fe6-b61d-01a812a85435_180x180.png</url><title>Felix Analytix</title><link>https://www.felixanalytix.com</link></image><generator>Substack</generator><lastBuildDate>Fri, 01 May 2026 11:34:39 GMT</lastBuildDate><atom:link href="https://www.felixanalytix.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Felix Analytix]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[felixanalytix@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[felixanalytix@substack.com]]></itunes:email><itunes:name><![CDATA[Felix Analytix]]></itunes:name></itunes:owner><itunes:author><![CDATA[Felix Analytix]]></itunes:author><googleplay:owner><![CDATA[felixanalytix@substack.com]]></googleplay:owner><googleplay:email><![CDATA[felixanalytix@substack.com]]></googleplay:email><googleplay:author><![CDATA[Felix Analytix]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[How to Use generative AI to Create World Maps in R]]></title><description><![CDATA[How to use the new Positron Assistant to generate interactive world maps with R]]></description><link>https://www.felixanalytix.com/p/how-to-create-world-maps-in-r-with</link><guid isPermaLink="false">https://www.felixanalytix.com/p/how-to-create-world-maps-in-r-with</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Fri, 05 Sep 2025 12:25:53 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/390024d6-2db4-4ae9-a257-0ed242b88dd9_1280x720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Would you like to learn how to create interactive world maps in R using generative AI?</strong></p><p>Discover how to use the new Positron Assistant AI agent to automatically generate R code that produces interactive world maps on your own dataset.</p><p>In this tutorial, you will learn how to:</p><ul><li><p>Use LLM prompts on your own data to generate R code with the Positron Assistant and the ellmer R package</p></li><li><p>Prepare and join your dataset with world geodata using rnaturalearth and countrycode</p></li><li><p>Design interactive choropleth world maps with the leaflet package</p></li><li><p>Add informative labels, detailed popups, and a polished legend to your maps</p></li></ul><p>We will carefully review the R code generated by the LLM prompt, so you can easily adapt the code for your own use cases.</p><p>Check out the video now:</p><div id="youtube2-SwwqfAf2QHs" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;SwwqfAf2QHs&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/SwwqfAf2QHs?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h1>Setting Up Claude Sonnet 4</h1><p>First, let&#8217;s see how to set up the ellmer package to chat with a large language model (LLM), in this case, Claude Sonnet 4. It&#8217;s also good practice to manage your API keys securely using your <code>.Renviron</code> file.</p><pre><code><code>library(ellmer)

# get the API key: https://console.anthropic.com/settings/admin-keys
# add ANTHROPIC_API_KEY=xxx in .Renviron using usethis::edit_r_environ()
chat &lt;- chat_anthropic(
  api_key = Sys.getenv("ANTHROPIC_API_KEY"),
  model = "claude-sonnet-4-20250514",
  params = params(temperature = 0) # reduce randomness
)</code></code></pre><p>Here, we&#8217;re first loading the <code>ellmer</code> package which is our interface to the LLM. We create a chat object using our API key (securely stored in <code>.Renviron</code>). Setting <code>temperature = 0</code> helps make the code generated by the LLM as reproducible as possible&#8212;this minimizes randomness.</p><h1>Generating R Code to Fetch Country GDP Data</h1><p>Now, we want the LLM to generate R code that fetches country-level GDP data for 2023 using the <code>wbstats</code> R package. The prompt is designed to return only the code, without code block syntax.</p><pre><code><code>prompt_get_data &lt;- "Get country gdp for 2023 using wbstats R package. Return only code without backsticks."

code_generated &lt;- chat$chat(prompt_get_data)

chat$get_cost() # show costs</code></code></pre><pre><code><code>[1] $0.00</code></code></pre><p>Here, we&#8217;ve put our prompt as a string and passed it to our <code>chat</code> object to generate the R code. We can also call <code>get_cost()</code> to check how much this interaction costs&#8212;it&#8217;s pretty cheap as less than $0.00.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"><strong>Subscribe to receive by email FOR FREE the URL of the GitHub repository where you can access all the R code of this tutorial.</strong></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h1>Fetching and Cleaning Data</h1><p>Let&#8217;s now use the generated code to fetch the GDP data and prepare it for mapping. Here, we use the <code>wbstats</code> package to get GDP values, and the <code>dplyr</code> package to clean and organize our dataset to two essential columns: country name and GDP.</p><pre><code><code>library(wbstats)

gdp_2023 &lt;- wb_data(
  indicator = "NY.GDP.MKTP.CD",
  start_date = 2023,
  end_date = 2023
)

print(gdp_2023)</code></code></pre><pre><code><code># A tibble: 217 &#215; 9
   iso2c iso3c country             date NY.GDP.MKTP.CD unit  obs_status footnote
   &lt;chr&gt; &lt;chr&gt; &lt;chr&gt;              &lt;dbl&gt;          &lt;dbl&gt; &lt;chr&gt; &lt;chr&gt;      &lt;chr&gt;   
 1 AW    ABW   Aruba               2023    3648573136. &lt;NA&gt;  &lt;NA&gt;       &lt;NA&gt;    
 2 AF    AFG   Afghanistan         2023   17152234637. &lt;NA&gt;  &lt;NA&gt;       &lt;NA&gt;    
 3 AO    AGO   Angola              2023   84875162197. &lt;NA&gt;  &lt;NA&gt;       &lt;NA&gt;    
 4 AL    ALB   Albania             2023   23547180412. &lt;NA&gt;  &lt;NA&gt;       &lt;NA&gt;    
 5 AD    AND   Andorra             2023    3785067332. &lt;NA&gt;  &lt;NA&gt;       &lt;NA&gt;    
 6 AE    ARE   United Arab Emira&#8230;  2023  514130432648. &lt;NA&gt;  &lt;NA&gt;       &lt;NA&gt;    
 7 AR    ARG   Argentina           2023  646075277525. &lt;NA&gt;  &lt;NA&gt;       &lt;NA&gt;    
 8 AM    ARM   Armenia             2023   24085749592. &lt;NA&gt;  &lt;NA&gt;       &lt;NA&gt;    
 9 AS    ASM   American Samoa      2023            NA  &lt;NA&gt;  &lt;NA&gt;       &lt;NA&gt;    
10 AG    ATG   Antigua and Barbu&#8230;  2023    2005785185. &lt;NA&gt;  &lt;NA&gt;       &lt;NA&gt;    
# &#8505; 207 more rows
# &#8505; 1 more variable: last_updated &lt;date&gt;</code></code></pre><pre><code><code>library(dplyr)

gdp_2023_cleaned &lt;- gdp_2023 |&gt;
  rename(GDP = NY.GDP.MKTP.CD) |&gt;
  select(country, GDP)</code></code></pre><p>We use <code>wb_data()</code> to download the data: each row represents a country and its 2023 GDP. The <code>rename</code> and <code>select</code> steps ensure our data has a simple and clean structure: just <code>country</code> and <code>GDP</code>, making it ready for merging with geographic data.</p><h1>Loading a System Prompt Template</h1><p>The next step is to use a prompt template. In Positron, you can specify reusable markdown prompt templates (with <code>.prompt.md</code> extension) that provide detailed instructions to the LLM. </p><p>By reading and collapsing the prompt template, we have a reproducible way to instruct the LLM exactly how we want our world map code to be generated&#8212;encapsulating prompt engineering in one place.</p><p>Let&#8217;s load our prompt template into R now.</p><p><strong>&#128071;&#128071;&#128071; Full prompt below &#128071;&#128071;&#128071;</strong></p><div class="paywall-jump" data-component-name="PaywallToDOM"></div><pre><code><code># path_prompt &lt;- ".github/prompts/create-world-map_leaflet-giscoR-countrycode.prompt.md"

# prompt_system &lt;- readLines(path_prompt) |&gt;
#   paste0(collapse = "\n") # clean text

prompt_system &lt;- "
You are an expert R programmer specializing in interactive geospatial visualizations and data mapping.

Your task is to create an interactive choropleth world map using the leaflet R package, incorporating user-provided data with world geodata from rnaturalearth package.

First, check if the user has provided a dataset. If no dataset is provided, only ask briefly the user to include a dataset.

Follow these steps:

1. Load necessary R packages (leaflet, rnaturalearth, rnaturalearthdata, countrycode, dplyr, and any other required packages)

2. Use the dataset provided by the user and examine its structure

3. Get world geodata using the rnaturalearth package (ne_countries function). Then use dplyr::filter on this geodata to keep only the variable names "name", "iso_a3" and "geometry"

4. Use the countrycode package to standardize country codes to ISO3 format for proper joining between the user dataset and geodata

5. Join the user dataset with the world geodata (using "iso_a3" column) using appropriate dplyr functions

6. Create color palette and binning for the choropleth visualization using leaflet's colorBin or colorNumeric functions. Adjust the color palette with the distribution of the user dataset.

7. Build the interactive map using leaflet package with:
   - Professional base tiles (consider CartoDB.Positron or similar clean options)
   - Choropleth polygons with appropriate fill colors and styling
   - Clean, informative labels for each country
   - Detailed popup information showing relevant data from the user's dataset
   - Professional legend with clear formatting
   - Appropriate zoom levels and center positioning

8. Apply professional styling including border colors, opacity settings, and hover effects

9. Ensure the map is responsive and visually appealing with proper color schemes

Always give minimal solution and avoid unnecessary additions. Return only R code without backticks such as ```r ```. Prefer using tidyverse packages and style guide.
"

str(prompt_system) # prompt in string

chat$set_system_prompt(prompt_system)</code></code></pre><h1>Resetting the LLM User Chat History</h1><p>When using the ellmer package, it&#8217;s good practice to clear previous chat history to avoid unwanted context carryover between different prompts. This step resets the user message history, but keeps our system prompt active in the background.</p><pre><code><code>chat$set_turns(list()) # clean user chat</code></code></pre><h1>Generating R Code with Context About Our Data</h1><p>Now, we want the LLM to generate the map code in the context of our cleaned GDP dataset. Here, we provide not only the dataset but also a summary of its structure (using the <code>btw</code> package, short for &#8220;by the way&#8221;) to help the LLM generate more relevant and accurate code.</p><pre><code><code>library(btw) # pak::pak("posit-dev/btw")

code_generated &lt;- chat$chat(
  btw::btw(gdp_2023_cleaned) # add data description in the LLM context
)

chat$get_turns(include_system_prompt = FALSE)

chat$get_cost() # show costs</code></code></pre><p>By using the <code>btw()</code> function from the <code>btw</code> package, we give the LLM a short summary of our dataset, which improves the quality of its output. We also check the cost of this prompt.</p><h1>Saving the Generated Code</h1><p>Sometimes, you might want to copy the LLM-generated code for quick re-use or review. The <code>clipr</code> package lets you send this code directly to your clipboard.</p><pre><code><code>library(clipr)

clipr::write_clip(code_generated)</code></code></pre><h1>Loading Packages for Mapping</h1><p>Now it&#8217;s time to prepare for the map creation. We load several packages that will help us draw the interactive world choropleth map, join geospatial data to our GDP data, and deal with country code mismatches.</p><pre><code><code>library(leaflet)
library(giscoR)
library(countrycode)
library(dplyr)</code></code></pre><p>These packages play key roles: <code>leaflet</code> for mapping, <code>giscoR</code> for geodata, <code>countrycode</code> for harmonizing country names, and <code>dplyr</code> for data manipulation.</p><h1>Downloading World Geodata</h1><p>Let&#8217;s get the geospatial data for world countries using <code>giscoR</code>. This function retrieves country polygons as <code>sf</code>-style geometries, which are compatible with <code>leaflet</code>.</p><pre><code><code>world &lt;- gisco_get_countries(resolution = "60", epsg = "4326")</code></code></pre><h1>Harmonizing Country Identifiers</h1><p>To safely join our GDP data to the world geodata&#8212;especially given possible variations in country name formats&#8212;we use ISO3 codes as a common identifier. The <code>countrycode</code> package helps us convert country names to ISO3 codes.</p><pre><code><code>gdp_2023_cleaned &lt;- gdp_2023_cleaned %&gt;%
  mutate(ISO3_CODE = countrycode(country, "country.name", "iso3c"))</code></code></pre><pre><code><code>Warning: There was 1 warning in `mutate()`.
&#8505; In argument: `ISO3_CODE = countrycode(country, "country.name", "iso3c")`.
Caused by warning:
! Some values were not matched unambiguously: Channel Islands, Kosovo</code></code></pre><p>This step creates a new column, <code>ISO3_CODE</code>, in our GDP dataset for safe merging based on standardized codes.</p><h1>Merging GDP Data with Geodata</h1><p>Now, we join the GDP data to the world polygons, ensuring every country polygon has its associated GDP value.</p><pre><code><code>world_data &lt;- world %&gt;%
  left_join(gdp_2023_cleaned, by = "ISO3_CODE")</code></code></pre><p>Now our <code>world_data</code> object contains geometries (for the map) and data (for color scaling and popups).</p><h1>Creating Color Palette for Choropleth Mapping</h1><p>GDP values span several orders of magnitude, so we want to use logarithmic bins for the map&#8217;s color palette. This ensures the visual differences are meaningful across high and low GDP countries.</p><pre><code><code>bins &lt;- c(0, 1e10, 5e10, 1e11, 5e11, 1e12, 5e12, 1e13, 5e13)
pal &lt;- colorBin(
  "Blues",
  domain = world_data$GDP,
  bins = bins,
  na.color = "#808080"
)</code></code></pre><p>We define custom bins and use a blue color palette for our map, making it intuitive and visually appealing while handling missing data gracefully.</p><h1>Creating Informative Labels for Popups</h1><p>Next, let&#8217;s create the contents for our pop-up labels. Here, we display the country name and its GDP (formatted in billions, with proper rounding and separators). Missing values are handled clearly.</p><pre><code><code>labels &lt;- sprintf(
  "&lt;strong&gt;%s&lt;/strong&gt;&lt;br/&gt;GDP: $%s billion",
  world_data$NAME_ENGL,
  ifelse(
    is.na(world_data$GDP),
    "No data",
    format(round(world_data$GDP / 1e9, 1), big.mark = ",")
  )
) %&gt;%
  lapply(htmltools::HTML)</code></code></pre><p>We use <code>sprintf()</code> for rich HTML formatting and wrap the result with <code>htmltools::HTML</code> so that <code>leaflet</code> will interpret it correctly.</p><h1>Building the Interactive Leaflet Map</h1><p>Now, we want to bring everything together and construct the actual interactive map using the <code>leaflet</code> package. The map uses dark base tiles, overlays colored polygons by GDP, and provides interactive popups and a legend.</p><pre><code><code>leaflet(world_data) %&gt;%
  addProviderTiles(providers$CartoDB.DarkMatter) %&gt;%
  addPolygons(
    fillColor = ~ pal(GDP),
    weight = 0.5,
    opacity = 1,
    color = "white",
    dashArray = "3",
    fillOpacity = 0.7,
    highlightOptions = highlightOptions(
      weight = 2,
      color = "#666",
      dashArray = "",
      fillOpacity = 0.8,
      bringToFront = TRUE
    ),
    label = labels,
    labelOptions = labelOptions(
      style = list("font-weight" = "normal", padding = "3px 8px"),
      textsize = "13px",
      direction = "auto"
    )
  ) %&gt;%
  addLegend(
    pal = pal,
    values = ~GDP,
    opacity = 0.7,
    title = "GDP (USD)",
    position = "bottomright",
    labFormat = labelFormat(
      prefix = "$",
      suffix = "B",
      transform = function(x) round(x / 1e9, 1)
    )
  ) %&gt;%
  setView(lng = 0, lat = 20, zoom = 2)</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rbDp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc9ed5f5-ca25-4357-9e63-5cc90a2209a4_996x577.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rbDp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc9ed5f5-ca25-4357-9e63-5cc90a2209a4_996x577.png 424w, https://substackcdn.com/image/fetch/$s_!rbDp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc9ed5f5-ca25-4357-9e63-5cc90a2209a4_996x577.png 848w, https://substackcdn.com/image/fetch/$s_!rbDp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc9ed5f5-ca25-4357-9e63-5cc90a2209a4_996x577.png 1272w, https://substackcdn.com/image/fetch/$s_!rbDp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc9ed5f5-ca25-4357-9e63-5cc90a2209a4_996x577.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rbDp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc9ed5f5-ca25-4357-9e63-5cc90a2209a4_996x577.png" width="996" height="577" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bc9ed5f5-ca25-4357-9e63-5cc90a2209a4_996x577.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:577,&quot;width&quot;:996,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:269122,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.felixanalytix.com/i/169543325?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc9ed5f5-ca25-4357-9e63-5cc90a2209a4_996x577.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!rbDp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc9ed5f5-ca25-4357-9e63-5cc90a2209a4_996x577.png 424w, https://substackcdn.com/image/fetch/$s_!rbDp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc9ed5f5-ca25-4357-9e63-5cc90a2209a4_996x577.png 848w, https://substackcdn.com/image/fetch/$s_!rbDp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc9ed5f5-ca25-4357-9e63-5cc90a2209a4_996x577.png 1272w, https://substackcdn.com/image/fetch/$s_!rbDp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc9ed5f5-ca25-4357-9e63-5cc90a2209a4_996x577.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This chunk creates the interactive world map. Each country is colored by its GDP, with a color gradient applied according to the custom bins. The map includes interactive popups for each country and a customized legend that displays GDP in billions of US dollars.</p><h1>Conclusion</h1><p>We&#8217;ve demonstrated how to automate the generation of world map code using LLM-powered tools directly from R, preparing real datasets and joining them with geospatial data, and customizing your interactive leaflet map for your own use cases. This approach gives you full programmatic control, reproducibility, and the opportunity to adapt AI-generated code exactly how you want it.</p><p>Thank you for following this tutorial&#8212;experiment and customize further to make your own interactive world maps!</p><p>See you in another tutorial, bye bye!</p>]]></content:encoded></item><item><title><![CDATA[Create BEAUTIFUL value boxes using R]]></title><description><![CDATA[How to fully customize value boxes using bslib and bs4Dash in Shiny and Quarto]]></description><link>https://www.felixanalytix.com/p/create-beautiful-value-boxes-using</link><guid isPermaLink="false">https://www.felixanalytix.com/p/create-beautiful-value-boxes-using</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Sun, 25 May 2025 07:57:56 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/jToYFUhDAxs" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Coders,</p><p><strong>I just published a <a href="https://youtu.be/jToYFUhDAxs">new YouTube tutorial</a> showing how to create and fully customize beautiful values boxes using the bslib and bs4Dash R packages. I also show how to include customized interactive graphics in the value boxes using the plotly R package.</strong></p><p>In the video you will learn how to: </p><ul><li><p>Explore value boxes parameters using the &#8220;Build a Box&#8221; demo app.</p></li><li><p>Customize bslib value boxes with the bslib R package.</p></li><li><p>Integrate customize interactive graphics in your value boxes.</p></li><li><p>Use values boxes in your shiny apps.</p></li><li><p>Create and customize value boxes using the bs4Dash R package.</p></li></ul><p>Watch the video now:</p><div id="youtube2-jToYFUhDAxs" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;jToYFUhDAxs&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/jToYFUhDAxs?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h1>Getting Started with the bslib demo app</h1><p>Let&#8217;s get started directly with this bslib &#8220;Build a box&#8221; demo application which you can access at: <em><a href="https://bslib.shinyapps.io/build-a-box/">https://bslib.shinyapps.io/build-a-box/</a></em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GS1o!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb995b37-83af-43bb-8805-8b6e2983a1f9_1138x773.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GS1o!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb995b37-83af-43bb-8805-8b6e2983a1f9_1138x773.png 424w, https://substackcdn.com/image/fetch/$s_!GS1o!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb995b37-83af-43bb-8805-8b6e2983a1f9_1138x773.png 848w, https://substackcdn.com/image/fetch/$s_!GS1o!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb995b37-83af-43bb-8805-8b6e2983a1f9_1138x773.png 1272w, https://substackcdn.com/image/fetch/$s_!GS1o!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb995b37-83af-43bb-8805-8b6e2983a1f9_1138x773.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GS1o!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb995b37-83af-43bb-8805-8b6e2983a1f9_1138x773.png" width="1138" height="773" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/db995b37-83af-43bb-8805-8b6e2983a1f9_1138x773.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:773,&quot;width&quot;:1138,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:206773,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.felixanalytix.com/i/164397641?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb995b37-83af-43bb-8805-8b6e2983a1f9_1138x773.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!GS1o!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb995b37-83af-43bb-8805-8b6e2983a1f9_1138x773.png 424w, https://substackcdn.com/image/fetch/$s_!GS1o!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb995b37-83af-43bb-8805-8b6e2983a1f9_1138x773.png 848w, https://substackcdn.com/image/fetch/$s_!GS1o!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb995b37-83af-43bb-8805-8b6e2983a1f9_1138x773.png 1272w, https://substackcdn.com/image/fetch/$s_!GS1o!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdb995b37-83af-43bb-8805-8b6e2983a1f9_1138x773.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Build a Box demo application</figcaption></figure></div><p>As you can see in the value box settings, you can <strong>change the inputs</strong> and whatever you write for example numbers and the value box will automatically update. When it&#8217;s done, you can click on <strong>show code</strong> and you can simply download the code.</p><h1>Key Features You Can Customize:</h1><p><strong>Title and Value:</strong> You can change the title, the value, you can easily add extra text in the markdown also if you want. And as you can see it will be showed in the value box.</p><p><strong>Theme Colors:</strong> You can change the theme. This is bootstrap color. So <strong>primary color is blue</strong>. But if you go on <strong>success</strong> for example, it will be <strong>green</strong> because this is a default color for success in bootstrap. If you want to use your own custom colors, you can do it also by clicking on this changing the URL of the colors and you will get the colors that you want.</p><p><strong>Full Screen Mode:</strong> You can also allow full screen. This is quite a nice features of the cards of bslib which is using the background of the value box function. You can click on this button to click on expand and you get extra information about the x-axis which you don&#8217;t see in a smaller value box. So this is pretty nice.</p><p><strong>Showcase Options:</strong> Also, the third section is related to the showcase. You can have a very simple value box like this. But if you want to add an interactive plot or an icon, you can do it very easily. So you can first choose where to put this showcase:</p><ul><li><p>At the bottom</p></li><li><p>As the left center (which is default)</p></li><li><p>At the top right</p></li></ul><p>Let&#8217;s put it back in the left center for example. You decided to have a plot. We can have a <strong>line</strong>, a <strong>bar</strong>, or possibly a <strong>box</strong>. We will see how you can also fully customize this interactive graphics a bit later on. You can also change the colors. You can fully customize them if you want.</p><p>You can also instead of a plot use an <strong>icon</strong> and you have the list of different icons you can use and you can choose the one you want.</p><h1>Required R Packages</h1><p>First, let&#8217;s load the <strong>necessary libraries/R packages</strong> we will use:</p><pre><code>library(bslib)
library(plotly)
library(htmltools)
library(bsicons)
library(shiny)
library(bs4Dash)
library(htmlwidgets)</code></pre><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of the GitHub repository where you can access all the R code of this tutorials.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h1>Creating Value Boxes with bslib</h1><p>Let&#8217;s copy paste the example code that we just created using the demo app. If we run this line of code, you will see in the viewer the <strong>cards appearing</strong>:</p><pre><code>layout_columns(
  value_box(
    title = "Package Numbers", value = "409,446", shiny::markdown("asdfsd"),
    theme = "primary", showcase = "Your Plot", showcase_layout = "left center",
    full_screen = TRUE, fill = TRUE, height = NULL
  ),
  value_box(
    title = "AWS Cloud Spending", value = "$3,463", , theme = "success",
    showcase = "Your Plot", showcase_layout = "left center",
    full_screen = TRUE, fill = TRUE, height = NULL
  ),
  value_box(
    title = "Project Stars", value = "5,100", , theme = NULL,
    showcase = bsicons::bs_icon("star"), showcase_layout = "left center",
    full_screen = FALSE, fill = TRUE, height = NULL
  )
)</code></pre><blockquote><p><em><strong>Key Function:</strong> </em><code>layout_columns</code><em> is a bslib function to create the layout by column as the function says.</em></p></blockquote><h1>Understanding the Parameters:</h1><p>You can simply call the <strong>value box function</strong> with:</p><ul><li><p><strong>Title</strong> &#8212; the main heading &#8226; <strong>Value</strong> &#8212; the key metric or number</p></li><li><p><strong>Extra content</strong> &#8212; markdown or HTML code to fully customize the content</p></li><li><p><strong>Theme</strong> &#8212; color scheme (primary, success, etc.)</p></li><li><p><strong>Showcase</strong> &#8212; where we put the code for plots or icons</p></li><li><p><strong>Full screen</strong> &#8212; set to <code>TRUE</code> to enable expansion, <code>FALSE</code> to disable</p></li><li><p><strong>Fill and height</strong> &#8212; layout customization options</p></li></ul><p>We can change the theme from <strong>primary</strong> (blue) to <strong>success</strong> (green). The showcase is where we will have to put the code for the plot, and if you want the full screen, you put it true. If you want to remove it false. We want to fill and you can also customize the height of your card.</p><h1>Creating Interactive Graphics (Spark Lines)</h1><p>As mentioned previously, we want to create the <strong>interactive graphics</strong> now. The documentation is at:<em> <a href="https://rstudio.github.io/bslib/articles/value-boxes/">https://rstudio.github.io/bslib/articles/value-boxes/</a></em></p><p>As you can see on the official bslib package documentation regarding value boxes, you have all the information to create the value boxes including using shiny and this layout column to wrap it differently using bslib.</p><p>If you go to <strong>expandable spark lines</strong>, which are actually the name for this <strong>mini interactive plots</strong>, you have the code to create this spark line object which is called in the showcase to create this.</p><p>Actually, what I did is simply copy paste the code, and this is the code. Let&#8217;s go quickly through it so you know also how you can <strong>customize your interactive spark line</strong>:</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive FOR FREE by email the URL of my GitHub repository, where you can get the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="paywall-jump" data-component-name="PaywallToDOM"></div><pre><code>sparkline &lt;- plot_ly(economics) %&gt;%
  add_lines(
    x = ~date, y = ~psavert,
    color = I("white"), span = I(1),
    fill = 'tozeroy', alpha = 0.2
  ) %&gt;%
  layout(
    xaxis = list(visible = F, showgrid = F, title = ""),
    yaxis = list(visible = F, showgrid = F, title = ""),
    hovermode = "x",
    margin = list(t = 0, r = 0, l = 0, b = 0),
    font = list(color = "white"),
    paper_bgcolor = "transparent",
    plot_bgcolor = "transparent"
  ) %&gt;%
  config(displayModeBar = FALSE) %&gt;%
  htmlwidgets::onRender(
    "function(el) {
      el.closest('.bslib-value-box')
        .addEventListener('bslib.card', function(ev) {
          Plotly.relayout(el, {'xaxis.visible': ev.detail.fullScreen});
        })
    }"
  )</code></pre><h1>Understanding the Spark Line Code:</h1><p><strong>Data Source:</strong> This is using <strong>plotly</strong> with the <strong>economics</strong> data set which is automatically loaded when you load the plotly package.</p><p><strong>Color Customization:</strong> If you run this first chunk of code, you will see in the viewer your plotly code, but you don&#8217;t see the line because we specify we wanted it <strong>white</strong>. But if you want to have it in a different color and you run again this chunk of code, as you can see now it&#8217;s in <strong>blue</strong>.</p><p><strong>Layout Cleanup:</strong> We also want to remove the grid and some information about the axis. So we use a <strong>layout argument</strong>. Now you don&#8217;t see anything because everything is either <strong>transparent</strong> or <strong>white</strong>. But this is what we want. We want also to remove this <strong>mode bar</strong> so you can call it the display mode bar inside the config function as <strong>false</strong> and this mode bar will disappear.</p><p><strong>Quarto Integration:</strong> Finally, and this is only specifically if you want to use spark line in your <strong>Quarto document reports</strong>, you can use this <strong>on render function</strong> with this javascript code. As explained in the documentation page, this JavaScript is actually equivalent to say <strong>show the x-axis on the chart when it&#8217;s taller than 200 pixels, otherwise hide it</strong>. There was a little bug when you display it with this x-axis and this is the code to fix this. We will see also how to do the equivalent with shiny.</p><h1>Creating a Complete Value Box with Spark Line:</h1><p>We will run this chunk of code to have our spark line object and we will again use value box with a different example with customized code to put extra lines if it&#8217;s useful for you:</p><pre><code>value_box(
  title = "Personal Savings Rate",
  value = "7.6%",
  p("Started at 12.6%"),
  p("Averaged 8.6% over that period"),
  p("Peaked 17.3% in May 1975"),
  showcase = sparkline,
  showcase_layout = "bottom",
  full_screen = TRUE,
  theme = "success"
)</code></pre><p>As you can see in the showcase we have our <strong>spark line object</strong> we just created and we have it at the <strong>bottom</strong>.</p><h1>Creating Value Boxes with Shiny</h1><p>Let&#8217;s see how to do it also with <strong>shiny</strong>. I&#8217;ll take again an example from the official documentation of bslib:</p><pre><code>ui &lt;- page_fluid(
  theme = bslib::bs_theme(
    base_font = font_google("Atkinson Hyperlegible")
  ),
  br(),
  layout_column_wrap(
    width = "200px",
    class = "mt-3",
    value_box(
      title = "Unemployment Rate",
      value = "2.7%",
      p("Started at 1.5%"),
      p("Averaging 3%"),
      p("Peaked at 5.2% in Dec 1982"),
      showcase = plotlyOutput("unemploy"),
      full_screen = TRUE
    ),
    value_box(
      title = "Personal Savings Rate",
      value = "7.6%",
      p("Started at 12.6%"),
      p("Averaging 8.6%"),
      p("Peaked at 17.3% in May 1975"),
      showcase = plotlyOutput("psavert"),
      showcase_layout = showcase_top_right(),
      full_screen = TRUE,
      theme = "success"
    ),
    value_box(
      title = "Personal Consumption",
      value = "$3.8B",
      p("Started at $0.25B"),
      p("Averaging $1.7B"),
      showcase = bsicons::bs_icon("piggy-bank", size = "100%"),
      full_screen = TRUE,
      theme = "danger"
    )
  )
)</code></pre><h1>UI Structure</h1><p>We will create a <strong>page fluid</strong> which is a function from bslib. You can customize it and as again this <strong>layout column wrap</strong> with some classes and the width and we will add the <strong>three value boxes</strong> as examples.</p><blockquote><p><em><strong>Important:</strong> As you can see in the showcase line, instead of calling our spark line object, we will call <strong>plotlyOutput</strong> with an <strong>id</strong> which is actually calling the spark line object in the server side.</em></p></blockquote><h1>Server Logic</h1><p>This is the server code from the documentation:</p><pre><code>server &lt;- function(input, output) {
  output$unemploy &lt;- renderPlotly({
    plotly_time_series(
      economics,
      x = ~date,
      y = ~ 100 * unemploy / pop
    )
  })
  
  output$psavert &lt;- renderPlotly({
    plotly_time_series(
      economics,
      x = ~date,
      y = ~psavert
    )
  })
  
  output$pce &lt;- renderPlotly({
    plotly_time_series(
      economics,
      x = ~date,
      y = ~ 100 * pce / pop
    )
  })
  
  plotly_time_series &lt;- function(d, x, y) {
    info &lt;- getCurrentOutputInfo()
    large &lt;- isTRUE(info$height() &gt; 200)
    
    plot_ly(d, x = x, y = y) %&gt;%
      add_lines(
        color = I(info$fg()),
        span = I(1),
        #hoverinfo = if (!large) "none",
        fill = 'tozeroy',
        alpha = 0.2
      ) %&gt;%
      layout(
        hovermode = "x",
        margin = list(t = 0, r = 0, l = 0, b = 0),
        font = list(color = info$fg()),
        paper_bgcolor = "transparent",
        plot_bgcolor = "transparent",
        xaxis = list(
          title = "",
          visible = large,
          showgrid = FALSE
        ),
        yaxis = list(
          title = "",
          visible = large,
          showgrid = FALSE
        )
      ) %&gt;%
      config(displayModeBar = FALSE)
  }
}

shinyApp(ui, server)</code></pre><p>Again for each <strong>spark line</strong> we have a different <strong>render plotly</strong> calling the <strong>plotly time series function</strong> which is actually a function created which creates a plotly object.</p><p>Let&#8217;s run this chunk of code so you have an idea about what it does. This is the <strong>shiny application</strong> running. And as expected we have our <strong>interactive spark lines</strong> with the different information we wanted. And the <strong>beauty of shiny</strong> is like every time the data updates in the server, new value boxes would be updated accordingly.</p><h1>Creating Value Boxes with bs4Dash</h1><p>Right now I want to show you also how to create value boxes. This time with the <strong>bs4Dash R package</strong>.</p><p><strong>bs4Dash</strong> has different functions to build your packages. It starts with a <strong>dashboard page function</strong> and then you can call the functions for the different argument to build the header, the sidebar, control bar, footer, the title and the body:</p><pre><code>ui &lt;- dashboardPage(
  header = dashboardHeader(),
  sidebar = dashboardSidebar(),
  controlbar = dashboardControlbar(),
  footer = dashboardFooter(),
  title = "test",
  body = dashboardBody(
    fluidRow(
      valueBoxOutput("vbox")
    )
  )
)

server = function(input, output) {
  output$vbox &lt;- renderValueBox({
    valueBox(
      value = 150,
      subtitle = "New orders",
      color = "primary",
      icon = icon("shopping-cart"),
      href = "#"
    )
  })
}

shinyApp(ui, server)</code></pre><p>This is an example taken once again from the <strong>official documentation</strong> from BS4Dash package. And as you can see we will create this time the <strong>value box in the server side</strong>. This is pretty similar to bslib. You can choose:</p><ul><li><p><strong>Value</strong> &#8212; the main metric</p></li><li><p><strong>Subtitle</strong> &#8212; descriptive text</p></li><li><p><strong>Color</strong> &#8212; theme color</p></li><li><p><strong>Icon</strong> &#8212; visual element</p></li><li><p><strong>Href</strong> &#8212; optional link</p></li></ul><p>And this value box is simply rendered in the UI.</p><p>Let&#8217;s run this chunk of code and we will see how it looks like with <strong>BS4Dash</strong>. This is the value box with BS4Dash with the different parameters as we customized them.</p><h1>Adding Spark Lines to bs4Dash Value Boxes</h1><p>Now we want to add a <strong>spark line</strong> also which we customize just as we did with bslib. Let&#8217;s see how to do that now.</p><p>Once again I will reuse the code from the <strong>official documentation</strong>:</p><h1>Enhanced bs4Dash UI with Spark Lines</h1><pre><code>ui &lt;- dashboardPage(
  header = dashboardHeader(),
  sidebar = dashboardSidebar(),
  controlbar = dashboardControlbar(),
  footer = dashboardFooter(),
  title = "bs4Dash value boxes",
  body = bs4DashBody(
    fluidRow(
      valueBox(
        value = 150,
        subtitle = "New orders",
        color = "primary",
        icon = icon("cart-shopping")
      ),
      valueBox(
        value = "53%",
        subtitle = "New orders",
        color = "indigo",
        icon = icon("gears"),
        footer = plotlyOutput("salesSparkline", height = "50px"),
        width = 4,
        elevation = 4
      ),
      valueBox(
        value = "44",
        subtitle = "User Registrations",
        color = "teal",
        icon = icon("sliders")
      )
    )
  )
)</code></pre><p>As you can see it&#8217;s pretty similar, but this time we use the <strong>value box function directly in the UI</strong>. And for the <strong>second value box</strong> we will use this time the <strong>footer argument</strong> to create your interactive spark line once again using <strong>plotly output</strong>.</p><h1>Customized Server Logic</h1><p>If we go on the server side, this time I customized a bit the <strong>economics data set</strong> with some <strong>data wrangling</strong> to make it look better:</p><pre><code>server &lt;- function(input, output) {
  
  output$salesSparkline &lt;- renderPlotly({
    
    econ_data &lt;- economics %&gt;%
      tail(24) %&gt;%  # Using last 24 months for visualization
      mutate(
        month_name = format(date, "%b %Y"),
        unemployment_millions = unemploy / 1000  # Convert to millions for cleaner display
      )
    
    plot_ly(
      data = econ_data, 
      x = ~date, 
      y = ~unemployment_millions, 
      type = "scatter", mode = "lines",
      line = list(color = "#ffffff", width = 2),
      text = ~paste("Date:", month_name, 
                    "&lt;br&gt;Unemployment:", round(unemployment_millions, 2), "M",
                    sep = ""),
      hoverinfo = "text") %&gt;%
      layout(
        paper_bgcolor = "rgba(0,0,0,0)",
        plot_bgcolor = "rgba(0,0,0,0)",
        margin = list(l = 0, r = 0, b = 0, t = 0, pad = 0),
        xaxis = list(
          showgrid = FALSE, 
          zeroline = FALSE, 
          showticklabels = FALSE,
          title = ""  # Removed x-axis title
        ),
        yaxis = list(
          showgrid = FALSE, 
          zeroline = FALSE, 
          showticklabels = FALSE,
          title = ""  # Removed y-axis title
        ),
        showlegend = FALSE,
        hovermode = "closest"
      ) %&gt;%
      config(displayModeBar = FALSE)
  })
}

shinyApp(ui, server)</code></pre><p>And you can again <strong>fully customize your plot object</strong>. And you have the <strong>two arguments to customize the tool tip</strong>. And you can change this code as you want.</p><p>And once it&#8217;s done, you can simply run the <strong>UI</strong>, the <strong>server</strong> and once again <strong>shiny apps</strong>. And as we wanted, we have our value box with this <strong>interactive spark lines</strong> with a nice <strong>tooltip</strong>.</p><h1>Conclusion</h1><p><strong>Value boxes</strong> are a powerful way to display key metrics and information in your R applications. Whether you&#8217;re using <strong>bslib</strong> for modern, customizable cards or <strong>bs4Dash</strong> for dashboard-style layouts, both packages offer extensive customization options.</p><h1>Key Takeaways from This Tutorial:</h1><ul><li><p><strong>Use the BSLib demo app</strong> to quickly prototype your value boxes</p></li><li><p><strong>Integrate interactive spark lines</strong> using plotly for enhanced data visualization</p></li><li><p><strong>Leverage the full-screen capability</strong> for detailed views</p></li><li><p><strong>Choose between bslib and bs4Dash</strong> based on your application&#8217;s design requirements</p></li></ul><p><strong>Happy coding with R!</strong> &#128640;</p>]]></content:encoded></item><item><title><![CDATA[How to FULLY Automate Reports using R]]></title><description><![CDATA[Using parameters in Quarto to automate the production of customized reports]]></description><link>https://www.felixanalytix.com/p/how-to-fully-automate-reports-using</link><guid isPermaLink="false">https://www.felixanalytix.com/p/how-to-fully-automate-reports-using</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Sun, 25 Aug 2024 18:28:11 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/7nIVSSoDFEs" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Would you like to know how to automate reports with customized parameters using the R programming language?</strong></p><p>In this video you will learn how to:</p><ul><li><p>create reports using Quarto in RStudio.</p></li><li><p>add specific parameters / variables to fully customize your reports.</p></li><li><p>use an R script to produce all your reports automatically.</p></li></ul><p>Watch the video now: </p><div id="youtube2-7nIVSSoDFEs" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;7nIVSSoDFEs&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/7nIVSSoDFEs?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h2>Why Automated Reports Are So Useful</h2><p>Posit, the company who developed and maintains Quarto, just <a href="https://posit.co/blog/parameterized-quarto/">released a new article</a> showing how to use parameters in Quarto to automate Word documents, PowerPoints, PDFs, and HTML pages very easily for full automation.</p><p>We&#8217;ll go through this article showing you how easy it is now to have specific parameters like years, months, weeks for example for the timeline, or by name, by companies, by regions - whatever you need.</p><h2>Concrete Example</h2><p>Here is a concrete example: if you want to create reports for different years, you can choose a year parameters and you can create different Word documents or PDFs based on this unique variable.</p><p>As Quarto supports either R or Python, we will concentrate on R programming.</p><h2>Setting Up Your First Parametric Report</h2><p>Let&#8217;s open RStudio now. In RStudio, you just go to File &gt; New File or click on the new file button and simply click on &#8220;Quarto Document&#8221;. This will open a window with a title that will be the title of your document, the author (Felix Analytix), and you can choose HTML, PDF, or Word format. Let&#8217;s go for Word here.</p><p>Here&#8217;s our basic Quarto template with parameters:</p><pre><code><code>---
title: "How to Fully Automate Reports using R programming"
author: "Felix Analytix"
format: docx
params:
  name: "Luke Skywalker"
  year: "2024"
---

Hello `{r} params$name`!

## Sub section for `{r} params$year`

Hello !</code></code></pre><pre><code><code>library(dplyr)

starwars |&gt; 
  dplyr::filter(name == params$name)</code></code></pre><p>Let&#8217;s break down what&#8217;s happening here:</p><ol><li><p>We add two parameters in our YAML header: <code>name</code> and <code>year</code></p></li><li><p>We use the <code>starwars</code> dataset from the <code>dplyr</code> package, which contains information about Star Wars characters</p></li><li><p>We can reference parameters inline using <code>params$name</code> or <code>params$year</code></p></li><li><p>We filter our dataset to show only information for the specified character</p></li></ol><p>When you click &#8220;Render&#8221;, you&#8217;ll get a Word document that says &#8220;Hello Luke Skywalker!&#8221; and shows the filtered dataset containing only Luke Skywalker&#8217;s information.</p><p>You can change the parameter values to get different reports for each Star Wars character. For example, changing the name to &#8220;C-3PO&#8221; will create a report specifically for that character.</p><h2>Full Automation: Creating Multiple Reports</h2><p>Now for the most exciting part - full automation! Our goal is to create different reports for each character name with a specific greeting and filtered dataset.</p><p>For this, we need to create another R script that will generate all reports automatically:</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive FOR FREE by email the URL of my GitHub repository, where you can get the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="paywall-jump" data-component-name="PaywallToDOM"></div><pre><code><code>library(dplyr)
library(purrr)

data &lt;- expand.grid(
  name = unique(starwars$name),
  year = "2024",
  stringsAsFactors = FALSE)

df &lt;- data |&gt; 
  dplyr::mutate(
    output_format = "docx",       # Output format (html, word, etc.)
    output_file = paste(          # Output file name
      name, year, "report.docx",
      sep = "_"
    ),
    execute_params = purrr::map2( # Named list of parameters
      year, name, 
      \(year, name) list(name = name, year = year)
    )
  ) |&gt; 
  dplyr::select(-c(name, year))

df

purrr::pwalk(
  .l = df,                      # Dataframe to map over
  .f = quarto::quarto_render,   # Quarto render function
  input = "20240823-automate-report.qmd",       # Named arguments of .f
  .progress = TRUE              # Optionally, show a progress bar
)</code></code></pre><p>Here&#8217;s what this automation script does:</p><ol><li><p><strong>Create parameter combinations</strong>: We use <code>expand.grid()</code> to create all combinations of names and years</p></li><li><p><strong>Build output specifications</strong>: We specify the output format (docx) and create unique filenames for each report</p></li><li><p><strong>Set up parameters</strong>: We create a list of parameters for each report using <code>purrr::map2()</code></p></li><li><p><strong>Generate all reports</strong>: We use <code>purrr::pwalk()</code> with <code>quarto::quarto_render()</code> to create all reports automatically</p></li></ol><p>When you run this code, you&#8217;ll see in the R console that it&#8217;s building reports for each Star Wars character. This process takes around 10 minutes depending on your system and the number of reports being generated.</p><h2>Key Benefits</h2><p>This automated approach offers several advantages:</p><ul><li><p><strong>Scalability</strong>: Generate hundreds or thousands of reports with minimal effort</p></li><li><p><strong>Consistency</strong>: All reports follow the same template and formatting</p></li><li><p><strong>Efficiency</strong>: Once set up, the entire process runs automatically</p></li><li><p><strong>Flexibility</strong>: Easy to modify parameters and add new variables</p></li></ul><p>While this was already possible with R Markdown, Quarto makes it even more streamlined and integrates better with other programming languages.</p><h2>Conclusion</h2><p>I hope this tutorial was useful! I wanted to share this amazing new feature of Quarto that makes report automation incredibly straightforward. Whether you&#8217;re creating reports for different time periods, clients, regions, or any other parameter, this approach can save you countless hours of manual work.</p><p>The combination of Quarto&#8217;s parametric reports with R&#8217;s data manipulation capabilities provides a powerful framework for automated reporting that scales beautifully with your needs.</p>]]></content:encoded></item><item><title><![CDATA[How to Create Multiple Pages Dashboards with R Shiny]]></title><description><![CDATA[Using bs4Dash to create an R Shiny dashboard with multiple pages in a sidebar]]></description><link>https://www.felixanalytix.com/p/how-to-create-multiple-pages-dashboards</link><guid isPermaLink="false">https://www.felixanalytix.com/p/how-to-create-multiple-pages-dashboards</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Sat, 10 Aug 2024 09:25:52 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/V8ne6_F7lIM" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Coders!</p><p><strong>Would you like to know how to create multiple pages dashboards using R Shiny?</strong></p><p>In this tutorial you will learn how to:</p><ul><li><p>create multiple pages using the bs4Dash R package;</p></li><li><p>build a fully customized sidebar with a logo and different categories of texts;</p></li><li><p>add a footer in your R Shiny dashboards;</p></li><li><p>change the theme and colors using Bootstrap 4;</p></li><li><p>and more&#8230;</p></li></ul><p>Watch the video now: </p><div id="youtube2-V8ne6_F7lIM" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;V8ne6_F7lIM&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/V8ne6_F7lIM?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>In this tutorial, you&#8217;ll learn how to create a dashboard with multiple tabs that function as different pages. While Shiny applications are technically single-page applications, we can create the illusion of multiple pages using tabs and navigation elements.</p><h2><strong>Required Packages</strong></h2><p>To create a dashboard with multiple pages, you&#8217;ll need to install the <code>shiny</code> package (if you haven&#8217;t already) and the <code>bs4Dash</code> package developed by David Granjon. Let&#8217;s start by installing and loading these packages:</p><pre><code><code># Install R packages if not installed

if (!require(shiny)) install.packages("shiny")
if (!require(bs4Dash)) install.packages("bs4Dash")</code></code></pre><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>Creating an Empty Skeleton</strong></h2><p>Let&#8217;s begin with an empty skeleton to understand the basic structure of a <code>bs4Dash</code> application:</p><pre><code><code># Empty skeleton

library(shiny)
library(bs4Dash)

ui &lt;- dashboardPage(
  title = "Basic Dashboard",
  header = dashboardHeader(),
  sidebar = dashboardSidebar(),
  controlbar = dashboardControlbar(),
  footer = dashboardFooter(),
  body = dashboardBody()
)

server &lt;- function(input, output) {}

shinyApp(ui = ui, server = server)</code></code></pre><p>This creates a basic dashboard with an empty sidebar. If you&#8217;re not familiar with Shiny&#8217;s structure (UI, server, and the interaction between them), I recommend checking out my previous videos in this series.</p><h2><strong>Understanding the Dashboard Structure</strong></h2><p>The <code>dashboardPage()</code> function is the main container for your bs4Dash application. It accepts several arguments:</p><ul><li><p><strong>header</strong>: The top navigation bar</p></li><li><p><strong>sidebar</strong>: The left navigation panel</p></li><li><p><strong>controlbar</strong>: An optional right sidebar</p></li><li><p><strong>footer</strong>: The bottom section</p></li><li><p><strong>body</strong>: The main content area</p></li></ul><p>For this tutorial, we&#8217;ll focus primarily on the user interface (UI) since our server function remains empty - we&#8217;re concentrating on layout and navigation rather than reactive functionality.</p><h2><strong>Building a Complete Dashboard</strong></h2><p>Now let&#8217;s create a full example with multiple pages. Here&#8217;s the complete code that demonstrates all the key concepts:</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><div class="paywall-jump" data-component-name="PaywallToDOM"></div><pre><code><code># Full example

ui &lt;- dashboardPage(
  title = "Basic Dashboard",
  header = dashboardHeader(
    title = dashboardBrand(
      title = "bs4Dash",
      color = "primary",
      href = "https://felixanalytix.com",
      image = "https://felixanalytix.com/about/image-profile.jpg",
    ),
    skin = "light",
    status = "white"
  ),
  sidebar = dashboardSidebar(
    skin = "light",
    status = "primary",
    elevation = 3,
    sidebarUserPanel(
      image = NULL,
      name = "Welcome Onboard!"
    ),
    sidebarMenu(
      sidebarHeader("Header 1"),
      menuItem(
        "Item 1",
        tabName = "item1",
        icon = icon("sliders")
      ),
      menuItem(
        "Item 2",
        tabName = "item2",
        icon = icon("chart-simple")
      )
    )
  ),
  footer = dashboardFooter(
    left = p("Download the R code: ", a(
      href = "https://felixanalytix.com",
      target = "_blank", "felixanalytix.com"
    ))
  ),
  body = dashboardBody(
    tabItems(
      tabItem(
        tabName = "item1",
        fluidRow(
          lapply(1:3, FUN = function(i) {
            sortable(
              width = 4,
              p(class = "text-center", paste("Column", i)),
              lapply(1:2, FUN = function(j) {
                box(
                  title = paste0("I am the ", j, "-th card of the ", i, "-th column"),
                  width = 12,
                  "Click on my header"
                )
              })
            )
          })
        )
      ),
      tabItem(
        tabName = "item2",
        box(
          title = "Card with charts",
          width = 9
        )
      )
    )
  )
)

server &lt;- function(input, output) {}

shinyApp(ui = ui, server = server)</code></code></pre><h2><strong>Key Components Explained</strong></h2><h3><strong>Header Configuration</strong></h3><p>The <code>dashboardHeader()</code> creates the top navigation bar. Key features include:</p><ul><li><p><strong>title</strong>: Uses <code>dashboardBrand()</code> to create a clickable logo/title</p></li><li><p><strong>color</strong>: &#8220;primary&#8221; gives it a blue color (Bootstrap default)</p></li><li><p><strong>href</strong>: Makes the title clickable and directs to your website</p></li><li><p><strong>skin</strong>: &#8220;light&#8221; provides a light theme (you can also use &#8220;dark&#8221;)</p></li></ul><h3><strong>Sidebar Navigation</strong></h3><p>The <code>dashboardSidebar()</code> contains your navigation menu:</p><ul><li><p><strong>status</strong>: Controls the color scheme (primary = blue)</p></li><li><p><strong>sidebarUserPanel()</strong>: Adds a welcome message or user info</p></li><li><p><strong>sidebarMenu()</strong>: Contains the navigation items</p></li></ul><h3><strong>Creating Multiple Pages</strong></h3><p>The key to creating multiple pages lies in the connection between <code>menuItem()</code> in the sidebar and <code>tabItem()</code> in the body:</p><ol><li><p><strong>menuItem()</strong>: Each menu item needs a unique <code>tabName</code></p></li><li><p><strong>tabItem()</strong>: Each tab content must have a matching <code>tabName</code></p></li></ol><p>This correspondence is crucial - the <code>tabName</code> in your <code>menuItem()</code> must exactly match the <code>tabName</code> in your <code>tabItem()</code>.</p><h3><strong>Bootstrap Integration</strong></h3><p>The <code>bs4Dash</code> package leverages Bootstrap CSS framework, which provides:</p><ul><li><p>Predefined color schemes (primary, secondary, warning, danger, info)</p></li><li><p>Consistent styling across components</p></li><li><p>Easy theme customization</p></li></ul><p>Instead of hard-coding colors, you can use Bootstrap&#8217;s semantic color names, making it easy to change your entire app&#8217;s color scheme by modifying just the theme.</p><h3><strong>HTML Structure in R</strong></h3><p>You might notice that functions are nested within other functions throughout the code. This mimics HTML&#8217;s tree-like structure where elements are nested within parent elements. This is necessary because our R code is ultimately generating HTML, CSS, and JavaScript for the web browser.</p><h2><strong>Adding Content to Your Pages</strong></h2><p>Each <code>tabItem()</code> can contain any Shiny UI elements:</p><ul><li><p><strong>boxes</strong>: For organizing content</p></li><li><p><strong>fluidRow()</strong>: For responsive layouts</p></li><li><p><strong>Charts, tables, inputs</strong>: Any interactive elements</p></li></ul><h2><strong>Conclusion</strong></h2><p>You now have the foundation for creating multi-page dashboards using <code>bs4Dash</code>. This approach gives you a sidebar-based navigation system, which is different from traditional Shiny applications that typically use tabs at the top of the page.</p><p>The key takeaways are:</p><ol><li><p>Use <code>dashboardPage()</code> as your main container</p></li><li><p>Create navigation with <code>menuItem()</code> in the sidebar</p></li><li><p>Match <code>tabName</code> between menu items and tab content</p></li><li><p>Leverage Bootstrap colors for consistent theming</p></li><li><p>Understand that nested functions mirror HTML structure</p></li></ol><p>In future tutorials, we&#8217;ll explore how to customize these cards and add interactive functionality to make your dashboards even more powerful.</p>]]></content:encoded></item><item><title><![CDATA[How to Customize the Layout of your Shiny Apps]]></title><description><![CDATA[Learn how to control the layout of Shiny apps and improve how their look]]></description><link>https://www.felixanalytix.com/p/how-to-customize-the-layout-of-your</link><guid isPermaLink="false">https://www.felixanalytix.com/p/how-to-customize-the-layout-of-your</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Sun, 28 Apr 2024 12:03:09 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/g46tUbp9uPI" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Would you like to know how to fully customize the layout of your R shiny web applications in a easy way?</strong></p><p>Today you will learn how to:</p><ul><li><p>create a simple R Shiny User Interface (UI) Layout;</p></li><li><p>how to add a sidebar with inputs;</p></li><li><p>add multiple rows, columns, tabs and pages in R Shiny;</p></li><li><p>add a theme and fully customize colors and fonts;</p></li><li><p>and more&#8230;</p></li></ul><p>Watch the video: </p><div id="youtube2-g46tUbp9uPI" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;g46tUbp9uPI&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/g46tUbp9uPI?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><h2>Load R packages</h2><p>Before we begin, make sure you have the required packages installed:</p><pre><code><code>install.packages(c("shiny", "tidyverse", "gt", "bslib"))</code></code></pre><h1>Single Page Layout with Sidebar</h1><p>Let&#8217;s start with the most common Shiny layout pattern - a sidebar layout. This creates a clean interface with controls on the left and main content on the right.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FfvM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70fc6375-f64f-4005-922d-238b8144e8cb_875x413.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FfvM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70fc6375-f64f-4005-922d-238b8144e8cb_875x413.png 424w, https://substackcdn.com/image/fetch/$s_!FfvM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70fc6375-f64f-4005-922d-238b8144e8cb_875x413.png 848w, https://substackcdn.com/image/fetch/$s_!FfvM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70fc6375-f64f-4005-922d-238b8144e8cb_875x413.png 1272w, https://substackcdn.com/image/fetch/$s_!FfvM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70fc6375-f64f-4005-922d-238b8144e8cb_875x413.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FfvM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70fc6375-f64f-4005-922d-238b8144e8cb_875x413.png" width="875" height="413" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/70fc6375-f64f-4005-922d-238b8144e8cb_875x413.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:413,&quot;width&quot;:875,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!FfvM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70fc6375-f64f-4005-922d-238b8144e8cb_875x413.png 424w, https://substackcdn.com/image/fetch/$s_!FfvM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70fc6375-f64f-4005-922d-238b8144e8cb_875x413.png 848w, https://substackcdn.com/image/fetch/$s_!FfvM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70fc6375-f64f-4005-922d-238b8144e8cb_875x413.png 1272w, https://substackcdn.com/image/fetch/$s_!FfvM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F70fc6375-f64f-4005-922d-238b8144e8cb_875x413.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Single Page Layout R Shiny app</figcaption></figure></div><pre><code><code>library(shiny)
library(tidyverse)
library(gt)

ui &lt;- fluidPage(
  titlePanel("Starwars Dashboard"),
  sidebarLayout(
    sidebarPanel(
      selectInput(
        inputId = "character", 
        label = "Choose a Character:", 
        choices = unique(starwars$name),
        selected = unique(starwars$name)[1],
        multiple = TRUE
      )
    ),
    mainPanel(
      gt::gt_output("characterInfos")
    )
  )
)

server &lt;- function(input, output) {
  output$characterInfos &lt;- gt::render_gt({
    dplyr::starwars %&gt;% 
      filter(name %in% input$character) %&gt;%
      gt() %&gt;%
      opt_interactive()
  })
}

shinyApp(ui = ui, server = server)</code></code></pre><p>This layout demonstrates the fundamental structure of a Shiny application:</p><ul><li><p><code>sidebarPanel</code>: Contains input controls (in this case, a character selector)</p></li><li><p><code>mainPanel</code>: Displays the main content (an interactive table)</p></li><li><p><strong>Reactive filtering</strong>: The table updates automatically based on user selection</p></li></ul><p>The sidebar layout is responsive and automatically adapts to different screen sizes, making it mobile-friendly.</p><h1>Multi-Row Layouts with Grid System</h1><p>For more complex interfaces, you&#8217;ll need precise control over element positioning. Shiny uses Bootstrap&#8217;s 12-column grid system, allowing you to create custom layouts with multiple rows and columns.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2-D_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01596551-a920-407b-8d2d-e1cbd9a6ac1a_875x500.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2-D_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01596551-a920-407b-8d2d-e1cbd9a6ac1a_875x500.png 424w, https://substackcdn.com/image/fetch/$s_!2-D_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01596551-a920-407b-8d2d-e1cbd9a6ac1a_875x500.png 848w, https://substackcdn.com/image/fetch/$s_!2-D_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01596551-a920-407b-8d2d-e1cbd9a6ac1a_875x500.png 1272w, https://substackcdn.com/image/fetch/$s_!2-D_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01596551-a920-407b-8d2d-e1cbd9a6ac1a_875x500.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2-D_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01596551-a920-407b-8d2d-e1cbd9a6ac1a_875x500.png" width="875" height="500" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/01596551-a920-407b-8d2d-e1cbd9a6ac1a_875x500.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:875,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!2-D_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01596551-a920-407b-8d2d-e1cbd9a6ac1a_875x500.png 424w, https://substackcdn.com/image/fetch/$s_!2-D_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01596551-a920-407b-8d2d-e1cbd9a6ac1a_875x500.png 848w, https://substackcdn.com/image/fetch/$s_!2-D_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01596551-a920-407b-8d2d-e1cbd9a6ac1a_875x500.png 1272w, https://substackcdn.com/image/fetch/$s_!2-D_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F01596551-a920-407b-8d2d-e1cbd9a6ac1a_875x500.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">example of creating multiple rows in R Shiny</figcaption></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="paywall-jump" data-component-name="PaywallToDOM"></div><pre><code><code>ui &lt;- fluidPage(
  titlePanel("Starwars Dashboard"),
  fluidRow(
    column(6, plotOutput("plot1")),
    column(6, plotOutput("plot2"))
  ),
  fluidRow(
    column(12, gt::gt_output("table"))
  )
)

server &lt;- function(input, output) {
  output$plot1 &lt;- renderPlot({ 
    starwars %&gt;%
      count(hair_color, gender) %&gt;%
      ggplot(aes(hair_color, n, fill = gender)) + 
      geom_col()
    })
  output$plot2 &lt;- renderPlot({ 
    starwars %&gt;%
      count(eye_color, gender) %&gt;%
      ggplot(aes(eye_color, n, fill = gender)) + 
      geom_col()
    })
  output$table &lt;- gt::render_gt({ starwars })
}

shinyApp(ui = ui, server = server)</code></code></pre><p><strong>Key concepts:</strong></p><ul><li><p><code>fluidRow()</code>: Creates horizontal rows</p></li><li><p><code>column(width, ...)</code>: Defines column width (1-12) and content</p></li><li><p><strong>Bootstrap grid</strong>: Total width of 12 columns per row</p></li><li><p><strong>Responsive design</strong>: Automatically stacks columns on smaller screens</p></li></ul><p>In this example: - First row: Two plots side by side (6 columns each) - Second row: One full-width table (12 columns)</p><h1>Tabbed Interfaces</h1><p>Tabs are perfect for organizing related content without overwhelming users. They provide a clean way to separate different views or analyses.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UW4i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ba7eb8e-5361-4f83-8fc0-c4fce981a681_875x439.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UW4i!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ba7eb8e-5361-4f83-8fc0-c4fce981a681_875x439.png 424w, https://substackcdn.com/image/fetch/$s_!UW4i!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ba7eb8e-5361-4f83-8fc0-c4fce981a681_875x439.png 848w, https://substackcdn.com/image/fetch/$s_!UW4i!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ba7eb8e-5361-4f83-8fc0-c4fce981a681_875x439.png 1272w, https://substackcdn.com/image/fetch/$s_!UW4i!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ba7eb8e-5361-4f83-8fc0-c4fce981a681_875x439.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UW4i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ba7eb8e-5361-4f83-8fc0-c4fce981a681_875x439.png" width="875" height="439" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0ba7eb8e-5361-4f83-8fc0-c4fce981a681_875x439.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:439,&quot;width&quot;:875,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!UW4i!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ba7eb8e-5361-4f83-8fc0-c4fce981a681_875x439.png 424w, https://substackcdn.com/image/fetch/$s_!UW4i!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ba7eb8e-5361-4f83-8fc0-c4fce981a681_875x439.png 848w, https://substackcdn.com/image/fetch/$s_!UW4i!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ba7eb8e-5361-4f83-8fc0-c4fce981a681_875x439.png 1272w, https://substackcdn.com/image/fetch/$s_!UW4i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ba7eb8e-5361-4f83-8fc0-c4fce981a681_875x439.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">R Shiny example with multiple panels</figcaption></figure></div><pre><code><code>ui &lt;- fluidPage(
  titlePanel("Starwars Dashboard"),
  tabsetPanel(
    tabPanel("Plot", plotOutput("plot")),
    tabPanel("Summary", verbatimTextOutput("summary")),
    tabPanel("Data", gt::gt_output("table"))
  )
)

server &lt;- function(input, output) {
  output$table &lt;- gt::render_gt({ starwars })
  output$summary &lt;- renderPrint({ summary(starwars$height) })
  output$plot &lt;- renderPlot({ ggplot(starwars, aes(x = height, fill = gender)) + 
      geom_density(alpha = 0.5) })
}

shinyApp(ui = ui, server = server)</code></code></pre><p><strong>Tab components:</strong> - <code>tabsetPanel()</code>: Container for all tabs - <code>tabPanel(title, content)</code>: Individual tab with title and content - <strong>Content flexibility</strong>: Each tab can contain any Shiny output</p><p>You can also combine tabs with the grid system by adding <code>fluidRow()</code> and <code>column()</code> within tab panels for even more layout control.</p><h1>Multi-Page Applications with Navigation</h1><p>For larger applications, you might need multiple pages. The <code>navbarPage()</code> function creates a professional navigation bar at the top of your application.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QkJT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4508e256-d40e-41a8-9137-4f5225318310_875x454.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QkJT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4508e256-d40e-41a8-9137-4f5225318310_875x454.png 424w, https://substackcdn.com/image/fetch/$s_!QkJT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4508e256-d40e-41a8-9137-4f5225318310_875x454.png 848w, https://substackcdn.com/image/fetch/$s_!QkJT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4508e256-d40e-41a8-9137-4f5225318310_875x454.png 1272w, https://substackcdn.com/image/fetch/$s_!QkJT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4508e256-d40e-41a8-9137-4f5225318310_875x454.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QkJT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4508e256-d40e-41a8-9137-4f5225318310_875x454.png" width="875" height="454" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4508e256-d40e-41a8-9137-4f5225318310_875x454.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:454,&quot;width&quot;:875,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!QkJT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4508e256-d40e-41a8-9137-4f5225318310_875x454.png 424w, https://substackcdn.com/image/fetch/$s_!QkJT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4508e256-d40e-41a8-9137-4f5225318310_875x454.png 848w, https://substackcdn.com/image/fetch/$s_!QkJT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4508e256-d40e-41a8-9137-4f5225318310_875x454.png 1272w, https://substackcdn.com/image/fetch/$s_!QkJT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4508e256-d40e-41a8-9137-4f5225318310_875x454.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Example of multiple pages with an navbar in R Shiny</figcaption></figure></div><pre><code><code>ui &lt;- navbarPage(
  title = "Starwars Dashboard",
  tabPanel("Introduction", p("An app for analyzing Starwars data.")),
  tabPanel("Data", gt::gt_output("dataTable")),
  navbarMenu("More Analyses",
             tabPanel("Height Analysis", plotOutput("heightPlot")),
             tabPanel("Mass Analysis", plotOutput("massPlot"))
  )
)

server &lt;- function(input, output) {
  output$dataTable &lt;- gt::render_gt({ gt(starwars) |&gt; opt_interactive() })
  output$heightPlot &lt;- renderPlot({ ggplot(starwars, aes(height)) + geom_histogram() })
  output$massPlot &lt;- renderPlot({ ggplot(starwars, aes(mass)) + geom_histogram() })
}

shinyApp(ui = ui, server = server)</code></code></pre><p><strong>Navigation features:</strong> - <code>navbarPage()</code>: Creates the main navigation structure - <code>tabPanel()</code>: Individual pages in the navigation - <code>navbarMenu()</code>: Dropdown menus for grouping related pages - <strong>Professional appearance</strong>: Clean, modern navigation bar</p><h1>Theming with Bootstrap</h1><p>The <code>bslib</code> package provides easy access to pre-built Bootstrap themes, allowing you to quickly change your application&#8217;s appearance.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hKUB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0c4d3a3-ede8-4f0d-ac33-8300f021e5af_875x327.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hKUB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0c4d3a3-ede8-4f0d-ac33-8300f021e5af_875x327.png 424w, https://substackcdn.com/image/fetch/$s_!hKUB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0c4d3a3-ede8-4f0d-ac33-8300f021e5af_875x327.png 848w, https://substackcdn.com/image/fetch/$s_!hKUB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0c4d3a3-ede8-4f0d-ac33-8300f021e5af_875x327.png 1272w, https://substackcdn.com/image/fetch/$s_!hKUB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0c4d3a3-ede8-4f0d-ac33-8300f021e5af_875x327.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hKUB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0c4d3a3-ede8-4f0d-ac33-8300f021e5af_875x327.png" width="875" height="327" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b0c4d3a3-ede8-4f0d-ac33-8300f021e5af_875x327.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:327,&quot;width&quot;:875,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!hKUB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0c4d3a3-ede8-4f0d-ac33-8300f021e5af_875x327.png 424w, https://substackcdn.com/image/fetch/$s_!hKUB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0c4d3a3-ede8-4f0d-ac33-8300f021e5af_875x327.png 848w, https://substackcdn.com/image/fetch/$s_!hKUB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0c4d3a3-ede8-4f0d-ac33-8300f021e5af_875x327.png 1272w, https://substackcdn.com/image/fetch/$s_!hKUB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb0c4d3a3-ede8-4f0d-ac33-8300f021e5af_875x327.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><pre><code><code>library(bslib)

ui &lt;- navbarPage(
  title = "Starwars Dashboard",
  theme = bslib::bs_theme(bootswatch = "cerulean"), # BOOTSWATCH THEME
  tabPanel("Introduction", p("An app for analyzing Starwars data.")),
  tabPanel("Data", gt::gt_output("dataTable")),
  navbarMenu("More Analyses",
             tabPanel("Height Analysis", plotOutput("heightPlot")),
             tabPanel("Mass Analysis", plotOutput("massPlot"))
  )
)

server &lt;- function(input, output) {
  output$dataTable &lt;- gt::render_gt({ gt(starwars) |&gt; opt_interactive() })
  output$heightPlot &lt;- renderPlot({ ggplot(starwars, aes(height)) + geom_histogram() })
  output$massPlot &lt;- renderPlot({ ggplot(starwars, aes(mass)) + geom_histogram() })
}

shinyApp(ui = ui, server = server)</code></code></pre><p>Popular Bootswatch themes include: - <code>cerulean</code> (shown above) - <code>flatly</code> - <code>darkly</code> - <code>cosmo</code> - <code>journal</code> - <code>lumen</code></p><h1>Custom Color Schemes</h1><p>For complete control over your application&#8217;s appearance, you can define custom colors and styling with <code>bslib</code>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jB_O!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6eff74-3939-45b0-8308-55ac8e2cf4a1_875x439.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jB_O!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6eff74-3939-45b0-8308-55ac8e2cf4a1_875x439.png 424w, https://substackcdn.com/image/fetch/$s_!jB_O!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6eff74-3939-45b0-8308-55ac8e2cf4a1_875x439.png 848w, https://substackcdn.com/image/fetch/$s_!jB_O!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6eff74-3939-45b0-8308-55ac8e2cf4a1_875x439.png 1272w, https://substackcdn.com/image/fetch/$s_!jB_O!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6eff74-3939-45b0-8308-55ac8e2cf4a1_875x439.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jB_O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6eff74-3939-45b0-8308-55ac8e2cf4a1_875x439.png" width="875" height="439" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5c6eff74-3939-45b0-8308-55ac8e2cf4a1_875x439.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:439,&quot;width&quot;:875,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!jB_O!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6eff74-3939-45b0-8308-55ac8e2cf4a1_875x439.png 424w, https://substackcdn.com/image/fetch/$s_!jB_O!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6eff74-3939-45b0-8308-55ac8e2cf4a1_875x439.png 848w, https://substackcdn.com/image/fetch/$s_!jB_O!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6eff74-3939-45b0-8308-55ac8e2cf4a1_875x439.png 1272w, https://substackcdn.com/image/fetch/$s_!jB_O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6eff74-3939-45b0-8308-55ac8e2cf4a1_875x439.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Background, foreground and primary color customized of an R shiny app</figcaption></figure></div><pre><code><code>library(bslib)

ui &lt;- fluidPage(
  theme = bslib::bs_theme(bg = "#E8E8E8", fg = "#242424", primary = "#D4B836"),
  titlePanel("Starwars Dashboard"),
  sidebarLayout(
    sidebarPanel(
      sliderInput("massInput", "Select Maximum Mass:",
                  min = min(starwars$mass, na.rm = TRUE),
                  max = max(starwars$mass, na.rm = TRUE),
                  value = 40)
    ),
    mainPanel(
      plotOutput("massPlot")
    )
  )
)

server &lt;- function(input, output) {
  output$massPlot &lt;- renderPlot({
    starwars %&gt;%
      filter(mass &lt;= input$massInput) %&gt;%
      ggplot(aes(x = name, y = mass)) +
      geom_col()
  })
}

shinyApp(ui = ui, server = server)</code></code></pre><p><strong>Custom theme parameters:</strong> - <code>bg</code>: Background color - <code>fg</code>: Foreground (text) color<br>- <code>primary</code>: Primary accent color for buttons, links, etc. - <strong>Additional options</strong>: <code>secondary</code>, <code>success</code>, <code>info</code>, <code>warning</code>, <code>danger</code></p><h1>Conclusion</h1><p>You now have the tools to create sophisticated Shiny layouts! Here&#8217;s what we covered:</p><ol><li><p><strong>Basic sidebar layouts</strong> for simple interfaces</p></li><li><p><strong>Grid system</strong> with rows and columns for precise control</p></li><li><p><strong>Tabbed interfaces</strong> to organize content</p></li><li><p><strong>Multi-page navigation</strong> for complex applications</p></li><li><p><strong>Theme customization</strong> for professional styling</p></li></ol><p>See you in another tutorial!</p>]]></content:encoded></item><item><title><![CDATA[R Shiny Web App for Data Science - The Basics]]></title><description><![CDATA[Learn the basics of R Shiny by creating your first Web application using R]]></description><link>https://www.felixanalytix.com/p/r-shiny-web-app-for-data-science</link><guid isPermaLink="false">https://www.felixanalytix.com/p/r-shiny-web-app-for-data-science</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Thu, 01 Feb 2024 16:15:16 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/15b2a5eb-8bf5-4c5c-9e54-f965eb32b5c6_1280x720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>I just publish <a href="https://youtu.be/qLgq8O22UsQ">the first video</a> of my new YouTube serie </strong><em><strong>R Shiny Web App for Data Science"</strong></em><strong>, where I teach you how to create fully customized and beautiful R Shiny applications.</strong></p><p>In this first episode you will learn how to:</p><ul><li><p>access a gallery of hundreds of examples of R Shiny applications;</p></li><li><p>fully understand all the R code behind a basic R Shiny app;</p></li><li><p>get the intuition of how input-output interactions work in a Shiny app;</p></li><li><p>add a new text input in a Shiny app.</p></li></ul><p>Check out the video now: </p><div id="youtube2-qLgq8O22UsQ" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;qLgq8O22UsQ&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/qLgq8O22UsQ?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h2>Starting with a Simple Single Layout</h2><p>Let&#8217;s begin with the most basic Shiny layout structure. Here&#8217;s our first example:</p><pre><code><code># This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
#    https://shiny.posit.co/
#

library(shiny)

# Define UI for application that draws a histogram
ui &lt;- fluidPage(

    # Application title
    titlePanel("Old Faithful Geyser Data"),

    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
            sliderInput("bins",
                        "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30)
        ),

        # Show a plot of the generated distribution
        mainPanel(
           plotOutput("distPlot")
        )
    )
)

# Define server logic required to draw a histogram
server &lt;- function(input, output) {

    output$distPlot &lt;- renderPlot({
        # generate bins based on input$bins from ui.R
        x    &lt;- faithful[, 2]
        bins &lt;- seq(min(x), max(x), length.out = input$bins + 1)

        # draw the histogram with the specified number of bins
        hist(x, breaks = bins, col = 'darkgray', border = 'white',
             xlab = 'Waiting time to next eruption (in mins)',
             main = 'Histogram of waiting times')
    })
}

# Run the application 
shinyApp(ui = ui, server = server)</code></code></pre><p>This code demonstrates the fundamental structure of a Shiny application. The <code>library(shiny)</code> command loads the Shiny package, which is essential for creating web applications. The <code>ui</code> object defines the user interface using <code>fluidPage()</code>, which creates a responsive layout that automatically adjusts to different screen sizes.</p><p>The <code>titlePanel()</code> function creates the application header, while <code>sidebarLayout()</code> establishes the classic two-column structure with a sidebar on the left and main panel on the right. Inside <code>sidebarPanel()</code>, we use <code>sliderInput()</code> to create an interactive slider that allows users to select the number of bins for the histogram. The <code>mainPanel()</code> contains <code>plotOutput()</code>, which displays the histogram generated by the server logic.</p><p>The <code>server</code> function contains the reactive logic. The <code>renderPlot()</code> function creates the histogram dynamically based on the user&#8217;s input from the slider. It accesses the faithful dataset (built into R), extracts the waiting times, creates bins based on the slider value, and generates the histogram with custom styling.</p><h2>Adding More Interactive Elements</h2><p>Now let&#8217;s enhance our application by adding more interactive elements to see how the layout adapts:</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get all the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="paywall-jump" data-component-name="PaywallToDOM"></div><pre><code><code>library(shiny)

# Define UI for application that draws a histogram
ui &lt;- fluidPage(

    # Application title
    titlePanel("Old Faithful Geyser Data"),

    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
            sliderInput("bins",
                        "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30),
            textInput("text",
                      label = h3("what is your name?"),
                      value = "Felix")
        ),

        # Show a plot of the generated distribution
        mainPanel(
           plotOutput("distPlot"),
           verbatimTextOutput("valueText")
        )
    )
)

# Define server logic required to draw a histogram
server &lt;- function(input, output) {

    output$valueText &lt;- renderText({ input$text })
    
    output$distPlot &lt;- renderPlot({
        # generate bins based on input$bins from ui.R
        x    &lt;- faithful[, 2]
        bins &lt;- seq(min(x), max(x), length.out = input$bins + 1)

        # draw the histogram with the specified number of bins
        hist(x, breaks = bins, col = 'darkgray', border = 'white',
             xlab = 'Waiting time to next eruption (in mins)',
             main = 'Histogram of waiting times')
    })
}

# Run the application 
shinyApp(ui = ui, server = server)</code></code></pre><p>In this enhanced version, we&#8217;ve expanded both the sidebar and main panel with additional elements. The <code>textInput()</code> function creates a text input field where users can enter their name. The <code>h3()</code> function creates an HTML heading for the input label, making it more prominent than regular text.</p><p>In the main panel, we&#8217;ve added <code>verbatimTextOutput()</code> alongside the existing plot. This creates a text output area that displays exactly what the user types in the text input field. The <code>renderText()</code> function in the server creates a reactive text output that automatically updates whenever the user changes the input.</p><p>This demonstrates how the <code>sidebarLayout()</code> can accommodate multiple elements in both panels while maintaining the clean, organized structure. The layout automatically handles the spacing and arrangement of these elements.</p><p>See you in another tutorial!</p>]]></content:encoded></item><item><title><![CDATA[How to Get Organized with RStudio Projects and Git]]></title><description><![CDATA[Use RStudio Projects and Git to organize your data science projects]]></description><link>https://www.felixanalytix.com/p/how-to-get-organized-with-rstudio</link><guid isPermaLink="false">https://www.felixanalytix.com/p/how-to-get-organized-with-rstudio</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Sat, 09 Dec 2023 13:45:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/N9uXRG6DYIw" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Would you like to better organize your data science projects following a simple and consistant workflow?</strong></p><p>In my <a href="https://youtu.be/N9uXRG6DYIw">new YouTube video</a>, I show you how to use RStudio Projects and Git version control to follow a consistant file structure and track all the changes in your code.</p><p>You will learn how to:</p><ul><li><p>use RStudio projects and connect them to Git and GitHub (the easy way);</p></li><li><p>use RStudio Git panel view to commit, push and pull files with 1 click;</p></li><li><p>see an example of a data science project file structure.</p></li></ul><p>Watch the video now:</p><div id="youtube2-N9uXRG6DYIw" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;N9uXRG6DYIw&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/N9uXRG6DYIw?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Data science projects can quickly become chaotic without proper organization and version control. In this tutorial, you&#8217;ll learn how to use RStudio Projects and Git to maintain clean, organized, and portable data science workflows.</p><h2>The Problem: Disorganized Project Folders</h2><p>Many data scientists end up with folders that look like this:</p><ul><li><p><code>analysis.R</code></p></li><li><p><code>new-analysis.R</code></p></li><li><p><code>new-analysis-version2.R</code></p></li><li><p><code>ideas-from-document.R</code></p></li><li><p>Mixed files from different projects in the same directory</p></li></ul><p>This disorganized approach leads to confusion, lost work, and difficulty collaborating with others. Let&#8217;s fix this using RStudio Projects and Git version control.</p><h2>Creating Your First RStudio Project</h2><h3>Step 1: Start a New Project</h3><ol><li><p>Click on the <strong>New Project</strong> button in the top-right corner of RStudio</p></li><li><p>You&#8217;ll see three options:</p><ul><li><p><strong>New Directory</strong>: Creates a new folder</p></li><li><p><strong>Existing Directory</strong>: Uses an existing folder</p></li><li><p><strong>Version Control</strong>: Connects to Git (we&#8217;ll cover this later)</p></li></ul></li></ol><h3>Step 2: Choose Project Type</h3><p>Select <strong>New Directory</strong>, then choose from various project templates: - Empty new project (recommended for beginners) - R package - Shiny application - Quarto projects - And other specialized templates</p><h3>Step 3: Configure Your Project</h3><ol><li><p><strong>Directory name</strong>: This becomes your project folder name (e.g., &#8220;organized&#8221;)</p></li><li><p><strong>Subdirectory</strong>: Choose where to save your project</p><ul><li><p>Pro tip: Create a dedicated folder for all your data science projects</p></li><li><p>Consider syncing with cloud storage (OneDrive, Google Drive, etc.)</p></li></ul></li><li><p><strong>Optional settings</strong>:</p><ul><li><p>Git repository (we&#8217;ll set this up separately)</p></li><li><p><code>renv</code> for package management (recommended for advanced users)</p></li></ul></li></ol><p>Click <strong>Create Project</strong> to finish setup.</p><h2>Understanding Project Benefits</h2><h3>Automatic Working Directory</h3><p>RStudio Projects automatically set your working directory to the project root. This means:</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get all the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="paywall-jump" data-component-name="PaywallToDOM"></div><pre><code><code># Instead of complex file paths like this:
read_csv("/Users/username/long/path/to/project/data/data.csv")

# You can use simple relative paths:
read_csv("data/data.csv")</code></code></pre><p>This makes your projects <strong>portable</strong> &#8211; you can move the entire folder anywhere and the code still works.</p><h3>Project File Structure</h3><p>The <code>.Rproj</code> file in your project folder tells RStudio this is an RStudio Project. Benefits include:</p><ul><li><p>Easy project switching via the top-right dropdown menu</p></li><li><p>Automatic working directory management</p></li><li><p>Project-specific settings and history</p></li><li><p>Better organization across multiple projects</p></li></ul><h2>Recommended Folder Structure</h2><p>For consistency across projects, follow R package conventions:</p><pre><code><code>your-project/
&#9500;&#9472;&#9472; data/           # Raw and processed data
&#9500;&#9472;&#9472; R/              # R scripts and functions
&#9500;&#9472;&#9472; docs/           # Documentation
&#9500;&#9472;&#9472; outputs/        # Results, plots, reports
&#9500;&#9472;&#9472; README.md       # Project description
&#9492;&#9472;&#9472; your-project.Rproj</code></code></pre><h2>Connecting Git to RStudio Projects</h2><h3>Why Use Git?</h3><p>Git helps you: </p><ul><li><p>Track changes to your code over time </p></li><li><p>Collaborate with others</p></li><li><p>Back up your work online</p></li><li><p>Revert to previous versions when needed</p></li></ul><h3>Method 1: Add Git to Existing Project</h3><p>If you already have an RStudio Project, use the <code>usethis</code> package:</p><pre><code><code>library(usethis)
use_git()
use_github(private = TRUE)  # Set private = TRUE for private repos</code></code></pre><p><strong>Important</strong>: The <code>private</code> argument defaults to <code>FALSE</code>, making your repository public!</p><h3>Method 2: Create Project with Git (Recommended)</h3><p>This approach avoids common Git setup issues:</p><ol><li><p><strong>Create GitHub repository first</strong>:</p><ul><li><p>Go to GitHub.com and create a new repository</p></li><li><p>Add a description</p></li><li><p>Choose public or private</p></li><li><p>Copy the HTTPS URL</p></li></ul></li><li><p><strong>Clone in RStudio</strong>:</p><ul><li><p>Choose <strong>New Project</strong> &gt; <strong>Version Control</strong> &gt; <strong>Git</strong></p></li><li><p>Paste the repository URL</p></li><li><p>RStudio will automatically name the project after your repository</p></li><li><p>Choose your subdirectory location</p></li></ul></li></ol><h2>Working with Git in RStudio</h2><h3>The Git Panel</h3><p>Once connected, you&#8217;ll see a <strong>Git</strong> tab in RStudio showing: - Modified files - New files - Files ready to commit</p><h3>Using .gitignore</h3><p>The <code>.gitignore</code> file tells Git which files to ignore. Common entries:</p><pre><code><code># Credentials and sensitive data
credentials.R
*.env

# Large data files
data/large-dataset.csv

# Temporary files
.Rhistory
.RData</code></code></pre><h3>The Git Workflow</h3><ol><li><p><strong>Stage files</strong>: Check the boxes next to files you want to commit</p></li><li><p><strong>Commit</strong>: Click &#8220;Commit&#8221; and write a descriptive message</p></li><li><p><strong>Push</strong>: Upload your changes to GitHub</p></li></ol><h3>Viewing Changes</h3><p>RStudio&#8217;s Git integration shows you: - Which files have been modified - Exactly what changed in each file (diff view) - When you right-click a file and select &#8220;Diff selected files&#8221;</p><p>This helps you: - Spot unintended changes or typos - Review your work before committing - Understand what&#8217;s changed since your last commit</p><h2>Best Practices Summary</h2><ol><li><p><strong>Always use RStudio Projects</strong> for better organization and portability</p></li><li><p><strong>Connect Git early</strong> in your project lifecycle</p></li><li><p><strong>Use consistent folder structures</strong> across projects</p></li><li><p><strong>Write meaningful commit messages</strong> to track your progress</p></li><li><p><strong>Regularly commit and push</strong> your work</p></li><li><p><strong>Review diffs</strong> before committing to catch errors</p></li></ol><p>See you in another tutorial!</p>]]></content:encoded></item><item><title><![CDATA[How to Create Interactive Pivot Tables with 1 Line of R Code]]></title><description><![CDATA[Learn how to create and customize interactive pivot tables using R]]></description><link>https://www.felixanalytix.com/p/how-to-create-interactive-pivot-tables</link><guid isPermaLink="false">https://www.felixanalytix.com/p/how-to-create-interactive-pivot-tables</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Sat, 11 Nov 2023 15:48:39 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/81bdfed5-cc1a-43b5-846e-c2866ecc7784_1280x720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Would you like to know how to create interactive pivot tables and customize them using R programming?</strong></p><p>In my <a href="https://youtu.be/C3oCd4_iQ0M">new YouTube video</a>, I show you how to use and customize interactive pivot tables for any dataset using the rpivotTable R package.</p><p>You will learn how to:</p><ul><li><p>create an interactive pivot table with 1 line of code;</p></li><li><p>use the pivot table functionalities to explore any dataset;</p></li><li><p>customize your pivot table with CSS and Javascript.</p></li></ul><p>Watch the video now:</p><div id="youtube2-C3oCd4_iQ0M" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;C3oCd4_iQ0M&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/C3oCd4_iQ0M?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get all the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[How to FULLY Customize Leaflet Maps using R programming]]></title><description><![CDATA[Reproducing an official Swiss map of the top surnames by commune with Leaflet]]></description><link>https://www.felixanalytix.com/p/how-to-fully-customize-leaflet-maps</link><guid isPermaLink="false">https://www.felixanalytix.com/p/how-to-fully-customize-leaflet-maps</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Fri, 06 Oct 2023 11:30:20 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/80fa0bec-458e-433e-8c10-e75ca947911a_1280x720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Would you like to know how to customize interactive maps using Leaflet with R programming?</strong></p><p>In this <a href="https://youtu.be/U0aJeaCnMeE">new YouTube video</a> I show you how to fully customize your leaflet maps. As an example I reproduce an Swiss official choropleth map of Switzerland showing the top 5 most common surnames by commune.</p><p>You will learn how to:</p><ul><li><p>Search and download official Swiss data.</p></li><li><p>Access official Swiss geographic data.</p></li><li><p>Fully customize the Leaflet map, such as:</p><ul><li><p>adding anything in tooltips.</p></li><li><p>customizing the legend.</p></li><li><p>controlling zooming and map limits.</p></li><li><p>creating an empty background.</p></li></ul></li></ul><p>Check out the video now:</p><div id="youtube2-U0aJeaCnMeE" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;U0aJeaCnMeE&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/U0aJeaCnMeE?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get all the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Interactive Country Maps with R]]></title><description><![CDATA[Learn how to create Interactive Maps of any European Country using R programming]]></description><link>https://www.felixanalytix.com/p/make-beautiful-interactive-maps-with</link><guid isPermaLink="false">https://www.felixanalytix.com/p/make-beautiful-interactive-maps-with</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Tue, 23 May 2023 14:03:27 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b1266034-d2e7-4a40-aeaf-cc685f26fc15_1280x720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Would you like to learn how to create interactive maps of any European country at a district level using R?</strong></p><p>In this <a href="https://youtu.be/f-Vnfe7WmJM">new YouTube video</a> I show you how to map Switzerland at a regional level using Eurostat geodata. But these steps can be used for any European country.</p><p>You will learn how to:</p><ul><li><p>Get counties/provinces/districts level data from any country in Europe.</p></li><li><p>Join your own dataset to the geographic dataset.</p></li><li><p>Build an interactive map with just 1 line of code.</p></li><li><p>Create an interactive slider to compare two maps.</p></li><li><p>Synchronize multiple interactive maps together.</p></li></ul><p>Check out the video now:</p><div id="youtube2-f-Vnfe7WmJM" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;f-Vnfe7WmJM&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/f-Vnfe7WmJM?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get all the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Geocode ANY location using R]]></title><description><![CDATA[Get the latitude and longitude of any place using Google Maps, Bing or Mapbox]]></description><link>https://www.felixanalytix.com/p/geocode-any-location-using-r</link><guid isPermaLink="false">https://www.felixanalytix.com/p/geocode-any-location-using-r</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Tue, 18 Apr 2023 11:39:48 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/8c5a34b9-756d-4aa6-85a4-7e8e434bc2c8_1280x720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Do you have a list of places/locations and you would like to visualize them on an interactive map?</strong></p><p>In my <a href="https://youtu.be/7C49i6JMZTc">new YouTube video</a>, I show you how to use geocoding services such as Google Maps, Bing, Mapbox or OpenStreetMap to get the latitude and longitude of any location and plot them on an interactive map.</p><div id="youtube2-7C49i6JMZTc" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;7C49i6JMZTc&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/7C49i6JMZTc?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get all the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Make Beautiful Tables using R]]></title><description><![CDATA[Fully customize your tables with colors, images and graphics with gt]]></description><link>https://www.felixanalytix.com/p/how-to-make-beautiful-tables-using</link><guid isPermaLink="false">https://www.felixanalytix.com/p/how-to-make-beautiful-tables-using</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Wed, 01 Mar 2023 15:11:08 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5b06ffd7-ff00-40a5-952a-7a12fd190eb8_1280x720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Would you like to learn how to create more engaging and better looking tables using the R programming language?</strong></p><p>I just release a <a href="https://youtu.be/tmLoa_zL010">new YouTube video</a> showing you how to access Spotify data to get the top 10 most listen songs and visualize them on a table.</p><p>You will learn how to:</p><ul><li><p><strong>fully customize any element of a table </strong>and use pre-built themes.</p></li><li><p><strong>add any images</strong> programmatically from the Web.</p></li><li><p><strong>insert graphics </strong>such as bar plots, sparklines, etc.</p></li><li><p><strong>merge columns</strong> to better synthesis information.</p></li></ul><div id="youtube2-tmLoa_zL010" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;tmLoa_zL010&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/tmLoa_zL010?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get all the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[How to Map ANY region of the world using R]]></title><description><![CDATA[Using R to Map Europe, Asia, Africa, America, etc.]]></description><link>https://www.felixanalytix.com/p/how-to-map-any-region-of-the-world</link><guid isPermaLink="false">https://www.felixanalytix.com/p/how-to-map-any-region-of-the-world</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Sun, 29 Jan 2023 13:30:29 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/8bb111be-a9a9-4d9f-9800-ff5a8bf5b071_1280x720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Would you like to learn how to create beautiful maps of any region of the world using the R programming language?</strong></p><p>I just published a <a href="https://youtu.be/KZcKv3HgzII">new YouTube video</a> showing you how to create a choropleth world map using World Bank unemployment data, and how to zoom on any specific region. As an example, we will visualize the European region only.</p><p>But of course, you can reuse the R code showed in the video with your own dataset on any region of your choice. You will see how easily it can be done using R.</p><p>At the end of the video I discuss some important points regarding the use of the scale and the color palette when creating a map, so be sure to watch the video until the end.</p><p>Don&#8217;t hesitate to write any question you have in the comments of the video.</p><div id="youtube2-KZcKv3HgzII" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;KZcKv3HgzII&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/KZcKv3HgzII?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get all the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Google Sheet Automation using R]]></title><description><![CDATA[How to read, write, append data, and change Google Sheet user permissions from R]]></description><link>https://www.felixanalytix.com/p/google-sheet-automation-using-r-programming</link><guid isPermaLink="false">https://www.felixanalytix.com/p/google-sheet-automation-using-r-programming</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Mon, 26 Dec 2022 14:19:53 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/8f7c25e8-4dcd-44bc-8947-f5e47f16ec43_1280x720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Would you like to learn how to connect R to your Google Drive and automate the data processing of your Google Sheets?</strong></p><p>I just published a new YouTube video showing the basics of working with Google Sheets data using R programming.</p><p>You will learn how to:</p><ol><li><p>Connect R to Google Drive and list your existing Sheets;</p></li><li><p>Write and read Google Sheets from R</p></li><li><p>Append data to a Google Sheet;</p></li><li><p>Rename a worksheet;</p></li><li><p>Change file access permission (from private to public)</p></li></ol><p>Check it out! </p><div id="youtube2-daEq5Yj6uZw" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;daEq5Yj6uZw&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/daEq5Yj6uZw?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get all the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Create an Interactive Network of the Marvel Universe using R]]></title><description><![CDATA[Let&#8217;s have some fun creating an interactive network of the Marvel Cinematic Universe using the R programming language.]]></description><link>https://www.felixanalytix.com/p/create-an-interactive-network-of-the-marvel-universe-using-r-1313547</link><guid isPermaLink="false">https://www.felixanalytix.com/p/create-an-interactive-network-of-the-marvel-universe-using-r-1313547</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Tue, 08 Nov 2022 15:25:40 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/206e42d8-d451-4b46-9282-6a4a0917d528_1280x720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Let&#8217;s have some fun creating an interactive network of the Marvel Cinematic Universe using the R programming language.</p><p>First we will scrape the data from Wikipedia, tidying the data, and build an interactive network of the main Marvel characters.</p><p>You can play with the final interactive network here: <a href="https://felixanalytix.com/vis/marvel-network.html">https://felixanalytix.com/vis/marvel-network.html</a></p><p>Learn how to make it yourself by watching my YouTube video &#10145;&#65039;</p><div id="youtube2-hgUJ-UFv4YY" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;hgUJ-UFv4YY&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/hgUJ-UFv4YY?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get all the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[How to Detect Emotions 😍😭😱 from any Text using R]]></title><description><![CDATA[Let's extract the main emotions from George Orwell's book 1984 and visualize its emotional trajectory.]]></description><link>https://www.felixanalytix.com/p/how-to-detect-emotions-from-any-text-using-r-1313485</link><guid isPermaLink="false">https://www.felixanalytix.com/p/how-to-detect-emotions-from-any-text-using-r-1313485</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Tue, 16 Aug 2022 12:45:19 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/db359797-958c-4c19-8cc7-a1e9d18125a1_1280x720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Would you like to know how to extract the main emotions from any text and visualize its emotional trajectory using the R programming language?</strong></p><p>And learn this in a fun way?</p><p>In this new YouTube video, we will implement a simple-but-efficient method (a lexicon-based sentiment analysis) generally used to analyse consumer feedbacks, social medias or texts in surveys. But, to make it more fun, we will instead analyse the book <a href="https://en.wikipedia.org/wiki/Nineteen_Eighty-Four">Nineteen Eighty-Four</a> from George Orwell.</p><p>Be sure to <a href="https://youtu.be/6el_C3PxSSw">check it out</a>! &#128073;</p><div><hr></div><div id="youtube2-6el_C3PxSSw" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;6el_C3PxSSw&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/6el_C3PxSSw?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get all the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[How to Read and Merge Hundreds of Excel files 😮 using R ]]></title><description><![CDATA[Learn how to create loops that read, clean, validate and merge hundreds of Excel files with R.]]></description><link>https://www.felixanalytix.com/p/how-to-read-and-merge-hundreds-of-excel-files-using-r-1254432</link><guid isPermaLink="false">https://www.felixanalytix.com/p/how-to-read-and-merge-hundreds-of-excel-files-using-r-1254432</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Tue, 05 Jul 2022 13:30:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/4927f95b-8cde-4fa0-8054-b18c6ccab947_1280x720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Do you want to know how to create R loops that read hundreds of Excel files into R? </p><p>And that your R loops clean, validate and even merge your Excel files data into a single and consolidated dataset? </p><p>Well, you are lucky... My new video just teaches you how to do that &#128521;. And in only 5 minutes. So be sure to check it out: </p><p>&#128073;<a href="https://youtu.be/BHdWYonrPAs">https://youtu.be/BHdWYonrPAs</a></p><div><hr></div><div id="youtube2-BHdWYonrPAs" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;BHdWYonrPAs&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/BHdWYonrPAs?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get all the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[3 Tips to Make R Shiny Buttons Look Better✨]]></title><description><![CDATA[A very short YouTube video (>1 min.) showing you how to improve the look of buttons in your R Shiny apps.]]></description><link>https://www.felixanalytix.com/p/make-your-r-shiny-buttons-look-better-with-these-3-tips-1193573</link><guid isPermaLink="false">https://www.felixanalytix.com/p/make-your-r-shiny-buttons-look-better-with-these-3-tips-1193573</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Tue, 24 May 2022 15:36:56 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/_zfdVvMywi4" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Here a very short video (1 minute) showing 3 tips to improve the look of the buttons in your R Shiny apps.</p><div id="youtube2-_zfdVvMywi4" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;_zfdVvMywi4&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/_zfdVvMywi4?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get all the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[How to Make World Maps using R]]></title><description><![CDATA[Learn how to download world map data, plot it, add texts and colors, and even change the map projection using the R packages {ggplot2} and {sf}.]]></description><link>https://www.felixanalytix.com/p/how-to-make-world-maps-using-r-960104</link><guid isPermaLink="false">https://www.felixanalytix.com/p/how-to-make-world-maps-using-r-960104</guid><dc:creator><![CDATA[Felix Analytix]]></dc:creator><pubDate>Fri, 11 Feb 2022 13:41:56 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/FoqiFR5ZCic" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Do you want to learn how to create beautiful world maps? In this video I show you how to download world map data, plot it, add texts and colors, and even change the map projection using the R packages {ggplot2} and {sf}.</p><p>Be sure to check it out!! &#10145;&#65039;</p><div><hr></div><div id="youtube2-FoqiFR5ZCic" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;FoqiFR5ZCic&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/FoqiFR5ZCic?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.felixanalytix.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Subscribe to receive by email FOR FREE the URL of my GitHub repository, where you can get all the R code of this tutorial.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item></channel></rss>