(三)基於商品屬性的相似商品推薦算法——批量處理商品屬性,得到屬性前綴及完整屬性字符串


系列隨筆:

(總覽)基於商品屬性的相似商品推薦算法

(一)基於商品屬性的相似商品推薦算法——整體框架及處理流程

(二)基於商品屬性的相似商品推薦算法——Flink SQL實時計算實現商品的隱式評分

(三)基於商品屬性的相似商品推薦算法——批量處理商品屬性,得到屬性前綴及完整屬性字符串

(四)基於商品屬性的相似商品推薦算法——推薦與評分高的商品屬性相似的商品

(五)基於商品屬性的相似商品推薦算法——算法調優及其他

 

 

2020.04.15  補充:協同過濾推薦算法.pptx

 

提取碼:4tds

 

 

批量處理商品屬性,得到屬性前綴及完整屬性字符串


 

一、查詢全部商品($lastCode默認0,$limit默認0)

$sql = "SELECT goods_code FROM sj_goods WHERE goods_code>{$lastCode} ORDER BY goods_code ASC" . ($limit>0? " LIMIT $limit":"");

注1:如果商品數量較多,建議配合 $lastCode 和 $limit 做分批處理

注2:查詢結果不要直接轉數組,用游標操作效率會好一點

 

二、每100個商品批量查詢處理商品屬性

$list = $this->db['seller']->getAll($sql);
$goodsCodes = [];
$count = 0;
while ($row = $list->fetch(PDO::FETCH_ASSOC)) {
    $count++;
    $goodsCodes[] = $row['goods_code'];
    if ($count%100 == 0) {
        // 每100個商品查詢一次屬性
        $properties = $this->genGoodsProperties($goodsCodes);
        // 屬性入庫
        $this->insertGoodsProperties($properties) or die($goodsCodes[0].' 異常!!');
        $goodsCodes = [];
    }
}

// 別忘了不夠100個商品的情況
if (!empty($goodsCodes)) {
    $properties = $this->genGoodsProperties($goodsCodes);
    $this->insertGoodsProperties($properties) or die($goodsCodes[0].' 異常!!');
}

 

三、具體的屬性查詢及處理(上面的 genGoodsProperties 方法)

1)批量查詢商品的屬性code及屬性value

$sql = "SELECT t1.goods_code,t1.brand_code,t4.property_name,t3.property_code,t3.property_value FROM sj_goods_product t1 LEFT JOIN sj_product t2 ON t2.product_code = t1.product_code LEFT JOIN sj_style_properties t3 ON t3.style_code = t2.style_code LEFT JOIN sp_product_property t4 ON t4.property_code = t3.property_code WHERE t1.goods_code in ('". implode("','", $goodsCodes) ."')";

注1:主要就是要把商品對應的所有屬性值都查詢出來,根據自己的項目數據庫設計來操作就好

注2:為了提高商品的相關性,這里我還查詢了 brand_code,這個稍候再說怎么用

 

2)確定要用哪些屬性組合來做前綴,以及完整的屬性組合

// 屬性前綴為:適用人群-佩戴場合-機芯類型-價格區間-表盤形狀-表盤直徑,后面的屬性排列順序可以隨意
$needs = [31=>'適用人群', 40=>'佩戴場合', 1=>'機芯類型', 39=>'價格區間', 9=>'表盤形狀', 11=>'表盤直徑', 13=>'表盤刻度', 17=>'表帶材質', 14=>'表盤顏色', 25=>'防水', 3=>'外殼材質', 38=>'表盤寬度', 3=>'表盤厚度', 12=>'鏡面材質', 16=>'表殼底蓋', 19=>'表帶顏色'];

注1:數組的鍵為屬性的code,值為屬性的名稱

注2:為何這樣選以及怎么優化,后面會在  (五)基於商品屬性的相似商品推薦算法——算法調優及其他 里做說明。簡單來說,屬性前綴一樣的商品,就是有40%-50%相似的

 

3)循環處理商品,利用 1) 和 2) 的數據讀取商品屬性值,拼接屬性組字符串

private function genGoodsProperties($goodsCodes)
{
    // 屬性前綴為:適用人群-佩戴場合-機芯類型-價格區間-表盤形狀-表盤直徑,后面的屬性排列順序可以隨意
    $needs = [31=>'適用人群', 40=>'佩戴場合', 1=>'機芯類型', 39=>'價格區間', 9=>'表盤形狀', 11=>'表盤直徑', 13=>'表盤刻度', 17=>'表帶材質', 14=>'表盤顏色', 25=>'防水', 3=>'外殼材質', 38=>'表盤寬度', 3=>'表盤厚度', 12=>'鏡面材質', 16=>'表殼底蓋', 19=>'表帶顏色'];
    $properties = $this->getGoodsProperty($goodsCodes);
    $return = [];
    foreach ($goodsCodes as $goodsCode) {
        $prefix = '';
        $full = '';
        foreach ($needs as $key => $value) {
            $brandCode = 0;
            // 這里的 $properties 是第1)步得到的,已轉換成了map
            if (isset($properties[$goodsCode][$key]) && !empty($properties[$goodsCode][$key])) {
                // 如果商品有此屬性就拼接屬性值
                $full .= $properties[$goodsCode][$key]['property_value'].'|';
                $brandCode = $properties[$goodsCode][$key]['brand_code'];
            } else {
                // 如果商品沒有這個屬性,或屬性值為空,那拼接一個 0
                $full .= '0|';
            }

            // 臨界點(拿到屬性前綴)
            if ($value == '表盤直徑') {
                $prefix = $full;
                // 加入品牌信息
                $full .= $brandCode.'|';
            }
        }

        // 截取掉最后多余的 |
        $prefix = substr($prefix, 0, -1);
        $full = substr($full, 0, -1);
        // 拆分多選屬性
        $prefix = $this->splitProperties($prefix);
        $full = $this->splitProperties($full);
        $return[$goodsCode] = [$prefix, $full];
    }
    return $return;
}

 

四、屬性半成品示例

例如:有兩個商品的屬性是這樣的(點擊放大查看)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

經過第三步的處理,它的屬性組值是這樣的:

商品A:

$prefix = '96|297,300|195,41|64|46|218';
$full   = '96|297,300|195,41|64|46|218|87|220,222|51,242|58|185|198,200|41mm|12mm|276|257';

商品B:

$prefix = '96|298,297,299|195|64|46|218';
$full   = '96|298,297,299|195|64|46|218|87|220,222|242|58|185|200|41mm|L619/888|276|257';

注:逗號隔開的屬性值表示多選屬性,例如商品A的佩戴場合為"運動、商務休閑"(297,300),商品B的佩戴場合為"商務休閑、時尚、正裝"(298,297,299)

通過觀察屬性組,可以看出,其實兩個商品是相似的(大部分屬性位的值相同);但因為多選屬性的影響,在使用屬性前綴查詢相似商品的時候,不太好辦,就是有那么一丟丟不相同(上面示例紅色字體部分)

 

五、拆分多選屬性

首先,我們先要想一下自己想得到什么,然后再想辦法實現。

例如,我想像中商品A的屬性前綴的拆分情況是這樣的:

觀察上圖可得到:

第一步,原屬性位 “297,300”,被替換成了“297”和“300”,其他屬性位不變,得到兩個新屬性組字符串;

第二步也一樣,兩個新屬性組字符串的屬性位“195,41”,被替換成“195”和“41”,其他屬性位不變;

很明顯,這就是一個很簡單的字符串替換過程,關鍵是怎么維護和保存原字符串

實現代碼如下:

private function splitProperties($propertyPrefixs)
{
    // 沒有多選屬性,直接返回
    if (strpos($propertyPrefixs, ',') === false) {
        return [$propertyPrefixs];
    }

    // 原屬性組(也是結果)
    $origin = [$propertyPrefixs];
    $return = '';
    // 拆分屬性位
    $arr1 = explode('|', $propertyPrefixs);
    // 循環屬性位
    foreach ($arr1 as $k => $v) {
        // 多選屬性位
        if (strpos($v, ',') !== false) {
            $new = [];
            // 拆分多選屬性
            $arr2 = explode(',', $v);
            foreach ($arr2 as $kk => $vv) {
                // 循環原屬性組,替換原屬性位
                foreach ($origin as $kkk => $vvv) {
                    $prefixs = str_replace($v, $vv, $vvv);
                    // 替換結果
                    $new[] = $prefixs;
                }
            }
            // 更新的原屬性組
            $origin = $new;
        }
    }
    return $origin;
}

 

六、最終結果展示

商品A:

 

商品B:

 


 

上一節:Flink SQL實時計算實現商品的隱式評分

下一節:(四)基於商品屬性的相似商品推薦算法——推薦與評分高的商品屬性相似的商品

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM