• Not Exported

    Detects patterns related to skip behavior at specific times of day or days of week

    Analyzes temporal distribution of skips to identify periods when users are more likely to skip tracks. This detection algorithm recognizes both:

    • Time-of-day patterns (e.g., skipping more during morning commute)
    • Day-of-week patterns (e.g., skipping more on Mondays)

    The algorithm performs several analytical steps:

    1. Identifying peak skip hours that significantly exceed average
    2. Formatting time periods in readable format (e.g., "9AM, 5PM")
    3. Analyzing day-of-week distributions to find patterns
    4. Calculating confidence based on statistical significance of the pattern

    Time patterns can reveal insights about:

    • Listening context (work, commute, relaxation, etc.)
    • Attention patterns during different parts of the day
    • Weekly routines and their impact on music preferences

    Parameters

    • timePatterns: TimePatterns

      Object containing hourly and daily skip distribution data

    Returns DetectedPattern[]

    Array of detected time-based patterns that meet confidence thresholds

    // Detecting time-of-day skip patterns
    const timeData = await analyzeTimeBasedPatterns();
    const timePatterns = detectTimeOfDayPatterns(timeData);

    // Sample result patterns:
    // [
    // { type: "time_of_day", description: "Tends to skip more tracks during 8AM, 5PM", ... },
    // { type: "time_of_day", description: "Skips more tracks on Monday", ... }
    // ]
    function detectTimeOfDayPatterns(
    timePatterns: TimePatterns,
    ): DetectedPattern[] {
    const patterns: DetectedPattern[] = [];

    // Check if there are significant peak hours
    if (timePatterns.peakSkipHours && timePatterns.peakSkipHours.length > 0) {
    // Format hours in a readable way
    const formatHour = (hour: number) => {
    const amPm = hour >= 12 ? "PM" : "AM";
    const hourFormatted = hour % 12 === 0 ? 12 : hour % 12;
    return `${hourFormatted}${amPm}`;
    };

    const peakHoursFormatted = timePatterns.peakSkipHours
    .map((hour) => formatHour(hour))
    .join(", ");

    // Calculate the average skip count and determine how much above average the peaks are
    const avgSkips =
    timePatterns.hourlyDistribution.reduce((sum, count) => sum + count, 0) /
    24;
    const peakSkipsAvg =
    timePatterns.peakSkipHours.reduce(
    (sum, hour) => sum + (timePatterns.hourlyDistribution[hour] || 0),
    0,
    ) / timePatterns.peakSkipHours.length;

    const peakFactor = peakSkipsAvg / avgSkips;
    const confidence = Math.min(0.9, peakFactor / 2); // Cap at 0.9

    if (confidence >= PATTERN_THRESHOLDS.CONFIDENCE_THRESHOLD) {
    patterns.push({
    type: PatternType.TIME_OF_DAY,
    confidence,
    description: `Tends to skip more tracks during ${peakHoursFormatted}`,
    occurrences: timePatterns.peakSkipHours.reduce(
    (sum, hour) => sum + (timePatterns.hourlyDistribution[hour] || 0),
    0,
    ),
    relatedItems: timePatterns.peakSkipHours.map((h) => formatHour(h)),
    details: {
    peakHours: timePatterns.peakSkipHours,
    peakFactor,
    hourlyDistribution: timePatterns.hourlyDistribution,
    },
    firstDetected: new Date().toISOString(),
    lastDetected: new Date().toISOString(),
    });
    }
    }

    // Check if there's a strong day-of-week pattern
    // Use dayOfWeekDistribution or dayDistribution, whichever is available
    const dayDistribution =
    timePatterns.dayOfWeekDistribution || timePatterns.dayDistribution;

    if (dayDistribution && dayDistribution.length > 0) {
    const dayNames = [
    "Sunday",
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday",
    ];

    const avgDaySkips =
    dayDistribution.reduce((sum, count) => sum + count, 0) / 7;

    dayDistribution.forEach((count, day) => {
    if (
    count > avgDaySkips * PATTERN_THRESHOLDS.TIME_FACTOR_THRESHOLD &&
    count >= PATTERN_THRESHOLDS.MIN_OCCURRENCES
    ) {
    const dayFactor = count / avgDaySkips;
    const confidence = Math.min(0.9, dayFactor / 2);

    if (confidence >= PATTERN_THRESHOLDS.CONFIDENCE_THRESHOLD) {
    patterns.push({
    type: PatternType.TIME_OF_DAY,
    confidence,
    description: `Skips more tracks on ${dayNames[day]}`,
    occurrences: count,
    relatedItems: [dayNames[day]],
    details: {
    day,
    dayName: dayNames[day],
    dayFactor,
    dayDistribution: dayDistribution,
    },
    firstDetected: new Date().toISOString(),
    lastDetected: new Date().toISOString(),
    });
    }
    }
    });
    }

    return patterns;
    }