'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
    n_similarity = params.pop('n_similarity', 'user_similarity')
    distance = params.pop('distance', self.similarity.distance)
    nhood_size = params.pop('nhood_size', None)
    nearest_neighbors = self.neighborhood_strategy.user_neighborhood(user_id, self.model, n_similarity, distance, nhood_size, **params)
    preference = 0.0
    total_similarity = 0.0
    similarities = np.array([self.similarity.get_similarity(user_id, to_user_id) for to_user_id in nearest_neighbors]).flatten()
    prefs = np.array([self.model.preference_value(to_user_id, item_id) for to_user_id in nearest_neighbors])
    prefs = prefs[(~ np.isnan(prefs))]
    similarities = similarities[(~ np.isnan(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
