Docs/Skills and code/src/

src/analysis/within-channel-performance.js

Code207 lines13.1 KBsrc/

import {
  ageCohort,
  concentrationShare,
  median,
  medianAbsoluteDeviation,
  numberOrNull,
  outlierLabel,
  prepareVideos,
  quantile,
  reliablePublicationDate,
  robustZ,
  round,
  sampleAssessment,
  viewRatio
} from "./within-channel-statistics.js";
import { buildWithinChannelQueueEvidence } from "./within-channel-queue-evidence.js";

const OUTLIER_VERSION = "within-channel-performance-v1.0.0";
const DAY_MS = 86_400_000;

export function analyzeWithinChannelCorpus({ channels, videosByChannel, nowMs = Date.now(), config = {} }) {
  const minCohortSize = config.minAgeCohortSize ?? 20;
  const preparedByChannel = new Map();
  const allPrepared = [];
  for (const channel of channels) {
    const sourceVideos = videosByChannel.get(channel.channel_url) ?? [];
    const prepared = prepareVideos(sourceVideos, nowMs);
    preparedByChannel.set(channel.channel_url, prepared);
    allPrepared.push(...prepared);
  }

  const cohortGroups = new Map();
  for (const video of allPrepared) {
    if (video.numeric_views == null || video.views_per_day == null || video.age_cohort === "unknown") continue;
    if (!cohortGroups.has(video.age_cohort)) cohortGroups.set(video.age_cohort, []);
    cohortGroups.get(video.age_cohort).push(video.views_per_day);
  }
  const cohortBenchmarks = [...cohortGroups.entries()].map(([cohort, values]) => ({
    age_cohort: cohort,
    eligible_video_count: values.length,
    median_views_per_day: round(median(values), 3),
    p90_views_per_day: round(quantile(values, 0.9), 3),
    benchmark_status: values.length >= minCohortSize ? "adequate_for_descriptive_comparison" : "small_cohort"
  }));
  const cohortBenchmarkByName = new Map(cohortBenchmarks.map(row => [row.age_cohort, row]));

  const profiles = [];
  const outliers = [];
  for (const channel of channels) {
    const videos = preparedByChannel.get(channel.channel_url) ?? [];
    const eligible = videos.filter(video => video.numeric_views != null);
    const dated = eligible.filter(video => video.date_reliable && video.views_per_day != null);
    const viewValues = eligible.map(video => video.numeric_views);
    const logValues = eligible.map(video => video.log_views);
    const sortedDesc = [...eligible].sort((a, b) => b.numeric_views - a.numeric_views || String(a.video_id).localeCompare(String(b.video_id)));
    const totalViews = viewValues.reduce((sum, value) => sum + value, 0);
    const medianViews = median(viewValues);
    const medianLogViews = median(logValues);
    const logMad = medianAbsoluteDeviation(logValues);
    const medianVpd = median(dated.map(video => video.views_per_day));
    const logVpd = dated.map(video => video.log_views_per_day);
    const medianLogVpd = median(logVpd);
    const logVpdMad = medianAbsoluteDeviation(logVpd);
    const subscriberCount = numberOrNull(channel.resolved_subscriber_count ?? channel.subscriber_count);
    const declaredCount = numberOrNull(channel.resolved_video_count ?? channel.video_count);
    const subscriberPrecision = channel.resolved_subscriber_count_precision ?? channel.subscriber_count_precision ?? null;
    const declaredPrecision = channel.resolved_video_count_precision ?? channel.video_count_precision ?? null;
    const threshold = Math.max(config.absoluteReachFloor ?? 1000, subscriberCount == null ? 0 : subscriberCount * (config.subscriberReachFraction ?? 0.25));
    const aboveThreshold = eligible.filter(video => video.numeric_views >= threshold).length;
    const aboveTwiceMedian = medianViews != null && medianViews > 0 ? eligible.filter(video => video.numeric_views >= medianViews * 2).length : 0;
    const datedMs = dated.map(video => reliablePublicationDate(video)).filter(Number.isFinite);
    const oldestMs = datedMs.length ? Math.min(...datedMs) : null;
    const newestMs = datedMs.length ? Math.max(...datedMs) : null;
    const warnings = [];
    if (eligible.length < 5) warnings.push("fewer_than_5_videos_with_views");
    if (eligible.some(video => String(video.view_count_precision ?? "").toLowerCase().includes("abbreviated"))) warnings.push("some_view_counts_are_abbreviated_estimates");
    if (eligible.some(video => !video.view_count_precision || ["unknown", "null"].includes(String(video.view_count_precision).toLowerCase()))) warnings.push("some_view_count_precision_is_unknown");
    if (declaredCount == null) warnings.push("declared_video_count_missing");
    if (String(subscriberPrecision ?? "").toLowerCase().includes("abbreviated")) warnings.push("subscriber_count_is_abbreviated_estimate_for_reach_threshold");
    if (String(declaredPrecision ?? "").toLowerCase().includes("abbreviated")) warnings.push("declared_video_count_is_abbreviated_estimate_for_coverage");
    if (declaredCount != null && videos.length > declaredCount) warnings.push("linked_video_count_exceeds_declared_catalogue");
    if (dated.length < 5) warnings.push("fewer_than_5_reliable_publication_dates_with_views");

    const profile = {
      channel_url: channel.channel_url,
      channel_name: channel.resolved_name ?? channel.name ?? null,
      linked_unique_video_count: videos.length,
      eligible_view_count: eligible.length,
      missing_view_count: videos.length - eligible.length,
      subscriber_count: subscriberCount,
      subscriber_count_precision: subscriberPrecision,
      declared_video_count_precision: declaredPrecision,
      exact_view_count: eligible.filter(video => String(video.view_count_precision ?? "").toLowerCase().startsWith("exact")).length,
      approximate_view_count: eligible.filter(video => String(video.view_count_precision ?? "").toLowerCase().includes("abbreviated")).length,
      declared_video_count: declaredCount,
      catalogue_coverage_ratio: declaredCount != null && declaredCount > 0 ? round(videos.length / declaredCount, 6) : null,
      eligible_catalogue_coverage_ratio: declaredCount != null && declaredCount > 0 ? round(eligible.length / declaredCount, 6) : null,
      linked_view_coverage_ratio: videos.length > 0 ? round(eligible.length / videos.length, 6) : null,
      sample_assessment: sampleAssessment(eligible.length),
      median_views: round(medianViews, 3),
      p25_views: round(quantile(viewValues, 0.25), 3),
      p75_views: round(quantile(viewValues, 0.75), 3),
      p90_views: round(quantile(viewValues, 0.9), 3),
      view_iqr: median(viewValues) == null ? null : round(quantile(viewValues, 0.75) - quantile(viewValues, 0.25), 3),
      view_mad: round(medianAbsoluteDeviation(viewValues), 3),
      log_view_median: round(medianLogViews, 6),
      log_view_mad: round(logMad, 6),
      sampled_view_total: round(totalViews, 3),
      top_1_view_share: round(concentrationShare(sortedDesc, 1, totalViews), 6),
      top_5_view_share: round(concentrationShare(sortedDesc, 5, totalViews), 6),
      top_10_view_share: round(concentrationShare(sortedDesc, 10, totalViews), 6),
      twice_median_count: aboveTwiceMedian,
      twice_median_share: eligible.length ? round(aboveTwiceMedian / eligible.length, 6) : null,
      reach_threshold_views: round(threshold, 3),
      reach_threshold_count: aboveThreshold,
      reach_threshold_share: eligible.length ? round(aboveThreshold / eligible.length, 6) : null,
      reliable_dated_view_count: dated.length,
      reliable_dated_view_share: eligible.length ? round(dated.length / eligible.length, 6) : null,
      median_views_per_day: round(medianVpd, 6),
      p90_views_per_day: round(quantile(dated.map(video => video.views_per_day), 0.9), 6),
      oldest_reliable_published_at: oldestMs == null ? null : new Date(oldestMs).toISOString(),
      newest_reliable_published_at: newestMs == null ? null : new Date(newestMs).toISOString(),
      reliable_publication_span_days: oldestMs != null && newestMs != null ? round((newestMs - oldestMs) / DAY_MS, 3) : null,
      outlier_candidate_count: 0,
      strong_outlier_count: 0,
      age_adjusted_outlier_candidate_count: 0,
      age_adjusted_strong_outlier_count: 0,
      warnings_json: JSON.stringify(warnings)
    };

    for (const video of videos) {
      const benchmark = cohortBenchmarkByName.get(video.age_cohort) ?? null;
      const hasViews = video.numeric_views != null;
      const lifetimeZ = hasViews && video.log_views != null ? robustZ(video.log_views, logValues) : { score: null, mad: logMad, center: medianLogViews };
      const lifetimeRatio = hasViews ? viewRatio(video.numeric_views, medianViews) : null;
      const lifetimeLabel = !hasViews ? "unavailable_no_view_count" : outlierLabel(lifetimeZ.score, lifetimeRatio, eligible.length, lifetimeZ.mad);
      const ageZ = video.views_per_day != null ? robustZ(video.log_views_per_day, logVpd) : { score: null, mad: logVpdMad, center: medianLogVpd };
      const ageRatio = video.views_per_day != null ? viewRatio(video.views_per_day, medianVpd) : null;
      const ageLabel = !hasViews
        ? "unavailable_no_view_count"
        : video.views_per_day == null
          ? "unavailable_unreliable_publication_date"
          : dated.length < 5 ? "insufficient_dated_sample" : outlierLabel(ageZ.score, ageRatio, dated.length, ageZ.mad);
      const cohortRatio = video.views_per_day != null && benchmark?.median_views_per_day > 0
        ? video.views_per_day / benchmark.median_views_per_day : null;
      const flags = [];
      if (!hasViews) flags.push("view_count_missing");
      if (hasViews && !String(video.view_count_precision ?? "").toLowerCase().startsWith("exact")) flags.push("view_count_not_confirmed_exact");
      if (!video.date_reliable) flags.push("publication_date_not_reliable_for_age_adjustment");
      if (benchmark && benchmark.eligible_video_count < minCohortSize) flags.push("age_cohort_benchmark_small");
      if (eligible.length < 10) flags.push("channel_view_sample_small");
      if (hasViews && lifetimeLabel.startsWith("potential_outlier")) profile.outlier_candidate_count += 1;
      if (hasViews && lifetimeLabel.startsWith("strong_outlier")) profile.strong_outlier_count += 1;
      if (ageLabel.startsWith("potential_outlier")) profile.age_adjusted_outlier_candidate_count += 1;
      if (ageLabel.startsWith("strong_outlier")) profile.age_adjusted_strong_outlier_count += 1;
      outliers.push({
        channel_url: channel.channel_url,
        channel_name: profile.channel_name,
        video_id: video.video_id,
        title: video.title ?? null,
        url: video.url ?? null,
        view_count: hasViews ? video.numeric_views : null,
        view_count_precision: video.view_count_precision ?? null,
        view_count_source: video.view_count_source ?? null,
        view_observed_at: video.view_observed_at ?? null,
        published_at: video.published_at_normalized || video.published_at || null,
        published_at_precision: video.published_at_precision ?? null,
        published_at_normalization_method: video.published_at_normalization_method ?? null,
        date_reliability: video.date_reliable ? "exact_calendar_date" : "not_reliable_for_age_adjustment",
        duration_seconds: numberOrNull(video.duration_seconds),
        duration_precision: video.duration_precision ?? null,
        category: video.category ?? null,
        age_days: round(video.age_days, 3),
        age_cohort: video.age_cohort,
        views_per_day: round(video.views_per_day, 6),
        channel_sample_size: eligible.length,
        channel_dated_sample_size: dated.length,
        channel_median_views: round(medianViews, 3),
        lifetime_relative_ratio: round(lifetimeRatio, 6),
        lifetime_log_robust_z: round(lifetimeZ.score, 6),
        lifetime_outlier_label: lifetimeLabel,
        lifetime_outlier_method: lifetimeZ.score != null ? "log1p_views_median_mad" : (lifetimeZ.mad === 0 && lifetimeRatio != null ? "relative_ratio_fallback_zero_mad" : "unavailable"),
        channel_median_views_per_day: round(medianVpd, 6),
        age_adjusted_relative_ratio: round(ageRatio, 6),
        age_adjusted_log_robust_z: round(ageZ.score, 6),
        age_adjusted_outlier_label: ageLabel,
        age_adjusted_outlier_method: ageZ.score != null ? "log1p_views_per_day_median_mad" : (ageZ.mad === 0 && ageRatio != null ? "relative_ratio_fallback_zero_mad" : "unavailable"),
        age_cohort_video_count: benchmark?.eligible_video_count ?? 0,
        age_cohort_median_views_per_day: benchmark?.median_views_per_day ?? null,
        age_cohort_relative_ratio: round(cohortRatio, 6),
        age_cohort_benchmark_status: benchmark?.benchmark_status ?? "no_benchmark",
        evidence_status: sampleAssessment(eligible.length),
        quality_flags_json: JSON.stringify(flags)
      });
    }
    profile.outlier_candidate_share = eligible.length ? round(profile.outlier_candidate_count / eligible.length, 6) : null;
    profile.strong_outlier_share = eligible.length ? round(profile.strong_outlier_count / eligible.length, 6) : null;
    profile.age_adjusted_outlier_candidate_share = dated.length ? round(profile.age_adjusted_outlier_candidate_count / dated.length, 6) : null;
    profile.age_adjusted_strong_outlier_share = dated.length ? round(profile.age_adjusted_strong_outlier_count / dated.length, 6) : null;
    profiles.push(profile);
  }

  const queueEvidence = buildWithinChannelQueueEvidence({ profiles, outliers, channels, version: OUTLIER_VERSION });

  return { profiles, outliers, cohortBenchmarks, queueEvidence, version: OUTLIER_VERSION };
}

Shown as it is in the repository at build time (2026-11-03); the home directory appears as <home>. If this page and the file differ, the file is right. Back to the catalogue.