You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mahout.apache.org by sr...@apache.org on 2010/03/18 13:15:46 UTC

svn commit: r924738 - in /lucene/mahout/trunk: core/src/main/java/org/apache/mahout/cf/taste/eval/ core/src/main/java/org/apache/mahout/cf/taste/impl/model/ core/src/main/java/org/apache/mahout/cf/taste/impl/model/file/ core/src/main/java/org/apache/ma...

Author: srowen
Date: Thu Mar 18 12:15:45 2010
New Revision: 924738

URL: http://svn.apache.org/viewvc?rev=924738&view=rev
Log:
MAHOUT-321. Don't add 1 to similarity scores as weights. Also reject user-based pref estimates based on one similarity, as item-based does.

Added:
    lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/AbstractDataModel.java
    lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/EstimatedPreferenceCapper.java
Modified:
    lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/eval/RecommenderEvaluator.java
    lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/GenericBooleanPrefDataModel.java
    lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/GenericDataModel.java
    lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserDataModel.java
    lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModel.java
    lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractBooleanPrefJDBCDataModel.java
    lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractJDBCDataModel.java
    lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/GenericJDBCDataModel.java
    lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/MySQLJDBCDataModel.java
    lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/GenericItemBasedRecommender.java
    lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/GenericUserBasedRecommender.java
    lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/DataModel.java
    lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/Recommender.java
    lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModelTest.java
    lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericItemBasedRecommenderTest.java
    lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericUserBasedRecommenderTest.java
    lucene/mahout/trunk/examples/src/main/java/org/apache/mahout/cf/taste/example/netflix/NetflixDataModel.java
    lucene/mahout/trunk/examples/src/main/java/org/apache/mahout/cf/taste/example/netflix/NetflixFileDataModel.java

Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/eval/RecommenderEvaluator.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/eval/RecommenderEvaluator.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/eval/RecommenderEvaluator.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/eval/RecommenderEvaluator.java Thu Mar 18 12:15:45 2010
@@ -83,28 +83,23 @@ public interface RecommenderEvaluator {
                   DataModel dataModel,
                   double trainingPercentage,
                   double evaluationPercentage) throws TasteException;
-  
-  float getMaxPreference();
-  
+
   /**
-   * Sets the maximum preference value that is possible in the current problem domain being evaluated. For
-   * example, if the domain is movie ratings on a scale of 1 to 5, this should be set to 5. While a
-   * {@link org.apache.mahout.cf.taste.recommender.Recommender} may estimate a preference value above 5.0, it
-   * isn't "fair" to consider that the system is actually suggesting an impossible rating of, say, 5.4 stars.
-   * In practice the application would cap this estimate to 5.0. Since s evaluate
-   * the difference between estimated and actual value, this at least prevents this effect from unfairly
-   * penalizing a {@link org.apache.mahout.cf.taste.recommender.Recommender}.
-   * 
-   * @see #setMinPreference(float)
+   * @deprecated see {@link DataModel#getMaxPreference()}
    */
+  @Deprecated
+  float getMaxPreference();
+
+  @Deprecated
   void setMaxPreference(float maxPreference);
-  
-  float getMinPreference();
-  
+
   /**
-   * Sets the minimum preference value that is possible in the current problem domain being evaluated.
-   *
+   * @deprecated see {@link DataModel#getMinPreference()}
    */
+  @Deprecated
+  float getMinPreference();
+
+  @Deprecated
   void setMinPreference(float minPreference);
   
 }
\ No newline at end of file

Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/AbstractDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/AbstractDataModel.java?rev=924738&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/AbstractDataModel.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/AbstractDataModel.java Thu Mar 18 12:15:45 2010
@@ -0,0 +1,53 @@
+/**
+ * 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.model;
+
+import org.apache.mahout.cf.taste.model.DataModel;
+
+/**
+ * Contains some features common to all implementations.
+ */
+public abstract class AbstractDataModel implements DataModel {
+
+  private float maxPreference;
+  private float minPreference;
+
+  protected AbstractDataModel() {
+    maxPreference = Float.NaN;
+    minPreference = Float.NaN;
+  }
+
+  @Override
+  public float getMaxPreference() {
+    return maxPreference;
+  }
+
+  protected void setMaxPreference(float maxPreference) {
+    this.maxPreference = maxPreference;
+  }
+
+  @Override
+  public float getMinPreference() {
+    return minPreference;
+  }
+
+  protected void setMinPreference(float minPreference) {
+    this.minPreference = minPreference;
+  }
+
+}

Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/GenericBooleanPrefDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/GenericBooleanPrefDataModel.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/GenericBooleanPrefDataModel.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/GenericBooleanPrefDataModel.java Thu Mar 18 12:15:45 2010
@@ -40,7 +40,7 @@ import org.apache.mahout.cf.taste.model.
  * is mostly useful for small experiments and is not recommended for contexts where performance is important.
  * </p>
  */
-public final class GenericBooleanPrefDataModel implements DataModel, Serializable {
+public final class GenericBooleanPrefDataModel extends AbstractDataModel implements Serializable {
   
   private final long[] userIDs;
   private final FastByIDMap<FastIDSet> preferenceFromUsers;

Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/GenericDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/GenericDataModel.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/GenericDataModel.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/GenericDataModel.java Thu Mar 18 12:15:45 2010
@@ -44,7 +44,7 @@ import org.slf4j.LoggerFactory;
  * is mostly useful for small experiments and is not recommended for contexts where performance is important.
  * </p>
  */
-public final class GenericDataModel implements DataModel, Serializable {
+public final class GenericDataModel extends AbstractDataModel implements Serializable {
   
   private static final Logger log = LoggerFactory.getLogger(GenericDataModel.class);
   
@@ -72,6 +72,8 @@ public final class GenericDataModel impl
     FastByIDMap<Collection<Preference>> prefsForItems = new FastByIDMap<Collection<Preference>>();
     FastIDSet itemIDSet = new FastIDSet();
     int currentCount = 0;
+    float maxPrefValue = Float.NEGATIVE_INFINITY;
+    float minPrefValue = Float.POSITIVE_INFINITY;
     for (Map.Entry<Long,PreferenceArray> entry : preferenceFromUsers.entrySet()) {
       PreferenceArray prefs = entry.getValue();
       prefs.sortByItem();
@@ -84,12 +86,22 @@ public final class GenericDataModel impl
           prefsForItems.put(itemID, prefsForItem);
         }
         prefsForItem.add(preference);
+        float value = preference.getValue();
+        if (value > maxPrefValue) {
+          maxPrefValue = value;
+        }
+        if (value < minPrefValue) {
+          minPrefValue = value;
+        }
       }
       if (++currentCount % 10000 == 0) {
         log.info("Processed {} users", currentCount);
       }
     }
     log.info("Processed {} users", currentCount);
+
+    setMinPreference(minPrefValue);
+    setMaxPreference(maxPrefValue);
     
     this.itemIDs = itemIDSet.toArray();
     itemIDSet = null; // Might help GC -- this is big

Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserDataModel.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserDataModel.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/PlusAnonymousUserDataModel.java Thu Mar 18 12:15:45 2010
@@ -208,5 +208,15 @@ public final class PlusAnonymousUserData
   public boolean hasPreferenceValues() {
     return delegate.hasPreferenceValues();
   }
-  
+
+  @Override
+  public float getMaxPreference() {
+    return delegate.getMaxPreference();
+  }
+
+  @Override
+  public float getMinPreference() {
+    return delegate.getMinPreference();
+  }
+
 }

Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModel.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModel.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModel.java Thu Mar 18 12:15:45 2010
@@ -34,6 +34,7 @@ import org.apache.mahout.cf.taste.common
 import org.apache.mahout.cf.taste.impl.common.FastByIDMap;
 import org.apache.mahout.cf.taste.impl.common.FastIDSet;
 import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;
+import org.apache.mahout.cf.taste.impl.model.AbstractDataModel;
 import org.apache.mahout.cf.taste.impl.model.GenericBooleanPrefDataModel;
 import org.apache.mahout.cf.taste.impl.model.GenericDataModel;
 import org.apache.mahout.cf.taste.impl.model.GenericPreference;
@@ -101,7 +102,7 @@ import org.slf4j.LoggerFactory;
  * application-specific needs and input formats. See {@link #processLine(String, FastByIDMap, boolean)} and
  * {@link #processLineWithoutID(String, FastByIDMap)}
  */
-public class FileDataModel implements DataModel {
+public class FileDataModel extends AbstractDataModel {
   
   private static final Logger log = LoggerFactory.getLogger(FileDataModel.class);
   

Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractBooleanPrefJDBCDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractBooleanPrefJDBCDataModel.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractBooleanPrefJDBCDataModel.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractBooleanPrefJDBCDataModel.java Thu Mar 18 12:15:45 2010
@@ -56,7 +56,8 @@ public abstract class AbstractBooleanPre
                                              String getNumPreferenceForItemsSQL) {
     super(dataSource, preferenceTable, userIDColumn, itemIDColumn, preferenceColumn, getPreferenceSQL,
         getUserSQL, getAllUsersSQL, getNumItemsSQL, getNumUsersSQL, setPreferenceSQL, removePreferenceSQL,
-        getUsersSQL, getItemsSQL, getPrefsForItemSQL, getNumPreferenceForItemSQL, getNumPreferenceForItemsSQL);
+        getUsersSQL, getItemsSQL, getPrefsForItemSQL, getNumPreferenceForItemSQL, getNumPreferenceForItemsSQL,
+        null, null);
     this.setPreferenceSQL = setPreferenceSQL;
   }
   
@@ -97,5 +98,15 @@ public abstract class AbstractBooleanPre
   public boolean hasPreferenceValues() {
     return false;
   }
+
+  @Override
+  public float getMaxPreference() {
+    return 1.0f;
+  }
+
+  @Override
+  public float getMinPreference() {
+    return 1.0f;
+  }
   
 }

Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractJDBCDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractJDBCDataModel.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractJDBCDataModel.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/AbstractJDBCDataModel.java Thu Mar 18 12:15:45 2010
@@ -94,9 +94,13 @@ public abstract class AbstractJDBCDataMo
   private final String getPrefsForItemSQL;
   private final String getNumPreferenceForItemSQL;
   private final String getNumPreferenceForItemsSQL;
+  private final String getMaxPreferenceSQL;
+  private final String getMinPreferenceSQL;
   private int cachedNumUsers;
   private int cachedNumItems;
   private final Cache<Long,Integer> itemPrefCounts;
+  private float maxPreference;
+  private float minPreference;
   
   protected AbstractJDBCDataModel(DataSource dataSource,
                                   String getPreferenceSQL,
@@ -110,12 +114,15 @@ public abstract class AbstractJDBCDataMo
                                   String getItemsSQL,
                                   String getPrefsForItemSQL,
                                   String getNumPreferenceForItemSQL,
-                                  String getNumPreferenceForItemsSQL) {
+                                  String getNumPreferenceForItemsSQL,
+                                  String getMaxPreferenceSQL,
+                                  String getMinPreferenceSQL) {
     this(dataSource, DEFAULT_PREFERENCE_TABLE,
         DEFAULT_USER_ID_COLUMN, DEFAULT_ITEM_ID_COLUMN,
         DEFAULT_PREFERENCE_COLUMN, getPreferenceSQL, getUserSQL, getAllUsersSQL,
         getNumItemsSQL, getNumUsersSQL, setPreferenceSQL, removePreferenceSQL, getUsersSQL, getItemsSQL,
-        getPrefsForItemSQL, getNumPreferenceForItemSQL, getNumPreferenceForItemsSQL);
+        getPrefsForItemSQL, getNumPreferenceForItemSQL, getNumPreferenceForItemsSQL,
+        getMaxPreferenceSQL, getMinPreferenceSQL);
   }
   
   protected AbstractJDBCDataModel(DataSource dataSource,
@@ -134,7 +141,9 @@ public abstract class AbstractJDBCDataMo
                                   String getItemsSQL,
                                   String getPrefsForItemSQL,
                                   String getNumPreferenceForItemSQL,
-                                  String getNumPreferenceForItemsSQL) {
+                                  String getNumPreferenceForItemsSQL,
+                                  String getMaxPreferenceSQL,
+                                  String getMinPreferenceSQL) {
     
     log.debug("Creating AbstractJDBCModel...");
     
@@ -156,7 +165,9 @@ public abstract class AbstractJDBCDataMo
     AbstractJDBCComponent.checkNotNullAndLog("getPrefsForItemSQL", getPrefsForItemSQL);
     AbstractJDBCComponent.checkNotNullAndLog("getNumPreferenceForItemSQL", getNumPreferenceForItemSQL);
     AbstractJDBCComponent.checkNotNullAndLog("getNumPreferenceForItemsSQL", getNumPreferenceForItemsSQL);
-    
+    AbstractJDBCComponent.checkNotNullAndLog("getMaxPreferenceSQL", getMaxPreferenceSQL);
+    AbstractJDBCComponent.checkNotNullAndLog("getMinPreferenceSQL", getMinPreferenceSQL);
+
     if (!(dataSource instanceof ConnectionPoolDataSource)) {
       AbstractJDBCDataModel.log
           .warn("You are not using ConnectionPoolDataSource. Make sure your DataSource pools connections "
@@ -181,11 +192,15 @@ public abstract class AbstractJDBCDataMo
     this.getPrefsForItemSQL = getPrefsForItemSQL;
     this.getNumPreferenceForItemSQL = getNumPreferenceForItemSQL;
     this.getNumPreferenceForItemsSQL = getNumPreferenceForItemsSQL;
+    this.getMaxPreferenceSQL = getMaxPreferenceSQL;
+    this.getMinPreferenceSQL = getMinPreferenceSQL;
     
     this.cachedNumUsers = -1;
     this.cachedNumItems = -1;
     this.itemPrefCounts = new Cache<Long,Integer>(new ItemPrefCountRetriever(getNumPreferenceForItemSQL));
-    
+
+    this.maxPreference = Float.NaN;
+    this.minPreference = Float.NaN;
   }
   
   /** @return the {@link DataSource} that this instance is using */
@@ -584,6 +599,8 @@ public abstract class AbstractJDBCDataMo
   public void refresh(Collection<Refreshable> alreadyRefreshed) {
     cachedNumUsers = -1;
     cachedNumItems = -1;
+    minPreference = Float.NaN;
+    maxPreference = Float.NaN;
     itemPrefCounts.clear();
   }
 
@@ -591,6 +608,56 @@ public abstract class AbstractJDBCDataMo
   public boolean hasPreferenceValues() {
     return true;
   }
+
+  @Override
+  public float getMaxPreference() {
+    if (Float.isNaN(maxPreference)) {
+      Connection conn = null;
+      PreparedStatement stmt = null;
+      ResultSet rs = null;
+      try {
+        conn = dataSource.getConnection();
+        stmt = conn.prepareStatement(getMaxPreferenceSQL);
+
+        log.debug("Executing SQL query: {}", getMaxPreferenceSQL);
+        rs = stmt.executeQuery();
+        rs.next();
+        maxPreference = rs.getFloat(1);
+
+      } catch (SQLException sqle) {
+        log.warn("Exception while removing preference", sqle);
+        // do nothing
+      } finally {
+        IOUtils.quietClose(rs, stmt, conn);
+      }
+    }
+    return maxPreference;
+  }
+
+  @Override
+  public float getMinPreference() {
+    if (Float.isNaN(minPreference)) {
+      Connection conn = null;
+      PreparedStatement stmt = null;
+      ResultSet rs = null;
+      try {
+        conn = dataSource.getConnection();
+        stmt = conn.prepareStatement(getMinPreferenceSQL);
+
+        log.debug("Executing SQL query: {}", getMinPreferenceSQL);
+        rs = stmt.executeQuery();
+        rs.next();
+        minPreference = rs.getFloat(1);
+
+      } catch (SQLException sqle) {
+        log.warn("Exception while removing preference", sqle);
+        // do nothing
+      } finally {
+        IOUtils.quietClose(rs, stmt, conn);
+      }
+    }
+    return minPreference;
+  }
   
   // Some overrideable methods to customize the class behavior:
   

Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/GenericJDBCDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/GenericJDBCDataModel.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/GenericJDBCDataModel.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/GenericJDBCDataModel.java Thu Mar 18 12:15:45 2010
@@ -53,7 +53,9 @@ public final class GenericJDBCDataModel 
   public static final String GET_PREFS_FOR_ITEM_SQL_KEY = "getPrefsForItemSQL";
   public static final String GET_NUM_PREFERENCE_FOR_ITEM_KEY = "getNumPreferenceForItemSQL";
   public static final String GET_NUM_PREFERENCE_FOR_ITEMS_KEY = "getNumPreferenceForItemsSQL";
-  
+  public static final String GET_MAX_PREFERENCE_KEY = "getMaxPreferenceSQL";
+  public static final String GET_MIN_PREFERENCE_KEY = "getMinPreferenceSQL";
+
   /**
    * <p>
    * Specifies all SQL queries in a {@link Properties} object. See the <code>*_KEY</code> constants in this
@@ -79,7 +81,9 @@ public final class GenericJDBCDataModel 
         props.getProperty(GET_ITEMS_SQL_KEY),
         props.getProperty(GET_PREFS_FOR_ITEM_SQL_KEY),
         props.getProperty(GET_NUM_PREFERENCE_FOR_ITEM_KEY),
-        props.getProperty(GET_NUM_PREFERENCE_FOR_ITEMS_KEY));
+        props.getProperty(GET_NUM_PREFERENCE_FOR_ITEMS_KEY),
+        props.getProperty(GET_MAX_PREFERENCE_KEY),
+        props.getProperty(GET_MIN_PREFERENCE_KEY));
   }
   
   /**

Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/MySQLJDBCDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/MySQLJDBCDataModel.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/MySQLJDBCDataModel.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/model/jdbc/MySQLJDBCDataModel.java Thu Mar 18 12:15:45 2010
@@ -197,7 +197,7 @@ public class MySQLJDBCDataModel extends 
                             String itemIDColumn,
                             String preferenceColumn) {
     super(dataSource, preferenceTable, userIDColumn, itemIDColumn, preferenceColumn,
-    // getPreferenceSQL
+        // getPreferenceSQL
         "SELECT " + preferenceColumn + " FROM " + preferenceTable + " WHERE " + userIDColumn + "=? AND "
             + itemIDColumn + "=?",
         // getUserSQL
@@ -226,7 +226,9 @@ public class MySQLJDBCDataModel extends 
         "SELECT COUNT(1) FROM " + preferenceTable + " WHERE " + itemIDColumn + "=?",
         // getNumPreferenceForItemsSQL
         "SELECT COUNT(1) FROM " + preferenceTable + " tp1 JOIN " + preferenceTable + " tp2 " + "USING ("
-            + userIDColumn + ") WHERE tp1." + itemIDColumn + "=? and tp2." + itemIDColumn + "=?");
+            + userIDColumn + ") WHERE tp1." + itemIDColumn + "=? and tp2." + itemIDColumn + "=?",
+        "SELECT MAX(" + preferenceColumn + ") FROM " + preferenceTable,
+        "SELECT MIN(" + preferenceColumn + ") FROM " + preferenceTable);
   }
   
   @Override

Added: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/EstimatedPreferenceCapper.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/EstimatedPreferenceCapper.java?rev=924738&view=auto
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/EstimatedPreferenceCapper.java (added)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/EstimatedPreferenceCapper.java Thu Mar 18 12:15:45 2010
@@ -0,0 +1,46 @@
+/**
+ * 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;
+
+import org.apache.mahout.cf.taste.model.DataModel;
+
+/**
+ * Simple class which encapsulates restricting a preference value
+ * to a predefined range. The simple logic is wrapped up here for
+ * performance reasons.
+ */
+public final class EstimatedPreferenceCapper {
+
+  private final float min;
+  private final float max;
+
+  public EstimatedPreferenceCapper(DataModel model) {
+    min = model.getMinPreference();
+    max = model.getMaxPreference();
+  }
+
+  public float capEstimate(float estimate) {
+    if (estimate > max) {
+      estimate = max;
+    } else if (estimate < min) {
+      estimate = min;
+    }
+    return estimate;
+  }
+
+}

Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/GenericItemBasedRecommender.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/GenericItemBasedRecommender.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/GenericItemBasedRecommender.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/GenericItemBasedRecommender.java Thu Mar 18 12:15:45 2010
@@ -67,7 +67,8 @@ public class GenericItemBasedRecommender
   
   private final ItemSimilarity similarity;
   private final RefreshHelper refreshHelper;
-  
+  private EstimatedPreferenceCapper capper;
+
   public GenericItemBasedRecommender(DataModel dataModel, ItemSimilarity similarity) {
     super(dataModel);
     if (similarity == null) {
@@ -77,6 +78,7 @@ public class GenericItemBasedRecommender
     this.refreshHelper = new RefreshHelper(null);
     refreshHelper.addDependency(dataModel);
     refreshHelper.addDependency(similarity);
+    capper = buildCapper();
   }
   
   public ItemSimilarity getSimilarity() {
@@ -186,10 +188,7 @@ public class GenericItemBasedRecommender
     for (int i = 0; i < size; i++) {
       double theSimilarity = similarity.itemSimilarity(itemID, prefs.getItemID(i));
       if (!Double.isNaN(theSimilarity)) {
-        // Why + 1.0? similarity ranges from -1.0 to 1.0, and we want to use it as a simple
-        // weight. To avoid negative values, we add 1.0 to put it in
-        // the [0.0,2.0] range which is reasonable for weights
-        theSimilarity += 1.0;
+        // Weights can be negative!
         preference += theSimilarity * prefs.getValue(i);
         totalSimilarity += theSimilarity;
         count++;
@@ -200,7 +199,14 @@ public class GenericItemBasedRecommender
     // The reason is that in this case the estimate is, simply, the user's rating for one item
     // that happened to have a defined similarity. The similarity score doesn't matter, and that
     // seems like a bad situation.
-    return count <= 1 ? Float.NaN : (float) (preference / totalSimilarity);
+    if (count <= 1) {
+      return Float.NaN;
+    }
+    float estimate = (float) (preference / totalSimilarity);
+    if (capper != null) {
+      estimate = capper.capEstimate(estimate);
+    }
+    return estimate;
   }
   
   private int getNumPreferences(long userID) throws TasteException {
@@ -210,12 +216,22 @@ public class GenericItemBasedRecommender
   @Override
   public void refresh(Collection<Refreshable> alreadyRefreshed) {
     refreshHelper.refresh(alreadyRefreshed);
+    capper = buildCapper();
   }
   
   @Override
   public String toString() {
     return "GenericItemBasedRecommender[similarity:" + similarity + ']';
   }
+
+  private EstimatedPreferenceCapper buildCapper() {
+    DataModel dataModel = getDataModel();
+    if (Float.isNaN(dataModel.getMinPreference()) && Float.isNaN(dataModel.getMaxPreference())) {
+      return null;
+    } else {
+      return new EstimatedPreferenceCapper(dataModel);
+    }
+  }
   
   public static class MostSimilarEstimator implements TopItems.Estimator<Long> {
     

Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/GenericUserBasedRecommender.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/GenericUserBasedRecommender.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/GenericUserBasedRecommender.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/impl/recommender/GenericUserBasedRecommender.java Thu Mar 18 12:15:45 2010
@@ -50,6 +50,7 @@ public class GenericUserBasedRecommender
   private final UserNeighborhood neighborhood;
   private final UserSimilarity similarity;
   private final RefreshHelper refreshHelper;
+  private EstimatedPreferenceCapper capper;
   
   public GenericUserBasedRecommender(DataModel dataModel,
                                      UserNeighborhood neighborhood,
@@ -64,6 +65,7 @@ public class GenericUserBasedRecommender
     refreshHelper.addDependency(dataModel);
     refreshHelper.addDependency(similarity);
     refreshHelper.addDependency(neighborhood);
+    capper = buildCapper();
   }
   
   public UserSimilarity getSimilarity() {
@@ -129,21 +131,34 @@ public class GenericUserBasedRecommender
     DataModel dataModel = getDataModel();
     double preference = 0.0;
     double totalSimilarity = 0.0;
+    int count = 0;
     for (long userID : theNeighborhood) {
       if (userID != theUserID) {
         // See GenericItemBasedRecommender.doEstimatePreference() too
         Float pref = dataModel.getPreferenceValue(userID, itemID);
         if (pref != null) {
-          double theSimilarity = similarity.userSimilarity(theUserID, userID) + 1.0;
-          // Similarity should not be NaN or else the user should never have showed up
-          // in the neighborhood. Adding 1.0 puts this in the range [0,2] which is
-          // more appropriate for weights
-          preference += theSimilarity * pref;
-          totalSimilarity += theSimilarity;
+          double theSimilarity = similarity.userSimilarity(theUserID, userID);
+          if (!Double.isNaN(theSimilarity)) {
+            preference += theSimilarity * pref;
+            totalSimilarity += theSimilarity;
+            count++;
+          }
         }
       }
     }
-    return totalSimilarity == 0.0 ? Float.NaN : (float) (preference / totalSimilarity);
+    // Throw out the estimate if it was based on no data points, of course, but also if based on
+    // just one. This is a bit of a band-aid on the 'stock' item-based algorithm for the moment.
+    // The reason is that in this case the estimate is, simply, the user's rating for one item
+    // that happened to have a defined similarity. The similarity score doesn't matter, and that
+    // seems like a bad situation.
+    if (count <= 1) {
+      return Float.NaN;
+    }
+    float estimate = (float) (preference / totalSimilarity);
+    if (capper != null) {
+      estimate = capper.capEstimate(estimate);
+    }
+    return estimate;
   }
   
   protected FastIDSet getAllOtherItems(long[] theNeighborhood, long theUserID) throws TasteException {
@@ -159,12 +174,22 @@ public class GenericUserBasedRecommender
   @Override
   public void refresh(Collection<Refreshable> alreadyRefreshed) {
     refreshHelper.refresh(alreadyRefreshed);
+    capper = buildCapper();
   }
   
   @Override
   public String toString() {
     return "GenericUserBasedRecommender[neighborhood:" + neighborhood + ']';
   }
+
+  private EstimatedPreferenceCapper buildCapper() {
+    DataModel dataModel = getDataModel();
+    if (Float.isNaN(dataModel.getMinPreference()) && Float.isNaN(dataModel.getMaxPreference())) {
+      return null;
+    } else {
+      return new EstimatedPreferenceCapper(dataModel);
+    }
+  }
   
   private static class MostSimilarEstimator implements TopItems.Estimator<Long> {
     

Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/DataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/DataModel.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/DataModel.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/model/DataModel.java Thu Mar 18 12:15:45 2010
@@ -163,6 +163,26 @@ public interface DataModel extends Refre
    */
   void removePreference(long userID, long itemID) throws TasteException;
 
+  /**
+   * @return true iff this implementation actually stores and returns distinct preference values;
+   *  that is, if it is not a 'boolean' DataModel
+   */
   boolean hasPreferenceValues();
+
+  /**
+   * @return the maximum preference value that is possible in the current problem domain being evaluated. For
+   * example, if the domain is movie ratings on a scale of 1 to 5, this should be 5. While a
+   * {@link org.apache.mahout.cf.taste.recommender.Recommender} may estimate a preference value above 5.0, it
+   * isn't "fair" to consider that the system is actually suggesting an impossible rating of, say, 5.4 stars.
+   * In practice the application would cap this estimate to 5.0. Since evaluators evaluate
+   * the difference between estimated and actual value, this at least prevents this effect from unfairly
+   * penalizing a {@link org.apache.mahout.cf.taste.recommender.Recommender}
+   */
+  float getMaxPreference();
+
+  /**
+   * @see #getMaxPreference()
+   */
+  float getMinPreference();
   
 }

Modified: lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/Recommender.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/Recommender.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/Recommender.java (original)
+++ lucene/mahout/trunk/core/src/main/java/org/apache/mahout/cf/taste/recommender/Recommender.java Thu Mar 18 12:15:45 2010
@@ -91,7 +91,10 @@ public interface Recommender extends Ref
    *           if an error occurs while accessing the {@link DataModel}
    */
   void removePreference(long userID, long itemID) throws TasteException;
-  
+
+  /**
+   * @return underlying {@link DataModel} used by this implementation
+   */
   DataModel getDataModel();
-  
+
 }

Modified: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModelTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModelTest.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModelTest.java (original)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/model/file/FileDataModelTest.java Thu Mar 18 12:15:45 2010
@@ -48,11 +48,16 @@ public final class FileDataModelTest ext
       "123,654,0.7",
       "234,123,0.5",
       "234,234,1.0",
+      "234,999,0.9",
       "345,789,0.6",
       "345,654,0.7",
       "345,123,1.0",
       "345,234,0.5",
-      "456,456,0.1"};
+      "345,999,0.5",
+      "456,456,0.1",
+      "456,789,0.5",
+      "456,654,0.0",
+      "456,999,0.2",};
 
   private DataModel model;
   private File testFile;
@@ -96,10 +101,10 @@ public final class FileDataModelTest ext
 
   public void testFile() throws Exception {
     UserSimilarity userSimilarity = new PearsonCorrelationSimilarity(model);
-    UserNeighborhood neighborhood = new NearestNUserNeighborhood(2, userSimilarity, model);
+    UserNeighborhood neighborhood = new NearestNUserNeighborhood(3, userSimilarity, model);
     Recommender recommender = new GenericUserBasedRecommender(model, neighborhood, userSimilarity);
-    assertEquals(2, recommender.recommend(123, 3).size());
-    assertEquals(2, recommender.recommend(234, 3).size());
+    assertEquals(1, recommender.recommend(123, 3).size());
+    assertEquals(0, recommender.recommend(234, 3).size());
     assertEquals(1, recommender.recommend(345, 3).size());
 
     // Make sure this doesn't throw an exception
@@ -129,6 +134,8 @@ public final class FileDataModelTest ext
     assertEquals(654, it.nextLong());
     assertTrue(it.hasNext());
     assertEquals(789, it.nextLong());
+    assertTrue(it.hasNext());
+    assertEquals(999, it.nextLong());
     assertFalse(it.hasNext());
     try {
       it.next();

Modified: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericItemBasedRecommenderTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericItemBasedRecommenderTest.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericItemBasedRecommenderTest.java (original)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericItemBasedRecommenderTest.java Thu Mar 18 12:15:45 2010
@@ -39,12 +39,12 @@ public final class GenericItemBasedRecom
     assertEquals(1, recommended.size());
     RecommendedItem firstRecommended = recommended.get(0);
     assertEquals(2, firstRecommended.getItemID());
-    assertEquals(0.18, firstRecommended.getValue(), EPSILON);
+    assertEquals(0.1f, firstRecommended.getValue(), EPSILON);
     recommender.refresh(null);
     recommended = recommender.recommend(1, 1);
     firstRecommended = recommended.get(0);    
     assertEquals(2, firstRecommended.getItemID());
-    assertEquals(0.18, firstRecommended.getValue(), EPSILON);
+    assertEquals(0.1f, firstRecommended.getValue(), EPSILON);
   }
 
   public void testHowMany() throws Exception {
@@ -113,7 +113,7 @@ public final class GenericItemBasedRecom
 
   public void testEstimatePref() throws Exception {
     Recommender recommender = buildRecommender();
-    assertEquals(0.18, recommender.estimatePreference(1, 2), EPSILON);
+    assertEquals(0.1f, recommender.estimatePreference(1, 2), EPSILON);
   }
 
   /**
@@ -129,7 +129,7 @@ public final class GenericItemBasedRecom
     RecommendedItem firstRecommended = recommended.get(0);
     // item one should be recommended because it has a greater rating/score
     assertEquals(2, firstRecommended.getItemID());
-    assertEquals(0.18, firstRecommended.getValue(), EPSILON);
+    assertEquals(0.1f, firstRecommended.getValue(), EPSILON);
   }
 
   public void testMostSimilar() throws Exception {
@@ -140,9 +140,9 @@ public final class GenericItemBasedRecom
     RecommendedItem first = similar.get(0);
     RecommendedItem second = similar.get(1);
     assertEquals(1, first.getItemID());
-    assertEquals(1.0, first.getValue(), EPSILON);
+    assertEquals(1.0f, first.getValue(), EPSILON);
     assertEquals(2, second.getItemID());
-    assertEquals(0.5, second.getValue(), EPSILON);
+    assertEquals(0.5f, second.getValue(), EPSILON);
   }
 
   public void testMostSimilarToMultiple() throws Exception {
@@ -153,9 +153,9 @@ public final class GenericItemBasedRecom
     RecommendedItem first = similar.get(0);
     RecommendedItem second = similar.get(1);
     assertEquals(2, first.getItemID());
-    assertEquals(0.85, first.getValue(), EPSILON);
+    assertEquals(0.85f, first.getValue(), EPSILON);
     assertEquals(3, second.getItemID());
-    assertEquals(-0.3, second.getValue(), EPSILON);
+    assertEquals(-0.3f, second.getValue(), EPSILON);
   }
 
   public void testRecommendedBecause() throws Exception {
@@ -167,11 +167,11 @@ public final class GenericItemBasedRecom
     RecommendedItem second = recommendedBecause.get(1);
     RecommendedItem third = recommendedBecause.get(2);
     assertEquals(2, first.getItemID());
-    assertEquals(0.99, first.getValue(), EPSILON);
+    assertEquals(0.99f, first.getValue(), EPSILON);
     assertEquals(3, second.getItemID());
-    assertEquals(0.4, second.getValue(), EPSILON);
+    assertEquals(0.4f, second.getValue(), EPSILON);
     assertEquals(0, third.getItemID());
-    assertEquals(0.2, third.getValue(), EPSILON);
+    assertEquals(0.2f, third.getValue(), EPSILON);
   }
 
   private static ItemBasedRecommender buildRecommender() {

Modified: lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericUserBasedRecommenderTest.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericUserBasedRecommenderTest.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericUserBasedRecommenderTest.java (original)
+++ lucene/mahout/trunk/core/src/test/java/org/apache/mahout/cf/taste/impl/recommender/GenericUserBasedRecommenderTest.java Thu Mar 18 12:15:45 2010
@@ -40,10 +40,10 @@ public final class GenericUserBasedRecom
     assertEquals(1, recommended.size());
     RecommendedItem firstRecommended = recommended.get(0);
     assertEquals(2, firstRecommended.getItemID());
-    assertEquals(0.3f, firstRecommended.getValue());
+    assertEquals(0.1f, firstRecommended.getValue());
     recommender.refresh(null);
     assertEquals(2, firstRecommended.getItemID());
-    assertEquals(0.3f, firstRecommended.getValue());
+    assertEquals(0.1f, firstRecommended.getValue());
   }
 
   public void testHowMany() throws Exception {
@@ -76,10 +76,10 @@ public final class GenericUserBasedRecom
             new Double[][] {
                     {0.1, 0.2},
                     {0.2, 0.3, 0.3, 0.6},
-                    {0.4, 0.4, 0.5, 0.9},
+                    {0.4, 0.5, 0.5, 0.9},
             });
     UserSimilarity similarity = new PearsonCorrelationSimilarity(dataModel);
-    UserNeighborhood neighborhood = new NearestNUserNeighborhood(1, similarity, dataModel);
+    UserNeighborhood neighborhood = new NearestNUserNeighborhood(2, similarity, dataModel);
     Recommender recommender = new GenericUserBasedRecommender(dataModel, neighborhood, similarity);
     List<RecommendedItem> originalRecommended = recommender.recommend(1, 2);
     List<RecommendedItem> rescoredRecommended =
@@ -94,7 +94,7 @@ public final class GenericUserBasedRecom
 
   public void testEstimatePref() throws Exception {
     Recommender recommender = buildRecommender();
-    assertEquals(0.3f, recommender.estimatePreference(1, 2));
+    assertEquals(0.1f, recommender.estimatePreference(1, 2));
   }
 
   public void testBestRating() throws Exception {
@@ -105,7 +105,7 @@ public final class GenericUserBasedRecom
     RecommendedItem firstRecommended = recommended.get(0);
     // item one should be recommended because it has a greater rating/score
     assertEquals(2, firstRecommended.getItemID());
-    assertEquals(0.3f, firstRecommended.getValue(), EPSILON);
+    assertEquals(0.1f, firstRecommended.getValue(), EPSILON);
   }
 
   public void testMostSimilar() throws Exception {
@@ -137,7 +137,7 @@ public final class GenericUserBasedRecom
   private static UserBasedRecommender buildRecommender() throws TasteException {
     DataModel dataModel = getDataModel();
     UserSimilarity similarity = new PearsonCorrelationSimilarity(dataModel);
-    UserNeighborhood neighborhood = new NearestNUserNeighborhood(1, similarity, dataModel);
+    UserNeighborhood neighborhood = new NearestNUserNeighborhood(2, similarity, dataModel);
     return new GenericUserBasedRecommender(dataModel, neighborhood, similarity);
   }
 

Modified: lucene/mahout/trunk/examples/src/main/java/org/apache/mahout/cf/taste/example/netflix/NetflixDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/examples/src/main/java/org/apache/mahout/cf/taste/example/netflix/NetflixDataModel.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/examples/src/main/java/org/apache/mahout/cf/taste/example/netflix/NetflixDataModel.java (original)
+++ lucene/mahout/trunk/examples/src/main/java/org/apache/mahout/cf/taste/example/netflix/NetflixDataModel.java Thu Mar 18 12:15:45 2010
@@ -170,6 +170,16 @@ public final class NetflixDataModel impl
   public boolean hasPreferenceValues() {
     return delegate.hasPreferenceValues();
   }
+
+  @Override
+  public float getMaxPreference() {
+    return delegate.getMaxPreference();
+  }
+
+  @Override
+  public float getMinPreference() {
+    return delegate.getMinPreference();
+  }
   
   private class MovieFilenameFilter implements FilenameFilter {
     @Override

Modified: lucene/mahout/trunk/examples/src/main/java/org/apache/mahout/cf/taste/example/netflix/NetflixFileDataModel.java
URL: http://svn.apache.org/viewvc/lucene/mahout/trunk/examples/src/main/java/org/apache/mahout/cf/taste/example/netflix/NetflixFileDataModel.java?rev=924738&r1=924737&r2=924738&view=diff
==============================================================================
--- lucene/mahout/trunk/examples/src/main/java/org/apache/mahout/cf/taste/example/netflix/NetflixFileDataModel.java (original)
+++ lucene/mahout/trunk/examples/src/main/java/org/apache/mahout/cf/taste/example/netflix/NetflixFileDataModel.java Thu Mar 18 12:15:45 2010
@@ -139,6 +139,16 @@ public final class NetflixFileDataModel 
   public boolean hasPreferenceValues() {
     return true;
   }
+
+  @Override
+  public float getMaxPreference() {
+    return 0.0f; // TODO
+  }
+
+  @Override
+  public float getMinPreference() {
+    return 0.0f; // TODO
+  }
   
   @Override
   public String toString() {