How to Create a 3D Scatter Plot in Plotly
Three-dimensional scatter plots excel at revealing relationships between three continuous variables simultaneously. They're particularly valuable for clustering analysis, principal component analysis...
Key Insights
- Plotly’s Scatter3d creates interactive 3D visualizations that users can rotate and zoom, making it superior to static matplotlib alternatives for exploratory data analysis
- Color mapping and marker sizing enable you to effectively visualize up to 5 dimensions of data simultaneously in a single 3D scatter plot
- Setting the initial camera position programmatically ensures your audience sees the most informative view of your data immediately, rather than forcing them to hunt for patterns
Introduction & Setup
Three-dimensional scatter plots excel at revealing relationships between three continuous variables simultaneously. They’re particularly valuable for clustering analysis, principal component analysis visualization, and any scenario where you need to understand how multiple variables interact in space.
Plotly stands out from alternatives like matplotlib’s mplot3d because it generates truly interactive visualizations. Users can rotate, pan, and zoom without additional code, making pattern discovery intuitive. The library also renders beautifully in Jupyter notebooks, standalone HTML files, and web applications.
Install Plotly using pip:
pip install plotly
For working with real datasets, you’ll also want pandas and numpy:
pip install pandas numpy scikit-learn
Here are the essential imports:
import plotly.graph_objects as go
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
Creating a Basic 3D Scatter Plot
The go.Scatter3d object is your primary tool. At minimum, you need three arrays for x, y, and z coordinates, plus a mode specification. The mode parameter accepts ‘markers’, ’lines’, or ‘markers+lines’.
# Generate random data
np.random.seed(42)
n_points = 100
x_data = np.random.randn(n_points)
y_data = np.random.randn(n_points)
z_data = np.random.randn(n_points)
# Create the 3D scatter plot
fig = go.Figure(data=[go.Scatter3d(
x=x_data,
y=y_data,
z=z_data,
mode='markers'
)])
fig.show()
This creates a basic interactive plot with default styling. You can immediately rotate it by clicking and dragging, zoom with your scroll wheel, and pan by right-clicking and dragging. These interactions are built-in—no configuration required.
Customizing Markers and Colors
Default markers are functional but boring. Plotly provides extensive customization through the marker parameter, which accepts a dictionary of properties.
For solid colors and fixed sizes, use simple scalar values:
fig = go.Figure(data=[go.Scatter3d(
x=x_data,
y=y_data,
z=z_data,
mode='markers',
marker=dict(
size=8,
color='rgb(255, 0, 0)',
opacity=0.8
)
)])
The real power emerges when you map marker properties to data dimensions. This lets you visualize four or five dimensions simultaneously:
# Generate a fourth dimension for color mapping
color_dimension = np.random.randn(n_points)
# And a fifth for size
size_dimension = np.abs(np.random.randn(n_points)) * 10
fig = go.Figure(data=[go.Scatter3d(
x=x_data,
y=y_data,
z=z_data,
mode='markers',
marker=dict(
size=size_dimension,
color=color_dimension,
colorscale='Viridis',
showscale=True,
colorbar=dict(title="4th Dimension"),
opacity=0.8,
line=dict(
color='black',
width=0.5
)
)
)])
fig.show()
The colorscale parameter accepts named scales like ‘Viridis’, ‘Plasma’, ‘Cividis’, or custom color arrays. Setting showscale=True adds a color bar legend. The line property adds borders to markers, improving visibility when points overlap.
Adding Labels, Titles, and Axis Configuration
Unlabeled plots are useless in professional contexts. Configure all text elements through the update_layout method:
fig = go.Figure(data=[go.Scatter3d(
x=x_data,
y=y_data,
z=z_data,
mode='markers',
marker=dict(size=5, color=color_dimension, colorscale='Viridis')
)])
fig.update_layout(
title='3D Scatter Plot with Custom Styling',
scene=dict(
xaxis=dict(
title='X Axis Label',
backgroundcolor="rgb(230, 230,230)",
gridcolor="white",
showbackground=True,
range=[-3, 3]
),
yaxis=dict(
title='Y Axis Label',
backgroundcolor="rgb(230, 230,230)",
gridcolor="white",
showbackground=True,
range=[-3, 3]
),
zaxis=dict(
title='Z Axis Label',
backgroundcolor="rgb(230, 230,230)",
gridcolor="white",
showbackground=True,
range=[-3, 3]
)
),
width=800,
height=700,
margin=dict(l=0, r=0, b=0, t=40)
)
fig.show()
The scene dictionary controls all 3D-specific properties. Setting explicit axis ranges prevents Plotly from auto-scaling, which can distort perception of your data’s distribution. Background colors and grid styling improve readability, especially when presenting to audiences unfamiliar with your data.
Interactive Features and Camera Angles
Plotly’s default camera position doesn’t always showcase your data optimally. Control the initial view using camera parameters:
fig = go.Figure(data=[go.Scatter3d(
x=x_data,
y=y_data,
z=z_data,
mode='markers',
marker=dict(size=5, color=color_dimension, colorscale='Plasma')
)])
fig.update_layout(
scene=dict(
camera=dict(
eye=dict(x=1.5, y=1.5, z=1.3),
center=dict(x=0, y=0, z=0),
up=dict(x=0, y=0, z=1)
),
xaxis=dict(title='X'),
yaxis=dict(title='Y'),
zaxis=dict(title='Z')
)
)
fig.show()
The eye parameter positions the camera in 3D space relative to your data’s center. Values greater than 1 or less than -1 zoom out; values closer to 0 zoom in. The up vector defines which direction is “up”—typically you want z-axis pointing upward, but you can rotate this for different perspectives.
For presentations, experiment with camera angles until you find one that immediately reveals the pattern you’re highlighting. Don’t make your audience work to discover what you already know.
Real-World Example: Visualizing Multi-Dimensional Data
Let’s apply these techniques to the iris dataset, a classic multi-dimensional dataset with four features and three species categories:
# Load the iris dataset
iris = load_iris()
df = pd.DataFrame(
data=iris.data,
columns=iris.feature_names
)
df['species'] = iris.target
# Map species numbers to names
species_names = ['Setosa', 'Versicolor', 'Virginica']
df['species_name'] = df['species'].map(lambda x: species_names[x])
# Define colors for each species
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1']
color_map = {0: colors[0], 1: colors[1], 2: colors[2]}
# Create the 3D scatter plot
fig = go.Figure()
for species_id in df['species'].unique():
species_df = df[df['species'] == species_id]
fig.add_trace(go.Scatter3d(
x=species_df['sepal length (cm)'],
y=species_df['sepal width (cm)'],
z=species_df['petal length (cm)'],
mode='markers',
name=species_names[species_id],
marker=dict(
size=species_df['petal width (cm)'] * 3, # 4th dimension
color=color_map[species_id],
opacity=0.8,
line=dict(color='white', width=0.5)
)
))
fig.update_layout(
title='Iris Dataset: 4D Visualization',
scene=dict(
xaxis=dict(title='Sepal Length (cm)'),
yaxis=dict(title='Sepal Width (cm)'),
zaxis=dict(title='Petal Length (cm)'),
camera=dict(
eye=dict(x=1.7, y=1.7, z=1.3)
)
),
width=900,
height=700,
legend=dict(
x=0.7,
y=0.9,
bgcolor='rgba(255, 255, 255, 0.8)'
)
)
fig.show()
This visualization reveals clear clustering between species while simultaneously showing four dimensions: three spatial coordinates plus petal width encoded in marker size. The color coding makes species identification immediate, and the semi-transparent markers with white borders prevent visual clutter where clusters overlap.
Exporting and Sharing
Interactive HTML files preserve all Plotly functionality and work in any modern browser:
# Save as interactive HTML
fig.write_html("3d_scatter_plot.html")
For reports and publications requiring static images:
# Requires kaleido package: pip install kaleido
fig.write_image("3d_scatter_plot.png", width=1200, height=900, scale=2)
# Or as a vector format for publications
fig.write_image("3d_scatter_plot.pdf")
The scale parameter controls resolution—use 2 or higher for high-DPI displays and print publications. PDF export maintains vector quality for infinite scaling.
For presentations, HTML files are superior because you can rotate the plot during your talk to emphasize different perspectives. Save PNGs of your preferred angles as backups in case of technical difficulties.
Three-dimensional scatter plots in Plotly transform complex multi-dimensional data into intuitive, explorable visualizations. The interactivity isn’t a gimmick—it’s a fundamental advantage that helps both you and your audience discover patterns that static plots obscure. Master the camera positioning, leverage color and size mapping for additional dimensions, and your 3D visualizations will communicate insights that tables and 2D plots simply cannot convey.