Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(178)

Side by Side Diff: components/ntp_snippets/user_classifier.cc

Issue 2804633003: Add base::FeatureParam<> struct (Closed)
Patch Set: Remove windows-incompatible constexpr Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/ntp_snippets/user_classifier.h" 5 #include "components/ntp_snippets/user_classifier.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <cfloat> 8 #include <cfloat>
9 #include <string> 9 #include <string>
10 10
11 #include "base/metrics/histogram_macros.h" 11 #include "base/metrics/histogram_macros.h"
12 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/string_number_conversions.h"
13 #include "base/time/clock.h" 13 #include "base/time/clock.h"
14 #include "components/ntp_snippets/features.h" 14 #include "components/ntp_snippets/features.h"
15 #include "components/ntp_snippets/pref_names.h" 15 #include "components/ntp_snippets/pref_names.h"
16 #include "components/prefs/pref_registry_simple.h" 16 #include "components/prefs/pref_registry_simple.h"
17 #include "components/prefs/pref_service.h" 17 #include "components/prefs/pref_service.h"
18 #include "components/variations/variations_associated_data.h" 18 #include "components/variations/variations_associated_data.h"
19 19
20 namespace ntp_snippets { 20 namespace ntp_snippets {
21 21
22 namespace { 22 namespace {
23 23
24 // The discount rate for computing the discounted-average metrics. Must be 24 // The discount rate for computing the discounted-average metrics. Must be
25 // strictly larger than 0 and strictly smaller than 1! 25 // strictly larger than 0 and strictly smaller than 1!
26 const double kDiscountRatePerDay = 0.25; 26 const double kDiscountRatePerDay = 0.25;
27 const char kDiscountRatePerDayParam[] = "user_classifier_discount_rate_per_day"; 27 constexpr base::FeatureParam<double> kDiscountRatePerDayParam{
28 &kArticleSuggestionsFeature, "user_classifier_discount_rate_per_day", 0.25};
28 29
29 // Never consider any larger interval than this (so that extreme situations such 30 // Never consider any larger interval than this (so that extreme situations such
30 // as losing your phone or going for a long offline vacation do not skew the 31 // as losing your phone or going for a long offline vacation do not skew the
31 // average too much). 32 // average too much).
32 // When everriding via variation parameters, it is better to use smaller values 33 // When everriding via variation parameters, it is better to use smaller values
33 // than |kMaxHours| as this it the maximum value reported in the histograms. 34 // than |kMaxHours| as this it the maximum value reported in the histograms.
34 const double kMaxHours = 7 * 24; 35 constexpr base::FeatureParam<double> kMaxHoursParam{
35 const char kMaxHoursParam[] = "user_classifier_max_hours"; 36 &kArticleSuggestionsFeature, "user_classifier_max_hours", 7 * 24};
36 37
37 // Ignore events within |kMinHours| hours since the last event (|kMinHours| is 38 // Ignore events within |kMinHours| hours since the last event (|kMinHours| is
38 // the length of the browsing session where subsequent events of the same type 39 // the length of the browsing session where subsequent events of the same type
39 // do not count again). 40 // do not count again).
40 const double kMinHours = 0.5; 41 constexpr base::FeatureParam<double> kMinHoursParam{
41 const char kMinHoursParam[] = "user_classifier_min_hours"; 42 &kArticleSuggestionsFeature, "user_classifier_min_hours", 0.5};
42 43
43 // Classification constants. 44 // Classification constants.
44 const double kActiveConsumerClicksAtLeastOncePerHours = 72; 45 constexpr base::FeatureParam<double>
45 const char kActiveConsumerClicksAtLeastOncePerHoursParam[] = 46 kActiveConsumerClicksAtLeastOncePerHoursParam{
46 "user_classifier_active_consumer_clicks_at_least_once_per_hours"; 47 &kArticleSuggestionsFeature,
48 "user_classifier_active_consumer_clicks_at_least_once_per_hours", 72};
47 49
48 const double kRareUserOpensNTPAtMostOncePerHours = 96; 50 constexpr base::FeatureParam<double> kRareUserOpensNTPAtMostOncePerHoursParam{
49 const char kRareUserOpensNTPAtMostOncePerHoursParam[] = 51 &kArticleSuggestionsFeature,
50 "user_classifier_rare_user_opens_ntp_at_most_once_per_hours"; 52 "user_classifier_rare_user_opens_ntp_at_most_once_per_hours", 96};
51 53
52 // Histograms for logging the estimated average hours to next event. 54 // Histograms for logging the estimated average hours to next event.
53 const char kHistogramAverageHoursToOpenNTP[] = 55 const char kHistogramAverageHoursToOpenNTP[] =
54 "NewTabPage.UserClassifier.AverageHoursToOpenNTP"; 56 "NewTabPage.UserClassifier.AverageHoursToOpenNTP";
55 const char kHistogramAverageHoursToShowSuggestions[] = 57 const char kHistogramAverageHoursToShowSuggestions[] =
56 "NewTabPage.UserClassifier.AverageHoursToShowSuggestions"; 58 "NewTabPage.UserClassifier.AverageHoursToShowSuggestions";
57 const char kHistogramAverageHoursToUseSuggestions[] = 59 const char kHistogramAverageHoursToUseSuggestions[] =
58 "NewTabPage.UserClassifier.AverageHoursToUseSuggestions"; 60 "NewTabPage.UserClassifier.AverageHoursToUseSuggestions";
59 61
60 // The enum used for iteration. 62 // The enum used for iteration.
61 const UserClassifier::Metric kMetrics[] = { 63 const UserClassifier::Metric kMetrics[] = {
62 UserClassifier::Metric::NTP_OPENED, 64 UserClassifier::Metric::NTP_OPENED,
63 UserClassifier::Metric::SUGGESTIONS_SHOWN, 65 UserClassifier::Metric::SUGGESTIONS_SHOWN,
64 UserClassifier::Metric::SUGGESTIONS_USED}; 66 UserClassifier::Metric::SUGGESTIONS_USED};
65 67
66 // The summary of the prefs. 68 // The summary of the prefs.
67 const char* kMetricKeys[] = { 69 const char* kMetricKeys[] = {
68 prefs::kUserClassifierAverageNTPOpenedPerHour, 70 prefs::kUserClassifierAverageNTPOpenedPerHour,
69 prefs::kUserClassifierAverageSuggestionsShownPerHour, 71 prefs::kUserClassifierAverageSuggestionsShownPerHour,
70 prefs::kUserClassifierAverageSuggestionsUsedPerHour}; 72 prefs::kUserClassifierAverageSuggestionsUsedPerHour};
71 const char* kLastTimeKeys[] = {prefs::kUserClassifierLastTimeToOpenNTP, 73 const char* kLastTimeKeys[] = {prefs::kUserClassifierLastTimeToOpenNTP,
72 prefs::kUserClassifierLastTimeToShowSuggestions, 74 prefs::kUserClassifierLastTimeToShowSuggestions,
73 prefs::kUserClassifierLastTimeToUseSuggestions}; 75 prefs::kUserClassifierLastTimeToUseSuggestions};
74 76
75 // Default lengths of the intervals for new users for the metrics. 77 // Default lengths of the intervals for new users for the metrics.
76 const double kInitialHoursBetweenEvents[] = {24, 48, 96}; 78 constexpr base::FeatureParam<double> kInitialHoursBetweenEventsParams[] = {
77 const char* kInitialHoursBetweenEventsParams[] = { 79 {&kArticleSuggestionsFeature, "user_classifier_default_interval_ntp_opened",
78 "user_classifier_default_interval_ntp_opened", 80 24},
79 "user_classifier_default_interval_suggestions_shown", 81 {&kArticleSuggestionsFeature,
80 "user_classifier_default_interval_suggestions_used"}; 82 "user_classifier_default_interval_suggestions_shown", 48},
83 {&kArticleSuggestionsFeature,
84 "user_classifier_default_interval_suggestions_used", 96}};
81 85
82 static_assert(arraysize(kMetrics) == 86 static_assert(arraysize(kMetrics) ==
83 static_cast<int>(UserClassifier::Metric::COUNT) && 87 static_cast<int>(UserClassifier::Metric::COUNT) &&
84 arraysize(kMetricKeys) == 88 arraysize(kMetricKeys) ==
85 static_cast<int>(UserClassifier::Metric::COUNT) && 89 static_cast<int>(UserClassifier::Metric::COUNT) &&
86 arraysize(kLastTimeKeys) == 90 arraysize(kLastTimeKeys) ==
87 static_cast<int>(UserClassifier::Metric::COUNT) && 91 static_cast<int>(UserClassifier::Metric::COUNT) &&
88 arraysize(kInitialHoursBetweenEvents) ==
89 static_cast<int>(UserClassifier::Metric::COUNT) &&
90 arraysize(kInitialHoursBetweenEventsParams) == 92 arraysize(kInitialHoursBetweenEventsParams) ==
91 static_cast<int>(UserClassifier::Metric::COUNT), 93 static_cast<int>(UserClassifier::Metric::COUNT),
92 "Fill in info for all metrics."); 94 "Fill in info for all metrics.");
93 95
94 // Computes the discount rate. 96 // Computes the discount rate.
95 double GetDiscountRatePerHour() { 97 double GetDiscountRatePerHour() {
96 double discount_rate_per_day = variations::GetVariationParamByFeatureAsDouble( 98 double discount_rate_per_day = kDiscountRatePerDayParam.Get();
97 kArticleSuggestionsFeature, kDiscountRatePerDayParam,
98 kDiscountRatePerDay);
99 // Check for illegal values. 99 // Check for illegal values.
100 if (discount_rate_per_day <= 0 || discount_rate_per_day >= 1) { 100 if (discount_rate_per_day <= 0 || discount_rate_per_day >= 1) {
101 DLOG(WARNING) << "Illegal value " << discount_rate_per_day 101 DLOG(WARNING) << "Illegal value " << discount_rate_per_day
102 << " for the parameter " << kDiscountRatePerDayParam 102 << " for the parameter " << kDiscountRatePerDayParam.name
103 << " (must be strictly between 0 and 1; the default " 103 << " (must be strictly between 0 and 1; the default "
104 << kDiscountRatePerDay << " is used, instead)."; 104 << kDiscountRatePerDay << " is used, instead).";
105 discount_rate_per_day = kDiscountRatePerDay; 105 discount_rate_per_day = kDiscountRatePerDay;
106 } 106 }
107 // Compute discount_rate_per_hour such that 107 // Compute discount_rate_per_hour such that
108 // discount_rate_per_day = 1 - e^{-discount_rate_per_hour * 24}. 108 // discount_rate_per_day = 1 - e^{-discount_rate_per_hour * 24}.
109 return std::log(1.0 / (1.0 - discount_rate_per_day)) / 24.0; 109 return std::log(1.0 / (1.0 - discount_rate_per_day)) / 24.0;
110 } 110 }
111 111
112 double GetInitialHoursBetweenEvents(UserClassifier::Metric metric) { 112 double GetInitialHoursBetweenEvents(UserClassifier::Metric metric) {
113 return variations::GetVariationParamByFeatureAsDouble( 113 return kInitialHoursBetweenEventsParams[static_cast<int>(metric)].Get();
114 kArticleSuggestionsFeature,
115 kInitialHoursBetweenEventsParams[static_cast<int>(metric)],
116 kInitialHoursBetweenEvents[static_cast<int>(metric)]);
117 }
118
119 double GetMinHours() {
120 return variations::GetVariationParamByFeatureAsDouble(
121 kArticleSuggestionsFeature, kMinHoursParam, kMinHours);
122 }
123
124 double GetMaxHours() {
125 return variations::GetVariationParamByFeatureAsDouble(
126 kArticleSuggestionsFeature, kMaxHoursParam, kMaxHours);
127 } 114 }
128 115
129 // Returns the new value of the metric using its |old_value|, assuming 116 // Returns the new value of the metric using its |old_value|, assuming
130 // |hours_since_last_time| hours have passed since it was last discounted. 117 // |hours_since_last_time| hours have passed since it was last discounted.
131 double DiscountMetric(double old_value, 118 double DiscountMetric(double old_value,
132 double hours_since_last_time, 119 double hours_since_last_time,
133 double discount_rate_per_hour) { 120 double discount_rate_per_hour) {
134 // Compute the new discounted average according to the formula 121 // Compute the new discounted average according to the formula
135 // avg_events := e^{-discount_rate_per_hour * hours_since} * avg_events 122 // avg_events := e^{-discount_rate_per_hour * hours_since} * avg_events
136 return std::exp(-discount_rate_per_hour * hours_since_last_time) * old_value; 123 return std::exp(-discount_rate_per_hour * hours_since_last_time) * old_value;
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
179 return 1.0 / (1.0 - std::exp(-discount_rate_per_hour * estimate_hours)); 166 return 1.0 / (1.0 - std::exp(-discount_rate_per_hour * estimate_hours));
180 } 167 }
181 168
182 } // namespace 169 } // namespace
183 170
184 UserClassifier::UserClassifier(PrefService* pref_service, 171 UserClassifier::UserClassifier(PrefService* pref_service,
185 std::unique_ptr<base::Clock> clock) 172 std::unique_ptr<base::Clock> clock)
186 : pref_service_(pref_service), 173 : pref_service_(pref_service),
187 clock_(std::move(clock)), 174 clock_(std::move(clock)),
188 discount_rate_per_hour_(GetDiscountRatePerHour()), 175 discount_rate_per_hour_(GetDiscountRatePerHour()),
189 min_hours_(GetMinHours()), 176 min_hours_(kMinHoursParam.Get()),
190 max_hours_(GetMaxHours()), 177 max_hours_(kMaxHoursParam.Get()),
191 active_consumer_clicks_at_least_once_per_hours_( 178 active_consumer_clicks_at_least_once_per_hours_(
192 variations::GetVariationParamByFeatureAsDouble( 179 kActiveConsumerClicksAtLeastOncePerHoursParam.Get()),
193 kArticleSuggestionsFeature,
194 kActiveConsumerClicksAtLeastOncePerHoursParam,
195 kActiveConsumerClicksAtLeastOncePerHours)),
196 rare_user_opens_ntp_at_most_once_per_hours_( 180 rare_user_opens_ntp_at_most_once_per_hours_(
197 variations::GetVariationParamByFeatureAsDouble( 181 kRareUserOpensNTPAtMostOncePerHoursParam.Get()) {
198 kArticleSuggestionsFeature,
199 kRareUserOpensNTPAtMostOncePerHoursParam,
200 kRareUserOpensNTPAtMostOncePerHours)) {
201 // The pref_service_ can be null in tests. 182 // The pref_service_ can be null in tests.
202 if (!pref_service_) { 183 if (!pref_service_) {
203 return; 184 return;
204 } 185 }
205 186
206 // TODO(jkrcal): Store the current discount rate per hour into prefs. If it 187 // TODO(jkrcal): Store the current discount rate per hour into prefs. If it
207 // differs from the previous value, rescale the metric values so that the 188 // differs from the previous value, rescale the metric values so that the
208 // expectation does not change abruptly! 189 // expectation does not change abruptly!
209 190
210 // Initialize the prefs storing the last time: the counter has just started! 191 // Initialize the prefs storing the last time: the counter has just started!
211 for (const Metric metric : kMetrics) { 192 for (const Metric metric : kMetrics) {
212 if (!HasLastTime(metric)) { 193 if (!HasLastTime(metric)) {
213 SetLastTimeToNow(metric); 194 SetLastTimeToNow(metric);
214 } 195 }
215 } 196 }
216 } 197 }
217 198
218 UserClassifier::~UserClassifier() = default; 199 UserClassifier::~UserClassifier() = default;
219 200
220 // static 201 // static
221 void UserClassifier::RegisterProfilePrefs(PrefRegistrySimple* registry) { 202 void UserClassifier::RegisterProfilePrefs(PrefRegistrySimple* registry) {
222 double discount_rate = GetDiscountRatePerHour(); 203 double discount_rate = GetDiscountRatePerHour();
223 double min_hours = GetMinHours(); 204 double min_hours = kMinHoursParam.Get();
224 double max_hours = GetMaxHours(); 205 double max_hours = kMaxHoursParam.Get();
225 206
226 for (Metric metric : kMetrics) { 207 for (Metric metric : kMetrics) {
227 double default_metric_value = GetMetricValueForEstimateHoursBetweenEvents( 208 double default_metric_value = GetMetricValueForEstimateHoursBetweenEvents(
228 GetInitialHoursBetweenEvents(metric), discount_rate, min_hours, 209 GetInitialHoursBetweenEvents(metric), discount_rate, min_hours,
229 max_hours); 210 max_hours);
230 registry->RegisterDoublePref(kMetricKeys[static_cast<int>(metric)], 211 registry->RegisterDoublePref(kMetricKeys[static_cast<int>(metric)],
231 default_metric_value); 212 default_metric_value);
232 registry->RegisterInt64Pref(kLastTimeKeys[static_cast<int>(metric)], 0); 213 registry->RegisterInt64Pref(kLastTimeKeys[static_cast<int>(metric)], 0);
233 } 214 }
234 } 215 }
235 216
236 void UserClassifier::OnEvent(Metric metric) { 217 void UserClassifier::OnEvent(Metric metric) {
237 DCHECK_NE(metric, Metric::COUNT); 218 DCHECK_NE(metric, Metric::COUNT);
238 double metric_value = UpdateMetricOnEvent(metric); 219 double metric_value = UpdateMetricOnEvent(metric);
239 220
240 double avg = GetEstimateHoursBetweenEvents( 221 double avg = GetEstimateHoursBetweenEvents(
241 metric_value, discount_rate_per_hour_, min_hours_, max_hours_); 222 metric_value, discount_rate_per_hour_, min_hours_, max_hours_);
242 // We use kMaxHours as the max value below as the maximum value for the 223 // We use kMaxHours as the max value below as the maximum value for the
243 // histograms must be constant. 224 // histograms must be constant.
244 switch (metric) { 225 switch (metric) {
245 case Metric::NTP_OPENED: 226 case Metric::NTP_OPENED:
246 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToOpenNTP, avg, 1, 227 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToOpenNTP, avg, 1,
247 kMaxHours, 50); 228 kMaxHoursParam.default_value, 50);
248 break; 229 break;
249 case Metric::SUGGESTIONS_SHOWN: 230 case Metric::SUGGESTIONS_SHOWN:
250 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToShowSuggestions, avg, 231 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToShowSuggestions, avg,
251 1, kMaxHours, 50); 232 1, kMaxHoursParam.default_value, 50);
252 break; 233 break;
253 case Metric::SUGGESTIONS_USED: 234 case Metric::SUGGESTIONS_USED:
254 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToUseSuggestions, avg, 235 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToUseSuggestions, avg,
255 1, kMaxHours, 50); 236 1, kMaxHoursParam.default_value, 50);
256 break; 237 break;
257 case Metric::COUNT: 238 case Metric::COUNT:
258 NOTREACHED(); 239 NOTREACHED();
259 break; 240 break;
260 } 241 }
261 } 242 }
262 243
263 double UserClassifier::GetEstimatedAvgTime(Metric metric) const { 244 double UserClassifier::GetEstimatedAvgTime(Metric metric) const {
264 DCHECK_NE(metric, Metric::COUNT); 245 DCHECK_NE(metric, Metric::COUNT);
265 double metric_value = GetUpToDateMetricValue(metric); 246 double metric_value = GetUpToDateMetricValue(metric);
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
375 356
376 void UserClassifier::SetMetricValue(Metric metric, double metric_value) { 357 void UserClassifier::SetMetricValue(Metric metric, double metric_value) {
377 pref_service_->SetDouble(kMetricKeys[static_cast<int>(metric)], metric_value); 358 pref_service_->SetDouble(kMetricKeys[static_cast<int>(metric)], metric_value);
378 } 359 }
379 360
380 void UserClassifier::ClearMetricValue(Metric metric) { 361 void UserClassifier::ClearMetricValue(Metric metric) {
381 pref_service_->ClearPref(kMetricKeys[static_cast<int>(metric)]); 362 pref_service_->ClearPref(kMetricKeys[static_cast<int>(metric)]);
382 } 363 }
383 364
384 } // namespace ntp_snippets 365 } // namespace ntp_snippets
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698