Behind the Numbers

Samples of code logic and derived output of the training styles that produced the final result.
This page visualizes the logic behind the scores and the model’s coefficients.

Main logic

Lexicon-based classification + heuristics, then aggregation. Word counts drive exact event totals (e.g. 8,297 affirmations, 3,450 repair attempts, 5,033 joy expressions). Rates per 1k messages feed composite connection/strain scores. Weekly rollups power the charts.

LEXICONS = {
    "affirmation": [
        "love", "heart", "hugs", "cute", "beautiful", "adore", "miss you", "proud",
        "love you", "i love you", "loving", "beloved", "sweetheart", "darling", "babe",
        "appreciate", "appreciate you", "grateful", "thankful", "thank you", "thanks",
        "amazing", "wonderful", "incredible", "fantastic", "brilliant", "remarkable",
        "support you", "believe in you", "you matter", "you're important", "you mean so much",
        "care about you", "care for you", "here for you", "got your back", "with you",
        "encourage", "encouragement", "you've got this", "you can do it", "proud of you",
        "compliment", "beautiful", "handsome", "gorgeous", "stunning", "attractive",
        "kind", "caring", "thoughtful", "considerate", "generous", "supportive",
        "pet name", "nickname", "endearment", "sweetie", "honey", "dear",
        "heart emoji", "xoxo", "kisses", "hug", "holding you", "thinking of you",
        "miss you", "miss you so much", "can't wait to see you", "looking forward to you",
        "love spending time", "love being with", "love talking to", "love our",
        "so lucky", "lucky to have you", "blessed", "grateful for you",
        "words of affirmation", "verbal love", "speaking your language",
    ],
    "repair": [
        "sorry", "apologize", "apology", "my bad", "i was wrong", "forgive me",
        "forgive", "forgiveness", "let's talk", "i understand", "i hear you",
        "make it right", "fix this", "work through this", "figure this out together",
        "mend", "reconcile", "reconciliation", "start fresh", "clean slate",
        "move forward", "move past this", "put this behind us", "let's move on",
        "i take responsibility", "that was on me", "my fault", "i messed up",
        "didn't mean to", "didn't intend to", "wasn't my intention", "i regret",
        "wish i could take it back", "shouldn't have said", "shouldn't have done",
        "let's work on this", "let's fix this", "we can get through this",
        "talk about it", "open up", "hear me out", "can we talk", "need to talk",
        "make amends", "make up", "kiss and make up", "bury the hatchet",
        "i'm here", "i'm listening", "tell me more", "help me understand",
        "what can i do", "how can i help", "how do we fix this", "what do you need",
        "rebuild", "rebuilding", "healing", "heal this", "repair", "repairing",
        "give us another chance", "try again", "do better", "be better",
        "acknowledge", "acknowledging", "own it", "owning it", "take accountability",
    ],
    "joy": [
        "happy", "excited", "amazing", "love it", "yay", "laugh", "so good",
        "wonderful", "fantastic", "thrilled", "delighted", "joyful", "ecstatic",
        "over the moon", "on cloud nine", "couldn't be happier", "so happy",
        "laughing", "lol", "haha", "hilarious", "funny", "cracked up", "dying",
        "celebrate", "celebration", "celebrating", "congrats", "congratulations",
        "best day", "great day", "amazing day", "perfect day", "lovely day",
        "best ever", "best time", "greatest", "favorite", "love this", "obsessed",
        "can't stop smiling", "grinning", "beaming", "glowing", "radiant",
        "blessed", "lucky", "fortunate", "grateful", "thankful", "appreciate",
        "fun", "had fun", "so fun", "so much fun", "good time", "great time",
        "love you", "miss you", "thinking of you", "made my day", "you made my day",
        "dream come true", "beyond happy", "overflowing", "bursting with joy",
        "relief", "relieved", "weight off", "stress free", "at peace",
        "content", "contentment", "fulfilled", "satisfied", "at ease",
        "cheerful", "upbeat", "positive", "optimistic", "hopeful", "looking up",
        "good news", "great news", "best news", "so excited", "can't wait",
    ],
    "conflict": [
        "anxious", "worried", "stressed", "panic", "panic attack", "breakdown",
        "depressed", "down", "low", "blue", "melancholy", "empty", "numb",
        "lonely", "isolated", "abandoned", "rejected", "unloved", "unwanted",
        "misunderstood", "unheard", "dismissed", "invalidated", "ignored",
        "confused", "lost", "bewildered", "disoriented", "can't think straight",
        "guilty", "ashamed", "embarrassed", "humiliated", "worthless",
        "jealous", "envious", "insecure", "inadequate", "comparison",
        "argument", "fight", "battle", "clash", "tension", "hostility",
        "screaming", "yelling", "shouting", "attack", "blame", "accusation",
        "defensive", "guarded", "shut down", "stonewall", "cold shoulder",
        "grudge", "resentment", "bitterness", "unforgiving", "holding grudges",
        "toxic", "dysfunctional", "unhealthy", "damaging", "destructive",
        "crying", "tears", "sobbing", "breakdown", "falling apart", "can't cope",
        "overwhelmed", "drowning", "too much", "breaking point", "snapping",
    ],
    "withdrawal": [
        "need space", "shut down", "can't talk", "leave me", "too much",
        "need time alone", "need to be alone", "give me space", "back off",
        "don't touch me", "not now", "later", "can't deal", "can't handle",
        "closing off", "retreat", "withdraw", "pull back", "step back",
        "need a break", "time out", "pause", "stop", "enough",
        "overstimulated", "overloaded", "sensory overload", "need quiet",
        "can't process", "need to think", "sort things out", "figure things out",
        "emotional exhaustion", "drained", "burnt out", "no energy",
        "walls up", "guarded", "protecting myself", "shutting out",
        "disconnect", "disengagement", "checked out", "not present",
        "silent", "gone quiet", "stopped responding", "no response",
        "need to recharge", "recover", "heal", "regroup", "reset",
        "leave me alone", "let me be", "give me room", "respect my space",
        "don't come in", "need my bubble", "personal space", "boundaries",
        "pulling away", "distancing", "creating distance", "creating space",
        "introvert time", "recharge time", "me time", "alone time",
        "overwhelmed and need space", "need to decompress", "unwind alone",
        "processing", "need silence", "no talking", "quiet time",
    ],
    "finance": [
        "rent", "bills", "money", "budget", "pay", "job", "work", "unemployment",
        "mortgage", "utilities", "electric", "gas", "water", "internet", "phone bill",
        "groceries", "food costs", "gas money", "car payment", "insurance",
        "credit card", "debt", "loan", "interest", "payment due", "overdue",
        "salary", "wage", "income", "paycheck", "direct deposit", "bonus",
        "unemployment", "benefits", "severance", "laid off", "fired", "termination",
        "job search", "interview", "hiring", "application", "resume",
        "expenses", "spending", "savings", "emergency fund", "nest egg",
        "broke", "can't afford", "struggling", "tight", "stretched",
        "overdraft", "bounced check", "overdrawn", "negative balance",
        "taxes", "tax return", "refund", "withholding", "irs",
        "investments", "stocks", "retirement", "401k", "ira", "portfolio",
        "cost of living", "inflation", "price increase", "everything's expensive",
        "side hustle", "gig", "freelance", "extra income", "overtime",
        "promotion", "raise", "pay increase", "negotiate salary",
        "collections", "creditor", "debt collector", "garnishment",
        "eviction", "foreclosure", "repossession", "bankruptcy",
        "financially stressed", "money worries", "financial anxiety", "money stress",
        "bill collectors", "past due", "late fees", "interest rate",
        "budgeting", "cutting costs", "trimming expenses", "belt tightening",
        "living paycheck to paycheck", "scraping by", "getting by", "making ends meet",
    ],
}

def count_lexicon_hits(text: str, terms: list) -> int:
    t = (text or "").lower()
    return sum(1 for w in terms if w in t)

def classify_message(text: str) -> dict:
    return {k: count_lexicon_hits(text, v) for k, v in LEXICONS.items()}

def aggregate_messages(messages: list) -> dict:
    totals = {k: 0 for k in LEXICONS}
    for msg in messages:
        hits = classify_message(msg["text"])
        for k in totals:
            totals[k] += hits[k]
    return totals

def rate_per_1k(count: int, total_msgs: int) -> float:
    return (count + 1) / (total_msgs + 200) * 1000

def weekly_rollup(messages_by_week: dict) -> list:
    rows = []
    for week_start, msgs in messages_by_week.items():
        totals = aggregate_messages(msgs)
        n = len(msgs)
        row = {
            "week_start": week_start,
            "total_msgs": n,
            "affection": totals["affirmation"],
            "apology": totals["repair"],
            "joy": totals["joy"],
            "conflict": totals["conflict"],
            "withdrawal": totals["withdrawal"],
            "finance": totals["finance"],
        }
        row["affection_per_1k"] = rate_per_1k(row["affection"], n)
        row["conflict_per_1k"] = rate_per_1k(row["conflict"], n)
        row["withdrawal_per_1k"] = rate_per_1k(row["withdrawal"], n)
        row["finance_per_1k"] = rate_per_1k(row["finance"], n)
        row["repair_per_1k"] = rate_per_1k(row["apology"], n)
        rows.append(row)
    return rows

def zscore(arr):
    m, s = np.mean(arr), np.std(arr) or 1
    return [(x - m) / s for x in arr]

def build_composite_scores(weekly: list) -> None:
    aff = zscore([r["affection_per_1k"] for r in weekly])
    con = zscore([r["conflict_per_1k"] for r in weekly])
    wit = zscore([r["withdrawal_per_1k"] for r in weekly])
    fin = zscore([r["finance_per_1k"] for r in weekly])
    rep = zscore([r["repair_per_1k"] for r in weekly])
    for i, r in enumerate(weekly):
        r["connection_score"] = aff[i] - con[i] - wit[i]
        r["strain_score"] = con[i] + wit[i] + fin[i] + rep[i]
    thresh = np.percentile([r["strain_score"] for r in weekly], 85)
    for r in weekly:
        r["crisis_label"] = 1 if r["strain_score"] >= thresh else 0

def same_episode(prev_ts, curr_ts, gap_hours=72):
    return (curr_ts - prev_ts).total_seconds() / 3600 < gap_hours

msgs = parse_transcript("full_conversation.md")
by_week = group_by_week(msgs)
weekly = weekly_rollup(by_week)
build_composite_scores(weekly)
totals = aggregate_messages(msgs)

Model output: learned coefficients

Logistic regression trained on weekly features to predict “crisis” weeks (top 15% strain). Higher abs_coef means a stronger signal; odds_ratio_per_1SD is the multiplier per standard deviation change.