OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "components/ntp_snippets/user_classifier.h" |
| 6 |
| 7 #include <float.h> |
| 8 |
| 9 #include <algorithm> |
| 10 #include <string> |
| 11 |
| 12 #include "base/metrics/histogram_macros.h" |
| 13 #include "base/strings/string_number_conversions.h" |
| 14 #include "components/ntp_snippets/pref_names.h" |
| 15 #include "components/prefs/pref_registry_simple.h" |
| 16 #include "components/prefs/pref_service.h" |
| 17 |
| 18 namespace { |
| 19 |
| 20 // TODO(jkrcal): Make all of this configurable via variations_service. |
| 21 |
| 22 // The discount factor for computing the discounted-average metrics. Must be |
| 23 // strictly larger than 0 and strictly smaller than 1! |
| 24 const double kDiscountFactorPerDay = 0.25; |
| 25 |
| 26 // Never consider any larger interval than this (so that extreme situations such |
| 27 // as losing your phone or going for a long offline vacation do not skew the |
| 28 // average too much). |
| 29 const double kMaxHours = 7 * 24; |
| 30 |
| 31 // Ignore events within |kMinHours| hours since the last event (|kMinHours| is |
| 32 // the length of the browsing session where subsequent events of the same type |
| 33 // do not count again). |
| 34 const double kMinHours = 0.5; |
| 35 |
| 36 const char kHistogramAverageHoursToOpenNTP[] = |
| 37 "NewTabPage.UserClassifier.AverageHoursToOpenNTP"; |
| 38 const char kHistogramAverageHoursToShowSuggestions[] = |
| 39 "NewTabPage.UserClassifier.AverageHoursToShowSuggestions"; |
| 40 const char kHistogramAverageHoursToUseSuggestions[] = |
| 41 "NewTabPage.UserClassifier.AverageHoursToUseSuggestions"; |
| 42 |
| 43 } // namespace |
| 44 |
| 45 namespace ntp_snippets { |
| 46 |
| 47 UserClassifier::UserClassifier(PrefService* pref_service) |
| 48 : pref_service_(pref_service), |
| 49 // Compute discount_rate_per_hour such that |
| 50 // kDiscountFactorPerDay = 1 - e^{-discount_rate_per_hour * 24}. |
| 51 discount_rate_per_hour_(std::log(1 / (1 - kDiscountFactorPerDay)) / 24) {} |
| 52 |
| 53 UserClassifier::~UserClassifier() {} |
| 54 |
| 55 // static |
| 56 void UserClassifier::RegisterProfilePrefs(PrefRegistrySimple* registry) { |
| 57 registry->RegisterDoublePref( |
| 58 prefs::kUserClassifierAverageNTPOpenedPerHour, 1); |
| 59 registry->RegisterDoublePref( |
| 60 prefs::kUserClassifierAverageSuggestionsShownPerHour, 1); |
| 61 registry->RegisterDoublePref( |
| 62 prefs::kUserClassifierAverageSuggestionsUsedPerHour, 1); |
| 63 |
| 64 registry->RegisterInt64Pref(prefs::kUserClassifierLastTimeToOpenNTP, 0); |
| 65 registry->RegisterInt64Pref(prefs::kUserClassifierLastTimeToShowSuggestions, |
| 66 0); |
| 67 registry->RegisterInt64Pref(prefs::kUserClassifierLastTimeToUseSuggestions, |
| 68 0); |
| 69 } |
| 70 |
| 71 void UserClassifier::OnNTPOpened() { |
| 72 UpdateMetricOnEvent(prefs::kUserClassifierAverageNTPOpenedPerHour, |
| 73 prefs::kUserClassifierLastTimeToOpenNTP); |
| 74 |
| 75 double avg = GetEstimateHoursBetweenEvents( |
| 76 prefs::kUserClassifierAverageNTPOpenedPerHour); |
| 77 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToOpenNTP, avg, 1, |
| 78 kMaxHours, 50); |
| 79 } |
| 80 |
| 81 void UserClassifier::OnSuggestionsShown() { |
| 82 UpdateMetricOnEvent(prefs::kUserClassifierAverageSuggestionsShownPerHour, |
| 83 prefs::kUserClassifierLastTimeToShowSuggestions); |
| 84 |
| 85 double avg = GetEstimateHoursBetweenEvents( |
| 86 prefs::kUserClassifierAverageSuggestionsShownPerHour); |
| 87 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToShowSuggestions, avg, 1, |
| 88 kMaxHours, 50); |
| 89 } |
| 90 |
| 91 void UserClassifier::OnSuggestionsUsed() { |
| 92 UpdateMetricOnEvent(prefs::kUserClassifierAverageSuggestionsUsedPerHour, |
| 93 prefs::kUserClassifierLastTimeToUseSuggestions); |
| 94 |
| 95 double avg = GetEstimateHoursBetweenEvents( |
| 96 prefs::kUserClassifierAverageSuggestionsUsedPerHour); |
| 97 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToUseSuggestions, avg, 1, |
| 98 kMaxHours, 50); |
| 99 } |
| 100 |
| 101 void UserClassifier::UpdateMetricOnEvent(const char* metric_pref_name, |
| 102 const char* last_time_pref_name) { |
| 103 if (!pref_service_) |
| 104 return; |
| 105 |
| 106 double hours_since_last_time = |
| 107 std::min(kMaxHours, GetHoursSinceLastTime(last_time_pref_name)); |
| 108 // Ignore events within the same "browsing session". |
| 109 if (hours_since_last_time < kMinHours) |
| 110 return; |
| 111 SetLastTimeToNow(last_time_pref_name); |
| 112 |
| 113 double avg_events_per_hour = pref_service_->GetDouble(metric_pref_name); |
| 114 // Compute and store the new discounted average according to the formula |
| 115 // avg_events := 1 + e^{-discount_rate_per_hour * hours_since} * avg_events. |
| 116 double new_avg_events_per_hour = |
| 117 1 + |
| 118 std::exp(discount_rate_per_hour_ * hours_since_last_time) * |
| 119 avg_events_per_hour; |
| 120 pref_service_->SetDouble(metric_pref_name, new_avg_events_per_hour); |
| 121 } |
| 122 |
| 123 double UserClassifier::GetEstimateHoursBetweenEvents( |
| 124 const char* metric_pref_name) { |
| 125 double avg_events_per_hour = pref_service_->GetDouble(metric_pref_name); |
| 126 |
| 127 // Right after the first update, the metric is equal to 1. |
| 128 if (avg_events_per_hour <= 1) |
| 129 return kMaxHours; |
| 130 |
| 131 // This is the estimate with the assumption that last event happened right |
| 132 // now and the system is in the steady-state. Solve estimate_hours in the |
| 133 // steady-state equation: |
| 134 // avg_events = 1 + e^{-discount_rate * estimate_hours} * avg_events. |
| 135 return std::min(kMaxHours, |
| 136 std::log(avg_events_per_hour / (avg_events_per_hour - 1)) / |
| 137 discount_rate_per_hour_); |
| 138 } |
| 139 |
| 140 double UserClassifier::GetHoursSinceLastTime( |
| 141 const char* last_time_pref_name) { |
| 142 if (!pref_service_->HasPrefPath(last_time_pref_name)) |
| 143 return DBL_MAX; |
| 144 |
| 145 base::TimeDelta since_last_time = |
| 146 base::Time::Now() - base::Time::FromInternalValue( |
| 147 pref_service_->GetInt64(last_time_pref_name)); |
| 148 return since_last_time.InSecondsF() / 3600; |
| 149 } |
| 150 |
| 151 void UserClassifier::SetLastTimeToNow(const char* last_time_pref_name) { |
| 152 pref_service_->SetInt64(last_time_pref_name, |
| 153 base::Time::Now().ToInternalValue()); |
| 154 } |
| 155 |
| 156 } // namespace ntp_snippets |
OLD | NEW |