OLD | NEW |
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 |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
146 | 146 |
147 // Compute the number of hours between two events for the given metric value | 147 // Compute the number of hours between two events for the given metric value |
148 // assuming the events were equally distributed. | 148 // assuming the events were equally distributed. |
149 double GetEstimateHoursBetweenEvents(double metric_value, | 149 double GetEstimateHoursBetweenEvents(double metric_value, |
150 double discount_rate_per_hour, | 150 double discount_rate_per_hour, |
151 double min_hours, | 151 double min_hours, |
152 double max_hours) { | 152 double max_hours) { |
153 // The computation below is well-defined only for |metric_value| > 1 (log of | 153 // The computation below is well-defined only for |metric_value| > 1 (log of |
154 // negative value or division by zero). When |metric_value| -> 1, the estimate | 154 // negative value or division by zero). When |metric_value| -> 1, the estimate |
155 // below -> infinity, so max_hours is a natural result, here. | 155 // below -> infinity, so max_hours is a natural result, here. |
156 if (metric_value <= 1) | 156 if (metric_value <= 1) { |
157 return max_hours; | 157 return max_hours; |
| 158 } |
158 | 159 |
159 // This is the estimate with the assumption that last event happened right | 160 // This is the estimate with the assumption that last event happened right |
160 // now and the system is in the steady-state. Solve estimate_hours in the | 161 // now and the system is in the steady-state. Solve estimate_hours in the |
161 // steady-state equation: | 162 // steady-state equation: |
162 // metric_value = 1 + e^{-discount_rate * estimate_hours} * metric_value, | 163 // metric_value = 1 + e^{-discount_rate * estimate_hours} * metric_value, |
163 // i.e. | 164 // i.e. |
164 // -discount_rate * estimate_hours = log((metric_value - 1) / metric_value), | 165 // -discount_rate * estimate_hours = log((metric_value - 1) / metric_value), |
165 // discount_rate * estimate_hours = log(metric_value / (metric_value - 1)), | 166 // discount_rate * estimate_hours = log(metric_value / (metric_value - 1)), |
166 // estimate_hours = log(metric_value / (metric_value - 1)) / discount_rate. | 167 // estimate_hours = log(metric_value / (metric_value - 1)) / discount_rate. |
167 double estimate_hours = | 168 double estimate_hours = |
(...skipping 25 matching lines...) Expand all Loading... |
193 discount_rate_per_hour_(GetDiscountRatePerHour()), | 194 discount_rate_per_hour_(GetDiscountRatePerHour()), |
194 min_hours_(GetMinHours()), | 195 min_hours_(GetMinHours()), |
195 max_hours_(GetMaxHours()), | 196 max_hours_(GetMaxHours()), |
196 active_consumer_scrolls_at_least_once_per_hours_( | 197 active_consumer_scrolls_at_least_once_per_hours_( |
197 GetParamValue(kActiveConsumerScrollsAtLeastOncePerHoursParam, | 198 GetParamValue(kActiveConsumerScrollsAtLeastOncePerHoursParam, |
198 kActiveConsumerScrollsAtLeastOncePerHours)), | 199 kActiveConsumerScrollsAtLeastOncePerHours)), |
199 rare_user_opens_ntp_at_most_once_per_hours_( | 200 rare_user_opens_ntp_at_most_once_per_hours_( |
200 GetParamValue(kRareUserOpensNTPAtMostOncePerHoursParam, | 201 GetParamValue(kRareUserOpensNTPAtMostOncePerHoursParam, |
201 kRareUserOpensNTPAtMostOncePerHours)) { | 202 kRareUserOpensNTPAtMostOncePerHours)) { |
202 // The pref_service_ can be null in tests. | 203 // The pref_service_ can be null in tests. |
203 if (!pref_service_) | 204 if (!pref_service_) { |
204 return; | 205 return; |
| 206 } |
205 | 207 |
206 // TODO(jkrcal): Store the current discount rate per hour into prefs. If it | 208 // 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 | 209 // differs from the previous value, rescale the metric values so that the |
208 // expectation does not change abruptly! | 210 // expectation does not change abruptly! |
209 | 211 |
210 // Initialize the prefs storing the last time: the counter has just started! | 212 // Initialize the prefs storing the last time: the counter has just started! |
211 for (const Metric metric : kMetrics) { | 213 for (const Metric metric : kMetrics) { |
212 if (!HasLastTime(metric)) | 214 if (!HasLastTime(metric)) { |
213 SetLastTimeToNow(metric); | 215 SetLastTimeToNow(metric); |
| 216 } |
214 } | 217 } |
215 } | 218 } |
216 | 219 |
217 UserClassifier::~UserClassifier() = default; | 220 UserClassifier::~UserClassifier() = default; |
218 | 221 |
219 // static | 222 // static |
220 void UserClassifier::RegisterProfilePrefs(PrefRegistrySimple* registry) { | 223 void UserClassifier::RegisterProfilePrefs(PrefRegistrySimple* registry) { |
221 double discount_rate = GetDiscountRatePerHour(); | 224 double discount_rate = GetDiscountRatePerHour(); |
222 double min_hours = GetMinHours(); | 225 double min_hours = GetMinHours(); |
223 double max_hours = GetMaxHours(); | 226 double max_hours = GetMaxHours(); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 | 264 |
262 double UserClassifier::GetEstimatedAvgTime(Metric metric) const { | 265 double UserClassifier::GetEstimatedAvgTime(Metric metric) const { |
263 DCHECK_NE(metric, Metric::COUNT); | 266 DCHECK_NE(metric, Metric::COUNT); |
264 double metric_value = GetUpToDateMetricValue(metric); | 267 double metric_value = GetUpToDateMetricValue(metric); |
265 return GetEstimateHoursBetweenEvents(metric_value, discount_rate_per_hour_, | 268 return GetEstimateHoursBetweenEvents(metric_value, discount_rate_per_hour_, |
266 min_hours_, max_hours_); | 269 min_hours_, max_hours_); |
267 } | 270 } |
268 | 271 |
269 UserClassifier::UserClass UserClassifier::GetUserClass() const { | 272 UserClassifier::UserClass UserClassifier::GetUserClass() const { |
270 // The pref_service_ can be null in tests. | 273 // The pref_service_ can be null in tests. |
271 if (!pref_service_) | 274 if (!pref_service_) { |
272 return UserClass::ACTIVE_NTP_USER; | 275 return UserClass::ACTIVE_NTP_USER; |
| 276 } |
273 | 277 |
274 if (GetEstimatedAvgTime(Metric::NTP_OPENED) >= | 278 if (GetEstimatedAvgTime(Metric::NTP_OPENED) >= |
275 rare_user_opens_ntp_at_most_once_per_hours_) { | 279 rare_user_opens_ntp_at_most_once_per_hours_) { |
276 return UserClass::RARE_NTP_USER; | 280 return UserClass::RARE_NTP_USER; |
277 } | 281 } |
278 | 282 |
279 if (GetEstimatedAvgTime(Metric::SUGGESTIONS_SHOWN) <= | 283 if (GetEstimatedAvgTime(Metric::SUGGESTIONS_SHOWN) <= |
280 active_consumer_scrolls_at_least_once_per_hours_) { | 284 active_consumer_scrolls_at_least_once_per_hours_) { |
281 return UserClass::ACTIVE_SUGGESTIONS_CONSUMER; | 285 return UserClass::ACTIVE_SUGGESTIONS_CONSUMER; |
282 } | 286 } |
283 | 287 |
284 return UserClass::ACTIVE_NTP_USER; | 288 return UserClass::ACTIVE_NTP_USER; |
285 } | 289 } |
286 | 290 |
287 std::string UserClassifier::GetUserClassDescriptionForDebugging() const { | 291 std::string UserClassifier::GetUserClassDescriptionForDebugging() const { |
288 switch (GetUserClass()) { | 292 switch (GetUserClass()) { |
289 case UserClass::RARE_NTP_USER: | 293 case UserClass::RARE_NTP_USER: |
290 return "Rare user of the NTP"; | 294 return "Rare user of the NTP"; |
291 case UserClass::ACTIVE_NTP_USER: | 295 case UserClass::ACTIVE_NTP_USER: |
292 return "Active user of the NTP"; | 296 return "Active user of the NTP"; |
293 case UserClass::ACTIVE_SUGGESTIONS_CONSUMER: | 297 case UserClass::ACTIVE_SUGGESTIONS_CONSUMER: |
294 return "Active consumer of NTP suggestions"; | 298 return "Active consumer of NTP suggestions"; |
295 } | 299 } |
296 NOTREACHED(); | 300 NOTREACHED(); |
297 return std::string(); | 301 return std::string(); |
298 } | 302 } |
299 | 303 |
300 void UserClassifier::ClearClassificationForDebugging() { | 304 void UserClassifier::ClearClassificationForDebugging() { |
301 // The pref_service_ can be null in tests. | 305 // The pref_service_ can be null in tests. |
302 if (!pref_service_) | 306 if (!pref_service_) { |
303 return; | 307 return; |
| 308 } |
304 | 309 |
305 for (const Metric& metric : kMetrics) { | 310 for (const Metric& metric : kMetrics) { |
306 ClearMetricValue(metric); | 311 ClearMetricValue(metric); |
307 SetLastTimeToNow(metric); | 312 SetLastTimeToNow(metric); |
308 } | 313 } |
309 } | 314 } |
310 | 315 |
311 double UserClassifier::UpdateMetricOnEvent(Metric metric) { | 316 double UserClassifier::UpdateMetricOnEvent(Metric metric) { |
312 // The pref_service_ can be null in tests. | 317 // The pref_service_ can be null in tests. |
313 if (!pref_service_) | 318 if (!pref_service_) { |
314 return 0; | 319 return 0; |
| 320 } |
315 | 321 |
316 double hours_since_last_time = | 322 double hours_since_last_time = |
317 std::min(max_hours_, GetHoursSinceLastTime(metric)); | 323 std::min(max_hours_, GetHoursSinceLastTime(metric)); |
318 // Ignore events within the same "browsing session". | 324 // Ignore events within the same "browsing session". |
319 if (hours_since_last_time < min_hours_) | 325 if (hours_since_last_time < min_hours_) { |
320 return GetUpToDateMetricValue(metric); | 326 return GetUpToDateMetricValue(metric); |
| 327 } |
321 | 328 |
322 SetLastTimeToNow(metric); | 329 SetLastTimeToNow(metric); |
323 | 330 |
324 double metric_value = GetMetricValue(metric); | 331 double metric_value = GetMetricValue(metric); |
325 // Add 1 to the discounted metric as the event has happened right now. | 332 // Add 1 to the discounted metric as the event has happened right now. |
326 double new_metric_value = | 333 double new_metric_value = |
327 1 + DiscountMetric(metric_value, hours_since_last_time, | 334 1 + DiscountMetric(metric_value, hours_since_last_time, |
328 discount_rate_per_hour_); | 335 discount_rate_per_hour_); |
329 SetMetricValue(metric, new_metric_value); | 336 SetMetricValue(metric, new_metric_value); |
330 return new_metric_value; | 337 return new_metric_value; |
331 } | 338 } |
332 | 339 |
333 double UserClassifier::GetUpToDateMetricValue(Metric metric) const { | 340 double UserClassifier::GetUpToDateMetricValue(Metric metric) const { |
334 // The pref_service_ can be null in tests. | 341 // The pref_service_ can be null in tests. |
335 if (!pref_service_) | 342 if (!pref_service_) { |
336 return 0; | 343 return 0; |
| 344 } |
337 | 345 |
338 double hours_since_last_time = | 346 double hours_since_last_time = |
339 std::min(max_hours_, GetHoursSinceLastTime(metric)); | 347 std::min(max_hours_, GetHoursSinceLastTime(metric)); |
340 | 348 |
341 double metric_value = GetMetricValue(metric); | 349 double metric_value = GetMetricValue(metric); |
342 return DiscountMetric(metric_value, hours_since_last_time, | 350 return DiscountMetric(metric_value, hours_since_last_time, |
343 discount_rate_per_hour_); | 351 discount_rate_per_hour_); |
344 } | 352 } |
345 | 353 |
346 double UserClassifier::GetHoursSinceLastTime(Metric metric) const { | 354 double UserClassifier::GetHoursSinceLastTime(Metric metric) const { |
347 if (!HasLastTime(metric)) | 355 if (!HasLastTime(metric)) { |
348 return 0; | 356 return 0; |
| 357 } |
349 | 358 |
350 base::TimeDelta since_last_time = | 359 base::TimeDelta since_last_time = |
351 base::Time::Now() - base::Time::FromInternalValue(pref_service_->GetInt64( | 360 base::Time::Now() - base::Time::FromInternalValue(pref_service_->GetInt64( |
352 kLastTimeKeys[static_cast<int>(metric)])); | 361 kLastTimeKeys[static_cast<int>(metric)])); |
353 return since_last_time.InSecondsF() / 3600; | 362 return since_last_time.InSecondsF() / 3600; |
354 } | 363 } |
355 | 364 |
356 bool UserClassifier::HasLastTime(Metric metric) const { | 365 bool UserClassifier::HasLastTime(Metric metric) const { |
357 return pref_service_->HasPrefPath(kLastTimeKeys[static_cast<int>(metric)]); | 366 return pref_service_->HasPrefPath(kLastTimeKeys[static_cast<int>(metric)]); |
358 } | 367 } |
359 | 368 |
360 void UserClassifier::SetLastTimeToNow(Metric metric) { | 369 void UserClassifier::SetLastTimeToNow(Metric metric) { |
361 pref_service_->SetInt64(kLastTimeKeys[static_cast<int>(metric)], | 370 pref_service_->SetInt64(kLastTimeKeys[static_cast<int>(metric)], |
362 base::Time::Now().ToInternalValue()); | 371 base::Time::Now().ToInternalValue()); |
363 } | 372 } |
364 | 373 |
365 double UserClassifier::GetMetricValue(Metric metric) const { | 374 double UserClassifier::GetMetricValue(Metric metric) const { |
366 return pref_service_->GetDouble(kMetricKeys[static_cast<int>(metric)]); | 375 return pref_service_->GetDouble(kMetricKeys[static_cast<int>(metric)]); |
367 } | 376 } |
368 | 377 |
369 void UserClassifier::SetMetricValue(Metric metric, double metric_value) { | 378 void UserClassifier::SetMetricValue(Metric metric, double metric_value) { |
370 pref_service_->SetDouble(kMetricKeys[static_cast<int>(metric)], metric_value); | 379 pref_service_->SetDouble(kMetricKeys[static_cast<int>(metric)], metric_value); |
371 } | 380 } |
372 | 381 |
373 void UserClassifier::ClearMetricValue(Metric metric) { | 382 void UserClassifier::ClearMetricValue(Metric metric) { |
374 pref_service_->ClearPref(kMetricKeys[static_cast<int>(metric)]); | 383 pref_service_->ClearPref(kMetricKeys[static_cast<int>(metric)]); |
375 } | 384 } |
376 | 385 |
377 } // namespace ntp_snippets | 386 } // namespace ntp_snippets |
OLD | NEW |