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 sklearn.datasets import make_blobs
import time
# Page config
st.set_page_config(page_title="3D Data Visualizer", layout="wide")
st.title("๐ 3D Data Visualizer")
st.markdown("Explore interactive 3D visualizations with customizable controls")
# Sidebar for visualization selection
st.sidebar.header("Visualization Controls")
viz_type = st.sidebar.selectbox(
"Choose Visualization",
["Spiral Helix", "Surface Plot", "3D Clusters"]
)
# Common styling controls
st.sidebar.subheader("Point Styling")
point_size = st.sidebar.slider("Point Size", 1, 20, 5)
opacity = st.sidebar.slider("Opacity", 0.1, 1.0, 0.8)
# Color scheme for points
color_scheme = st.sidebar.selectbox(
"Color Scheme",
["viridis", "plasma", "rainbow", "turbo", "blues", "reds"]
)
# Axis settings
st.sidebar.subheader("Axis Settings")
show_grid = st.sidebar.checkbox("Show Grid", True)
show_axes = st.sidebar.checkbox("Show Axes", True)
# Animation settings
st.sidebar.subheader("Animation")
animation_speed = st.sidebar.slider("Animation Speed (ms)", 100, 2000, 1000)
auto_rotate = st.sidebar.checkbox("Auto Rotate", False)
def generate_spiral_helix(n_points=100):
"""Generate spiral helix data with noise"""
t = np.linspace(0, 4*np.pi, n_points)
noise_factor = st.sidebar.slider("Noise Factor", 0.0, 1.0, 0.1)
x = np.cos(t) + noise_factor * np.random.normal(0, 0.1, n_points)
y = np.sin(t) + noise_factor * np.random.normal(0, 0.1, n_points)
z = t/2 + noise_factor * np.random.normal(0, 0.1, n_points)
return pd.DataFrame({'x': x, 'y': y, 'z': z, 't': t})
def generate_surface_data():
"""Generate surface plot data"""
st.sidebar.subheader("Surface Parameters")
x_range = st.sidebar.slider("X Range", 1, 10, 5)
y_range = st.sidebar.slider("Y Range", 1, 10, 5)
resolution = st.sidebar.slider("Resolution", 20, 100, 50)
freq_x = st.sidebar.slider("X Frequency", 0.5, 3.0, 1.0)
freq_y = st.sidebar.slider("Y Frequency", 0.5, 3.0, 1.0)
x = np.linspace(-x_range, x_range, resolution)
y = np.linspace(-y_range, y_range, resolution)
X, Y = np.meshgrid(x, y)
Z = np.sin(freq_x * X) * np.cos(freq_y * Y)
return X, Y, Z
def generate_3d_clusters():
"""Generate 3D cluster data"""
st.sidebar.subheader("Cluster Parameters")
n_clusters = st.sidebar.slider("Number of Clusters", 2, 6, 3)
points_per_cluster = st.sidebar.slider("Points per Cluster", 20, 100, 50)
cluster_std = st.sidebar.slider("Cluster Spread", 0.5, 3.0, 1.0)
X, y = make_blobs(
n_samples=n_clusters * points_per_cluster,
centers=n_clusters,
n_features=3,
cluster_std=cluster_std,
random_state=42
)
return pd.DataFrame({'x': X[:, 0], 'y': X[:, 1], 'z': X[:, 2], 'cluster': y})
# Main content area
col1, col2 = st.columns([3, 1])
with col1:
if viz_type == "Spiral Helix":
st.subheader("๐ Spiral Helix with Noise")
# Generate data
df = generate_spiral_helix()
# Create 3D scatter plot
fig = go.Figure(data=go.Scatter3d(
x=df['x'],
y=df['y'],
z=df['z'],
mode='markers',
marker=dict(
size=point_size,
color=df['t'],
colorscale=color_scheme,
opacity=opacity,
colorbar=dict(title="Parameter t")
),
text=[f"Point {i}<br>t={t:.2f}" for i, t in enumerate(df['t'])],
hovertemplate="<b>Point %{text}</b><br>" +
"X: %{x:.3f}<br>" +
"Y: %{y:.3f}<br>" +
"Z: %{z:.3f}<extra></extra>"
))
# Add spiral path line
if st.sidebar.checkbox("Show Path", True):
fig.add_trace(go.Scatter3d(
x=df['x'],
y=df['y'],
z=df['z'],
mode='lines',
line=dict(color='rgba(255,255,255,0.3)', width=2),
showlegend=False,
hoverinfo='skip'
))
elif viz_type == "Surface Plot":
st.subheader("๐๏ธ Surface Plot: z = sin(x)ยทcos(y)")
# Generate surface data
X, Y, Z = generate_surface_data()
# Surface plot options
st.sidebar.subheader("Surface Options")
surface_color_scheme = st.sidebar.selectbox(
"Surface Color Scheme",
["viridis", "plasma", "rainbow", "turbo", "blues", "reds"],
key="surface_color"
)
show_contours = st.sidebar.checkbox("Show Contour Lines", True)
wireframe_mode = st.sidebar.checkbox("Wireframe Mode", False)
# Create surface plot
fig = go.Figure()
if wireframe_mode:
fig.add_trace(go.Surface(
x=X, y=Y, z=Z,
colorscale=surface_color_scheme,
opacity=0.7,
showscale=True,
contours=dict(
z=dict(show=show_contours, usecolormap=True, highlightcolor="white", project_z=True)
) if show_contours else None
))
else:
fig.add_trace(go.Surface(
x=X, y=Y, z=Z,
colorscale=surface_color_scheme,
showscale=True,
contours=dict(
z=dict(show=show_contours, usecolormap=True, highlightcolor="white", project_z=True)
) if show_contours else None
))
else: # 3D Clusters
st.subheader("๐ฏ 3D Clusters")
# Generate cluster data
df = generate_3d_clusters()
# Create 3D scatter plot
fig = go.Figure()
# Add each cluster as a separate trace for better legend
for cluster_id in df['cluster'].unique():
cluster_data = df[df['cluster'] == cluster_id]
fig.add_trace(go.Scatter3d(
x=cluster_data['x'],
y=cluster_data['y'],
z=cluster_data['z'],
mode='markers',
marker=dict(
size=point_size,
opacity=opacity,
color=px.colors.qualitative.Set1[cluster_id % len(px.colors.qualitative.Set1)]
),
name=f'Cluster {cluster_id}',
text=[f"Cluster {cluster_id}<br>Point {i}" for i in range(len(cluster_data))],
hovertemplate="<b>%{text}</b><br>" +
"X: %{x:.3f}<br>" +
"Y: %{y:.3f}<br>" +
"Z: %{z:.3f}<extra></extra>"
))
# Update layout
fig.update_layout(
scene=dict(
xaxis=dict(
showgrid=show_grid,
visible=show_axes,
title="X Axis"
),
yaxis=dict(
showgrid=show_grid,
visible=show_axes,
title="Y Axis"
),
zaxis=dict(
showgrid=show_grid,
visible=show_axes,
title="Z Axis"
),
camera=dict(
eye=dict(x=1.5, y=1.5, z=1.5)
)
),
width=800,
height=600,
title=f"Interactive {viz_type}",
showlegend=viz_type == "3D Clusters"
)
# Auto-rotation animation
if auto_rotate:
frames = []
for i in range(36):
angle = i * 10
frames.append(go.Frame(
layout=dict(
scene_camera_eye=dict(
x=1.5 * np.cos(np.radians(angle)),
y=1.5 * np.sin(np.radians(angle)),
z=1.5
)
)
))
fig.frames = frames
fig.update_layout(
updatemenus=[dict(
type="buttons",
buttons=[dict(
label="Play",
method="animate",
args=[None, {"frame": {"duration": animation_speed}}]
)]
)]
)
st.plotly_chart(fig, use_container_width=True)
with col2:
st.subheader("๐ Data Info")
if viz_type == "Spiral Helix":
df = generate_spiral_helix()
st.metric("Total Points", len(df))
st.metric("Parameter Range", f"0 to {df['t'].max():.1f}")
st.subheader("Statistics")
st.write("**X Coordinate:**")
st.write(f"Range: [{df['x'].min():.2f}, {df['x'].max():.2f}]")
st.write("**Y Coordinate:**")
st.write(f"Range: [{df['y'].min():.2f}, {df['y'].max():.2f}]")
st.write("**Z Coordinate:**")
st.write(f"Range: [{df['z'].min():.2f}, {df['z'].max():.2f}]")
elif viz_type == "Surface Plot":
X, Y, Z = generate_surface_data()
st.metric("Grid Resolution", f"{X.shape[0]}ร{X.shape[1]}")
st.metric("Total Points", X.shape[0] * X.shape[1])
st.subheader("Value Ranges")
st.write(f"**X:** [{X.min():.1f}, {X.max():.1f}]")
st.write(f"**Y:** [{Y.min():.1f}, {Y.max():.1f}]")
st.write(f"**Z:** [{Z.min():.2f}, {Z.max():.2f}]")
st.subheader("Surface Function")
st.latex(r"z = \sin(f_x \cdot x) \cdot \cos(f_y \cdot y)")
else: # 3D Clusters
df = generate_3d_clusters()
st.metric("Total Points", len(df))
st.metric("Number of Clusters", df['cluster'].nunique())
st.subheader("Cluster Distribution")
cluster_counts = df['cluster'].value_counts().sort_index()
for cluster_id, count in cluster_counts.items():
st.write(f"**Cluster {cluster_id}:** {count} points")
st.subheader("Data Ranges")
st.write(f"**X:** [{df['x'].min():.1f}, {df['x'].max():.1f}]")
st.write(f"**Y:** [{df['y'].min():.1f}, {df['y'].max():.1f}]")
st.write(f"**Z:** [{df['z'].min():.1f}, {df['z'].max():.1f}]")
# Instructions
st.markdown("---")
st.subheader("๐ฎ Interaction Guide")
col1, col2, col3 = st.columns(3)
with col1:
st.markdown("""
**๐ฑ๏ธ Mouse Controls:**
- Left click + drag: Rotate
- Right click + drag: Pan
- Scroll wheel: Zoom
""")
with col2:
st.markdown("""
**๐๏ธ Customization:**
- Use sidebar controls for styling
- Adjust parameters in real-time
- Toggle features on/off
""")
with col3:
st.markdown("""
**๐ฌ Animation:**
- Enable auto-rotate for cinematic view
- Adjust animation speed
- Click play button when available
""")
# Additional tips
st.info("๐ก **Tip:** Try different combinations of parameters to explore various 3D patterns and structures!")
Hi! I can help you with any questions about Streamlit and Python. What would you like to know?