php Jaccard

<?php

 /**
 * $item_ids => 商品IDの配列[1,2,3,4,5]の配列
 */

 foreach($item_ids as $item_id1){
   $base = $Redis->lRange('Viewer:Item' . $item_id1, 0, 999);
   if (count($base) === 0){
     continue;
   }
   foreach ($item_ids as $item_id2){
     if ($item_id1 === $item_id2){
       continue;
     }
     $target = $Redis->lRange('Viewer:Item:' . $item_id2, 0, 999);
     if (count($target) === 0){
       continue;
     }

     #ジャッカード指数を計算
     $join = floatval(count(array_unique(array_merge($base, $target))));
     $intersect = floatval(count(array_intersect($base, $target)));
     if ($intersect == 0 || $join == 0){
       continue;
     }
     $jaccard = $intersect / $join;

     $redis->zAdd('Jaccard:Item:' . $item_id1, $jaccard, $item_id2);

   }
 }
<?
/**
* $item_id => 商品id
* $user_id => ユーザid
*/
$Redis->lRem('Viewer:Item' . $item_id, $user_id);
$Redis->lPush('Viewer:Item' . $item_id, $user_id);
$Redis->lTrim('Viewer:Item' . $item_id, 0, 999);
<?php
 $Redis->zRevRange('Jaccard:Item:' . $item_id, 0, -1);

php redis

%e7%84%a1%e9%a1%8c

redisがインストールされた状態

git clone git://github.com/nicolasff/phpredis.git
cd phpredis
phpize
./configure
make
make install

php iniにextension=redis.soを追加。

[vagrant@localhost rss11]$ sudo vi /etc/php.ini
[vagrant@localhost rss11]$ php -m | grep redis
redis

phpでredis操作

<?php
$redis = new Redis();
$redis->connect("127.0.0.1",6379);

// PINGで確認する
echo $redis->ping();

set

<?php
$redis = new Redis();
$redis->connect("127.0.0.1",6379);

// PINGで確認する
echo $redis->set('hoge', 'huga');

$value = $redis->get('hoge');

echo $value;

python redis module

Python client for Redis key-value store

$ sudo pip install redis

index.python

# _*_ coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals

def jaccard(e1, e2):
    """
    :param e1: list of int
    :param e2: list of int
    :rtype: float
    """
    set_e1 = set(e1)
    set_e2 = set(e2)
    return float(len(set_e1 & set_e2)) / float(len(set_e1 | set_e2))

def get_key(k):
    return 'JACCARD:PRODUCT:{}'.format(k)

# 商品xを購入した人が1,3,5
product_x = [1, 3, 5]
product_a = [2, 4, 5]
product_b = [1, 2, 3]
product_c = [2, 3, 4, 7]
product_d = [3]
product_e = [4, 6, 7]

# 商品データ
products = {
'X': product_x,
'A': product_a,
'B': product_b,
'C': product_c,
'D': product_d,
'E': product_e,
}

#redis
import redis
r = redis.Redis(host='localhost', port=6379, db=0)

for key in products:
    base_customers = products[key]
    for key2 in products:
        if key == key2:
            continue
        target_customers = products[key2]
        # ジャッカード指数
        j = jaccard(base_customers, target_customers)
        # redis Sortedに記録
        r.zadd(get_key(key), key2, j)

    # 例1 商品xを買った人はこんな商品も買っています
    print(r.zrevrange(get_key('X'), 0, 2))

    # 例2 商品Eを買った人はこんな商品も買っています。
    print(r.zrevrange(get_key('E'), 0, 2))
[vagrant@localhost rss10]$ python index.py
[b'B', b'D', b'A']
[b'C', b'A', b'X']

レコメンドアルゴリズムには、協調フィルタリングと内容ベース(コンテンツベース)フィルタリングがある

redis

サーバー起動
redis-server

クライアントの起動と終了
redis-cli

データベースの選択
0~15までデフォルトで設定されている
> select 1
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]>

データの保存
> bgsave
データはredis serverを立ち上げたディレクトリにdump.rdbで保存される

string
-set key value
mset key value key value

-get key
mget key key key

数字の操作
incr,decrby

key
> keys *
> keys *m*
> exits scroe
> rename age neneri
> del nenrei
> randomkey

list
push, pop
>rpush mycolor pink
>rpush mycolor green
追加した要素を見る
>lrange mycolor 0 3
>lrange mycolor 0 -1
削除
> rpop mycolor (rightから)
> lpop mycolor (leftから)
> lindex myclor 2
> llen mycolor
> ltrim mycolor 0 2

Set
追加: sadd
削除: srem

一覧: smembers
和集合: sunion
積集合: sinter (共通部分)
差集合: sdiff 

>suionstore myunion myset1 myset2

sorted set
追加: zadd
削除: zrem
一覧: zrange

Redisの操作


127.0.0.1:6379> rpush mycolor pink
(integer) 1
127.0.0.1:6379> rpush mycolor green
(integer) 2
127.0.0.1:6379> rpush mycolor red
(integer) 3
127.0.0.1:6379> rpush mycolor yellow
(integer) 4
127.0.0.1:6379> rpush mycolor blue
(integer) 5
127.0.0.1:6379> rpush myclolor purple
(integer) 1
127.0.0.1:6379> lrange mycolor 0 5
1) "pink"
2) "green"
3) "red"
4) "yellow"
5) "blue"
127.0.0.1:6379> lrange mycolor 0 -1
1) "pink"
2) "green"
3) "red"
4) "yellow"
5) "blue"

リスト操作

127.0.0.1:6379> rpop mycolor
"blue"
127.0.0.1:6379> lrange mycolor 0 -1
1) "pink"
2) "green"
3) "red"
4) "yellow"
127.0.0.1:6379> lpop mycolor
"pink"
127.0.0.1:6379> lrange mycolor 0 -1
1) "green"
2) "red"
3) "yellow"
127.0.0.1:6379> lindex mycolor 2
"yellow"
127.0.0.1:6379> llen mycolor
(integer) 3
127.0.0.1:6379> ltrim mycolor 0 2
OK
127.0.0.1:6379> lrange mycolor 0 -1
1) "green"
2) "red"
3) "yellow"

セット型の計算

127.0.0.1:6379> sadd myset1 a
(integer) 1
127.0.0.1:6379> sadd myset1 b
(integer) 1
127.0.0.1:6379> sadd myset1 c
(integer) 1
127.0.0.1:6379> smembers myset1
1) "c"
2) "b"
3) "a"
127.0.0.1:6379> sadd myset1 d
(integer) 1
127.0.0.1:6379> smembers myset1
1) "d"
2) "c"
3) "b"
4) "a"
127.0.0.1:6379> srem myset1 d
(integer) 1
127.0.0.1:6379> smembers myset1
1) "c"
2) "b"
3) "a"
127.0.0.1:6379> sadd myset2 b
(integer) 1
127.0.0.1:6379> sadd myset2 c
(integer) 1
127.0.0.1:6379> sadd myset2 e
(integer) 1
127.0.0.1:6379> smembers myset1
1) "c"
2) "b"
3) "a"
127.0.0.1:6379> smembers myset2
1) "c"
2) "b"
3) "e"
127.0.0.1:6379> sunion myset1 myset2
1) "b"
2) "c"
3) "e"
4) "a"
127.0.0.1:6379> sinter mysete1 myset2
(empty list or set)
127.0.0.1:6379> sdiff myset1 myset2
1) "a"

ソート済みの集合

127.0.0.1:6379> zadd hs 22 yamada
(integer) 1
127.0.0.1:6379> zadd hs 50 tanaka
(integer) 1
127.0.0.1:6379> zadd hs 80 yasuda
(integer) 1
127.0.0.1:6379> zadd hs 21 okamoto
(integer) 1
127.0.0.1:6379> zrange hs 0 -1
1) "okamoto"
2) "yamada"
3) "tanaka"
4) "yasuda"
127.0.0.1:6379> zrevrange hs 0 -1
1) "yasuda"
2) "tanaka"
3) "yamada"
4) "okamoto"
127.0.0.1:6379> zrank hs yamada
(integer) 1
127.0.0.1:6379> zrevrank hs yamada
(integer) 2

Redis

永続化の機能があるインメモリデータベースです。高速処理用に部分的に使われるなどのケースが多いようです。

http://redis.io/commands

インストールして、サーバーを立ち上げます。

[vagrant@localhost redis]$ redis-server
[14193] 12 Nov 04:58:10.521 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
[14193] 12 Nov 04:58:10.522 # You requested maxclients of 10000 requiring at least 10032 max file descriptors.
[14193] 12 Nov 04:58:10.522 # Redis can't set maximum open files to 10032 because of OS error: Operation not permitted.
[14193] 12 Nov 04:58:10.522 # Current maximum open files is 1024. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
                _._
           _.-``__ ''-._
      _.-``    `.  `_.  ''-._           Redis 2.8.12 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._
 (    '      ,       .-`  | `,    )     Running in stand alone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 14193
  `-._    `-._  `-./  _.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |           http://redis.io
  `-._    `-._`-.__.-'_.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |
  `-._    `-._`-.__.-'_.-'    _.-'
      `-._    `-.__.-'    _.-'
          `-._        _.-'
              `-.__.-'

[14193] 12 Nov 04:58:10.524 # Server started, Redis version 2.8.12
[14193] 12 Nov 04:58:10.525 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
[14193] 12 Nov 04:58:10.526 * The server is now ready to accept connections on port 6379

cliからのアクセス, exit, shutdouwn

[vagrant@localhost redis]$ redis-cli

データベースの選択
> select

データの保存
> bgsave
bgsaveでデータベースを保存すると、redis-serverを立ち上げたディレクトリに、dump.rdbが作成されます。

データの設定

127.0.0.1:6379> set name yamada
OK
127.0.0.1:6379> get name
"yamada"
127.0.0.1:6379> mset email yamada@gmail.com score 120
OK
127.0.0.1:6379> get name email score
(error) ERR wrong number of arguments for 'get' command
127.0.0.1:6379> mget name email score
1) "yamada"
2) "yamada@gmail.com"
3) "120"

incr score

127.0.0.1:6379> incr score
(integer) 121
127.0.0.1:6379> get score
"121"
127.0.0.1:6379> decrby score 10
(integer) 111
127.0.0.1:6379> get score
"111"

keyの操作

127.0.0.1:6379> keys *
1) "name"
2) "score"
3) "email"
127.0.0.1:6379> keys *m*
1) "name"
2) "email"
127.0.0.1:6379> exists score
(integer) 1
127.0.0.1:6379> exists age
(integer) 0
127.0.0.1:6379> set age 14
OK
127.0.0.1:6379> rename age nenrei
OK
127.0.0.1:6379> keys *
1) "nenrei"
2) "email"
3) "score"
4) "name"
127.0.0.1:6379> del nenrei
(integer) 1
127.0.0.1:6379> keys *
1) "email"
2) "score"
3) "name"
127.0.0.1:6379> set age 14
OK
127.0.0.1:6379> expire age 5
(integer) 1
127.0.0.1:6379> get age
(nil)