zoukankan      html  css  js  c++  java
  • (四)基于商品属性的相似商品推荐算法——推荐与评分高的商品属性相似的商品

    系列随笔:

    (总览)基于商品属性的相似商品推荐算法

    (一)基于商品属性的相似商品推荐算法——整体框架及处理流程

    (二)基于商品属性的相似商品推荐算法——Flink SQL实时计算实现商品的隐式评分

    (三)基于商品属性的相似商品推荐算法——批量处理商品属性,得到属性前缀及完整属性字符串

    (四)基于商品属性的相似商品推荐算法——推荐与评分高的商品属性相似的商品

    (五)基于商品属性的相似商品推荐算法——算法调优及其他

    2020.04.15  补充:协同过滤推荐算法.pptx

    提取码:4tds

    推荐与评分高的商品属性相似的商品


    重点:相似系数计算公式:相同属性位个数/总属性位个数

    一、按评分倒序,查询会员浏览过的商品

    $sql = "SELECT t1.member_code,t1.goods_code,t1.score,t2.goods_code,t2.goods_name,t2.shopping_guide,t2.market_price,t2.wbiao_price,t2.sale_type,t2.promotion,t2.models,t2.products,t2.image_url,t3.property_prefix,t3.properties FROM rc_member_goods t1 LEFT JOIN sj_goods t2 ON t2.goods_code=t1.goods_code LEFT JOIN rc_goods_properties t3 ON t3.goods_code=t1.goods_code WHERE t1.score<1000 AND t1.member_code IN ('". implode("','", $memberCodes) ."') ORDER BY t1.score DESC LIMIT {$nums}";

    注1:添加 t1.score<1000 条件是为了过滤恶意(或机器)的行为记录

    注2:为了方便计算,这里的 $nums 默认取值为 2

    注3:虽然这里 limit 2,但因为 rc_member_goods 和 rc_goods_properties 是一对多的关系,返回的数据行数会 >= 2;所以后面还要合并去重。

    合并去重:

    $return = [];
    while ($v = $records->fetch(PDO::FETCH_ASSOC)) {
        if (!isset($return[$v['member_code']])) {
            $return[$v['member_code']] = [];
        }
        if (!isset($return[$v['member_code']][$v['goods_code']])) {
            $return[$v['member_code']][$v['goods_code']] = $v;
        } else {
            $return[$v['member_code']][$v['goods_code']]['property_prefix'] += ','.$v['property_prefix'];
            $return[$v['member_code']][$v['goods_code']]['properties'] += ','.$v['properties'];
        }
    }

    二、准备一下需要查询的属性前缀和需要排除的商品

    // $records为上面查了出来的两条记录
    $one = $records[0];
    $memberCode = isset($one['member_code'])? $one['member_code']:0;
    $goodsCodes = [];
    $propertyPrefixs = [];
    
    if ($memberCode > 0) {
        // 会员的下过的订单的商品(后面有用)
        $goodsCodes = $this->getMemberOrderGoods($memberCode);
    }
    
    $temp = [];
    foreach ($records as $key => $value) {
        $temp[$value['goods_code']] = [];
        $goodsCodes[] = $value['goods_code'];
        // 上面合并出来的前缀,现在又拆开[笑哭]
        if (strpos($value['property_prefix'], ',') !== false) {
            $prefixs = explode(',', $value['property_prefix']);
            $propertyPrefixs = array_merge($propertyPrefixs, $prefixs);
        } else {
            $propertyPrefixs[] = $value['property_prefix'];
        }
    }

     三、查询属性前缀相同的商品(注:前缀相同,说明大致相似)

    $sql = "SELECT t2.goods_code,t2.goods_name,t2.shopping_guide,t2.market_price,t2.wbiao_price,t2.sale_type,t2.promotion,t2.models,t2.products,t2.image_url,t1.properties FROM rc_goods_properties t1 LEFT JOIN sj_goods t2 ON t1.goods_code=t2.goods_code WHERE t1.goods_code NOT IN ('". implode("','", $goodsCodes) ."') AND t1.property_prefix IN ('". implode("','", $propertyPrefixs) ."') AND t2.status=1 AND t2.shelf_status=1 AND t2.view_status=1";

    注:$goodsCode 和 $propertyPrefixs 为上一步得出的值

    四、循环处理对比商品完整属性,得出相似系数

    while ($row = $list->fetch(PDO::FETCH_ASSOC)) {
        $goodsCode = $row['goods_code'];
        $properties = $row['properties'];
        unset($row['properties']);
        foreach ($records as $key => $value) {
            if (strpos($value['properties'], ',') !== false) {
                $vProperties = explode(',', $value['properties']);
                $row['similarity'] = 0;
                foreach ($vProperties as $p) {
                    $row['similarity'] = max($this->genSimilarity($p, $properties), $row['similarity']);
                }
            } else {
                $row['similarity'] = $this->genSimilarity($value['properties'], $properties);
            }
        }
    }

     相似系数计算公式:相同属性位个数/总属性位个数

    private function genSimilarity($s1, $s2)
    {
        $arr1 = explode('|', $s1);
        $arr2 = explode('|', $s2);
        $same = 0;
        $total = count($arr1);
    
        foreach ($arr1 as $key => $v1) {
            $v2 = $arr2[$key];
            if ($v1 == $v2) {
                $same++;
            } else {
                $t1 = explode(',', $v1);
                $t2 = explode(',', $v2);
                if (array_intersect($t1, $t2)) {
                    $same++;
                }
            }
        }
        return $same/$total;
    }

    五、过滤相似系数低的商品、按相似系数倒序排列

    // $similarity 为你需要的最低相似度
    if ($row['similarity'] >= $similarity) {
        if (isset($temp[$value['goods_code']][$goodsCode])) {
            // 因为多选属性拆分的问题,多个前缀可能对应的同一个商品;这里通过比较,取相似度最高的记录
            if ($row['similarity'] > $temp[$value['goods_code']][$goodsCode]['similarity']) {
                $temp[$value['goods_code']][$goodsCode] = $row;
            }
        } else {
            // $value['goods_code'] 为原记录的商品, $goodsCode 为正在对比的商品
            $temp[$value['goods_code']][$goodsCode] = $row;
        }
    }
    private function sortAndFilter($arr)
    {
        $return = [];
        foreach ($arr as $k => $v) {
            if (!empty($v)) {
                $v = array_values($v);
                uasort($v, function($a, $b){
                    if ($a['similarity'] == $b['similarity']) {
                        return 0;
                    }
                    return $a['similarity'] < $b['similarity']? 1:-1;
                });
                $return[] = $v;
            }
        }
        return $return;
    }

    六、按评分比例,取N个商品

    $return = [];
    // 商品B的占比
    $rate = 0;
    if (isset($records[1]['score'])) {
        $rate = $records[1]['score']/($records[0]['score']+$records[1]['score']);
    }
    // 商品B的截取个数
    $num2 = intval($nums * $rate);
    // 商品A的截取个数
    $num1 = $nums - $num2;
    foreach ($temp as $key => $value) {
        if ($key == 0) {
            $p = array_slice($value, 0, $num1);
            $return = array_merge($return, $p);
            // 商品A的相似商品可能不够 $num1 个,不够就补给 $num2
            $num2 += $num1 - count($p);
        } elseif ($key == 1) {
            $p = array_slice($value, 0, $num2);
            $return = array_merge($return, $p);
        }
    }
    
    // 假设商品B没推荐商品或者总的推荐商品还是不够 $num 个
    $has = count($return);
    if ($has < $nums) {
        $p = array_slice($temp[0], $has, $nums-$has);
        $return = array_merge($return, $p);
    }

    七、其他

    如果最终的推荐商品数量还是不及 $num 个,则考虑补充销量高的商品(随机)

    上一节:(三)基于商品属性的相似商品推荐算法——批量处理商品属性,得到属性前缀及完整属性字符串

    下一节:(五)基于商品属性的相似商品推荐算法——算法调优及其他

  • 相关阅读:
    easyui datagrid client搜索、分页、排序
    tomcat安全配置之禁用Directory Listing
    关于一哥们离职
    <微软的软件测试之道>读书笔记3
    hdu 1685 Booksort (IDA*)
    百度——LBS.云 v2.0——创建自己的地理云数据
    Sublime Text 常用快捷键
    第二节,CCSpriteBatchNode CCSpriteFrameCache
    [置顶] hdu 1890 伸展树区间翻转
    @余凯_西二旗民工 【SVM之菜鸟实现】—5步SVM
  • 原文地址:https://www.cnblogs.com/tujia/p/12360063.html
Copyright © 2011-2022 走看看