├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── codecov.yml ├── pom.xml ├── rival-core ├── pom.xml └── src │ ├── main │ └── java │ │ └── net │ │ └── recommenders │ │ └── rival │ │ └── core │ │ ├── AbstractParser.java │ │ ├── DataModel.java │ │ ├── DataModelFactory.java │ │ ├── DataModelIF.java │ │ ├── DataModelUtils.java │ │ ├── Parser.java │ │ ├── ParserWithIdMapping.java │ │ ├── SimpleParser.java │ │ ├── TemporalDataModel.java │ │ ├── TemporalDataModelIF.java │ │ ├── UIPParser.java │ │ ├── ext │ │ ├── MahoutDataModel.java │ │ └── RankSysDataModel.java │ │ └── package-info.java │ └── test │ └── java │ └── net │ └── recommenders │ └── rival │ └── core │ └── DataModelTest.java ├── rival-evaluate ├── pom.xml ├── run.sh └── src │ ├── main │ ├── java │ │ └── net │ │ │ └── recommenders │ │ │ └── rival │ │ │ └── evaluation │ │ │ ├── Pair.java │ │ │ ├── metric │ │ │ ├── AbstractMetric.java │ │ │ ├── EvaluationMetric.java │ │ │ ├── EvaluationMetricRunner.java │ │ │ ├── MultipleEvaluationMetricRunner.java │ │ │ ├── error │ │ │ │ ├── AbstractErrorMetric.java │ │ │ │ ├── MAE.java │ │ │ │ ├── RMSE.java │ │ │ │ └── package-info.java │ │ │ ├── package-info.java │ │ │ └── ranking │ │ │ │ ├── AbstractRankingMetric.java │ │ │ │ ├── MAP.java │ │ │ │ ├── NDCG.java │ │ │ │ ├── PopularityStratifiedRecall.java │ │ │ │ ├── Precision.java │ │ │ │ ├── Recall.java │ │ │ │ └── package-info.java │ │ │ ├── package-info.java │ │ │ ├── parser │ │ │ ├── TrecEvalParser.java │ │ │ └── package-info.java │ │ │ ├── statistics │ │ │ ├── ConfidenceInterval.java │ │ │ ├── EffectSize.java │ │ │ ├── StandardError.java │ │ │ ├── StatisticalSignificance.java │ │ │ ├── StatisticsRunner.java │ │ │ └── package-info.java │ │ │ └── strategy │ │ │ ├── AbstractStrategy.java │ │ │ ├── AllItems.java │ │ │ ├── EvaluationStrategy.java │ │ │ ├── EvaluationStrategyPerItem.java │ │ │ ├── MultipleStrategyRunner.java │ │ │ ├── MultipleStrategyRunnerInfile.java │ │ │ ├── RelPlusN.java │ │ │ ├── StrategyIO.java │ │ │ ├── StrategyRunner.java │ │ │ ├── StrategyRunnerInfile.java │ │ │ ├── TestItems.java │ │ │ ├── TrainItems.java │ │ │ ├── UserTest.java │ │ │ └── package-info.java │ └── resources │ │ ├── allitems.strategy.properties │ │ ├── metric.properties │ │ ├── relplusn.strategy.properties │ │ ├── statistics.properties │ │ ├── strategy.properties │ │ ├── testitems.strategy.properties │ │ ├── trainitems.strategy.properties │ │ └── usertest.strategy.properties │ └── test │ └── java │ └── net │ └── recommenders │ └── rival │ └── evaluation │ ├── metric │ ├── MAETest.java │ ├── MAPTest.java │ ├── NDCGTest.java │ ├── PopularityStratifiedRecallTest.java │ ├── PrecisionTest.java │ ├── RMSETest.java │ └── RecallTest.java │ └── statistics │ └── StatisticsTest.java ├── rival-examples ├── pom.xml └── src │ └── main │ └── java │ └── net │ └── recommenders │ └── rival │ └── examples │ ├── CompletePipelineInMemory.java │ ├── DataDownloader.java │ ├── mdp │ ├── CrossValidationRecSysEvaluator.java │ ├── EvaluationMetrics.java │ ├── MahoutItemBasedCFRecSysEvaluator.java │ └── Main.java │ ├── movielens100k │ ├── CrossValidatedMahoutKNNRecommenderEvaluator.java │ ├── IterativeCrossValidatedMahoutKNNRecommenderEvaluator.java │ ├── RandomSplitMahoutKNNRecommenderEvaluator.java │ ├── TemporalSplitMahoutKNNRecommenderEvaluator.java │ └── package-info.java │ ├── movietweetings │ ├── RandomMahoutIBRecommenderEvaluator.java │ └── package-info.java │ └── package-info.java ├── rival-package ├── pom.xml └── src │ └── text │ ├── APL.txt │ └── LICENSE.txt ├── rival-recommend ├── pom.xml └── src │ ├── main │ ├── java │ │ └── net │ │ │ └── recommenders │ │ │ └── rival │ │ │ └── recommend │ │ │ └── frameworks │ │ │ ├── AbstractRunner.java │ │ │ ├── MultipleRecommendationRunner.java │ │ │ ├── RecommendationRunner.java │ │ │ ├── RecommenderIO.java │ │ │ ├── exceptions │ │ │ ├── RecommenderException.java │ │ │ └── package-info.java │ │ │ ├── lenskit │ │ │ ├── EventDAOWrapper.java │ │ │ ├── LenskitRecommenderRunner.java │ │ │ └── package-info.java │ │ │ ├── librec │ │ │ ├── DataDAOWrapper.java │ │ │ └── LibrecRecommenderRunner.java │ │ │ ├── mahout │ │ │ ├── DataModelWrapper.java │ │ │ ├── GenericRecommenderBuilder.java │ │ │ ├── MahoutRecommenderRunner.java │ │ │ ├── PopularityBasedRecommender.java │ │ │ └── package-info.java │ │ │ ├── package-info.java │ │ │ └── ranksys │ │ │ ├── ItemIndexWrapper.java │ │ │ ├── PreferenceDataWrapper.java │ │ │ ├── RanksysRecommenderRunner.java │ │ │ ├── UserIndexWrapper.java │ │ │ └── package-info.java │ └── resources │ │ ├── lenskit.item-based.properties │ │ ├── lenskit.properties │ │ ├── lenskit.svd.properties │ │ ├── lenskit.user-based.properties │ │ ├── librec.svd.properties │ │ ├── mahout.item-based.properties │ │ ├── mahout.properties │ │ ├── mahout.svd.properties │ │ ├── mahout.user-based.properties │ │ ├── ranksys.properties │ │ ├── ranksys.svd.properties │ │ └── ranksys.user-based.properties │ └── test │ └── java │ └── net │ └── recommenders │ └── rival │ └── recommend │ └── frameworks │ └── mahout │ └── GenericRecommenderBuilderTest.java ├── rival-split ├── pom.xml ├── run.sh ├── scripts │ ├── crossvalidation_splitter.sh │ ├── random_splitter.sh │ └── temporal_splitter.sh └── src │ ├── main │ ├── java │ │ └── net │ │ │ └── recommenders │ │ │ └── rival │ │ │ └── split │ │ │ ├── parser │ │ │ ├── AbstractLastfmCelmaParser.java │ │ │ ├── LastfmCelma1KParser.java │ │ │ ├── LastfmCelma360KParser.java │ │ │ ├── MovielensParser.java │ │ │ ├── ParserRunner.java │ │ │ └── package-info.java │ │ │ └── splitter │ │ │ ├── CrossValidationSplitter.java │ │ │ ├── IterativeCrossValidationSplitter.java │ │ │ ├── IterativeLeaveOneOutSplitter.java │ │ │ ├── RandomSplitter.java │ │ │ ├── Split.java │ │ │ ├── Splitter.java │ │ │ ├── SplitterRunner.java │ │ │ ├── TemporalSplitter.java │ │ │ ├── ValidationSplitter.java │ │ │ └── package-info.java │ └── resources │ │ ├── lastfmcelma360k.cv.global.artist.splitter.properties │ │ ├── lastfmcelma360k.rnd.global.artist.splitter.properties │ │ ├── movielens100k.cv.global.splitter.properties │ │ ├── movielens100k.cv.peruser.splitter.properties │ │ ├── movielens100k.rnd.global.splitter.properties │ │ └── movielens100k.rnd.peruser.splitter.properties │ └── test │ └── java │ └── net │ └── recommenders │ └── rival │ └── split │ └── splitter │ └── SplitTest.java └── src ├── assemble └── source-package.xml ├── main └── resources │ └── rival_checks.xml └── site ├── markdown ├── index.md └── releases │ ├── index.md.vm │ ├── relnotes.vm │ └── rival-0.1.md ├── resources └── logo.png └── site.xml /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.iml 3 | nbactions.xml 4 | .idea/ 5 | nb-configuration.xml 6 | rival-examples/data/ 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | sudo: false 5 | notifications: 6 | slack: 7 | secure: IdTbYOGWdDQsVowiseeb09qk/T0zdadBSZeofi4L015xzFfuMs6tiOECrEjNr+2Pa7vbZlnCQRgWdFKBRL3urXf7+LO7RI9j57SsJ8sI6LaqbMJHcvs8jxm4Lp1HT4QEzHwrxPJxJtmAv/w6lJ95hgTejcQktgqfMsbGSlfkfaE= 8 | script: "mvn cobertura:cobertura" 9 | after_success: 10 | - bash <(curl -s https://codecov.io/bash) 11 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at info@recommenders.net. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | We're very happy if you want to contribute to RiVal. If you are not sure on what to work on, have a look a the current [issues](/../../issues/). 2 | 3 | If you have any questions on how to use RiVal, you may get ann answer by asking on the 4 | the RiVal [mailing list](https://groups.google.com/forum/#!forum/rival-user) or [Gitter chat room](https://gitter.im/recommenders/rival 5 | ). 6 | 7 | 8 | If you want to contribute, do it in the form of GitHub pull requests. To do this: 9 | 10 | 1. Find the [issue](/../../issues/) you want to fix, or create a new one describing what your contribution fixes. 11 | 1. Fork RiVal [rival](/../../) on GitHub 12 | 2. Push your changes to your fork 13 | 3. Submit a pull request via the GitHub Web interface 14 | 15 | Note that all your contributions will be licensed under RiVal's copyright licences (Apache). If you are committing a new class, make sure to include the copyright statement at the top. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RiVal 2 | 3 | 4 | [![Build Status](https://travis-ci.org/recommenders/rival.png?branch=master)](https://travis-ci.org/recommenders/rival) 5 | [![Join the chat at https://gitter.im/recommenders/rival](https://badges.gitter.im/recommenders/rival.svg)](https://gitter.im/recommenders/rival?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 6 | [![codecov](https://codecov.io/gh/recommenders/rival/branch/master/graph/badge.svg)](https://codecov.io/gh/recommenders/rival) 7 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 8 | 9 | 10 | 11 | **RiVal** is a toolkit for data splitting and evaluation of recommender systems. This page contains information on how to work with the RiVal code and how to compile RiVal. 12 | For more information about RiVal and the documentation, visit the RiVal [website](http://rival.recommenders.net) or [wiki][]. 13 | If you have not used RiVal before, do check out the [Getting Started][] guide. 14 | 15 | [website]: http://rival.recommenders.net 16 | [wiki]: http://github.com/recommenders/rival/wiki/ 17 | [Getting Started]: http://github.com/recommenders/rival/wiki/GettingStarted 18 | 19 | RiVal is made available under Apache License, Version 2.0. 20 | 21 | ## Installation and Dependency Management 22 | 23 | RiVal is built and deployed via [Maven][]. In order to install it, check out 24 | this repository and run `mvn install`. This will make it available for other Maven projects as a dependency. 25 | 26 | [Maven]: http://maven.apache.org 27 | 28 | ## Modules 29 | 30 | RiVal is comprised of one top-level module and four sub-modules. The top-level `rival` 31 | module is a container module used to build the submodules and provide all needed settings 32 | and dependencies. The five sub-modules are as follows: 33 | 34 | * `rival-core` -- the common data structures and similar object used throughout RiVal. 35 | * `rival-evaluate` -- the evaluation module, contains metrics and strategies used for evaluation. 36 | * `rival-examples` -- a module containing examples on how to use RiVal programmatically. 37 | * `rival-recommend` -- the recommendation modeule, contains hooks to Apache Mahout and LensKit. 38 | * `rival-split` -- the data splitting module, contains different data splitting strategies. 39 | * `rival-package` -- a configuration module for bulding rival distributions. 40 | 41 | ## Contributing to RiVal 42 | 43 | We're very happy if you want to contribute to RiVal. If you are not sure on what to work on, have a look a the current [issues](/../../issues/). 44 | 45 | If you want to contribute, do it in the form of GitHub pull requests. To do this: 46 | 47 | 48 | 1. Find the [issue](/../../issues/) you want to fix, or create a new one describing what your contribution fixes. 49 | 1. Fork RiVal [rival](/../../) on GitHub 50 | 2. Push your changes to your fork 51 | 3. Submit a pull request via the GitHub Web interface 52 | 53 | Note that all your contributions will be licensed under RiVal's copyright licences (Apache). If you are committing a new class, make sure to include the copyright statement at the top. 54 | 55 | ## Mailing list 56 | 57 | To subscribe to the rival mailing list, visit and join the [rival-users](https://groups.google.com/forum/#!forum/rival-user) Google group. 58 | 59 | ## More information 60 | * [RiVal website](http://rival.recommenders.net) 61 | * RiVal in [research](../../wiki/Research) 62 | * RiVal on [Maven central](http://search.maven.org/#search%7Cga%7C1%7Cnet.recommenders.rival) 63 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | 2 | codecov: 3 | branch: master 4 | bot: null 5 | 6 | coverage: 7 | precision: 2 8 | round: down 9 | range: "70...100" 10 | 11 | notify: 12 | slack: 13 | default: 14 | url: null 15 | threshold: null 16 | branches: null 17 | attachments: "sunburst, diff" 18 | 19 | hipchat: 20 | default: 21 | url: null 22 | notify: no 23 | threshold: null 24 | branches: null 25 | card: yes 26 | only_pulls: null 27 | message: null 28 | 29 | gitter: 30 | default: 31 | url: null 32 | threshold: null 33 | branches: null 34 | message: null 35 | 36 | webhook: 37 | default: 38 | url: null 39 | threshold: null 40 | branches: null 41 | only_pulls: null 42 | 43 | irc: 44 | default: 45 | server: null 46 | channel: null 47 | branches: null 48 | threshold: null 49 | message: null 50 | 51 | status: 52 | project: 53 | default: 54 | target: auto 55 | threshold: null 56 | branches: null 57 | 58 | patch: 59 | default: 60 | target: auto 61 | branches: null 62 | 63 | changes: 64 | default: 65 | branches: null 66 | 67 | ignore: null 68 | 69 | comment: 70 | layout: "header, diff, changes, sunburst, uncovered, tree" 71 | branches: null 72 | behavior: default 73 | -------------------------------------------------------------------------------- /rival-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | rival 7 | net.recommenders.rival 8 | 0.3-SNAPSHOT 9 | 10 | rival-core 11 | jar 12 | RiVal Core 13 | 14 | 15 | 16 | junit 17 | junit 18 | 19 | 20 | 21 | org.apache.commons 22 | commons-csv 23 | 1.0 24 | 25 | 26 | 27 | org.apache.mahout 28 | mahout-core 29 | ${mahout.version} 30 | 31 | 32 | asm 33 | asm 34 | 35 | 36 | commons-lang 37 | commons-lang 38 | 39 | 40 | 41 | 42 | 43 | org.ranksys.RankSys 44 | RankSys-core 45 | ${ranksys.version} 46 | 47 | 48 | org.ranksys.RankSys 49 | RankSys-fast 50 | ${ranksys.version} 51 | 52 | 53 | -------------------------------------------------------------------------------- /rival-core/src/main/java/net/recommenders/rival/core/AbstractParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.core; 17 | 18 | /** 19 | * Abstract class for datamodel parsers. 20 | * 21 | * @author Alan. 22 | */ 23 | public abstract class AbstractParser { 24 | 25 | /** 26 | * Default for the column index for the user id in the file. 27 | */ 28 | public static final int USER_TOK = 0; 29 | /** 30 | * Default for the column index for the item id in the file. 31 | */ 32 | public static final int ITEM_TOK = 1; 33 | /** 34 | * Default for the column index for the interaction value (e.g. rating) in 35 | * the file. 36 | */ 37 | public static final int PREFERENCE_TOK = 2; 38 | /** 39 | * Default for the column index for the timestamp in the file. 40 | */ 41 | public static final int TIME_TOK = 3; 42 | /** 43 | * Default for whether the file contains a column header. 44 | */ 45 | public static final boolean HAS_HEADER = false; 46 | /** 47 | * Default for the column delimiter. 48 | */ 49 | public static final char DELIMITER = ','; 50 | /** 51 | * The column index for the user id in the file. 52 | */ 53 | private int userTok; 54 | /** 55 | * The column index for the item id in the file. 56 | */ 57 | private int itemTok; 58 | /** 59 | * The column index for the interaction value (e.g. rating) in the file. 60 | */ 61 | private int prefTok; 62 | /** 63 | * The column index for the timestamp in the file. 64 | */ 65 | private int timeTok; 66 | /** 67 | * Whether the file contains a column header. 68 | */ 69 | private boolean hasHeader; 70 | /** 71 | * The column delimiter. 72 | */ 73 | private char delimiter; 74 | 75 | /** 76 | * Default constructor. 77 | */ 78 | public AbstractParser() { 79 | this.userTok = USER_TOK; 80 | this.itemTok = ITEM_TOK; 81 | this.prefTok = PREFERENCE_TOK; 82 | this.timeTok = TIME_TOK; 83 | this.hasHeader = HAS_HEADER; 84 | this.delimiter = DELIMITER; 85 | } 86 | 87 | /** 88 | * Gets the delimiter. 89 | * 90 | * @return the delimiter 91 | */ 92 | public char getDelimiter() { 93 | return delimiter; 94 | } 95 | 96 | /** 97 | * Checks if this parser has a header. 98 | * 99 | * @return true if it has a header 100 | */ 101 | public boolean isHasHeader() { 102 | return hasHeader; 103 | } 104 | 105 | /** 106 | * Gets the column index for items. 107 | * 108 | * @return the column index for items 109 | */ 110 | public int getItemTok() { 111 | return itemTok; 112 | } 113 | 114 | /** 115 | * Gets the column index for preferences. 116 | * 117 | * @return the column index for preferences 118 | */ 119 | public int getPrefTok() { 120 | return prefTok; 121 | } 122 | 123 | /** 124 | * Gets the column index for time. 125 | * 126 | * @return the column index for time 127 | */ 128 | public int getTimeTok() { 129 | return timeTok; 130 | } 131 | 132 | /** 133 | * Gets the column index for users. 134 | * 135 | * @return the column index for users 136 | */ 137 | public int getUserTok() { 138 | return userTok; 139 | } 140 | 141 | /** 142 | * Sets the field delimiter. 143 | * 144 | * @param del the delimiter between fields 145 | */ 146 | public void setDelimiter(final char del) { 147 | this.delimiter = del; 148 | } 149 | 150 | /** 151 | * Sets the flag indicating whether the file has a header. 152 | * 153 | * @param header flag indicating whether the file has a header 154 | */ 155 | public void setHasHeader(final boolean header) { 156 | this.hasHeader = header; 157 | } 158 | 159 | /** 160 | * Sets the column index for items. 161 | * 162 | * @param itemToken the column index for items 163 | */ 164 | public void setItemTok(final int itemToken) { 165 | this.itemTok = itemToken; 166 | } 167 | 168 | /** 169 | * Sets the column index for preferences. 170 | * 171 | * @param prefToken the column index for preferences 172 | */ 173 | public void setPrefTok(final int prefToken) { 174 | this.prefTok = prefToken; 175 | } 176 | 177 | /** 178 | * Sets the column index for time. 179 | * 180 | * @param timeToken the column index for time 181 | */ 182 | public void setTimeTok(final int timeToken) { 183 | this.timeTok = timeToken; 184 | } 185 | 186 | /** 187 | * Sets the column index for users. 188 | * 189 | * @param userToken the column index for users 190 | */ 191 | public void setUserTok(final int userToken) { 192 | this.userTok = userToken; 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /rival-core/src/main/java/net/recommenders/rival/core/DataModelFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.core; 17 | 18 | import net.recommenders.rival.core.ext.MahoutDataModel; 19 | import net.recommenders.rival.core.ext.RankSysDataModel; 20 | 21 | /** 22 | * 23 | * @author Alejandro 24 | */ 25 | public class DataModelFactory { 26 | 27 | public static DataModelIF getDefaultModel() { 28 | return getSimpleModel(); 29 | } 30 | 31 | public static TemporalDataModelIF getDefaultTemporalModel() { 32 | return getSimpleTemporalModel(); 33 | } 34 | 35 | public static DataModelIF getSimpleModel() { 36 | return new DataModel<>(); 37 | } 38 | 39 | public static TemporalDataModelIF getSimpleTemporalModel() { 40 | return new TemporalDataModel<>(); 41 | } 42 | 43 | public static TemporalDataModelIF getMahoutTemporalModel() { 44 | return new MahoutDataModel(); 45 | } 46 | 47 | public static DataModelIF getRankSysModel() { 48 | return new RankSysDataModel<>(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /rival-core/src/main/java/net/recommenders/rival/core/DataModelIF.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.core; 17 | 18 | /** 19 | * Interface for the data model used throughout the toolkit. Able to store 20 | * users, items, and preferences. 21 | * 22 | * @author Alejandro, Alan 24 | * 25 | * @param generic type for users 26 | * @param generic type for items 27 | */ 28 | public interface DataModelIF { 29 | 30 | /** 31 | * Method that returns the preference between a user and an item. 32 | * 33 | * @param u the user. 34 | * @param i the item. 35 | * @return the preference between a user and an item. 36 | */ 37 | public Double getUserItemPreference(U u, I i); 38 | 39 | /** 40 | * Method that returns the items of a user. 41 | * 42 | * @param u the user. 43 | * @return the items of a user. 44 | */ 45 | public Iterable getUserItems(U u); 46 | 47 | /** 48 | * Method that adds a preference to the model between a user and an item. 49 | * 50 | * @param u the user. 51 | * @param i the item. 52 | * @param d the preference. 53 | */ 54 | public void addPreference(final U u, final I i, final Double d); 55 | 56 | /** 57 | * Method that returns the items in the model. 58 | * 59 | * @return the items in the model. 60 | */ 61 | public Iterable getItems(); 62 | 63 | /** 64 | * Method that returns the users in the model. 65 | * 66 | * @return the users in the model. 67 | */ 68 | public Iterable getUsers(); 69 | 70 | /** 71 | * Method that returns the number of items in the model. 72 | * 73 | * @return the number of items in the model. 74 | */ 75 | public int getNumItems(); 76 | 77 | /** 78 | * Method that returns the number of users in the model. 79 | * 80 | * @return the number of users in the model. 81 | */ 82 | public int getNumUsers(); 83 | 84 | /** 85 | * Method that clears all the maps contained in the model. 86 | */ 87 | public void clear(); 88 | } 89 | -------------------------------------------------------------------------------- /rival-core/src/main/java/net/recommenders/rival/core/DataModelUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.core; 17 | 18 | import java.io.File; 19 | import java.io.FileNotFoundException; 20 | import java.io.PrintStream; 21 | import java.io.UnsupportedEncodingException; 22 | 23 | /** 24 | * Utilities for datamodels. 25 | * 26 | * @author Alan. 27 | */ 28 | public final class DataModelUtils { 29 | 30 | /** 31 | * Utility classes should not have a public constructor. 32 | */ 33 | private DataModelUtils() { 34 | } 35 | 36 | /** 37 | * Method that saves a data model to a file. 38 | * 39 | * @param dm the data model 40 | * @param outfile file where the model will be saved 41 | * @param overwrite flag that indicates if the file should be overwritten 42 | * @param delimiter field delimiter 43 | * @param type of users 44 | * @param type of items 45 | * @throws FileNotFoundException when outfile cannot be used. 46 | * @throws UnsupportedEncodingException when the requested encoding (UTF-8) 47 | * is not available. 48 | */ 49 | public static void saveDataModel(final DataModelIF dm, final String outfile, final boolean overwrite, final String delimiter) 50 | throws FileNotFoundException, UnsupportedEncodingException { 51 | if (new File(outfile).exists() && !overwrite) { 52 | System.out.println("Ignoring " + outfile); 53 | } else { 54 | PrintStream out = new PrintStream(outfile, "UTF-8"); 55 | for (U user : dm.getUsers()) { 56 | for (I item : dm.getUserItems(user)) { 57 | Double pref = dm.getUserItemPreference(user, item); 58 | out.println(user + delimiter + item + delimiter + pref); 59 | } 60 | } 61 | out.close(); 62 | } 63 | } 64 | 65 | /** 66 | * Method that saves a temporal data model to a file. 67 | * 68 | * @param dm the data model 69 | * @param outfile file where the model will be saved 70 | * @param overwrite flag that indicates if the file should be overwritten 71 | * @param delimiter field delimiter 72 | * @param type of users 73 | * @param type of items 74 | * @throws FileNotFoundException when outfile cannot be used. 75 | * @throws UnsupportedEncodingException when the requested encoding (UTF-8) 76 | * is not available. 77 | */ 78 | public static void saveDataModel(final TemporalDataModelIF dm, final String outfile, final boolean overwrite, String delimiter) 79 | throws FileNotFoundException, UnsupportedEncodingException { 80 | if (new File(outfile).exists() && !overwrite) { 81 | System.out.println("Ignoring " + outfile); 82 | } else { 83 | PrintStream out = new PrintStream(outfile, "UTF-8"); 84 | for (U user : dm.getUsers()) { 85 | for (I item : dm.getUserItems(user)) { 86 | Double pref = dm.getUserItemPreference(user, item); 87 | Iterable time = dm.getUserItemTimestamps(user, item); 88 | if (time == null) { 89 | out.println(user + delimiter + item + delimiter + pref + delimiter + "-1"); 90 | } else { 91 | for (Long t : time) { 92 | out.println(user + delimiter + item + delimiter + pref + delimiter + t); 93 | } 94 | } 95 | } 96 | } 97 | out.close(); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /rival-core/src/main/java/net/recommenders/rival/core/Parser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.core; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | 21 | /** 22 | * Data model parser interface. 23 | * 24 | * @author Alejandro 25 | * 26 | * @param generic type of users 27 | * @param generic type of items 28 | */ 29 | public interface Parser { 30 | 31 | /** 32 | * Parse a temporal data file. 33 | * 34 | * @param f The file to be parsed. 35 | * @return A dataset created from the file. 36 | * @throws IOException if the file cannot be read. 37 | */ 38 | TemporalDataModelIF parseTemporalData(File f) throws IOException; 39 | 40 | /** 41 | * Parse a data file. 42 | * 43 | * @param f The file to be parsed. 44 | * @return A dataset created from the file. 45 | * @throws IOException if the file cannot be read. 46 | */ 47 | DataModelIF parseData(File f) throws IOException; 48 | } 49 | -------------------------------------------------------------------------------- /rival-core/src/main/java/net/recommenders/rival/core/ParserWithIdMapping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.core; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | 21 | /** 22 | * Parser of files where users or items are not represented as integer ids. 23 | * 24 | * @author Alejandro 25 | * 26 | * @param generic type of users 27 | * @param generic type of items 28 | */ 29 | public interface ParserWithIdMapping extends Parser { 30 | 31 | /** 32 | * Parse a temporal data file. 33 | * 34 | * @param f The file to be parsed. 35 | * @param mapIdsPrefix The prefix of the file where the id mapping will be 36 | * stored (and will be read from). 37 | * @return A dataset created from the file. 38 | * @throws IOException if the file cannot be read. 39 | */ 40 | TemporalDataModelIF parseTemporalData(File f, String mapIdsPrefix) throws IOException; 41 | 42 | /** 43 | * Parse data file. 44 | * 45 | * @param f The file to parse. 46 | * @param mapIdsPrefix The prefix of the file where the id mapping will be 47 | * stored (and will be read from). 48 | * @return The data model created from the file. 49 | * @throws IOException if the file cannot be read. 50 | */ 51 | DataModelIF parseData(File f, String mapIdsPrefix) throws IOException; 52 | } 53 | -------------------------------------------------------------------------------- /rival-core/src/main/java/net/recommenders/rival/core/SimpleParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.core; 17 | 18 | import java.io.BufferedReader; 19 | import java.io.File; 20 | import java.io.FileInputStream; 21 | import java.io.IOException; 22 | import java.io.InputStreamReader; 23 | import java.util.zip.GZIPInputStream; 24 | 25 | /** 26 | * Data parser for tab-separated data files. 27 | * 28 | * @author Alejandro 29 | */ 30 | public class SimpleParser implements Parser { 31 | 32 | /** 33 | * The column index for the user id in the file. 34 | */ 35 | public static final int USER_TOK = 0; 36 | /** 37 | * The column index for the item id in the file. 38 | */ 39 | public static final int ITEM_TOK = 1; 40 | /** 41 | * The column index for the rating in the file. 42 | */ 43 | public static final int RATING_TOK = 2; 44 | /** 45 | * The column index for the time in the file. 46 | */ 47 | public static final int TIME_TOK = 3; 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | @Override 53 | public DataModelIF parseData(final File f) throws IOException { 54 | return parseData(f, "\t", false); 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | @Override 61 | public TemporalDataModelIF parseTemporalData(final File f) throws IOException { 62 | return parseData(f, "\t", true); 63 | } 64 | 65 | /** 66 | * Parses a data file with a specific separator between fields. 67 | * 68 | * @param f The file to be parsed. 69 | * @param token The separator to be used. 70 | * @param isTemporal A flag indicating if the file contains temporal 71 | * information. 72 | * @return A dataset created from the file. 73 | * @throws IOException if the file cannot be read. 74 | */ 75 | public TemporalDataModelIF parseData(final File f, final String token, final boolean isTemporal) throws IOException { 76 | TemporalDataModelIF dataset = DataModelFactory.getDefaultTemporalModel(); 77 | 78 | BufferedReader br = SimpleParser.getBufferedReader(f); 79 | String line = br.readLine(); 80 | if ((line != null) && (!line.matches(".*[a-zA-Z].*"))) { 81 | parseLine(line, dataset, token, isTemporal); 82 | } 83 | while ((line = br.readLine()) != null) { 84 | parseLine(line, dataset, token, isTemporal); 85 | } 86 | br.close(); 87 | 88 | return dataset; 89 | } 90 | 91 | /** 92 | * Obtains an instance of BufferedReader depending on the file extension: if 93 | * it ends with gz, zip, or tgz then a compressed reader is used instead of 94 | * the standard one. 95 | * 96 | * @param f The file to be opened. 97 | * @return An instance of BufferedReader or null if there is a problem 98 | * @throws IOException when the file cannot be read. 99 | * @see BufferedReader 100 | */ 101 | public static BufferedReader getBufferedReader(final File f) throws IOException { 102 | BufferedReader br = null; 103 | if ((f == null) || (!f.isFile())) { 104 | return br; 105 | } 106 | if (f.getName().endsWith(".gz") || f.getName().endsWith(".zip") || f.getName().endsWith(".tgz")) { 107 | br = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(f)), "UTF-8")); 108 | } else { 109 | br = new BufferedReader(new InputStreamReader(new FileInputStream(f), "UTF-8")); 110 | } 111 | return br; 112 | } 113 | 114 | /** 115 | * Parses line from data file. 116 | * 117 | * @param line The line to be parsed. 118 | * @param dataset The dataset to add data from line to. 119 | * @param token the token to split on. 120 | * @param isTemporal A flag indicating if the line contains temporal 121 | * information. 122 | */ 123 | private void parseLine(final String line, final TemporalDataModelIF dataset, final String token, final boolean isTemporal) { 124 | if (line == null) { 125 | return; 126 | } 127 | String[] toks = line.split(token); 128 | // user 129 | long userId = Long.parseLong(toks[USER_TOK]); 130 | // item 131 | long itemId = Long.parseLong(toks[ITEM_TOK]); 132 | // preference 133 | double preference = Double.parseDouble(toks[RATING_TOK]); 134 | // timestamp 135 | long timestamp = -1; 136 | // allow no timestamp information 137 | if (isTemporal && toks.length > TIME_TOK) { 138 | timestamp = Long.parseLong(toks[TIME_TOK]); 139 | } 140 | ////// 141 | // update information 142 | ////// 143 | dataset.addPreference(userId, itemId, preference); 144 | if (timestamp != -1) { 145 | dataset.addTimestamp(userId, itemId, timestamp); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /rival-core/src/main/java/net/recommenders/rival/core/TemporalDataModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.core; 17 | 18 | import java.util.HashMap; 19 | import java.util.HashSet; 20 | import java.util.Map; 21 | import java.util.Set; 22 | 23 | /** 24 | * Temporal data model used throughout the toolkit. It is able to store users, 25 | * items, preferences, and timestamps. 26 | * 27 | * @author Alejandro, Alan 29 | * 30 | * @param generic type for users 31 | * @param generic type for items 32 | */ 33 | public class TemporalDataModel extends DataModel implements TemporalDataModelIF { 34 | 35 | /** 36 | * The map with the timestamps between users and items. 37 | */ 38 | protected Map>> userItemTimestamps; 39 | 40 | /** 41 | * Default constructor. 42 | */ 43 | public TemporalDataModel() { 44 | super(); 45 | userItemTimestamps = new HashMap<>(); 46 | } 47 | 48 | /** 49 | * Constructor with parameters. 50 | * 51 | * @param ignoreDupPreferences The flag to indicate whether preferences 52 | * should be ignored. 53 | */ 54 | public TemporalDataModel(final boolean ignoreDupPreferences) { 55 | super(ignoreDupPreferences); 56 | userItemTimestamps = new HashMap<>(); 57 | } 58 | 59 | /** 60 | * Constructor with parameters. 61 | * 62 | * @param ignoreDupPreferences The flag to indicate whether preferences 63 | * should be ignored. 64 | * @param userItemPreference The preference map between users and items. 65 | * @param itemSet The items. 66 | * @param userItemTimestamp The map with the timestamps between users and 67 | * items 68 | */ 69 | public TemporalDataModel(final boolean ignoreDupPreferences, final Map> userItemPreference, final Set itemSet, 70 | final Map>> userItemTimestamp) { 71 | super(ignoreDupPreferences, userItemPreference, itemSet); 72 | this.userItemTimestamps = userItemTimestamp; 73 | } 74 | 75 | /** 76 | * Method that returns the map with the timestamps between users and items. 77 | * 78 | * @return the map with the timestamps between users and items. 79 | */ 80 | @Override 81 | public Iterable getUserItemTimestamps(U u, I i) { 82 | if (userItemTimestamps.containsKey(u) && userItemTimestamps.get(u).containsKey(i)) { 83 | return userItemTimestamps.get(u).get(i); 84 | } 85 | return null; 86 | } 87 | 88 | /** 89 | * Method that adds a timestamp to the model between a user and an item. 90 | * 91 | * @param u the user. 92 | * @param i the item. 93 | * @param t the timestamp. 94 | */ 95 | @Override 96 | public void addTimestamp(final U u, final I i, final Long t) { 97 | Map> userTimestamps = userItemTimestamps.get(u); 98 | if (userTimestamps == null) { 99 | userTimestamps = new HashMap<>(); 100 | userItemTimestamps.put(u, userTimestamps); 101 | } 102 | Set timestamps = userTimestamps.get(i); 103 | if (timestamps == null) { 104 | timestamps = new HashSet<>(); 105 | userTimestamps.put(i, timestamps); 106 | } 107 | timestamps.add(t); 108 | } 109 | 110 | /** 111 | * Method that clears all the maps contained in the model. 112 | */ 113 | public void clear() { 114 | super.clear(); 115 | userItemTimestamps.clear(); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /rival-core/src/main/java/net/recommenders/rival/core/TemporalDataModelIF.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.core; 17 | 18 | /** 19 | * Interface for a temporal data model. It is able to store users, items, 20 | * preferences, and timestamps. 21 | * 22 | * @author Alejandro, Alan 24 | * 25 | * @param generic type for users 26 | * @param generic type for items 27 | */ 28 | public interface TemporalDataModelIF extends DataModelIF { 29 | 30 | /** 31 | * Method that returns the timestamps between a user and an item. 32 | * 33 | * @param u the user. 34 | * @param i the item. 35 | * @return the timestamps between a user and an item. 36 | */ 37 | public Iterable getUserItemTimestamps(U u, I i); 38 | 39 | /** 40 | * Method that adds a timestamp to the model between a user and an item. 41 | * 42 | * @param u the user. 43 | * @param i the item. 44 | * @param t the timestamp. 45 | */ 46 | public void addTimestamp(final U u, final I i, final Long t); 47 | } 48 | -------------------------------------------------------------------------------- /rival-core/src/main/java/net/recommenders/rival/core/UIPParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.core; 17 | 18 | import org.apache.commons.csv.CSVFormat; 19 | import org.apache.commons.csv.CSVRecord; 20 | 21 | import java.io.File; 22 | import java.io.FileInputStream; 23 | import java.io.Reader; 24 | import java.io.IOException; 25 | import java.io.InputStreamReader; 26 | 27 | /** 28 | * User-Item-Preference (rating) Parser. 29 | * 30 | * @author Alan. 31 | */ 32 | public class UIPParser extends AbstractParser implements Parser { 33 | 34 | /** 35 | * Default constructor. 36 | */ 37 | public UIPParser() { 38 | super(); 39 | } 40 | 41 | /** 42 | * {@inheritDoc} 43 | */ 44 | @Override 45 | public TemporalDataModelIF parseTemporalData(final File f) throws IOException { 46 | TemporalDataModelIF dataset = DataModelFactory.getDefaultTemporalModel(); 47 | Reader in = new InputStreamReader(new FileInputStream(f), "UTF-8"); 48 | 49 | Iterable records; 50 | if (isHasHeader()) { 51 | records = CSVFormat.EXCEL.withDelimiter(getDelimiter()).withHeader().parse(in); 52 | } else { 53 | records = CSVFormat.EXCEL.withDelimiter(getDelimiter()).parse(in); 54 | } 55 | for (CSVRecord record : records) { 56 | long userID = Long.parseLong(record.get(getUserTok())); 57 | long itemID = Long.parseLong(record.get(getItemTok())); 58 | long timestamp = -1L; 59 | if (getTimeTok() != -1) { 60 | timestamp = Long.parseLong(record.get(getTimeTok())); 61 | } 62 | double preference = Double.parseDouble(record.get(getPrefTok())); 63 | dataset.addPreference(userID, itemID, preference); 64 | dataset.addTimestamp(userID, itemID, timestamp); 65 | } 66 | in.close(); 67 | return dataset; 68 | } 69 | 70 | /** 71 | * {@inheritDoc} 72 | */ 73 | @Override 74 | public DataModelIF parseData(final File f) throws IOException { 75 | DataModelIF dataset = new DataModel<>(); 76 | Reader in = new InputStreamReader(new FileInputStream(f), "UTF-8"); 77 | 78 | Iterable records; 79 | if (isHasHeader()) { 80 | records = CSVFormat.EXCEL.withDelimiter(getDelimiter()).withHeader().parse(in); 81 | } else { 82 | records = CSVFormat.EXCEL.withDelimiter(getDelimiter()).parse(in); 83 | } 84 | for (CSVRecord record : records) { 85 | long userID = Long.parseLong(record.get(getUserTok())); 86 | long itemID = Long.parseLong(record.get(getItemTok())); 87 | double preference = Double.parseDouble(record.get(getPrefTok())); 88 | dataset.addPreference(userID, itemID, preference); 89 | } 90 | in.close(); 91 | return dataset; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /rival-core/src/main/java/net/recommenders/rival/core/ext/RankSysDataModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.core.ext; 17 | 18 | import es.uam.eps.ir.ranksys.fast.index.FastItemIndex; 19 | import es.uam.eps.ir.ranksys.fast.index.FastUserIndex; 20 | import es.uam.eps.ir.ranksys.fast.index.SimpleFastItemIndex; 21 | import es.uam.eps.ir.ranksys.fast.index.SimpleFastUserIndex; 22 | import es.uam.eps.ir.ranksys.fast.preference.SimpleFastPreferenceData; 23 | import java.util.ArrayList; 24 | import java.util.HashSet; 25 | import java.util.Iterator; 26 | import java.util.List; 27 | import java.util.NoSuchElementException; 28 | import java.util.Set; 29 | import java.util.stream.Stream; 30 | import net.recommenders.rival.core.DataModelIF; 31 | import org.jooq.lambda.tuple.Tuple; 32 | import org.jooq.lambda.tuple.Tuple3; 33 | 34 | /** 35 | * 36 | * @author Alejandro 37 | */ 38 | public class RankSysDataModel implements DataModelIF { 39 | 40 | private SimpleFastPreferenceData model; 41 | private List> tuples; 42 | private Set users; 43 | private Set items; 44 | 45 | public RankSysDataModel() { 46 | clear(); 47 | } 48 | 49 | private void generateDatamodel() { 50 | FastUserIndex uIndex = SimpleFastUserIndex.load(users.stream()); 51 | FastItemIndex iIndex = SimpleFastItemIndex.load(items.stream()); 52 | model = SimpleFastPreferenceData.load(tuples.stream(), uIndex, iIndex); 53 | } 54 | 55 | @Override 56 | public void addPreference(U u, I i, Double d) { 57 | if (model != null) { 58 | throw new IllegalArgumentException("DataModel already generated. It is not possible to add more information."); 59 | } 60 | users.add(u); 61 | items.add(i); 62 | tuples.add(Tuple.tuple(u, i, d)); 63 | } 64 | 65 | @Override 66 | public Double getUserItemPreference(U u, I i) { 67 | if (model == null) { 68 | generateDatamodel(); 69 | } 70 | try { 71 | return model.getPreference(u, i).get().v2; 72 | } catch (NoSuchElementException e) { 73 | return Double.NaN; 74 | } 75 | } 76 | 77 | @Override 78 | public Iterable getUserItems(U u) { 79 | if (model == null) { 80 | generateDatamodel(); 81 | } 82 | Stream s = model.getUserPreferences(u).map(p -> p.v1()); 83 | return new Iterable() { 84 | @Override 85 | public Iterator iterator() { 86 | return s.iterator(); 87 | } 88 | }; 89 | } 90 | 91 | @Override 92 | public Iterable getItems() { 93 | if (model == null) { 94 | generateDatamodel(); 95 | } 96 | Stream s = model.getItemsWithPreferences(); 97 | return new Iterable() { 98 | @Override 99 | public Iterator iterator() { 100 | return s.iterator(); 101 | } 102 | }; 103 | } 104 | 105 | @Override 106 | public Iterable getUsers() { 107 | if (model == null) { 108 | generateDatamodel(); 109 | } 110 | Stream s = model.getUsersWithPreferences(); 111 | return new Iterable() { 112 | @Override 113 | public Iterator iterator() { 114 | return s.iterator(); 115 | } 116 | }; 117 | } 118 | 119 | @Override 120 | public int getNumItems() { 121 | if (model == null) { 122 | generateDatamodel(); 123 | } 124 | return model.numItems(); 125 | } 126 | 127 | @Override 128 | public int getNumUsers() { 129 | if (model == null) { 130 | generateDatamodel(); 131 | } 132 | return model.numUsers(); 133 | } 134 | 135 | @Override 136 | public void clear() { 137 | model = null; 138 | tuples = new ArrayList<>(); 139 | users = new HashSet<>(); 140 | items = new HashSet<>(); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /rival-core/src/main/java/net/recommenders/rival/core/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Core API classes for RiVal. 3 | */ 4 | package net.recommenders.rival.core; 5 | -------------------------------------------------------------------------------- /rival-core/src/test/java/net/recommenders/rival/core/DataModelTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.core; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | 20 | import org.junit.Before; 21 | import org.junit.Test; 22 | import org.junit.runner.RunWith; 23 | import org.junit.runners.JUnit4; 24 | 25 | /** 26 | * Tests for {@link net.recommenders.rival.core.DataModel}. 27 | * 28 | * @author Alan 29 | */ 30 | @RunWith(JUnit4.class) 31 | public class DataModelTest { 32 | 33 | /** 34 | * The data model. 35 | */ 36 | private DataModel dm = new DataModel<>(); 37 | /** 38 | * The number of users in the data model. 39 | */ 40 | private static final int USERS = 3; 41 | /** 42 | * The number of items in the data model. 43 | */ 44 | private static final int ITEMS = 3; 45 | 46 | @Before 47 | public void initialize() { 48 | for (long u = 1L; u <= USERS; u++) { 49 | for (long i = 1L; i <= ITEMS; i++) { 50 | dm.addPreference(u, i, 1.0 * u * i); 51 | } 52 | } 53 | } 54 | 55 | @Test 56 | public void testGetUserPreferences() { 57 | for (long u = 1L; u <= USERS; u++) { 58 | for (long i = 1L; i <= ITEMS; i++) { 59 | assertEquals(1.0 * u * i, dm.getUserItemPreference(u, i), 0.0); 60 | } 61 | } 62 | } 63 | 64 | @Test 65 | public void testGetNumItems() { 66 | assertEquals(ITEMS, dm.getNumItems()); 67 | } 68 | 69 | @Test 70 | public void testGetNumUsers() { 71 | assertEquals(USERS, dm.getNumUsers()); 72 | } 73 | 74 | @Test 75 | public void testGetItems() { 76 | assertEquals(ITEMS, dm.getNumItems()); 77 | } 78 | 79 | @Test 80 | public void testClearItems() { 81 | dm.clear(); 82 | assertEquals(0, dm.getNumItems()); 83 | } 84 | 85 | @Test 86 | public void testDuplicatePreferences() { 87 | DataModel unconstrainedModel = new DataModel<>(); 88 | for (long u = 1L; u <= USERS; u++) { 89 | for (long i = 1L; i <= ITEMS; i++) { 90 | unconstrainedModel.addPreference(u, i, 1.0 * u * i); 91 | } 92 | // duplicate preferences 93 | for (long i = 1L; i <= ITEMS; i++) { 94 | unconstrainedModel.addPreference(u, i, 1.0 * u * i); 95 | } 96 | } 97 | for (long u = 1L; u <= USERS; u++) { 98 | for (long i = 1L; i <= ITEMS; i++) { 99 | assertEquals(2.0 * u * i, unconstrainedModel.getUserItemPreference(u, i), 0.0); 100 | } 101 | } 102 | DataModel constrainedModel = new DataModel<>(true); 103 | for (long u = 1L; u <= USERS; u++) { 104 | for (long i = 1L; i <= ITEMS; i++) { 105 | constrainedModel.addPreference(u, i, 1.0 * u * i); 106 | } 107 | // duplicate preferences 108 | for (long i = 1L; i <= ITEMS; i++) { 109 | constrainedModel.addPreference(u, i, 1.0 * u * i); 110 | } 111 | } 112 | for (long u = 1L; u <= USERS; u++) { 113 | for (long i = 1L; i <= ITEMS; i++) { 114 | assertEquals(1.0 * u * i, constrainedModel.getUserItemPreference(u, i), 0.0); 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /rival-evaluate/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | net.recommenders.rival 6 | rival 7 | 0.3-SNAPSHOT 8 | ../ 9 | 10 | rival-evaluate 11 | jar 12 | RiVal Evaluation 13 | 14 | 15 | 16 | junit 17 | junit 18 | 19 | 20 | net.recommenders.rival 21 | rival-core 22 | ${project.version} 23 | 24 | 25 | org.apache.commons 26 | commons-math3 27 | 3.5 28 | 29 | 30 | 31 | 32 | 33 | 34 | org.apache.maven.plugins 35 | maven-compiler-plugin 36 | 37 | 38 | org.codehaus.mojo 39 | exec-maven-plugin 40 | 41 | net.recommenders.rival.evaluation.strategy.MultipleStrategyRunnerInfile 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /rival-evaluate/run.sh: -------------------------------------------------------------------------------- 1 | mvn exec:java -e -DpropertyFile=$1 2 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/Pair.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation; 17 | 18 | /** 19 | * Bean class to store an element of type A and another of type B. 20 | * 21 | * @param The type of the first element in the pair. 22 | * @param The type of the second element in the pair. 23 | * 24 | * @author Alejandro 25 | */ 26 | public class Pair { 27 | 28 | /** 29 | * First element. 30 | */ 31 | private A first; 32 | /** 33 | * Second element. 34 | */ 35 | private B second; 36 | 37 | /** 38 | * Default constructor. 39 | * 40 | * @param firstElement first element to store 41 | * @param secondElement second element to store 42 | */ 43 | public Pair(final A firstElement, final B secondElement) { 44 | this.first = firstElement; 45 | this.second = secondElement; 46 | } 47 | 48 | /** 49 | * Gets the first element of the pair. 50 | * 51 | * @return the first element of the pair 52 | */ 53 | public A getFirst() { 54 | return first; 55 | } 56 | 57 | /** 58 | * Gets the second element of the pair. 59 | * 60 | * @return the second element of the pair 61 | */ 62 | public B getSecond() { 63 | return second; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/metric/EvaluationMetric.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.metric; 17 | 18 | import java.util.Map; 19 | 20 | /** 21 | * An evaluation metric expressing the quality of the evaluated system. 22 | * 23 | * @author Alejandro 24 | * 25 | * @param generic type for users 26 | */ 27 | public interface EvaluationMetric { 28 | 29 | /** 30 | * Get the overall value of the metric. 31 | * 32 | * @return The overall value of the metric. 33 | */ 34 | double getValue(); 35 | 36 | /** 37 | * Get the value of the metric on a per-user basis. 38 | * 39 | * @return Map containing the values per user. 40 | */ 41 | Map getValuePerUser(); 42 | 43 | /** 44 | * Get the value of the metric for a specific user. 45 | * 46 | * @param u user whose metric value will be computed 47 | * 48 | * @return a value for user u. 49 | */ 50 | double getValue(V u); 51 | 52 | /** 53 | * Computes the evaluation metric. This method should be called 54 | * before asking for the values of the metric. 55 | */ 56 | void compute(); 57 | } 58 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/metric/error/MAE.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.metric.error; 17 | 18 | import net.recommenders.rival.core.DataModelIF; 19 | import net.recommenders.rival.evaluation.metric.EvaluationMetric; 20 | 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | /** 25 | * Mean absolute 26 | * error (MAE) of a list of predicted ratings. 27 | * 28 | * @author Alejandro. 29 | * 30 | * @param - type associated to users' ids 31 | * @param - type associated to items' ids 32 | */ 33 | public class MAE extends AbstractErrorMetric implements EvaluationMetric { 34 | 35 | /** 36 | * Default constructor with predictions and groundtruth information. 37 | * 38 | * @param predictions predicted scores for users and items 39 | * @param test groundtruth information for users and items 40 | */ 41 | public MAE(final DataModelIF predictions, final DataModelIF test) { 42 | super(predictions, test); 43 | } 44 | 45 | /** 46 | * Constructor where the error strategy can be initialized. 47 | * 48 | * @param predictions predicted scores for users and items 49 | * @param test groundtruth information for users and items 50 | * @param errorStrategy the error strategy 51 | */ 52 | public MAE(final DataModelIF predictions, final DataModelIF test, final ErrorStrategy errorStrategy) { 53 | super(predictions, test, errorStrategy); 54 | } 55 | 56 | /** 57 | * Instantiates and computes the MAE value. Prior to running this, there is 58 | * no valid value. 59 | * 60 | */ 61 | @Override 62 | public void compute() { 63 | if (!Double.isNaN(getValue())) { 64 | // since the data cannot change, avoid re-doing the calculations 65 | return; 66 | } 67 | iniCompute(); 68 | 69 | Map> data = processDataAsPredictedDifferencesToTest(); 70 | 71 | int testItems = 0; 72 | for (U testUser : getTest().getUsers()) { 73 | int userItems = 0; 74 | double ume = 0.0; 75 | 76 | if (data.containsKey(testUser)) { 77 | for (double difference : data.get(testUser)) { 78 | ume += Math.abs(difference); 79 | userItems++; 80 | } 81 | } 82 | 83 | testItems += userItems; 84 | setValue(getValue() + ume); 85 | if (userItems == 0) { 86 | ume = Double.NaN; 87 | } else { 88 | ume = ume / userItems; 89 | } 90 | getMetricPerUser().put(testUser, ume); 91 | } 92 | if (testItems == 0) { 93 | setValue(Double.NaN); 94 | } else { 95 | setValue(getValue() / testItems); 96 | } 97 | } 98 | 99 | /** 100 | * {@inheritDoc} 101 | */ 102 | @Override 103 | public String toString() { 104 | return "MAE_" + getStrategy(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/metric/error/RMSE.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.metric.error; 17 | 18 | import net.recommenders.rival.core.DataModelIF; 19 | import net.recommenders.rival.evaluation.metric.EvaluationMetric; 20 | 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | /** 25 | * Root mean square 26 | * error (RMSE) of a list of predicted ratings. 27 | * 28 | * @author Alan. 29 | * 30 | * @param - type associated to users' ids 31 | * @param - type associated to items' ids 32 | */ 33 | public class RMSE extends AbstractErrorMetric implements EvaluationMetric { 34 | 35 | /** 36 | * Default constructor with predictions and groundtruth information. 37 | * 38 | * @param predictions predicted scores for users and items 39 | * @param test groundtruth information for users and items 40 | */ 41 | public RMSE(final DataModelIF predictions, final DataModelIF test) { 42 | super(predictions, test); 43 | } 44 | 45 | /** 46 | * Constructor where the error strategy can be initialized. 47 | * 48 | * @param predictions predicted scores for users and items 49 | * @param test groundtruth information for users and items 50 | * @param errorStrategy the error strategy 51 | */ 52 | public RMSE(final DataModelIF predictions, final DataModelIF test, final ErrorStrategy errorStrategy) { 53 | super(predictions, test, errorStrategy); 54 | } 55 | 56 | /** 57 | * Instantiates and computes the RMSE value. Prior to running this, there is 58 | * no valid value. 59 | * 60 | */ 61 | @Override 62 | public void compute() { 63 | if (!Double.isNaN(getValue())) { 64 | // since the data cannot change, avoid re-doing the calculations 65 | return; 66 | } 67 | iniCompute(); 68 | 69 | Map> data = processDataAsPredictedDifferencesToTest(); 70 | int testItems = 0; 71 | for (U testUser : getTest().getUsers()) { 72 | int userItems = 0; 73 | double umse = 0.0; 74 | 75 | if (data.containsKey(testUser)) { 76 | for (double difference : data.get(testUser)) { 77 | umse += difference * difference; 78 | userItems++; 79 | } 80 | } 81 | 82 | testItems += userItems; 83 | setValue(getValue() + umse); 84 | if (userItems == 0) { 85 | umse = Double.NaN; 86 | } else { 87 | umse = Math.sqrt(umse / userItems); 88 | } 89 | getMetricPerUser().put(testUser, umse); 90 | } 91 | if (testItems == 0) { 92 | setValue(Double.NaN); 93 | } else { 94 | setValue(Math.sqrt(getValue() / testItems)); 95 | } 96 | } 97 | 98 | /** 99 | * {@inheritDoc} 100 | */ 101 | @Override 102 | public String toString() { 103 | return "RMSE_" + getStrategy(); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/metric/error/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal Rating Prediction error metrics. 3 | */ 4 | package net.recommenders.rival.evaluation.metric.error; 5 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/metric/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal Evaluation metrics. 3 | */ 4 | package net.recommenders.rival.evaluation.metric; 5 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/metric/ranking/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal Recommendation/Ranking accuracy metrics. 3 | */ 4 | package net.recommenders.rival.evaluation.metric.ranking; 5 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal Evaluation classes. 3 | */ 4 | package net.recommenders.rival.evaluation; 5 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/parser/TrecEvalParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.parser; 17 | 18 | import java.io.BufferedReader; 19 | import java.io.File; 20 | import java.io.FileInputStream; 21 | import java.io.IOException; 22 | import java.io.InputStreamReader; 23 | import net.recommenders.rival.core.DataModel; 24 | import net.recommenders.rival.core.DataModelFactory; 25 | import net.recommenders.rival.core.DataModelIF; 26 | import net.recommenders.rival.core.Parser; 27 | import net.recommenders.rival.core.TemporalDataModelIF; 28 | 29 | /** 30 | * A parser based on the format of trec_eval output (no timestamp info). 31 | * 32 | * @author Alejandro 33 | */ 34 | public class TrecEvalParser implements Parser { 35 | 36 | /** 37 | * The column index for the user id in the file. 38 | */ 39 | public static final int USER_TOK = 0; 40 | /** 41 | * The column index for the item id in the file. 42 | */ 43 | public static final int ITEM_TOK = 2; 44 | /** 45 | * The column index for the rating in the file. 46 | */ 47 | public static final int RATING_TOK = 4; 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | @Override 53 | public TemporalDataModelIF parseTemporalData(final File f) throws IOException { 54 | return null; 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | @Override 61 | public DataModelIF parseData(final File f) throws IOException { 62 | DataModelIF dataset = DataModelFactory.getDefaultModel(); 63 | 64 | BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f), "UTF-8")); 65 | try { 66 | String line = null; 67 | while ((line = br.readLine()) != null) { 68 | parseLine(line, dataset); 69 | } 70 | } finally { 71 | br.close(); 72 | } 73 | 74 | return dataset; 75 | } 76 | 77 | /** 78 | * A method that parses a line from the file. 79 | * 80 | * @param line the line to be parsed 81 | * @param dataset the dataset where the information parsed from the line 82 | * will be stored into. 83 | */ 84 | private void parseLine(final String line, final DataModelIF dataset) { 85 | String[] toks = line.split("\t"); 86 | // user 87 | long userId = Long.parseLong(toks[USER_TOK]); 88 | // item 89 | long itemId = Long.parseLong(toks[ITEM_TOK]); 90 | // preference 91 | double preference = Double.parseDouble(toks[RATING_TOK]); 92 | ////// 93 | // update information 94 | ////// 95 | dataset.addPreference(userId, itemId, preference); 96 | // no timestamp info 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/parser/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal Parsers for evaluation purposes. 3 | */ 4 | package net.recommenders.rival.evaluation.parser; 5 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/statistics/StandardError.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.statistics; 17 | 18 | import java.util.HashSet; 19 | import java.util.Map; 20 | import java.util.Set; 21 | import org.apache.commons.math3.stat.descriptive.SummaryStatistics; 22 | 23 | /** 24 | * 25 | * Class used to compute the standard error of an algorithm with respect to the 26 | * baseline. 27 | * 28 | * @author Alejandro 29 | * 30 | * @param generic type for users 31 | */ 32 | public class StandardError { 33 | 34 | /** 35 | * Baseline metric for each dimension (users). 36 | */ 37 | private Map baselineMetricPerDimension; 38 | /** 39 | * Test metric for each dimension (users). 40 | */ 41 | private Map testMetricPerDimension; 42 | 43 | /** 44 | * Default constructor. 45 | * 46 | * @param theBaselineMetricPerDimension map for the baseline method, one 47 | * value for each user (dimension) 48 | * @param theTestMetricPerDimension map for the test method, one value for 49 | * each user (dimension) 50 | */ 51 | public StandardError(final Map theBaselineMetricPerDimension, final Map theTestMetricPerDimension) { 52 | this.baselineMetricPerDimension = theBaselineMetricPerDimension; 53 | this.testMetricPerDimension = theTestMetricPerDimension; 54 | } 55 | 56 | /** 57 | * Implements equation (8.13) from "Elementary Statistics: A Problem Solving 58 | * Approach 4th Edition", Andrew L. Comrey, Howard B. Lee 59 | * 60 | * @return the standard error as the ratio of the standard deviation divided 61 | * by the sqrt(number of users) of the distribution of difference scores. 62 | */ 63 | public double getStandardError() { 64 | Set overlap = new HashSet(baselineMetricPerDimension.keySet()); 65 | overlap.retainAll(testMetricPerDimension.keySet()); 66 | 67 | // paired or matched samples --> analyse distribution of difference scores 68 | SummaryStatistics differences = new SummaryStatistics(); 69 | for (V key : overlap) { 70 | double diff = baselineMetricPerDimension.get(key) - testMetricPerDimension.get(key); 71 | differences.addValue(diff); 72 | } 73 | 74 | double e = differences.getStandardDeviation() / Math.sqrt(differences.getN()); 75 | return e; 76 | } 77 | 78 | /** 79 | * {@inheritDoc} 80 | */ 81 | @Override 82 | public String toString() { 83 | return "StandardError"; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/statistics/StatisticalSignificance.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.statistics; 17 | 18 | import java.util.HashSet; 19 | import java.util.Map; 20 | import java.util.Set; 21 | import org.apache.commons.math3.stat.inference.TestUtils; 22 | import org.apache.commons.math3.stat.inference.WilcoxonSignedRankTest; 23 | 24 | /** 25 | * 26 | * Class used to compute statistical significance methods, such as t's Student 27 | * (paired or not) and Wilcoxon. 28 | * 29 | * @author Alejandro 30 | */ 31 | public class StatisticalSignificance { 32 | 33 | /** 34 | * Baseline metric for each dimension (users). 35 | */ 36 | private Map baselineMetricPerDimension; 37 | /** 38 | * Test metric for each dimension (users). 39 | */ 40 | private Map testMetricPerDimension; 41 | /** 42 | * Users. 43 | */ 44 | private Set dimensions; 45 | 46 | /** 47 | * Default constructor. 48 | * 49 | * @param theBaselineMetricPerDimension map for the baseline method, one 50 | * value for each user (dimension) 51 | * @param theTestMetricPerDimension map for the test method, one value for 52 | * each user (dimension) 53 | */ 54 | public StatisticalSignificance(final Map theBaselineMetricPerDimension, final Map theTestMetricPerDimension) { 55 | this.baselineMetricPerDimension = theBaselineMetricPerDimension; 56 | this.testMetricPerDimension = theTestMetricPerDimension; 57 | this.dimensions = new HashSet<>(baselineMetricPerDimension.keySet()); 58 | this.dimensions.retainAll(testMetricPerDimension.keySet()); 59 | } 60 | 61 | /** 62 | * Gets the p-value according to the requested method. 63 | * 64 | * @param method one of "t", "pairedT", "wilcoxon" 65 | * @return the p-value according to the requested method 66 | */ 67 | public double getPValue(final String method) { 68 | double p = Double.NaN; 69 | 70 | if ("t".equals(method)) { 71 | double[] baselineValues = new double[baselineMetricPerDimension.values().size()]; 72 | int i = 0; 73 | for (Double d : baselineMetricPerDimension.values()) { 74 | baselineValues[i] = d; 75 | i++; 76 | } 77 | 78 | double[] testValues = new double[testMetricPerDimension.values().size()]; 79 | i = 0; 80 | for (Double d : testMetricPerDimension.values()) { 81 | testValues[i] = d; 82 | i++; 83 | } 84 | 85 | p = TestUtils.tTest(baselineValues, testValues); 86 | } else { 87 | double[] baselineValues = new double[dimensions.size()]; 88 | int i = 0; 89 | for (Object d : dimensions) { 90 | baselineValues[i] = baselineMetricPerDimension.get(d); 91 | i++; 92 | } 93 | 94 | double[] testValues = new double[dimensions.size()]; 95 | i = 0; 96 | for (Object d : dimensions) { 97 | testValues[i] = testMetricPerDimension.get(d); 98 | i++; 99 | } 100 | if ("pairedT".equals(method)) { 101 | p = TestUtils.pairedTTest(baselineValues, testValues); 102 | } else if ("wilcoxon".equals(method)) { 103 | p = new WilcoxonSignedRankTest().wilcoxonSignedRankTest(baselineValues, testValues, false); 104 | } 105 | } 106 | return p; 107 | } 108 | 109 | /** 110 | * {@inheritDoc} 111 | */ 112 | @Override 113 | public String toString() { 114 | return "StatisticalSignificance"; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/statistics/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal Metrics related to statistics measures. 3 | */ 4 | package net.recommenders.rival.evaluation.statistics; 5 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/strategy/AllItems.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.strategy; 17 | 18 | import java.util.Set; 19 | import net.recommenders.rival.core.DataModelIF; 20 | 21 | /** 22 | * An evaluation strategy where all the items are used as candidates. 23 | * 24 | * @author Alejandro 25 | */ 26 | public class AllItems extends AbstractStrategy { 27 | 28 | /** 29 | * Default constructor. 30 | * 31 | * @see 32 | * AbstractStrategy#AbstractStrategy(net.recommenders.rival.core.DataModelIF, 33 | * net.recommenders.rival.core.DataModelIF, double) 34 | * 35 | * @param training The training set. 36 | * @param test The test set. 37 | * @param threshold The relevance threshold. 38 | */ 39 | public AllItems(final DataModelIF training, final DataModelIF test, final double threshold) { 40 | super(training, test, threshold); 41 | } 42 | 43 | /** 44 | * {@inheritDoc} 45 | */ 46 | @Override 47 | public Set getCandidateItemsToRank(final Long user) { 48 | final Set items = getModelTrainingDifference(getTraining(), user); 49 | items.addAll(getModelTrainingDifference(getTest(), user)); 50 | return items; 51 | } 52 | 53 | /** 54 | * {@inheritDoc} 55 | */ 56 | @Override 57 | public String toString() { 58 | return "AllItems_" + getThreshold(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/strategy/EvaluationStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.strategy; 17 | 18 | import java.io.PrintStream; 19 | import java.util.List; 20 | import java.util.Set; 21 | import net.recommenders.rival.evaluation.Pair; 22 | 23 | /** 24 | * An interface for evaluation strategies. 25 | * 26 | * @author Alejandro 27 | * 28 | * @param generic type for users 29 | * @param generic type for items 30 | */ 31 | public interface EvaluationStrategy { 32 | 33 | /** 34 | * Enumeration that defines two output formats: a simple one (tab-separated) 35 | * and another compatible with the one used by the treceval program. 36 | */ 37 | public enum OUTPUT_FORMAT { 38 | 39 | /** 40 | * Tab-separated format. 41 | */ 42 | SIMPLE, 43 | /** 44 | * Format as followed by the trec_eval program. 45 | */ 46 | TRECEVAL; 47 | } 48 | 49 | /** 50 | * Get the items to rank. 51 | * 52 | * @param user The user. 53 | * @return The items to rank. 54 | */ 55 | Set getCandidateItemsToRank(U user); 56 | 57 | /** 58 | * Print rankings for a user. 59 | * 60 | * @param user The user. 61 | * @param scoredItems The scored items to print. 62 | * @param out Where to print. 63 | * @param format The format of the printer (see {@link OUTPUT_FORMAT}). 64 | */ 65 | void printRanking(U user, List> scoredItems, PrintStream out, OUTPUT_FORMAT format); 66 | 67 | /** 68 | * Print the ground truth. 69 | * 70 | * @param user The user. 71 | * @param out Where to print. 72 | * @param format The format of the printer (see {@link OUTPUT_FORMAT}). 73 | */ 74 | void printGroundtruth(U user, PrintStream out, OUTPUT_FORMAT format); 75 | } 76 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/strategy/EvaluationStrategyPerItem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.strategy; 17 | 18 | import java.util.Set; 19 | 20 | /** 21 | * An interface for the per-item evaluation strategy. 22 | * 23 | * @author Alejandro 24 | * 25 | * @param generic type for users 26 | * @param generic type for items 27 | */ 28 | public interface EvaluationStrategyPerItem { 29 | 30 | /** 31 | * Get the items to rank. 32 | * 33 | * @param user The user. 34 | * @param item The item. 35 | * @return The items to rank. 36 | */ 37 | Set getCandidateItemsToRank(U user, I item); 38 | } 39 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/strategy/StrategyIO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.strategy; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.Map; 21 | import net.recommenders.rival.evaluation.Pair; 22 | 23 | /** 24 | * Methods related to input/output of strategies. 25 | * 26 | * @author Alan. 27 | */ 28 | public final class StrategyIO { 29 | 30 | /** 31 | * Utility classes should not have a public or default constructor. 32 | */ 33 | private StrategyIO() { 34 | } 35 | 36 | /** 37 | * Read a file from the recommended items file. 38 | * 39 | * @param line The line. 40 | * @param mapUserRecommendations The recommendations for the users where 41 | * information will be stored into. 42 | */ 43 | public static void readLine(final String line, final Map>> mapUserRecommendations) { 44 | String[] toks = line.split("\t"); 45 | // mymedialite format: user \t [item:score,item:score,...] 46 | if (line.contains(":") && line.contains(",")) { 47 | Long user = Long.parseLong(toks[0]); 48 | String items = toks[1].replace("[", "").replace("]", ""); 49 | for (String pair : items.split(",")) { 50 | String[] pairToks = pair.split(":"); 51 | Long item = Long.parseLong(pairToks[0]); 52 | Double score = Double.parseDouble(pairToks[1]); 53 | List> userRec = mapUserRecommendations.get(user); 54 | if (userRec == null) { 55 | userRec = new ArrayList>(); 56 | mapUserRecommendations.put(user, userRec); 57 | } 58 | userRec.add(new Pair(item, score)); 59 | } 60 | } else { 61 | Long user = Long.parseLong(toks[0]); 62 | Long item = Long.parseLong(toks[1]); 63 | Double score = Double.parseDouble(toks[2]); 64 | List> userRec = mapUserRecommendations.get(user); 65 | if (userRec == null) { 66 | userRec = new ArrayList>(); 67 | mapUserRecommendations.put(user, userRec); 68 | } 69 | userRec.add(new Pair(item, score)); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/strategy/TestItems.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.strategy; 17 | 18 | import java.util.Set; 19 | import net.recommenders.rival.core.DataModelIF; 20 | 21 | /** 22 | * An evaluation strategy where only the test items are used as candidates. 23 | * 24 | * @author Alejandro 25 | */ 26 | public class TestItems extends AbstractStrategy { 27 | 28 | /** 29 | * Default constructor. 30 | * 31 | * @see 32 | * AbstractStrategy#AbstractStrategy(net.recommenders.rival.core.DataModelIF, 33 | * net.recommenders.rival.core.DataModelIF, double) 34 | * 35 | * @param training The training set. 36 | * @param test The test set. 37 | * @param threshold The relevance threshold. 38 | */ 39 | public TestItems(final DataModelIF training, final DataModelIF test, final double threshold) { 40 | super(training, test, threshold); 41 | } 42 | 43 | /** 44 | * {@inheritDoc} 45 | */ 46 | @Override 47 | public Set getCandidateItemsToRank(final Long user) { 48 | return getModelTrainingDifference(getTest(), user); 49 | } 50 | 51 | /** 52 | * {@inheritDoc} 53 | */ 54 | @Override 55 | public String toString() { 56 | return "TestItems_" + getThreshold(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/strategy/TrainItems.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.strategy; 17 | 18 | import java.util.Set; 19 | import net.recommenders.rival.core.DataModelIF; 20 | 21 | /** 22 | * An evaluation strategy where only the items in training are used as 23 | * candidates. 24 | * 25 | * @author Alejandro 26 | */ 27 | public class TrainItems extends AbstractStrategy { 28 | 29 | /** 30 | * Default constructor. 31 | * 32 | * @see 33 | * AbstractStrategy#AbstractStrategy(net.recommenders.rival.core.DataModelIF, 34 | * net.recommenders.rival.core.DataModelIF, double) 35 | * 36 | * @param training The training set. 37 | * @param test The test set. 38 | * @param threshold The relevance threshold. 39 | */ 40 | public TrainItems(final DataModelIF training, final DataModelIF test, final double threshold) { 41 | super(training, test, threshold); 42 | } 43 | 44 | /** 45 | * {@inheritDoc} 46 | */ 47 | @Override 48 | public Set getCandidateItemsToRank(final Long user) { 49 | return getModelTrainingDifference(getTraining(), user); 50 | } 51 | 52 | /** 53 | * {@inheritDoc} 54 | */ 55 | @Override 56 | public String toString() { 57 | return "TrainItems_" + getThreshold(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/strategy/UserTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.strategy; 17 | 18 | import java.util.HashSet; 19 | import java.util.Set; 20 | import net.recommenders.rival.core.DataModelIF; 21 | 22 | /** 23 | * An evaluation strategy where only the items in the user's test are used as 24 | * candidates. 25 | * 26 | * @author Alejandro 27 | */ 28 | public class UserTest extends AbstractStrategy { 29 | 30 | /** 31 | * Default constructor. 32 | * 33 | * @see 34 | * AbstractStrategy#AbstractStrategy(net.recommenders.rival.core.DataModelIF, 35 | * net.recommenders.rival.core.DataModelIF, double) 36 | * 37 | * @param training The training set. 38 | * @param test The test set. 39 | * @param threshold The relevance threshold. 40 | */ 41 | public UserTest(final DataModelIF training, final DataModelIF test, final double threshold) { 42 | super(training, test, threshold); 43 | } 44 | 45 | /** 46 | * {@inheritDoc} 47 | */ 48 | @Override 49 | public Set getCandidateItemsToRank(final Long user) { 50 | Set items = new HashSet<>(); 51 | for (Long i : getTest().getUserItems(user)) { 52 | items.add(i); 53 | } 54 | return items; 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | @Override 61 | public String toString() { 62 | return "UserTest_" + getThreshold(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/java/net/recommenders/rival/evaluation/strategy/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal evaluation strategies. 3 | */ 4 | package net.recommenders.rival.evaluation.strategy; 5 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/resources/allitems.strategy.properties: -------------------------------------------------------------------------------- 1 | split.training.file= 2 | split.test.file= 3 | recommendation.file= 4 | output.format=TRECEVAL 5 | output.file.ranking= 6 | output.file.groundtruth= 7 | strategy.class=net.recommenders.rival.evaluation.strategy.AllItems 8 | strategy.relevance.threshold=4 9 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/resources/metric.properties: -------------------------------------------------------------------------------- 1 | evaluation.pred.file= 2 | evaluation.pred.format=TRECEVAL 3 | evaluation.test.file= 4 | evaluation.output.overwrite=false 5 | evaluation.output.append=true 6 | evaluation.output.file= 7 | evaluation.classes=net.recommenders.rival.evaluation.metric.error.MAE,net.recommenders.rival.evaluation.metric.error.RMSE,net.recommenders.rival.evaluation.metric.ranking.MAP,net.recommenders.rival.evaluation.metric.ranking.NDCG,net.recommenders.rival.evaluation.metric.ranking.Precision,net.recommenders.rival.evaluation.metric.ranking.Recall 8 | evaluation.ndcg.type=exp 9 | evaluation.relevance.threshold=5 10 | evaluation.ranking.cutoffs=1,5,10,50 11 | evaluation.error.strategy=NOT_CONSIDER_NAN 12 | evaluation.peruser=false 13 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/resources/relplusn.strategy.properties: -------------------------------------------------------------------------------- 1 | split.training.file= 2 | split.test.file= 3 | recommendation.file= 4 | output.format=TRECEVAL 5 | output.file.ranking= 6 | output.file.groundtruth= 7 | strategy.class=net.recommenders.rival.evaluation.strategy.RelPlusN 8 | strategy.relevance.threshold=5 9 | strategy.relplusn.N=100 10 | strategy.relplusn.seed=2014 11 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/resources/statistics.properties: -------------------------------------------------------------------------------- 1 | algorithm.baseline.file= 2 | algorithm.methods.files= 3 | input.format.statistics=default 4 | output.overwrite.statistics=false 5 | output.file.statistics= 6 | statistics.functions=confidence_interval,effect_size_d,effect_size_dLS,effect_size_pairedT,standard_error,statistical_significance_t,statistical_significance_pairedT,statistical_significance_wilcoxon 7 | statistics.alpha=0.05 8 | statistics.users_to_avoid=all 9 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/resources/strategy.properties: -------------------------------------------------------------------------------- 1 | split.folder= 2 | split.training.suffix= 3 | split.test.suffix= 4 | recommendation.folder= 5 | recommendation.suffix= 6 | output.format=TRECEVAL 7 | output.overwrite=false 8 | output.ranking.folder= 9 | output.groundtruth.folder= 10 | strategy.classes=net.recommenders.rival.evaluation.strategy.RelPlusN,net.recommenders.rival.evaluation.strategy.TestItems,net.recommenders.rival.evaluation.strategy.AllItems,net.recommenders.rival.evaluation.strategy.TrainItems,net.recommenders.rival.evaluation.strategy.UserTest 11 | strategy.relevance.thresholds=5 12 | strategy.relplusn.N=100 13 | strategy.relplusn.seed=2014 14 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/resources/testitems.strategy.properties: -------------------------------------------------------------------------------- 1 | split.training.file= 2 | split.test.file= 3 | recommendation.file= 4 | output.format=TRECEVAL 5 | output.file.ranking= 6 | output.file.groundtruth= 7 | strategy.class=net.recommenders.rival.evaluation.strategy.TestItems 8 | strategy.relevance.threshold=4 9 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/resources/trainitems.strategy.properties: -------------------------------------------------------------------------------- 1 | split.training.file= 2 | split.test.file= 3 | recommendation.file= 4 | output.format=TRECEVAL 5 | output.file.ranking= 6 | output.file.groundtruth= 7 | strategy.class=net.recommenders.rival.evaluation.strategy.TrainItems 8 | strategy.relevance.threshold=4 9 | -------------------------------------------------------------------------------- /rival-evaluate/src/main/resources/usertest.strategy.properties: -------------------------------------------------------------------------------- 1 | split.training.file= 2 | split.test.file= 3 | recommendation.file= 4 | output.format=TRECEVAL 5 | output.file.ranking= 6 | output.file.groundtruth= 7 | strategy.class=net.recommenders.rival.evaluation.strategy.UserTest 8 | strategy.relevance.threshold=4 9 | -------------------------------------------------------------------------------- /rival-evaluate/src/test/java/net/recommenders/rival/evaluation/metric/MAETest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.metric; 17 | 18 | import net.recommenders.rival.core.DataModelFactory; 19 | import net.recommenders.rival.core.DataModelIF; 20 | import net.recommenders.rival.evaluation.metric.error.MAE; 21 | import org.junit.Test; 22 | import org.junit.runner.RunWith; 23 | import org.junit.runners.JUnit4; 24 | 25 | import java.util.Map; 26 | 27 | import static org.junit.Assert.assertEquals; 28 | 29 | /** 30 | * Test for {@link MAE}. 31 | * 32 | * @author Alan. 33 | */ 34 | @RunWith(JUnit4.class) 35 | public class MAETest { 36 | 37 | @Test 38 | public void testSameGroundtruthAsPredictions() { 39 | DataModelIF predictions = DataModelFactory.getDefaultModel(); 40 | DataModelIF test = DataModelFactory.getDefaultModel(); 41 | for (int i = 0; i < 5; i++) { 42 | for (int j = 0; j < 10; j++) { 43 | test.addPreference((long) i, (long) j, (double) i * j); 44 | predictions.addPreference((long) i, (long) j, (double) i * j); 45 | } 46 | } 47 | MAE mae = new MAE(predictions, test); 48 | 49 | mae.compute(); 50 | 51 | assertEquals(0.0, mae.getValue(), 0.0); 52 | 53 | Map maePerUser = mae.getValuePerUser(); 54 | for (Map.Entry e : maePerUser.entrySet()) { 55 | double value = e.getValue(); 56 | assertEquals(0.0, value, 0.0); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /rival-evaluate/src/test/java/net/recommenders/rival/evaluation/metric/MAPTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.metric; 17 | 18 | import net.recommenders.rival.core.DataModel; 19 | import net.recommenders.rival.evaluation.metric.ranking.MAP; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | import org.junit.runners.JUnit4; 23 | 24 | import java.util.Map; 25 | import net.recommenders.rival.core.DataModelFactory; 26 | import net.recommenders.rival.core.DataModelIF; 27 | 28 | import static org.junit.Assert.assertEquals; 29 | 30 | /** 31 | * Tests for {@link MAP}. 32 | * 33 | * @author Alan. 34 | */ 35 | @RunWith(JUnit4.class) 36 | public class MAPTest { 37 | 38 | @Test 39 | public void testSameGroundtruthAsPredictions() { 40 | DataModelIF predictions = DataModelFactory.getDefaultModel(); 41 | DataModelIF test = DataModelFactory.getDefaultModel(); 42 | int nUsers = 20; 43 | int nItems = 15; 44 | for (long i = 1L; i < nUsers + 1; i++) { 45 | for (long j = 1L; j < nItems + 1; j++) { 46 | test.addPreference(i, j, i * j % 5 + 1.0); 47 | predictions.addPreference(i, j, i * j % 5 + 1.0); 48 | } 49 | } 50 | MAP map = new MAP(predictions, test, 1.0, new int[]{1, 5, 10, 20}); 51 | 52 | map.compute(); 53 | 54 | assertEquals(1.0, map.getValue(), 0.0); 55 | assertEquals(1.0 / nItems, map.getValueAt(1), 0.0001); 56 | assertEquals(1.0 / nItems * 5, map.getValueAt(5), 0.0001); 57 | assertEquals(1.0 / nItems * 10, map.getValueAt(10), 0.0001); 58 | 59 | Map mapPerUser = map.getValuePerUser(); 60 | for (Map.Entry e : mapPerUser.entrySet()) { 61 | long user = e.getKey(); 62 | double value = e.getValue(); 63 | assertEquals(1.0, value, 0.0); 64 | } 65 | } 66 | 67 | @Test 68 | public void testOneUserTrecevalStrategySingleRelevance() { 69 | // groundtruth: ? 0 1 1 70 | // predictions: 3 4 5 1 71 | DataModelIF test = DataModelFactory.getDefaultModel(); 72 | DataModelIF predictions = DataModelFactory.getDefaultModel(); 73 | test.addPreference(1L, 2L, 0.0); 74 | test.addPreference(1L, 3L, 1.0); 75 | test.addPreference(1L, 4L, 1.0); 76 | predictions.addPreference(1L, 1L, 3.0); 77 | predictions.addPreference(1L, 2L, 4.0); 78 | predictions.addPreference(1L, 3L, 5.0); 79 | predictions.addPreference(1L, 4L, 1.0); 80 | 81 | MAP map = new MAP(predictions, test, 1.0, new int[]{1, 2, 3, 4, 5}); 82 | 83 | map.compute(); 84 | 85 | assertEquals(0.75, map.getValue(), 0.001); 86 | assertEquals(0.5, map.getValueAt(1), 0.001); 87 | assertEquals(0.5, map.getValueAt(2), 0.001); 88 | assertEquals(0.5, map.getValueAt(3), 0.001); 89 | assertEquals(0.75, map.getValueAt(4), 0.001); 90 | assertEquals(0.75, map.getValueAt(5), 0.001); 91 | 92 | Map mapPerUser = map.getValuePerUser(); 93 | for (Map.Entry e : mapPerUser.entrySet()) { 94 | long user = e.getKey(); 95 | double value = e.getValue(); 96 | assertEquals(0.75, value, 0.001); 97 | } 98 | } 99 | 100 | @Test 101 | public void testOneUserTrecevalStrategyMultipleRelevance() { 102 | // groundtruth: ? 0 1 2 103 | // predictions: 3 4 5 1 104 | DataModelIF test = DataModelFactory.getDefaultModel(); 105 | DataModelIF predictions =DataModelFactory.getDefaultModel(); 106 | test.addPreference(1L, 2L, 0.0); 107 | test.addPreference(1L, 3L, 1.0); 108 | test.addPreference(1L, 4L, 2.0); 109 | predictions.addPreference(1L, 1L, 3.0); 110 | predictions.addPreference(1L, 2L, 4.0); 111 | predictions.addPreference(1L, 3L, 5.0); 112 | predictions.addPreference(1L, 4L, 1.0); 113 | 114 | MAP map = new MAP(predictions, test, 1.0, new int[]{1, 2, 3, 4, 5}); 115 | 116 | map.compute(); 117 | 118 | assertEquals(0.75, map.getValue(), 0.001); 119 | assertEquals(0.5, map.getValueAt(1), 0.001); 120 | assertEquals(0.5, map.getValueAt(2), 0.001); 121 | assertEquals(0.5, map.getValueAt(3), 0.001); 122 | assertEquals(0.75, map.getValueAt(4), 0.001); 123 | assertEquals(0.75, map.getValueAt(5), 0.001); 124 | 125 | Map mapPerUser = map.getValuePerUser(); 126 | for (Map.Entry e : mapPerUser.entrySet()) { 127 | long user = e.getKey(); 128 | double value = e.getValue(); 129 | assertEquals(0.75, value, 0.001); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /rival-evaluate/src/test/java/net/recommenders/rival/evaluation/metric/PopularityStratifiedRecallTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.metric; 17 | 18 | import java.util.HashMap; 19 | import net.recommenders.rival.core.DataModelIF; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | import org.junit.runners.JUnit4; 23 | 24 | import java.util.Map; 25 | import net.recommenders.rival.core.DataModelFactory; 26 | import net.recommenders.rival.evaluation.metric.ranking.PopularityStratifiedRecall; 27 | 28 | import static org.junit.Assert.assertEquals; 29 | 30 | /** 31 | * Test for {@link PopularityStratifiedRecall}. 32 | * 33 | * @author Alejandro. 34 | */ 35 | @RunWith(JUnit4.class) 36 | public class PopularityStratifiedRecallTest { 37 | 38 | @Test 39 | public void testSameGroundtruthAsPredictions() { 40 | DataModelIF predictions = DataModelFactory.getDefaultModel(); 41 | DataModelIF test = DataModelFactory.getDefaultModel(); 42 | int nUsers = 20; 43 | int nItems = 15; 44 | for (long i = 1L; i < nUsers + 1; i++) { 45 | for (long j = 1L; j < nItems + 1; j++) { 46 | test.addPreference(i, j, i * j % 5 + 1.0); 47 | predictions.addPreference(i, j, i * j % 5 + 1.0); 48 | } 49 | } 50 | Map observedItemRelevance = new HashMap(); 51 | for (long j = 1L; j < nItems + 1; j++) { 52 | observedItemRelevance.put(j, Long.valueOf(j).intValue()); 53 | } 54 | 55 | double gamma = 0.0; 56 | PopularityStratifiedRecall recall = new PopularityStratifiedRecall(predictions, test, 1.0, new int[]{5, 10, 20}, gamma, observedItemRelevance); 57 | 58 | recall.compute(); 59 | 60 | assertEquals(1.0, recall.getValue(), 0.0); 61 | // assertEquals(, recall.getValueAt(5), 0.0001); 62 | // assertEquals(, recall.getValueAt(10), 0.0001); 63 | 64 | Map recallPerUser = recall.getValuePerUser(); 65 | for (Map.Entry e : recallPerUser.entrySet()) { 66 | long user = e.getKey(); 67 | double value = e.getValue(); 68 | assertEquals(1.0, value, 0.0); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /rival-evaluate/src/test/java/net/recommenders/rival/evaluation/metric/RMSETest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.metric; 17 | 18 | import net.recommenders.rival.core.DataModelIF; 19 | import net.recommenders.rival.evaluation.metric.error.AbstractErrorMetric; 20 | import net.recommenders.rival.evaluation.metric.error.RMSE; 21 | import org.junit.Test; 22 | import org.junit.runner.RunWith; 23 | import org.junit.runners.JUnit4; 24 | 25 | import java.util.Map; 26 | import net.recommenders.rival.core.DataModelFactory; 27 | 28 | import static org.junit.Assert.assertEquals; 29 | import static org.junit.Assert.assertNotNull; 30 | 31 | /** 32 | * Test for {@link RMSE}. 33 | * 34 | * @author Alan. 35 | */ 36 | @RunWith(JUnit4.class) 37 | public class RMSETest { 38 | 39 | 40 | @Test 41 | public void testSameGroundtruthAsPredictions() { 42 | DataModelIF predictions = DataModelFactory.getDefaultModel(); 43 | DataModelIF test = DataModelFactory.getDefaultModel(); 44 | for (int i = 0; i < 5; i++) { 45 | for (int j = 0; j < 10; j++) { 46 | test.addPreference((long) i, (long) j, (double) i * j); 47 | predictions.addPreference((long) i, (long) j, (double) i * j); 48 | } 49 | } 50 | RMSE rmse = new RMSE(predictions, test); 51 | 52 | RMSE rmseStrat = new RMSE(predictions, test, AbstractErrorMetric.ErrorStrategy.CONSIDER_EVERYTHING); 53 | assertNotNull(rmseStrat); 54 | 55 | assertEquals("RMSE_CONSIDER_EVERYTHING", rmseStrat.toString()); 56 | 57 | rmse.compute(); 58 | 59 | assertEquals(0.0, rmse.getValue(), 0.0); 60 | 61 | Map rmsePerUser = rmse.getValuePerUser(); 62 | for (Map.Entry e : rmsePerUser.entrySet()) { 63 | double value = e.getValue(); 64 | assertEquals(0.0, value, 0.0); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /rival-evaluate/src/test/java/net/recommenders/rival/evaluation/metric/RecallTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.evaluation.metric; 17 | 18 | import net.recommenders.rival.core.DataModelIF; 19 | import net.recommenders.rival.evaluation.metric.ranking.Recall; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | import org.junit.runners.JUnit4; 23 | 24 | import java.util.Map; 25 | import net.recommenders.rival.core.DataModelFactory; 26 | 27 | import static org.junit.Assert.assertEquals; 28 | 29 | /** 30 | * Tests for {@link Recall}. 31 | * 32 | * @author Alan. 33 | */ 34 | @RunWith(JUnit4.class) 35 | public class RecallTest { 36 | 37 | @Test 38 | public void testSameGroundtruthAsPredictions() { 39 | DataModelIF predictions = DataModelFactory.getDefaultModel(); 40 | DataModelIF test = DataModelFactory.getDefaultModel(); 41 | int nUsers = 20; 42 | int nItems = 15; 43 | for (long i = 1L; i < nUsers + 1; i++) { 44 | for (long j = 1L; j < nItems + 1; j++) { 45 | test.addPreference(i, j, i * j % 5 + 1.0); 46 | predictions.addPreference(i, j, i * j % 5 + 1.0); 47 | } 48 | } 49 | Recall recall = new Recall(predictions, test, 1.0, new int[]{5, 10, 20}); 50 | 51 | recall.compute(); 52 | 53 | assertEquals(1.0, recall.getValue(), 0.0); 54 | assertEquals(5.0 / nItems, recall.getValueAt(5), 0.0001); 55 | assertEquals(10.0 / nItems, recall.getValueAt(10), 0.0001); 56 | 57 | Map recallPerUser = recall.getValuePerUser(); 58 | for (Map.Entry e : recallPerUser.entrySet()) { 59 | long user = e.getKey(); 60 | double value = e.getValue(); 61 | assertEquals(1.0, value, 0.0); 62 | } 63 | } 64 | 65 | @Test 66 | public void testOneUserTrecevalStrategySingleRelevance() { 67 | // groundtruth: ? 0 1 1 68 | // predictions: 3 4 5 1 69 | DataModelIF test = DataModelFactory.getDefaultModel(); 70 | DataModelIF predictions = DataModelFactory.getDefaultModel(); 71 | test.addPreference(1L, 2L, 0.0); 72 | test.addPreference(1L, 3L, 1.0); 73 | test.addPreference(1L, 4L, 1.0); 74 | predictions.addPreference(1L, 1L, 3.0); 75 | predictions.addPreference(1L, 2L, 4.0); 76 | predictions.addPreference(1L, 3L, 5.0); 77 | predictions.addPreference(1L, 4L, 1.0); 78 | 79 | Recall recall = new Recall(predictions, test, 1.0, new int[]{1, 2, 3, 4, 5}); 80 | 81 | recall.compute(); 82 | 83 | assertEquals(1.0, recall.getValue(), 0.001); 84 | assertEquals(0.5, recall.getValueAt(1), 0.001); 85 | assertEquals(0.5, recall.getValueAt(2), 0.001); 86 | assertEquals(0.5, recall.getValueAt(3), 0.001); 87 | assertEquals(1.0, recall.getValueAt(4), 0.001); 88 | assertEquals(1.0, recall.getValueAt(5), 0.001); 89 | 90 | Map recallPerUser = recall.getValuePerUser(); 91 | for (Map.Entry e : recallPerUser.entrySet()) { 92 | long user = e.getKey(); 93 | double value = e.getValue(); 94 | assertEquals(1.0, value, 0.001); 95 | } 96 | } 97 | 98 | @Test 99 | public void testOneUserTrecevalStrategyMultipleRelevance() { 100 | // groundtruth: ? 0 1 2 101 | // predictions: 3 4 5 1 102 | DataModelIF test = DataModelFactory.getDefaultModel(); 103 | DataModelIF predictions = DataModelFactory.getDefaultModel(); 104 | test.addPreference(1L, 2L, 0.0); 105 | test.addPreference(1L, 3L, 1.0); 106 | test.addPreference(1L, 4L, 2.0); 107 | predictions.addPreference(1L, 1L, 3.0); 108 | predictions.addPreference(1L, 2L, 4.0); 109 | predictions.addPreference(1L, 3L, 5.0); 110 | predictions.addPreference(1L, 4L, 1.0); 111 | 112 | Recall recall = new Recall(predictions, test, 1.0, new int[]{1, 2, 3, 4, 5}); 113 | 114 | recall.compute(); 115 | 116 | assertEquals(1.0, recall.getValue(), 0.001); 117 | assertEquals(0.5, recall.getValueAt(1), 0.001); 118 | assertEquals(0.5, recall.getValueAt(2), 0.001); 119 | assertEquals(0.5, recall.getValueAt(3), 0.001); 120 | assertEquals(1.0, recall.getValueAt(4), 0.001); 121 | assertEquals(1.0, recall.getValueAt(5), 0.001); 122 | 123 | Map recallPerUser = recall.getValuePerUser(); 124 | for (Map.Entry e : recallPerUser.entrySet()) { 125 | long user = e.getKey(); 126 | double value = e.getValue(); 127 | assertEquals(1.0, value, 0.001); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /rival-examples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | rival 5 | net.recommenders.rival 6 | 0.3-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | rival-examples 11 | jar 12 | RiVal Examples 13 | 14 | 15 | 16 | net.recommenders.rival 17 | rival-core 18 | ${project.version} 19 | 20 | 21 | net.recommenders.rival 22 | rival-split 23 | ${project.version} 24 | 25 | 26 | net.recommenders.rival 27 | rival-recommend 28 | ${project.version} 29 | 30 | 31 | net.recommenders.rival 32 | rival-evaluate 33 | ${project.version} 34 | 35 | 36 | 37 | org.apache.mahout 38 | mahout-core 39 | ${mahout.version} 40 | 41 | 42 | junit 43 | junit 44 | test 45 | 46 | 47 | commons-io 48 | commons-io 49 | 2.4 50 | 51 | 52 | net.lingala.zip4j 53 | zip4j 54 | 1.3.2 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-compiler-plugin 63 | 64 | 65 | org.codehaus.mojo 66 | exec-maven-plugin 67 | 68 | net.recommenders.rival.examples.movietweetings.RandomMahoutIBRecommenderEvaluator 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /rival-examples/src/main/java/net/recommenders/rival/examples/DataDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.examples; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.net.MalformedURLException; 21 | import java.net.URL; 22 | import net.lingala.zip4j.core.ZipFile; 23 | import net.lingala.zip4j.exception.ZipException; 24 | import org.apache.commons.io.FileUtils; 25 | 26 | /** 27 | * Class used to download a file and unzips it. 28 | * 29 | * @author Alan. 30 | */ 31 | public class DataDownloader { 32 | 33 | /** 34 | * The URL. 35 | */ 36 | private String url; 37 | /** 38 | * The folder where the file will be unzipped it. 39 | */ 40 | private String folder; 41 | 42 | /** 43 | * Default constructor. 44 | * 45 | * @param theUrl the URL from where the file will be downloaded 46 | * @param theFolder the folder where the file will be uncompressed 47 | */ 48 | public DataDownloader(final String theUrl, final String theFolder) { 49 | this.url = theUrl; 50 | this.folder = theFolder; 51 | } 52 | 53 | /** 54 | * Main method. 55 | * 56 | * @param args argument (not used) 57 | */ 58 | public static void main(final String[] args) { 59 | String url = "http://files.grouplens.org/datasets/movielens/ml-100k.zip"; 60 | String folder = "data3/ml-100k"; 61 | DataDownloader dd = new DataDownloader(url, folder); 62 | dd.downloadAndUnzip(); 63 | } 64 | 65 | /** 66 | * Downloads the file from the provided url. 67 | */ 68 | public void download() { 69 | URL dataURL = null; 70 | String fileName = folder + "/" + url.substring(url.lastIndexOf("/") + 1); 71 | if (new File(fileName).exists()) { 72 | return; 73 | } 74 | try { 75 | dataURL = new URL(url); 76 | } catch (MalformedURLException e) { 77 | e.printStackTrace(); 78 | } 79 | 80 | File downloadedData = new File(fileName); 81 | try { 82 | assert dataURL != null; 83 | FileUtils.copyURLToFile(dataURL, downloadedData); 84 | } catch (IOException e) { 85 | e.printStackTrace(); 86 | } 87 | } 88 | 89 | /** 90 | * Downloads the file from the provided url and uncompresses it to the given 91 | * folder. 92 | */ 93 | public void downloadAndUnzip() { 94 | URL dataURL = null; 95 | String fileName = folder + "/" + url.substring(url.lastIndexOf("/") + 1); 96 | File compressedData = new File(fileName); 97 | if (!new File(fileName).exists()) { 98 | try { 99 | dataURL = new URL(url); 100 | } catch (MalformedURLException e) { 101 | e.printStackTrace(); 102 | } 103 | try { 104 | assert dataURL != null; 105 | FileUtils.copyURLToFile(dataURL, compressedData); 106 | } catch (IOException e) { 107 | e.printStackTrace(); 108 | } 109 | } 110 | try { 111 | ZipFile zipFile = new ZipFile(compressedData); 112 | File dataFolder = new File(folder); 113 | zipFile.extractAll(dataFolder.getCanonicalPath()); 114 | } catch (ZipException | IOException e) { 115 | e.printStackTrace(); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /rival-examples/src/main/java/net/recommenders/rival/examples/mdp/EvaluationMetrics.java: -------------------------------------------------------------------------------- 1 | package net.recommenders.rival.examples.mdp; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class EvaluationMetrics { 7 | 8 | private Double rmse; 9 | private Double mae; 10 | private Double precision; 11 | private Double recall; 12 | private Double ndcg; 13 | 14 | private Map precisionAtK; 15 | private Map recallAtK; 16 | private Map ndcgAtK; 17 | 18 | public Double getRmse() { 19 | return rmse; 20 | } 21 | 22 | public Double getMae() { 23 | return mae; 24 | } 25 | 26 | public Double getPrecision() { 27 | return precision; 28 | } 29 | 30 | public Double getRecall() { 31 | return recall; 32 | } 33 | 34 | public Double getNdcg() { 35 | return ndcg; 36 | } 37 | 38 | public Double getPrecisionAtK(int cutoff) { 39 | if (precisionAtK.containsKey(cutoff)) 40 | return precisionAtK.get(cutoff); 41 | return Double.NaN; 42 | } 43 | 44 | public Double getRecallAtK(int cutoff) { 45 | if (recallAtK.containsKey(cutoff)) 46 | return recallAtK.get(cutoff); 47 | return Double.NaN; 48 | } 49 | 50 | public Double getNdcgAtK(int cutoff) { 51 | if (ndcgAtK.containsKey(cutoff)) 52 | return ndcgAtK.get(cutoff); 53 | return Double.NaN; 54 | } 55 | 56 | public EvaluationMetrics() { 57 | this.rmse = 0d; 58 | this.mae = 0d; 59 | this.precision = 0d; 60 | this.recall = 0d; 61 | this.ndcg = 0d; 62 | this.precisionAtK = new HashMap<>(); 63 | this.recallAtK = new HashMap<>(); 64 | this.ndcgAtK = new HashMap<>(); 65 | } 66 | 67 | public void setRMSE(Double rmse) { 68 | this.rmse = rmse; 69 | } 70 | 71 | public void setMAE(Double mae) { 72 | this.mae = mae; 73 | } 74 | 75 | public void setPrecisionAtK(int cutoff, Double precision) { 76 | this.precisionAtK.put(cutoff, precision); 77 | } 78 | 79 | public void setRecallAtK(int cutoff, Double recall) { 80 | this.recallAtK.put(cutoff, recall); 81 | } 82 | 83 | public void setNDCGAtK(int cutoff, Double ndcg) { 84 | this.ndcgAtK.put(cutoff, ndcg); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /rival-examples/src/main/java/net/recommenders/rival/examples/mdp/MahoutItemBasedCFRecSysEvaluator.java: -------------------------------------------------------------------------------- 1 | package net.recommenders.rival.examples.mdp; 2 | 3 | import org.apache.commons.lang.NotImplementedException; 4 | import org.apache.mahout.cf.taste.common.TasteException; 5 | import org.apache.mahout.cf.taste.impl.recommender.GenericItemBasedRecommender; 6 | import org.apache.mahout.cf.taste.impl.similarity.AbstractItemSimilarity; 7 | import org.apache.mahout.cf.taste.impl.similarity.EuclideanDistanceSimilarity; 8 | import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity; 9 | import org.apache.mahout.cf.taste.impl.similarity.TanimotoCoefficientSimilarity; 10 | import org.apache.mahout.cf.taste.model.DataModel; 11 | import org.apache.mahout.cf.taste.recommender.Recommender; 12 | 13 | public class MahoutItemBasedCFRecSysEvaluator extends CrossValidationRecSysEvaluator { 14 | 15 | public enum Distance {PEARSON, EUCLIDEAN, TANIMOTO} 16 | 17 | private Distance distance; 18 | 19 | public MahoutItemBasedCFRecSysEvaluator(int numFolds, double relevanceThreshold, Distance distance) { 20 | super(numFolds, relevanceThreshold); 21 | this.distance = distance; 22 | } 23 | 24 | @Override 25 | protected Recommender buildRecommender(DataModel trainingModel) throws TasteException { 26 | 27 | AbstractItemSimilarity similarity; 28 | 29 | switch (this.distance) { 30 | case EUCLIDEAN: 31 | similarity = new EuclideanDistanceSimilarity(trainingModel); 32 | break; 33 | case TANIMOTO: 34 | similarity = new TanimotoCoefficientSimilarity(trainingModel); 35 | break; 36 | case PEARSON: 37 | similarity = new PearsonCorrelationSimilarity(trainingModel); 38 | break; 39 | default: 40 | throw new NotImplementedException("Unknown distance specified!"); 41 | } 42 | 43 | return new GenericItemBasedRecommender(trainingModel, similarity); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /rival-examples/src/main/java/net/recommenders/rival/examples/mdp/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.examples.mdp; 17 | 18 | /** 19 | * 20 | * @author Alejandro 21 | */ 22 | public class Main { 23 | 24 | public static void main(String[] args) throws Exception { 25 | String datasetFile = "../../problem__marco_di_pietro/tripadvisor_ratings_u0_i0_.csv"; 26 | String out = "../../problem__marco_di_pietro/"; 27 | 28 | MahoutItemBasedCFRecSysEvaluator eval = new MahoutItemBasedCFRecSysEvaluator(/*5*/ 2, 4.0, MahoutItemBasedCFRecSysEvaluator.Distance.PEARSON); 29 | // eval.split(datasetFile, out, false, 0, ","); 30 | // eval.recommend(out, out); 31 | eval.buildEvaluationModels(out, out, out); 32 | eval.evaluate(out, out); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rival-examples/src/main/java/net/recommenders/rival/examples/movielens100k/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal movielens100k examples. 3 | */ 4 | package net.recommenders.rival.examples.movielens100k; 5 | -------------------------------------------------------------------------------- /rival-examples/src/main/java/net/recommenders/rival/examples/movietweetings/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal MovieTweetings examples. 3 | */ 4 | package net.recommenders.rival.examples.movietweetings; 5 | -------------------------------------------------------------------------------- /rival-examples/src/main/java/net/recommenders/rival/examples/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal examples. 3 | */ 4 | package net.recommenders.rival.examples; 5 | -------------------------------------------------------------------------------- /rival-package/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | rival 6 | net.recommenders.rival 7 | 0.3-SNAPSHOT 8 | .. 9 | 10 | rival-package 11 | RiVal Package 12 | 13 | This module prepares the packaging of the binary RiVal distribution. 14 | 15 | pom 16 | 17 | 18 | ${project.build.directory}/jars 19 | 20 | 21 | 22 | 23 | net.recommenders.rival 24 | rival-core 25 | ${project.version} 26 | 27 | 28 | net.recommenders.rival 29 | rival-evaluate 30 | ${project.version} 31 | 32 | 33 | net.recommenders.rival 34 | rival-split 35 | ${project.version} 36 | 37 | 38 | net.recommenders.rival 39 | rival-recommend 40 | ${project.version} 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.codehaus.mojo 48 | appassembler-maven-plugin 49 | 1.2 50 | 51 | ${project.build.directory}/dist/rival 52 | flat 53 | lib 54 | 55 | 59 | 60 | 61 | 62 | 63 | assemble 64 | package 65 | 66 | assemble 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | release 77 | 78 | false 79 | 80 | 81 | 82 | 83 | org.apache.maven.plugins 84 | maven-antrun-plugin 85 | 86 | 87 | dist-package 88 | package 89 | 90 | run 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /rival-package/src/text/APL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2015 recommenders.net 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /rival-package/src/text/LICENSE.txt: -------------------------------------------------------------------------------- 1 | RiVal: an open source recommender systems evaluation toolkit. 2 | Copyright 2013-2015 recommenders.net 3 | Parts of the work on RiVal has received funding from the European 4 | Union Seventh Framework Programme (FP7/2007-2013) under grant 5 | agreement no. 6 | 7 | This program is distributed in the hope that it will be useful, but WITHOUT 8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 9 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 10 | details. 11 | 12 | 13 | LensKit uses several libraries covered by the Apache Public License, version 2; 14 | this license is in the file APL.txt. A full list of licenses for third-paty 15 | software is in the file THIRD-PARTY.txt. 16 | 17 | SLF4J carries the following copyright: 18 | 19 | Copyright (c) 2004-2013 QOS.ch 20 | All rights reserved. 21 | 22 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 23 | and associated documentation files (the "Software"), to deal in the Software without 24 | restriction, including without limitation the rights to use, copy, modify, merge, publish, 25 | distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom 26 | the Software is furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all copies or 29 | substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 32 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 34 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 35 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /rival-recommend/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | net.recommenders.rival 7 | rival 8 | 0.3-SNAPSHOT 9 | 10 | rival-recommend 11 | jar 12 | RiVal Recommendation 13 | 14 | 15 | 16 | junit 17 | junit 18 | test 19 | 20 | 21 | 22 | net.recommenders.rival 23 | rival-core 24 | ${project.version} 25 | 26 | 27 | 28 | org.apache.mahout 29 | mahout-core 30 | ${mahout.version} 31 | 32 | 33 | commons-lang 34 | commons-lang 35 | 36 | 37 | asm 38 | asm 39 | 40 | 41 | org.apache.commons 42 | commons-lang3 43 | 44 | 45 | org.apache.commons 46 | commons-math3 47 | 48 | 49 | 50 | 51 | 52 | org.lenskit 53 | lenskit-core 54 | ${lenskit.version} 55 | 56 | 57 | org.apache.commons 58 | commons-lang3 59 | 60 | 61 | 62 | 63 | 64 | org.lenskit 65 | lenskit-knn 66 | ${lenskit.version} 67 | 68 | 69 | 70 | org.lenskit 71 | lenskit-svd 72 | ${lenskit.version} 73 | 74 | 75 | 76 | net.librec 77 | librec-core 78 | ${librec.version} 79 | 80 | 81 | commons-lang 82 | commons-lang 83 | 84 | 85 | 86 | 87 | 88 | org.ranksys.RankSys 89 | RankSys-core 90 | ${ranksys.version} 91 | 92 | 93 | org.ranksys.RankSys 94 | RankSys-fast 95 | ${ranksys.version} 96 | 97 | 98 | org.ranksys.RankSys 99 | RankSys-formats 100 | ${ranksys.version} 101 | 102 | 103 | org.ranksys.RankSys 104 | RankSys-mf 105 | ${ranksys.version} 106 | 107 | 108 | org.ranksys.RankSys 109 | RankSys-nn 110 | ${ranksys.version} 111 | 112 | 113 | 114 | 115 | org.slf4j 116 | slf4j-simple 117 | 1.6.6 118 | 119 | 120 | org.apache.commons 121 | commons-lang3 122 | 3.6 123 | 124 | 125 | 126 | 127 | 128 | 129 | org.apache.maven.plugins 130 | maven-compiler-plugin 131 | 132 | 133 | org.codehaus.mojo 134 | exec-maven-plugin 135 | 136 | java 137 | net.recommenders.rival.recommend.frameworks.MultipleRecommendationRunner 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /rival-recommend/src/main/java/net/recommenders/rival/recommend/frameworks/RecommenderIO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.recommend.frameworks; 17 | 18 | //import org.grouplens.lenskit.scored.ScoredId; 19 | 20 | import java.io.BufferedWriter; 21 | import java.io.File; 22 | import java.io.FileOutputStream; 23 | import java.io.IOException; 24 | import java.io.OutputStreamWriter; 25 | import java.util.List; 26 | import net.recommenders.rival.core.TemporalDataModelIF; 27 | 28 | /** 29 | * Recommender-related IO operations. 30 | * 31 | * @author Alan. 32 | */ 33 | public final class RecommenderIO { 34 | 35 | /** 36 | * Utility classes should not have a public or default constructor. 37 | */ 38 | private RecommenderIO() { 39 | } 40 | 41 | /** 42 | * Write recommendations to file. 43 | * 44 | * @param user the user 45 | * @param recommendations the recommendations 46 | * @param path directory where fileName will be written (if not null) 47 | * @param fileName name of the file, if null recommendations will not be 48 | * printed 49 | * @param append flag to decide if recommendations should be appended to 50 | * file 51 | * @param model if not null, recommendations will be saved here 52 | */ 53 | public static void writeData(final long user, final List> recommendations, final String path, final String fileName, final boolean append, final TemporalDataModelIF model) { 54 | BufferedWriter out = null; 55 | try { 56 | File dir = null; 57 | if (path != null) { 58 | dir = new File(path); 59 | if (!dir.isDirectory()) { 60 | if (!dir.mkdir() && (fileName != null)) { 61 | System.out.println("Directory " + path + " could not be created"); 62 | return; 63 | } 64 | } 65 | } 66 | if ((path != null) && (fileName != null)) { 67 | out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path + "/" + fileName, append), "UTF-8")); 68 | } 69 | for (Preference recItem : recommendations) { 70 | if (out != null) { 71 | out.write(user + "\t" + recItem.getItem()+ "\t" + recItem.getScore() + "\n"); 72 | } 73 | if (model != null) { 74 | model.addPreference(user, recItem.getItem(), recItem.getScore()); 75 | } 76 | } 77 | if (out != null) { 78 | out.flush(); 79 | out.close(); 80 | } 81 | } catch (IOException e) { 82 | System.out.println(e.getMessage()); 83 | // logger.error(e.getMessage()); 84 | } finally { 85 | if (out != null) { 86 | try { 87 | out.close(); 88 | } catch (IOException e) { 89 | e.printStackTrace(); 90 | } 91 | } 92 | } 93 | } 94 | 95 | public static class Preference { 96 | 97 | private U user; 98 | private I item; 99 | private double score; 100 | 101 | public Preference(U user, I item, double score) { 102 | this.user = user; 103 | this.item = item; 104 | this.score = score; 105 | } 106 | 107 | public U getUser() { 108 | return user; 109 | } 110 | 111 | public I getItem() { 112 | return item; 113 | } 114 | 115 | public double getScore() { 116 | return score; 117 | } 118 | 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /rival-recommend/src/main/java/net/recommenders/rival/recommend/frameworks/exceptions/RecommenderException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.recommend.frameworks.exceptions; 17 | 18 | /** 19 | * Exception to be used in the recommender module. 20 | * 21 | * @author Alan 22 | */ 23 | @SuppressWarnings("serial") 24 | public class RecommenderException extends Exception { 25 | 26 | /** 27 | * Constructs an exception with the specified message. 28 | * 29 | * @param msg the message to be shown later. 30 | */ 31 | public RecommenderException(final String msg) { 32 | super(msg); 33 | } 34 | 35 | /** 36 | * Constructs a new exception with the specified message and cause. 37 | * 38 | * @param msg the message to be shown later. 39 | * @param t the cause of the exception. 40 | */ 41 | public RecommenderException(final String msg, final Throwable t) { 42 | super(msg, t); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /rival-recommend/src/main/java/net/recommenders/rival/recommend/frameworks/exceptions/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal Exceptions used in the recommender module. 3 | */ 4 | package net.recommenders.rival.recommend.frameworks.exceptions; 5 | -------------------------------------------------------------------------------- /rival-recommend/src/main/java/net/recommenders/rival/recommend/frameworks/lenskit/EventDAOWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.recommend.frameworks.lenskit; 17 | 18 | import it.unimi.dsi.fastutil.longs.LongSet; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.Set; 22 | import net.recommenders.rival.core.TemporalDataModelIF; 23 | import org.lenskit.data.dao.DataAccessObject; 24 | import org.lenskit.data.dao.EntityCollectionDAO; 25 | import org.lenskit.data.dao.EntityQuery; 26 | import org.lenskit.data.dao.Query; 27 | import org.lenskit.data.entities.Entity; 28 | import org.lenskit.data.entities.EntityType; 29 | import org.lenskit.data.entities.TypedName; 30 | import org.lenskit.data.ratings.Rating; 31 | import org.lenskit.data.ratings.RatingBuilder; 32 | import org.lenskit.util.IdBox; 33 | import org.lenskit.util.io.ObjectStream; 34 | 35 | /** 36 | * Lenskit's EventDAO wrapper for {@link net.recommenders.rival.core.DataModel}. 37 | * 38 | * @author Alejandro 39 | */ 40 | public class EventDAOWrapper implements DataAccessObject { 41 | 42 | /** 43 | * Serial version UID. 44 | */ 45 | private static final long serialVersionUID = 170150729L; 46 | /** 47 | * Lenskit's DataAccessObject that will be used as wrapper. 48 | */ 49 | private DataAccessObject wrapper; 50 | 51 | /** 52 | * Constructs the wrapper using the provided model. 53 | * 54 | * @param model the model to be used to create the wrapped model 55 | */ 56 | public EventDAOWrapper(final TemporalDataModelIF model) { 57 | List events = new ArrayList<>(); 58 | RatingBuilder rb = new RatingBuilder(); 59 | for (Long u : model.getUsers()) { 60 | rb.setUserId(u); 61 | for (Long i : model.getUserItems(u)) { 62 | rb.setItemId(i); 63 | rb.setRating(model.getUserItemPreference(u, i)); 64 | Iterable timestamps = model.getUserItemTimestamps(u, i); 65 | long t = -1; 66 | if (timestamps != null) { 67 | for (Long tt : timestamps) { 68 | t = tt; 69 | break; 70 | } 71 | } 72 | rb.setTimestamp(t); 73 | events.add(rb.build()); 74 | } 75 | } 76 | wrapper = EntityCollectionDAO.create(events); 77 | } 78 | 79 | /** 80 | * {@inheritDoc} 81 | */ 82 | @Override 83 | public Set getEntityTypes() { 84 | return wrapper.getEntityTypes(); 85 | } 86 | 87 | /** 88 | * {@inheritDoc} 89 | */ 90 | @Override 91 | public LongSet getEntityIds(EntityType et) { 92 | return wrapper.getEntityIds(et); 93 | } 94 | 95 | /** 96 | * {@inheritDoc} 97 | */ 98 | @Override 99 | public Entity lookupEntity(EntityType et, long l) { 100 | return wrapper.lookupEntity(et, l); 101 | } 102 | 103 | /** 104 | * {@inheritDoc} 105 | */ 106 | @Override 107 | public E lookupEntity(EntityType et, long l, Class type) { 108 | return wrapper.lookupEntity(et, l, type); 109 | } 110 | 111 | /** 112 | * {@inheritDoc} 113 | */ 114 | @Override 115 | public ObjectStream streamEntities(EntityType et) { 116 | return wrapper.streamEntities(et); 117 | } 118 | 119 | /** 120 | * {@inheritDoc} 121 | */ 122 | @Override 123 | public ObjectStream streamEntities(EntityQuery eq) { 124 | return wrapper.streamEntities(eq); 125 | } 126 | 127 | /** 128 | * {@inheritDoc} 129 | */ 130 | @Override 131 | public ObjectStream>> streamEntityGroups(EntityQuery eq, TypedName tn) { 132 | return wrapper.streamEntityGroups(eq, tn); 133 | } 134 | 135 | /** 136 | * {@inheritDoc} 137 | */ 138 | @Override 139 | public Query query(EntityType et) { 140 | return wrapper.query(et); 141 | } 142 | 143 | /** 144 | * {@inheritDoc} 145 | */ 146 | @Override 147 | public Query query(Class type) { 148 | return wrapper.query(type); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /rival-recommend/src/main/java/net/recommenders/rival/recommend/frameworks/lenskit/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal Lenskit recommender interface. 3 | */ 4 | package net.recommenders.rival.recommend.frameworks.lenskit; 5 | -------------------------------------------------------------------------------- /rival-recommend/src/main/java/net/recommenders/rival/recommend/frameworks/librec/DataDAOWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.recommend.frameworks.librec; 17 | 18 | import com.google.common.collect.BiMap; 19 | import java.io.File; 20 | import java.io.PrintStream; 21 | import net.librec.common.LibrecException; 22 | import net.librec.conf.Configuration; 23 | import net.librec.conf.Configured; 24 | import net.librec.data.DataAppender; 25 | import net.librec.data.DataContext; 26 | import net.librec.data.DataModel; 27 | import net.librec.data.DataSplitter; 28 | import net.librec.data.model.TextDataModel; 29 | import net.librec.math.structure.DataSet; 30 | import net.recommenders.rival.core.TemporalDataModelIF; 31 | 32 | /** 33 | * 34 | * @author Alejandro 35 | */ 36 | public class DataDAOWrapper implements DataModel { 37 | 38 | /** 39 | * Serial version UID. 40 | */ 41 | private static final long serialVersionUID = 170160729L; 42 | /** 43 | * Librec's DataModel that will be used as wrapper. 44 | */ 45 | private DataModel wrapper; 46 | 47 | /** 48 | * Constructs the wrapper using the provided model. 49 | * 50 | * @param model the model to be used to create the wrapped model 51 | */ 52 | public DataDAOWrapper(final TemporalDataModelIF model) { 53 | super(); 54 | try { 55 | // generate file based on the model 56 | File path = File.createTempFile("librec", "datadao"); 57 | PrintStream out = new PrintStream(path); 58 | for (Long u : model.getUsers()) { 59 | for (Long i : model.getUserItems(u)) { 60 | Double d = model.getUserItemPreference(u, i); 61 | Iterable time = model.getUserItemTimestamps(u, i); 62 | if (time == null) { 63 | out.println(u + "\t" + i + "\t" + d); 64 | } else { 65 | for (Long t : time) { 66 | out.println(u + "\t" + i + "\t" + d + "\t" + t); 67 | break; 68 | } 69 | } 70 | } 71 | } 72 | out.close(); 73 | // create the wrapper based on this file 74 | Configuration confTraining = new Configuration(); 75 | confTraining.set(Configured.CONF_DATA_INPUT_PATH, path.getAbsolutePath()); 76 | confTraining.set(Configured.CONF_DATA_COLUMN_FORMAT, "UIR"); 77 | confTraining.set("data.model.splitter", "ratio"); 78 | confTraining.set("data.splitter.trainset.ratio", "0.999"); 79 | confTraining.set("data.splitter.ratio", "rating"); 80 | wrapper = new TextDataModel(confTraining); 81 | wrapper.buildDataModel(); 82 | } catch (Exception e) { 83 | e.printStackTrace(); 84 | } 85 | } 86 | 87 | @Override 88 | public void buildDataModel() throws LibrecException { 89 | wrapper.buildDataModel(); 90 | } 91 | 92 | @Override 93 | public void loadDataModel() throws LibrecException { 94 | wrapper.loadDataModel(); 95 | } 96 | 97 | @Override 98 | public void saveDataModel() throws LibrecException { 99 | wrapper.saveDataModel(); 100 | } 101 | 102 | @Override 103 | public DataSplitter getDataSplitter() { 104 | return wrapper.getDataSplitter(); 105 | } 106 | 107 | @Override 108 | public DataSet getTrainDataSet() { 109 | return wrapper.getTrainDataSet(); 110 | } 111 | 112 | @Override 113 | public DataSet getTestDataSet() { 114 | return wrapper.getTestDataSet(); 115 | } 116 | 117 | @Override 118 | public DataSet getValidDataSet() { 119 | return wrapper.getValidDataSet(); 120 | } 121 | 122 | @Override 123 | public DataSet getDatetimeDataSet() { 124 | return wrapper.getDatetimeDataSet(); 125 | } 126 | 127 | @Override 128 | public BiMap getUserMappingData() { 129 | return wrapper.getUserMappingData(); 130 | } 131 | 132 | @Override 133 | public BiMap getItemMappingData() { 134 | return wrapper.getItemMappingData(); 135 | } 136 | 137 | @Override 138 | public DataAppender getDataAppender() { 139 | return wrapper.getDataAppender(); 140 | } 141 | 142 | @Override 143 | public DataContext getContext() { 144 | return wrapper.getContext(); 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /rival-recommend/src/main/java/net/recommenders/rival/recommend/frameworks/mahout/PopularityBasedRecommender.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.recommend.frameworks.mahout; 17 | 18 | import java.util.Collection; 19 | import java.util.List; 20 | import org.apache.mahout.cf.taste.common.Refreshable; 21 | import org.apache.mahout.cf.taste.common.TasteException; 22 | import org.apache.mahout.cf.taste.impl.recommender.AbstractRecommender; 23 | import org.apache.mahout.cf.taste.model.DataModel; 24 | import org.apache.mahout.cf.taste.recommender.CandidateItemsStrategy; 25 | import org.apache.mahout.cf.taste.recommender.IDRescorer; 26 | import org.apache.mahout.cf.taste.recommender.RecommendedItem; 27 | import org.apache.mahout.cf.taste.recommender.Recommender; 28 | 29 | /** 30 | * Basic popularity-based recommender. 31 | * 32 | * @author Alejandro 33 | */ 34 | public class PopularityBasedRecommender extends AbstractRecommender implements Recommender { 35 | 36 | /** 37 | * Constructor when a canidate item strategy is to be used. 38 | * 39 | * @param dataModel the data model 40 | * @param candidateItemsStrategy the strategy 41 | */ 42 | public PopularityBasedRecommender(final DataModel dataModel, final CandidateItemsStrategy candidateItemsStrategy) { 43 | super(dataModel, candidateItemsStrategy); 44 | } 45 | 46 | /** 47 | * Default constructor. 48 | * 49 | * @param dataModel the data model. 50 | */ 51 | public PopularityBasedRecommender(final DataModel dataModel) { 52 | super(dataModel); 53 | } 54 | 55 | /** 56 | * Estimate the preference of @u of @i. 57 | * 58 | * @param u the user 59 | * @param i the item 60 | * @return the preference 61 | * @throws TasteException when the recommender cannot estimate the 62 | * preference. 63 | */ 64 | @Override 65 | public float estimatePreference(final long u, final long i) throws TasteException { 66 | return 1.0f * getDataModel().getPreferencesForItem(i).length(); 67 | } 68 | 69 | /** 70 | * Recommend items to a user. 71 | * 72 | * @param userID the user 73 | * @param howMany how many items to recommend 74 | * @param rescorer what rescorer to use 75 | * @return the list of recommendations 76 | * @throws TasteException if something in the recommender breaks. 77 | */ 78 | public List recommend(final long userID, final int howMany, final IDRescorer rescorer) throws TasteException { 79 | throw new UnsupportedOperationException("Not supported yet."); 80 | } 81 | 82 | /** 83 | * Refresh the recommender. 84 | * 85 | * @param clctn the data. 86 | */ 87 | @Override 88 | public void refresh(final Collection clctn) { 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /rival-recommend/src/main/java/net/recommenders/rival/recommend/frameworks/mahout/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal Mahout recommender interface. 3 | */ 4 | package net.recommenders.rival.recommend.frameworks.mahout; 5 | -------------------------------------------------------------------------------- /rival-recommend/src/main/java/net/recommenders/rival/recommend/frameworks/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal interfaces to recommender frameworks. 3 | */ 4 | package net.recommenders.rival.recommend.frameworks; 5 | -------------------------------------------------------------------------------- /rival-recommend/src/main/java/net/recommenders/rival/recommend/frameworks/ranksys/ItemIndexWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.recommend.frameworks.ranksys; 17 | 18 | import es.uam.eps.ir.ranksys.fast.index.FastItemIndex; 19 | import es.uam.eps.ir.ranksys.fast.index.SimpleFastItemIndex; 20 | import java.util.HashSet; 21 | import java.util.Set; 22 | import java.util.stream.Stream; 23 | import net.recommenders.rival.core.TemporalDataModelIF; 24 | 25 | /** 26 | * 27 | * @author Alejandro 28 | */ 29 | public class ItemIndexWrapper implements FastItemIndex { 30 | 31 | private final FastItemIndex wrapper; 32 | 33 | public ItemIndexWrapper(TemporalDataModelIF training, TemporalDataModelIF test) { 34 | Set items = new HashSet<>(); 35 | for (Long u : training.getItems()) { 36 | items.add(u); 37 | } 38 | for (Long u : test.getItems()) { 39 | items.add(u); 40 | } 41 | wrapper = SimpleFastItemIndex.load(items.stream()); 42 | } 43 | 44 | @Override 45 | public int item2iidx(Long i) { 46 | return wrapper.item2iidx(i); 47 | } 48 | 49 | @Override 50 | public Long iidx2item(int iidx) { 51 | return wrapper.iidx2item(iidx); 52 | } 53 | 54 | @Override 55 | public boolean containsItem(Long i) { 56 | return wrapper.containsItem(i); 57 | } 58 | 59 | @Override 60 | public int numItems() { 61 | return wrapper.numItems(); 62 | } 63 | 64 | @Override 65 | public Stream getAllItems() { 66 | return wrapper.getAllItems(); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /rival-recommend/src/main/java/net/recommenders/rival/recommend/frameworks/ranksys/UserIndexWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.recommend.frameworks.ranksys; 17 | 18 | import es.uam.eps.ir.ranksys.fast.index.FastUserIndex; 19 | import es.uam.eps.ir.ranksys.fast.index.SimpleFastUserIndex; 20 | import java.util.HashSet; 21 | import java.util.Set; 22 | import java.util.stream.Stream; 23 | import net.recommenders.rival.core.TemporalDataModelIF; 24 | 25 | /** 26 | * 27 | * @author Alejandro 28 | */ 29 | public class UserIndexWrapper implements FastUserIndex { 30 | 31 | private final FastUserIndex wrapper; 32 | 33 | public UserIndexWrapper(TemporalDataModelIF training, TemporalDataModelIF test) { 34 | Set users = new HashSet<>(); 35 | for (Long u : training.getUsers()) { 36 | users.add(u); 37 | } 38 | for (Long u : test.getUsers()) { 39 | users.add(u); 40 | } 41 | wrapper = SimpleFastUserIndex.load(users.stream()); 42 | } 43 | 44 | @Override 45 | public int user2uidx(Long u) { 46 | return wrapper.user2uidx(u); 47 | } 48 | 49 | @Override 50 | public Long uidx2user(int uidx) { 51 | return wrapper.uidx2user(uidx); 52 | } 53 | 54 | @Override 55 | public boolean containsUser(Long u) { 56 | return wrapper.containsUser(u); 57 | } 58 | 59 | @Override 60 | public int numUsers() { 61 | return wrapper.numUsers(); 62 | } 63 | 64 | @Override 65 | public Stream getAllUsers() { 66 | return wrapper.getAllUsers(); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /rival-recommend/src/main/java/net/recommenders/rival/recommend/frameworks/ranksys/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal RankSys recommender interface. 3 | */ 4 | package net.recommenders.rival.recommend.frameworks.ranksys; 5 | -------------------------------------------------------------------------------- /rival-recommend/src/main/resources/lenskit.item-based.properties: -------------------------------------------------------------------------------- 1 | recommender=org.grouplens.lenskit.knn.item.ItemItemScorer 2 | similarity=org.grouplens.lenskit.vectors.similarity.CosineVectorSimilarity 3 | training= 4 | test= 5 | output= 6 | framework=lenskit 7 | -------------------------------------------------------------------------------- /rival-recommend/src/main/resources/lenskit.properties: -------------------------------------------------------------------------------- 1 | lenskit.rec.ib=org.grouplens.lenskit.knn.item.ItemItemScorer 2 | lenskit.rec.ub=org.grouplens.lenskit.knn.user.UserUserItemScorer 3 | lenskit.rec.svd=org.grouplens.lenskit.mf.funksvd.FunkSVDItemScorer 4 | 5 | lenskit.sim=org.grouplens.lenskit.vectors.similarity.CosineVectorSimilarity,org.grouplens.lenskit.vectors.similarity.PearsonCorrelation 6 | 7 | svd.iterations=50 8 | neighborhood=-1,10,50 9 | input= 10 | output=./output 11 | -------------------------------------------------------------------------------- /rival-recommend/src/main/resources/lenskit.svd.properties: -------------------------------------------------------------------------------- 1 | recommender=org.grouplens.lenskit.mf.funksvd.FunkSVDItemScorer 2 | iterations=50 3 | factors=10 4 | training= 5 | test= 6 | output= 7 | framework=lenskit 8 | -------------------------------------------------------------------------------- /rival-recommend/src/main/resources/lenskit.user-based.properties: -------------------------------------------------------------------------------- 1 | recommender=org.grouplens.lenskit.knn.user.UserUserItemScorer 2 | similarity=org.grouplens.lenskit.vectors.similarity.CosineVectorSimilarity 3 | neighborhood=50 4 | training= 5 | test= 6 | output= 7 | framework=lenskit 8 | -------------------------------------------------------------------------------- /rival-recommend/src/main/resources/librec.svd.properties: -------------------------------------------------------------------------------- 1 | recommender=librec.rating.SVDPlusPlus 2 | factors=10 3 | iterations=10 4 | training= 5 | test= 6 | output= 7 | framework=librec 8 | -------------------------------------------------------------------------------- /rival-recommend/src/main/resources/mahout.item-based.properties: -------------------------------------------------------------------------------- 1 | recommender=org.apache.mahout.cf.taste.impl.recommender.GenericItemBasedRecommender 2 | similarity=org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity 3 | training= 4 | test= 5 | output= 6 | framework=mahout 7 | -------------------------------------------------------------------------------- /rival-recommend/src/main/resources/mahout.properties: -------------------------------------------------------------------------------- 1 | mahout.rec.ib=org.apache.mahout.cf.taste.impl.recommender.GenericItemBasedRecommender 2 | mahout.rec.ub=org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender 3 | mahout.rec.svd=org.apache.mahout.cf.taste.impl.recommender.svd.SVDRecommender 4 | 5 | mahout.sim=org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity 6 | #org.apache.mahout.cf.taste.impl.similarity.UncenteredCosineSimilarity 7 | mahout.svd.factorizer=org.apache.mahout.cf.taste.impl.recommender.svd.FunkSVDFactorizer 8 | 9 | svd.iterations=50 10 | neighborhood=-1,10 11 | input=/Users/alan/Documents/workspace/data/evaluation/ 12 | output=./output 13 | -------------------------------------------------------------------------------- /rival-recommend/src/main/resources/mahout.svd.properties: -------------------------------------------------------------------------------- 1 | recommender=org.apache.mahout.cf.taste.impl.recommender.svd.SVDRecommender 2 | factorizer=org.apache.mahout.cf.taste.impl.recommender.svd.SVDPlusPlusFactorizer 3 | factors=10 4 | iterations=10 5 | training= 6 | test= 7 | output= 8 | framework=mahout 9 | -------------------------------------------------------------------------------- /rival-recommend/src/main/resources/mahout.user-based.properties: -------------------------------------------------------------------------------- 1 | recommender=org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender 2 | similarity=org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity 3 | training= 4 | test= 5 | output= 6 | framework=mahout 7 | -------------------------------------------------------------------------------- /rival-recommend/src/main/resources/ranksys.properties: -------------------------------------------------------------------------------- 1 | ranksys.rec.ib= 2 | ranksys.rec.ub=es.uam.eps.ir.ranksys.nn.user.UserNeighborhoodRecommender 3 | ranksys.rec.svd=es.uam.eps.ir.ranksys.mf.rec.MFRecommender 4 | 5 | ranksys.sim=es.uam.eps.ir.ranksys.nn.user.sim.VectorCosineUserSimilarity 6 | ranksys.svd.factorizer=es.uam.eps.ir.ranksys.mf.als.HKVFactorizer 7 | 8 | svd.iterations=50 9 | neighborhood=-1,10,50 10 | input=./splits/ 11 | output=./recommendations/ 12 | -------------------------------------------------------------------------------- /rival-recommend/src/main/resources/ranksys.svd.properties: -------------------------------------------------------------------------------- 1 | recommender=es.uam.eps.ir.ranksys.mf.rec.MFRecommender 2 | factorizer=es.uam.eps.ir.ranksys.mf.als.HKVFactorizer 3 | factors=10 4 | iterations=10 5 | training= 6 | test= 7 | output= 8 | framework=ranksys 9 | -------------------------------------------------------------------------------- /rival-recommend/src/main/resources/ranksys.user-based.properties: -------------------------------------------------------------------------------- 1 | recommender=es.uam.eps.ir.ranksys.nn.user.UserNeighborhoodRecommender 2 | similarity=es.uam.eps.ir.ranksys.nn.user.sim.VectorCosineUserSimilarity 3 | training= 4 | test= 5 | output= 6 | framework=ranksys 7 | -------------------------------------------------------------------------------- /rival-recommend/src/test/java/net/recommenders/rival/recommend/frameworks/mahout/GenericRecommenderBuilderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.recommend.frameworks.mahout; 17 | 18 | import net.recommenders.rival.recommend.frameworks.exceptions.RecommenderException; 19 | import org.apache.mahout.cf.taste.common.TasteException; 20 | import org.apache.mahout.cf.taste.eval.RecommenderBuilder; 21 | import org.apache.mahout.cf.taste.impl.common.FastByIDMap; 22 | import org.apache.mahout.cf.taste.impl.model.GenericDataModel; 23 | import org.apache.mahout.cf.taste.impl.model.GenericPreference; 24 | import org.apache.mahout.cf.taste.impl.model.GenericUserPreferenceArray; 25 | import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender; 26 | import org.apache.mahout.cf.taste.impl.recommender.RandomRecommender; 27 | import org.apache.mahout.cf.taste.model.DataModel; 28 | import org.apache.mahout.cf.taste.model.PreferenceArray; 29 | import org.apache.mahout.cf.taste.recommender.Recommender; 30 | import org.junit.Test; 31 | 32 | import java.util.Arrays; 33 | 34 | import static org.junit.Assert.assertTrue; 35 | 36 | /** 37 | * Test for {@link GenericRecommenderBuilder}. 38 | * 39 | * @author Alan. 40 | */ 41 | public class GenericRecommenderBuilderTest { 42 | 43 | @Test 44 | public void testBuildDefaultRecommender() { 45 | 46 | RecommenderBuilder rb = new GenericRecommenderBuilder(); 47 | FastByIDMap userData = new FastByIDMap(); 48 | userData.put(1, new GenericUserPreferenceArray(Arrays.asList(new GenericPreference(1, 1, 1), 49 | new GenericPreference(1, 2, 1), new GenericPreference(1, 3, 1)))); 50 | userData.put(2, new GenericUserPreferenceArray(Arrays.asList(new GenericPreference(2, 1, 1), 51 | new GenericPreference(2, 2, 1), new GenericPreference(2, 4, 1)))); 52 | DataModel dm = new GenericDataModel(userData); 53 | 54 | Recommender rec = null; 55 | try { 56 | rec = rb.buildRecommender(dm); 57 | } catch (TasteException e) { 58 | e.printStackTrace(); 59 | } 60 | assertTrue(rec instanceof RandomRecommender); 61 | } 62 | 63 | @Test 64 | public void testBuildKNNRecommender() { 65 | GenericRecommenderBuilder rb = new GenericRecommenderBuilder(); 66 | FastByIDMap userData = new FastByIDMap(); 67 | userData.put(1, new GenericUserPreferenceArray(Arrays.asList(new GenericPreference(1, 1, 1), 68 | new GenericPreference(1, 2, 1), new GenericPreference(1, 3, 1)))); 69 | userData.put(2, new GenericUserPreferenceArray(Arrays.asList(new GenericPreference(2, 1, 1), 70 | new GenericPreference(2, 2, 1), new GenericPreference(2, 4, 1)))); 71 | DataModel dm = new GenericDataModel(userData); 72 | 73 | Recommender rec = null; 74 | String recommenderType = "org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender"; 75 | String similarityType = "org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity"; 76 | try { 77 | rec = rb.buildRecommender(dm, recommenderType, similarityType); 78 | } catch (RecommenderException e) { 79 | e.printStackTrace(); 80 | } 81 | assertTrue(rec instanceof GenericUserBasedRecommender); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /rival-split/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | net.recommenders.rival 6 | rival 7 | 0.3-SNAPSHOT 8 | 9 | rival-split 10 | jar 11 | RiVal Data Splitter 12 | 13 | 14 | 15 | junit 16 | junit 17 | test 18 | 19 | 20 | ${project.groupId} 21 | rival-core 22 | ${project.version} 23 | 24 | 25 | 26 | 27 | 28 | 29 | org.apache.maven.plugins 30 | maven-compiler-plugin 31 | 32 | 33 | org.codehaus.mojo 34 | exec-maven-plugin 35 | 36 | net.recommenders.rival.split.splitter.Split 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /rival-split/run.sh: -------------------------------------------------------------------------------- 1 | mvn exec:java -e -DpropertyFile=$1 2 | -------------------------------------------------------------------------------- /rival-split/scripts/crossvalidation_splitter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # similar to 'split_ratings.sh' from movielens10m package 3 | 4 | # usage: dataset field_separator num_folds output_folder training_prefix training_suffix test_prefix test_suffix per_user overwrite 5 | dataset=${1} 6 | field_separator=${2} 7 | num_fold=${3} 8 | output_folder=${4} 9 | training_prefix=${5} 10 | training_suffix=${6} 11 | test_prefix=${7} 12 | test_suffix=${8} 13 | per_user=${9} 14 | overwrite=${10} 15 | 16 | # a tab is passed as $'\t' 17 | if [[ $field_separator == " " ]] 18 | then 19 | users=`cut -f1 $dataset | sort | uniq` 20 | else 21 | users=`cut -f1 -d $'$field_separator' $dataset | sort | uniq` 22 | fi 23 | 24 | if $per_user 25 | then 26 | echo "Per user cross validation" 27 | # per_user cross validation 28 | for (( i=1; i <= $num_fold; i++ )) 29 | do 30 | training_file=$output_folder$training_prefix$i$training_suffix 31 | test_file=$output_folder$test_prefix$i$test_suffix 32 | 33 | if [ -f $training_file ] && [ -f $test_file ] && ! $overwrite 34 | then 35 | echo "ignoring $training_file and $test_file " 36 | else 37 | if [ -f $training_file ] 38 | then 39 | rm $training_file 40 | fi 41 | if [ -f $test_file ] 42 | then 43 | rm $test_file 44 | fi 45 | for user in $users 46 | do 47 | dataset_user=$dataset\_$user 48 | grep -P "^$user$field_separator" $dataset | shuf > $dataset_user 49 | 50 | RATINGS_COUNT=`wc -l $dataset_user | cut -d ' ' -f 1` 51 | SET_SIZE=`expr $RATINGS_COUNT / $num_fold` 52 | REMAINDER=`expr $RATINGS_COUNT % $num_fold` 53 | 54 | # training 55 | head -$(( ($i - 1) * $SET_SIZE )) $dataset_user >> $training_file 56 | if [ $i -ne $num_fold ]; then 57 | tail -n +$(( $i * $SET_SIZE + 1 )) $dataset_user >> $training_file 58 | fi 59 | 60 | # test 61 | sed -n "$(( ($i - 1) * $SET_SIZE + 1 )), $(( $i * SET_SIZE ))p" $dataset_user >> $test_file 62 | if [ $i -eq $num_fold ]; then 63 | tail -$REMAINDER $dataset_user >> $test_file 64 | fi 65 | 66 | rm $dataset_user 67 | done 68 | echo "$training_file created. `wc -l $training_file | cut -d " " -f 1` lines." 69 | echo "$test_file created. `wc -l $test_file | cut -d " " -f 1` lines." 70 | fi 71 | done 72 | else 73 | echo "Global cross validation" 74 | # global cross validation 75 | dataset_shuf=$dataset\_shuf 76 | shuf $dataset > $dataset_shuf 77 | 78 | RATINGS_COUNT=`wc -l $dataset_shuf| cut -d ' ' -f 1` 79 | SET_SIZE=`expr $RATINGS_COUNT / $num_fold` 80 | REMAINDER=`expr $RATINGS_COUNT % $num_fold` 81 | 82 | for (( i=1; i <= $num_fold; i++ )) 83 | do 84 | training_file=$output_folder$training_prefix$i$training_suffix 85 | test_file=$output_folder$test_prefix$i$test_suffix 86 | 87 | if [ -f $training_file ] && ! $overwrite 88 | then 89 | echo "ignoring $training_file" 90 | else 91 | head -$(( ($i - 1) * $SET_SIZE )) $dataset_shuf > $training_file 92 | if [ $i -ne $num_fold ]; then 93 | tail -n +$(( $i * $SET_SIZE + 1 )) $dataset_shuf >> $training_file 94 | fi 95 | echo "$training_file created. `wc -l $training_file | cut -d " " -f 1` lines." 96 | fi 97 | 98 | if [ -f $test_file ] && ! $overwrite 99 | then 100 | echo "ignoring $test_file" 101 | else 102 | sed -n "$(( ($i - 1) * $SET_SIZE + 1 )), $(( $i * SET_SIZE ))p" $dataset_shuf > $test_file 103 | if [ $i -eq $num_fold ]; then 104 | tail -$REMAINDER $dataset_shuf >> $test_file 105 | fi 106 | echo "$test_file created. `wc -l $test_file | cut -d " " -f 1` lines." 107 | fi 108 | done 109 | 110 | rm $dataset_shuf 111 | fi 112 | -------------------------------------------------------------------------------- /rival-split/scripts/random_splitter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # usage: dataset field_separator percentage_training output_folder training_prefix training_suffix test_prefix test_suffix per_user per_items overwrite 4 | dataset=${1} 5 | field_separator=${2} 6 | percentage_training=${3} 7 | output_folder=${4} 8 | training_prefix=${5} 9 | training_suffix=${6} 10 | test_prefix=${7} 11 | test_suffix=${8} 12 | per_user=${9} 13 | per_items=${10} # unused 14 | overwrite=${11} 15 | 16 | # a tab is passed as $'\t' 17 | if [[ $field_separator == " " ]] 18 | then 19 | users=`cut -f1 $dataset | sort | uniq` 20 | else 21 | users=`cut -f1 -d $'$field_separator' $dataset | sort | uniq` 22 | fi 23 | 24 | if $per_user 25 | then 26 | # per_user random split 27 | training_file=$output_folder$training_prefix$percentage_training$training_suffix 28 | test_file=$output_folder$test_prefix$percentage_training$test_suffix 29 | 30 | if [ -f $training_file ] && [ -f $test_file ] && ! $overwrite 31 | then 32 | echo "ignoring $training_file and $test_file " 33 | else 34 | if [ -f $training_file ] 35 | then 36 | rm $training_file 37 | fi 38 | if [ -f $test_file ] 39 | then 40 | rm $test_file 41 | fi 42 | for user in $users 43 | do 44 | dataset_user=$dataset\_$user 45 | grep -P "^$user$field_separator" $dataset | shuf > $dataset_user 46 | 47 | RATINGS_COUNT=`wc -l $dataset_user | cut -d ' ' -f 1` 48 | SPLIT_SIZE=`echo $RATINGS_COUNT $percentage_training | awk '{print int($1 * $2)}'` 49 | DIFFERENCE=`echo $RATINGS_COUNT $SPLIT_SIZE | awk '{print $1 - $2}'` 50 | 51 | # training 52 | head -$SPLIT_SIZE $dataset_user >> $training_file 53 | # test 54 | tail -$DIFFERENCE $dataset_user >> $test_file 55 | 56 | rm $dataset_user 57 | done 58 | echo "$training_file created. `wc -l $training_file | cut -d " " -f 1` lines." 59 | echo "$test_file created. `wc -l $test_file | cut -d " " -f 1` lines." 60 | fi 61 | else 62 | # global random split 63 | RATINGS_COUNT=`wc -l $dataset | cut -d ' ' -f 1` 64 | SPLIT_SIZE=`echo $RATINGS_COUNT $percentage_training | awk '{print int($1 * $2)}'` 65 | DIFFERENCE=`echo $RATINGS_COUNT $SPLIT_SIZE | awk '{print $1 - $2}'` 66 | 67 | dataset_shuf=$dataset\_shuf 68 | shuf $dataset > $dataset_shuf 69 | 70 | training_file=$output_folder$training_prefix$percentage_training$training_suffix 71 | test_file=$output_folder$test_prefix$percentage_training$test_suffix 72 | 73 | if [ -f $training_file ] && ! $overwrite 74 | then 75 | echo "ignoring $training_file" 76 | else 77 | head -$SPLIT_SIZE $dataset_shuf > $training_file 78 | echo "$training_file created. `wc -l $training_file | cut -d " " -f 1` lines." 79 | fi 80 | 81 | if [ -f $test_file ] && ! $overwrite 82 | then 83 | echo "ignoring $test_file" 84 | else 85 | tail -$DIFFERENCE $dataset_shuf > $test_file 86 | echo "$test_file created. `wc -l $test_file | cut -d " " -f 1` lines." 87 | fi 88 | 89 | # delete files 90 | rm $dataset_shuf 91 | fi 92 | -------------------------------------------------------------------------------- /rival-split/scripts/temporal_splitter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # usage: dataset field_separator percentage_training output_folder training_prefix training_suffix test_prefix test_suffix per_user per_items overwrite 4 | dataset=${1} 5 | field_separator=${2} 6 | percentage_training=${3} 7 | output_folder=${4} 8 | training_prefix=${5} 9 | training_suffix=${6} 10 | test_prefix=${7} 11 | test_suffix=${8} 12 | per_user=${9} 13 | per_items=${10} 14 | overwrite=${11} 15 | 16 | if [[ $field_separator == " " ]] 17 | then 18 | users=`cut -f1 $dataset | sort | uniq` 19 | else 20 | users=`cut -f1 -d $'$field_separator' $dataset | sort | uniq` 21 | fi 22 | 23 | if $per_user 24 | then 25 | # per_user temporal split 26 | training_file=$output_folder$training_prefix$percentage_training$training_suffix 27 | test_file=$output_folder$test_prefix$percentage_training$test_suffix 28 | 29 | if [ -f $training_file ] && [ -f $test_file ] && ! $overwrite 30 | then 31 | echo "ignoring $training_file and $test_file " 32 | else 33 | if [ -f $training_file ] 34 | then 35 | rm $training_file 36 | fi 37 | if [ -f $test_file ] 38 | then 39 | rm $test_file 40 | fi 41 | for user in $users 42 | do 43 | dataset_user=$dataset\_$user 44 | # sort the user's ratings, first by timestamp, then by item id (deterministic order) 45 | grep -P "^$user$field_separator" $dataset | sort -k4 -nk2 > $dataset_user 46 | 47 | RATINGS_COUNT=`wc -l $dataset_user | cut -d ' ' -f 1` 48 | SPLIT_SIZE=`echo $RATINGS_COUNT $percentage_training | awk '{print int($1 * $2)}'` 49 | DIFFERENCE=`echo $RATINGS_COUNT $SPLIT_SIZE | awk '{print $1 - $2}'` 50 | 51 | # training 52 | head -$SPLIT_SIZE $dataset_user >> $training_file 53 | # test 54 | tail -$DIFFERENCE $dataset_user >> $test_file 55 | 56 | rm $dataset_user 57 | done 58 | echo "$training_file created. `wc -l $training_file | cut -d " " -f 1` lines." 59 | echo "$test_file created. `wc -l $test_file | cut -d " " -f 1` lines." 60 | fi 61 | else 62 | # global temporal split 63 | RATINGS_COUNT=`wc -l $dataset | cut -d ' ' -f 1` 64 | SPLIT_SIZE=`echo $RATINGS_COUNT $percentage_training | awk '{print int($1 * $2)}'` 65 | DIFFERENCE=`echo $RATINGS_COUNT $SPLIT_SIZE | awk '{print $1 - $2}'` 66 | 67 | # sort the user's ratings, first by timestamp, then by item and user id (deterministic order) 68 | dataset_sort=$dataset\_sort 69 | sort -k4 -nk2 -nk1 $dataset > $dataset_sort 70 | 71 | training_file=$output_folder$training_prefix$percentage_training$training_suffix 72 | test_file=$output_folder$test_prefix$percentage_training$test_suffix 73 | 74 | if [ -f $training_file ] && ! $overwrite 75 | then 76 | echo "ignoring $training_file" 77 | else 78 | head -$SPLIT_SIZE $dataset_sort > $training_file 79 | echo "$training_file created. `wc -l $training_file | cut -d " " -f 1` lines." 80 | fi 81 | 82 | if [ -f $test_file ] && ! $overwrite 83 | then 84 | echo "ignoring $test_file" 85 | else 86 | tail -$DIFFERENCE $dataset_sort > $test_file 87 | echo "$test_file created. `wc -l $test_file | cut -d " " -f 1` lines." 88 | fi 89 | 90 | # delete files 91 | rm $dataset_sort 92 | fi 93 | -------------------------------------------------------------------------------- /rival-split/src/main/java/net/recommenders/rival/split/parser/AbstractLastfmCelmaParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.split.parser; 17 | 18 | import java.io.BufferedReader; 19 | import java.io.File; 20 | import java.io.IOException; 21 | import java.util.Map; 22 | import net.recommenders.rival.core.SimpleParser; 23 | 24 | /** 25 | * Parser for the Last.fm dataset by O Celma. 26 | * 27 | * @author Alejandro 28 | */ 29 | public class AbstractLastfmCelmaParser { 30 | 31 | /** 32 | * A flag that indicates if the artists should be considered as the items 33 | * (instead of tracks). 34 | */ 35 | private boolean useArtists; 36 | 37 | /** 38 | * Default constructor. 39 | * 40 | * @param useTheArtists Flag to consider artists as the items (instead of 41 | * tracks). 42 | */ 43 | public AbstractLastfmCelmaParser(final boolean useTheArtists) { 44 | this.useArtists = useTheArtists; 45 | } 46 | 47 | /** 48 | * Read a user/item mapping (user/item original value, user/item internal 49 | * id) from a file and return the maximum index number in that file. 50 | * 51 | * @param in The file with id mapping. 52 | * @param map The user/item mapping 53 | * @return The largest id number. 54 | * @throws IOException if file does not exist. 55 | */ 56 | public static long getIndexMap(final File in, final Map map) throws IOException { 57 | long id = 0; 58 | if (in.exists()) { 59 | BufferedReader br = SimpleParser.getBufferedReader(in); 60 | String line; 61 | while ((line = br.readLine()) != null) { 62 | String[] toks = line.split("\t"); 63 | long i = Long.parseLong(toks[1]); 64 | map.put(toks[0], i); 65 | id = Math.max(i, id); 66 | } 67 | br.close(); 68 | } 69 | return id + 1; 70 | } 71 | 72 | /** 73 | * Gets the value of the flag indicating if the artists should be considered 74 | * as items (instead of tracks). 75 | * 76 | * @return the flag 77 | */ 78 | protected boolean isUseArtists() { 79 | return useArtists; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /rival-split/src/main/java/net/recommenders/rival/split/parser/MovielensParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.split.parser; 17 | 18 | import java.io.BufferedReader; 19 | import java.io.File; 20 | import java.io.IOException; 21 | import net.recommenders.rival.core.DataModelFactory; 22 | import net.recommenders.rival.core.DataModelIF; 23 | import net.recommenders.rival.core.Parser; 24 | import net.recommenders.rival.core.SimpleParser; 25 | import net.recommenders.rival.core.TemporalDataModel; 26 | import net.recommenders.rival.core.TemporalDataModelIF; 27 | 28 | /** 29 | * A parser based on the format of Movielens files. 30 | * 31 | * @author Alejandro 32 | */ 33 | public class MovielensParser implements Parser { 34 | 35 | /** 36 | * The column index for the user id in the file. 37 | */ 38 | public static final int USER_TOK = 0; 39 | /** 40 | * The column index for the item id in the file. 41 | */ 42 | public static final int ITEM_TOK = 1; 43 | /** 44 | * The column index for the rating in the file. 45 | */ 46 | public static final int RATING_TOK = 2; 47 | /** 48 | * The column index for the time in the file. 49 | */ 50 | public static final int TIME_TOK = 3; 51 | 52 | /** 53 | * {@inheritDoc} 54 | */ 55 | @Override 56 | public DataModelIF parseData(final File f) throws IOException { 57 | return parseTemporalData(f); 58 | } 59 | 60 | /** 61 | * {@inheritDoc} 62 | */ 63 | @Override 64 | public TemporalDataModelIF parseTemporalData(final File f) throws IOException { 65 | TemporalDataModelIF dataset = DataModelFactory.getDefaultTemporalModel(); 66 | 67 | BufferedReader br = SimpleParser.getBufferedReader(f); 68 | String line; 69 | while ((line = br.readLine()) != null) { 70 | parseLine(line, dataset); 71 | } 72 | br.close(); 73 | 74 | return dataset; 75 | } 76 | 77 | /** 78 | * A method that parses a line from the file. 79 | * 80 | * @param line the line to be parsed 81 | * @param dataset the dataset where the information parsed from the line 82 | * will be stored into. 83 | */ 84 | private void parseLine(final String line, final TemporalDataModelIF dataset) { 85 | String[] toks; 86 | if (line.contains("::")) { 87 | toks = line.split("::"); 88 | } else { 89 | toks = line.split("\t"); 90 | } 91 | // user 92 | long userId = Long.parseLong(toks[USER_TOK]); 93 | // item 94 | long itemId = Long.parseLong(toks[ITEM_TOK]); 95 | // timestamp 96 | long timestamp = Long.parseLong(toks[TIME_TOK]); 97 | // preference 98 | double preference = Double.parseDouble(toks[RATING_TOK]); 99 | ////// 100 | // update information 101 | ////// 102 | dataset.addPreference(userId, itemId, preference); 103 | dataset.addTimestamp(userId, itemId, timestamp); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /rival-split/src/main/java/net/recommenders/rival/split/parser/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal data parsers. 3 | */ 4 | package net.recommenders.rival.split.parser; 5 | -------------------------------------------------------------------------------- /rival-split/src/main/java/net/recommenders/rival/split/splitter/IterativeLeaveOneOutSplitter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.split.splitter; 17 | 18 | import java.io.FileNotFoundException; 19 | import java.io.PrintWriter; 20 | import java.util.ArrayList; 21 | import java.util.Collections; 22 | import java.util.List; 23 | import java.util.Random; 24 | 25 | import net.recommenders.rival.core.DataModelIF; 26 | import net.recommenders.rival.core.TemporalDataModelIF; 27 | 28 | /** 29 | * Class that splits a dataset using a leave one out user based splitter It 30 | * leaves the result in the path passed as parameters, returns null. 31 | * 32 | * @author Andrés Moreno 33 | * 34 | */ 35 | public class IterativeLeaveOneOutSplitter implements Splitter { 36 | 37 | /** 38 | * An instance of a Random class. 39 | */ 40 | private Random rnd = new Random(0); 41 | 42 | /** 43 | * Minimum number of preferences of user to include it on train and test set 44 | */ 45 | private int minPreferences = 0; 46 | 47 | /** 48 | * Path out 49 | */ 50 | private String outPath; 51 | 52 | /** 53 | * Constructor. 54 | * 55 | * @param seed 56 | * value to initialize a Random class 57 | * @param minPreferences 58 | * minimum number of preferences either the user must have to be 59 | * included in the splits. 60 | * @param outPath 61 | * folder where each split (train and test) will be written 62 | */ 63 | public IterativeLeaveOneOutSplitter(final long seed, final int minPreferences, final String outPath) { 64 | 65 | this.rnd = new Random(seed); 66 | this.minPreferences = minPreferences; 67 | this.outPath = outPath; 68 | } 69 | 70 | /** 71 | * {@inheritDoc} 72 | */ 73 | @Override 74 | public DataModelIF[] split(DataModelIF data) { 75 | 76 | @SuppressWarnings("unchecked") 77 | final PrintWriter[] splits = new PrintWriter[2]; 78 | 79 | TemporalDataModelIF temporalModel = null; 80 | 81 | boolean hasTimestamps = data instanceof TemporalDataModelIF; 82 | if (hasTimestamps) { 83 | temporalModel = (TemporalDataModelIF) data; 84 | } 85 | String trainFile = outPath + "train_0.csv"; 86 | String testFile = outPath + "test_0.csv"; 87 | try { 88 | splits[0] = new PrintWriter(trainFile); 89 | splits[1] = new PrintWriter(testFile); 90 | } catch (FileNotFoundException e) { 91 | throw new RuntimeException("Error writting: " + e); 92 | } 93 | 94 | for (U user : data.getUsers()) { 95 | 96 | List items = new ArrayList<>(); 97 | for (I i : data.getUserItems(user)) { 98 | items.add(i); 99 | } 100 | 101 | if (items.size() >= minPreferences) { 102 | 103 | Collections.shuffle(items, rnd); 104 | I crossValidatedItem = items.remove(0); 105 | double prefCV = data.getUserItemPreference(user, crossValidatedItem); 106 | String timestamp = null; 107 | 108 | if (hasTimestamps) { 109 | List times = new ArrayList<>(); 110 | for (Long time : temporalModel.getUserItemTimestamps(user, crossValidatedItem)) { 111 | times.add(time); 112 | } 113 | 114 | timestamp = "" + Collections.min(times); 115 | splits[1].println(user + "\t" + crossValidatedItem + "\t" + prefCV + "\t" + timestamp); 116 | } else { 117 | splits[1].println(user + "\t" + crossValidatedItem + "\t" + prefCV); 118 | } 119 | 120 | for (I item : items) { 121 | Double pref = data.getUserItemPreference(user, item); 122 | if (hasTimestamps) { 123 | List times = new ArrayList<>(); 124 | for (Long time : temporalModel.getUserItemTimestamps(user, item)) { 125 | times.add(time); 126 | } 127 | timestamp = "" + Collections.min(times); 128 | splits[0].println(user + "\t" + item + "\t" + pref + "\t" + timestamp); 129 | } 130 | else { 131 | splits[0].println(user + "\t" + item + "\t" + pref); 132 | } 133 | 134 | } 135 | 136 | } 137 | 138 | } 139 | for (int i = 0; i < splits.length; i++) { 140 | splits[i].flush(); 141 | splits[i].close(); 142 | } 143 | return null; 144 | } 145 | 146 | @Override 147 | public TemporalDataModelIF[] split(TemporalDataModelIF data) { 148 | 149 | return (TemporalDataModelIF[]) this.split(((DataModelIF) data)); 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /rival-split/src/main/java/net/recommenders/rival/split/splitter/Split.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.split.splitter; 17 | 18 | import java.io.FileInputStream; 19 | import java.io.IOException; 20 | import java.util.Properties; 21 | 22 | import net.recommenders.rival.core.TemporalDataModelIF; 23 | import net.recommenders.rival.split.parser.ParserRunner; 24 | 25 | /** 26 | * Main class that parses a data set and splits it according to a property file. 27 | * 28 | * @author Alejandro 29 | */ 30 | public final class Split { 31 | 32 | /** 33 | * Utility classes should not have a public or default constructor. 34 | */ 35 | private Split() { 36 | } 37 | 38 | /** 39 | * Main method that loads properties from a file and runs a SplitterRunner. 40 | * 41 | * @param args program arguments (not used) 42 | * @throws Exception see {@link net.recommenders.rival.split.splitter.SplitterRunner#run(Properties, TemporalDataModelIF, boolean)} 43 | * @see net.recommenders.rival.split.splitter.SplitterRunner 44 | */ 45 | public static void main(final String[] args) throws Exception { 46 | String propertyFile = System.getProperty("propertyFile"); 47 | 48 | final Properties properties = new Properties(); 49 | try { 50 | properties.load(new FileInputStream(propertyFile)); 51 | } catch (IOException ie) { 52 | ie.printStackTrace(); 53 | } 54 | 55 | SplitterRunner.run(properties, ParserRunner.run(properties), true); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /rival-split/src/main/java/net/recommenders/rival/split/splitter/Splitter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 recommenders.net. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.recommenders.rival.split.splitter; 17 | 18 | import net.recommenders.rival.core.DataModelIF; 19 | import net.recommenders.rival.core.TemporalDataModelIF; 20 | 21 | /** 22 | * Interface for the data splitter. 23 | * 24 | * @author Alejandro 25 | * 26 | * @param generic type for users 27 | * @param generic type for items 28 | */ 29 | public interface Splitter { 30 | 31 | /** 32 | * Splits the data. 33 | * 34 | * @param data The data. 35 | * @return The split data model. 36 | */ 37 | DataModelIF[] split(DataModelIF data); 38 | 39 | /** 40 | * Splits temporal data. 41 | * 42 | * @param data The data. 43 | * @return The split data model. 44 | */ 45 | TemporalDataModelIF[] split(TemporalDataModelIF data); 46 | } 47 | -------------------------------------------------------------------------------- /rival-split/src/main/java/net/recommenders/rival/split/splitter/ValidationSplitter.java: -------------------------------------------------------------------------------- 1 | package net.recommenders.rival.split.splitter; 2 | 3 | import net.recommenders.rival.core.DataModelIF; 4 | import net.recommenders.rival.core.TemporalDataModelIF; 5 | 6 | 7 | public class ValidationSplitter implements Splitter { 8 | private final Splitter splitter; 9 | 10 | public ValidationSplitter(Splitter splitter) { 11 | if (splitter instanceof ValidationSplitter) { 12 | throw new IllegalArgumentException("Unable to apply a validation splitter recursively!"); 13 | } 14 | this.splitter = splitter; 15 | } 16 | 17 | @SuppressWarnings("unchecked") 18 | @Override 19 | public DataModelIF[] split(DataModelIF data) { 20 | DataModelIF[] trainingTestSplits = this.splitter.split(data); 21 | DataModelIF[] newSplits = new DataModelIF[trainingTestSplits.length / 2 * 3]; 22 | 23 | for (int i = 0; i < trainingTestSplits.length / 2; i++) { 24 | DataModelIF trainingVal = trainingTestSplits[2 * i]; 25 | DataModelIF[] trainingValSplit = splitter.split(trainingVal); 26 | DataModelIF test = trainingTestSplits[2 * i + 1]; 27 | 28 | newSplits[3 * i] = trainingValSplit[0]; 29 | newSplits[3 * i + 1] = trainingValSplit[1]; 30 | newSplits[3 * i + 2] = test; 31 | } 32 | return newSplits; 33 | 34 | } 35 | 36 | @SuppressWarnings("unchecked") 37 | @Override 38 | public TemporalDataModelIF[] split(TemporalDataModelIF data) { 39 | TemporalDataModelIF[] trainingTestSplits = this.splitter.split(data); 40 | TemporalDataModelIF[] newSplits = new TemporalDataModelIF[trainingTestSplits.length / 2 * 3]; 41 | 42 | for (int i = 0; i < trainingTestSplits.length / 2; i++) { 43 | TemporalDataModelIF trainingVal = trainingTestSplits[2 * i]; 44 | TemporalDataModelIF[] trainingValSplit = splitter.split(trainingVal); 45 | TemporalDataModelIF test = trainingTestSplits[2 * i + 1]; 46 | 47 | newSplits[3 * i] = trainingValSplit[0]; 48 | newSplits[3 * i + 1] = trainingValSplit[1]; 49 | newSplits[3 * i + 2] = test; 50 | } 51 | return newSplits; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /rival-split/src/main/java/net/recommenders/rival/split/splitter/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * RiVal data splitters. 3 | */ 4 | package net.recommenders.rival.split.splitter; 5 | -------------------------------------------------------------------------------- /rival-split/src/main/resources/lastfmcelma360k.cv.global.artist.splitter.properties: -------------------------------------------------------------------------------- 1 | dataset.file= 2 | dataset.parser=net.recommenders.rival.split.parser.LastfmCelma360KParser 3 | dataset.splitter=net.recommenders.rival.split.splitter.CrossValidationSplitter 4 | dataset.parser.lastfm.idsprefix=lastfmcelma360k_artistmap 5 | dataset.parser.lastfm.useartists=true 6 | split.peruser=false 7 | split.seed=2014 8 | split.cv.nfolds=5 9 | split.output.folder=./ 10 | split.training.prefix=lastfmcelma360k_artist_fold 11 | split.test.prefix=lastfmcelma360k_artist_fold 12 | split.training.suffix=_global.train 13 | split.test.suffix=_global.test 14 | -------------------------------------------------------------------------------- /rival-split/src/main/resources/lastfmcelma360k.rnd.global.artist.splitter.properties: -------------------------------------------------------------------------------- 1 | dataset.file= 2 | dataset.parser=net.recommenders.rival.split.parser.LastfmCelma360KParser 3 | dataset.splitter=net.recommenders.rival.split.splitter.RandomSplitter 4 | dataset.parser.lastfm.idsprefix=lastfmcelma360k_artistmap 5 | dataset.parser.lastfm.useartists=true 6 | split.peruser=false 7 | split.seed=2014 8 | split.random.percentage=0.8 9 | split.output.folder=./ 10 | split.training.prefix=lastfmcelma360k_artist_r0.8_ 11 | split.test.prefix=lastfmcelma360k_artist_r0.8_ 12 | split.training.suffix=_global.train 13 | split.test.suffix=_global.test 14 | -------------------------------------------------------------------------------- /rival-split/src/main/resources/movielens100k.cv.global.splitter.properties: -------------------------------------------------------------------------------- 1 | dataset.file= 2 | dataset.parser=net.recommenders.rival.split.parser.MovielensParser 3 | dataset.splitter=net.recommenders.rival.split.splitter.CrossValidationSplitter 4 | split.peruser=false 5 | split.seed=2014 6 | split.cv.nfolds=5 7 | split.output.folder=./ 8 | split.training.prefix=mov100k_fold 9 | split.test.prefix=mov100k_fold 10 | split.training.suffix=_global.train 11 | split.test.suffix=_global.test 12 | -------------------------------------------------------------------------------- /rival-split/src/main/resources/movielens100k.cv.peruser.splitter.properties: -------------------------------------------------------------------------------- 1 | dataset.file= 2 | dataset.parser=net.recommenders.rival.split.parser.MovielensParser 3 | dataset.splitter=net.recommenders.rival.split.splitter.CrossValidationSplitter 4 | split.peruser=true 5 | split.seed=2014 6 | split.cv.nfolds=5 7 | split.output.folder=./ 8 | split.training.prefix=mov100k_fold 9 | split.test.prefix=mov100k_fold 10 | split.training.suffix=_peruser.train 11 | split.test.suffix=_peruser.test 12 | -------------------------------------------------------------------------------- /rival-split/src/main/resources/movielens100k.rnd.global.splitter.properties: -------------------------------------------------------------------------------- 1 | dataset.file= 2 | dataset.parser=net.recommenders.rival.split.parser.MovielensParser 3 | dataset.splitter=net.recommenders.rival.split.splitter.RandomSplitter 4 | split.peruser=false 5 | split.seed=2014 6 | split.random.percentage=0.8 7 | split.output.folder=./ 8 | split.training.prefix=mov100k_r0.8_ 9 | split.test.prefix=mov100k_r0.8_ 10 | split.training.suffix=_global.train 11 | split.test.suffix=_global.test 12 | -------------------------------------------------------------------------------- /rival-split/src/main/resources/movielens100k.rnd.peruser.splitter.properties: -------------------------------------------------------------------------------- 1 | dataset.file= 2 | dataset.parser=net.recommenders.rival.split.parser.MovielensParser 3 | dataset.splitter=net.recommenders.rival.split.splitter.RandomSplitter 4 | split.peruser=true 5 | split.seed=2014 6 | split.random.percentage=0.8 7 | split.output.folder=./ 8 | split.training.prefix=mov100k_r0.8_ 9 | split.test.prefix=mov100k_r0.8_ 10 | split.training.suffix=_peruser.train 11 | split.test.suffix=_peruser.test 12 | -------------------------------------------------------------------------------- /src/assemble/source-package.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | source 8 | 9 | tar.gz 10 | zip 11 | 12 | 13 | 14 | ${project.basedir} 15 | / 16 | true 17 | 18 | **/*.log 19 | **/*~ 20 | .* 21 | **/*.iml 22 | **/target/** 23 | **/.idea 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/site/markdown/index.md: -------------------------------------------------------------------------------- 1 | Title: About 2 | 3 | RiVal Recommender System Evaluation Toolkit 4 | 5 | RiVal is an open source toolkit for evalauting recommender systems. 6 | 7 | This is a Maven-generated site for RiVal, providing auto-generated documentation, release and other RiVal-related information. 8 | Some useful resources for using or contributing RiVal are: 9 | 10 | - [RiVal home page](http://rival.recommenders.net) 11 | 12 | - [RiVal source on GitHub](http://github.com/recommenders/rival) 13 | 14 | - [RiVal Wiki](http://github.com/recommenders/rival/wiki) 15 | 16 | - [JavaDoc](./apidocs) 17 | 18 | -------------------------------------------------------------------------------- /src/site/markdown/releases/index.md.vm: -------------------------------------------------------------------------------- 1 | Title: Release Notes 2 | 3 | 4 | #macro ( release $ver ) 5 | #if( $project.version == "${ver}-SNAPSHOT" )## 6 | - [Release $ver](./rival-${ver}.html) (in progress) 7 | #else 8 | - [Release $ver](./rival-${ver}.html) 9 | #end 10 | #end 11 | 12 | # RiVal Releases 13 | 14 | #release("0.1") (June 2, 2014) 15 | -------------------------------------------------------------------------------- /src/site/markdown/releases/relnotes.vm: -------------------------------------------------------------------------------- 1 | #macro( issue $num )## 2 | [#$num](https://github.com/recommenders/rival/issues/$num)## 3 | #end 4 | #macro( user $name)## 5 | [@$name](https://github.com/$name)## 6 | #end 7 | 8 | #macro( makeHeader $ver $tail )## 9 | Title: Release $ver $tail 10 | 11 | 12 | # Release $ver $tail 13 | #end 14 | 15 | #macro( header $ver )## 16 | #if( $project.version == "${ver}-SNAPSHOT" )## 17 | #makeHeader($ver, "(in progress)") 18 | #else 19 | #makeHeader($ver, "") 20 | #end 21 | #end 22 | 23 | #macro( pmIntro $ver $ms ) 24 | 25 | #if( $project.version == "${ver}-SNAPSHOT" ) 26 | [changelog]: https://github.com/recommenders/rival/commits 27 | #else 28 | [changelog]: https://github.com/recommenders/rival/commits/rival-$ver 29 | #end 30 | 31 | #if($ms) 32 | [issues]: https://github.com/recommenders/rival/issues?milestone=$ms&state=closed 33 | 34 | Have a look at the [Git changelog][changelog] and the [list of tickets][issues] to see more details 35 | on changes in this version. 36 | #else 37 | Have a look at the [Git changelog][changelog] to see more details on changes in this version. 38 | #end 39 | #end 40 | 41 | #macro( stdHeader $ver $ms ) 42 | #header($ver) 43 | 44 | #pmIntro($ver, $ms) 45 | #end 46 | 47 | -- file based on LensKit -- https://github.com/grouplens/lenskit/blob/master/src/site/markdown/releases/relnotes.vm -------------------------------------------------------------------------------- /src/site/markdown/releases/rival-0.1.md: -------------------------------------------------------------------------------- 1 | Title: Release 0.1 2 | 3 | #Release 0.1 (June 2, 2014) 4 | Runnable data splitters, recommendations through Mahout or Lenskit, evaluation strategies and basic set of metrics. 5 | 6 | -------------------------------------------------------------------------------- /src/site/resources/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/recommenders/rival/6ee8223e91810ae1c6052899595af3906e0c34c6/src/site/resources/logo.png -------------------------------------------------------------------------------- /src/site/site.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | org.apache.maven.skins 8 | maven-fluido-skin 9 | 1.3.1 10 | 11 | 12 | 13 | 14 | 15 | RiVal 16 | logo.png 17 | http://rival.recommenders.net/ 18 | 19 | 20 | 21 | 22 | recommenders/rival 23 | right 24 | black 25 | 26 | 27 | 28 | 29 | 30 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | --------------------------------------------------------------------------------