net.librec.similarity.AbstractRecommenderSimilarity Maven / Gradle / Ivy
/**
* Copyright (C) 2016 LibRec
*
* This file is part of LibRec.
* LibRec is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LibRec is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LibRec. If not, see .
*/
package net.librec.similarity;
import net.librec.conf.Configuration;
import net.librec.data.DataModel;
import net.librec.data.convertor.appender.SocialDataAppender;
import net.librec.math.structure.SparseMatrix;
import net.librec.math.structure.SparseVector;
import net.librec.math.structure.SymmMatrix;
import org.apache.commons.lang.StringUtils;
import java.util.ArrayList;
import java.util.List;
/**
* Calculate Recommender Similarity, such as cosine, Pearson, Jaccard
* similarity, etc.
*
* @author zhanghaidong
*/
public abstract class AbstractRecommenderSimilarity implements RecommenderSimilarity {
/**
* Configuration
*/
protected Configuration conf;
/**
* Similarity Matrix
*/
protected SymmMatrix similarityMatrix;
/**
* Build social similarity matrix with trainMatrix in dataModel.
*
* @param dataModel
* the input data model
*/
@Override
public void buildSimilarityMatrix(DataModel dataModel) {
conf = dataModel.getContext().getConf();
String similarityKey = conf.get("rec.recommender.similarity.key", "user");
if(StringUtils.isNotBlank(similarityKey)){
if (StringUtils.equals(similarityKey, "social")) {
buildSocialSimilarityMatrix(dataModel);
} else {
// calculate the similarity between users, or the similarity between
// items.
boolean isUser = StringUtils.equals(similarityKey, "user") ? true : false;
SparseMatrix trainMatrix = dataModel.getDataSplitter().getTrainData();
int numUsers = trainMatrix.numRows();
int numItems = trainMatrix.numColumns();
int count = isUser ? numUsers : numItems;
similarityMatrix = new SymmMatrix(count);
for (int i = 0; i < count; i++) {
SparseVector thisVector = isUser ? trainMatrix.row(i) : trainMatrix.column(i);
if (thisVector.getCount() == 0) {
continue;
}
// user/item itself exclusive
for (int j = i + 1; j < count; j++) {
SparseVector thatVector = isUser ? trainMatrix.row(j) : trainMatrix.column(j);
if (thatVector.getCount() == 0) {
continue;
}
double sim = getCorrelation(thisVector, thatVector);
if (!Double.isNaN(sim)) {
similarityMatrix.set(i, j, sim);
}
}
}
}
}
}
/**
* Build social similarity matrix with trainMatrix
* and socialMatrix in dataModel.
*
* @param dataModel
* the input data model
*/
public void buildSocialSimilarityMatrix(DataModel dataModel) {
SparseMatrix trainMatrix = dataModel.getDataSplitter().getTrainData();
SparseMatrix socialMatrix = ((SocialDataAppender) dataModel.getDataAppender()).getUserAppender();
int numUsers = trainMatrix.numRows();
similarityMatrix = new SymmMatrix(numUsers);
for (int userIdx = 0; userIdx < numUsers; userIdx++) {
SparseVector userVector = trainMatrix.row(userIdx);
if (userVector.getCount() == 0) {
continue;
}
List socialList = socialMatrix.getRows(userIdx);
for (int socialIdx : socialList) {
SparseVector socialVector = trainMatrix.row(socialIdx);
if (socialVector.getCount() == 0) {
continue;
}
double sim = getCorrelation(userVector, socialVector);
if (!Double.isNaN(sim)) {
similarityMatrix.set(userIdx, socialIdx, sim);
}
}
}
}
/**
* Find the common rated items by this user and that user, or the common
* users have rated this item or that item. And then return the similarity.
*
* @param thisVector:
* the rated items by this user, or users that have rated this
* item .
* @param thatVector:
* the rated items by that user, or users that have rated that
* item.
* @return similarity
*/
public double getCorrelation(SparseVector thisVector, SparseVector thatVector) {
// compute similarity
List thisList = new ArrayList();
List thatList = new ArrayList();
for (Integer idx : thatVector.getIndex()) {
if (thisVector.contains(idx)) {
thisList.add(thisVector.get(idx));
thatList.add(thatVector.get(idx));
}
}
double sim = getSimilarity(thisList, thatList);
// shrink to account for vector size
if (!Double.isNaN(sim)) {
int n = thisList.size();
int shrinkage = conf.getInt("rec.similarity.shrinkage", 0);
if (shrinkage > 0)
sim *= n / (n + shrinkage + 0.0);
}
return sim;
}
/**
* Calculate the similarity between thisList and thatList.
*
* @param thisList
* this list
* @param thatList
* that list
* @return similarity
*/
protected abstract double getSimilarity(List extends Number> thisList, List extends Number> thatList);
/**
* Return the similarity matrix.
*
* @return the similarity matrix
*/
@Override
public SymmMatrix getSimilarityMatrix() {
return similarityMatrix;
}
}