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 plotly.graph_objects as go
import plotly.express as px
import numpy as np
import pandas as pd
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
st.set_page_config(page_title="3D Data Visualizer", layout="wide")
# Generate mock 3D data
@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(resolution=50, freq_x=1, freq_y=1, amplitude=1):
"""Generate surface plot data: z = amplitude * sin(freq_x*x) * cos(freq_y*y)"""
x = np.linspace(-2*np.pi, 2*np.pi, resolution)
y = np.linspace(-2*np.pi, 2*np.pi, 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"""
np.random.seed(42)
# Cluster centers
centers = np.array([
[2, 2, 2],
[-2, -2, 2],
[0, 2, -2]
])
data = []
labels = []
for i, center in enumerate(centers):
# Generate points around each center
cluster_points = np.random.multivariate_normal(
center,
np.eye(3) * 0.5,
points_per_cluster
)
data.extend(cluster_points)
labels.extend([f'Cluster {i+1}'] * points_per_cluster)
df = pd.DataFrame(data, columns=['x', 'y', 'z'])
df['cluster'] = labels
return df
# Main app
st.title("🌟 3D Data Visualizer")
st.markdown("Interactive 3D visualizations with customizable controls")
# Sidebar controls
st.sidebar.header("Visualization Settings")
# Visualization selector
viz_type = st.sidebar.selectbox(
"Select Visualization",
["Spiral Helix", "Surface Plot", "3D Clusters"]
)
st.sidebar.markdown("---")
# Common styling controls
st.sidebar.subheader("Point Styling")
point_size = st.sidebar.slider("Point Size", 2, 20, 8)
opacity = st.sidebar.slider("Opacity", 0.1, 1.0, 0.8)
# Color scheme
color_scheme = st.sidebar.selectbox(
"Color Scheme",
["viridis", "plasma", "inferno", "magma", "rainbow", "turbo"]
)
st.sidebar.markdown("---")
# Animation controls
st.sidebar.subheader("Animation")
animation_speed = st.sidebar.slider("Animation Speed (ms)", 50, 1000, 500)
auto_rotate = st.sidebar.checkbox("Auto Rotate", False)
# Create main content area
col1, col2 = st.columns([3, 1])
if viz_type == "Spiral Helix":
with st.sidebar:
st.subheader("Spiral Parameters")
n_points = st.slider("Number of Points", 50, 200, 100)
noise_level = st.slider("Noise Level", 0.0, 0.5, 0.1)
show_line = st.checkbox("Show Connecting Line", True)
# Generate data
spiral_data = generate_spiral_helix(n_points, noise_level)
with col1:
fig = go.Figure()
# Add scatter plot
scatter = go.Scatter3d(
x=spiral_data['x'],
y=spiral_data['y'],
z=spiral_data['z'],
mode='markers+lines' if show_line else 'markers',
marker=dict(
size=point_size,
color=spiral_data['t'],
colorscale=color_scheme,
opacity=opacity,
colorbar=dict(title="Time Parameter")
),
line=dict(
color='rgba(255,255,255,0.5)',
width=2
) if show_line else None,
name="Spiral Helix"
)
fig.add_trace(scatter)
# Update layout
fig.update_layout(
title="Spiral Helix with Noise",
scene=dict(
xaxis_title="X",
yaxis_title="Y",
zaxis_title="Z",
bgcolor="rgba(0,0,0,0)",
camera=dict(
eye=dict(x=1.5, y=1.5, z=1.5)
)
),
height=600,
margin=dict(l=0, r=0, t=30, b=0)
)
if auto_rotate:
fig.update_layout(
updatemenus=[{
"type": "buttons",
"buttons": [{
"label": "Rotate",
"method": "animate",
"args": [None, {"frame": {"duration": animation_speed}, "fromcurrent": True}]
}]
}]
)
st.plotly_chart(fig, use_container_width=True)
with col2:
st.subheader("Data Statistics")
st.write(f"**Points:** {len(spiral_data)}")
st.write(f"**X Range:** {spiral_data['x'].min():.2f} to {spiral_data['x'].max():.2f}")
st.write(f"**Y Range:** {spiral_data['y'].min():.2f} to {spiral_data['y'].max():.2f}")
st.write(f"**Z Range:** {spiral_data['z'].min():.2f} to {spiral_data['z'].max():.2f}")
elif viz_type == "Surface Plot":
with st.sidebar:
st.subheader("Surface Parameters")
resolution = st.slider("Resolution", 20, 100, 50)
freq_x = st.slider("X Frequency", 0.5, 3.0, 1.0)
freq_y = st.slider("Y Frequency", 0.5, 3.0, 1.0)
amplitude = st.slider("Amplitude", 0.5, 2.0, 1.0)
show_contours = st.checkbox("Show Contours", True)
surface_opacity = st.slider("Surface Opacity", 0.3, 1.0, 0.8)
# Generate surface data
X, Y, Z = generate_surface_data(resolution, freq_x, freq_y, amplitude)
with col1:
fig = go.Figure()
# Add surface plot
surface = go.Surface(
x=X, y=Y, z=Z,
colorscale=color_scheme,
opacity=surface_opacity,
showscale=True,
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.add_trace(surface)
# Update layout
fig.update_layout(
title=f"Surface: z = {amplitude}×sin({freq_x}x)×cos({freq_y}y)",
scene=dict(
xaxis_title="X",
yaxis_title="Y",
zaxis_title="Z",
bgcolor="rgba(0,0,0,0)",
camera=dict(
eye=dict(x=1.5, y=1.5, z=1.5)
)
),
height=600,
margin=dict(l=0, r=0, t=30, b=0)
)
st.plotly_chart(fig, use_container_width=True)
with col2:
st.subheader("Surface Info")
st.write(f"**Resolution:** {resolution}×{resolution}")
st.write(f"**Function:** z = {amplitude}×sin({freq_x}x)×cos({freq_y}y)")
st.write(f"**Z Range:** {Z.min():.2f} to {Z.max():.2f}")
st.write(f"**Domain:** [-2Ï€, 2Ï€]")
elif viz_type == "3D Clusters":
with st.sidebar:
st.subheader("Cluster Parameters")
n_clusters = st.slider("Number of Clusters", 2, 5, 3)
points_per_cluster = st.slider("Points per Cluster", 20, 100, 50)
show_centers = st.checkbox("Show Cluster Centers", True)
show_hull = st.checkbox("Show Convex Hull", False)
# Generate cluster data
cluster_data = generate_3d_clusters(n_clusters, points_per_cluster)
with col1:
fig = go.Figure()
# Color palette for clusters
colors = px.colors.qualitative.Set1[:n_clusters]
# Add scatter plot for each cluster
for i, cluster_name in enumerate(cluster_data['cluster'].unique()):
cluster_subset = cluster_data[cluster_data['cluster'] == cluster_name]
scatter = go.Scatter3d(
x=cluster_subset['x'],
y=cluster_subset['y'],
z=cluster_subset['z'],
mode='markers',
marker=dict(
size=point_size,
color=colors[i % len(colors)],
opacity=opacity,
line=dict(width=1, color='white')
),
name=cluster_name
)
fig.add_trace(scatter)
# Add cluster centers if requested
if show_centers:
center_x = cluster_subset['x'].mean()
center_y = cluster_subset['y'].mean()
center_z = cluster_subset['z'].mean()
center = go.Scatter3d(
x=[center_x],
y=[center_y],
z=[center_z],
mode='markers',
marker=dict(
size=point_size*2,
color='black',
symbol='diamond',
line=dict(width=2, color='white')
),
name=f"{cluster_name} Center",
showlegend=False
)
fig.add_trace(center)
# Update layout
fig.update_layout(
title="3D Clusters",
scene=dict(
xaxis_title="X",
yaxis_title="Y",
zaxis_title="Z",
bgcolor="rgba(0,0,0,0)",
camera=dict(
eye=dict(x=1.5, y=1.5, z=1.5)
)
),
height=600,
margin=dict(l=0, r=0, t=30, b=0),
legend=dict(
yanchor="top",
y=0.99,
xanchor="left",
x=0.01
)
)
st.plotly_chart(fig, use_container_width=True)
with col2:
st.subheader("Cluster Statistics")
st.write(f"**Total Points:** {len(cluster_data)}")
for cluster_name in cluster_data['cluster'].unique():
cluster_subset = cluster_data[cluster_data['cluster'] == cluster_name]
st.write(f"**{cluster_name}:** {len(cluster_subset)} points")
# Show cluster centers
if show_centers:
st.subheader("Cluster Centers")
for cluster_name in cluster_data['cluster'].unique():
cluster_subset = cluster_data[cluster_data['cluster'] == cluster_name]
center = [
cluster_subset['x'].mean(),
cluster_subset['y'].mean(),
cluster_subset['z'].mean()
]
st.write(f"**{cluster_name}:** ({center[0]:.2f}, {center[1]:.2f}, {center[2]:.2f})")
# Footer
st.markdown("---")
st.markdown("**💡 Tips:**")
st.markdown("- Use mouse to rotate, zoom, and pan the 3D plots")
st.markdown("- Hover over points to see coordinates")
st.markdown("- Use the camera controls in the plot toolbar")
st.markdown("- Adjust settings in the sidebar to customize visualizations")
Hi! I can help you with any questions about Streamlit and Python. What would you like to know?