From Data to Narrative

Interactive Storytelling with Shiny

Francisco Alfaro

2025-04-11

About me


  • Francisco Alfaro Medina aka fralfaro
  • Member of Python Chile
  • Lead Data Scientist at Grupo Security
  • Associate Lecturer at UTFSM

Agenda

  • No

  • Nope

  • No chance

Tachado

Storytelling principle #1

Never reveal the ending too soon.
Always build up tension and drama

Agenda (v2)

  1. Never reveal the ending
  2. Details are important, but not all details are important.
  3. Your first draft will be horrible.
  4. Explain less, show more

Storytelling

Why Storytelling?

What is Storytelling?

🔥 Stories are the first human technology.

Ohh, those hackable brains



Peak-End Rule


Game of Thrones Rating, by Kelvin Neo

Narrative



Use Storytelling tricks (narrative) to create presentations that will be remembered and make an impact

🎭 Emotions inspire action

The best example



How to make millions of people share statistics on social media?

Data Storytelling

Tool #1: Visualization

Examples

🔢 Don’t share numbers

🪶 Share a story



(C) Storytelling with Data, by Cole Nussbaumer Knaflic.

Storytelling principle #2

Details matter, but not all details are important.

Best Charts for your Data


(C) Essential chart types for data visualization, por Atlassian.

Data Storytelling

Tool #2: AI

Examples


🥱 1° version \(<\)\(<\) 😊 last version

Storytelling principle #3

Your first draft will always be horrible.

From Ideas to Impact… Faster with AI


  • Faster structure, less effort
  • Clearer stories, better impact
  • Visuals in seconds
  • More time for creativity

💡Unlocking Creativity with AI Tools

Data Storytelling

Tool #3: Presentation

Examples

Let’s see how much code we need to say something as simple as:
“Do you like this presentation?”

It may seem like a small question, but turning that into code requires defining categories, assigning values, choosing colors, and setting plot parameters.
And that’s just to get a basic bar chart.

categories <- c("Yes", "No")
values <- c(75, 25)
colors <- c("lightblue", "salmon")
barplot(
  values, names.arg = categories, col = colors, 
  main = "😊 Do you like the presentation so far? 😊", cex.main = 0.9
)

Storytelling principle #4

Explain less, show more.

📝 Quarto (+ Extensions)



Quarto is an open system for scientific publications with markdown and interactive code (Python/R).


🔧 Need more power? Use Extensions

Código: example.qmd

---
title: "Habits"
author: "John Doe"
format:
  revealjs:
    transition: fade
    theme: black
    toc: true
    center: true
---

## Getting up

- Turn off alarm
- Get out of bed

---

## Going to sleep 
::: { .incremental }

- Get in bed
- Count sheep

:::

Slides: example.html

🌐 Quarto WebR


  1. WebR: Run R code in the browser, no installation required.

    # To install WebR in your environment
    remotes::install_github("attiyap/WebR")
  2. Pyodide: Same, but for Python.

    # To install Pyodide
    pip install pyodide

Example

Shiny



Shiny is an R package that allows you to easily create interactive web applications using R (also available for Python).

Shiny


🌐 Quarto Shinylive


  • Deploy Shiny apps that run 100% in the browser — with Python & R (via WebAssembly).
  • No servers. No setup. Just code & run.


Stay in your presentation.
Stay in the flow. 🎯

Quarto & Shiny

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| viewerHeight: 550

from shiny import App, ui, render, reactive
import matplotlib.pyplot as plt
import io
import base64

# Function to generate the plot dynamically based on input values
def create_plot(yes_value, no_value):
    categories = ['Yes', 'No']
    values = [yes_value, no_value]

    fig, ax = plt.subplots(figsize=(10, 3))
    ax.bar(categories, values, color=['lightblue', 'salmon'])
    ax.set_title('😊 Do you like the presentation so far? 😊')

    # Convert the image to base64 for display in Shiny
    buf = io.BytesIO()
    plt.savefig(buf, format="png")
    plt.close(fig)
    buf.seek(0)
    encoded_image = base64.b64encode(buf.getvalue()).decode()
    return f'<img src="data:image/png;base64,{encoded_image}" style="max-width:100%;">'

# UI definition
app_ui = ui.page_fluid(
    ui.h2("Interactive Survey"),
    
    # Sliders to change values dynamically
    ui.input_slider("yes_value", "Yes responses:", min=0, max=50, value=20),
    ui.input_slider("no_value", "No responses:", min=0, max=50, value=10),
    
    # Output area for the plot
    ui.output_ui("plot_output")
)

# Server function
def server(input, output, session):
    @output
    @render.ui
    def plot_output():
        return ui.HTML(create_plot(input.yes_value(), input.no_value()))

# Create the Shiny app
app = App(app_ui, server)

Agenda (v2)

  1. Never reveal the ending
  2. Details are important, but not all details are important
  3. Your first draft will be horrible
  4. Explain less, show more

🎉 Thank You for Participating!


❓ Questions?

👏 Fill out the survey

🥳 Thank You Again!

✨ Shine On with Shiny! (www.shinyconf.com)