Recommender support added
authorTasuku Suzuki <stasuku@gmail.com>
Sun, 26 Jan 2014 05:31:49 +0000 (14:31 +0900)
committerTasuku Suzuki <stasuku@gmail.com>
Sun, 26 Jan 2014 05:34:47 +0000 (14:34 +0900)
Change-Id: I952c55da450ef2501ce6367609e7e7f3f38b1428
Reviewed-on: http://cr.qtquick.me/798
Reviewed-by: Tasuku Suzuki <stasuku@gmail.com>
Tested-by: Tasuku Suzuki <stasuku@gmail.com>
36 files changed:
examples/cpp/anomaly/nikkei_stock_avarage/main.cpp
examples/cpp/classifier/gender/widget.cpp
examples/cpp/cpp.pro
examples/cpp/recommender/npb_similar_player/baseball.csv [new file with mode: 0644]
examples/cpp/recommender/npb_similar_player/dialog.cpp [new file with mode: 0644]
examples/cpp/recommender/npb_similar_player/dialog.h [new file with mode: 0644]
examples/cpp/recommender/npb_similar_player/dialog.ui [new file with mode: 0644]
examples/cpp/recommender/npb_similar_player/main.cpp [new file with mode: 0644]
examples/cpp/recommender/npb_similar_player/npb_similar_player.json [new file with mode: 0644]
examples/cpp/recommender/npb_similar_player/npb_similar_player.pro [new file with mode: 0644]
examples/cpp/recommender/npb_similar_player/npb_similar_player.qrc [new file with mode: 0644]
examples/cpp/recommender/recommender.pro [new file with mode: 0644]
examples/qml/qml.pro
examples/qml/recommender/npb_similar_player/baseball.csv [new file with mode: 0644]
examples/qml/recommender/npb_similar_player/main.cpp [new file with mode: 0644]
examples/qml/recommender/npb_similar_player/npb_similar_player.json [new file with mode: 0644]
examples/qml/recommender/npb_similar_player/npb_similar_player.pro [new file with mode: 0644]
examples/qml/recommender/npb_similar_player/npb_similar_player.qml [new file with mode: 0644]
examples/qml/recommender/npb_similar_player/npb_similar_player.qrc [new file with mode: 0644]
examples/qml/recommender/recommender.pro [new file with mode: 0644]
src/imports/jubatus/jubatus.pro
src/imports/jubatus/main.cpp
src/imports/jubatus/qmljubatusclassifier.cpp [new file with mode: 0644]
src/imports/jubatus/qmljubatusclassifier.h [new file with mode: 0644]
src/imports/jubatus/qmljubatusrecommender.cpp [new file with mode: 0644]
src/imports/jubatus/qmljubatusrecommender.h [new file with mode: 0644]
src/jubatus/anomaly/qjubatusanomaly.cpp
src/jubatus/anomaly/qjubatusanomaly.h
src/jubatus/classifier/qjubatusclassifier.cpp
src/jubatus/classifier/qjubatusclassifier.h
src/jubatus/client/qjubatusclient.cpp
src/jubatus/client/qjubatusclient.h
src/jubatus/jubatus.pro
src/jubatus/recommender/qjubatusrecommender.cpp [new file with mode: 0644]
src/jubatus/recommender/qjubatusrecommender.h [new file with mode: 0644]
src/jubatus/recommender/recommender.pri [new file with mode: 0644]

index 03b9de6..648e4aa 100644 (file)
@@ -15,6 +15,7 @@ protected:
 private:
     QNetworkAccessManager manager;
     struct Data {
+        Data() : first(0), last(0), score(0) {}
         float first;
         float last;
         float score;
@@ -28,7 +29,9 @@ Graph::Graph(QWidget *parent)
 {
     QObject::connect(&manager, &QNetworkAccessManager::finished, [this](QNetworkReply *reply) {
         QJubatusAnomaly anomaly;
-        anomaly.setTimeout(60);
+        bool hasError = false;
+        connect(&anomaly, &QJubatusAnomaly::error, [&hasError](const QString &message) { hasError = true; });
+
         QStringList lines = QTextCodec::codecForName("Shift-JIS")->toUnicode(reply->readAll()).split(QStringLiteral("\r\n"));
         foreach (const QString &line, lines) {
             QStringList columns = line.split(QStringLiteral(","));
@@ -46,8 +49,10 @@ Graph::Graph(QWidget *parent)
                 params.insert("first", d.first * 100);
                 params.insert("last", d.last * 100);
                 params.insert("diff", (d.last - d.first) * 100);
-                QJubatusAnomaly::IdAndScore ret = anomaly.add(params);
-                d.score = ret.score;
+                if (!hasError) {
+                    QJubatusAnomaly::IdAndScore ret = anomaly.add(params);
+                    d.score = ret.score;
+                }
                 data.append(d);
             }
         }
index 260f6d4..4cda290 100644 (file)
@@ -37,12 +37,12 @@ static QVariantMap makeDatum(const Ui::Widget *ui)
     return ret;
 }
 
-static QList<QJubatusClassifier::TrainData> makeTrainData(const QString &label, const Ui::Widget *ui)
+static QList<QJubatusClassifier::LabeledDatum> makeTrainData(const QString &label, const Ui::Widget *ui)
 {
-    QList<QJubatusClassifier::TrainData> ret;
-    QJubatusClassifier::TrainData data;
-    data.first = label;
-    data.second = makeDatum(ui);
+    QList<QJubatusClassifier::LabeledDatum> ret;
+    QJubatusClassifier::LabeledDatum data;
+    data.label = label;
+    data.data = makeDatum(ui);
     ret.append(data);
     return ret;
 }
@@ -51,8 +51,10 @@ Widget::Widget(QWidget *parent)
     : QDialog(parent)
     , ui(new Ui::Widget)
 {
-    classifier.setTimeout(10.0);
     classifier.setName(QStringLiteral("test"));
+    connect(&classifier, &QJubatusClassifier::error, [this](const QString &message) {
+        QMessageBox::warning(this, tr("Error"), message);
+    });
     ui->setupUi(this);
 
     connect(ui->male, &QPushButton::clicked, [&] {
index ea31fc9..ceee12f 100644 (file)
@@ -1,2 +1,2 @@
 TEMPLATE = subdirs
-SUBDIRS += anomaly classifier
+SUBDIRS += anomaly classifier recommender
diff --git a/examples/cpp/recommender/npb_similar_player/baseball.csv b/examples/cpp/recommender/npb_similar_player/baseball.csv
new file mode 100644 (file)
index 0000000..c9c6fd6
--- /dev/null
@@ -0,0 +1,144 @@
+長野久義,巨人,0.301,144,653,574,173,14,60,20,75,1,100,2,5,0.432,0.382,0.815,6.45,6.2
+大島洋平,中日,0.31,144,631,555,172,1,13,32,46,13,80,17,7,0.368,0.376,0.744,5.13,4.91
+鳥谷敬,阪神,0.262,144,624,515,135,8,59,15,94,2,91,5,12,0.375,0.373,0.748,5.28,5.33
+坂本勇人,巨人,0.311,144,619,557,173,14,69,16,39,6,90,12,5,0.456,0.359,0.815,6.29,6.06
+中田翔,日本ハム,0.239,144,606,547,131,24,77,5,50,5,101,0,8,0.42,0.307,0.727,4.44,4.54
+李大浩,オリックス,0.286,144,601,525,150,24,91,0,64,7,85,0,18,0.478,0.368,0.846,5.99,5.95
+陽岱鋼,日本ハム,0.287,144,599,533,153,7,55,17,37,6,123,18,10,0.398,0.337,0.735,4.7,4.59
+糸井嘉男,日本ハム,0.304,134,597,510,155,9,48,22,75,11,86,0,9,0.41,0.404,0.813,6.47,6.26
+聖澤諒,楽天,0.27,138,595,523,141,4,45,54,49,7,104,12,4,0.331,0.338,0.669,4.55,4.54
+田中浩康,ヤクルト,0.274,139,593,486,133,2,40,1,54,9,60,40,15,0.323,0.354,0.677,3.89,3.86
+和田一浩,中日,0.285,144,586,508,145,9,63,2,71,1,72,0,14,0.409,0.37,0.78,5.57,5.47
+根元俊一,ロッテ,0.279,133,584,512,143,9,41,6,31,1,98,40,7,0.396,0.322,0.718,4.16,4.01
+ヘルマン,西武,0.27,144,583,507,137,3,60,41,57,4,75,10,20,0.343,0.346,0.689,4.06,4.05
+井口資仁,ロッテ,0.255,140,578,505,129,11,60,3,53,16,99,0,11,0.384,0.343,0.727,4.73,4.7
+村田修一,巨人,0.252,144,575,516,130,12,58,1,36,15,85,2,16,0.374,0.316,0.69,3.83,3.89
+梵英心,広島,0.244,137,575,499,122,10,52,14,50,1,70,19,7,0.359,0.311,0.67,3.77,3.85
+バルディリス,オリックス,0.264,143,574,503,133,10,55,1,54,14,76,0,13,0.394,0.35,0.744,4.9,4.8
+荒木雅博,中日,0.251,129,569,510,128,3,31,12,19,2,65,36,4,0.314,0.28,0.593,2.93,2.89
+内川聖一,ソフトバンク,0.3,138,567,523,157,7,53,6,31,6,36,0,12,0.392,0.342,0.734,4.81,4.73
+中島裕之,西武,0.311,136,567,499,155,13,74,7,52,9,76,1,10,0.451,0.382,0.833,6.29,6.14
+明石健志,ソフトバンク,0.254,135,567,508,129,1,27,25,33,2,98,23,2,0.299,0.301,0.601,3.21,3.21
+サブロー,ロッテ,0.239,137,560,476,114,7,52,0,78,2,105,0,12,0.342,0.346,0.689,4.29,4.32
+阿部慎之助,巨人,0.34,138,556,467,159,27,104,0,69,9,47,2,11,0.565,0.429,0.994,9,8.79
+堂林翔太,広島,0.242,144,554,488,118,14,45,5,44,14,150,5,8,0.395,0.321,0.716,4.4,4.42
+井端弘和,中日,0.284,140,553,489,139,2,35,4,52,3,58,8,14,0.331,0.356,0.687,4.18,4.06
+荒波翔,DeNA,0.268,141,550,504,135,1,25,24,23,5,90,16,2,0.333,0.305,0.639,3.59,3.52
+小谷野栄一,日本ハム,0.228,134,550,478,109,3,39,6,27,4,72,40,8,0.293,0.275,0.567,2.48,2.47
+後藤光尊,オリックス,0.242,131,546,520,126,4,43,3,14,5,74,1,8,0.306,0.266,0.572,2.6,2.69
+ミレッジ,ヤクルト,0.3,125,546,476,143,21,65,9,57,6,79,3,11,0.485,0.379,0.865,6.46,6.34
+本多雄一,ソフトバンク,0.246,123,536,480,118,0,31,34,37,1,65,14,6,0.294,0.299,0.593,3.26,3.3
+角中勝也,ロッテ,0.312,128,525,477,149,3,61,8,38,5,68,1,9,0.415,0.366,0.782,5.67,5.43
+平野恵一,阪神,0.245,134,519,458,112,1,24,6,42,4,61,15,4,0.271,0.313,0.584,3.12,3.13
+ペーニャ,ソフトバンク,0.28,130,507,461,129,21,76,2,35,8,130,0,11,0.49,0.339,0.829,5.8,5.68
+川端慎吾,ヤクルト,0.298,125,507,453,135,4,49,3,35,2,56,13,7,0.38,0.348,0.728,4.82,4.72
+田中賢介,日本ハム,0.3,114,505,457,137,3,32,13,35,2,36,8,2,0.363,0.35,0.713,4.81,4.7
+ラミレス,DeNA,0.3,137,504,476,143,19,76,0,18,7,60,0,18,0.473,0.333,0.806,5.26,5.13
+今江敏晃,ロッテ,0.253,136,501,446,113,6,47,0,19,5,37,23,15,0.354,0.287,0.641,2.98,3.08
+中村紀洋,DeNA,0.274,126,500,442,121,11,61,1,50,2,72,0,14,0.407,0.346,0.753,4.86,4.87
+中村剛也,西武,0.231,123,498,432,100,27,79,2,56,9,125,0,11,0.461,0.331,0.792,5.04,5.16
+稲葉篤紀,日本ハム,0.29,127,497,449,130,10,61,0,32,5,70,8,3,0.421,0.342,0.762,5.38,5.22
+畠山和洋,ヤクルト,0.266,121,497,455,121,13,55,2,37,2,64,1,13,0.402,0.323,0.725,4.39,4.35
+森野将彦,中日,0.249,124,494,434,108,6,50,1,49,3,68,4,13,0.348,0.327,0.674,3.87,3.89
+新井貴浩,阪神,0.25,122,493,460,115,9,52,1,30,1,85,0,12,0.363,0.296,0.659,3.43,3.4
+フェルナンデス,楽天,0.243,129,489,440,107,3,51,2,44,2,67,0,11,0.311,0.313,0.624,3.25,3.28
+銀次,楽天,0.28,126,485,432,121,4,45,8,22,4,37,23,4,0.354,0.318,0.672,3.98,3.94
+川端崇義,オリックス,0.266,125,484,429,114,2,27,6,22,9,59,24,8,0.317,0.315,0.632,3.36,3.25
+岡田幸文,ロッテ,0.262,131,482,431,113,0,18,23,18,6,47,25,4,0.288,0.3,0.587,2.82,2.85
+長谷川勇也,ソフトバンク,0.278,126,473,403,112,4,37,16,40,9,87,16,4,0.36,0.352,0.712,4.77,4.76
+マートン,阪神,0.26,121,473,453,118,5,38,2,18,1,56,0,14,0.342,0.29,0.632,3.01,2.96
+栗山巧,西武,0.289,103,467,394,114,2,33,3,52,6,62,12,4,0.353,0.378,0.731,5.28,5.18
+浅村栄斗,西武,0.245,114,459,404,99,7,37,13,34,4,63,13,9,0.376,0.307,0.683,3.79,3.84
+谷繁元信,中日,0.228,134,458,386,88,5,32,0,52,4,67,14,15,0.303,0.324,0.627,3.11,3.16
+秋山翔吾,西武,0.293,107,450,403,118,4,37,10,28,3,70,15,7,0.404,0.343,0.747,4.82,4.63
+筒香嘉智,DeNA,0.218,108,446,386,84,10,45,1,51,2,102,2,4,0.352,0.309,0.661,3.8,3.98
+高橋由伸,巨人,0.239,130,442,368,88,8,56,2,61,8,77,1,14,0.351,0.356,0.707,4.23,4.32
+里崎智也,ロッテ,0.244,120,439,385,94,9,41,0,33,4,80,14,11,0.345,0.308,0.654,3.38,3.46
+松井稼頭央,楽天,0.266,106,433,402,107,9,43,14,26,1,55,3,6,0.408,0.312,0.72,4.58,4.45
+大引啓次,オリックス,0.224,110,432,352,79,6,20,6,46,3,74,28,8,0.315,0.317,0.632,3.33,3.44
+牧田明久,楽天,0.225,123,425,378,85,9,53,5,23,9,49,10,11,0.331,0.283,0.613,2.68,2.86
+バレンティン,ヤクルト,0.272,106,422,353,96,31,81,1,64,3,92,0,14,0.572,0.386,0.958,7.34,7.35
+炭谷銀仁朗,西武,0.194,139,414,360,70,0,23,0,17,1,74,35,9,0.233,0.232,0.466,1.17,1.23
+金本知憲,阪神,0.258,126,406,356,92,6,30,3,49,0,54,0,2,0.36,0.347,0.707,4.78,4.74
+T-岡田,オリックス,0.28,103,404,378,106,10,56,4,22,4,81,0,5,0.418,0.327,0.745,4.87,4.7
+天谷宗一郎,広島,0.265,108,397,359,95,6,25,12,32,3,64,2,3,0.373,0.329,0.702,4.33,4.25
+宮本慎也,ヤクルト,0.267,110,394,356,95,3,23,1,16,5,38,16,9,0.306,0.307,0.613,3.08,3.05
+松田宣浩,ソフトバンク,0.3,95,390,360,108,9,56,16,27,1,63,0,7,0.492,0.349,0.84,5.87,5.59
+新井良太,阪神,0.28,110,370,322,90,11,32,1,38,6,73,3,7,0.438,0.365,0.803,5.77,5.65
+スケールズ,オリックス,0.262,85,362,305,80,5,23,4,47,8,103,0,3,0.384,0.373,0.757,5.6,5.54
+ブランコ,中日,0.248,96,359,311,77,24,65,2,40,5,84,0,5,0.511,0.34,0.851,6.06,6.22
+小久保裕紀,ソフトバンク,0.239,103,357,331,79,4,34,0,20,0,67,3,6,0.308,0.28,0.588,2.8,2.89
+鉄平,楽天,0.251,121,355,315,79,1,33,8,11,6,45,18,7,0.302,0.285,0.586,2.62,2.75
+大和,阪神,0.257,128,349,311,80,0,26,17,15,4,46,19,3,0.318,0.3,0.618,3.19,3.1
+今宮健太,ソフトバンク,0.238,126,343,307,73,2,14,8,10,3,75,21,2,0.29,0.267,0.557,2.44,2.51
+丸佳浩,広島,0.247,106,339,283,70,4,22,14,46,2,59,5,4,0.353,0.353,0.707,4.62,4.7
+廣瀬純,広島,0.241,102,337,294,71,6,33,0,24,11,55,7,3,0.347,0.321,0.668,4.02,4.02
+金城龍彦,DeNA,0.238,129,331,294,70,3,18,2,26,6,37,3,1,0.306,0.311,0.617,3.58,3.65
+鶴岡慎也,日本ハム,0.266,116,328,289,77,0,25,3,14,1,41,23,11,0.308,0.302,0.61,2.68,2.61
+ホフパワー,日本ハム,0.247,109,325,296,73,14,37,1,25,3,81,0,3,0.432,0.311,0.743,4.74,4.77
+嶋基宏,楽天,0.291,91,316,265,77,1,8,3,33,4,51,13,5,0.332,0.376,0.708,4.53,4.47
+金子誠,日本ハム,0.227,103,311,277,63,0,22,1,15,0,53,17,2,0.292,0.265,0.558,2.3,2.31
+石川雄洋,DeNA,0.285,80,304,263,75,1,14,7,24,3,49,13,3,0.342,0.351,0.693,4.21,4.11
+平田良介,中日,0.216,91,301,269,58,11,32,1,28,1,59,1,9,0.361,0.29,0.651,3.26,3.47
+大崎雄太朗,西武,0.269,107,298,260,70,1,22,1,17,7,27,10,4,0.346,0.326,0.673,4.06,4.04
+ガルシア,楽天,0.227,77,296,269,61,7,30,0,23,2,65,0,6,0.349,0.291,0.64,3.27,3.38
+ブラゼル,阪神,0.233,98,295,275,64,12,43,0,18,2,72,0,16,0.404,0.285,0.688,3.15,3.21
+清田育宏,ロッテ,0.281,87,292,253,71,3,29,5,34,0,46,3,5,0.391,0.363,0.755,5,4.9
+枡田慎太郎,楽天,0.295,79,290,254,75,5,32,1,19,5,56,7,2,0.433,0.35,0.783,5.55,5.51
+内村賢介,DeNA,0.237,77,289,236,56,0,18,18,31,0,40,21,4,0.263,0.325,0.587,3.19,3.22
+森岡良介,ヤクルト,0.249,100,282,245,61,1,18,1,11,5,44,19,1,0.318,0.293,0.611,3.16,3.17
+東出輝裕,広島,0.247,91,282,247,61,0,6,1,13,3,22,19,3,0.279,0.293,0.572,2.62,2.57
+藤村大介,巨人,0.252,109,279,238,60,0,10,14,15,0,35,26,2,0.298,0.296,0.595,2.84,2.79
+大松尚逸,ロッテ,0.198,89,275,258,51,5,22,0,9,2,46,3,7,0.291,0.228,0.519,1.7,1.91
+ホワイトセル,ロッテ,0.309,63,257,223,69,9,43,0,29,4,75,0,2,0.489,0.397,0.886,7.37,7.16
+谷佳知,巨人,0.258,89,255,229,59,3,22,1,14,0,48,11,5,0.319,0.299,0.618,2.97,2.97
+梶谷隆幸,DeNA,0.179,80,252,223,40,2,11,5,21,1,61,7,4,0.256,0.253,0.509,1.48,1.64
+エルドレッド,広島,0.262,65,251,225,59,11,35,0,20,3,67,0,3,0.453,0.327,0.78,5.29,5.38
+石原慶幸,広島,0.24,77,250,217,52,1,22,0,22,1,37,9,7,0.323,0.311,0.634,3.11,2.99
+中村悠平,ヤクルト,0.254,91,249,209,53,1,15,1,28,2,31,8,10,0.292,0.344,0.636,3.23,3.25
+赤松真人,広島,0.242,76,246,207,50,3,15,18,18,4,33,16,4,0.314,0.313,0.627,3.62,3.65
+相川亮二,ヤクルト,0.245,72,244,220,54,1,28,0,20,1,33,1,7,0.295,0.309,0.604,2.98,3
+岩本貴裕,広島,0.268,71,242,239,64,6,27,1,2,0,43,1,5,0.406,0.274,0.68,3.63,3.5
+菊池涼介,広島,0.229,63,234,201,46,2,12,4,6,1,42,25,5,0.294,0.254,0.547,1.99,2.04
+片岡易之,西武,0.225,52,231,204,46,2,19,8,15,1,30,7,6,0.294,0.277,0.571,2.19,2.44
+森本稀哲,DeNA,0.244,108,230,201,49,3,18,0,20,3,44,4,6,0.323,0.32,0.643,3.27,3.34
+鶴岡一成,DeNA,0.189,102,229,201,38,1,15,0,19,0,39,8,4,0.254,0.258,0.512,1.87,1.94
+松本哲也,巨人,0.258,83,229,198,51,0,11,12,17,0,34,12,4,0.313,0.313,0.626,3.5,3.5
+藤井彰人,阪神,0.248,81,228,210,52,1,10,0,9,2,39,7,3,0.286,0.285,0.571,2.59,2.54
+寺内崇幸,巨人,0.241,103,225,191,46,1,5,11,16,3,43,13,6,0.293,0.307,0.6,2.97,3.06
+上本博紀,阪神,0.254,62,224,197,50,1,7,13,19,5,46,2,3,0.345,0.333,0.679,4.26,4.18
+多村仁志,ソフトバンク,0.25,79,218,200,50,4,20,0,18,0,43,0,7,0.365,0.312,0.677,3.57,3.53
+細川亨,ソフトバンク,0.157,92,217,185,29,2,13,0,8,1,50,21,1,0.205,0.194,0.399,0.72,0.98
+福地寿樹,ヤクルト,0.255,83,216,188,48,0,19,12,16,4,29,6,3,0.335,0.324,0.659,4.17,4.1
+山崎武司,中日,0.209,90,215,191,40,1,13,1,18,4,44,0,5,0.293,0.288,0.582,2.72,2.79
+オーティズ,西武,0.286,64,214,199,57,9,21,0,14,1,38,0,8,0.462,0.336,0.799,5,4.91
+渡辺直人,DeNA,0.224,70,213,174,39,0,10,2,24,10,27,5,1,0.247,0.351,0.598,3.35,3.44
+柳田悠岐,ソフトバンク,0.246,68,212,195,48,5,18,6,10,5,56,2,2,0.385,0.3,0.685,4.14,4.05
+高須洋介,楽天,0.244,58,212,176,43,0,9,2,24,2,15,8,5,0.284,0.338,0.622,3.47,3.53
+ボウカー,巨人,0.196,69,204,184,36,3,10,2,16,2,54,2,3,0.31,0.267,0.577,2.64,2.66
+ニック,広島,0.238,52,204,181,43,9,24,0,22,1,42,0,4,0.431,0.324,0.754,4.78,4.79
+福浦和也,ロッテ,0.25,84,199,180,45,0,25,0,15,1,27,1,3,0.267,0.308,0.575,2.92,3.03
+伊藤光,オリックス,0.205,66,197,176,36,0,10,0,5,0,48,12,2,0.256,0.222,0.477,1.49,1.7
+エドガー,巨人,0.236,57,190,174,41,4,19,0,13,1,46,1,6,0.374,0.291,0.665,3.17,3.19
+藤田一也,楽天,0.308,63,188,172,53,0,15,5,4,0,10,11,4,0.378,0.322,0.7,4.05,3.84
+荻野貴司,ロッテ,0.224,61,187,165,37,1,8,13,11,5,17,6,0,0.273,0.293,0.566,3.11,3.14
+堂上直倫,中日,0.21,116,182,167,35,0,11,1,5,1,34,8,6,0.257,0.236,0.493,1.39,1.42
+江川智晃,ソフトバンク,0.244,56,175,160,39,4,18,1,8,0,37,4,3,0.388,0.275,0.662,3.5,3.7
+上田剛史,ヤクルト,0.257,50,173,148,38,0,12,8,12,0,30,13,2,0.318,0.313,0.63,3.23,3.15
+小宮山慎二,阪神,0.148,72,171,149,22,1,4,0,11,2,32,9,4,0.174,0.216,0.391,0.53,0.69
+堂上剛裕,中日,0.282,85,166,156,44,4,17,1,8,0,36,1,4,0.41,0.315,0.725,4.3,4.19
+坂口智隆,オリックス,0.228,40,165,158,36,0,8,2,5,0,13,2,0,0.253,0.252,0.505,1.96,2.01
+松中信彦,ソフトバンク,0.221,65,164,136,30,4,13,1,26,2,26,0,1,0.324,0.354,0.677,4.6,4.69
+倉義和,広島,0.195,70,160,133,26,1,15,1,14,5,20,8,6,0.263,0.296,0.559,2.23,2.28
+鈴木大地,ロッテ,0.274,62,160,135,37,0,11,0,13,1,23,10,1,0.326,0.34,0.666,4.07,4
+大野奨太,日本ハム,0.171,70,159,140,24,2,11,0,8,2,30,9,2,0.243,0.227,0.47,1.2,1.35
+北川博敏,オリックス,0.221,59,159,140,31,1,11,0,17,0,23,0,3,0.286,0.302,0.588,2.95,3.12
+柴田講平,阪神,0.234,73,157,128,30,0,2,4,16,1,23,11,1,0.273,0.322,0.595,3.03,3.12
+小池正晃,DeNA,0.192,88,155,130,25,3,19,0,10,8,31,6,5,0.292,0.289,0.581,2.43,2.63
+西川遥輝,日本ハム,0.239,71,155,134,32,2,13,7,14,0,34,7,0,0.343,0.311,0.654,4.18,4.11
+雄平,ヤクルト,0.28,47,153,143,40,0,8,2,7,0,19,3,0,0.308,0.313,0.621,3.4,3.31
+スレッジ,日本ハム,0.232,47,152,138,32,5,23,0,12,2,47,0,2,0.406,0.303,0.708,4.22,4.22
+下園辰哉,DeNA,0.252,90,150,139,35,0,14,0,10,0,21,1,6,0.324,0.302,0.626,2.93,2.77
+松井淳,ヤクルト,0.287,46,150,143,41,5,15,1,6,1,26,0,5,0.462,0.32,0.782,4.79,4.6
+野中信吾,オリックス,0.217,74,149,120,26,0,7,12,11,2,20,16,2,0.275,0.293,0.568,2.4,2.44
+古城茂幸,巨人,0.209,65,149,129,27,0,8,2,12,0,28,6,5,0.256,0.273,0.529,1.96,2.07
diff --git a/examples/cpp/recommender/npb_similar_player/dialog.cpp b/examples/cpp/recommender/npb_similar_player/dialog.cpp
new file mode 100644 (file)
index 0000000..8c4fcf4
--- /dev/null
@@ -0,0 +1,75 @@
+#include "dialog.h"
+#include "ui_dialog.h"
+
+#include <QtCore>
+#include <QtJubatus>
+
+class Dialog::Private
+{
+public:
+    Private(Dialog *parent);
+
+private:
+    void playerChanged(const QString &id);
+
+private:
+    Dialog *q;
+    Ui::Dialog ui;
+    QJubatusRecommender recommender;
+};
+
+Dialog::Private::Private(Dialog *parent)
+    : q(parent)
+{
+    ui.setupUi(q);
+
+    recommender.setName("npb-similar-player");
+    recommender.setTimeout(10);
+
+    bool learn = recommender.getAllRows().isEmpty();
+
+    QFile file(":/baseball.csv");
+    file.open(QFile::ReadOnly | QFile::Text);
+    QTextStream stream(&file);
+    QStringList fields = QStringList() << "チーム" << "打率" << "試合数" << "打席" << "打数" << "安打" << "本塁打" << "打点" << "盗塁" << "四球" << "死球" << "三振" << "犠打" << "併殺打" << "長打率" << "出塁率" << "OPS" << "RC27" << "XR27";
+
+    while (!stream.atEnd()) {
+        QStringList list = stream.readLine().split(",");
+        QString id = list.takeFirst();
+        if (learn) {
+            QVariantMap data;
+            data.insert(fields.at(0), list.at(0));
+            for(int i = 1; i < fields.length(); i++) {
+                data.insert(fields.at(i), list.at(i).toFloat());
+            }
+            recommender.updateRow(id, data);
+        }
+        ui.player->addItem(id);
+    }
+    file.close();
+
+    connect(ui.buttonBox, &QDialogButtonBox::rejected, q, &QDialog::close);
+
+    connect(ui.player, &QComboBox::currentTextChanged, [this](const QString &id) {
+        playerChanged(id);
+    });
+    playerChanged(ui.player->currentText());
+}
+
+void Dialog::Private::playerChanged(const QString &id)
+{
+    QStringList results;
+    QList<QJubatusRecommender::IdWithScore> list = recommender.similarRowFromId(id, 10);
+    foreach (const QJubatusRecommender::IdWithScore idWithScore, list) {
+        if (idWithScore.id == id) continue;
+        results.append(QString("%1 (%2)").arg(idWithScore.id).arg(idWithScore.score));
+    }
+    ui.result->setPlainText(results.join(QStringLiteral("\n")));
+}
+
+Dialog::Dialog(QWidget *parent)
+    : QDialog(parent)
+    , d(new Private(this))
+{
+    connect(this, &QObject::destroyed, [this]() { delete d; });
+}
diff --git a/examples/cpp/recommender/npb_similar_player/dialog.h b/examples/cpp/recommender/npb_similar_player/dialog.h
new file mode 100644 (file)
index 0000000..2bb6c7c
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef DIALOG_H
+#define DIALOG_H
+
+#include <QDialog>
+
+class Dialog : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit Dialog(QWidget *parent = 0);
+
+private:
+    class Private;
+    Private *d;
+};
+
+#endif // DIALOG_H
diff --git a/examples/cpp/recommender/npb_similar_player/dialog.ui b/examples/cpp/recommender/npb_similar_player/dialog.ui
new file mode 100644 (file)
index 0000000..3144ce5
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Dialog</class>
+ <widget class="QDialog" name="Dialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>npb_similar_player</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QComboBox" name="player"/>
+   </item>
+   <item>
+    <widget class="QTextBrowser" name="result"/>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/examples/cpp/recommender/npb_similar_player/main.cpp b/examples/cpp/recommender/npb_similar_player/main.cpp
new file mode 100644 (file)
index 0000000..27efccc
--- /dev/null
@@ -0,0 +1,13 @@
+#include <QtWidgets>
+
+#include "dialog.h"
+
+int main(int argc, char **argv)
+{
+    QApplication app(argc, argv);
+
+    Dialog dialog;
+    dialog.show();
+
+    return app.exec();
+}
diff --git a/examples/cpp/recommender/npb_similar_player/npb_similar_player.json b/examples/cpp/recommender/npb_similar_player/npb_similar_player.json
new file mode 100644 (file)
index 0000000..f95a474
--- /dev/null
@@ -0,0 +1,16 @@
+{
+  "method": "inverted_index",
+  "converter": {
+    "string_filter_types": {},
+    "string_filter_rules": [],
+    "num_filter_types": {},
+    "num_filter_rules": [],
+    "string_types": {},
+    "string_rules": [],
+    "num_types": {},
+    "num_rules": [
+      {"key" : "*", "type" : "num"}
+    ]
+  },
+  "parameter": {}
+}
diff --git a/examples/cpp/recommender/npb_similar_player/npb_similar_player.pro b/examples/cpp/recommender/npb_similar_player/npb_similar_player.pro
new file mode 100644 (file)
index 0000000..569aa12
--- /dev/null
@@ -0,0 +1,17 @@
+TEMPLATE = app
+TARGET = jubatus-npb-similar-player
+QT = core network gui widgets jubatus
+CONFIG += c++11
+SOURCES = main.cpp \
+    dialog.cpp
+
+OTHER_FILES += npb_similar_player
+
+RESOURCES += \
+    npb_similar_player.qrc
+
+FORMS += \
+    dialog.ui
+
+HEADERS += \
+    dialog.h
diff --git a/examples/cpp/recommender/npb_similar_player/npb_similar_player.qrc b/examples/cpp/recommender/npb_similar_player/npb_similar_player.qrc
new file mode 100644 (file)
index 0000000..b70726f
--- /dev/null
@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/">
+        <file>baseball.csv</file>
+    </qresource>
+</RCC>
diff --git a/examples/cpp/recommender/recommender.pro b/examples/cpp/recommender/recommender.pro
new file mode 100644 (file)
index 0000000..c82aed6
--- /dev/null
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS += npb_similar_player
index d2a4c21..a2832bd 100644 (file)
@@ -1,3 +1,3 @@
 TEMPLATE = subdirs
-SUBDIRS += classifier
+SUBDIRS += classifier recommender
 
diff --git a/examples/qml/recommender/npb_similar_player/baseball.csv b/examples/qml/recommender/npb_similar_player/baseball.csv
new file mode 100644 (file)
index 0000000..c9c6fd6
--- /dev/null
@@ -0,0 +1,144 @@
+長野久義,巨人,0.301,144,653,574,173,14,60,20,75,1,100,2,5,0.432,0.382,0.815,6.45,6.2
+大島洋平,中日,0.31,144,631,555,172,1,13,32,46,13,80,17,7,0.368,0.376,0.744,5.13,4.91
+鳥谷敬,阪神,0.262,144,624,515,135,8,59,15,94,2,91,5,12,0.375,0.373,0.748,5.28,5.33
+坂本勇人,巨人,0.311,144,619,557,173,14,69,16,39,6,90,12,5,0.456,0.359,0.815,6.29,6.06
+中田翔,日本ハム,0.239,144,606,547,131,24,77,5,50,5,101,0,8,0.42,0.307,0.727,4.44,4.54
+李大浩,オリックス,0.286,144,601,525,150,24,91,0,64,7,85,0,18,0.478,0.368,0.846,5.99,5.95
+陽岱鋼,日本ハム,0.287,144,599,533,153,7,55,17,37,6,123,18,10,0.398,0.337,0.735,4.7,4.59
+糸井嘉男,日本ハム,0.304,134,597,510,155,9,48,22,75,11,86,0,9,0.41,0.404,0.813,6.47,6.26
+聖澤諒,楽天,0.27,138,595,523,141,4,45,54,49,7,104,12,4,0.331,0.338,0.669,4.55,4.54
+田中浩康,ヤクルト,0.274,139,593,486,133,2,40,1,54,9,60,40,15,0.323,0.354,0.677,3.89,3.86
+和田一浩,中日,0.285,144,586,508,145,9,63,2,71,1,72,0,14,0.409,0.37,0.78,5.57,5.47
+根元俊一,ロッテ,0.279,133,584,512,143,9,41,6,31,1,98,40,7,0.396,0.322,0.718,4.16,4.01
+ヘルマン,西武,0.27,144,583,507,137,3,60,41,57,4,75,10,20,0.343,0.346,0.689,4.06,4.05
+井口資仁,ロッテ,0.255,140,578,505,129,11,60,3,53,16,99,0,11,0.384,0.343,0.727,4.73,4.7
+村田修一,巨人,0.252,144,575,516,130,12,58,1,36,15,85,2,16,0.374,0.316,0.69,3.83,3.89
+梵英心,広島,0.244,137,575,499,122,10,52,14,50,1,70,19,7,0.359,0.311,0.67,3.77,3.85
+バルディリス,オリックス,0.264,143,574,503,133,10,55,1,54,14,76,0,13,0.394,0.35,0.744,4.9,4.8
+荒木雅博,中日,0.251,129,569,510,128,3,31,12,19,2,65,36,4,0.314,0.28,0.593,2.93,2.89
+内川聖一,ソフトバンク,0.3,138,567,523,157,7,53,6,31,6,36,0,12,0.392,0.342,0.734,4.81,4.73
+中島裕之,西武,0.311,136,567,499,155,13,74,7,52,9,76,1,10,0.451,0.382,0.833,6.29,6.14
+明石健志,ソフトバンク,0.254,135,567,508,129,1,27,25,33,2,98,23,2,0.299,0.301,0.601,3.21,3.21
+サブロー,ロッテ,0.239,137,560,476,114,7,52,0,78,2,105,0,12,0.342,0.346,0.689,4.29,4.32
+阿部慎之助,巨人,0.34,138,556,467,159,27,104,0,69,9,47,2,11,0.565,0.429,0.994,9,8.79
+堂林翔太,広島,0.242,144,554,488,118,14,45,5,44,14,150,5,8,0.395,0.321,0.716,4.4,4.42
+井端弘和,中日,0.284,140,553,489,139,2,35,4,52,3,58,8,14,0.331,0.356,0.687,4.18,4.06
+荒波翔,DeNA,0.268,141,550,504,135,1,25,24,23,5,90,16,2,0.333,0.305,0.639,3.59,3.52
+小谷野栄一,日本ハム,0.228,134,550,478,109,3,39,6,27,4,72,40,8,0.293,0.275,0.567,2.48,2.47
+後藤光尊,オリックス,0.242,131,546,520,126,4,43,3,14,5,74,1,8,0.306,0.266,0.572,2.6,2.69
+ミレッジ,ヤクルト,0.3,125,546,476,143,21,65,9,57,6,79,3,11,0.485,0.379,0.865,6.46,6.34
+本多雄一,ソフトバンク,0.246,123,536,480,118,0,31,34,37,1,65,14,6,0.294,0.299,0.593,3.26,3.3
+角中勝也,ロッテ,0.312,128,525,477,149,3,61,8,38,5,68,1,9,0.415,0.366,0.782,5.67,5.43
+平野恵一,阪神,0.245,134,519,458,112,1,24,6,42,4,61,15,4,0.271,0.313,0.584,3.12,3.13
+ペーニャ,ソフトバンク,0.28,130,507,461,129,21,76,2,35,8,130,0,11,0.49,0.339,0.829,5.8,5.68
+川端慎吾,ヤクルト,0.298,125,507,453,135,4,49,3,35,2,56,13,7,0.38,0.348,0.728,4.82,4.72
+田中賢介,日本ハム,0.3,114,505,457,137,3,32,13,35,2,36,8,2,0.363,0.35,0.713,4.81,4.7
+ラミレス,DeNA,0.3,137,504,476,143,19,76,0,18,7,60,0,18,0.473,0.333,0.806,5.26,5.13
+今江敏晃,ロッテ,0.253,136,501,446,113,6,47,0,19,5,37,23,15,0.354,0.287,0.641,2.98,3.08
+中村紀洋,DeNA,0.274,126,500,442,121,11,61,1,50,2,72,0,14,0.407,0.346,0.753,4.86,4.87
+中村剛也,西武,0.231,123,498,432,100,27,79,2,56,9,125,0,11,0.461,0.331,0.792,5.04,5.16
+稲葉篤紀,日本ハム,0.29,127,497,449,130,10,61,0,32,5,70,8,3,0.421,0.342,0.762,5.38,5.22
+畠山和洋,ヤクルト,0.266,121,497,455,121,13,55,2,37,2,64,1,13,0.402,0.323,0.725,4.39,4.35
+森野将彦,中日,0.249,124,494,434,108,6,50,1,49,3,68,4,13,0.348,0.327,0.674,3.87,3.89
+新井貴浩,阪神,0.25,122,493,460,115,9,52,1,30,1,85,0,12,0.363,0.296,0.659,3.43,3.4
+フェルナンデス,楽天,0.243,129,489,440,107,3,51,2,44,2,67,0,11,0.311,0.313,0.624,3.25,3.28
+銀次,楽天,0.28,126,485,432,121,4,45,8,22,4,37,23,4,0.354,0.318,0.672,3.98,3.94
+川端崇義,オリックス,0.266,125,484,429,114,2,27,6,22,9,59,24,8,0.317,0.315,0.632,3.36,3.25
+岡田幸文,ロッテ,0.262,131,482,431,113,0,18,23,18,6,47,25,4,0.288,0.3,0.587,2.82,2.85
+長谷川勇也,ソフトバンク,0.278,126,473,403,112,4,37,16,40,9,87,16,4,0.36,0.352,0.712,4.77,4.76
+マートン,阪神,0.26,121,473,453,118,5,38,2,18,1,56,0,14,0.342,0.29,0.632,3.01,2.96
+栗山巧,西武,0.289,103,467,394,114,2,33,3,52,6,62,12,4,0.353,0.378,0.731,5.28,5.18
+浅村栄斗,西武,0.245,114,459,404,99,7,37,13,34,4,63,13,9,0.376,0.307,0.683,3.79,3.84
+谷繁元信,中日,0.228,134,458,386,88,5,32,0,52,4,67,14,15,0.303,0.324,0.627,3.11,3.16
+秋山翔吾,西武,0.293,107,450,403,118,4,37,10,28,3,70,15,7,0.404,0.343,0.747,4.82,4.63
+筒香嘉智,DeNA,0.218,108,446,386,84,10,45,1,51,2,102,2,4,0.352,0.309,0.661,3.8,3.98
+高橋由伸,巨人,0.239,130,442,368,88,8,56,2,61,8,77,1,14,0.351,0.356,0.707,4.23,4.32
+里崎智也,ロッテ,0.244,120,439,385,94,9,41,0,33,4,80,14,11,0.345,0.308,0.654,3.38,3.46
+松井稼頭央,楽天,0.266,106,433,402,107,9,43,14,26,1,55,3,6,0.408,0.312,0.72,4.58,4.45
+大引啓次,オリックス,0.224,110,432,352,79,6,20,6,46,3,74,28,8,0.315,0.317,0.632,3.33,3.44
+牧田明久,楽天,0.225,123,425,378,85,9,53,5,23,9,49,10,11,0.331,0.283,0.613,2.68,2.86
+バレンティン,ヤクルト,0.272,106,422,353,96,31,81,1,64,3,92,0,14,0.572,0.386,0.958,7.34,7.35
+炭谷銀仁朗,西武,0.194,139,414,360,70,0,23,0,17,1,74,35,9,0.233,0.232,0.466,1.17,1.23
+金本知憲,阪神,0.258,126,406,356,92,6,30,3,49,0,54,0,2,0.36,0.347,0.707,4.78,4.74
+T-岡田,オリックス,0.28,103,404,378,106,10,56,4,22,4,81,0,5,0.418,0.327,0.745,4.87,4.7
+天谷宗一郎,広島,0.265,108,397,359,95,6,25,12,32,3,64,2,3,0.373,0.329,0.702,4.33,4.25
+宮本慎也,ヤクルト,0.267,110,394,356,95,3,23,1,16,5,38,16,9,0.306,0.307,0.613,3.08,3.05
+松田宣浩,ソフトバンク,0.3,95,390,360,108,9,56,16,27,1,63,0,7,0.492,0.349,0.84,5.87,5.59
+新井良太,阪神,0.28,110,370,322,90,11,32,1,38,6,73,3,7,0.438,0.365,0.803,5.77,5.65
+スケールズ,オリックス,0.262,85,362,305,80,5,23,4,47,8,103,0,3,0.384,0.373,0.757,5.6,5.54
+ブランコ,中日,0.248,96,359,311,77,24,65,2,40,5,84,0,5,0.511,0.34,0.851,6.06,6.22
+小久保裕紀,ソフトバンク,0.239,103,357,331,79,4,34,0,20,0,67,3,6,0.308,0.28,0.588,2.8,2.89
+鉄平,楽天,0.251,121,355,315,79,1,33,8,11,6,45,18,7,0.302,0.285,0.586,2.62,2.75
+大和,阪神,0.257,128,349,311,80,0,26,17,15,4,46,19,3,0.318,0.3,0.618,3.19,3.1
+今宮健太,ソフトバンク,0.238,126,343,307,73,2,14,8,10,3,75,21,2,0.29,0.267,0.557,2.44,2.51
+丸佳浩,広島,0.247,106,339,283,70,4,22,14,46,2,59,5,4,0.353,0.353,0.707,4.62,4.7
+廣瀬純,広島,0.241,102,337,294,71,6,33,0,24,11,55,7,3,0.347,0.321,0.668,4.02,4.02
+金城龍彦,DeNA,0.238,129,331,294,70,3,18,2,26,6,37,3,1,0.306,0.311,0.617,3.58,3.65
+鶴岡慎也,日本ハム,0.266,116,328,289,77,0,25,3,14,1,41,23,11,0.308,0.302,0.61,2.68,2.61
+ホフパワー,日本ハム,0.247,109,325,296,73,14,37,1,25,3,81,0,3,0.432,0.311,0.743,4.74,4.77
+嶋基宏,楽天,0.291,91,316,265,77,1,8,3,33,4,51,13,5,0.332,0.376,0.708,4.53,4.47
+金子誠,日本ハム,0.227,103,311,277,63,0,22,1,15,0,53,17,2,0.292,0.265,0.558,2.3,2.31
+石川雄洋,DeNA,0.285,80,304,263,75,1,14,7,24,3,49,13,3,0.342,0.351,0.693,4.21,4.11
+平田良介,中日,0.216,91,301,269,58,11,32,1,28,1,59,1,9,0.361,0.29,0.651,3.26,3.47
+大崎雄太朗,西武,0.269,107,298,260,70,1,22,1,17,7,27,10,4,0.346,0.326,0.673,4.06,4.04
+ガルシア,楽天,0.227,77,296,269,61,7,30,0,23,2,65,0,6,0.349,0.291,0.64,3.27,3.38
+ブラゼル,阪神,0.233,98,295,275,64,12,43,0,18,2,72,0,16,0.404,0.285,0.688,3.15,3.21
+清田育宏,ロッテ,0.281,87,292,253,71,3,29,5,34,0,46,3,5,0.391,0.363,0.755,5,4.9
+枡田慎太郎,楽天,0.295,79,290,254,75,5,32,1,19,5,56,7,2,0.433,0.35,0.783,5.55,5.51
+内村賢介,DeNA,0.237,77,289,236,56,0,18,18,31,0,40,21,4,0.263,0.325,0.587,3.19,3.22
+森岡良介,ヤクルト,0.249,100,282,245,61,1,18,1,11,5,44,19,1,0.318,0.293,0.611,3.16,3.17
+東出輝裕,広島,0.247,91,282,247,61,0,6,1,13,3,22,19,3,0.279,0.293,0.572,2.62,2.57
+藤村大介,巨人,0.252,109,279,238,60,0,10,14,15,0,35,26,2,0.298,0.296,0.595,2.84,2.79
+大松尚逸,ロッテ,0.198,89,275,258,51,5,22,0,9,2,46,3,7,0.291,0.228,0.519,1.7,1.91
+ホワイトセル,ロッテ,0.309,63,257,223,69,9,43,0,29,4,75,0,2,0.489,0.397,0.886,7.37,7.16
+谷佳知,巨人,0.258,89,255,229,59,3,22,1,14,0,48,11,5,0.319,0.299,0.618,2.97,2.97
+梶谷隆幸,DeNA,0.179,80,252,223,40,2,11,5,21,1,61,7,4,0.256,0.253,0.509,1.48,1.64
+エルドレッド,広島,0.262,65,251,225,59,11,35,0,20,3,67,0,3,0.453,0.327,0.78,5.29,5.38
+石原慶幸,広島,0.24,77,250,217,52,1,22,0,22,1,37,9,7,0.323,0.311,0.634,3.11,2.99
+中村悠平,ヤクルト,0.254,91,249,209,53,1,15,1,28,2,31,8,10,0.292,0.344,0.636,3.23,3.25
+赤松真人,広島,0.242,76,246,207,50,3,15,18,18,4,33,16,4,0.314,0.313,0.627,3.62,3.65
+相川亮二,ヤクルト,0.245,72,244,220,54,1,28,0,20,1,33,1,7,0.295,0.309,0.604,2.98,3
+岩本貴裕,広島,0.268,71,242,239,64,6,27,1,2,0,43,1,5,0.406,0.274,0.68,3.63,3.5
+菊池涼介,広島,0.229,63,234,201,46,2,12,4,6,1,42,25,5,0.294,0.254,0.547,1.99,2.04
+片岡易之,西武,0.225,52,231,204,46,2,19,8,15,1,30,7,6,0.294,0.277,0.571,2.19,2.44
+森本稀哲,DeNA,0.244,108,230,201,49,3,18,0,20,3,44,4,6,0.323,0.32,0.643,3.27,3.34
+鶴岡一成,DeNA,0.189,102,229,201,38,1,15,0,19,0,39,8,4,0.254,0.258,0.512,1.87,1.94
+松本哲也,巨人,0.258,83,229,198,51,0,11,12,17,0,34,12,4,0.313,0.313,0.626,3.5,3.5
+藤井彰人,阪神,0.248,81,228,210,52,1,10,0,9,2,39,7,3,0.286,0.285,0.571,2.59,2.54
+寺内崇幸,巨人,0.241,103,225,191,46,1,5,11,16,3,43,13,6,0.293,0.307,0.6,2.97,3.06
+上本博紀,阪神,0.254,62,224,197,50,1,7,13,19,5,46,2,3,0.345,0.333,0.679,4.26,4.18
+多村仁志,ソフトバンク,0.25,79,218,200,50,4,20,0,18,0,43,0,7,0.365,0.312,0.677,3.57,3.53
+細川亨,ソフトバンク,0.157,92,217,185,29,2,13,0,8,1,50,21,1,0.205,0.194,0.399,0.72,0.98
+福地寿樹,ヤクルト,0.255,83,216,188,48,0,19,12,16,4,29,6,3,0.335,0.324,0.659,4.17,4.1
+山崎武司,中日,0.209,90,215,191,40,1,13,1,18,4,44,0,5,0.293,0.288,0.582,2.72,2.79
+オーティズ,西武,0.286,64,214,199,57,9,21,0,14,1,38,0,8,0.462,0.336,0.799,5,4.91
+渡辺直人,DeNA,0.224,70,213,174,39,0,10,2,24,10,27,5,1,0.247,0.351,0.598,3.35,3.44
+柳田悠岐,ソフトバンク,0.246,68,212,195,48,5,18,6,10,5,56,2,2,0.385,0.3,0.685,4.14,4.05
+高須洋介,楽天,0.244,58,212,176,43,0,9,2,24,2,15,8,5,0.284,0.338,0.622,3.47,3.53
+ボウカー,巨人,0.196,69,204,184,36,3,10,2,16,2,54,2,3,0.31,0.267,0.577,2.64,2.66
+ニック,広島,0.238,52,204,181,43,9,24,0,22,1,42,0,4,0.431,0.324,0.754,4.78,4.79
+福浦和也,ロッテ,0.25,84,199,180,45,0,25,0,15,1,27,1,3,0.267,0.308,0.575,2.92,3.03
+伊藤光,オリックス,0.205,66,197,176,36,0,10,0,5,0,48,12,2,0.256,0.222,0.477,1.49,1.7
+エドガー,巨人,0.236,57,190,174,41,4,19,0,13,1,46,1,6,0.374,0.291,0.665,3.17,3.19
+藤田一也,楽天,0.308,63,188,172,53,0,15,5,4,0,10,11,4,0.378,0.322,0.7,4.05,3.84
+荻野貴司,ロッテ,0.224,61,187,165,37,1,8,13,11,5,17,6,0,0.273,0.293,0.566,3.11,3.14
+堂上直倫,中日,0.21,116,182,167,35,0,11,1,5,1,34,8,6,0.257,0.236,0.493,1.39,1.42
+江川智晃,ソフトバンク,0.244,56,175,160,39,4,18,1,8,0,37,4,3,0.388,0.275,0.662,3.5,3.7
+上田剛史,ヤクルト,0.257,50,173,148,38,0,12,8,12,0,30,13,2,0.318,0.313,0.63,3.23,3.15
+小宮山慎二,阪神,0.148,72,171,149,22,1,4,0,11,2,32,9,4,0.174,0.216,0.391,0.53,0.69
+堂上剛裕,中日,0.282,85,166,156,44,4,17,1,8,0,36,1,4,0.41,0.315,0.725,4.3,4.19
+坂口智隆,オリックス,0.228,40,165,158,36,0,8,2,5,0,13,2,0,0.253,0.252,0.505,1.96,2.01
+松中信彦,ソフトバンク,0.221,65,164,136,30,4,13,1,26,2,26,0,1,0.324,0.354,0.677,4.6,4.69
+倉義和,広島,0.195,70,160,133,26,1,15,1,14,5,20,8,6,0.263,0.296,0.559,2.23,2.28
+鈴木大地,ロッテ,0.274,62,160,135,37,0,11,0,13,1,23,10,1,0.326,0.34,0.666,4.07,4
+大野奨太,日本ハム,0.171,70,159,140,24,2,11,0,8,2,30,9,2,0.243,0.227,0.47,1.2,1.35
+北川博敏,オリックス,0.221,59,159,140,31,1,11,0,17,0,23,0,3,0.286,0.302,0.588,2.95,3.12
+柴田講平,阪神,0.234,73,157,128,30,0,2,4,16,1,23,11,1,0.273,0.322,0.595,3.03,3.12
+小池正晃,DeNA,0.192,88,155,130,25,3,19,0,10,8,31,6,5,0.292,0.289,0.581,2.43,2.63
+西川遥輝,日本ハム,0.239,71,155,134,32,2,13,7,14,0,34,7,0,0.343,0.311,0.654,4.18,4.11
+雄平,ヤクルト,0.28,47,153,143,40,0,8,2,7,0,19,3,0,0.308,0.313,0.621,3.4,3.31
+スレッジ,日本ハム,0.232,47,152,138,32,5,23,0,12,2,47,0,2,0.406,0.303,0.708,4.22,4.22
+下園辰哉,DeNA,0.252,90,150,139,35,0,14,0,10,0,21,1,6,0.324,0.302,0.626,2.93,2.77
+松井淳,ヤクルト,0.287,46,150,143,41,5,15,1,6,1,26,0,5,0.462,0.32,0.782,4.79,4.6
+野中信吾,オリックス,0.217,74,149,120,26,0,7,12,11,2,20,16,2,0.275,0.293,0.568,2.4,2.44
+古城茂幸,巨人,0.209,65,149,129,27,0,8,2,12,0,28,6,5,0.256,0.273,0.529,1.96,2.07
diff --git a/examples/qml/recommender/npb_similar_player/main.cpp b/examples/qml/recommender/npb_similar_player/main.cpp
new file mode 100644 (file)
index 0000000..1b05961
--- /dev/null
@@ -0,0 +1,61 @@
+/* Copyright (c) 2012 Silk Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the Silk nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL SILK BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <QtGui/QGuiApplication>
+#include <QtQml/QQmlEngine>
+#include <QtQml/QQmlComponent>
+#include <QtQuick/QQuickItem>
+#include <QtQuick/QQuickView>
+
+int main(int argc, char *argv[])
+{
+    QGuiApplication app(argc, argv);
+
+    QUrl url = QUrl(QStringLiteral("qrc:/npb_similar_player.qml"));
+
+    QQmlEngine engine;
+    QObject::connect(&engine, SIGNAL(quit()), &app, SLOT(quit()));
+    QQmlComponent *component = new QQmlComponent(&engine);
+    component->loadUrl(url);
+    QObject *object = component->create();
+    QQuickWindow *window = qobject_cast<QQuickWindow *>(object);
+    QQuickView *view = 0;
+    if (!window) {
+        QQuickItem *item = qobject_cast<QQuickItem *>(object);
+        if (item) {
+            view = new QQuickView(&engine, NULL);
+            window = view;
+            view->setResizeMode(QQuickView::SizeRootObjectToView);
+            view->setContent(url, component, item);
+        }
+    }
+
+    if (window) {
+        window->show();
+    }
+
+    return app.exec();
+}
diff --git a/examples/qml/recommender/npb_similar_player/npb_similar_player.json b/examples/qml/recommender/npb_similar_player/npb_similar_player.json
new file mode 100644 (file)
index 0000000..7beb1f7
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "method": "AROW",
+  "converter": {
+    "num_filter_types": {},
+    "num_filter_rules": [],
+    "string_filter_types": {},
+    "string_filter_rules": [],
+    "num_types": {},
+    "num_rules": [
+      { "key": "*", "type": "num"}
+    ],
+    "string_types": {
+    },
+    "string_rules": [
+      { "key": "*", "type": "str", "sample_weight": "bin", "global_weight": "bin" }
+    ]
+  },
+  "parameter": {
+    "regularization_weight" : 1.0
+  }
+}
diff --git a/examples/qml/recommender/npb_similar_player/npb_similar_player.pro b/examples/qml/recommender/npb_similar_player/npb_similar_player.pro
new file mode 100644 (file)
index 0000000..3bf3fec
--- /dev/null
@@ -0,0 +1,10 @@
+TEMPLATE = app
+TARGET = jubatus-npb-similar-player-quick
+QT = quick jubatus
+
+SOURCES = main.cpp
+
+RESOURCES += \
+    npb_similar_player.qrc
+
+OTHER_FILES += npb_similar_player.qml npb_similar_player.json
diff --git a/examples/qml/recommender/npb_similar_player/npb_similar_player.qml b/examples/qml/recommender/npb_similar_player/npb_similar_player.qml
new file mode 100644 (file)
index 0000000..0896a1b
--- /dev/null
@@ -0,0 +1,118 @@
+/* Copyright (c) 2012 Silk Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the Silk nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL SILK BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import QtQuick 2.0
+import QtJubatus 0.1 as Jubatus
+
+Rectangle {
+    id: root
+    width: 600
+    height: 400
+
+    Jubatus.Recommender {
+        id: recommender
+        name: 'npb-similar-player'
+        timeout: 10
+        property bool hasError: false
+        onError: {
+            console.debug(message)
+            hasError = true
+        }
+    }
+
+    ListView {
+        id: playerView
+        anchors.left: parent.left
+        anchors.top: parent.top
+        anchors.bottom: parent.bottom
+        width: parent.width / 2
+        model: ListModel {
+            id: playerModel
+        }
+
+        delegate: Text {
+            text: model.id
+            MouseArea {
+                anchors.fill: parent
+                onClicked: {
+                    similarPlayerModel.clear()
+                    if (!recommender.hasError) {
+                        var results = recommender.similarRowFromId(model.id, 10)
+                        results.shift()
+                        for (var i in results) {
+                            similarPlayerModel.append(results[i])
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    ListView {
+        id: similar_players
+        anchors.left: playerView.right
+        anchors.top: parent.top
+        anchors.bottom: parent.bottom
+        anchors.right: parent.right
+        model: ListModel {
+            id: similarPlayerModel
+        }
+        delegate: Text {
+            text: '%1(%2)'.arg(model.id).arg(model.score)
+        }
+    }
+
+    Component.onCompleted: {
+        var request = new XMLHttpRequest
+        request.onreadystatechange = function() {
+            switch (request.readyState) {
+            case XMLHttpRequest.DONE:
+                var learn = recommender.getAllRows().length === 0
+                var lines = request.responseText.split('\n')
+                var fields = ["打率", "試合数", "打席", "打数", "安打", "本塁打", "打点", "盗塁", "四球", "死球", "三振", "犠打", "併殺打", "長打率", "出塁率", "OPS", "RC27", "XR27"]
+                for (var i in lines) {
+                    if (lines[i] === '') continue
+                    var columns = lines[i].split(',')
+                    var id = columns.shift()
+                    playerModel.append({'id': id})
+                    if (learn && !recommender.hasError) {
+                        var data = new Object
+                        data["チーム"] = columns.shift()
+                        for (var j = 0; j < fields.length; j++) {
+                            data[fields[j]] = parseFloat(columns[j])
+                        }
+                        recommender.updateRow(id, data)
+                    }
+                }
+
+                break
+            }
+        }
+
+        request.open("GET", "qrc:/baseball.csv");
+        request.send();
+    }
+}
diff --git a/examples/qml/recommender/npb_similar_player/npb_similar_player.qrc b/examples/qml/recommender/npb_similar_player/npb_similar_player.qrc
new file mode 100644 (file)
index 0000000..96be0fe
--- /dev/null
@@ -0,0 +1,6 @@
+<RCC>
+    <qresource prefix="/">
+        <file>baseball.csv</file>
+        <file>npb_similar_player.qml</file>
+    </qresource>
+</RCC>
diff --git a/examples/qml/recommender/recommender.pro b/examples/qml/recommender/recommender.pro
new file mode 100644 (file)
index 0000000..b1b73d9
--- /dev/null
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+SUBDIRS += npb_similar_player
+
index 63a6899..4142ce5 100644 (file)
@@ -5,8 +5,14 @@ LIBS += -L$$QT.jubatus.libs
 
 CONFIG += exceptions
 
-SOURCES += main.cpp
+SOURCES += main.cpp \
+    qmljubatusclassifier.cpp \
+    qmljubatusrecommender.cpp
 
 load(qml_plugin)
 
 OTHER_FILES = plugins.qmltypes qmldir
+
+HEADERS += \
+    qmljubatusclassifier.h \
+    qmljubatusrecommender.h
index 0316fb6..688df7c 100644 (file)
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <QtCore/QDebug>
 #include <QtQml/QQmlExtensionPlugin>
 #include <QtQml/qqml.h>
 
-#include <QtJubatus/QJubatusClassifier>
-
-#include <string>
-#include <vector>
-
-#include <jubatus/client.hpp>
-
-class QmlJubatusClassifier : public QJubatusClassifier
-{
-    Q_OBJECT
-public:
-    QmlJubatusClassifier(QObject *parent = 0) : QJubatusClassifier(parent) {}
-
-    using QJubatusClassifier::train;
-    Q_INVOKABLE void train(const QVariantList &data);
-    Q_INVOKABLE void train(const QString &label, const QVariantMap &data);
-    using QJubatusClassifier::classify;
-    Q_INVOKABLE QVariantList classify(const QVariantList &data);
-    Q_INVOKABLE QVariantList classify(const QVariantMap &data);
-};
-
-void QmlJubatusClassifier::train(const QVariantList &data)
-{
-    std::vector<jubatus::classifier::labeled_datum> train_data;
-    foreach (const QVariant &v, data) {
-        switch (v.type()) {
-        case QVariant::List: {
-            QVariantList list = v.toList();
-            if (!list.isEmpty()) {
-                QString name = list.takeFirst().toString();
-                foreach (const QVariant &v2, list) {
-                    train_data.push_back(jubatus::classifier::labeled_datum(name.toStdString(), convert(v2.toMap())));
-                }
-            }
-            break; }
-        default:
-            qDebug() << Q_FUNC_INFO << __LINE__ << v.type();
-            break;
-        }
-    }
-    train(train_data);
-}
-
-void QmlJubatusClassifier::train(const QString &label, const QVariantMap &data)
-{
-    std::vector<jubatus::classifier::labeled_datum> train_data;
-    train_data.push_back(jubatus::classifier::labeled_datum(label.toStdString(), convert(data)));
-    train(train_data);
-}
-
-QVariantList QmlJubatusClassifier::classify(const QVariantList &data)
-{
-    QVariantList ret;
-
-    std::vector<jubatus::client::common::datum> test_data;
-    foreach (const QVariant &v, data) {
-        test_data.push_back(convert(v.toMap()));
-    }
-
-    std::vector<std::vector<jubatus::classifier::estimate_result> > results = classify(test_data);
-
-    for (size_t i = 0; i < results.size(); ++i) {
-        QVariantList list;
-        for (size_t j = 0; j < results[i].size(); ++j) {
-            const jubatus::classifier::estimate_result& jr = results[i][j];
-            QVariantMap qr;
-            qr.insert(QStringLiteral("label"), QString::fromStdString(jr.label));
-            qr.insert(QStringLiteral("score"), jr.score);
-            list.append(qr);
-        }
-        ret.append(QVariant(list));
-    }
-    return ret;
-}
-
-QVariantList QmlJubatusClassifier::classify(const QVariantMap &data)
-{
-    QVariantList ret;
-
-    std::vector<jubatus::client::common::datum> test_data;
-    test_data.push_back(convert(data));
-
-    std::vector<std::vector<jubatus::classifier::estimate_result> > results = classify(test_data);
-
-    for (size_t i = 0; i < results[0].size(); ++i) {
-        const jubatus::classifier::estimate_result& jr = results[0][i];
-        QVariantMap qr;
-        qr.insert(QStringLiteral("label"), QString::fromStdString(jr.label));
-        qr.insert(QStringLiteral("score"), jr.score);
-        ret.append(qr);
-    }
-    return ret;
-}
+#include "qmljubatusclassifier.h"
+#include "qmljubatusrecommender.h"
 
 class QmlJubatusPlugin : public QQmlExtensionPlugin
 {
@@ -131,8 +39,9 @@ public:
     virtual void registerTypes(const char *uri)
     {
         Q_ASSERT(QLatin1String(uri) == QLatin1String("QtJubatus"));
-        // @uri QtFluentd
+        // @uri QtJubatus
         qmlRegisterType<QmlJubatusClassifier>(uri, 0, 1, "Classifier");
+        qmlRegisterType<QmlJubatusRecommender>(uri, 0, 1, "Recommender");
     }
 };
 
diff --git a/src/imports/jubatus/qmljubatusclassifier.cpp b/src/imports/jubatus/qmljubatusclassifier.cpp
new file mode 100644 (file)
index 0000000..c7ea9ee
--- /dev/null
@@ -0,0 +1,40 @@
+#include "qmljubatusclassifier.h"
+
+QmlJubatusClassifier::QmlJubatusClassifier(QObject *parent) :
+    QJubatusClassifier(parent)
+{
+}
+
+void QmlJubatusClassifier::train(const QString &label, const QVariantMap &data)
+{
+    LabeledDatum datum;
+    datum.label = label;
+    datum.data = data;
+    QJubatusClassifier::train(QList<QJubatusClassifier::LabeledDatum>() << datum);
+}
+
+QVariantList QmlJubatusClassifier::classify(const QVariantMap &data)
+{
+    QVariantList ret;
+    QList<QList<QJubatusClassifier::EstimateResult> > result = QJubatusClassifier::classify(QList<QVariantMap>() << data);
+    if (!result.isEmpty())
+        ret = convert(result.first());
+    return ret;
+}
+
+QVariantMap QmlJubatusClassifier::convert(const QJubatusClassifier::EstimateResult &data) const
+{
+    QVariantMap ret;
+    ret.insert(QStringLiteral("label"), data.label);
+    ret.insert(QStringLiteral("score"), data.score);
+    return ret;
+}
+
+QVariantList QmlJubatusClassifier::convert(const QList<QJubatusClassifier::EstimateResult> &data) const
+{
+    QVariantList ret;
+    foreach (const QJubatusClassifier::EstimateResult &datum, data) {
+        ret.append(convert(datum));
+    }
+    return ret;
+}
diff --git a/src/imports/jubatus/qmljubatusclassifier.h b/src/imports/jubatus/qmljubatusclassifier.h
new file mode 100644 (file)
index 0000000..8855a08
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef QMLJUBATUSCLASSIFIER_H
+#define QMLJUBATUSCLASSIFIER_H
+
+#include <QtJubatus/QJubatusClassifier>
+
+class QmlJubatusClassifier : public QJubatusClassifier
+{
+    Q_OBJECT
+public:
+    QmlJubatusClassifier(QObject *parent = 0);
+
+    Q_INVOKABLE void train(const QString &label, const QVariantMap &data);
+    Q_INVOKABLE QVariantList classify(const QVariantMap &data);
+
+protected:
+    using QJubatusClassifier::convert;
+    QVariantMap convert(const QJubatusClassifier::EstimateResult &data) const;
+    QVariantList convert(const QList<QJubatusClassifier::EstimateResult> &data) const;
+};
+
+#endif // QMLJUBATUSCLASSIFIER_H
diff --git a/src/imports/jubatus/qmljubatusrecommender.cpp b/src/imports/jubatus/qmljubatusrecommender.cpp
new file mode 100644 (file)
index 0000000..da62f62
--- /dev/null
@@ -0,0 +1,33 @@
+#include "qmljubatusrecommender.h"
+
+QmlJubatusRecommender::QmlJubatusRecommender(QObject *parent) :
+    QJubatusRecommender(parent)
+{
+}
+
+QVariantList QmlJubatusRecommender::similarRowFromId(const QString &id, uint size)
+{
+    return convert(QJubatusRecommender::similarRowFromId(id, size));
+}
+
+QVariantList QmlJubatusRecommender::similarRowFromDatum(const QVariantMap &data, uint size)
+{
+    return convert(QJubatusRecommender::similarRowFromDatum(data, size));
+}
+
+QVariantMap QmlJubatusRecommender::convert(const QJubatusRecommender::IdWithScore &data) const
+{
+    QVariantMap ret;
+    ret.insert(QStringLiteral("id"), data.id);
+    ret.insert(QStringLiteral("score"), data.score);
+    return ret;
+}
+
+QVariantList QmlJubatusRecommender::convert(const QList<QJubatusRecommender::IdWithScore> &data) const
+{
+    QVariantList ret;
+    foreach (const IdWithScore &idWithScore, data) {
+        ret.append(convert(idWithScore));
+    }
+    return ret;
+}
diff --git a/src/imports/jubatus/qmljubatusrecommender.h b/src/imports/jubatus/qmljubatusrecommender.h
new file mode 100644 (file)
index 0000000..e03a148
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef QMLJUBATUSRECOMMENDER_H
+#define QMLJUBATUSRECOMMENDER_H
+
+#include <QtJubatus/QJubatusRecommender>
+
+class QmlJubatusRecommender : public QJubatusRecommender
+{
+    Q_OBJECT
+public:
+    explicit QmlJubatusRecommender(QObject *parent = 0);
+
+    Q_INVOKABLE QVariantList similarRowFromId(const QString &id, uint size);
+    Q_INVOKABLE QVariantList similarRowFromDatum(const QVariantMap &data, uint size);
+
+protected:
+    QVariantMap convert(const IdWithScore &data) const;
+    QVariantList convert(const QList<IdWithScore> &data) const;
+};
+
+#endif // QMLJUBATUSRECOMMENDER_H
index a7fd739..1b46cad 100644 (file)
 
 #include <jubatus/client/anomaly_client.hpp>
 
-class QJubatusAnomaly::Private
-{
-public:
-    Private();
-    ~Private();
-
-    jubatus::anomaly::client::anomaly *client;
-};
-
-QJubatusAnomaly::Private::Private()
-    : client(0)
-{
-}
-
-QJubatusAnomaly::Private::~Private()
-{
-    delete client;
-}
-
 QJubatusAnomaly::QJubatusAnomaly(QObject *parent)
     : QJubatusClient(parent)
-    , d(new Private)
 {
-    connect(this, &QJubatusAnomaly::destroyed, [this](){ delete d; });
-    auto deleteClient = [this]() { delete d->client; d->client = 0; };
-    connect(this, &QJubatusAnomaly::hostChanged, deleteClient);
-    connect(this, &QJubatusAnomaly::portChanged, deleteClient);
-    connect(this, &QJubatusAnomaly::nameChanged, deleteClient);
-    connect(this, &QJubatusAnomaly::timeoutChanged, deleteClient);
 }
 
 bool QJubatusAnomaly::clearRow(const QString &id)
 {
-    if (!d->client) {
-        d->client = new jubatus::anomaly::client::anomaly(host().toStdString(), port(), name().toStdString(), timeout());
-    }
-    return d->client->clear_row(id.toStdString());
+    bool ret = false;
+    EXEC_JUBATUS_COMMAND( ret = client()->clear_row(convert(id)); )
+    return ret;
 }
 
 QJubatusAnomaly::IdAndScore QJubatusAnomaly::add(const QVariantMap &data)
 {
-    if (!d->client) {
-        d->client = new jubatus::anomaly::client::anomaly(host().toStdString(), port(), name().toStdString(), timeout());
-    }
-    jubatus::anomaly::id_with_score id_with_score = d->client->add(convert(data));
-    IdAndScore ret;
-    ret.id = QString::fromStdString(id_with_score.id);
-    ret.score = id_with_score.score;
+    QJubatusAnomaly::IdAndScore ret;
+    EXEC_JUBATUS_COMMAND( ret = convert(client()->add(convert(data))); )
     return ret;
 }
 
 float QJubatusAnomaly::update(const QString &id, const QVariantMap &data)
 {
-    if (!d->client) {
-        d->client = new jubatus::anomaly::client::anomaly(host().toStdString(), port(), name().toStdString(), timeout());
-    }
-    return d->client->update(id.toStdString(), convert(data));
+    float ret = 0.0;
+    EXEC_JUBATUS_COMMAND( ret = client()->update(convert(id), convert(data)); )
+    return ret;
 }
 
 float QJubatusAnomaly::overwrite(const QString &id, const QVariantMap &data)
 {
-    if (!d->client) {
-        d->client = new jubatus::anomaly::client::anomaly(host().toStdString(), port(), name().toStdString(), timeout());
-    }
-    return d->client->overwrite(id.toStdString(), convert(data));
+    float ret = 0.0;
+    EXEC_JUBATUS_COMMAND( client()->overwrite(convert(id), convert(data)); )
+    return ret;
 }
 
 float QJubatusAnomaly::calcScore(const QVariantMap &data)
 {
-    if (!d->client) {
-        d->client = new jubatus::anomaly::client::anomaly(host().toStdString(), port(), name().toStdString(), timeout());
-    }
-    return d->client->calc_score(convert(data));
+    float ret = 0.0;
+    EXEC_JUBATUS_COMMAND( ret = client()->calc_score(convert(data)); )
+    return ret;
 }
 
 QStringList QJubatusAnomaly::getAllRows()
 {
-    if (!d->client) {
-        d->client = new jubatus::anomaly::client::anomaly(host().toStdString(), port(), name().toStdString(), timeout());
-    }
     QStringList ret;
-    foreach (std::string id, d->client->get_all_rows())
-        ret.append(QString::fromStdString(id));
+    EXEC_JUBATUS_COMMAND( ret = convert(client()->get_all_rows()); )
+    return ret;
+}
+
+jubatus::anomaly::id_with_score QJubatusAnomaly::convert(const QJubatusAnomaly::IdAndScore &data) const
+{
+    jubatus::anomaly::id_with_score ret;
+    ret.id = convert(data.id);
+    ret.score = data.score;
+    return ret;
+}
+
+QJubatusAnomaly::IdAndScore QJubatusAnomaly::convert(const jubatus::anomaly::id_with_score &data) const
+{
+    IdAndScore ret;
+    ret.id = convert(data.id);
+    ret.score = data.score;
     return ret;
 }
 
+jubatus::anomaly::client::anomaly *QJubatusAnomaly::client()
+{
+    return QJubatusClient::client<jubatus::anomaly::client::anomaly>();
+}
index e5362ca..5418ec7 100644 (file)
@@ -35,6 +35,9 @@
 namespace jubatus {
     namespace anomaly {
         struct id_with_score;
+        namespace client {
+            class anomaly;
+        }
     }
 }
 
@@ -45,6 +48,7 @@ public:
     explicit QJubatusAnomaly(QObject *parent = 0);
 
     struct IdAndScore {
+        IdAndScore() : score(0.0) {}
         QString id;
         float score;
     };
@@ -56,9 +60,13 @@ public:
     float calcScore(const QVariantMap &data);
     QStringList getAllRows();
 
+protected:
+    using QJubatusClient::convert;
+    jubatus::anomaly::id_with_score convert(const IdAndScore &data) const;
+    IdAndScore convert(const jubatus::anomaly::id_with_score &data) const;
+
 private:
-    class Private;
-    Private *d;
+    jubatus::anomaly::client::anomaly *client();
 };
 
 #endif // QJUBATUSANOMALY_H
index 5d7c99e..3f81107 100644 (file)
 
 #include <jubatus/client/classifier_client.hpp>
 
-class QJubatusClassifier::Private
+QJubatusClassifier::QJubatusClassifier(QObject *parent)
+    : QJubatusClient(parent)
 {
-public:
-    Private();
-    ~Private();
-
-    jubatus::classifier::client::classifier *client;
-};
+}
 
-QJubatusClassifier::Private::Private()
-    : client(0)
+void QJubatusClassifier::train(const QList<QJubatusClassifier::LabeledDatum> &data)
 {
+    EXEC_JUBATUS_COMMAND( client()->train(convert(data)); )
 }
 
-QJubatusClassifier::Private::~Private()
+QList<QList<QJubatusClassifier::EstimateResult> > QJubatusClassifier::classify(const QList<QVariantMap> &data)
 {
-    delete client;
+    QList<QList<QJubatusClassifier::EstimateResult> > ret;
+    EXEC_JUBATUS_COMMAND( ret = convert(client()->classify(convert(data))); )
+    return ret;
 }
 
-QJubatusClassifier::QJubatusClassifier(QObject *parent)
-    : QJubatusClient(parent)
-    , d(new Private)
+jubatus::classifier::labeled_datum QJubatusClassifier::convert(const QJubatusClassifier::LabeledDatum &data) const
 {
-    connect(this, &QJubatusClassifier::destroyed, [this](){ delete d; });
-    auto deleteClient = [this]() { delete d->client; d->client = 0; };
-    connect(this, &QJubatusClassifier::hostChanged, deleteClient);
-    connect(this, &QJubatusClassifier::portChanged, deleteClient);
-    connect(this, &QJubatusClassifier::nameChanged, deleteClient);
-    connect(this, &QJubatusClassifier::timeoutChanged, deleteClient);
+    jubatus::classifier::labeled_datum ret;
+    ret.label = convert(data.label);
+    ret.data = convert(data.data);
+    return ret;
 }
 
-void QJubatusClassifier::train(const QList<QJubatusClassifier::TrainData> &data)
+std::vector<jubatus::classifier::labeled_datum> QJubatusClassifier::convert(const QList<QJubatusClassifier::LabeledDatum> &data) const
 {
-    std::vector<jubatus::classifier::labeled_datum> train_data;
-    foreach (const TrainData &v, data) {
-        train_data.push_back(jubatus::classifier::labeled_datum(v.first.toStdString(), convert(v.second)));
+    std::vector<jubatus::classifier::labeled_datum> ret;
+    foreach (const QJubatusClassifier::LabeledDatum &datum, data) {
+        ret.push_back(convert(datum));
     }
-    train(train_data);
+    return ret;
 }
 
-void QJubatusClassifier::train(const std::vector<jubatus::classifier::labeled_datum> &data)
+QJubatusClassifier::EstimateResult QJubatusClassifier::convert(const jubatus::classifier::estimate_result &data) const
 {
-    if (!d->client) {
-        d->client = new jubatus::classifier::client::classifier(host().toStdString(), port(), name().toStdString(), timeout());
-    }
-    d->client->train(data);
+    QJubatusClassifier::EstimateResult ret;
+    ret.label = convert(data.label);
+    ret.score = data.score;
+    return ret;
 }
 
-QList<QList<QJubatusClassifier::EstimateResult>> QJubatusClassifier::classify(const QList<QVariantMap> &data)
+QList<QJubatusClassifier::EstimateResult> QJubatusClassifier::convert(const std::vector<jubatus::classifier::estimate_result> &data) const
 {
-    QList<QList<EstimateResult>> ret;
-
-    std::vector<jubatus::client::common::datum> test_data;
-    foreach (const QVariant &v, data) {
-        test_data.push_back(convert(v.toMap()));
-    }
-
-    std::vector<std::vector<jubatus::classifier::estimate_result> > results = classify(test_data);
-
-    for (size_t i = 0; i < results.size(); ++i) {
-        QList<EstimateResult> list;
-        for (size_t j = 0; j < results[i].size(); ++j) {
-            const jubatus::classifier::estimate_result& jr = results[i][j];
-            EstimateResult qr;
-            qr.label = QString::fromStdString(jr.label);
-            qr.score = jr.score;
-            list.append(qr);
-        }
-        ret.append(list);
+    QList<QJubatusClassifier::EstimateResult> ret;
+    for (auto it = data.begin(); it != data.end(); ++it) {
+        ret.append(convert(*it));
     }
     return ret;
 }
 
-std::vector<std::vector<jubatus::classifier::estimate_result>> QJubatusClassifier::classify(const std::vector<jubatus::client::common::datum> &data)
+QList<QList<QJubatusClassifier::EstimateResult>> QJubatusClassifier::convert(const std::vector<std::vector<jubatus::classifier::estimate_result>> &data) const
 {
-    if (!d->client) {
-        d->client = new jubatus::classifier::client::classifier(host().toStdString(), port(), name().toStdString(), timeout());
+    QList<QList<QJubatusClassifier::EstimateResult>> ret;
+    for (auto it = data.begin(); it != data.end(); ++it) {
+        ret.append(convert(*it));
     }
-    return d->client->classify(data);
+    return ret;
 }
 
-
+jubatus::classifier::client::classifier *QJubatusClassifier::client()
+{
+    return QJubatusClient::client<jubatus::classifier::client::classifier>();
+}
index e907290..850c9f6 100644 (file)
@@ -37,6 +37,9 @@ namespace jubatus {
     namespace classifier {
         struct estimate_result;
         struct labeled_datum;
+        namespace client {
+            class classifier;
+        }
     }
 }
 
@@ -46,6 +49,11 @@ class JUBATUS_EXPORT QJubatusClassifier : public QJubatusClient
 public:
     explicit QJubatusClassifier(QObject *parent = 0);
 
+    struct LabeledDatum {
+        QString label;
+        QVariantMap data;
+    };
+
     struct EstimateResult {
         QString label;
         double score;
@@ -53,16 +61,19 @@ public:
 
     typedef QPair<QString, QVariantMap> TrainData;
 
-    void train(const QList<QJubatusClassifier::TrainData> &data);
-    QList<QList<QJubatusClassifier::EstimateResult>> classify(const QList<QVariantMap> &data);
+    Q_INVOKABLE void train(const QList<QJubatusClassifier::LabeledDatum> &data);
+    Q_INVOKABLE QList<QList<QJubatusClassifier::EstimateResult>> classify(const QList<QVariantMap> &data);
 
 protected:
-    void train(const std::vector<jubatus::classifier::labeled_datum> &data);
-    std::vector<std::vector<jubatus::classifier::estimate_result>> classify(const std::vector<jubatus::client::common::datum> &data);
+    using QJubatusClient::convert;
+    jubatus::classifier::labeled_datum convert(const QJubatusClassifier::LabeledDatum &data) const;
+    std::vector<jubatus::classifier::labeled_datum> convert(const QList<QJubatusClassifier::LabeledDatum> &data) const;
+    QJubatusClassifier::EstimateResult convert(const jubatus::classifier::estimate_result &data) const;
+    QList<QJubatusClassifier::EstimateResult> convert(const std::vector<jubatus::classifier::estimate_result> &data) const;
+    QList<QList<QJubatusClassifier::EstimateResult>> convert(const std::vector<std::vector<jubatus::classifier::estimate_result>> &data) const;
 
 private:
-    class Private;
-    Private *d;
+    jubatus::classifier::client::classifier *client();
 };
 
 #endif // QJUBATUSCLASSIFIER_H
index 943a39c..86e8a72 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <QtCore/QDebug>
 
+#include <jubatus/client/common/client.hpp>
 #include <jubatus/client/common/datum.hpp>
 
 class QJubatusClient::Private
@@ -52,8 +53,14 @@ QJubatusClient::Private::Private()
 QJubatusClient::QJubatusClient(QObject *parent)
     : QObject(parent)
     , d(new Private())
+    , m_client(0)
 {
     connect(this, &QJubatusClient::destroyed, [this](){ delete d; });
+    auto deleteClient = [this]() { delete m_client; m_client = 0; };
+    connect(this, &QJubatusClient::hostChanged, deleteClient);
+    connect(this, &QJubatusClient::portChanged, deleteClient);
+    connect(this, &QJubatusClient::nameChanged, deleteClient);
+    connect(this, &QJubatusClient::timeoutChanged, deleteClient);
 }
 
 const QString &QJubatusClient::host() const
@@ -104,24 +111,52 @@ void QJubatusClient::setTimeout(double timeout)
     emit timeoutChanged(timeout);
 }
 
+std::string QJubatusClient::convert(const QString &data) const
+{
+    return data.toStdString();
+}
+
+QString QJubatusClient::convert(const std::string &data) const
+{
+    return QString::fromStdString(data);
+}
+
+std::vector<std::string> QJubatusClient::convert(const QStringList &data) const
+{
+    std::vector<std::string> ret;
+    foreach (const QString &str, data) {
+        ret.push_back(convert(str));
+    }
+    return ret;
+}
+
+QStringList QJubatusClient::convert(const std::vector<std::string> &data) const
+{
+    QStringList ret;
+    for (auto it = data.begin(); it != data.end(); ++it) {
+        ret.append(convert(*it));
+    }
+    return ret;
+}
+
 jubatus::client::common::datum QJubatusClient::convert(const QVariantMap &data) const
 {
     jubatus::client::common::datum ret;
 
     foreach (const QString &key, data.keys()) {
         QVariant value = data.value(key);
-        switch (value.type()) {
+        switch (static_cast<int>(value.type())) {
         case QVariant::String:
-            ret.string_values.push_back(make_pair(key.toStdString(), value.toString().toStdString()));
+            ret.add_string(key.toStdString(), value.toString().toStdString());
             break;
         case QVariant::Int:
-            ret.num_values.push_back(make_pair(key.toStdString(), value.toInt()));
+            ret.add_number(key.toStdString(), value.toInt());
             break;
         case QMetaType::Float:
-            ret.num_values.push_back(make_pair(key.toStdString(), value.toFloat()));
+            ret.add_number(key.toStdString(), value.toFloat());
             break;
         case QVariant::Double:
-            ret.num_values.push_back(make_pair(key.toStdString(), value.toDouble()));
+            ret.add_number(key.toStdString(), value.toDouble());
             break;
         default:
             qDebug() << Q_FUNC_INFO << __LINE__ << value.type() << value << "not supported.";
@@ -131,3 +166,32 @@ jubatus::client::common::datum QJubatusClient::convert(const QVariantMap &data)
 
     return ret;
 }
+
+QVariantMap QJubatusClient::convert(const jubatus::client::common::datum &data) const
+{
+    QVariantMap ret;
+    for (auto it = data.num_values.begin(); it != data.num_values.end(); ++it) {
+        ret.insert(convert(it->first), it->second);
+    }
+    for (auto it = data.string_values.begin(); it != data.string_values.end(); ++it) {
+        ret.insert(convert(it->first), convert(it->second));
+    }
+    return ret;
+}
+
+std::vector<jubatus::client::common::datum> QJubatusClient::convert(const QList<QVariantMap> &data) const
+{
+    std::vector<jubatus::client::common::datum> ret;
+    foreach (const QVariantMap &datum, data) {
+        ret.push_back(convert(datum));
+    }
+    return ret;
+}
+QList<QVariantMap> QJubatusClient::convert(const std::vector<jubatus::client::common::datum> &data) const
+{
+    QList<QVariantMap> ret;
+    for (auto it = data.begin(); it != data.end(); ++it) {
+        ret.append(convert(*it));
+    }
+    return ret;
+}
index f5ff43f..eb069da 100644 (file)
@@ -36,6 +36,7 @@ namespace jubatus {
     namespace client {
         namespace common {
             struct datum;
+            class client;
         }
     }
 }
@@ -67,12 +68,50 @@ signals:
     void nameChanged(const QString &name);
     void timeoutChanged(double timeout);
 
+    void error(const QString &message);
+
 protected:
+    template <class T>
+    T *client() {
+        if (!m_client) {
+            m_client = new T(convert(host()), port(), convert(name()), timeout());
+        }
+        return static_cast<T *>(m_client);
+    }
+
+    std::string convert(const QString &data) const;
+    QString convert(const std::string &data) const;
+    std::vector<std::string> convert(const QStringList &data) const;
+    QStringList convert(const std::vector<std::string> &data) const;
     jubatus::client::common::datum convert(const QVariantMap &data) const;
+    QVariantMap convert(const jubatus::client::common::datum &data) const;
+    std::vector<jubatus::client::common::datum> convert(const QList<QVariantMap> &data) const;
+    QList<QVariantMap> convert(const std::vector<jubatus::client::common::datum> &data) const;
 
 private:
     class Private;
     Private *d;
+
+    jubatus::client::common::client *m_client;
 };
 
+#define EXEC_JUBATUS_COMMAND(COMMAND) \
+    try { \
+        for (int i = 0; i < 3; i++) { \
+            try { \
+                COMMAND; \
+                break; \
+            } catch (msgpack::rpc::connection_closed_error &e) { \
+                client()->get_client().close(); \
+            } catch (msgpack::rpc::system_error &e) { \
+                client()->get_client().close(); \
+            } catch (msgpack::rpc::timeout_error &e) { \
+                client()->get_client().close(); \
+            } \
+            ::sleep(1); \
+        } \
+    } catch(msgpack::rpc::rpc_error &e) { \
+        emit error(QString::fromUtf8(e.what())); \
+    }
+
 #endif // QJUBATUSCLIENT_H
index 8edfd78..ebf0d0a 100644 (file)
@@ -7,6 +7,8 @@ load(qt_module)
 CONFIG += link_pkgconfig
 PKGCONFIG += jubatus-client
 
+CONFIG += exception
+
 DEFINES += JUBATUS_LIBRARY
 HEADERS += \
     jubatus_global.h
@@ -14,3 +16,4 @@ HEADERS += \
 include(./client/client.pri)
 include(./anomaly/anomaly.pri)
 include(./classifier/classifier.pri)
+include(./recommender/recommender.pri)
diff --git a/src/jubatus/recommender/qjubatusrecommender.cpp b/src/jubatus/recommender/qjubatusrecommender.cpp
new file mode 100644 (file)
index 0000000..e342420
--- /dev/null
@@ -0,0 +1,129 @@
+/* Copyright (c) 2012 Silk Project.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the Silk nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL SILK BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "qjubatusrecommender.h"
+
+#include <QtCore/QDebug>
+
+#include <vector>
+
+#include <jubatus/client/recommender_client.hpp>
+
+QJubatusRecommender::QJubatusRecommender(QObject *parent)
+    : QJubatusClient(parent)
+{
+}
+
+bool QJubatusRecommender::clearRow(const QString &id)
+{
+    bool ret = false;
+    EXEC_JUBATUS_COMMAND( ret = client()->clear_row(convert(id)); )
+    return ret;
+}
+
+bool QJubatusRecommender::updateRow(const QString &id, const QVariantMap &data)
+{
+    bool ret = false;
+    EXEC_JUBATUS_COMMAND( ret = client()->update_row(convert(id), convert(data)); )
+    return ret;
+}
+
+QVariantMap QJubatusRecommender::completeRowFromId(const QString &id)
+{
+    QVariantMap ret;
+    EXEC_JUBATUS_COMMAND( ret = convert(client()->complete_row_from_id(convert(id))); )
+    return ret;
+}
+
+QVariantMap QJubatusRecommender::completeRowFromDatum(const QVariantMap &data)
+{
+    QVariantMap ret;
+    EXEC_JUBATUS_COMMAND( ret = convert(client()->complete_row_from_datum(convert(data))); )
+    return ret;
+}
+
+QList<QJubatusRecommender::IdWithScore> QJubatusRecommender::similarRowFromId(const QString &id, uint size)
+{
+    QList<QJubatusRecommender::IdWithScore> ret;
+    EXEC_JUBATUS_COMMAND( ret = convert(client()->similar_row_from_id(convert(id), size)); )
+    return ret;
+}
+
+QList<QJubatusRecommender::IdWithScore> QJubatusRecommender::similarRowFromDatum(const QVariantMap &data, uint size)
+{
+    QList<QJubatusRecommender::IdWithScore> ret;
+    EXEC_JUBATUS_COMMAND( ret = convert(client()->similar_row_from_datum(convert(data), size)); )
+    return ret;
+}
+
+QVariantMap QJubatusRecommender::decodeRow(const QString &id)
+{
+    QVariantMap ret;
+    EXEC_JUBATUS_COMMAND( ret = convert(client()->decode_row(id.toStdString())); )
+    return ret;
+}
+
+QStringList QJubatusRecommender::getAllRows()
+{
+    QStringList ret;
+    EXEC_JUBATUS_COMMAND( ret = convert(client()->get_all_rows()); )
+    return ret;
+}
+
+float QJubatusRecommender::calcSimilarity(const QVariantMap &data1, const QVariantMap &data2)
+{
+    float ret = 0.0;
+    EXEC_JUBATUS_COMMAND( ret = client()->calc_similarity(convert(data1), convert(data2)); )
+    return ret;
+}
+
+float QJubatusRecommender::calcL2Norm(const QVariantMap &data)
+{
+    float ret = 0.0;
+    EXEC_JUBATUS_COMMAND( ret = client()->calc_l2norm(convert(data)); )
+    return ret;
+}
+
+QJubatusRecommender::IdWithScore QJubatusRecommender::convert(const jubatus::recommender::id_with_score &data) const
+{
+    QJubatusRecommender::IdWithScore ret;
+    ret.id = convert(data.id);
+    ret.score = data.score;
+    return ret;
+}
+
+QList<QJubatusRecommender::IdWithScore> QJubatusRecommender::convert(const std::vector<jubatus::recommender::id_with_score> &data) const
+{
+    QList<QJubatusRecommender::IdWithScore> ret;
+    foreach (const jubatus::recommender::id_with_score id_with_score, data) {
+        ret.append(convert(id_with_score));
+    }
+    return ret;
+}
+
+jubatus::recommender::client::recommender *QJubatusRecommender::client() {
+    return QJubatusClient::client<jubatus::recommender::client::recommender>();
+}
diff --git a/src/jubatus/recommender/qjubatusrecommender.h b/src/jubatus/recommender/qjubatusrecommender.h
new file mode 100644 (file)
index 0000000..df8138d
--- /dev/null
@@ -0,0 +1,75 @@
+/* Copyright (c) 2012 Silk Project.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the Silk nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL SILK BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef QJUBATUSRECOMMENDER_H
+#define QJUBATUSRECOMMENDER_H
+
+#include "jubatus_global.h"
+#include "qjubatusclient.h"
+
+#include <QtCore/QVariant>
+
+namespace jubatus {
+    namespace recommender {
+        struct id_with_score;
+        namespace client {
+            class recommender;
+        }
+    }
+}
+
+class JUBATUS_EXPORT QJubatusRecommender : public QJubatusClient
+{
+    Q_OBJECT
+public:
+    explicit QJubatusRecommender(QObject *parent = 0);
+
+    struct IdWithScore {
+        QString id;
+        float score;
+    };
+
+    Q_INVOKABLE bool clearRow(const QString &id);
+    Q_INVOKABLE bool updateRow(const QString &id, const QVariantMap &data);
+    Q_INVOKABLE QVariantMap completeRowFromId(const QString &id);
+    Q_INVOKABLE QVariantMap completeRowFromDatum(const QVariantMap &data);
+    Q_INVOKABLE QList<IdWithScore> similarRowFromId(const QString &id, uint size);
+    Q_INVOKABLE QList<IdWithScore> similarRowFromDatum(const QVariantMap &data, uint size);
+    Q_INVOKABLE QVariantMap decodeRow(const QString &id);
+    Q_INVOKABLE QStringList getAllRows();
+    Q_INVOKABLE float calcSimilarity(const QVariantMap &data1, const QVariantMap &data2);
+    Q_INVOKABLE float calcL2Norm(const QVariantMap &data);
+
+protected:
+    using QJubatusClient::convert;
+    IdWithScore convert(const jubatus::recommender::id_with_score &data) const;
+    QList<IdWithScore> convert(const std::vector<jubatus::recommender::id_with_score> &data) const;
+
+private:
+    jubatus::recommender::client::recommender *client();
+};
+
+#endif // QJUBATUSRECOMMENDER_H
diff --git a/src/jubatus/recommender/recommender.pri b/src/jubatus/recommender/recommender.pri
new file mode 100644 (file)
index 0000000..618c126
--- /dev/null
@@ -0,0 +1,6 @@
+INCLUDEPATH += $$PWD
+DEPENDPATH += $$PWD
+
+PUBLIC_HEADERS += $$PWD/qjubatusrecommender.h
+HEADERS += $$PUBLIC_HEADERS
+SOURCES += $$PWD/qjubatusrecommender.cpp