Home | History | Annotate | Download | only in ranking
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings.intelligence.suggestions.ranking;
     18 
     19 import android.content.Context;
     20 import android.service.settings.suggestions.Suggestion;
     21 
     22 import java.util.ArrayList;
     23 import java.util.HashMap;
     24 import java.util.List;
     25 import java.util.Map;
     26 
     27 /**
     28  * Creates a set of interaction features (i.e., metrics) to represent each setting suggestion. These
     29  * features currently include normalized time from previous events (shown, dismissed and clicked)
     30  * for any particular suggestion and also counts of these events. These features are used as signals
     31  * to find the best ranking for suggestion items.
     32  * <p/>
     33  * Copied from packages/apps/Settings/src/.../dashboard/suggestions/SuggestionFeaturizer
     34  */
     35 public class SuggestionFeaturizer {
     36     // Key of the features used for ranking.
     37     public static final String FEATURE_IS_SHOWN = "is_shown";
     38     public static final String FEATURE_IS_DISMISSED = "is_dismissed";
     39     public static final String FEATURE_IS_CLICKED = "is_clicked";
     40     public static final String FEATURE_TIME_FROM_LAST_SHOWN = "time_from_last_shown";
     41     public static final String FEATURE_TIME_FROM_LAST_DISMISSED = "time_from_last_dismissed";
     42     public static final String FEATURE_TIME_FROM_LAST_CLICKED = "time_from_last_clicked";
     43     public static final String FEATURE_SHOWN_COUNT = "shown_count";
     44     public static final String FEATURE_DISMISSED_COUNT = "dismissed_count";
     45     public static final String FEATURE_CLICKED_COUNT = "clicked_count";
     46 
     47     // The following numbers are estimated from histograms.
     48     public static final double TIME_NORMALIZATION_FACTOR = 2e10;
     49     public static final double COUNT_NORMALIZATION_FACTOR = 500;
     50 
     51     private final SuggestionEventStore mEventStore;
     52 
     53     public SuggestionFeaturizer(Context context) {
     54         mEventStore = SuggestionEventStore.get(context);
     55     }
     56 
     57     /**
     58      * Extracts the features for each package name.
     59      *
     60      * @return A Map containing the features, keyed by the package names. Each map value contains
     61      * another map with key-value pairs of the features.
     62      */
     63     public Map<String, Map<String, Double>> featurize(List<Suggestion> suggestions) {
     64         Map<String, Map<String, Double>> features = new HashMap<>();
     65         Long curTimeMs = System.currentTimeMillis();
     66         final List<String> suggestionIds = new ArrayList<>(suggestions.size());
     67         for (Suggestion suggestion : suggestions) {
     68             suggestionIds.add(suggestion.getId());
     69         }
     70         for (String id : suggestionIds) {
     71             Map<String, Double> featureMap = new HashMap<>();
     72             features.put(id, featureMap);
     73             Long lastShownTime = mEventStore.readMetric(id,
     74                     SuggestionEventStore.EVENT_SHOWN,
     75                     SuggestionEventStore.METRIC_LAST_EVENT_TIME);
     76             Long lastDismissedTime = mEventStore.readMetric(id,
     77                     SuggestionEventStore.EVENT_DISMISSED,
     78                     SuggestionEventStore.METRIC_LAST_EVENT_TIME);
     79             Long lastClickedTime = mEventStore.readMetric(id,
     80                     SuggestionEventStore.EVENT_CLICKED,
     81                     SuggestionEventStore.METRIC_LAST_EVENT_TIME);
     82             featureMap.put(FEATURE_IS_SHOWN, booleanToDouble(lastShownTime > 0));
     83             featureMap.put(FEATURE_IS_DISMISSED, booleanToDouble(lastDismissedTime > 0));
     84             featureMap.put(FEATURE_IS_CLICKED, booleanToDouble(lastClickedTime > 0));
     85             featureMap.put(FEATURE_TIME_FROM_LAST_SHOWN,
     86                     normalizedTimeDiff(curTimeMs, lastShownTime));
     87             featureMap.put(FEATURE_TIME_FROM_LAST_DISMISSED,
     88                     normalizedTimeDiff(curTimeMs, lastDismissedTime));
     89             featureMap.put(FEATURE_TIME_FROM_LAST_CLICKED,
     90                     normalizedTimeDiff(curTimeMs, lastClickedTime));
     91             featureMap.put(FEATURE_SHOWN_COUNT, normalizedCount(mEventStore.readMetric(id,
     92                     SuggestionEventStore.EVENT_SHOWN, SuggestionEventStore.METRIC_COUNT)));
     93             featureMap.put(FEATURE_DISMISSED_COUNT, normalizedCount(mEventStore.readMetric(id,
     94                     SuggestionEventStore.EVENT_DISMISSED, SuggestionEventStore.METRIC_COUNT)));
     95             featureMap.put(FEATURE_CLICKED_COUNT, normalizedCount(mEventStore.readMetric(id,
     96                     SuggestionEventStore.EVENT_CLICKED, SuggestionEventStore.METRIC_COUNT)));
     97         }
     98         return features;
     99     }
    100 
    101     private static double booleanToDouble(boolean bool) {
    102         return bool ? 1 : 0;
    103     }
    104 
    105     private static double normalizedTimeDiff(long curTimeMs, long preTimeMs) {
    106         return Math.min(1, (curTimeMs - preTimeMs) / TIME_NORMALIZATION_FACTOR);
    107     }
    108 
    109     private static double normalizedCount(long count) {
    110         return Math.min(1, count / COUNT_NORMALIZATION_FACTOR);
    111     }
    112 }