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 |