/*
* Seldon -- open source prediction engine
* =======================================
*
* Copyright 2011-2015 Seldon Technologies Ltd and Rummble Ltd (http://www.seldon.io/)
*
* ********************************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ********************************************************************************************
*/
package io.seldon.recommendation.combiner;
import io.seldon.clustering.recommender.ItemRecommendationResultSet;
import io.seldon.recommendation.RecommendationPeer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
*
* Combine recommendations from algorithms based on their rank in the list of recs.
*
* @author firemanphil
* Date: 23/02/15
* Time: 11:00
*/
@Component
public class RankSumCombiner implements AlgorithmResultsCombiner{
private final int numResultSetsToUse;
private final boolean strict;
@Autowired
public RankSumCombiner(@Value("${combiner.ranksum.maxResultSets:2}") int numResultSetsToUse, @Value("${combiner.ranksum.strict:false}") boolean strict ){
this.numResultSetsToUse = numResultSetsToUse;
this.strict = strict;
}
@Override
public boolean isEnoughResults(int numRecsRequired, List<RecommendationPeer.RecResultContext> resultsSets) {
int numValidSets = 0;
for (RecommendationPeer.RecResultContext set : resultsSets){
if (set.resultSet.getResults().size() >= numRecsRequired){
numValidSets++;
}
}
return numValidSets >= numResultSetsToUse;
}
@Override
public RecommendationPeer.RecResultContext combine(int numRecsRequired, List<RecommendationPeer.RecResultContext> resultsSets) {
Map<Long,String> item_recommender_lookup = new HashMap<>();
Map<ItemRecommendationResultSet.ItemRecommendationResult, Integer> rankSumMap = new HashMap<>();
List<RecommendationPeer.RecResultContext> validResultSets = new ArrayList<>();
List<String> validResultsAlgKeys = new ArrayList<>();
for (RecommendationPeer.RecResultContext set : resultsSets){
if((strict && set.resultSet.getResults().size() >= numRecsRequired) || (!strict && set.resultSet.getResults().size()>0)) {
validResultSets.add(set);
validResultsAlgKeys.add(set.algKey);
}
}
for (int i = 0; i < numRecsRequired; i++){
for (RecommendationPeer.RecResultContext validResultSet : validResultSets) {
List<ItemRecommendationResultSet.ItemRecommendationResult> ordered = validResultSet.resultSet.getResults();
Collections.sort(ordered, Collections.reverseOrder());
if (i<ordered.size())
{
final ItemRecommendationResultSet.ItemRecommendationResult itemRecommendationResult = ordered.get(i);
Integer rankSum = rankSumMap.get(itemRecommendationResult);
if(rankSum == null) rankSum = 0;
rankSum += (numRecsRequired -i);
rankSumMap.put(itemRecommendationResult, rankSum);
{ // capture the recommender used for item
String original_value = item_recommender_lookup.put(itemRecommendationResult.item, validResultSet.resultSet.getRecommenderName());
if (original_value != null) {
item_recommender_lookup.put(itemRecommendationResult.item, original_value);
}
}
}
}
}
List<ItemRecommendationResultSet.ItemRecommendationResult> orderedResults = new ArrayList<>();
for (Map.Entry<ItemRecommendationResultSet.ItemRecommendationResult, Integer> entry : rankSumMap.entrySet()) {
Float newScore = entry.getValue().floatValue();
Long item = entry.getKey().item;
ItemRecommendationResultSet.ItemRecommendationResult result = new ItemRecommendationResultSet.ItemRecommendationResult(item, newScore);
orderedResults.add(result);
}
Collections.sort(orderedResults, Collections.reverseOrder());
RecommendationPeer.RecResultContext recResultContext = new RecommendationPeer.RecResultContext(new ItemRecommendationResultSet(orderedResults, StringUtils.join(validResultsAlgKeys,':')), StringUtils.join(validResultsAlgKeys,':'));
recResultContext.item_recommender_lookup = item_recommender_lookup;
return recResultContext;
}
}