'Parameters
user_id: int or string
User for which recommendations are to be computed.
item_id:  int or string
ID of item for which wants to find the estimated preference.
Returns
Return an estimated preference if the user has not expressed a
preference for the item, or else the user\'s actual preference for the
item. If a preference cannot be estimated, returns None.'

def estimate_preference(self, user_id, item_id, **params):

    preference = self.model.preference_value(user_id, item_id)
    if (not np.isnan(preference)):
        return preference
    prefs = self.model.preferences_from_user(user_id)
    if (not self.model.has_preference_values()):
        prefs = [(pref, 1.0) for pref in prefs]
    similarities = np.array([self.similarity.get_similarity(item_id, to_item_id) for (to_item_id, pref) in prefs if (to_item_id != item_id)]).flatten()
    prefs = np.array([pref for (it, pref) in prefs])
    prefs_sim = np.sum((prefs[(~ np.isnan(similarities))] * similarities[(~ np.isnan(similarities))]))
    total_similarity = np.sum(similarities)
    if ((total_similarity == 0.0) or (not similarities[(~ np.isnan(similarities))].size)):
        return np.nan
    estimated = (prefs_sim / total_similarity)
    if self.capper:
        max_p = self.model.maximum_preference_value()
        min_p = self.model.minimum_preference_value()
        estimated = (max_p if (estimated > max_p) else (min_p if (estimated < min_p) else estimated))
    return estimated
