Upload beautify
This commit is contained in:
139
app/Services/Recommendations/FeedOfflineEvaluationService.php
Normal file
139
app/Services/Recommendations/FeedOfflineEvaluationService.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Recommendations;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
final class FeedOfflineEvaluationService
|
||||
{
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function evaluateAlgo(string $algoVersion, string $from, string $to): array
|
||||
{
|
||||
$row = DB::table('feed_daily_metrics')
|
||||
->selectRaw('SUM(impressions) as impressions')
|
||||
->selectRaw('SUM(clicks) as clicks')
|
||||
->selectRaw('SUM(saves) as saves')
|
||||
->selectRaw('SUM(dwell_0_5) as dwell_0_5')
|
||||
->selectRaw('SUM(dwell_5_30) as dwell_5_30')
|
||||
->selectRaw('SUM(dwell_30_120) as dwell_30_120')
|
||||
->selectRaw('SUM(dwell_120_plus) as dwell_120_plus')
|
||||
->where('algo_version', $algoVersion)
|
||||
->whereBetween('metric_date', [$from, $to])
|
||||
->first();
|
||||
|
||||
$impressions = (int) ($row->impressions ?? 0);
|
||||
$clicks = (int) ($row->clicks ?? 0);
|
||||
$saves = (int) ($row->saves ?? 0);
|
||||
|
||||
$dwell05 = (int) ($row->dwell_0_5 ?? 0);
|
||||
$dwell530 = (int) ($row->dwell_5_30 ?? 0);
|
||||
$dwell30120 = (int) ($row->dwell_30_120 ?? 0);
|
||||
$dwell120Plus = (int) ($row->dwell_120_plus ?? 0);
|
||||
|
||||
$ctr = $impressions > 0 ? $clicks / $impressions : 0.0;
|
||||
$saveRate = $clicks > 0 ? $saves / $clicks : 0.0;
|
||||
$longDwellShare = $clicks > 0 ? ($dwell30120 + $dwell120Plus) / $clicks : 0.0;
|
||||
$bounceRate = $clicks > 0 ? $dwell05 / $clicks : 0.0;
|
||||
|
||||
$objectiveWeights = (array) config('discovery.evaluation.objective_weights', []);
|
||||
$wCtr = (float) ($objectiveWeights['ctr'] ?? 0.45);
|
||||
$wSave = (float) ($objectiveWeights['save_rate'] ?? 0.35);
|
||||
$wLong = (float) ($objectiveWeights['long_dwell_share'] ?? 0.25);
|
||||
$wBouncePenalty = (float) ($objectiveWeights['bounce_rate_penalty'] ?? 0.15);
|
||||
|
||||
$saveRateInformational = (bool) config('discovery.evaluation.save_rate_informational', true);
|
||||
if ($saveRateInformational) {
|
||||
$wSave = 0.0;
|
||||
}
|
||||
|
||||
$normalizationSum = $wCtr + $wSave + $wLong + $wBouncePenalty;
|
||||
if ($normalizationSum > 0.0) {
|
||||
$wCtr /= $normalizationSum;
|
||||
$wSave /= $normalizationSum;
|
||||
$wLong /= $normalizationSum;
|
||||
$wBouncePenalty /= $normalizationSum;
|
||||
}
|
||||
|
||||
$objectiveScore = ($wCtr * $ctr)
|
||||
+ ($wSave * $saveRate)
|
||||
+ ($wLong * $longDwellShare)
|
||||
- ($wBouncePenalty * $bounceRate);
|
||||
|
||||
return [
|
||||
'algo_version' => $algoVersion,
|
||||
'from' => $from,
|
||||
'to' => $to,
|
||||
'impressions' => $impressions,
|
||||
'clicks' => $clicks,
|
||||
'saves' => $saves,
|
||||
'ctr' => round($ctr, 6),
|
||||
'save_rate' => round($saveRate, 6),
|
||||
'long_dwell_share' => round($longDwellShare, 6),
|
||||
'bounce_rate' => round($bounceRate, 6),
|
||||
'dwell_buckets' => [
|
||||
'0_5' => $dwell05,
|
||||
'5_30' => $dwell530,
|
||||
'30_120' => $dwell30120,
|
||||
'120_plus' => $dwell120Plus,
|
||||
],
|
||||
'objective_score' => round($objectiveScore, 6),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function evaluateAll(string $from, string $to): array
|
||||
{
|
||||
$algoVersions = DB::table('feed_daily_metrics')
|
||||
->select('algo_version')
|
||||
->whereBetween('metric_date', [$from, $to])
|
||||
->distinct()
|
||||
->orderBy('algo_version')
|
||||
->pluck('algo_version')
|
||||
->map(static fn (mixed $v): string => (string) $v)
|
||||
->all();
|
||||
|
||||
$out = [];
|
||||
foreach ($algoVersions as $algoVersion) {
|
||||
$out[] = $this->evaluateAlgo($algoVersion, $from, $to);
|
||||
}
|
||||
|
||||
usort($out, static fn (array $a, array $b): int => $b['objective_score'] <=> $a['objective_score']);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function compareBaselineCandidate(string $baselineAlgoVersion, string $candidateAlgoVersion, string $from, string $to): array
|
||||
{
|
||||
$baseline = $this->evaluateAlgo($baselineAlgoVersion, $from, $to);
|
||||
$candidate = $this->evaluateAlgo($candidateAlgoVersion, $from, $to);
|
||||
|
||||
$deltaObjective = (float) $candidate['objective_score'] - (float) $baseline['objective_score'];
|
||||
$objectiveLiftPct = (float) $baseline['objective_score'] !== 0.0
|
||||
? ($deltaObjective / (float) $baseline['objective_score']) * 100.0
|
||||
: null;
|
||||
|
||||
return [
|
||||
'from' => $from,
|
||||
'to' => $to,
|
||||
'baseline' => $baseline,
|
||||
'candidate' => $candidate,
|
||||
'delta' => [
|
||||
'objective_score' => round($deltaObjective, 6),
|
||||
'objective_lift_pct' => $objectiveLiftPct !== null ? round($objectiveLiftPct, 4) : null,
|
||||
'ctr' => round((float) $candidate['ctr'] - (float) $baseline['ctr'], 6),
|
||||
'save_rate' => round((float) $candidate['save_rate'] - (float) $baseline['save_rate'], 6),
|
||||
'long_dwell_share' => round((float) $candidate['long_dwell_share'] - (float) $baseline['long_dwell_share'], 6),
|
||||
'bounce_rate' => round((float) $candidate['bounce_rate'] - (float) $baseline['bounce_rate'], 6),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user