NUM_SETS = 5 RANK_SIZE = 5 REP = 3 REVIEW_FEATS = {
  'www': ['num_tokens', 'num_sents', 'uni_ratio', 'avg_sent',
    'cap_sent', 'noun_ratio', 'adj_ratio', 'comp_ratio', 'verb_ratio',
    'adv_ratio', 'fw_ratio', 'sym_ratio', 'num_ratio', 'punct_ratio', 'kl',
    'pos_ratio', 'neg_ratio'],
  'sel': ['num_tokens', 'uni_ratio', 'noun_ratio', 'sym_ratio', 'fw_ratio',
    'kl', 'avg_sent', 'punct_ratio'],
  'cap': ['num_tokens', 'num_sents', 'uni_ratio', 'avg_sent',
    'cap_sent', 'noun_ratio', 'adj_ratio', 'comp_ratio', 'verb_ratio',
    'adv_ratio', 'fw_ratio', 'sym_ratio', 'num_ratio', 'punct_ratio', 'kl',
    'pos_ratio', 'neg_ratio'],
  'all': ['num_tokens', 'num_sents', 'uni_ratio', 'avg_sent',
    'cap_sent', 'noun_ratio', 'adj_ratio', 'comp_ratio', 'verb_ratio',
    'adv_ratio', 'fw_ratio', 'sym_ratio', 'num_ratio', 'punct_ratio', 'kl',
    'pos_ratio', 'neg_ratio'],
  'svr_top10': ['noun_ratio', 'uni_ratio', 'sym_ratio', 'punct_ratio',
    'verb_ratio', 'comp_ratio', 'kl'],
  'spear_top10': ['num_tokens', 'num_sents', 'uni_ratio'],
  'spear_top10m': ['num_tokens', 'uni_ratio'],
  'spear_top10': ['num_tokens', 'uni_ratio'],
  'new_sel': ['num_tokens', 'uni_ratio'],
  'ig_top5': ['num_tokens', 'num_sents', 'uni_ratio'],
  'ig_top10': ['num_tokens', 'num_sents', 'uni_ratio'],
  'spear_top1': ['num_tokens'],
  'spear_top2': ['num_tokens', 'num_sents'],
  'spear_top3': ['num_tokens', 'num_sents', 'uni_ratio'],
  'spear_top4': ['num_tokens', 'num_sents', 'uni_ratio'],
  'spear_top5': ['num_tokens', 'num_sents', 'uni_ratio'],
  'spear_top5m': ['num_tokens', 'uni_ratio'],
  'spear_top6': ['num_tokens', 'num_sents', 'uni_ratio'],
  'spear_top7': ['num_tokens', 'num_sents', 'uni_ratio'],
  'spear_top8': ['num_tokens', 'num_sents', 'uni_ratio'],
  'spear_top9': ['num_tokens', 'num_sents', 'uni_ratio']
} AUTHOR_FEATS = {
  'www': ['num_reviews', 'avg_rating', 'num_trustors', 'num_trustees',
    'pagerank'],
  'sel': ['num_trustors'],
  'cap': ['num_reviews', 'avg_rating', 'num_trustors', 'num_trustees',
    'pagerank'],
  'all': ['num_reviews', 'avg_rating', 'num_trustors', 'num_trustees',
    'pagerank', 'num_votes_rec', 'num_votes_giv', 'sd_rating', 'sd_help_rec',
    'sd_help_giv', 'avg_rel_rating', 'avg_help_rec', 'avg_help_giv',
    'avg_rel_help_giv', 'avg_rating_sim', 'avg_help_giv_sim',
    'avg_rating_dir_net', 'avg_help_giv_tru_net'],
  'svr_top10': [],
  'spear_top10': ['num_trustees', 'num_trustors', 'num_reviews'],
  'spear_top10m': ['num_trustees', 'num_trustors', 'num_reviews'],
  'new_sel': ['num_trustees', 'num_trustors', 'num_reviews'],
  'ig_top5': ['pagerank', 'num_trustees'],
  'ig_top10': ['pagerank', 'num_trustees'],
  'spear_top1': [],
  'spear_top2': [],
  'spear_top3': [],
  'spear_top4': [],
  'spear_top5': ['num_trustees'],
  'spear_top5m': ['num_trustees'],
  'spear_top6': ['num_trustees', 'num_trustors'],
  'spear_top7': ['num_trustees', 'num_trustors'],
  'spear_top8': ['num_trustees', 'num_trustors'],
  'spear_top9': ['num_trustees', 'num_trustors']
} VOTER_FEATS = {
  'www': [],
  'sel': ['avg_help_giv', 'avg_help_giv_tru_net'],
  'cap': ['num_trustors', 'num_trustees', 'pagerank', 'avg_rating',
    'avg_rating_dir_net', 'avg_rating_sim', 'avg_help_giv', 'avg_help_giv_sim', 
    'avg_help_giv_tru_net'],
  'all': ['num_reviews', 'avg_rating', 'num_trustors', 'num_trustees',
    'pagerank', 'num_votes_rec', 'num_votes_giv', 'sd_rating', 'sd_help_rec',
    'sd_help_giv', 'avg_rel_rating', 'avg_help_rec', 'avg_help_giv',
    'avg_rel_help_giv', 'avg_rating_sim', 'avg_help_giv_sim',
    'avg_rating_dir_net', 'avg_help_giv_tru_net'],
  'svr_top10': ['avg_help_giv'],
  'spear_top10': ['avg_help_giv'],
  'spear_top10m': ['avg_help_giv'],
  'new_sel': ['avg_help_giv'],
  'ig_top5': [],
  'ig_top10': ['avg_help_giv', 'pagerank', 'avg_rating_dir_net',
    'avg_rating_sim', 'avg_help_giv_sim'],
  'spear_top1': [],
  'spear_top2': [],
  'spear_top3': [],
  'spear_top4': ['avg_help_giv'],
  'spear_top5': ['avg_help_giv'],
  'spear_top5m': ['avg_help_giv'],
  'spear_top6': ['avg_help_giv'],
  'spear_top7': ['avg_help_giv'],
  'spear_top8': ['avg_help_giv'],
  'spear_top9': ['avg_help_giv']
} SIM_FEATS = {
  'www': [],
  'sel': ['jacc_rated', 'common_rated'],
  'cap': ['common_rated', 'jacc_rated', 'cos_ratings', 'pear_ratings',
    'diff_avg_ratings', 'diff_max_ratings', 'diff_min_ratings'],
  'all': ['common_rated', 'jacc_rated', 'cos_ratings', 'pear_ratings',
    'diff_avg_ratings', 'diff_max_ratings', 'diff_min_ratings'],
  'svr_top10': [],
  'svr_top10m': [],
  'new_sel': ['diff_trustors', 'diff_reviews', 'diff_pagerank', 'diff_close',
    'diff_eigen'],
  'spear_top10': [],
  'ig_top5': [],
  'ig_top10': [],
  'spear_top1': [],
  'spear_top2': [],
  'spear_top3': [],
  'spear_top4': [],
  'spear_top5': [],
  'spear_top5m': [],
  'spear_top6': [],
  'spear_top7': [],
  'spear_top8': [],
  'spear_top9': []
} CONN_FEATS = {
  'www': [],
  'cap': ['jacc_trustees', 'jacc_trustors', 'adamic_adar_trustees',
    'adamic_adar_trustors', 'katz'],
  'sel': ['jacc_trustees', 'adamic_adar_trustees', 'adamic_adar_trustors'], 
  'all': ['jacc_trustees', 'jacc_trustors', 'adamic_adar_trustees',
    'adamic_adar_trustors', 'katz'],
  'svr_top10': ['jacc_trustors', 'adamic_adar_trustors'],
  'spear_top10': ['jacc_trustees', 'adamic_adar_trustees',
    'adamic_adar_trustors'],
  'spear_top10m': ['jacc_trustees', 'adamic_adar_trustees',
    'adamic_adar_trustors'],
  'new_sel': ['jacc_trustees', 'adamic_adar_trustees', 'adamic_adar_trustors', 
    'voter_trust',  'author_trust', 'inv_from_vot_path', 'inv_from_aut_path'],
  'ig_top5': [],
  'ig_top10': [],
  'spear_top1': [],
  'spear_top2': [],
  'spear_top3': [],
  'spear_top4': [],
  'spear_top5': [],
  'spear_top5m': [],
  'spear_top6': ['jacc_trustees'],
  'spear_top7': ['jacc_trustees'],
  'spear_top8': ['jacc_trustees', 'adamic_adar_trustees'],
  'spear_top9': ['jacc_trustees', 'adamic_adar_trustees',
      'adamic_adar_trustors']
} def calculate_rmse(result, truth):
  """ Calculates RMSE (Root Mean Squared Error) of a prediction task.

      Args:
        result: a list of predicted values assigned to the instances.
        truth: a list with the corrected values.

      Returns:
        A real value representing the RMSE.
  """
  errors = calculate_squared_errors(result, truth)
  rmse = sqrt(float(sum(errors)) / len(errors))
  return rmse def calculate_avg_ndcg(votes, reviews, pred, truth, k):
  """ Calculates the average nDCG by grouping votes in (user, product) pairs in
      order to compose rankings.

      Args:
        votes: set of votes to evaluate through grouping and ranking.
        reviews: dictionary of reviews.
        pred: list of floats with predicted relevances for each vote in votes.
        truth: list of floats (or integers) with true relevances for each vote
      in votes.
        k: an integer with the limit position of the ranking to calculate the
      score.

      Returns:
        A float in range [0, 1] with nDCG@K score.
  """
  pred_group = {}
  truth_group = {}
  for i, vote in enumerate(votes):
    voter = vote['voter']
    product = reviews[vote['review']]['product']
    key = (voter, product)
    if key not in pred_group:
      pred_group[key] = []
      truth_group[key] =[]
    pred_group[key].append(pred[i])
    truth_group[key].append(truth[i])
  score_sum = 0.0
  for key in pred_group:
    ndcg = calculate_ndcg(pred_group[key], truth_group[key], k)
    score_sum += ndcg
  score = score_sum / len(pred_group)
  return score def compute_avg_user(users):
  """ Creates an average user model for mean imputation.

      Args:
        users: the modeled dictionary of users.

      Returns:
        A dictionary representing a user with average feature values.
  """
  avg_user = {}
  for feature in users.itervalues().next():
    avg_user[feature] = 0.0
  missing = []
  for feature in avg_user:
    if feature in ['id', 'ratings', '_id', 'similars', 'trustees', 'trustors']:
      continue
    avg_user[feature] = 0.0
    count = 0
    for user in users.itervalues():
      if not isnan(user[feature]):
        try:
          avg_user[feature] += user[feature]
        except:
          print feature
          print avg_user
          print user
        count += 1
    if count == 0:
      missing.append(feature)
    else:
      avg_user[feature] /= float(count)
  for feature in missing:
    if feature == 'avg_help_giv_sim' or feature == 'avg_help_giv_tru_net':
      avg_user[feature] = avg_user['avg_help_giv']
    elif feature == 'avg_rating_sim' or feature == 'avg_rating_fir_net':
      avg_user[feature] = avg_user['avg_rating']
  avg_user['id'] = avg_user['_id'] = None
  avg_user['ratings'] = {}
  avg_user['similars'] = []
  avg_max = []
  avg_min = []
  for user in users.itervalues():
    if user['ratings']:
      avg_max.append(max(user['ratings'].values()))
      avg_min.append(min(user['ratings'].values()))
  avg_user['avg_max'] = mean(avg_max)
  avg_user['avg_min'] = mean(avg_min)
  return avg_user def compute_avg_model(model):
  """ Creates an average user model for mean imputation.

      Args:
        users: the modeled dictionary of users.

      Returns:
        A dictionary representing a user with average feature values.
  """
  avg_entity = {}
  for feature in model.itervalues().next():
    avg_entity[feature] = 0.0
  for feature in avg_entity:
    count = 0.0
    for user in model.itervalues():
      if not isnan(user[feature]):
        avg_entity[feature] += user[feature]
        count += 1.0
    avg_entity[feature] /= count
  return avg_entity class BiasModel(object):
  """ Class implementing a Bias Baseline Model. """

  def __init__(self):
    """ Discriminates existing attributes, initilizing all to None.

        Args:
          None.

        Returns:
          None.
    """
    self.product_bias = None
    self.author_bias = None
    self.voter_bias = None
    self.overall_mean = None

  def _initialize(self, votes, reviews):
    """ Initializes bias values for each entity and calculates overall average.

        Observation:
        - Instead of using random values, static estimation is used by averaging
          deviation from mean (or mean + previous bias).

        Args:
          votes: list of votes (training set).
          reviews: dictionary of reviews, to obtain product.
        
        Returns:
          None. Instance fields are updated.
    """
    self.voter_bias = {}
    voter_count = {}
    self.author_bias = {}
    author_count = {}
    self.product_bias = {}
    product_count = {}
    self.overall_mean = 0
    count = 0
    for vote in votes:
      self.overall_mean += vote['vote']
      count += 1
    self.overall_mean /= float(count)
    for vote in votes:
      voter = vote['voter']
      if voter not in self.voter_bias:
        self.voter_bias[voter] = 0
        voter_count[voter] = 0
      self.voter_bias[voter] += (vote['vote'] - self.overall_mean)
      voter_count[voter] += 1
    for voter in self.voter_bias:
      self.voter_bias[voter] /= float(voter_count[voter])
    for vote in votes:
      voter = vote['voter']
      author = vote['author']
      if author not in self.author_bias:
        self.author_bias[author] = 0
        author_count[author] = 0
      self.author_bias[author] += (vote['vote'] - self.overall_mean -
          self.voter_bias[voter]) 
      author_count[author] += 1
    for author in self.author_bias:
      self.author_bias[author] /= float(author_count[author])
    for vote in votes:
      voter = vote['voter']
      author = vote['author']
      product = reviews[vote['review']]['product']
      if product not in self.product_bias:
        self.product_bias[product] = 0
        product_count[product] = 0
      self.product_bias[product] += (vote['vote'] - self.overall_mean -
          self.voter_bias[voter] - self.author_bias[author]) 
      product_count[product] += 1
    for product in self.product_bias:
      self.product_bias[product] /= float(product_count[product])
    
  def fit(self, votes, reviews):
    """ Fits a Bias Baseline model given training set (votes).

        Args:
          vote: list of votes, represented as dictionaries (training set).
          reviews: dictionary of reviews.
        
        Returns:
          None. Instance fields are updated.
    """
    self._initialize(votes, reviews)
    previous = float('inf')
    for _ in xrange(_ITER):
      for vote in votes:
        voter = vote['voter']
        author = vote['author']
        product = reviews[vote['review']]['product']
        pred = self.overall_mean + self.voter_bias[voter] + \
            self.author_bias[author] + self.product_bias[product]
        error = pred - float(vote['vote']) 
        # one does not depend on the other: no need for temporary variables
        self.voter_bias[voter] -= _ALPHA * 2 * (error + _BETA *
            self.voter_bias[voter])
        self.author_bias[author] -= _ALPHA * 2 * (error + _BETA * \
            self.author_bias[author])
        self.product_bias[product] -= _ALPHA * 2 * (error + _BETA * \
            self.product_bias[product])
      value = 0.0
      for vote in votes:
        voter = vote['voter']
        author = vote['author']
        product = reviews[vote['review']]['product']
        pred = self.overall_mean + self.voter_bias[voter] + \
            self.author_bias[author] + self.product_bias[product]
        value += (vote['vote'] - pred) ** 2
      for voter in self.voter_bias:
        value += self.voter_bias[voter] ** 2
      for author in self.author_bias:
        value += self.author_bias[author] ** 2
      for product in self.product_bias:
        value += self.product_bias[product] ** 2
      if abs(previous - value) < _TOL:
        print 'Convergence'
        break
      previous = value

  def fit_transform(self, votes, reviews):
    """ Fits Bias Model on data and adjust vote values to contain only unbiased
        value.

        Args:
          votes: list of votes to learn model and transform values.
          reviews: dictionary of reviews.

        Returns:
          None. Changes are made in place.
    """
    self.fit(votes, reviews)
    return self.transform(votes, reviews)

  def predict(self, votes, reviews):
    """ Predicts a set of vote examples using previous fitted model.

        Args:
          votes: list of dictionaries, representing votes, to predict
        helpfulness vote value.
          reviews: dictionary of reviews.

        Returns:
          A list of floats with predicted vote values.
    """
    pred = []
    for vote in votes:
      voter = vote['voter']
      author = vote['author']
      product = reviews[vote['review']]['product'] 
      value = self.overall_mean
      if voter in self.voter_bias: # if no bias, we assume 0
        dot += self.voter_bias[voter]
      if author in self.author_bias:
        dot += self.author_bias[author]
      if product in self.product_bias:
        dot += self.product_bias[product]
      pred.append(value)
    return pred

  def transform(self, votes, reviews):
    """ Removes bias values and overall mean from votes.

        Args:
          votes: list of votes to remove bias; it should be the same list
        fitted, because all entities have to have a bias associated.
          reviews: dictionary of reviews.

        Returns:
          A new list of votes with unbiased values. 
    """
    new_votes = []
    for vote in votes:
      new_vote = vote.copy()
      voter = vote['voter']
      author = vote['author']
      product = reviews[vote['review']]['product']
      new_vote['vote'] -= self.overall_mean + self.voter_bias[voter] + \
          self.author_bias[author] + self.product_bias[product]
      new_votes.append(new_vote) 
    return new_votes

  def add_bias(self, votes, reviews, pred):
    """ Removes bias values and overall mean from votes.

        Args:
          votes: votes whose values are being predicted.
          reviews: dictionary of reviews.
          pred: unbiased estimates of votes.

        Returns:
          None. The pred list is changed in place.
    """
    for index, vote in enumerate(votes):
      voter = vote['voter']
      author = vote['author']
      product = reviews[vote['review']]['product']
      if isnan(pred[index]):
        continue
      pred[index] += self.overall_mean
      if voter in self.voter_bias:
        pred[index] += self.voter_bias[voter]
      if author in self.author_bias:
        pred[index] += self.author_bias[author]
      if product in self.product_bias:
        pred[index] += self.product_bias[product] def fit_scaler(scale_type, data):
  """ Fits a scaler to a data.

      Args:
        scale_type: indicates the type of scale to adopt. It can be 'standard' to 
          scale with zero mean and unit standard deviation, or 'minmax' for range
          between 0 and 1.
        data: list of arrays with instances to be scaled.

      Returns:
        A scaler that fits the data.
  """
  if scale_type == 'standard':
    return StandardScaler(copy=False).fit(data)
  if scale_type == 'minmax':
    return MinMaxScaler(copy=False).fit(data) def fit_scaler_by_query(scale_type, data, qid, qid_dep_size):
  """ Fits a scaler to data.

      Args:
        scale_type: indicates the type of scale to adopt. It can be 'standard' to 
          scale with zero mean and unit standard deviation, or 'minmax' for range
          between 0 and 1.
        data: list of arrays with instances to be scaled.
        qid: list of query ids associated to each instance.
        qid_dep_size: number of features query-dependent, which are in the end
          of the instance array of features.

      Returns:
        A scaler that fits the data.
  """
  data = array(data)
  dim = data.shape[1]
  q_undep, q_dep = hsplit(data, [dim-qid_dep_size])
  if scale_type == 'standard':
    overall_scaler = StandardScaler(copy=False).fit(q_undep)
  if scale_type == 'minmax':
    overall_scaler = MinMaxScaler(copy=False).fit(q_undep)
  return overall_scaler def scale_features(scaler, data, qid=None, qid_dep_size=None, 
    scale_type='minmax'):
  """ Scales features from data given a fitted scaler. 

      Args:
        scaler: a scaler object with transform function.
        data: list of instances to scale. 
        qid: list of query ids associated to each instance.
        qid_dep_size: number of features query-dependent, which are in the end
          of the instance array of features.
        scale_type: indicates the type of scale to adopt. It can be 'standard' to 
          scale with zero mean and unit standard deviation, or 'minmax' for range
          between 0 and 1.

      Returns:
        A pair with scaled train and test sets. 
  """
  data = array(data)
  if qid is None:
    data = scaler.transform(data)
  else:
    dim = data.shape[1]
    overall_scaler = scaler 
    q_undep, q_dep = hsplit(data, [dim-qid_dep_size])
    q_undep = overall_scaler.transform(q_undep)
    qid_grouped = group_by_qid(q_dep, qid)
    q_scalers = {}
    for q in qid_grouped: 
      if scale_type == 'minmax':
        q_scalers[q] = MinMaxScaler(copy=False).fit(qid_grouped[q])
      if scale_type == 'standard':
        q_scalers[q] = StandardScaler(copy=False).fit(qid_grouped[q])
    for i in xrange(q_dep.shape[0]):
      q_dep[i] = q_scalers[qid[i]].transform([q_dep[i]])
    data = hstack((q_undep, q_dep))
  return data 
from commands import getstatusoutput, getoutput
from sys import argv
from numpy import nan, isnan
from pickle import load
from algo.const import NUM_SETS, RANK_SIZE, REP, REVIEW_FEATS, AUTHOR_FEATS, \
    VOTER_FEATS, SIM_FEATS, CONN_FEATS
from perf.metrics import calculate_rmse, calculate_avg_ndcg
from util.avg_model import compute_avg_user, compute_avg_model 
from util.bias import BiasModel
from util.scaling import fit_scaler, fit_scaler_by_query, scale_features
        avg_sim: dictionary of an average similarity relation.
        avg_conn: dictionary of an average connection strength relation.

      Returns:
        A triple with an list of features' lists, a list of true votes and a
      list with query ids. 
  """
  X = []
  y = []
  qid = []
  voter_map = {}
  q_count = 0
  for vote in votes:
    example = []
    review = reviews[vote['review']]
    for feature in REVIEW_FEATS[_FEAT_TYPE]:
      example.append(review[feature])
    author = users[vote['author']] if vote['author'] in users else avg_user
    for feature in AUTHOR_FEATS[_FEAT_TYPE]:
      if isnan(author[feature]):
        example.append(avg_user[feature]) 
      else:
        example.append(author[feature])
    voter = users[vote['voter']] if vote['voter'] in users else avg_user
    av = (author['id'], voter['id'])
    u_sim = sim[av] if av in sim else avg_sim
    for feature in SIM_FEATS[_FEAT_TYPE]:
      if isnan(u_sim[feature]):
        example.append(avg_sim[feature]) 
      else:
        example.append(u_sim[feature]) 
    u_conn = conn[av] if av in conn else avg_conn
    for feature in CONN_FEATS[_FEAT_TYPE]:
      if isnan(u_conn[feature]):
        example.append(avg_conn[feature]) 
      else:
        example.append(u_conn[feature]) 
    X.append(example)
    y.append(vote['vote'])
    qid.append(int(vote['voter']))
  return X, y, qid


def main():
  """ Predicts votes by applying LambdaMART technique.

      Args:
        None.

      Returns:
        None.
  """
  load_args()
  
  for i in xrange(NUM_SETS):
    print 'Reading data'
    reviews = load(open('%s/reviews-%d.pkl' % (_PKL_DIR, i), 'r'))
    users = load(open('%s/users-%d.pkl' % (_PKL_DIR, i), 'r'))
    train = load(open('%s/train-%d.pkl' % (_PKL_DIR, i), 'r'))
    test = load(open('%s/test-%d.pkl' % (_PKL_DIR, i), 'r'))
    val = load(open('%s/validation-%d.pkl' % (_PKL_DIR, i), 'r'))
    sim = load(open('%s/sim-%d.pkl' % (_PKL_DIR, i), 'r'))
    conn = load(open('%s/conn-%d.pkl' % (_PKL_DIR, i), 'r'))
   
    train_truth = [v['vote'] for v in train]
    if _BIAS:
      bias = BiasModel()
      train = bias.fit_transform(train, reviews)
 
    print 'Creating average user (for mean imputation)'
    avg_user = compute_avg_user(users)
    avg_sim = compute_avg_model(sim)
    avg_conn = compute_avg_model(conn)
    X_train, y_train, qid_train = generate_input(reviews, users, sim, conn,
        train, avg_user, avg_sim, avg_conn)
    X_val, _, qid_val = generate_input(reviews, users, sim, conn, val, 
        avg_user, avg_sim, avg_conn)
    X_test, _, qid_test = generate_input(reviews, users, sim, conn, 
        test, avg_user, avg_sim, avg_conn)
    
    scaler = fit_scaler('minmax', X_train)
    X_train = scale_features(scaler, X_train)
    X_val = scale_features(scaler, X_val)
    X_test = scale_features(scaler, X_test)

    print 'Outputting model'
    outfile = open('%s/rank_train-%s-%d.dat' % (_DATA_DIR, _CONF_STR, i), 'w')
    train_index = output_model(X_train, y_train, qid_train, outfile)
    outfile.close()
    outfile = open('%s/rank_val-%s-%d.dat' % (_DATA_DIR, _CONF_STR, i), 'w')
    val_index = output_model(X_val, None, qid_val, outfile)
    outfile.close()
    outfile = open('%s/rank_test-%s-%d.dat' % (_DATA_DIR, _CONF_STR, i), 'w')
    test_index = output_model(X_test, None, qid_test, outfile)
    outfile.close()

    for j in xrange(REP):
      print 'Fitting model'
      print getoutput(('java -jar lib/ranklib/RankLib.jar -train '
          '%s/rank_train-%s-%d.dat -save %s/lambdamart_model-%s-%d-%d.dat '
          '-gmax 5 -ranker 6 -metric2t NDCG@5 -tree %d -leaf %d -shrinkage '
          '%f') % (_DATA_DIR, _CONF_STR, i, _MODEL_DIR, _CONF_STR, i, j, _T,
          _L, _ALPHA)) 

      print 'Evaluating in train'
      print getoutput(('java -jar lib/ranklib/RankLib.jar -load '
          '%s/lambdamart_model-%s-%d-%d.dat -rank %s/rank_train-%s-%d.dat '
          '-score %s/rank_pred_train-%s-%d-%d.dat -gmax 5 -metric2T NDCG@5') % \
          (_MODEL_DIR, _CONF_STR, i, j, _DATA_DIR, _CONF_STR, i, _DATA_DIR,
          _CONF_STR, i, j))
      raw_pred = []
      predfile = open('%s/rank_pred_train-%s-%d-%d.dat' % (_DATA_DIR, _CONF_STR,
          i, j), 'r')
      raw_pred = [float(p.strip().split()[2]) for p in predfile]
      predfile.close()
      pred = [raw_pred[k] for k in train_index]
      if _BIAS:
        bias.add_bias(train, reviews, pred)
      print '~ Training error on set %d repetition %d' % (i, j)
      print 'RMSE: %f' % calculate_rmse(pred, train_truth)

      print 'nDCG@%d: %f' % (RANK_SIZE, calculate_avg_ndcg(train, reviews,