Ga naar hoofdinhoud

Plotly-shapes: een gids voor annoteren en stijlen van grafieken

Leer hoe je je dataverhaal versterkt met Plotly-shapes. Deze gids biedt een volledige walkthrough voor het toevoegen en stijlen van annotaties in Python, van basisrechthoeken en -lijnen tot geavanceerde interactieve geometrieën.
· 12 min lezen

Verken met AI

Plotly is een van de krachtigste en populairste Python-bibliotheken voor het maken van interactieve grafieken. Basisgrafieken maken met Plotly is eenvoudig, maar echt impactvolle datavisualisaties vragen vaak om extra elementen die de aandacht van de kijker sturen en belangrijke context bieden. Daar komen Plotly-shapes goed van pas.

Shapes zijn grafische elementen zoals rechthoeken, lijnen en cirkels die je boven op je Plotly-figuren kunt tekenen om te annoteren, te highlighten en cruciale context toe te voegen. Ze zijn de sleutel om een standaardplot te verheffen tot een rijke, inzichtelijke visualisatie van publicatieniveau.

plotly shapes

Afbeelding door de auteur

In dit artikel bekijken we alles wat je moet weten over plotly-shapes, van basisimplementatie tot geavanceerde aanpassingstechnieken. Voor wie nieuw is met Plotly is vertrouwd raken met de high-level interface een goede eerste stap, en deze cursus Introduction to Data Visualization with Plotly in Python helpt je de basis onder de knie te krijgen voordat je dieper duikt in aanpassingen zoals shapes.

Wat zijn Plotly-shapes?

Plotly-shapes zijn vectorafbeeldingen die op een figuur worden getekend om extra context te bieden of specifieke gebieden te markeren. Deze annotaties zijn belangrijk om grafieken te bouwen die niet alleen visueel aantrekkelijk zijn, maar ook rijk aan informatie.

Het belang van shapes in datavisualisatie

In datavisualisatie is helderheid cruciaal. Terwijl grafieken zoals scatterplots of staafdiagrammen de data tonen, helpen shapes het verhaal achter de data te vertellen. Ze hebben twee primaire functies:

  • Datatrends highlighten: Shapes kunnen de aandacht vestigen op sleutelperiodes of -gebeurtenissen in je data. Zo kun je bijvoorbeeld een recessieperiode arceren met een rechthoek op een tijdreeksdiagram van het bbp, of een lijn tekenen om een belangrijke beleidswijziging te markeren. Deze visuele aanwijzing helpt de kijker direct de impact van externe factoren op de data te begrijpen.
  • Contextuele annotaties bieden: Shapes kunnen specifieke datapunten of clusters omcirkelen of aanwijzen, zoals uitschieters, interessante groepen of kritieke drempels. Denk aan een kwaliteitscontrolekaart waarop je cirkels gebruikt om metingen te markeren die buiten de toegestane marges vallen. Dit maakt de interpretatie van de data sneller en intuïtiever.

Wil je in één oogopslag de meest gebruikte visualisaties zien, wanneer je ze gebruikt en hun typische use-cases? Bekijk dan onze Data Visualization-cheat sheet.

Overzicht van shapetypen in Plotly

Plotly ondersteunt verschillende geometrische shapetypen die je aan de layout van een figuur kunt toevoegen. Elk type wordt gedefinieerd door zijn type en specifieke coördinatenparameters.

Hier is een korte blik op de belangrijkste shapes:

  • Rechthoek (type: 'rect'): Gedefinieerd door twee tegenoverliggende hoeken (x0, y0) en (x1, y1). Rechthoeken zijn perfect om interessegebieden te arceren of een specifiek datavenster te highlighten.
  • Lijn (type: 'line'): Gedefinieerd door een startpunt (x0, y0) en een eindpunt (x1, y1). Lijnen zijn uitstekend voor trendlijnen, drempels of referentiemarkeringen.
  • Cirkel (type: 'circle'): Gedefinieerd door een middelpunt (x0, y0) en een straal. Cirkels zijn ideaal om individuele datapunten of kleine clusters te benadrukken. Let op: een cirkel maken vereist vaak een gelijke beeldverhouding voor de assen (fig.update_layout(yaxis_scaleanchor="x")) om te voorkomen dat hij op een ovaal lijkt.
  • Pad (type: 'path'): Gedefinieerd door een SVG-padstring. Paden laten je complexe, vrije vormen tekenen, inclusief polygonen. Je geeft een reeks commando's (zoals M voor move, L voor line to, Z voor pad sluiten) om elke gewenste vorm te tekenen. Dit is de meest veelzijdige optie voor aangepaste geometrische annotaties.

Deze basistypen begrijpen is de eerste stap naar het beheersen van visuele annotaties in je Plotly-figuren.

Shapes toevoegen aan Plotly-grafieken

Plotly-shapes implementeren in je visualisaties draait om twee primaire benaderingen: de add_shape()-methode voor individuele shapes en de update_layout()-aanpak voor meerdere shapes. Beide bieden unieke voordelen afhankelijk van je use-case en workflowvoorkeuren. Laten we zien hoe je shapes toevoegt met de methode add_shape().

Hoe shapes toevoegen met add_shape()?

De primaire methode om een enkele shape toe te voegen aan een Plotly-figuur is fig.add_shape(). De methode add_shape() is een functie van het object plotly.graph_objects.Figure. Je maakt eerst je basisfiguur (bijv. een scatterplot of lijngrafiek) en roept daarna deze methode aan om de shape erbovenop te leggen. 

Dit is vooral handig wanneer je shapes programmatisch wilt toevoegen op basis van bepaalde voorwaarden of datawaarden. Laten we een eenvoudig voorbeeld bekijken waarin we een specifiek gebied op een tijdreeksplot highlighten.

import plotly.graph_objects as go
import numpy as np

# Create sample data
x = np.linspace(0, 10, 100)
y = np.sin(x) + np.random.normal(0, 0.1, 100)

# Create base figure
fig = go.Figure()
fig.add_trace(go.Scatter(x=x, y=y, mode='lines', name='Signal'))

# Add a shape using add_shape method
fig.add_shape(
    type="rect",           # Shape type
    x0=2, x1=4,           # X coordinates
    y0=-2, y1=2,          # Y coordinates
    fillcolor="rgba(255, 0, 0, 0.3)",  # Fill color with transparency
    line=dict(color="red", width=2)     # Border styling
)

fig.show()

Output:

Plotly add shapes using add_shape()

De plotly-add_shape()-functionaliteit wordt vooral krachtig als je het coördinatensysteem begrijpt. Shapes kunnen naar verschillende coördinatensystemen verwijzen met de parameters xref en yref. Ze bepalen het coördinatenkader: gebruik 'x'/'y' om waarden in data-eenheden te interpreteren, of 'paper' om het genormaliseerde plotgebied te gebruiken waarbij 0 – 1 de volledige breedte/hoogte van linksonder naar rechtsboven beslaat. Laten we de bovenstaande grafiek aanpassen met deze parameters zoals hieronder getoond:

# Shape referenced to data coordinates (default)
fig.add_shape(type="line", x0=1, x1=9, y0=0, y1=0,
              line=dict(color="green", width=3))

# Shape referenced to paper coordinates (0-1 scale)
fig.add_shape(type="rect", xref="paper", yref="paper",
              x0=0.1, x1=0.9, y0=0.1, y1=0.9,
              fillcolor="rgba(0, 0, 255, 0.1)")

Output:

Rechthoeken en lijnen aanpassen

Rechthoeken en lijnen zijn de meest gebruikte shapes voor annotatie. Plotly biedt uitgebreide opties om hun uiterlijk te regelen. Voor een snel naslagwerk van veelgebruikte Plotly-syntax is de Plotly Express Cheat Sheet van onschatbare waarde. Laten we bekijken hoe je deze shapes aanpast.

Aanpassen van rechthoek-shapes

Rechthoeken worden gedefinieerd door twee punten (x0,y0 en x1, y1). Je kunt de vulling en de rand apart stylen.

# Advanced rectangle customization
fig.add_shape(
    type="rect",
    x0=3, x1=7,
    y0=-1.5, y1=1.5,
    # Fill properties
    fillcolor="rgba(50, 171, 96, 0.4)",
    # Line properties
    line=dict(
        color="darkgreen",
        width=3,
        dash="dashdot"  # Options: solid, dot, dash, longdash, dashdot
    ),
    # Layer control
    layer="below"  # Options: above, below
)

Output:

Plotly Rectangle Shape Customization

Aanpassen van lijn-shapes

Lijnen kun je toevoegen met de speciale methoden ‘add_hline’ en ‘add_vline’ voor horizontale en verticale lijnen die de volledige plot beslaan, of met de algemene methode ‘add_shape’ voor meer precieze controle. Laten we een voorbeeld bekijken:

# Horizontal threshold lines
fig.add_hline(y=1.0, line_dash="dash", line_color="red", 
              annotation_text="Upper Threshold")
fig.add_hline(y=-1.0, line_dash="dash", line_color="red", 
              annotation_text="Lower Threshold")

# Custom diagonal line
fig.add_shape(
    type="line",
    x0=0, x1=10,
    y0=-2, y1=2,
    line=dict(
        color="purple",
        width=4,
        dash="longdashdot"
    )
)

Output:

Plotly Line shape customization

Deze voorbeelden laten zien hoe je eenvoudig fundamentele Plotly-shapes toevoegt en stijlt om je grafieken informatiever te maken.

Geavanceerde configuratie en aanpassing

Naast het toevoegen van basale shapes biedt Plotly krachtige tools om meerdere shapes te beheren en hun visuele hiërarchie te bepalen. Deze geavanceerde functies zijn essentieel voor het creëren van complexe, gelaagde en dynamische visualisaties. Laten we enkele manieren bekijken om de grafieken te configureren en aan te passen.

plotly.update_layout gebruiken voor dynamische updates

Hoewel de add_shape()-methode perfect is om shapes één voor één toe te voegen, is de methode update_layout() efficiënter om een verzameling shapes te beheren, vooral wanneer ze programmatisch worden gegenereerd. In plaats van voor elke shape een functie aan te roepen, kun je een lijst met shape-dictionaries rechtstreeks doorgeven aan het argument shapes binnen update_layout(). 

Laten we een voorbeeld zien van het gebruik van deze methode:

import plotly.graph_objects as go
import numpy as np

# Sample scatter plot data
x_data = np.linspace(0, 10, 50)
y_data = np.sin(x_data) + np.random.normal(0, 0.1, 50)

fig = go.Figure(data=go.Scatter(x=x_data, y=y_data, mode='markers', name='Data Points'))

# Define a list of shapes to add
shapes_to_add = [
    # A rectangle highlighting a peak area
    dict(
        type="rect",
        x0=1, x1=2.5, y0=0.8, y1=1.2,
        fillcolor="rgba(255, 165, 0, 0.4)",
        line_width=0
    ),
    # A vertical line marking an event
    dict(
        type="line",
        x0=7.5, x1=7.5, y0=0, y1=1,
        yref="paper", # Span the full plot height
        line=dict(color="red", width=2, dash="dot")
    )
]

# Add all shapes at once using update_layout
fig.update_layout(
    shapes=shapes_to_add
)

fig.show()

Output:

Using plotly.update_layout for dynamic updates

Deze methode is vooral krachtig voor dynamische toepassingen, zoals een Dash-dashboard waar shapes moeten veranderen op basis van gebruikersinvoer. Je kunt een nieuwe lijst met shape-objecten genereren en de layout in één schone handeling updaten. Deze aanpak houdt je code overzichtelijk en is doorgaans performanter om in één keer een groot aantal shapes toe te voegen.

Layering en z-indexcontrole

Wanneer je shapes toevoegt aan een plot, is het belangrijk te bepalen of ze bovenop of achter je data verschijnen. Plotly beheert dit via de eigenschap layer. Hoewel het geen fijne z-index is zoals in CSS, biedt het essentiële controle over de visuele stapeling.

De eigenschap layer kan twee waarden hebben:

  • 'above': De shape wordt bovenop datatraces getekend. Dit is de standaardinstelling en handig voor annotaties die duidelijk zichtbaar moeten zijn.
  • 'below': De shape wordt onder datatraces en rasterlijnen getekend. Dit is ideaal voor achtergrondarcering of het highlighten van gebieden zonder de datapunten zelf te bedekken.

De volgorde van shapes binnen de shapes-lijst is ook van belang: shapes die later in de lijst staan, worden bovenop eerder gedefinieerde shapes getekend (binnen dezelfde layer).

Hier is een voorbeeld dat het effect van layering laat zien:

import plotly.graph_objects as go

fig = go.Figure(data=go.Scatter(x=[1, 2, 3, 4, 5], y=[5, 3, 4, 2, 6], mode='markers', marker_size=20, name='Data'))

# Add a shape BELOW the data points
fig.add_shape(
    type="rect",
    x0=2.5, y0=2.5, x1=4.5, y1=4.5,
    fillcolor="LightPink",
    opacity=0.8,
    layer="below", # Key parameter
    line_width=0
)

# Add a shape ABOVE the data points
fig.add_shape(
    type="circle",
    x0=0.5, y0=4.5, x1=1.5, y1=5.5,
    fillcolor="LightSeaGreen",
    opacity=0.6,
    layer="above", # Key parameter
    line_width=0
)

fig.update_layout(
    xaxis=dict(showgrid=False), # Turn off gridlines to see effect clearly
    yaxis=dict(showgrid=False)
)

fig.show()

Output:

Layering and z-index control

Zoals je ziet zit de roze rechthoek achter de markers, terwijl de groene cirkel het datapunt overlapt. 

Deze layout- en layeringtechnieken beheersen is een belangrijk onderdeel van het maken van geavanceerdere en interactieve datavisualisaties met Plotly. Voor wie in R werkt, gelden vergelijkbare concepten, zoals behandeld in Intermediate Interactive Data Visualization with Plotly in R.

Interactief shapes bewerken

Een van de krachtigste features van Plotly is de interactiviteit. Je kunt Plotly-shapes bewerkbaar maken, zodat gebruikers ze direct op de plot kunnen slepen, schalen en roteren. Deze functie schakel je in door editable=True te zetten binnen de definitie van de shape. Laten we zien hoe je dat doet:

import plotly.graph_objects as go
import numpy as np

# Create a scatter plot
fig = go.Figure(go.Scatter(
    x=np.random.rand(100),
    y=np.random.rand(100),
    mode='markers'
))

# Add a draggable rectangle
fig.add_shape(
    type="rect",
    x0=0.2, y0=0.2, x1=0.5, y1=0.5,
    line=dict(color="RoyalBlue", width=2),
    fillcolor="LightSkyBlue",
    opacity=0.5,
    editable=True # This makes the shape interactive
)

fig.update_layout(
    xaxis=dict(range=[0, 1]),
    yaxis=dict(range=[0, 1]),
    # The 'newshape' button in the modebar allows drawing new shapes
    # The config dictionary enables editing of existing shapes
    dragmode='select'
)

# To enable editing, the modebar must be visible (default) which you will see when hovering over the graph with the mouse.

# and the plot's config can ensure editing tools are available.
fig.show(config={'edits': {'shapePosition': True}})

Output:

Interactieve shapes zijn ontzettend nuttig voor exploratieve data-analyse. Zo kan een gebruiker een rechthoek over verschillende perioden slepen om bijbehorende samenvattende statistieken in realtime te laten updaten in een Dash-app. Of een cirkel vergroten/verkleinen om een ander cluster punten te selecteren voor verder onderzoek.

Hoewel krachtig, houd er rekening mee dat veelvuldig gebruik van bewerkbare shapes prestatie-implicaties kan hebben in complexe webapplicaties. Voor het maken van echt dynamische, door de gebruiker gestuurde tools zijn ze echter onmisbaar. 

Interactiviteit verkennen is een belangrijk onderdeel van moderne datavisualisatie, een onderwerp dat uitgebreid wordt behandeld in cursussen zoals Interactive Data Visualization with Plotly in R, die veel principes deelt met de Python-tegenhanger.

Plotly integreren met andere bibliotheken

Hoewel de ingebouwde shapes van Plotly veelzijdig zijn, heb je soms zeer complexe of aangepaste geometrieën nodig. Door te integreren met gespecialiseerde bibliotheken zoals Shapely kun je geavanceerde polygonen definiëren en ze als aangepaste Plotly-shapes aan je visualisaties toevoegen.

Shapely gebruiken voor geavanceerde geometrische shapes

Shapely is een krachtige Python-bibliotheek voor de bewerking en analyse van vlakke geometrische objecten. Het is de go-to tool voor computationele geometrie, vaak gebruikt in geospatiale analyses. Je kunt er ingewikkelde vormen mee maken, zoals een unie van twee overlappende cirkels of een polygoon met een gat, en die vervolgens in Plotly visualiseren.

Het proces omvat twee hoofdstappen:

  1. Definieer de complexe vorm met Shapely.
  2. Haal de coördinaten uit het Shapely-object en vorm ze om tot een SVG-padstring, die Plotly kan renderen met type: 'path'.

Een SVG-padstring is een mini-taal om te tekenen. Ze gebruikt commando's zoals M (moveto) om een startpunt te zetten, L (lineto) om een lijn naar een nieuw punt te trekken, en Z om het pad te sluiten door een lijn terug naar het begin te tekenen.

Laten we zien hoe je een polygoon met een uitsparing maakt en toevoegt aan een Plotly-figuur.

import plotly.graph_objects as go
from shapely.geometry import Polygon

# 1. Create a complex shape using Shapely
# An outer rectangle
outer_coords = [(1, 1), (1, 5), (8, 5), (8, 1)]
# An inner rectangle (this will be the "hole")
inner_coords = [(3, 2), (3, 4), (6, 4), (6, 2)]

# Define the polygon with a hole
polygon_with_hole = Polygon(outer_coords, [inner_coords])

# 2. Extract coordinates and build the SVG path string
# The exterior path
exterior_x, exterior_y = polygon_with_hole.exterior.xy
path = f"M {exterior_x[0]} {exterior_y[0]}"
for x, y in zip(exterior_x[1:], exterior_y[1:]):
    path += f" L {x} {y}"
path += " Z" # Close the exterior path

# The interior path (the hole)
interior_x, interior_y = polygon_with_hole.interiors[0].xy
path += f" M {interior_x[0]} {interior_y[0]}"
for x, y in zip(interior_x[1:], interior_y[1:]):
    path += f" L {x} {y}"
path += " Z" # Close the interior path

# Create a figure
fig = go.Figure()

# Add the complex shape to the figure
fig.add_shape(
    type="path",
    path=path,
    fillcolor="LightSeaGreen",
    opacity=0.7,
    line_color="DarkSlateGray"
)

# You can add data to see how the shape interacts with it
fig.add_trace(go.Scatter(
    x=[2, 7, 4.5], 
    y=[3, 3, 1.5], 
    mode='markers', 
    marker=dict(size=15, color='DarkRed')
))

# Use scaleanchor to ensure the shape's aspect ratio is preserved
fig.update_yaxes(scaleanchor="x", scaleratio=1)
fig.show()

Output:

Using Shapely for advanced geometric shapes

Deze techniek is van onschatbare waarde voor geospatiale toepassingen, zoals het plotten van aangepaste verkoopregio's, of in wetenschappelijke contexten waar je onregelmatige interessegebieden wilt highlighten. 

Begrijpen hoe verschillende type dataplots worden gemaakt en hoe je ze kunt verrijken met dit soort tools is een fundamentele vaardigheid voor elke dataprofessional. Je kunt meer leren over verschillende plottypes in deze gids over hoe je ze in Python maakt.

Styling en esthetische aanpassing

De standaardweergave van Plotly-shapes is functioneel, maar het stylen ervan is belangrijk om een verzorgde, professioneel ogende visualisatie te maken. Doordacht gebruik van kleur, transparantie en vullingen kan de leesbaarheid en esthetiek aanzienlijk verbeteren.

Kleur, transparantie en patronenvullingen

Plotly geeft je fijne controle over het uiterlijk van shapes, zodat je ze kunt afstemmen op het kleurenschema en de vormentaal van je plot.

Voor nauwkeurige controle over kleur en transparantie is het gebruik van de RGBA-kleurspecificatie (Rood, Groen, Blauw, Alpha) sterk aan te raden. Het a-kanaal regelt de dekking, waarbij een waarde van 1 volledig ondoorzichtig is en 0 volledig transparant. 

Dit is essentieel voor subtiele highlights, waarbij de shape context toevoegt zonder de onderliggende data volledig te verbergen. Je kunt RGBA-waarden als string meegeven, zoals 'rgba(255, 99, 71, 0.5)'. Laten we een voorbeeld bekijken:

import plotly.graph_objects as go
import numpy as np

# Create sample data for styling demonstration
x = np.linspace(0, 10, 100)
y1 = np.sin(x) + np.random.normal(0, 0.1, 100)
y2 = np.cos(x) + np.random.normal(0, 0.1, 100)

fig = go.Figure()
fig.add_trace(go.Scatter(x=x, y=y1, name='Signal 1', line=dict(color='blue')))
fig.add_trace(go.Scatter(x=x, y=y2, name='Signal 2', line=dict(color='red')))

# RGBA Color Technique 1: Graduated opacity for depth
opacity_levels = [0.15, 0.25, 0.35]
colors = ['255,0,0', '0,255,0', '0,0,255']

for i, (opacity, color) in enumerate(zip(opacity_levels, colors)):
    fig.add_shape(
        type="rect",
        x0=i*2.5, x1=(i+1)*2.5,
        y0=-3, y1=3,
        fillcolor=f"rgba({color},{opacity})",
        line=dict(color=f"rgb({color})", width=2),
        layer="below"
    )

# RGBA Technique 2: Color blending for overlapping regions
fig.add_shape(
    type="circle",
    x0=7.5, x1=9.5,
    y0=-1, y1=1,
    fillcolor="rgba(255, 255, 0, 0.4)",  # Semi-transparent yellow
    line=dict(color="orange", width=3),
    layer="below"
)

fig.update_layout(
    title="Advanced RGBA Color Techniques in Plotly Shapes",
    showlegend=True
)
fig.show()

Output:

Color, opacity, and pattern fills

Strategieën om patronenvullingen te simuleren

Hoewel Plotly geen patronenvullingen native ondersteunt, kunnen dataprofessionals complexe vullingen simuleren met creatieve technieken. Zo kunnen we patronen in de plot creëren met een functie zoals hieronder:

def create_pattern_fill_simulation(fig, x0, x1, y0, y1, pattern_type='diagonal'):
    """Simulate pattern fills using multiple overlapping shapes"""
    
    if pattern_type == 'diagonal':
        # Create diagonal line pattern
        line_spacing = 0.2
        line_width = 1
        
        # Calculate diagonal lines
        range_x = x1 - x0
        range_y = y1 - y0
        num_lines = int((range_x + range_y) / line_spacing)
        
        for i in range(num_lines):
            offset = i * line_spacing
            
            # Calculate line endpoints for diagonal pattern
            start_x = max(x0, x0 + offset - range_y)
            start_y = max(y0, y1 - offset)
            end_x = min(x1, x0 + offset)
            end_y = min(y1, y1 - offset + range_x)
            
            fig.add_shape(
                type="line",
                x0=start_x, x1=end_x,
                y0=start_y, y1=end_y,
                line=dict(color="rgba(0, 0, 0, 0.3)", width=line_width),
                layer="above"
            )
    
    elif pattern_type == 'crosshatch':
        # Create crosshatch pattern
        spacing = 0.3
        
        # Vertical lines
        x_lines = np.arange(x0, x1, spacing)
        for x_line in x_lines:
            fig.add_shape(
                type="line",
                x0=x_line, x1=x_line,
                y0=y0, y1=y1,
                line=dict(color="rgba(100, 100, 100, 0.4)", width=1),
                layer="above"
            )
        
        # Horizontal lines
        y_lines = np.arange(y0, y1, spacing)
        for y_line in y_lines:
            fig.add_shape(
                type="line",
                x0=x0, x1=x1,
                y0=y_line, y1=y_line,
                line=dict(color="rgba(100, 100, 100, 0.4)", width=1),
                layer="above"
            )
    
    return fig

# Demonstration of pattern fill simulation
pattern_fig = go.Figure()
pattern_fig.add_trace(go.Scatter(x=[1, 2, 3, 4, 5], y=[2, 4, 3, 5, 1], 
                                mode='lines+markers', name='Data'))

# Add base rectangle
pattern_fig.add_shape(
    type="rect",
    x0=1.5, x1=3.5,
    y0=1, y1=5,
    fillcolor="rgba(200, 200, 255, 0.3)",
    line=dict(color="blue", width=2),
    layer="below"
)

# Apply pattern fill simulation
pattern_fig = create_pattern_fill_simulation(pattern_fig, 1.5, 3.5, 1, 5, 'diagonal')
pattern_fig.show()

Pattern fill simulation strategies

Gradiënteffect simuleren

Een gradiëntachtig effect maken vereist strategisch stapelen van meerdere shapes met variërende transparantie. We kunnen dit creëren met dezelfde strategie die we voor de patronen gebruikten, zoals hieronder getoond:

def create_gradient_effect(fig, x0, x1, y0, y1, start_color, end_color, num_bands=20):
    """Simulate gradient fill using multiple rectangular bands"""
    
    # Parse RGB values from color strings
    def parse_rgb(color_str):
        if color_str.startswith('rgb('):
            values = color_str[4:-1].split(',')
            return [int(v.strip()) for v in values]
        return [0, 0, 0]  # Default to black if parsing fails
    
    start_rgb = parse_rgb(start_color) if isinstance(start_color, str) else start_color
    end_rgb = parse_rgb(end_color) if isinstance(end_color, str) else end_color
    
    band_height = (y1 - y0) / num_bands
    
    for i in range(num_bands):
        # Calculate interpolated color
        ratio = i / (num_bands - 1)
        r = int(start_rgb[0] + (end_rgb[0] - start_rgb[0]) * ratio)
        g = int(start_rgb[1] + (end_rgb[1] - start_rgb[1]) * ratio)
        b = int(start_rgb[2] + (end_rgb[2] - start_rgb[2]) * ratio)
        
        # Calculate band position
        band_y0 = y0 + i * band_height
        band_y1 = band_y0 + band_height
        
        fig.add_shape(
            type="rect",
            x0=x0, x1=x1,
            y0=band_y0, y1=band_y1,
            fillcolor=f"rgba({r}, {g}, {b}, 0.7)",
            line=dict(width=0),  # No border for seamless gradient
            layer="below"
        )
    
    return fig

# Apply gradient effect
gradient_fig = go.Figure()
gradient_fig.add_trace(go.Scatter(x=list(range(10)), 
                                 y=np.random.randn(10).cumsum(),
                                 mode='lines', name='Time Series'))

gradient_fig = create_gradient_effect(
    gradient_fig, 2, 8, -3, 3, 
    [255, 0, 0], [0, 0, 255],  # Red to blue gradient
    num_bands=30
)
gradient_fig.show()

Output: 

Gradient effect simulation

Probleemoplossing en optimalisatie

Hoewel het toevoegen van Plotly-shapes doorgaans eenvoudig is, kun je problemen ondervinden met uitlijning of performance, vooral in complexe plots. Deze veelvoorkomende valkuilen begrijpen en aanpakken bespaart je veel tijd en moeite.

Veelvoorkomende problemen met shape-uitlijning

Een van de meest voorkomende uitdagingen is om shapes precies te laten verschijnen waar jij ze wilt. Een lijn die de volledige breedte van de plot zou moeten beslaan, kan tekortschieten, of een rechthoek lijnt niet uit met de rasterlijnen zoals verwacht. Deze problemen komen bijna altijd voort uit onjuiste coördinatenreferenties.

Plotly gebruikt twee coördinatensystemen voor het plaatsen van shapes:

  • Datacoördinaten (xref: 'x', yref: 'y'): Dit is de standaard. De positie van de shape is gekoppeld aan de waarden op de x- en y-as. Bijvoorbeeld, x0=50 plaatst het punt op de 50-markering van de x-as.
  • Papiercoördinaten (xref: 'paper', yref: 'paper'): De positie is relatief aan het plotgebied zelf, waar 0 het begin is (linker-/onderrand) en 1 het einde (rechter-/bovenzijde). Dit systeem is onafhankelijk van datapunten of asbereiken.

Het probleem: Je wilt een horizontale lijn tekenen over de hele plot op y=10. Als je die definieert met x0 en x1 in datacoördinaten, beslaat ze slechts het huidige asbereik. Als de gebruiker zoomt of panned, reikt de lijn niet tot de nieuwe randen.

De oplossing: Gebruik 'paper'-coördinaten voor de as die je volledig wilt laten beslaan, om een shape over de volledige breedte of hoogte van het plotgebied te trekken.

import plotly.graph_objects as go

fig = go.Figure(data=go.Scatter(x=[0, 10], y=[0, 20]))

# Incorrect: Vertical line that might not span the full height if the user zooms out.
# This line is drawn from y=0 to y=20 in DATA units.
fig.add_shape(
    type="line",
    x0=5, y0=0, x1=5, y1=20,
    line=dict(color="Red", width=2, dash="dash"),
    name="Incorrect Line" # Name for legend
)

# Correct: Vertical line that ALWAYS spans the full plot height.
# It is drawn from the bottom edge (0) to the top edge (1) of the PLOT area.
fig.add_shape(
    type="line",
    x0=8, y0=0, x1=8, y1=1,
    yref="paper", # The key to spanning the full height
    line=dict(color="Green", width=3),
    name="Correct Line"
)

fig.update_layout(title_text="Correctly Spanning the Plot Area")
fig.show()

Output:

Controleer altijd je xref- en yref-eigenschappen om te zorgen dat je shapes zich gedragen zoals verwacht tijdens gebruikersinteractie, zoals zoomen en pannen.

Prestatieoverwegingen

Als je een handvol shapes aan een plot toevoegt, is de performance-impact te verwaarlozen. Maar het toevoegen van honderden of duizenden shapes kan de rendering en interactiviteit aanzienlijk vertragen. Elke shape is een afzonderlijk SVG-element (Scalable Vector Graphics) dat aan de DOM van de webpagina wordt toegevoegd. Een groot aantal DOM-elementen belast de browser.

Hier zijn strategieën om de performance te optimaliseren:

  • Beperk het aantal shapes: De eenvoudigste oplossing. Als je duizenden rechthoekige gebieden wilt visualiseren, overweeg dan of een ander grafiektype, zoals een go.Heatmap, dezelfde informatie efficiënter kan weergeven als één enkele trace in plaats van duizenden shape-objecten.
  • Gebruik update_layout voor bulktoevoegingen: Zoals eerder vermeld is het toevoegen van een lijst met shapes via fig.update_layout(shapes=...) performanter dan fig.add_shape() in een lus aan te roepen.
  • Overweeg WebGL-traces voor data: Hoewel shapes zelf als SVG worden gerenderd, kun je de algehele performance van een complexe plot verbeteren door WebGL-gebaseerde traces (bijv. go.Scattergl) te gebruiken voor je hoofddata. Dit verplaatst de datarendering naar de GPU, waardoor de CPU soepeler de SVG-shapes kan afhandelen. Dit is nuttig wanneer je een enorme hoeveelheid datapunten hebt én een gematigd aantal shapes.

Voor de meeste use-cases is het de kunst om bewust om te gaan met het aantal unieke shape-elementen dat je creëert. Voordat je duizenden Plotly-shapes in een lus toevoegt, sta even stil en bedenk of er een productievere visualisatiestrategie bestaat.

Conclusie

Plotly-shapes beheersen transformeert je grafieken van simpele datavertegenwoordigingen naar meeslepende, contextrijke verhalen. We hebben gekeken van het tekenen van basislijnen en -rechthoeken tot het stapelen van complexe, interactieve geometrieën. 

Door deze technieken toe te passen, kun je de focus van je publiek sturen, cruciale inzichten highlighten en intuïtievere visualisaties bouwen die helder en impactvol communiceren. 

Klaar om deze skills toe te passen? Ga vandaag nog aan de slag met krachtigere visualisaties door de cursus Introduction to Data Visualization with Plotly in Python te verkennen en je kennis in de praktijk te brengen.

Plotly-shapes: veelgestelde vragen

Waarvoor worden Plotly-shapes gebruikt?

Plotly-shapes worden gebruikt om annotaties zoals lijnen, rechthoeken en cirkels toe te voegen om belangrijke datagebieden in een grafiek te highlighten.

Hoe voeg je meerdere shapes toe in Plotly?

Je kunt efficiënt meerdere shapes toevoegen door een lijst met shape-dictionaries door te geven aan het attribuut shapes in fig.update_layout().

Hoe teken je een verticale lijn in Plotly?

Je kunt een volledige verticale lijn tekenen door type='line' te gebruiken en de eigenschap yref='paper' in te stellen om de volledige plothoogte te beslaan.

Kun je een shape achter de data plaatsen in Plotly?

Ja, je kunt een shape achter datatraces plaatsen door de eigenschap layer op 'below' te zetten.

Hoe voeg je een shape toe aan slechts één subplot in Plotly?

Je kunt een shape aan slechts één subplot toevoegen door de argumenten xref en yref op te geven in de definitie ervan.

Auteur

Rajesh Kumar

Ik ben contentschrijver op het gebied van data science. Ik maak graag content over AI/ML/DS-onderwerpen. Ook ontdek ik nieuwe AI-tools en schrijf ik erover.

Onderwerpen