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.
To upload files, please first save the app
import streamlit as st
import numpy as np
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
from scipy.spatial.distance import cdist
st.set_page_config(page_title="3D Data Visualizer", layout="wide")
# Generate mock data functions
@st.cache_data
def generate_spiral_helix(n_points=100, noise_level=0.1):
"""Generate spiral helix with noise"""
t = np.linspace(0, 4*np.pi, n_points)
x = np.cos(t) + noise_level * np.random.randn(n_points)
y = np.sin(t) + noise_level * np.random.randn(n_points)
z = t + noise_level * np.random.randn(n_points)
return pd.DataFrame({'x': x, 'y': y, 'z': z, 't': t})
@st.cache_data
def generate_surface_data(x_range=(-5, 5), y_range=(-5, 5), resolution=50,
amplitude=1.0, freq_x=1.0, freq_y=1.0):
"""Generate surface plot data: z = amplitude * sin(freq_x*x) * cos(freq_y*y)"""
x = np.linspace(x_range[0], x_range[1], resolution)
y = np.linspace(y_range[0], y_range[1], resolution)
X, Y = np.meshgrid(x, y)
Z = amplitude * np.sin(freq_x * X) * np.cos(freq_y * Y)
return X, Y, Z
@st.cache_data
def generate_3d_clusters(n_clusters=3, points_per_cluster=50):
"""Generate 3D clusters"""
data = []
cluster_centers = [
[2, 2, 2],
[-2, -2, 2],
[0, 2, -2]
]
for i, center in enumerate(cluster_centers):
# Generate points around each center
points = np.random.multivariate_normal(
center,
[[0.5, 0, 0], [0, 0.5, 0], [0, 0, 0.5]],
points_per_cluster
)
cluster_data = pd.DataFrame({
'x': points[:, 0],
'y': points[:, 1],
'z': points[:, 2],
'cluster': f'Cluster {i+1}'
})
data.append(cluster_data)
return pd.concat(data, ignore_index=True)
# Main app
st.title("๐ 3D Data Visualizer")
st.markdown("Interactive 3D data visualizations with customizable styling and controls")
# Sidebar for visualization selection
st.sidebar.header("๐ Visualization Settings")
viz_type = st.sidebar.selectbox(
"Select Visualization Type",
["Spiral Helix", "Surface Plot", "3D Clusters"]
)
# Common styling controls
st.sidebar.subheader("๐จ Styling Controls")
point_size = st.sidebar.slider("Point Size", 2, 15, 6)
opacity = st.sidebar.slider("Opacity", 0.1, 1.0, 0.8)
# Axis settings
st.sidebar.subheader("๐ Axis Settings")
show_grid = st.sidebar.checkbox("Show Grid", True)
show_axes = st.sidebar.checkbox("Show Axes", True)
# Main content area
col1, col2 = st.columns([3, 1])
with col1:
if viz_type == "Spiral Helix":
st.subheader("๐ Spiral Helix with Noise")
# Controls
with st.expander("Helix Parameters"):
n_points = st.slider("Number of Points", 50, 200, 100)
noise_level = st.slider("Noise Level", 0.0, 0.5, 0.1)
color_mode = st.selectbox("Color Mode", ["Time", "Height", "Distance"])
# Generate data
helix_data = generate_spiral_helix(n_points, noise_level)
# Color mapping
if color_mode == "Time":
color_vals = helix_data['t']
colorscale = 'viridis'
elif color_mode == "Height":
color_vals = helix_data['z']
colorscale = 'plasma'
else: # Distance
origin = np.array([0, 0, 0])
points = helix_data[['x', 'y', 'z']].values
color_vals = np.sqrt(np.sum((points - origin)**2, axis=1))
colorscale = 'turbo'
# Create 3D scatter plot
fig = go.Figure(data=go.Scatter3d(
x=helix_data['x'],
y=helix_data['y'],
z=helix_data['z'],
mode='markers',
marker=dict(
size=point_size,
color=color_vals,
colorscale=colorscale,
opacity=opacity,
colorbar=dict(title=color_mode)
),
text=[f"Point {i}<br>t: {t:.2f}" for i, t in enumerate(helix_data['t'])],
hovertemplate="<b>Point %{text}</b><br>" +
"X: %{x:.2f}<br>" +
"Y: %{y:.2f}<br>" +
"Z: %{z:.2f}<extra></extra>"
))
fig.update_layout(
title="Interactive Spiral Helix",
scene=dict(
xaxis=dict(showgrid=show_grid, visible=show_axes),
yaxis=dict(showgrid=show_grid, visible=show_axes),
zaxis=dict(showgrid=show_grid, visible=show_axes),
camera=dict(eye=dict(x=1.2, y=1.2, z=1.2))
),
height=600
)
st.plotly_chart(fig, use_container_width=True)
elif viz_type == "Surface Plot":
st.subheader("๐๏ธ Interactive Surface Plot")
# Surface parameters
with st.expander("Surface Parameters"):
col_a, col_b = st.columns(2)
with col_a:
amplitude = st.slider("Amplitude", 0.5, 3.0, 1.0)
freq_x = st.slider("X Frequency", 0.5, 3.0, 1.0)
with col_b:
freq_y = st.slider("Y Frequency", 0.5, 3.0, 1.0)
resolution = st.slider("Resolution", 20, 100, 50)
# Color and style options
with st.expander("Surface Styling"):
colorscale = st.selectbox("Color Scheme",
['viridis', 'plasma', 'inferno', 'magma', 'turbo', 'rainbow'])
show_contours = st.checkbox("Show Contour Lines", False)
surface_opacity = st.slider("Surface Opacity", 0.3, 1.0, 0.9)
# Generate surface data
X, Y, Z = generate_surface_data(
amplitude=amplitude,
freq_x=freq_x,
freq_y=freq_y,
resolution=resolution
)
# Create surface plot
fig = go.Figure()
fig.add_trace(go.Surface(
x=X, y=Y, z=Z,
colorscale=colorscale,
opacity=surface_opacity,
contours=dict(
z=dict(show=show_contours, usecolormap=True),
x=dict(show=show_contours),
y=dict(show=show_contours)
) if show_contours else None
))
fig.update_layout(
title=f"Surface: z = {amplitude}รsin({freq_x}x)รcos({freq_y}y)",
scene=dict(
xaxis=dict(title="X", showgrid=show_grid, visible=show_axes),
yaxis=dict(title="Y", showgrid=show_grid, visible=show_axes),
zaxis=dict(title="Z", showgrid=show_grid, visible=show_axes),
camera=dict(eye=dict(x=1.5, y=1.5, z=1.5))
),
height=600
)
st.plotly_chart(fig, use_container_width=True)
else: # 3D Clusters
st.subheader("๐ฏ 3D Cluster Visualization")
# Cluster parameters
with st.expander("Cluster Parameters"):
points_per_cluster = st.slider("Points per Cluster", 20, 100, 50)
color_clusters = st.checkbox("Color by Cluster", True)
# Generate cluster data
cluster_data = generate_3d_clusters(points_per_cluster=points_per_cluster)
if color_clusters:
# Create scatter plot with cluster colors
fig = px.scatter_3d(
cluster_data,
x='x', y='y', z='z',
color='cluster',
size_max=point_size,
opacity=opacity,
title="3D Clusters"
)
else:
# Single color
fig = go.Figure(data=go.Scatter3d(
x=cluster_data['x'],
y=cluster_data['y'],
z=cluster_data['z'],
mode='markers',
marker=dict(
size=point_size,
opacity=opacity,
color='lightblue'
),
text=cluster_data['cluster'],
hovertemplate="<b>%{text}</b><br>" +
"X: %{x:.2f}<br>" +
"Y: %{y:.2f}<br>" +
"Z: %{z:.2f}<extra></extra>"
))
fig.update_layout(
scene=dict(
xaxis=dict(title="X", showgrid=show_grid, visible=show_axes),
yaxis=dict(title="Y", showgrid=show_grid, visible=show_axes),
zaxis=dict(title="Z", showgrid=show_grid, visible=show_axes),
camera=dict(eye=dict(x=1.2, y=1.2, z=1.2))
),
height=600
)
st.plotly_chart(fig, use_container_width=True)
with col2:
st.subheader("๐ Data Statistics")
if viz_type == "Spiral Helix":
data = generate_spiral_helix()
st.write("**Spiral Helix Stats:**")
st.write(f"Points: {len(data)}")
st.write(f"X range: [{data['x'].min():.2f}, {data['x'].max():.2f}]")
st.write(f"Y range: [{data['y'].min():.2f}, {data['y'].max():.2f}]")
st.write(f"Z range: [{data['z'].min():.2f}, {data['z'].max():.2f}]")
elif viz_type == "Surface Plot":
st.write("**Surface Function:**")
st.write("z = A ร sin(fx ร x) ร cos(fy ร y)")
st.write("where:")
st.write("- A = amplitude")
st.write("- fx = x frequency")
st.write("- fy = y frequency")
else: # 3D Clusters
data = generate_3d_clusters()
st.write("**Cluster Stats:**")
st.write(f"Total points: {len(data)}")
st.write(f"Clusters: {data['cluster'].nunique()}")
for cluster in data['cluster'].unique():
cluster_data = data[data['cluster'] == cluster]
st.write(f"**{cluster}:** {len(cluster_data)} points")
# Footer with controls explanation
st.markdown("---")
st.markdown("""
### ๐ฎ Interactive Controls
- **Rotate:** Click and drag to rotate the 3D view
- **Zoom:** Use mouse wheel or pinch gesture
- **Pan:** Hold Shift + click and drag
- **Reset View:** Double-click on the plot
- **Hover:** Mouse over points for detailed information
""")
# Animation controls (placeholder for future enhancement)
with st.sidebar:
st.markdown("---")
st.subheader("๐ฌ Animation (Coming Soon)")
animation_speed = st.slider("Animation Speed", 0.1, 2.0, 1.0, disabled=True)
st.info("Animation features will be added in a future update!")
Hi! I can help you with any questions about Streamlit and Python. What would you like to know?