Spaces:
Sleeping
Sleeping
Upload app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# start by importing the necessary packages
|
| 2 |
+
#standard
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pandas as pd
|
| 5 |
+
|
| 6 |
+
#plt packages
|
| 7 |
+
import seaborn as sns
|
| 8 |
+
import altair as alt
|
| 9 |
+
import matplotlib.pyplot as plt
|
| 10 |
+
#streamlit
|
| 11 |
+
import streamlit as st
|
| 12 |
+
|
| 13 |
+
#sklearn
|
| 14 |
+
from sklearn.decomposition import PCA
|
| 15 |
+
from sklearn.preprocessing import StandardScaler
|
| 16 |
+
from sklearn.cluster import KMeans
|
| 17 |
+
from sklearn.metrics import silhouette_score
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
st.set_page_config(page_title="StressedOUT – Cached/DB", page_icon=":skull:", layout="wide")
|
| 22 |
+
st.title("StressedOOUT – Looking into a dataset of stressed students (Cached)")
|
| 23 |
+
st.caption("Reads .csv files. Uses Streamlit caching and a form submit gate.")
|
| 24 |
+
|
| 25 |
+
BASE_DIR = "StressLevelDataset.csv" #
|
| 26 |
+
|
| 27 |
+
@st.cache_data
|
| 28 |
+
def load_data(path):
|
| 29 |
+
data = pd.read_csv(path)
|
| 30 |
+
return data
|
| 31 |
+
|
| 32 |
+
data = load_data(BASE_DIR).drop(columns=['future_career_concerns', 'anxiety_level', 'depression', 'bullying','peer_pressure'])
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
with st.sidebar:
|
| 36 |
+
st.header("Filters")
|
| 37 |
+
with st.form("filters"):
|
| 38 |
+
analysis = st.radio(
|
| 39 |
+
"Select your dataset",
|
| 40 |
+
('PCA reduced', 'No dimensionality reduction'),
|
| 41 |
+
captions=('PCA reduced', 'No dimensionality reduction')
|
| 42 |
+
)
|
| 43 |
+
k = st.slider("Select number of clusters (k)", 2, 10, 4, step=1)
|
| 44 |
+
iterations = st.slider("Select number of iterations to show", 1, 10, 5, step=1)
|
| 45 |
+
seed = st.number_input("Random seed", min_value=0, max_value=100, value=42, step=1)
|
| 46 |
+
st.write("For no dimensionality reduction, the first two features will be used for visualization.")
|
| 47 |
+
feature_x = st.selectbox("Select X-axis feature", data.columns, index=0)
|
| 48 |
+
feature_y = st.selectbox("Select Y-axis feature", data.columns, index=1)
|
| 49 |
+
|
| 50 |
+
submitted = st.form_submit_button("Apply")
|
| 51 |
+
if not submitted:
|
| 52 |
+
st.info("Adjust filters and click **Apply**.")
|
| 53 |
+
st.stop()
|
| 54 |
+
|
| 55 |
+
def kmeans_iteration_demo(X, k, max_iters=iterations):
|
| 56 |
+
# Initialize centers randomly
|
| 57 |
+
np.random.seed(seed)
|
| 58 |
+
centers = X[np.random.choice(len(X), k, replace=False)]
|
| 59 |
+
|
| 60 |
+
fig, axes = plt.subplots(1, max_iters + 1, figsize=(20, 4))
|
| 61 |
+
|
| 62 |
+
for iteration in range(max_iters + 1):
|
| 63 |
+
if iteration == 0:
|
| 64 |
+
# Show initial random centers
|
| 65 |
+
axes[iteration].scatter(X[:, 0], X[:, 1], c='lightgray', alpha=0.6, s=30)
|
| 66 |
+
axes[iteration].scatter(centers[:, 0], centers[:, 1], c='red', s=200, marker='X',
|
| 67 |
+
edgecolors='black', linewidths=2)
|
| 68 |
+
axes[iteration].set_title(f'Iteration {iteration}\n(Random Initialization)')
|
| 69 |
+
|
| 70 |
+
else:
|
| 71 |
+
# Assign points to nearest center
|
| 72 |
+
distances = np.sqrt(((X - centers[:, np.newaxis])**2).sum(axis=2))
|
| 73 |
+
labels = np.argmin(distances, axis=0)
|
| 74 |
+
|
| 75 |
+
# Plot current clustering
|
| 76 |
+
colors = ['blue', 'green', 'red', 'purple', 'orange']
|
| 77 |
+
for j in range(k):
|
| 78 |
+
mask = labels == j
|
| 79 |
+
axes[iteration].scatter(X[mask, 0], X[mask, 1],
|
| 80 |
+
c=colors[j], alpha=0.6, s=30, label=f'Cluster {j+1}')
|
| 81 |
+
|
| 82 |
+
axes[iteration].scatter(centers[:, 0], centers[:, 1], c='black', s=200, marker='X',
|
| 83 |
+
edgecolors='white', linewidths=2)
|
| 84 |
+
axes[iteration].set_title(f'Iteration {iteration}')
|
| 85 |
+
|
| 86 |
+
# Update centers
|
| 87 |
+
new_centers = np.array([X[labels == j].mean(axis=0) for j in range(k)])
|
| 88 |
+
|
| 89 |
+
# Show center movement with arrows
|
| 90 |
+
if iteration > 1:
|
| 91 |
+
for j in range(k):
|
| 92 |
+
axes[iteration].annotate('', xy=new_centers[j], xytext=centers[j],
|
| 93 |
+
arrowprops=dict(arrowstyle='->', lw=2, color='red', alpha=0.7))
|
| 94 |
+
|
| 95 |
+
centers = new_centers
|
| 96 |
+
|
| 97 |
+
axes[iteration].set_xlabel('PC1')
|
| 98 |
+
axes[iteration].set_ylabel('PC2')
|
| 99 |
+
axes[iteration].grid(True, alpha=0.3)
|
| 100 |
+
|
| 101 |
+
plt.tight_layout()
|
| 102 |
+
st.pyplot(fig)
|
| 103 |
+
|
| 104 |
+
if analysis == 'PCA reduced':
|
| 105 |
+
data_scaled = StandardScaler().fit_transform(data)
|
| 106 |
+
data_reduced_df = pd.DataFrame(data_scaled, columns=data.columns)
|
| 107 |
+
st.write('You selected PCA reduced')
|
| 108 |
+
pca = PCA()
|
| 109 |
+
pca_data = pca.fit_transform(data_reduced_df)
|
| 110 |
+
pca_data_pd = pd.DataFrame(pca_data, columns=[f'PC{i+1}' for i in range(pca_data.shape[1])])
|
| 111 |
+
st.write('The PCA reduced data is shown below')
|
| 112 |
+
st.dataframe(pca_data_pd.head(10))
|
| 113 |
+
|
| 114 |
+
explained_variance = pca.explained_variance_ratio_
|
| 115 |
+
cumulative_variance = np.cumsum(explained_variance)
|
| 116 |
+
|
| 117 |
+
st.write("Explained Variance by Component:")
|
| 118 |
+
for i in range(min(10, len(explained_variance))):
|
| 119 |
+
st.write(f"PC{i+1}: {explained_variance[i]:.3f} ({explained_variance[i]*100:.1f}%)")
|
| 120 |
+
|
| 121 |
+
st.write(f"\nFirst 3 components explain {cumulative_variance[2]*100:.1f}% of total variance")
|
| 122 |
+
st.write(f"First 5 components explain {cumulative_variance[4]*100:.1f}% of total variance")
|
| 123 |
+
|
| 124 |
+
#visualizations
|
| 125 |
+
fig,(ax1,ax2)=plt.subplots(1,2,figsize=(12,5))
|
| 126 |
+
|
| 127 |
+
#scree plot
|
| 128 |
+
ax1.plot(range(1,len(explained_variance)+1),explained_variance,marker='o',linestyle='--')
|
| 129 |
+
ax1.set_title('Scree Plot')
|
| 130 |
+
ax1.set_xlabel('Principal Component')
|
| 131 |
+
ax1.set_ylabel('Variance Explained')
|
| 132 |
+
ax1.axvline(x=3,color='r',linestyle='--',label='3 components')
|
| 133 |
+
ax1.axvline(x=5,color='g',linestyle='--',label='5 components')
|
| 134 |
+
ax1.legend()
|
| 135 |
+
ax1.grid()
|
| 136 |
+
#cumulative variance plot
|
| 137 |
+
ax2.plot(range(1,len(cumulative_variance)+1),cumulative_variance,marker='o',linestyle='--',color='orange')
|
| 138 |
+
ax2.set_title('Cumulative Variance Explained')
|
| 139 |
+
ax2.set_xlabel('Number of Principal Components')
|
| 140 |
+
ax2.set_ylabel('Cumulative Variance Explained')
|
| 141 |
+
ax2.axhline(y=0.9,color='r',linestyle='--',label='90% variance')
|
| 142 |
+
ax2.axhline(y=0.95,color='g',linestyle='--',label='95% variance')
|
| 143 |
+
ax2.legend()
|
| 144 |
+
ax2.grid()
|
| 145 |
+
st.pyplot(fig)
|
| 146 |
+
|
| 147 |
+
components_df = pd.DataFrame(
|
| 148 |
+
pca.components_[:5].T, # First 5 components
|
| 149 |
+
columns=[f'PC{i+1}' for i in range(5)],
|
| 150 |
+
index=data_reduced_df.columns
|
| 151 |
+
)
|
| 152 |
+
st.write("PCA Component Loadings (first 5 components):")
|
| 153 |
+
st.dataframe(components_df)
|
| 154 |
+
|
| 155 |
+
# Visualize component loadings for interpretation
|
| 156 |
+
fig, axes = plt.subplots(3, 2, figsize=(16, 12))
|
| 157 |
+
|
| 158 |
+
# PC1 loadings
|
| 159 |
+
pc1_loadings = components_df['PC1'].sort_values(key=abs, ascending=False)
|
| 160 |
+
axes[0,0].barh(range(len(pc1_loadings)), pc1_loadings.values)
|
| 161 |
+
axes[0,0].set_yticks(range(len(pc1_loadings)))
|
| 162 |
+
axes[0,0].set_yticklabels(pc1_loadings.index, fontsize=9)
|
| 163 |
+
axes[0,0].set_title(f'PC1 Loadings (Explains {explained_variance[0]*100:.1f}% of variance)')
|
| 164 |
+
axes[0,0].axvline(x=0, color='black', linestyle='-', alpha=0.3)
|
| 165 |
+
|
| 166 |
+
# PC2 loadings
|
| 167 |
+
pc2_loadings = components_df['PC2'].sort_values(key=abs, ascending=False)
|
| 168 |
+
axes[0,1].barh(range(len(pc2_loadings)), pc2_loadings.values, color='orange')
|
| 169 |
+
axes[0,1].set_yticks(range(len(pc2_loadings)))
|
| 170 |
+
axes[0,1].set_yticklabels(pc2_loadings.index, fontsize=9)
|
| 171 |
+
axes[0,1].set_title(f'PC2 Loadings (Explains {explained_variance[1]*100:.1f}% of variance)')
|
| 172 |
+
axes[0,1].axvline(x=0, color='black', linestyle='-', alpha=0.3)
|
| 173 |
+
|
| 174 |
+
# PC3 loadings
|
| 175 |
+
pc3_loadings = components_df['PC3'].sort_values(key=abs, ascending=False)
|
| 176 |
+
axes[1,0].barh(range(len(pc3_loadings)), pc3_loadings.values, color='green')
|
| 177 |
+
axes[1,0].set_yticks(range(len(pc3_loadings)))
|
| 178 |
+
axes[1,0].set_yticklabels(pc3_loadings.index, fontsize=9)
|
| 179 |
+
axes[1,0].set_title(f'PC3 Loadings (Explains {explained_variance[2]*100:.1f}% of variance)')
|
| 180 |
+
axes[1,0].axvline(x=0, color='black', linestyle='-', alpha=0.3)
|
| 181 |
+
|
| 182 |
+
# PC1 vs PC2 scatter plot of cities
|
| 183 |
+
axes[1,1].scatter(pca_data[:, 0], pca_data[:, 1], alpha=0.6)
|
| 184 |
+
axes[1,1].set_xlabel('PC1')
|
| 185 |
+
axes[1,1].set_ylabel('PC2')
|
| 186 |
+
axes[1,1].set_title('Students in PC1-PC2 Space')
|
| 187 |
+
axes[1,1].grid(True, alpha=0.3)
|
| 188 |
+
|
| 189 |
+
# PC1 vs PC3 scatter plot of cities
|
| 190 |
+
axes[2,0].scatter(pca_data[:, 0], pca_data[:, 2], alpha=0.6)
|
| 191 |
+
axes[2,0].set_xlabel('PC1')
|
| 192 |
+
axes[2,0].set_ylabel('PC3')
|
| 193 |
+
axes[2,0].set_title('Students in PC1-PC3 Space')
|
| 194 |
+
axes[2,0].grid(True, alpha=0.3)
|
| 195 |
+
|
| 196 |
+
# PC2 vs PC3 scatter plot of cities
|
| 197 |
+
axes[2,1].scatter(pca_data[:, 1], pca_data[:, 2], alpha=0.6)
|
| 198 |
+
axes[2,1].set_xlabel('PC2')
|
| 199 |
+
axes[2,1].set_ylabel('PC3')
|
| 200 |
+
axes[2,1].set_title('Students in PC2-PC3 Space')
|
| 201 |
+
axes[2,1].grid(True, alpha=0.3)
|
| 202 |
+
plt.tight_layout()
|
| 203 |
+
st.pyplot(fig)
|
| 204 |
+
|
| 205 |
+
# KMeans clustering on PCA reduced data
|
| 206 |
+
kmeans = KMeans(n_clusters=k, random_state=42)
|
| 207 |
+
cluster_labels = kmeans.fit_predict(pca_data[:,:5]) # Using first 5 PCs
|
| 208 |
+
silhouette_avg = silhouette_score(pca_data[:,:5], cluster_labels)
|
| 209 |
+
st.write(f"Silhouette Score for k={k}: {silhouette_avg:.3f}")
|
| 210 |
+
pca_data_pd['Cluster'] = cluster_labels
|
| 211 |
+
pca = PCA(n_components=2, random_state=42)
|
| 212 |
+
pca_2d = pca.fit_transform(pca_data_pd.drop(columns=['Cluster']))
|
| 213 |
+
pca_2d_df = pd.DataFrame(pca_2d, columns=['PC1', 'PC2'])
|
| 214 |
+
pca_2d_df['Cluster'] = cluster_labels
|
| 215 |
+
st.write("2D PCA plot with KMeans clusters:")
|
| 216 |
+
|
| 217 |
+
kmeans_iteration_demo(pca_2d_df[['PC1', 'PC2']].values, k)
|
| 218 |
+
|
| 219 |
+
# ...existing code...
|
| 220 |
+
# ...existing code...
|
| 221 |
+
else:
|
| 222 |
+
st.write('You selected No dimensionality reduction')
|
| 223 |
+
st.write('The original data is shown below')
|
| 224 |
+
st.dataframe(data.head(10))
|
| 225 |
+
|
| 226 |
+
# Standardize the data
|
| 227 |
+
data_scaled = StandardScaler().fit_transform(data)
|
| 228 |
+
data_scaled_df = pd.DataFrame(data_scaled, columns=data.columns)
|
| 229 |
+
st.dataframe(data_scaled_df.head(10))
|
| 230 |
+
|
| 231 |
+
# KMeans clustering on original scaled data
|
| 232 |
+
kmeans = KMeans(n_clusters=k, random_state=seed)
|
| 233 |
+
cluster_labels = kmeans.fit_predict(data_scaled_df)
|
| 234 |
+
silhouette_avg = silhouette_score(data_scaled_df, cluster_labels)
|
| 235 |
+
st.write(f"Silhouette Score for k={k}: {silhouette_avg:.3f}")
|
| 236 |
+
|
| 237 |
+
# Add cluster labels for plotting
|
| 238 |
+
data_scaled_df['Cluster'] = cluster_labels
|
| 239 |
+
|
| 240 |
+
# 2D scatter plot using two original features for visualization
|
| 241 |
+
fig, ax = plt.subplots(figsize=(8, 6))
|
| 242 |
+
scatter = ax.scatter(
|
| 243 |
+
data_scaled_df[feature_x], data_scaled_df[feature_y],
|
| 244 |
+
c=cluster_labels, cmap='tab10', alpha=0.7, s=50
|
| 245 |
+
)
|
| 246 |
+
ax.set_xlabel(feature_x)
|
| 247 |
+
ax.set_ylabel(feature_y)
|
| 248 |
+
ax.set_title('KMeans Clusters (Original Scaled Features)')
|
| 249 |
+
plt.colorbar(scatter, ax=ax, label='Cluster')
|
| 250 |
+
st.pyplot(fig)
|