Package org.apache.mahout.cf.taste.impl.model

Source Code of org.apache.mahout.cf.taste.impl.model.GenericDataModel

/**
* 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.common.Refreshable;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.common.NoSuchUserException;
import org.apache.mahout.cf.taste.common.NoSuchItemException;
import org.apache.mahout.cf.taste.impl.common.ArrayIterator;
import org.apache.mahout.cf.taste.impl.common.EmptyIterable;
import org.apache.mahout.cf.taste.impl.common.FastMap;
import org.apache.mahout.cf.taste.impl.common.FastSet;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.model.Item;
import org.apache.mahout.cf.taste.model.Preference;
import org.apache.mahout.cf.taste.model.User;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* <p>A simple {@link DataModel} which uses a given {@link List} of {@link User}s as
* its data source. This implementation is mostly useful for small experiments and is not
* recommended for contexts where performance is important.</p>
*/
public final class GenericDataModel implements DataModel, Serializable {

  private static final Preference[] NO_PREFS_ARRAY = new Preference[0];
  private static final Iterable<Preference> NO_PREFS_ITERABLE = new EmptyIterable<Preference>();

  private final List<User> users;
  private final Map<Object, User> userMap;
  private final List<Item> items;
  private final Map<Object, Item> itemMap;
  private final Map<Object, Preference[]> preferenceForItems;

  /**
   * <p>Creates a new {@link GenericDataModel} from the given {@link User}s (and their preferences).
   * This {@link DataModel} retains all this information in memory and is effectively immutable.</p>
   *
   * @param users {@link User}s to include in this {@link GenericDataModel}
   */
  @SuppressWarnings("unchecked")
  public GenericDataModel(Iterable<? extends User> users) {
    if (users == null) {
      throw new IllegalArgumentException("users is null");
    }

    this.userMap = new FastMap<Object, User>();
    this.itemMap = new FastMap<Object, Item>();
    // I'm abusing generics a little here since I want to use this (huge) map to hold Lists,
    // then arrays, and don't want to allocate two Maps at once here.
    Map<Object, Object> prefsForItems = new FastMap<Object, Object>();
    for (User user : users) {
      userMap.put(user.getID(), user);
      Preference[] prefsArray = user.getPreferencesAsArray();
      for (Preference preference : prefsArray) {
        Item item = preference.getItem();
        Object itemID = item.getID();
        itemMap.put(itemID, item);
        List<Preference> prefsForItem = (List<Preference>) prefsForItems.get(itemID);
        if (prefsForItem == null) {
          prefsForItem = new ArrayList<Preference>();
          prefsForItems.put(itemID, prefsForItem);
        }
        prefsForItem.add(preference);
      }
    }

    List<User> usersCopy = new ArrayList<User>(userMap.values());
    Collections.sort(usersCopy);
    this.users = Collections.unmodifiableList(usersCopy);

    List<Item> itemsCopy = new ArrayList<Item>(itemMap.values());
    Collections.sort(itemsCopy);
    this.items = Collections.unmodifiableList(itemsCopy);

    // Swap out lists for arrays here -- using the same Map. This is why the generics mess is worth it.
    for (Map.Entry<Object, Object> entry : prefsForItems.entrySet()) {
      List<Preference> list = (List<Preference>) entry.getValue();
      Preference[] prefsAsArray = list.toArray(new Preference[list.size()]);
      Arrays.sort(prefsAsArray, ByUserPreferenceComparator.getInstance());
      entry.setValue(prefsAsArray);
    }
    // Yeah more generics ugliness
    this.preferenceForItems = (Map<Object, Preference[]>) (Map<Object, ?>) prefsForItems;
  }

  /**
   * <p>Creates a new {@link GenericDataModel} containing an immutable copy of the data from another
   * given {@link DataModel}.</p>
   *
   * @param dataModel {@link DataModel} to copy
   * @throws TasteException if an error occurs while retrieving the other {@link DataModel}'s users
   */
  public GenericDataModel(DataModel dataModel) throws TasteException {
    this(dataModel.getUsers());
  }

  @Override
  public Iterable<? extends User> getUsers() {
    return users;
  }

  /**
   * @throws NoSuchUserException if there is no such {@link User}
   */
  @Override
  public User getUser(Object id) throws NoSuchUserException {
    User user = userMap.get(id);
    if (user == null) {
      throw new NoSuchUserException();
    }
    return user;
  }

  @Override
  public Iterable<? extends Item> getItems() {
    return items;
  }

  /**
   * @throws NoSuchItemException if there is no such {@link Item}
   */
  @Override
  public Item getItem(Object id) throws NoSuchItemException {
    Item item = itemMap.get(id);
    if (item == null) {
      throw new NoSuchItemException();
    }
    return item;
  }

  @Override
  public Iterable<? extends Preference> getPreferencesForItem(Object itemID) {
    Preference[] prefs = preferenceForItems.get(itemID);
    return prefs == null ? NO_PREFS_ITERABLE : new ArrayIterator<Preference>(prefs);
  }

  @Override
  public Preference[] getPreferencesForItemAsArray(Object itemID) {
    Preference[] prefs = preferenceForItems.get(itemID);
    return prefs == null ? NO_PREFS_ARRAY : prefs;
  }

  @Override
  public int getNumItems() {
    return items.size();
  }

  @Override
  public int getNumUsers() {
    return users.size();
  }

  @Override
  public int getNumUsersWithPreferenceFor(Object... itemIDs) {
    if (itemIDs == null) {
      throw new IllegalArgumentException("itemIDs is null");
    }
    int length = itemIDs.length;
    if (length == 0 || length > 2) {
      throw new IllegalArgumentException("Illegal number of item IDs: " + length);
    }
    if (length == 1) {
      Preference[] prefs = preferenceForItems.get(itemIDs[0]);
      return prefs == null ? 0 : prefs.length;
    } else {
      Preference[] prefs1 = preferenceForItems.get(itemIDs[0]);
      Preference[] prefs2 = preferenceForItems.get(itemIDs[1]);
      if (prefs1 == null || prefs2 == null) {
        return 0;
      }
      Set<Object> users1 = new FastSet<Object>(prefs1.length);
      for (Preference aPrefs1 : prefs1) {
        users1.add(aPrefs1.getUser().getID());
      }
      Set<Object> users2 = new FastSet<Object>(prefs2.length);
      for (Preference aPrefs2 : prefs2) {
        users2.add(aPrefs2.getUser().getID());
      }
      users1.retainAll(users2);
      return users1.size();
    }
  }

  /**
   * @throws UnsupportedOperationException
   */
  @Override
  public void setPreference(Object userID, Object itemID, double value) {
    throw new UnsupportedOperationException();
  }

  /**
   * @throws UnsupportedOperationException
   */
  @Override
  public void removePreference(Object userID, Object itemID) {
    throw new UnsupportedOperationException();
  }

  @Override
  public void refresh(Collection<Refreshable> alreadyRefreshed) {
    // Does nothing
  }

  @Override
  public String toString() {
    return "GenericDataModel[users:" + users + ']';
  }

}
TOP

Related Classes of org.apache.mahout.cf.taste.impl.model.GenericDataModel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.