/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.mahout.cf.taste.impl.recommender.slopeone; import java.util.Collection; import java.util.List; import org.apache.mahout.cf.taste.common.NoSuchUserException; import org.apache.mahout.cf.taste.common.Refreshable; import org.apache.mahout.cf.taste.common.TasteException; import org.apache.mahout.cf.taste.common.Weighting; import org.apache.mahout.cf.taste.impl.common.FastIDSet; import org.apache.mahout.cf.taste.impl.common.RefreshHelper; import org.apache.mahout.cf.taste.impl.common.RunningAverage; import org.apache.mahout.cf.taste.impl.common.RunningAverageAndStdDev; import org.apache.mahout.cf.taste.impl.recommender.AbstractRecommender; import org.apache.mahout.cf.taste.impl.recommender.TopItems; import org.apache.mahout.cf.taste.model.DataModel; import org.apache.mahout.cf.taste.model.PreferenceArray; import org.apache.mahout.cf.taste.recommender.IDRescorer; import org.apache.mahout.cf.taste.recommender.RecommendedItem; import org.apache.mahout.cf.taste.recommender.slopeone.DiffStorage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; /** *
* A basic "slope one" recommender. (See an * excellent summary here for example.) This {@link org.apache.mahout.cf.taste.recommender.Recommender} is * especially suitable when user preferences are updating frequently as it can incorporate this information * without expensive recomputation. *
* ** This implementation can also be used as a "weighted slope one" recommender. *
*/ public final class SlopeOneRecommender extends AbstractRecommender { private static final Logger log = LoggerFactory.getLogger(SlopeOneRecommender.class); private final boolean weighted; private final boolean stdDevWeighted; private final DiffStorage diffStorage; /** ** Creates a default (weighted) based on the given {@link DataModel}. *
*/ public SlopeOneRecommender(DataModel dataModel) throws TasteException { this(dataModel, Weighting.WEIGHTED, Weighting.WEIGHTED, new MemoryDiffStorage(dataModel, Weighting.WEIGHTED, Long.MAX_VALUE)); } /** ** Creates a based on the given {@link DataModel}. *
* ** If {@code weighted} is set, acts as a weighted slope one recommender. This implementation also * includes an experimental "standard deviation" weighting which weights item-item ratings diffs with lower * standard deviation more highly, on the theory that they are more reliable. *
* * @param weighting * if {@link Weighting#WEIGHTED}, acts as a weighted slope one recommender * @param stdDevWeighting * use optional standard deviation weighting of diffs * @throws IllegalArgumentException * if {@code diffStorage} is null, or stdDevWeighted is set when weighted is not set */ public SlopeOneRecommender(DataModel dataModel, Weighting weighting, Weighting stdDevWeighting, DiffStorage diffStorage) { super(dataModel); Preconditions.checkArgument(stdDevWeighting != Weighting.WEIGHTED || weighting != Weighting.UNWEIGHTED, "weighted required when stdDevWeighted is set"); Preconditions.checkArgument(diffStorage != null, "diffStorage is null"); this.weighted = weighting == Weighting.WEIGHTED; this.stdDevWeighted = stdDevWeighting == Weighting.WEIGHTED; this.diffStorage = diffStorage; } @Override public List