package happy.research.cf;
import happy.coding.math.Sims;
import happy.coding.system.Debug;
import happy.research.cf.ConfigParams.PredictMethod;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class TSF_t extends Thread_t
{
public TSF_t(int id)
{
super(id);
}
@Override
protected Map<String, Double>[] buildModel(Rating testRating)
{
return null;
}
@Override
protected void runLeaveOneOut()
{
for (Rating testRating : threadRatings)
{
reportProgress(numRating);
String user = testRating.getUserId();
String item = testRating.getItemId();
double meanA = 0.0;
Map<String, Rating> asRatings = userRatingsMap.get(user);
if (params.PREDICT_METHOD == PredictMethod.resnick_formula)
{
meanA = RatingUtils.mean(asRatings, testRating);
if (Double.isNaN(meanA)) continue;
}
/* user-based collaborative filtering */
Map<String, Double>[] nnScoresMap = buildUserNNs(testRating);
double sum = 0.0;
double weights = 0.0;
if (nnScoresMap != null)
{
Map<String, Double> nnSims = nnScoresMap[0];
Map<String, Double> nnTrusts = nnScoresMap[1];
Map<String, Double> nnRatings = nnScoresMap[2];
// nearest neighbors
if (nnSims != null && nnSims.size() > 0)
{
for (Entry<String, Double> entry : nnSims.entrySet())
{
String nn = entry.getKey();
if (nn.equals(user)) continue;
double bsRating = nnRatings.get(nn);
double meanB = 0.0;
if (params.PREDICT_METHOD == PredictMethod.resnick_formula)
{
Map<String, Rating> bsRatings = userRatingsMap.get(nn);
meanB = RatingUtils.mean(bsRatings, null);
if (Double.isNaN(meanB)) continue;
}
double score = entry.getValue();
sum += score * (bsRating - meanB);
weights += Math.abs(score);
}
}
// trusted neighbors
if (nnTrusts != null && nnTrusts.size() > 0)
{
for (Entry<String, Double> entry : nnTrusts.entrySet())
{
String nn = entry.getKey();
if (nn.equals(user)) continue;
double bsRating = nnRatings.get(nn);
double meanB = 0.0;
if (params.PREDICT_METHOD == PredictMethod.resnick_formula)
{
Map<String, Rating> bsRatings = userRatingsMap.get(nn);
meanB = RatingUtils.mean(bsRatings, null);
if (Double.isNaN(meanB)) continue;
}
double score = entry.getValue();
sum += score * (bsRating - meanB);
weights += Math.abs(score);
}
}
}
double pTeCF = 0;
if (weights > 0.0) pTeCF = meanA + sum / weights;
/* item-based collaborative filtering */
double pSeCF = 0;
if (Debug.OFF)
{
Map<String, Double>[] itemScoresMap = buildItemNNs(testRating);
double sum_item = 0;
double ws_item = 0;
double ma = 0.0;
Map<String, Rating> ar = itemRatingsMap.get(item);
if (params.PREDICT_METHOD == PredictMethod.resnick_formula)
{
ma = RatingUtils.mean(ar, testRating);
if (Double.isNaN(ma)) continue;
}
if (itemScoresMap != null)
{
Map<String, Double> nnSims = itemScoresMap[0];
Map<String, Double> nnRatings = itemScoresMap[1];
// item nearest neighbors
if (nnSims != null && nnSims.size() > 0)
{
for (Entry<String, Double> entry : nnSims.entrySet())
{
String nn = entry.getKey();
if (nn.equals(item)) continue;
double bsRating = nnRatings.get(nn);
double meanB = 0.0;
if (params.PREDICT_METHOD == PredictMethod.resnick_formula)
{
Map<String, Rating> bsRatings = itemRatingsMap.get(nn);
meanB = RatingUtils.mean(bsRatings, null);
if (Double.isNaN(meanB)) continue;
}
double score = entry.getValue();
sum_item += score * (bsRating - meanB);
ws_item += Math.abs(score);
}
}
}
if (ws_item > 0.0) pSeCF = ma + sum_item / ws_item;
}
/* fusion of prediction */
double pred = 0;
if (pTeCF == 0 && pSeCF != 0) pred = pSeCF;
else if (pTeCF != 0 && pSeCF == 0) pred = pTeCF;
else pred = 2 * pTeCF * pSeCF / (pTeCF + pSeCF);
if (pred > 0) pf.addPredicts(new Prediction(testRating, pred));
}
}
@SuppressWarnings("unchecked")
private Map<String, Double>[] buildItemNNs(Rating testRating)
{
String testUser = testRating.getUserId();
String testItem = testRating.getItemId();
Map<String, Rating> asRatings = itemRatingsMap.get(testItem);
if (asRatings == null) return null;
Map<String, Double> nnSims = new HashMap<>();
Map<String, Double> nnScores = new HashMap<>();
for (Entry<String, Map<String, Rating>> entry : itemRatingsMap.entrySet())
{
String item = entry.getKey();
if (item.equalsIgnoreCase(testItem)) continue;
Map<String, Rating> bsRatings = entry.getValue();
if (bsRatings == null) continue;
if (!bsRatings.containsKey(testUser)) continue;
double mu_b = RatingUtils.mean(bsRatings, null);
List<Double> as = new ArrayList<>();
List<Double> bs = new ArrayList<>();
for (String user : asRatings.keySet())
{
if (user.equalsIgnoreCase(testUser)) continue;
if (bsRatings.containsKey(user))
{
double ar = asRatings.get(user).getRating();
double br = bsRatings.get(user).getRating();
as.add(ar);
bs.add(br);
}
}
if (as.size() < 1) continue;
double icos = Sims.pcc(as, bs);
if (Double.isNaN(icos)) icos = 0;
double ij = as.size() / (asRatings.size() - 1 + bsRatings.size() - as.size() + 0.0);
double sim = icos * ij;
if (sim > 0)
{
double ir = bsRatings.size() / (userRatingsMap.size() + 0.0) * mu_b;
nnSims.put(item, sim * ir);
nnScores.put(item, bsRatings.get(testUser).getRating());
}
}
return new Map[] { nnSims, nnScores };
}
@SuppressWarnings("unchecked")
private Map<String, Double>[] buildUserNNs(Rating testRating)
{
String testUser = testRating.getUserId();
String testItem = testRating.getItemId();
Map<String, Rating> asRatings = userRatingsMap.get(testUser);
if (asRatings == null) return null;
double mu_a = RatingUtils.mean(asRatings, testRating);
if (Double.isNaN(mu_a)) mu_a = (Dataset.maxScale + Dataset.minScale) / 2.0;
Map<String, Double> nnSims = new HashMap<>();
Map<String, Double> nnTrusts = new HashMap<>();
Map<String, Double> nnScores = new HashMap<>();
for (Entry<String, Map<String, Rating>> entry : userRatingsMap.entrySet())
{
String user = entry.getKey();
if (user.equals(testUser)) continue;
Map<String, Rating> bsRatings = entry.getValue();
if (bsRatings == null) continue;
if (!bsRatings.containsKey(testItem)) continue;
double mu_b = RatingUtils.mean(bsRatings, null);
List<Double> as = new ArrayList<>();
List<Double> bs = new ArrayList<>();
double es = 0;
for (String item : asRatings.keySet())
{
if (item.equalsIgnoreCase(testItem)) continue;
if (bsRatings.containsKey(item))
{
double ar = asRatings.get(item).getRating();
double br = bsRatings.get(item).getRating();
as.add(ar);
bs.add(br);
double pr = mu_a + (br - mu_b);
double e = pr - ar;
es += e * e;
}
}
if (as.size() < 1) continue;
double upcc = Sims.pcc(as, bs);
if (Double.isNaN(upcc)) upcc = 0;
double uj = as.size() / (asRatings.size() - 1 + bsRatings.size() - as.size() + 0.0);
double sim = upcc * uj;
double msd = 1 - es / as.size();
double trust = msd * uj;
double lambda = 0.15;// [0.05, 0.15]
if (sim > 0 || trust > lambda)
{
if (sim > 0) nnSims.put(user, sim);
if (trust > lambda) nnTrusts.put(user, trust);
nnScores.put(user, bsRatings.get(testItem).getRating());
}
}
return new Map[] { nnSims, nnTrusts, nnScores };
}
}