Chromium Code Reviews| 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; | |
|
Bernhard Bauer
2016/09/07 21:31:00
Nit: Space before and after multiplication operato
jkrcal
2016/09/08 05:45:24
Done.
| |
| 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 the discount_rate_per_hour such that | |
| 50 // e^{-discount_rate_per_hour * 24} = (1 - kDiscountFactorPerDay). | |
| 51 discount_rate_per_hour_ = std::log(1 / (1 - kDiscountFactorPerDay)) / 24; | |
| 52 } | |
| 53 | |
| 54 UserClassifier::~UserClassifier() {} | |
| 55 | |
| 56 // static | |
| 57 void UserClassifier::RegisterProfilePrefs(PrefRegistrySimple* registry) { | |
| 58 registry->RegisterDoublePref( | |
| 59 prefs::kUserClassifierAverageNTPOpenedPerHour, 1); | |
| 60 registry->RegisterDoublePref( | |
| 61 prefs::kUserClassifierAverageSuggestionsShownPerHour, 1); | |
| 62 registry->RegisterDoublePref( | |
| 63 prefs::kUserClassifierAverageSuggestionsUsedPerHour, 1); | |
| 64 | |
| 65 registry->RegisterInt64Pref(prefs::kUserClassifierLastTimeToOpenNTP, 0); | |
| 66 registry->RegisterInt64Pref(prefs::kUserClassifierLastTimeToShowSuggestions, | |
| 67 0); | |
| 68 registry->RegisterInt64Pref(prefs::kUserClassifierLastTimeToUseSuggestions, | |
| 69 0); | |
| 70 } | |
| 71 | |
| 72 void UserClassifier::OnNTPOpened() { | |
| 73 UpdateMetricOnEvent(prefs::kUserClassifierAverageNTPOpenedPerHour, | |
| 74 prefs::kUserClassifierLastTimeToOpenNTP); | |
| 75 | |
| 76 double avg = GetEstimateHoursBetweenEvents( | |
| 77 prefs::kUserClassifierAverageNTPOpenedPerHour); | |
| 78 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToOpenNTP, avg, 1, | |
| 79 kMaxHours, 50); | |
| 80 } | |
| 81 | |
| 82 void UserClassifier::OnSuggestionsShown() { | |
| 83 UpdateMetricOnEvent(prefs::kUserClassifierAverageSuggestionsShownPerHour, | |
| 84 prefs::kUserClassifierLastTimeToShowSuggestions); | |
| 85 | |
| 86 double avg = GetEstimateHoursBetweenEvents( | |
| 87 prefs::kUserClassifierAverageSuggestionsShownPerHour); | |
| 88 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToShowSuggestions, avg, 1, | |
| 89 kMaxHours, 50); | |
| 90 } | |
| 91 | |
| 92 void UserClassifier::OnSuggestionsUsed() { | |
| 93 UpdateMetricOnEvent(prefs::kUserClassifierAverageSuggestionsUsedPerHour, | |
| 94 prefs::kUserClassifierLastTimeToUseSuggestions); | |
| 95 | |
| 96 double avg = GetEstimateHoursBetweenEvents( | |
| 97 prefs::kUserClassifierAverageSuggestionsUsedPerHour); | |
| 98 UMA_HISTOGRAM_CUSTOM_COUNTS(kHistogramAverageHoursToUseSuggestions, avg, 1, | |
| 99 kMaxHours, 50); | |
| 100 } | |
| 101 | |
| 102 void UserClassifier::UpdateMetricOnEvent(const char* metric_pref_name, | |
| 103 const char* last_time_pref_name) { | |
| 104 if (!pref_service_) | |
| 105 return; | |
| 106 | |
| 107 double hours_since_last_time = | |
| 108 std::min(kMaxHours, GetHoursSinceLastTime(last_time_pref_name)); | |
| 109 // Ignore events within the same "browsing session". | |
| 110 if (hours_since_last_time < kMinHours) | |
| 111 return; | |
| 112 SetLastTimeToNow(last_time_pref_name); | |
| 113 | |
| 114 double avg_events_per_hour = pref_service_->GetDouble(metric_pref_name); | |
| 115 // Compute and store the new discounted average according to the formula | |
| 116 // avg_events := 1 + e^{-discount_rate_per_hour * hours_since} * avg_events. | |
| 117 avg_events_per_hour = | |
|
Bernhard Bauer
2016/09/07 21:31:00
Maybe use a new variable |new_avg_events_per_hour|
jkrcal
2016/09/08 05:45:24
Done.
| |
| 118 1 + | |
| 119 std::exp(discount_rate_per_hour_ * hours_since_last_time) * | |
| 120 avg_events_per_hour; | |
| 121 pref_service_->SetDouble(metric_pref_name, avg_events_per_hour); | |
| 122 } | |
| 123 | |
| 124 double UserClassifier::GetEstimateHoursBetweenEvents( | |
| 125 const char* metric_pref_name) { | |
| 126 double avg_events_per_hour = pref_service_->GetDouble(metric_pref_name); | |
| 127 | |
| 128 if (avg_events_per_hour > 1) { | |
|
Bernhard Bauer
2016/09/07 21:31:00
Flip the condition and return kMaxHours in the oth
jkrcal
2016/09/08 05:45:24
Done. Yes, it can be equal to 1.
| |
| 129 // This is the estimate with the assumption that last event happened right | |
| 130 // now (for the case when getting the estimate right after the event). | |
| 131 // Solve estimate_hours in the equation: | |
| 132 // avg_events = 1 + e^{-discount_rate * estimate_hours} * avg_events. | |
| 133 return std::min(kMaxHours, | |
| 134 std::log(avg_events_per_hour / (avg_events_per_hour - 1)) / | |
| 135 discount_rate_per_hour_); | |
| 136 } else { | |
| 137 return kMaxHours; | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 double UserClassifier::GetHoursSinceLastTime( | |
| 142 const char* last_time_pref_name) { | |
| 143 if (!pref_service_->HasPrefPath(last_time_pref_name)) | |
| 144 return DBL_MAX; | |
| 145 | |
| 146 base::TimeDelta since_last_time = | |
| 147 base::Time::Now() - base::Time::FromInternalValue( | |
| 148 pref_service_->GetInt64(last_time_pref_name)); | |
| 149 return since_last_time.InSecondsF() / 3600; | |
| 150 } | |
| 151 | |
| 152 void UserClassifier::SetLastTimeToNow(const char* last_time_pref_name) { | |
| 153 pref_service_->SetInt64(last_time_pref_name, | |
| 154 base::Time::Now().ToInternalValue()); | |
| 155 } | |
| 156 | |
| 157 } // namespace ntp_snippets | |
| OLD | NEW |