/*
* Copyright (C) 2015 RankSys (http://ranksys.org)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.ranksys.diversity.prop.metrics;
import es.uam.eps.ir.ranksys.core.feature.FeatureData;
import es.uam.eps.ir.ranksys.core.Recommendation;
import es.uam.eps.ir.ranksys.diversity.binom.BinomialModel;
import es.uam.eps.ir.ranksys.metrics.AbstractRecommendationMetric;
import es.uam.eps.ir.ranksys.metrics.rel.NoRelevanceModel;
import es.uam.eps.ir.ranksys.metrics.rel.RelevanceModel;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.ranksys.core.util.tuples.Tuple2od;
/**
* Cumulative proportionality metric.
* <br>
*
* Dang, V., Croft, W. B. (2012). Diversity by Proportionality: An Election-based Approach to Search Result Diversification. In Proceedings of the 35th International ACM SIGIR Conference on Research and Development in Information Retrieval (pp. 65–74). New York, NY, USA: ACM. doi:10.1145/2348283.2348296
*
* @author Saúl Vargas (Saul.Vargas@mendeley.com)
*
* @param <U> type of user
* @param <I> type of item
* @param <F> type of feature
*/
public class CPR<U, I, F> extends AbstractRecommendationMetric<U, I> {
private final BinomialModel<U, I, F> binomialModel;
private final FeatureData<I, F, ?> featureData;
private final int cutoff;
private final RelevanceModel<U, I> relModel;
/**
* Constructor.
*
* @param binomialModel binomial model for features
* @param featureData feature data
* @param cutoff metric cutoff
* @param relModel relevance model
*/
public CPR(BinomialModel<U, I, F> binomialModel, FeatureData<I, F, ?> featureData, int cutoff, RelevanceModel<U, I> relModel) {
this.binomialModel = binomialModel;
this.featureData = featureData;
this.cutoff = cutoff;
this.relModel = relModel;
}
@Override
public double evaluate(Recommendation<U, I> recommendation) {
RelevanceModel.UserRelevanceModel<U, I> userRelModel = relModel.getModel(recommendation.getUser());
BinomialModel<U, I, F>.UserBinomialModel prob = binomialModel.getModel(recommendation.getUser());
Object2IntOpenHashMap<F> count = new Object2IntOpenHashMap<>();
count.defaultReturnValue(0);
int rank = 0;
int nr = 0;
double cpr = 0.0;
for (Tuple2od<I> iv : recommendation.getItems()) {
if (userRelModel.isRelevant(iv.v1)) {
featureData.getItemFeatures(iv.v1)
.forEach(fv -> count.addTo(fv.v1, 1));
} else {
nr++;
}
double[] disprop = {0.5 * nr * nr};
double[] ideal = {0};
if (relModel instanceof NoRelevanceModel) {
ideal[0] = 0;
} else {
ideal[0] = 0.5 * (rank + 1) * (rank + 1);
}
int _rank = rank;
prob.getFeatures().forEach(f -> {
double v = prob.p(f) * (_rank + 1);
int c = count.getInt(f);
if (v >= c) {
disprop[0] += (v - c) * (v - c);
}
ideal[0] += v * v;
});
cpr += 1 - disprop[0] / ideal[0];
rank++;
if (rank >= cutoff) {
break;
}
}
cpr /= rank;
return cpr;
}
}