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

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

Issue 2804633003: Add base::FeatureParam<> struct (Closed)
Patch Set: rebase Created 3 years, 3 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 = 96; 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", 96};
47 49
48 // The previous value in production was 72, i.e. 3 days. The new value is a 50 constexpr base::FeatureParam<double> kRareUserOpensNTPAtMostOncePerHoursParam{
49 // cautios shift in the direction we want (having slightly more rare users). 51 &kArticleSuggestionsFeature,
50 const double kRareUserOpensNTPAtMostOncePerHours = 66; 52 "user_classifier_rare_user_opens_ntp_at_most_once_per_hours", 66};
51 const char kRareUserOpensNTPAtMostOncePerHoursParam[] =
52 "user_classifier_rare_user_opens_ntp_at_most_once_per_hours";
53 53
54 // Histograms for logging the estimated average hours to next event. 54 // Histograms for logging the estimated average hours to next event.
55 const char kHistogramAverageHoursToOpenNTP[] = 55 const char kHistogramAverageHoursToOpenNTP[] =
56 "NewTabPage.UserClassifier.AverageHoursToOpenNTP"; 56 "NewTabPage.UserClassifier.AverageHoursToOpenNTP";
57 const char kHistogramAverageHoursToShowSuggestions[] = 57 const char kHistogramAverageHoursToShowSuggestions[] =
58 "NewTabPage.UserClassifier.AverageHoursToShowSuggestions"; 58 "NewTabPage.UserClassifier.AverageHoursToShowSuggestions";
59 const char kHistogramAverageHoursToUseSuggestions[] = 59 const char kHistogramAverageHoursToUseSuggestions[] =
60 "NewTabPage.UserClassifier.AverageHoursToUseSuggestions"; 60 "NewTabPage.UserClassifier.AverageHoursToUseSuggestions";
61 61
62 // The enum used for iteration. 62 // The enum used for iteration.
63 const UserClassifier::Metric kMetrics[] = { 63 const UserClassifier::Metric kMetrics[] = {
64 UserClassifier::Metric::NTP_OPENED, 64 UserClassifier::Metric::NTP_OPENED,
65 UserClassifier::Metric::SUGGESTIONS_SHOWN, 65 UserClassifier::Metric::SUGGESTIONS_SHOWN,
66 UserClassifier::Metric::SUGGESTIONS_USED}; 66 UserClassifier::Metric::SUGGESTIONS_USED};
67 67
68 // The summary of the prefs. 68 // The summary of the prefs.
69 const char* kMetricKeys[] = { 69 const char* kMetricKeys[] = {
70 prefs::kUserClassifierAverageNTPOpenedPerHour, 70 prefs::kUserClassifierAverageNTPOpenedPerHour,
71 prefs::kUserClassifierAverageSuggestionsShownPerHour, 71 prefs::kUserClassifierAverageSuggestionsShownPerHour,
72 prefs::kUserClassifierAverageSuggestionsUsedPerHour}; 72 prefs::kUserClassifierAverageSuggestionsUsedPerHour};
73 const char* kLastTimeKeys[] = {prefs::kUserClassifierLastTimeToOpenNTP, 73 const char* kLastTimeKeys[] = {prefs::kUserClassifierLastTimeToOpenNTP,
74 prefs::kUserClassifierLastTimeToShowSuggestions, 74 prefs::kUserClassifierLastTimeToShowSuggestions,
75 prefs::kUserClassifierLastTimeToUseSuggestions}; 75 prefs::kUserClassifierLastTimeToUseSuggestions};
76 76
77 // Default lengths of the intervals for new users for the metrics. 77 // Default lengths of the intervals for new users for the metrics.
78 const double kInitialHoursBetweenEvents[] = {24, 48, 120}; 78 constexpr base::FeatureParam<double> kInitialHoursBetweenEventsParams[] = {
79 const char* kInitialHoursBetweenEventsParams[] = { 79 {&kArticleSuggestionsFeature, "user_classifier_default_interval_ntp_opened",
80 "user_classifier_default_interval_ntp_opened", 80 24},
81 "user_classifier_default_interval_suggestions_shown", 81 {&kArticleSuggestionsFeature,
82 "user_classifier_default_interval_suggestions_used"}; 82 "user_classifier_default_interval_suggestions_shown", 48},
83 {&kArticleSuggestionsFeature,
84 "user_classifier_default_interval_suggestions_used", 120}};
83 85
84 static_assert(arraysize(kMetrics) == 86 static_assert(arraysize(kMetrics) ==
85 static_cast<int>(UserClassifier::Metric::COUNT) && 87 static_cast<int>(UserClassifier::Metric::COUNT) &&
86 arraysize(kMetricKeys) == 88 arraysize(kMetricKeys) ==
87 static_cast<int>(UserClassifier::Metric::COUNT) && 89 static_cast<int>(UserClassifier::Metric::COUNT) &&
88 arraysize(kLastTimeKeys) == 90 arraysize(kLastTimeKeys) ==
89 static_cast<int>(UserClassifier::Metric::COUNT) && 91 static_cast<int>(UserClassifier::Metric::COUNT) &&
90 arraysize(kInitialHoursBetweenEvents) ==
91 static_cast<int>(UserClassifier::Metric::COUNT) &&
92 arraysize(kInitialHoursBetweenEventsParams) == 92 arraysize(kInitialHoursBetweenEventsParams) ==
93 static_cast<int>(UserClassifier::Metric::COUNT), 93 static_cast<int>(UserClassifier::Metric::COUNT),
94 "Fill in info for all metrics."); 94 "Fill in info for all metrics.");
95 95
96 // Computes the discount rate. 96 // Computes the discount rate.
97 double GetDiscountRatePerHour() { 97 double GetDiscountRatePerHour() {
98 double discount_rate_per_day = variations::GetVariationParamByFeatureAsDouble( 98 double discount_rate_per_day = kDiscountRatePerDayParam.Get();
99 kArticleSuggestionsFeature, kDiscountRatePerDayParam,
100 kDiscountRatePerDay);
101 // Check for illegal values. 99 // Check for illegal values.
102 if (discount_rate_per_day <= 0 || discount_rate_per_day >= 1) { 100 if (discount_rate_per_day <= 0 || discount_rate_per_day >= 1) {
103 DLOG(WARNING) << "Illegal value " << discount_rate_per_day 101 DLOG(WARNING) << "Illegal value " << discount_rate_per_day
104 << " for the parameter " << kDiscountRatePerDayParam 102 << " for the parameter " << kDiscountRatePerDayParam.name
105 << " (must be strictly between 0 and 1; the default " 103 << " (must be strictly between 0 and 1; the default "
106 << kDiscountRatePerDay << " is used, instead)."; 104 << kDiscountRatePerDay << " is used, instead).";
107 discount_rate_per_day = kDiscountRatePerDay; 105 discount_rate_per_day = kDiscountRatePerDay;
108 } 106 }
109 // Compute discount_rate_per_hour such that 107 // Compute discount_rate_per_hour such that
110 // discount_rate_per_day = 1 - e^{-discount_rate_per_hour * 24}. 108 // discount_rate_per_day = 1 - e^{-discount_rate_per_hour * 24}.
111 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;
112 } 110 }
113 111
114 double GetInitialHoursBetweenEvents(UserClassifier::Metric metric) { 112 double GetInitialHoursBetweenEvents(UserClassifier::Metric metric) {
115 return variations::GetVariationParamByFeatureAsDouble( 113 return kInitialHoursBetweenEventsParams[static_cast<int>(metric)].Get();
116 kArticleSuggestionsFeature,
117 kInitialHoursBetweenEventsParams[static_cast<int>(metric)],
118 kInitialHoursBetweenEvents[static_cast<int>(metric)]);
119 }
120
121 double GetMinHours() {
122 return variations::GetVariationParamByFeatureAsDouble(
123 kArticleSuggestionsFeature, kMinHoursParam, kMinHours);
124 }
125
126 double GetMaxHours() {
127 return variations::GetVariationParamByFeatureAsDouble(
128 kArticleSuggestionsFeature, kMaxHoursParam, kMaxHours);
129 } 114 }
130 115
131 // 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
132 // |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.
133 double DiscountMetric(double old_value, 118 double DiscountMetric(double old_value,
134 double hours_since_last_time, 119 double hours_since_last_time,
135 double discount_rate_per_hour) { 120 double discount_rate_per_hour) {
136 // Compute the new discounted average according to the formula 121 // Compute the new discounted average according to the formula
137 // avg_events := e^{-discount_rate_per_hour * hours_since} * avg_events 122 // avg_events := e^{-discount_rate_per_hour * hours_since} * avg_events
138 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
181 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));
182 } 167 }
183 168
184 } // namespace 169 } // namespace
185 170
186 UserClassifier::UserClassifier(PrefService* pref_service, 171 UserClassifier::UserClassifier(PrefService* pref_service,
187 std::unique_ptr<base::Clock> clock) 172 std::unique_ptr<base::Clock> clock)
188 : pref_service_(pref_service), 173 : pref_service_(pref_service),
189 clock_(std::move(clock)), 174 clock_(std::move(clock)),
190 discount_rate_per_hour_(GetDiscountRatePerHour()), 175 discount_rate_per_hour_(GetDiscountRatePerHour()),
191 min_hours_(GetMinHours()), 176 min_hours_(kMinHoursParam.Get()),
192 max_hours_(GetMaxHours()), 177 max_hours_(kMaxHoursParam.Get()),
193 active_consumer_clicks_at_least_once_per_hours_( 178 active_consumer_clicks_at_least_once_per_hours_(
194 variations::GetVariationParamByFeatureAsDouble( 179 kActiveConsumerClicksAtLeastOncePerHoursParam.Get()),
195 kArticleSuggestionsFeature,
196 kActiveConsumerClicksAtLeastOncePerHoursParam,
197 kActiveConsumerClicksAtLeastOncePerHours)),
198 rare_user_opens_ntp_at_most_once_per_hours_( 180 rare_user_opens_ntp_at_most_once_per_hours_(
199 variations::GetVariationParamByFeatureAsDouble( 181 kRareUserOpensNTPAtMostOncePerHoursParam.Get()) {
200 kArticleSuggestionsFeature,
201 kRareUserOpensNTPAtMostOncePerHoursParam,
202 kRareUserOpensNTPAtMostOncePerHours)) {
203 // The pref_service_ can be null in tests. 182 // The pref_service_ can be null in tests.
204 if (!pref_service_) { 183 if (!pref_service_) {
205 return; 184 return;
206 } 185 }
207 186
208 // 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
209 // 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
210 // expectation does not change abruptly! 189 // expectation does not change abruptly!
211 190
212 // 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!
213 for (const Metric metric : kMetrics) { 192 for (const Metric metric : kMetrics) {
214 if (!HasLastTime(metric)) { 193 if (!HasLastTime(metric)) {
215 SetLastTimeToNow(metric); 194 SetLastTimeToNow(metric);
216 } 195 }
217 } 196 }
218 } 197 }
219 198
220 UserClassifier::~UserClassifier() = default; 199 UserClassifier::~UserClassifier() = default;
221 200
222 // static 201 // static
223 void UserClassifier::RegisterProfilePrefs(PrefRegistrySimple* registry) { 202 void UserClassifier::RegisterProfilePrefs(PrefRegistrySimple* registry) {
224 double discount_rate = GetDiscountRatePerHour(); 203 double discount_rate = GetDiscountRatePerHour();
225 double min_hours = GetMinHours(); 204 double min_hours = kMinHoursParam.Get();
226 double max_hours = GetMaxHours(); 205 double max_hours = kMaxHoursParam.Get();
227 206
228 for (Metric metric : kMetrics) { 207 for (Metric metric : kMetrics) {
229 double default_metric_value = GetMetricValueForEstimateHoursBetweenEvents( 208 double default_metric_value = GetMetricValueForEstimateHoursBetweenEvents(
230 GetInitialHoursBetweenEvents(metric), discount_rate, min_hours, 209 GetInitialHoursBetweenEvents(metric), discount_rate, min_hours,
231 max_hours); 210 max_hours);
232 registry->RegisterDoublePref(kMetricKeys[static_cast<int>(metric)], 211 registry->RegisterDoublePref(kMetricKeys[static_cast<int>(metric)],
233 default_metric_value); 212 default_metric_value);
234 registry->RegisterInt64Pref(kLastTimeKeys[static_cast<int>(metric)], 0); 213 registry->RegisterInt64Pref(kLastTimeKeys[static_cast<int>(metric)], 0);
235 } 214 }
236 } 215 }
237 216
238 void UserClassifier::OnEvent(Metric metric) { 217 void UserClassifier::OnEvent(Metric metric) {
239 DCHECK_NE(metric, Metric::COUNT); 218 DCHECK_NE(metric, Metric::COUNT);
240 double metric_value = UpdateMetricOnEvent(metric); 219 double metric_value = UpdateMetricOnEvent(metric);
241 220
242 double avg = GetEstimateHoursBetweenEvents( 221 double avg = GetEstimateHoursBetweenEvents(
243 metric_value, discount_rate_per_hour_, min_hours_, max_hours_); 222 metric_value, discount_rate_per_hour_, min_hours_, max_hours_);
244 // 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
245 // histograms must be constant. 224 // histograms must be constant.
246 switch (metric) { 225 switch (metric) {
247 case Metric::NTP_OPENED: 226 case Metric::NTP_OPENED:
248 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToOpenNTP, avg, 1, 227 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToOpenNTP, avg, 1,
249 kMaxHours, 50); 228 kMaxHoursParam.default_value, 50);
250 break; 229 break;
251 case Metric::SUGGESTIONS_SHOWN: 230 case Metric::SUGGESTIONS_SHOWN:
252 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToShowSuggestions, avg, 231 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToShowSuggestions, avg,
253 1, kMaxHours, 50); 232 1, kMaxHoursParam.default_value, 50);
254 break; 233 break;
255 case Metric::SUGGESTIONS_USED: 234 case Metric::SUGGESTIONS_USED:
256 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToUseSuggestions, avg, 235 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToUseSuggestions, avg,
257 1, kMaxHours, 50); 236 1, kMaxHoursParam.default_value, 50);
258 break; 237 break;
259 case Metric::COUNT: 238 case Metric::COUNT:
260 NOTREACHED(); 239 NOTREACHED();
261 break; 240 break;
262 } 241 }
263 } 242 }
264 243
265 double UserClassifier::GetEstimatedAvgTime(Metric metric) const { 244 double UserClassifier::GetEstimatedAvgTime(Metric metric) const {
266 DCHECK_NE(metric, Metric::COUNT); 245 DCHECK_NE(metric, Metric::COUNT);
267 double metric_value = GetUpToDateMetricValue(metric); 246 double metric_value = GetUpToDateMetricValue(metric);
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
377 356
378 void UserClassifier::SetMetricValue(Metric metric, double metric_value) { 357 void UserClassifier::SetMetricValue(Metric metric, double metric_value) {
379 pref_service_->SetDouble(kMetricKeys[static_cast<int>(metric)], metric_value); 358 pref_service_->SetDouble(kMetricKeys[static_cast<int>(metric)], metric_value);
380 } 359 }
381 360
382 void UserClassifier::ClearMetricValue(Metric metric) { 361 void UserClassifier::ClearMetricValue(Metric metric) {
383 pref_service_->ClearPref(kMetricKeys[static_cast<int>(metric)]); 362 pref_service_->ClearPref(kMetricKeys[static_cast<int>(metric)]);
384 } 363 }
385 364
386 } // namespace ntp_snippets 365 } // namespace ntp_snippets
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698