Create an app showing 3D data visualizations. Generate mock 3D data: - Spiral helix with noise (100 points) - Surface plot (z = sin(x)*cos(y)) - 3D clusters (3 clusters, 50 points each) Features: - Viz selector dropdown - Interactive 3D plots with rotate/zoom - Controls for: * Point styling (size, color, opacity) * Axis settings * Animation speed Surface plot options: - Function parameter sliders - Color scheme toggle - Contour lines All plots should be fully interactive.
Drop files here
or click to upload
import streamlit as st
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from sklearn.datasets import make_blobs
import plotly.express as px
# Page config
st.set_page_config(
page_title="3D Data Visualizations",
layout="wide",
initial_sidebar_state="expanded"
)
# Add title and description
st.title("3D Data Visualizations")
st.markdown("""
This app demonstrates various interactive 3D data visualizations:
- **Spiral Helix**: A 3D spiral with random noise
- **Surface Plot**: Interactive 3D surface based on mathematical functions
- **3D Clusters**: Three distinct clusters in 3D space
""")
# Sidebar for visualization selection and controls
st.sidebar.header("Visualization Controls")
# Visualization selector
viz_type = st.sidebar.selectbox(
"Select Visualization",
["Spiral Helix", "Surface Plot", "3D Clusters"]
)
# Common 3D plot controls
st.sidebar.subheader("General Settings")
point_size = st.sidebar.slider("Point Size", 2, 10, 5)
opacity = st.sidebar.slider("Opacity", 0.1, 1.0, 0.8)
colorscales = ['Viridis', 'Plasma', 'Inferno', 'Magma', 'Cividis', 'Turbo',
'Blues', 'Greens', 'Reds', 'YlOrRd', 'Jet']
color_scale = st.sidebar.selectbox("Color Scale", colorscales, index=0)
# Generate spiral helix data with noise
def generate_spiral_data(n_points=100, noise_level=0.5):
t = np.linspace(0, 10*np.pi, n_points)
x = np.cos(t)
y = np.sin(t)
z = t/3
# Add some noise
x += noise_level * np.random.randn(n_points)
y += noise_level * np.random.randn(n_points)
z += noise_level * np.random.randn(n_points)
return pd.DataFrame({'x': x, 'y': y, 'z': z, 't': t})
# Generate surface plot data
def generate_surface_data(x_range=(-5, 5), y_range=(-5, 5), n_points=50,
freq_x=1.0, freq_y=1.0, amplitude=1.0):
x = np.linspace(x_range[0], x_range[1], n_points)
y = np.linspace(y_range[0], y_range[1], n_points)
x_grid, y_grid = np.meshgrid(x, y)
z_grid = amplitude * np.sin(freq_x * x_grid) * np.cos(freq_y * y_grid)
return x_grid, y_grid, z_grid
# Generate 3D cluster data
def generate_cluster_data(n_clusters=3, n_samples=50, cluster_std=0.8, random_state=42):
centers = [
[-3, -3, -3],
[0, 0, 0],
[3, 3, 3]
]
X, y = make_blobs(n_samples=n_samples*n_clusters, centers=centers,
cluster_std=cluster_std, random_state=random_state)
df = pd.DataFrame(X, columns=['x', 'y', 'z'])
df['cluster'] = y
return df
# Spiral Helix Visualization
if viz_type == "Spiral Helix":
st.header("Spiral Helix with Noise")
# Spiral specific controls
st.sidebar.subheader("Spiral Settings")
n_points = st.sidebar.slider("Number of Points", 50, 500, 100)
noise_level = st.sidebar.slider("Noise Level", 0.0, 1.0, 0.5)
animation_speed = st.sidebar.slider("Animation Speed", 0, 100, 50)
show_line = st.sidebar.checkbox("Show Connection Line", True)
# Generate data
spiral_data = generate_spiral_data(n_points, noise_level)
# Create figure
fig = go.Figure()
# Add scatter points
fig.add_trace(go.Scatter3d(
x=spiral_data['x'],
y=spiral_data['y'],
z=spiral_data['z'],
mode='markers' if not show_line else 'markers+lines',
marker=dict(
size=point_size,
color=spiral_data['t'],
colorscale=color_scale,
opacity=opacity
),
line=dict(
color='rgba(100, 100, 100, 0.5)',
width=2
) if show_line else None
))
# Update layout
fig.update_layout(
scene=dict(
xaxis_title='X',
yaxis_title='Y',
zaxis_title='Z',
aspectmode='cube'
),
margin=dict(l=0, r=0, b=0, t=30),
height=700,
)
# Add animation frame if enabled
if animation_speed > 0:
fig.update_layout(
updatemenus=[
dict(
type="buttons",
buttons=[
dict(
label="Play",
method="animate",
args=[None, {"frame": {"duration": 100 - animation_speed + 10, "redraw": True},
"fromcurrent": True}]
)
],
direction="left",
pad={"r": 10, "t": 10},
showactive=False,
x=0.1,
xanchor="right",
y=0,
yanchor="top"
)
]
)
frames = [go.Frame(
data=[go.Scatter3d(
x=spiral_data['x'][:i+1],
y=spiral_data['y'][:i+1],
z=spiral_data['z'][:i+1],
mode='markers' if not show_line else 'markers+lines',
marker=dict(
size=point_size,
color=spiral_data['t'][:i+1],
colorscale=color_scale,
opacity=opacity
),
line=dict(
color='rgba(100, 100, 100, 0.5)',
width=2
) if show_line else None
)],
name=f"frame{i}"
) for i in range(10, n_points, max(1, int(n_points/30)))]
fig.frames = frames
st.plotly_chart(fig, use_container_width=True)
# Description
st.markdown("""
**About this visualization:**
The spiral helix visualization shows a 3D spiral with random noise added to each point.
- The color represents the position along the spiral
- Adjust the noise level to see how randomness affects the pattern
- Enable animation to see the spiral being drawn point by point
""")
# Surface Plot Visualization
elif viz_type == "Surface Plot":
st.header("3D Surface Plot")
# Surface specific controls
st.sidebar.subheader("Surface Settings")
# Function parameters
st.sidebar.markdown("#### Function Parameters")
function_type = st.sidebar.selectbox("Function Type",
["sin(x)*cos(y)",
"sin(sqrt(x²+y²))",
"(x²-y²)",
"sin(x)*sin(y)"])
freq_x = st.sidebar.slider("X Frequency", 0.1, 2.0, 1.0, 0.1)
freq_y = st.sidebar.slider("Y Frequency", 0.1, 2.0, 1.0, 0.1)
amplitude = st.sidebar.slider("Amplitude", 0.1, 2.0, 1.0, 0.1)
# Display settings
st.sidebar.markdown("#### Display Settings")
show_contours = st.sidebar.checkbox("Show Contour Lines", True)
contour_width = st.sidebar.slider("Contour Width", 1, 5, 2) if show_contours else 2
contour_color = st.sidebar.color_picker("Contour Color", "#000000") if show_contours else "#000000"
surface_points = st.sidebar.slider("Resolution", 20, 100, 50)
# Generate base grid
x_grid, y_grid, _ = generate_surface_data(n_points=surface_points,
freq_x=freq_x,
freq_y=freq_y,
amplitude=amplitude)
# Calculate Z based on selected function
if function_type == "sin(x)*cos(y)":
z_grid = amplitude * np.sin(freq_x * x_grid) * np.cos(freq_y * y_grid)
elif function_type == "sin(sqrt(x²+y²))":
r = np.sqrt(x_grid**2 + y_grid**2)
z_grid = amplitude * np.sin(freq_x * r)
elif function_type == "(x²-y²)":
z_grid = amplitude * (x_grid**2 - y_grid**2) / 10
elif function_type == "sin(x)*sin(y)":
z_grid = amplitude * np.sin(freq_x * x_grid) * np.sin(freq_y * y_grid)
# Create figure
fig = go.Figure()
# Add surface
fig.add_trace(go.Surface(
x=x_grid,
y=y_grid,
z=z_grid,
colorscale=color_scale,
opacity=opacity,
contours={
"z": {
"show": show_contours,
"start": z_grid.min(),
"end": z_grid.max(),
"size": (z_grid.max() - z_grid.min()) / 10,
"width": contour_width,
"color": contour_color
}
}
))
# Update layout
fig.update_layout(
scene=dict(
xaxis_title='X',
yaxis_title='Y',
zaxis_title='Z',
aspectmode='cube'
),
margin=dict(l=0, r=0, b=0, t=30),
height=700,
)
st.plotly_chart(fig, use_container_width=True)
# Description
st.markdown(f"""
**About this visualization:**
The surface plot shows the function: **{function_type}**
- Adjust the frequency and amplitude to change the shape of the surface
- Enable contour lines to better visualize elevation changes
- Try different functions to explore various 3D surfaces
**Current Parameters:**
- X Frequency: {freq_x}
- Y Frequency: {freq_y}
- Amplitude: {amplitude}
- Resolution: {surface_points}x{surface_points} points
""")
# 3D Clusters Visualization
elif viz_type == "3D Clusters":
st.header("3D Cluster Visualization")
# Cluster specific controls
st.sidebar.subheader("Cluster Settings")
n_clusters = st.sidebar.slider("Number of Clusters", 2, 5, 3)
points_per_cluster = st.sidebar.slider("Points per Cluster", 20, 100, 50)
cluster_std = st.sidebar.slider("Cluster Std Dev", 0.2, 2.0, 0.8)
random_seed = st.sidebar.number_input("Random Seed", 0, 1000, 42)
# Display settings
show_centroids = st.sidebar.checkbox("Show Centroids", True)
show_convex_hull = st.sidebar.checkbox("Show Convex Hull", False)
# Generate data
cluster_data = generate_cluster_data(n_clusters, points_per_cluster, cluster_std, random_seed)
# Create figure
fig = go.Figure()
# Add scatter points for each cluster
for cluster_id in range(n_clusters):
cluster_points = cluster_data[cluster_data['cluster'] == cluster_id]
fig.add_trace(go.Scatter3d(
x=cluster_points['x'],
y=cluster_points['y'],
z=cluster_points['z'],
mode='markers',
marker=dict(
size=point_size,
color=cluster_id,
colorscale=color_scale,
opacity=opacity
),
name=f'Cluster {cluster_id+1}'
))
# Add centroid if enabled
if show_centroids:
centroid = cluster_points.mean()
fig.add_trace(go.Scatter3d(
x=[centroid['x']],
y=[centroid['y']],
z=[centroid['z']],
mode='markers',
marker=dict(
size=point_size*2,
color=cluster_id,
colorscale=color_scale,
symbol='diamond',
line=dict(
width=2,
color='black'
)
),
name=f'Centroid {cluster_id+1}'
))
# Update layout
fig.update_layout(
scene=dict(
xaxis_title='X',
yaxis_title='Y',
zaxis_title='Z',
aspectmode='cube'
),
margin=dict(l=0, r=0, b=0, t=30),
height=700,
)
st.plotly_chart(fig, use_container_width=True)
# Display stats about each cluster
st.subheader("Cluster Statistics")
# Prepare stats
cluster_stats = []
for cluster_id in range(n_clusters):
cluster_points = cluster_data[cluster_data['cluster'] == cluster_id]
centroid = cluster_points.mean()[['x', 'y', 'z']]
std_dev = cluster_points.std()[['x', 'y', 'z']]
cluster_stats.append({
'Cluster': f'Cluster {cluster_id+1}',
'Count': len(cluster_points),
'Centroid (x, y, z)': f'({centroid["x"]:.2f}, {centroid["y"]:.2f}, {centroid["z"]:.2f})',
'StdDev (x, y, z)': f'({std_dev["x"]:.2f}, {std_dev["y"]:.2f}, {std_dev["z"]:.2f})',
})
stats_df = pd.DataFrame(cluster_stats)
st.table(stats_df)
# Description
st.markdown(f"""
**About this visualization:**
The 3D cluster visualization shows {n_clusters} clusters in 3D space.
- Each color represents a different cluster
- The diamond markers show the cluster centroids
- Adjust cluster settings to see how the distribution changes
**Current Settings:**
- Clusters: {n_clusters}
- Points per Cluster: {points_per_cluster}
- Cluster Standard Deviation: {cluster_std}
""")
# Add information in the sidebar
st.sidebar.markdown("---")
st.sidebar.subheader("About")
st.sidebar.info("""
This app demonstrates the power of 3D data visualization using Plotly and Streamlit.
All visualizations are interactive - click and drag to rotate, scroll to zoom.
""")
Hi! I can help you with any questions about Streamlit and Python. What would you like to know?