Switch samples to see another group’s favorites. Keep the sample fixed and switch scoring modes to see how feature scaling changes the rankings.
10 guests → 100 picks → 77 unique candidates → 25 final tracks
What stands out at this party?
Most shared guest picks: Whine Up (Nicky Jam) and Don't Start Now (Dua Lipa). Guest votes per track: 3. Switching scoring modes replaces 10 tracks in the final playlist.
Final playlist
Genre breakdown
- pop15 tracks
- rap5 tracks
- latin5 tracks
Final playlist
Genre breakdown
- pop18 tracks
- latin4 tracks
- rap3 tracks
What did each guest pick?
Guest 1 · 10 tracks
Guest 2 · 10 tracks
Guest 3 · 10 tracks
Guest 4 · 10 tracks
Guest 5 · 10 tracks
Guest 6 · 10 tracks
Guest 7 · 10 tracks
Guest 8 · 10 tracks
Guest 9 · 10 tracks
Guest 10 · 10 tracks
10 guests → 100 picks → 72 unique candidates → 25 final tracks
What stands out at this party?
Most shared guest picks: New Rules (Dua Lipa). Guest votes per track: 4. Switching scoring modes replaces 12 tracks in the final playlist.
Final playlist
Genre breakdown
- pop16 tracks
- latin4 tracks
- rap4 tracks
- r&b1 track
Final playlist
Genre breakdown
- pop17 tracks
- latin4 tracks
- rap2 tracks
- rock1 track
- r&b1 track
What did each guest pick?
Guest 1 · 10 tracks
Guest 2 · 10 tracks
Guest 3 · 10 tracks
Guest 4 · 10 tracks
Guest 5 · 10 tracks
Guest 6 · 10 tracks
Guest 7 · 10 tracks
Guest 8 · 10 tracks
Guest 9 · 10 tracks
Guest 10 · 10 tracks
10 guests → 100 picks → 73 unique candidates → 25 final tracks
What stands out at this party?
Most shared guest picks: QUE PRETENDES (J Balvin). Guest votes per track: 4. Switching scoring modes replaces 10 tracks in the final playlist.
Final playlist
Genre breakdown
- pop19 tracks
- latin4 tracks
- rap2 tracks
Final playlist
Genre breakdown
- pop20 tracks
- latin4 tracks
- rap1 track
What did each guest pick?
Guest 1 · 10 tracks
Guest 2 · 10 tracks
Guest 3 · 10 tracks
Guest 4 · 10 tracks
Guest 5 · 10 tracks
Guest 6 · 10 tracks
Guest 7 · 10 tracks
Guest 8 · 10 tracks
Guest 9 · 10 tracks
Guest 10 · 10 tracks
Explore the Python source · vibe.py
from pathlib import Path
import numpy as np
import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.preprocessing import StandardScaler
FEATURES = ["danceability", "energy", "speechiness", "instrumentalness", "acousticness", "valence"]
def load_songs():
columns = ["track_id", "track_name", "track_artist", "track_popularity",
"playlist_name", "playlist_genre", *FEATURES]
raw = pd.read_csv(Path(__file__).with_name("spotify_songs.csv"), usecols=columns)
party = raw.playlist_name.str.contains("party", case=False, na=False)
songs = raw.drop_duplicates("track_id").set_index("track_id")
songs["in_party_playlist"] = party.groupby(raw.track_id).any()
songs["party_score"] = songs.danceability + songs.energy - songs.acousticness
return songs
def build_mix(songs, guests=10, songs_per_guest=25, top_n=10, seed=42, standardized=False):
pool = songs.reset_index().sort_values(
["track_popularity", "track_id"], ascending=[False, True], kind="mergesort"
).head(300).set_index("track_id")
rng = np.random.default_rng(seed)
picks = {}
for i in range(guests):
sample = pool.sample(songs_per_guest, random_state=int(rng.integers(1_000_000)))
picks[f"Guest {i + 1}"] = sample.nlargest(top_n, "party_score")
candidates = pd.concat(picks.values())
candidates = candidates.loc[~candidates.index.duplicated()].copy()
candidates = score_candidates(candidates, standardized)
return picks, candidates, candidates.nlargest(25, "mean_sim")
def score_candidates(candidates, standardized):
candidates = candidates.copy()
features = candidates[FEATURES]
if standardized:
features = StandardScaler().fit_transform(features)
similarity = cosine_similarity(features)
np.fill_diagonal(similarity, np.nan)
candidates["mean_sim"] = np.nanmean(similarity, axis=1) if len(candidates) > 1 else np.nan
return candidates