Skip to content

Commit b8b26de

Browse files
authored
Normalize trend by overall average (#8767)
1 parent 60290d7 commit b8b26de

File tree

3 files changed

+30
-12
lines changed

3 files changed

+30
-12
lines changed

app/lib/service/download_counts/package_trends.dart

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'dart:math';
66

77
const analysisWindowDays = 30;
8+
const totalTrendWindowDays = 330;
89
const minThirtyDaysDownloadThreshold = 30000;
910

1011
/// Calculates the relative daily growth rate of a package's downloads.
@@ -22,6 +23,9 @@ const minThirtyDaysDownloadThreshold = 30000;
2223
/// downloads (10% relative growth) than for a package with 10000 average daily
2324
/// downloads (0.1% relative growth).
2425
double computeRelativeGrowthRate(List<int> totalDownloads) {
26+
if (totalDownloads.isEmpty) {
27+
return 0;
28+
}
2529
final List<int> data;
2630
if (totalDownloads.length < analysisWindowDays) {
2731
data = [
@@ -38,7 +42,12 @@ double computeRelativeGrowthRate(List<int> totalDownloads) {
3842
recentDownloads.reduce((prev, element) => prev + element) /
3943
recentDownloads.length;
4044

41-
if (averageRecentDownloads == 0) {
45+
final m = min(totalDownloads.length, totalTrendWindowDays);
46+
final averageTotalDownloads =
47+
totalDownloads.sublist(0, m).reduce((prev, element) => prev + element) /
48+
m;
49+
50+
if (averageRecentDownloads == 0 || averageTotalDownloads == 0) {
4251
return 0;
4352
}
4453

@@ -51,7 +60,7 @@ double computeRelativeGrowthRate(List<int> totalDownloads) {
5160
// Normalize slope by average downloads to represent relative growth.
5261
// This measures how much the download count is growing relative to its
5362
// current volume.
54-
return growthRate / averageRecentDownloads;
63+
return growthRate / averageTotalDownloads;
5564
}
5665

5766
/// Computes the slope of the best-fit line for a given list of data points

app/test/service/download_counts/computations_test.dart

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,10 +314,16 @@ void main() {
314314
'fake_download_counts_data_for_trend2.jsonl'));
315315
await processDownloadCounts(d);
316316
}
317-
final neonTrend = computeTrendScore(
318-
[...List.filled(15, 2000), ...List.filled(15, 1000)]);
319-
final oxygenTrend = computeTrendScore(
320-
[...List.filled(15, 5000), ...List.filled(15, 3000)]);
317+
final neonTrend = computeTrendScore([
318+
...List.filled(15, 2000),
319+
...List.filled(15, 1000),
320+
...List.filled(701, -1)
321+
]);
322+
final oxygenTrend = computeTrendScore([
323+
...List.filled(15, 5000),
324+
...List.filled(15, 3000),
325+
...List.filled(701, -1)
326+
]);
321327

322328
expect(await computeTrend(),
323329
{'flutter_titanium': 0.0, 'neon': neonTrend, 'oxygen': oxygenTrend});

app/test/service/download_counts/package_trends_test.dart

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,12 @@ void main() {
3636
test('calculates positive relative growth rate for positive trend', () {
3737
// Input list (newest first): [1645, 1635, ..., 1355] (30 values)
3838
// Average = 1500 for the first 30 values. Slope: 10.
39-
final downloads =
40-
List<int>.generate(analysisWindowDays * 2, (i) => 1645 - (i * 10));
41-
final expectedRate = 10.0 / 1500.0;
39+
final downloads = <int>[
40+
...List<int>.generate(analysisWindowDays * 2, (i) => 1645 - (i * 10)),
41+
...List.filled(300, 0)
42+
];
43+
final avg = downloads.reduce((prev, element) => prev + element) / 330;
44+
final expectedRate = 10.0 / avg;
4245
expect(computeRelativeGrowthRate(downloads), expectedRate);
4346
});
4447

@@ -107,10 +110,10 @@ void main() {
107110
final downloads = [100, 50];
108111
// For relativeGrowth:
109112
// Padded data: [100, 50, 0...0] (28 zeros)
110-
// avg = 150/30 = 5
113+
// avg = (100 + 50) / 2 = 75.
111114
// growthRate = 63750 / 67425
112115
final expectedDampening = min(1.0, 150 / 30000);
113-
final expectedRelativeGrowth = 63750 / 67425 / 5;
116+
final expectedRelativeGrowth = (63750 / 67425) / 75;
114117
final expectedScore =
115118
expectedRelativeGrowth * expectedDampening * expectedDampening;
116119
expect(computeTrendScore(downloads), expectedScore);
@@ -178,7 +181,7 @@ void main() {
178181
test('Short history, high sum meets threshold -> no dampening', () {
179182
final downloads = List<int>.filled(15, 2000);
180183
final expectedDampening = min(1.0, 30000 / 30000);
181-
final expectedRelativeGrowth = 6750000 / 67425 / 1000;
184+
final expectedRelativeGrowth = (6750000 / 67425) / 2000;
182185
final expectedScore =
183186
expectedRelativeGrowth * expectedDampening * expectedDampening;
184187

0 commit comments

Comments
 (0)