| import streamlit as st |
| import pandas as pd |
| import joblib |
| import matplotlib.pyplot as plt |
| import os |
|
|
| |
| |
| |
| st.set_page_config(page_title="Spotify Clustering", layout="wide") |
|
|
| |
| |
| |
| def get_path(filename): |
| base_dir = os.path.dirname(os.path.abspath(__file__)) |
|
|
| local_path = os.path.join(base_dir, filename) |
| parent_path = os.path.join(base_dir, "..", filename) |
|
|
| if os.path.exists(local_path): |
| return local_path |
| elif os.path.exists(parent_path): |
| return parent_path |
| else: |
| st.error(f"{filename} bulunamadı!") |
| st.stop() |
|
|
| DATA_PATH = get_path("spotify_clustered.csv") |
| MODEL_PATH = get_path("kmeans_music_model.pkl") |
| SCALER_PATH = get_path("scaler_music.pkl") |
|
|
| |
| |
| |
| @st.cache_data |
| def load_data(): |
| df = pd.read_csv(DATA_PATH) |
|
|
| unnecessary_cols = ['Unnamed: 0', 'track_id', 'album_name', 'explicit'] |
| df_display = df.drop(columns=[c for c in unnecessary_cols if c in df.columns]) |
|
|
| return df, df_display |
|
|
| @st.cache_resource |
| def load_model(): |
| model = joblib.load(MODEL_PATH) |
| scaler = joblib.load(SCALER_PATH) |
| return model, scaler |
|
|
| df, df_display = load_data() |
| model, scaler = load_model() |
|
|
| |
| |
| |
| if "prediction" not in st.session_state: |
| st.session_state.prediction = None |
| st.session_state.samples = None |
|
|
| |
| |
| |
| st.title("🎵 Spotify Music Clustering / Spotify Müzik Kümeleme") |
| st.markdown("---") |
|
|
| |
| |
| |
| st.subheader("📄 Dataset Preview / Veri Önizleme") |
| st.dataframe(df_display.head(10), use_container_width=True) |
|
|
| |
| |
| |
| col1, col2 = st.columns(2) |
|
|
| with col1: |
| st.subheader("📊 Cluster Distribution / Küme Dağılımı") |
| st.bar_chart(df['cluster'].value_counts(), use_container_width=True) |
|
|
| with col2: |
| st.subheader("🎯 Feature Analysis / Özellik Analizi") |
|
|
| fig, ax = plt.subplots(figsize=(6, 4)) |
| scatter = ax.scatter( |
| df['danceability'], |
| df['energy'], |
| c=df['cluster'], |
| cmap='viridis', |
| alpha=0.6 |
| ) |
|
|
| ax.set_xlabel("Danceability / Dans Edilebilirlik") |
| ax.set_ylabel("Energy / Enerji") |
|
|
| plt.colorbar(scatter, ax=ax) |
|
|
| st.pyplot(fig, clear_figure=True) |
| plt.close(fig) |
|
|
| |
| |
| |
| st.divider() |
| st.subheader("🤖 Predict New Song Cluster / Yeni Şarkı Tahmini") |
|
|
| c1, c2, c3 = st.columns(3) |
|
|
| with c1: |
| pop = st.slider("Popularity / Popülerlik", 0, 100, 50) |
| dur = st.slider("Duration (ms) / Süre", 0, 600000, 200000) |
| dance = st.slider("Danceability / Dans Edilebilirlik", 0.0, 1.0, 0.5) |
|
|
| with c2: |
| energy = st.slider("Energy / Enerji", 0.0, 1.0, 0.5) |
| loud = st.slider("Loudness / Ses Yüksekliği", -60.0, 0.0, -10.0) |
| tempo = st.slider("Tempo / Tempo", 0.0, 250.0, 120.0) |
|
|
| with c3: |
| speech = st.slider("Speechiness / Konuşma Oranı", 0.0, 1.0, 0.1) |
|
|
| if st.button("Predict Cluster / Kümeyi Tahmin Et ✨"): |
| new_data = [[pop, dur, dance, energy, loud, tempo, speech]] |
|
|
| try: |
| new_data_scaled = scaler.transform(new_data) |
| res = model.predict(new_data_scaled)[0] |
|
|
| st.session_state.prediction = res |
| st.session_state.samples = df[df['cluster'] == res][['track_name', 'artists']].head(5) |
|
|
| except Exception as e: |
| st.error(f"Error / Hata: {e}") |
|
|
| |
| if st.session_state.prediction is not None: |
| st.success(f"### Predicted Cluster / Tahmin Edilen Küme: {st.session_state.prediction}") |
| st.write("Similar Songs / Benzer Şarkılar:") |
| st.table(st.session_state.samples) |
|
|
| |
| |
| |
| st.divider() |
| st.subheader("🔍 Cluster Characteristics / Küme Özellikleri") |
|
|
| numeric_only = df.select_dtypes(include=['float64', 'int64']) |
|
|
| if 'cluster' in df.columns: |
| means = numeric_only.groupby(df['cluster']).mean() |
| st.dataframe(means, use_container_width=True) |