Loading your dashboard…
Loading your dashboard…
Pulling your latest taste profile and insight cards.
Everything on this page maps to analytics computed in src/lib/analytics.ts, via computeMoviePersonality(ratings).
This is intentionally focused on result math only: what each metric means, which inputs it uses, and the exact formulas behind the numbers you see.
Before we get into each card, these helpers appear throughout the analytics code:
average(values) = sum(values) / n (returns 0 when empty)variance(values) = average((x - mean)^2) (returns 0 when empty)clamp(x, min, max) keeps values inside a rangelinearRegressionSlope(points) estimates trend direction and strengthlinearRegression(points) returns { slope, intercept }toDecade(year) groups years like 1994 -> "1990s"These are the simplest counts, but they control confidence everywhere else.
ratingsCount = total imported ratingsmatchedCount = number of ratings where tmdbId existsunresolvedCount = ratingsCount - matchedCountIf matchedCount is low, metrics that depend on TMDB attributes (genres, vote averages, cast/directors, popularity) become less representative.
This is computed in buildCalibration.
For every movie with a TMDB average:
x = voteAverage / 2 (TMDB's 10-point scale mapped to your 5-point scale)y = yourRatingFrom those points, we compute:
strictnessOffset = average(y - x)slope and intercept from linear regression (y = slope * x + intercept)bucket = round(x * 2) / 2Interpretation:
strictnessOffset: you rate lower than baseline on averagestrictnessOffset: you rate higher than baseline on averageThis comes from buildProfile(ratings, strictnessOffset).
averageRating = average of your ratingsvariance = spread of your ratings around your own meanHigher variance means you use more of the rating scale (more "picky"/spread out).
Label thresholds are based on strictnessOffset:
harsh if < -0.25generous if > 0.25balancedPer movie:
voteCountNorm = voteCount / maxVoteCountpopularityNorm = popularity / maxPopularityratingWeight = yourRating / 5perMovie = (voteCountNorm * 0.7 + popularityNorm * 0.3) * ratingWeightOverall:
mainstreamScore = clamp(average(perMovie) * 100, 0, 100)nicheScore = 100 - mainstreamScoreSo "mainstream" is not just popularity; it is popularity weighted by how positively you rated those movies.
For each genre:
rawAvg = average(genreRatings)count = number of ratings in that genrecount >= max(5, ceil(0.02 * N))Ranking uses shrinkage (to avoid tiny-sample spikes):
shrinkageScore = (priorStrength * globalMean + count * rawAvg) / (priorStrength + count)priorStrength = 10 and globalMean = your overall averageDisplayed score remains rawAvg; shrinkage is used only for ranking fairness.
The same idea as favorite genres, but grouped by decade:
toDecade(year)count >= max(3, ceil(0.015 * N))This is computed in buildGenreAffinity.
For each genre, we build two averages:
userScore = your average rating in that genrebaselineScore = average of (voteAverage / 2) in that genreTwo outputs are produced:
matchPercent = clamp((1 - abs(userScore - baselineScore) / 5) * 100, 0, 100)
cosineSimilarityPercent = clamp(cosine(userVector, baselineVector) * 100, 0, 100)
Per-genre rows are sorted by matchPercent descending.
This is computed in buildTemporalTrends.
For each genre:
YYYY-MM)x = monthIndex, y = monthlyAverage)slopePerMonth via linear regression slopeDirection is bucketed as:
up if slope > 0.03down if slope < -0.03flat otherwiseOutput is limited to the top 8 genres by absolute slope magnitude.
This is computed in buildFranchiseTrends.
For each franchise (collectionName):
1, 2, 3, ...)slopeRules:
slope < 0 after sorting by slope ascendingThis comes from buildAffinityScores.
For each person:
rawAverage = average of your ratings where they appearcount = number of appearancesscore uses shrinkage to stabilize small samples:score = (priorStrength * globalMean + count * rawAverage) / (priorStrength + count) with priorStrength = 5
Confidence bands:
high if count >= 8medium if count >= 4low otherwiseAdditional rules:
count >= 2, then take top 10 by scoreThis is computed from parseWatchedCsv, computeMostViewedActorsFromWatched, and rankMostViewedActorsFromMovies.
For each watched movie:
normalizedTitle + "::" + yearFor each actor:
count = number of unique watched movies where they appear (top-3 cast only)examples = up to 3 watched movie titles for tooltip contextRules:
watched.csv, not ratingscount >= 2, then take top 10 by count descendingThis is computed in buildEraHeatmap.
Each cell is (decade, genre). For each cell:
averageRating = your raw mean rating in that cellcount = number of ratings in that cellscore uses shrinkage:score = (priorStrength * globalMean + count * rawAverage) / (priorStrength + count) with priorStrength = 3
Only cells with count >= 2 are kept, sorted by score descending.
This is computed in buildYearOnYear.
Review year is taken from rating.date.slice(0, 4). For each year:
count = number of ratings logged that yearaverageRating = average of ratings that yeartopGenres = top 5 genres by frequency in that yearThe narrative text compares the latest year against the previous year:
more, fewer, or same)similar, slightly higher, or slightly lower)