| 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 |