You are here

function recommender_prediction_slopeone in Recommender API 5

Same name and namespace in other branches
  1. 6 recommender.module \recommender_prediction_slopeone()
  2. 6.2 recommender.module \recommender_prediction_slopeone()

This is the implementation of slope-one algorithm, which is supposed to be faster than other algrithms. From the original research paper, the author argues slope-one support incremental update. But this is quite hard to implement. The incremental update doesn't work for now. Missing data is just treated as missing. For slope-one, we don't automatically expand the matrix to have zeros. The responsibility of handling missing data is on the caller functions.

Parameters

$app_name the application name that uses this function.:

$table_name the input table name:

$field_mouse the input table field for mouse:

$field_cheese the input table field for cheese:

$field_weight the input table field weight:

$options an array of options: 'extention': whether to use 'basic', 'weighted', or 'bipolar' extensions of the algorithm. Please refer to the original research paper. Usually it could just be 'weighted'. 'duplicate': how to handle predictions that already exists in mouse-cheese evaluation. 'preserve' or 'eliminate' 'incremental': whether to 'rebuild' or to 'update'. CAUTION: 'update' doesn't work now.

Return value

null {recommender_prediction} will be filled with prediction data

File

./recommender.module, line 452
Providing generic recommender system algorithms.

Code

function recommender_prediction_slopeone($app_name, $table_name, $field_mouse, $field_cheese, $field_weight, $options = array()) {

  // get param values
  $app_id = recommender_get_app_id($app_name);
  $extension = isset($options['extension']) ? $options['extension'] : 'weighted';

  // could be 'weighted', 'bipolar'
  $incremental = isset($options['incremental']) ? $options['incremental'] : 'rebuild';

  // could be 'update'
  $duplicate = isset($options['duplicate']) ? $options['duplicate'] : 'eliminate';

  // or 'preserve'
  $created = time();
  db_query("DELETE FROM {recommender_slopeone_dev} WHERE app_id=%d", $app_id);

  // create dev(i,j)
  db_query("INSERT INTO {recommender_slopeone_dev}(app_id, cheese1_id, cheese2_id, count, dev)\n            SELECT %d, n1.{$field_cheese}, n2.{$field_cheese},\n            COUNT(*), AVG(n1.{$field_weight}-n2.{$field_weight}) FROM {{$table_name}} n1\n            INNER JOIN {{$table_name}} n2 ON n1.{$field_mouse}=n2.{$field_mouse}\n            AND n1.{$field_cheese} <> n2.{$field_cheese}\n            GROUP BY n1.{$field_cheese}, n2.{$field_cheese}", $app_id);

  // create P(u,j)
  if ($extension == 'basic') {
    $extension_sql = "AVG(t.{$field_weight}+p.dev)";
  }
  else {
    if ($extension == 'weighted') {

      // the 'weighted slope one'
      $extension_sql = "SUM((t.{$field_weight}+p.dev)*p.count)/SUM(p.count)";
    }
  }

  // haven't implemented the "bipolar" extension of Slope One.
  // generate predictions.
  db_query("INSERT INTO {recommender_prediction}(app_id, mouse_id, cheese_id, prediction, created)\n            SELECT %d, t.{$field_mouse}, p.cheese1_id, {$extension_sql}, %d\n            FROM {recommender_slopeone_dev} p INNER JOIN {{$table_name}} t ON p.cheese2_id=t.{$field_cheese}\n            GROUP BY t.{$field_mouse}, p.cheese1_id", $app_id, $created);

  // remove duplicate prediction
  if ($duplicate == 'eliminate') {
    db_query("DELETE FROM {recommender_prediction} WHERE app_id=%d AND created=%d AND (mouse_id, cheese_id)\n              IN (SELECT {$field_mouse}, {$field_cheese} FROM {{$table_name}})", $app_id, $created);
  }
  db_query("DELETE FROM {recommender_prediction} WHERE app_id=%d AND created<>%d", $app_id, $created);
}