Given the success of Plotly Dash and Plotly Express, it's easy to forget that Plotly's rise to success began with a product that was neither of these household names. Dash has cornered the interactive dashboard market, while Plotly Express has become the de facto Python library generating inline charts, particularly for Jupyter notebooks. What we don't get from either of these tools is a way to create data visualizations for any other scenario, such as creating a single chart to serve publicly as an image or interactive plot.

Plotly Chart Studio shines as a tool to instantly create cloud-hosted data visualizations. The syntax will appear familiar to those who have used Plotly Express (or any other Python data vis library). Still, Plotly Chart Studio stands alone in how these charts are created: on a publicly accessible cloud. With a Chart Studio account, each chart you plot is saved to a public Chart Studio profile, like mine:

Plotly Chart Studio Dashboard
My collection of publically accessible Plotly charts

Each chart in my profile is publicly accessible as your choice of an embedded plot, an image, raw HTML, etc. Here's an example chart as an iFrame:

Every chart created with Plotly Studio automatically generates interactive plots and static image varieties like PNGs. This opens the door to possibilities for us to create charts on demand, like creating dumb Discord bots, for example:

Plotly Discord Bot
Serving charts on demand on third-party services

This tutorial will demonstrate plot creation in Chart Studio with a fairly common real-life scenario. We'll be fetching stock price data from IEX Cloud, transforming the data in a Pandas DataFrame, and outputting a Candlestick chart. This same workflow can be applied to create any of Plotly's many chart types:

Plotly
Plotly’s

Getting Set Up

The two Plotly-related libraries we need are plotly and chart-studio. We'll install these along with the usual suspects for Python data analysis:

$ pip install requests pandas plotly chart-studio python-dotenv

Install requirements

Accounts & Configuration

You can create a Plotly Chart Studio account here; get yourself set up and grab an API key. I'm also using an API key to access data from IEX, which can be substituted for whichever API credentials you happen to need:

"""Configuration via environment variables."""
from os import environ, path
from dotenv import load_dotenv


# Load values from .env
basedir = path.abspath(path.dirname(__file__))
load_dotenv(path.join(basedir, '.env'))


# API
IEX_API_TOKEN = environ.get('IEX_API_TOKEN')
IEX_API_BASE_URL = 'https://cloud.iexapis.com/stable/stock/'

# Plotly
PLOTLY_API_KEY = environ.get('PLOTLY_API_KEY')
PLOTLY_USERNAME = environ.get('PLOTLY_USERNAME')

config.py

Save your secrets in a .env file as always. If you're not sure what this means, please stop coding and Google what environment variables are. Seriously, stop DMing me that my code is "broken" because you didn't create a .env file or take 5 minutes to grasp what that even means.

What were we talking about again? Right, charts.

Initializing Chart Studio

chart_studio is a standalone Python library that serves the sole purpose of saving charts to Plotly Chart Studio. set_credentials_file function and passing our Plotly username and API key:

"""Prepare credentials to initialize a Python Plot Chart Studio client."""
from chart_studio.tools import set_credentials_file
from config import (
    PLOTLY_USERNAME,
    PLOTLY_API_KEY
)


# Plotly Chart Studio authentication
set_credentials_file(
    username=PLOTLY_USERNAME,
    api_key=PLOTLY_API_KEY
)

Authenticating with Plotly Chart Studio

We'll come back to this later. Let's get some data.

Fetching & Preparing Data

Beautiful charts need beautiful data. We'll get some beautiful data using two libraries you should hopefully be sick of by now: requests and Pandas.

Fetch Data from an API

Let's GET this bread! Below is a function called fetch_stock_data(), which constructs a GET request to our API. In my example, we're fetching 1-month's worth of stock price data for a given stock symbol:

"""Fetch stock price from third-party API."""
import requests
from config import IEX_API_BASE_URL, IEX_API_TOKEN


def fetch_stock_data(stock_symbol: str):
    """
    Fetch stock data from third-party IEX API.

    :param str stock_symbol: Symbol of the company stock as listed.
    """
    params = {'token': IEX_API_TOKEN, 'includeToday': 'true'}
    url = f'{IEX_API_BASE_URL}/{stock_symbol}/chart/1m'
    req = requests.get(url, params=params)
    data = req.content
    return data

Fetch Stock Price Data via simple HTTP API request

This is a straightforward GET request made to a REST API endpoint. The above effectively constructs and hits this URL:

https://cloud.iexapis.com/stable/stock/ddog/chart/1m?token=sk_54632753687697685697456&includeToday=true

IEX endpoint for 1-month price data

If everything goes correctly, req.content should spit out some raw JSON. Here's a glance at what that looks like:

[
    ...
    {
        "date": "2020-08-20",
        "open": 39.28,
        "close": 40.31,
        "high": 40.44,
        "low": 39.12,
        "volume": 3953482,
        "uOpen": 39.28,
        "uClose": 40.31,
        "uHigh": 40.44,
        "uLow": 39.12,
        "uVolume": 3953482,
        "change": 1.02,
        "changePercent": 2.5961,
        "label": "Aug 20",
        "changeOverTime": 0.059674
    },
    {
        "date": "2020-08-21",
        "open": 40.45,
        "close": 38.92,
        "high": 40.49,
        "low": 38.9,
        "volume": 3271742,
        "uOpen": 40.45,
        "uClose": 38.92,
        "uHigh": 40.49,
        "uLow": 38.9,
        "uVolume": 3271742,
        "change": -1.39,
        "changePercent": -3.4483,
        "label": "Aug 21",
        "changeOverTime": 0.023134
    }
    ...
]

JSON API response from IEX

This response might seem daunting at first glance, but it's exactly what we seek. We can make good use of this data via an old friend...

Parsing with Pandas

It's time for some mo effin' Pandas! We will take data what we fetched from our API request and passed into a new function called parse_data(), which will load our data into a Pandas DataFrame. Shoutout to the beautiful new Pandas method called read_json():

"""Parse raw API data into Pandas DataFrame."""
import pandas as pd


def parse_data(data) -> pd.DataFrame:
    """Parse JSON as Pandas DataFrame."""
    stock_df = pd.read_json(data)
    stock_df = stock_df.loc[stock_df['date'].dt.dayofweek < 5]
    stock_df.set_index(keys=stock_df['date'], inplace=True)
    return stock_df

Create Pandas DataFrame from raw JSON

It's important to note that we pass in req.content from our request - not req.json(). req.json() renders JSON as a Python dictionary, which you could load into Pandas with from_dict(), but whatever. Stick with me here.

Create a Chart with Plotly Studio

Let's handle this one part one step at a time. There are three things we need to do here:

  1. Create a Plotly chart with the data from our Pandas DataFrame.
  2. Adjust our chart's visual layout (title, color, etc.).
  3. Saving our chart to Plotly's cloud.

Each step above is nice and easy. Here's step 1:

"""Create a Plotly chart using data from a Pandas DataFrame."""
import plotly.graph_objects as go
import chart_studio.plotly as py
from chart_studio.tools import set_credentials_file
from pandas import DataFrame
from config import PLOTLY_API_KEY, PLOTLY_USERNAME


# Plotly Chart Studio authentication
set_credentials_file(
    username=PLOTLY_USERNAME,
    api_key=PLOTLY_API_KEY
)


def create_chart(stock_df: DataFrame, symbol: str) -> py.plot:
    """
    Create Plotly chart from Pandas DataFrame.
    
    :param DataFrame stock_df: Stock price data parsed as a Pandas DataFrame.
    :param str symbol: Symbol of the company stock as listed.

    :returns: py.plot
    """
    fig = go.Figure(data=[
        go.Candlestick(
            x=stock_df.index,
            open=stock_df['open'],
            high=stock_df['high'],
            low=stock_df['low'],
            close=stock_df['close'],
        )],
    )

Create a chart from Pandas DataFrame

If you've ever used Plotly or Plotly Dash, this syntax should look familiar! go.Figure() is a Python class used to create charts/plots/figures/whatever. We're passing data as a keyword argument, which is just one of 3 notable arguments we could pass:

  • data: Accepts one or multiple "trace" types. A "trace" refers to the data on our plot; in other words, we could easily chart different types of data on the same plot, such as a combination of line chart data ( go.Line ) and bar chart data ( go.Bar ) on the same plot. In our case, we're sticking with a single candlestick trace, where we pass in columns of our Pandas DataFrame for each value go.Candlestick() expected.
  • layout: A plot's layout gives us the power to change everything about our chart's appearance. This could be background color, trace color, plot title, margins, and so much more. Just look at all the ways we can customize our plot - it's almost ridiculous!
  • frames: Apparently, we could animate our charts by passing multiple frames of changing data into the frames keyword argument. That's just fuckin bonkers IMO and not something we should worry about until we're older.

Anyway, on to step 2: customizing our layout:

...

def create_chart(stock_df: DataFrame, symbol: str) -> py.plot:
    """
    Create Plotly chart from Pandas DataFrame.
    
    :param DataFrame stock_df: Stock price data parsed as a Pandas DataFrame.
    :param str symbol: Symbol of the company stock as listed.

    :returns: py.plot
    """
    fig = go.Figure(data=[
        go.Candlestick(
            x=stock_df.index,
            open=stock_df['open'],
            high=stock_df['high'],
            low=stock_df['low'],
            close=stock_df['close'],
        )],
        layout=go.Layout(
            title=f'30-day performance of {symbol.upper()}',
            xaxis={
              'type': 'date',
              'rangeslider': {
                  'visible': False
              },
            },
        )
    )

Visualize stock price data as a Candlestick chart

We pass layout into our figure immediately after passing data. As we've already established, there are a shit ton of possibilities we could get deep into here. I'm keeping things modest by setting a chart title and ensuring the x-axis knows it's a range of dates.

We can finally save our hard work with step 3: saving our plot to the cloud:

...

def create_chart(stock_df: DataFrame, symbol: str) -> py.plot:
    """
    Create Plotly chart from Pandas DataFrame.
    
    :param DataFrame stock_df: Stock price data parsed as a Pandas DataFrame.
    :param str symbol: Symbol of the company stock as listed.

    :returns: py.plot
    """
    fig = go.Figure(data=[
        go.Candlestick(
            x=stock_df.index,
            open=stock_df['open'],
            high=stock_df['high'],
            low=stock_df['low'],
            close=stock_df['close'],
        )],
        layout=go.Layout(
            title=f'30-day performance of {symbol.upper()}',
            xaxis={
              'type': 'date',
              'rangeslider': {
                  'visible': False
              },
            },
        )
    )
    chart = py.plot(
        fig,
        filename=symbol,
        auto_open=False,
        fileopt='overwrite',
        sharing='public'
    )
    return chart

Create a visualization from a Pandas Dataframe

The options we're passing to py.plot() are pretty straightforward:

  • Setting the filename.
  • Handling file clashes by overwriting preexisting charts with the same name.
  • Making the chart available to the public

So, what does chart output? Here's what a print() would reveal:

https://chart-studio.plotly.com/~toddbirchard/514/

Output of print(chart)

It's a link to our chart! But wait, what if I don't want to view my chart on Plotly's site? What if I want an image? Check out what chart[:-1] + '.png' gives you:

https://chart-studio.plotly.com/~toddbirchard/514.png

Output an interactive chart as an image file (.png)

Which is a URL to...

Plot created using IEX data to display DataDog's stock price over time
Plot created using IEX data to display DataDog's stock price over time

WE DID IT!

Organizing Our Work

Throughout this tutorial, you've been watching me dump random code blocks. If this confuses the shit out of you, it'll probably help to know that this is how I chose to structure my project:

/plotly-chartstudio-tutorial
├── /plotly_chartstudio_tutorial
│   ├── __init__.py
│   ├── api.py
│   ├── chart.py
│   ├── data.py
│   └── log.py
├── config.py
├── main.py
├── requirements.txt
└── .env

Example Python project structure utilizing Plotly Chart Studio

"That doesn't tell me shit," you might exclaim. Luckily, that was just a setup to tell you that the source code for this tutorial is available for up on Github. Enjoy:

hackersandslackers/plotly-chartstudio-tutorial
📈 📊 Create Cloud-hosted Charts with Plotly Chart Studio. - hackersandslackers/plotly-chartstudio-tutorial