浏览代码

生成推荐报告

Sherlock 11 月之前
父节点
当前提交
fed0ff1309
共有 4 个文件被更改,包括 95 次插入13 次删除
  1. 12 0
      database/dao/mysql_dao.py
  2. 14 7
      inference.py
  3. 8 1
      models/rank/gbdt_lr_inference.py
  4. 61 5
      utils/result_process.py

+ 12 - 0
database/dao/mysql_dao.py

@@ -138,6 +138,18 @@ class MySqlDao:
         
         return data
     
+    def get_order_by_cust(self, city_uuid, cust_id):
+        query = f"""
+            SELECT *
+            FROM {self._order_tablename}
+            WHERE city_uuid = :city_uuid
+            AND cust_code = :cust_id
+        """
+        params = {"city_uuid": city_uuid, "cust_id": cust_id}
+        data = self.db_helper.load_data_with_page(query, params)
+        
+        return data
+    
     def data_preprocess(self, data: pd.DataFrame):
         
         data.drop(["cust_uuid", "longitude", "latitude", "range_radius"], axis=1, inplace=True)

+ 14 - 7
inference.py

@@ -1,6 +1,6 @@
 
 from database import RedisDatabaseHelper, MySqlDao
-from models.rank.data.config import CustConfig, ProductConfig, ShopConfig
+from models.rank.data.config import CustConfig, ProductConfig, ShopConfig, OrderConfig
 from models.rank.data.utils import sample_data_clear
 from models.rank.gbdt_lr_inference import GbdtLrModel
 from utils.result_process import split_relation_subtable, generate_report
@@ -26,6 +26,8 @@ def get_recall_cust(city_uuid, product_id, recall_count):
     """根据协同过滤和热度召回召回商户"""
     itemcf_recall_list = get_itemcf_recall(city_uuid, product_id)
     hot_recall_list = get_hot_recall(city_uuid)
+    for i in hot_recall_list:
+        print(i)
     
     result = list(dict.fromkeys(itemcf_recall_list))
     
@@ -67,10 +69,12 @@ def get_recommend_list(city_uuid, product_id):
 def gbdt_lr_inference(city_uuid, product_id):
     pass
 
-def generate_features_shap(city_uuid, product_id):
-    feats_sample, filter_dict, _ = generate_recommend_sample(city_uuid, product_id)
+def generate_features_shap(city_uuid, product_id, delivery_count):
+    feats_sample, filter_dict, cust_list = generate_recommend_sample(city_uuid, product_id)
     result = gbdtlr_model.generate_shap_interance(feats_sample)
-    generate_report(result, filter_dict, "./data")
+    
+    recommend_data = gbdtlr_model.get_recommend_list(feats_sample, cust_list)
+    generate_report(city_uuid, result, filter_dict, recommend_data, delivery_count, "./data")
     
 
 def generate_delivery_strategy():
@@ -81,7 +85,10 @@ def run():
     pass
 
 if __name__ == '__main__':
-    generate_features_shap("00000000000000000000000011445301", "350139")
-    # recommend_list = get_recommend_list("00000000000000000000000011445301", "530246")
+    # generate_features_shap("00000000000000000000000011445301", "420202", delivery_count=5000)
+    # recommend_list = get_recommend_list("00000000000000000000000011445301", "420202")
     # recommend_list = pd.DataFrame(recommend_list)
-    # recommend_list.to_csv("./data/recommend_list.csv", index=False, encoding="utf-8-sig")
+    # recommend_list.to_csv("./data/recommend_list.csv", index=False, encoding="utf-8-sig")
+    data = dao.get_order_by_cust("00000000000000000000000011445301", "445323105795")
+    data = data.groupby(["cust_code", "product_code", "product_name"], as_index=False)["sale_qty"].sum()
+    data.to_csv("./data/cust.csv", index=False)

+ 8 - 1
models/rank/gbdt_lr_inference.py

@@ -1,3 +1,4 @@
+import gc
 import joblib
 # from dao import Redis, get_product_by_id, get_custs_by_ids, load_cust_data_from_mysql
 from database import RedisDatabaseHelper, MySqlDao
@@ -118,8 +119,13 @@ class GbdtLrModel:
         batch_size = 500  # 可根据内存调整
         
         # 创建临时内存映射文件
-        temp_dir = tempfile.mkdtemp()
+        # temp_dir = tempfile.mkdtemp()
+        temp_dir = "./data/tmp"
         temp_file = os.path.join(temp_dir, "shap_interaction_temp.dat")
+        if os.path.exists(temp_dir):
+            os.remove(temp_file)
+        else:
+            os.makedirs(temp_dir)
         
         try:
             # 预创建内存映射文件
@@ -208,6 +214,7 @@ class GbdtLrModel:
             # 清理临时文件
             try:
                 del fp  # 必须先删除内存映射对象
+                gc.collect()
                 os.remove(temp_file)
                 os.rmdir(temp_dir)
             except Exception as e:

+ 61 - 5
utils/result_process.py

@@ -28,7 +28,7 @@ def split_relation_subtable(data, filter_dict, save_dir):
     for name, sub_data in sub_tables.items():
         sub_data.to_csv(os.path.join(save_dir, f"{name}.csv"), index=False, encoding='utf-8-sig')
         
-def generate_report(data, filter_dict, save_dir):
+def generate_report(city_uuid, data, filter_dict, recommend_data, delivery_count, save_dir):
     """根据总表筛选结果"""
     # 1. 筛选商户相关性排序结果
     data = filter_data(data, filter_dict).copy()
@@ -45,12 +45,68 @@ def generate_report(data, filter_dict, save_dir):
         for key, value in filter_dict.items():
             if key != 'product_code':
                 f.write(f"{ImportanceFeaturesMap.PRODUCT_FEATRUES_MAP[key]}, {value}\n")
+                
+    # 3. 生成推荐报告
+    recommend_report = generate_recommend_report(city_uuid, recommend_data, delivery_count)
+    recommend_report.to_csv(os.path.join(save_dir, "recommend_report.csv"), index=False, encoding="utf-8-sig")
     
-        
+def generate_recommend_report(city_uuid, recommend_data, delivery_count):
+    recommend_data = pd.DataFrame(recommend_data)
+    
+    recpmmend_list = recommend_data["cust_code"].to_list()
+    recommend_cust_info = dao.get_cust_by_ids(city_uuid, recpmmend_list)
+    
+    cust_ids = recommend_cust_info.set_index("BB_RETAIL_CUSTOMER_CODE")
+    recommend_data = recommend_data.join(cust_ids, on="cust_code", how="inner")
+    recommend_data = recommend_data[["cust_code", "BB_RETAIL_CUSTOMER_NAME", "recommend_score"]]
+   # 1. 计算每个商户的理论应得数量(带小数)
+    recommend_data["delivery_float"] = (
+        recommend_data["recommend_score"] / recommend_data["recommend_score"].sum() * delivery_count
+    )
+
+    # 2. 向下取整得到基础配额
+    recommend_data["delivery_count"] = recommend_data["delivery_float"].astype(int)
+
+    # 3. 计算余数并排序
+    recommend_data["remainder"] = recommend_data["delivery_float"] - recommend_data["delivery_count"]
+    recommend_data = recommend_data.sort_values("remainder", ascending=False)
+
+    # 4. 将剩余配额按余数从大到小分配
+    remaining = delivery_count - recommend_data["delivery_count"].sum()
+    recommend_data.iloc[:remaining, recommend_data.columns.get_loc("delivery_count")] += 1
+    
+    recommend_data = recommend_data.drop(columns=["delivery_float", "remainder"])
+    recommend_data = recommend_data.reset_index()
+    # 5. 按recommend_score从大到小重新排序
+    recommend_data = recommend_data.sort_values("index")
+    recommend_data = recommend_data.rename(columns={"index": "推荐序号", "BB_RETAIL_CUSTOMER_NAME": "商户名称", "recommend_score": "匹配评分", "delivery_count": "建议投放量(条)"})
+    recommend_data["推荐序号"] = recommend_data["推荐序号"] + 1
+    
+    return recommend_data
+       
 def get_cust_list_from_history_order(city_uuid, product_code):
+    # 获取订单数据并处理
     order_data = dao.get_order_by_product(city_uuid, product_code)
-    return order_data
+    order_data = order_data[["cust_code", "cust_name", "product_code", "product_name", "sale_qty", "sale_amt"]]
+    
+    # 确保cust_code是字符串类型
+    order_data["cust_code"] = order_data["cust_code"].astype(str)
+    
+    order_data = order_data.groupby(["cust_code", "cust_name", "product_code", "product_name"])[["sale_qty", "sale_amt"]].sum().reset_index()
+    order_data = order_data.sort_values("sale_qty", ascending=False)
+    
+    # 读取推荐数据
+    recommend_data = pd.read_csv('./data/recommend_report.csv')
+    
+    # 确保recommend_data中的cust_code也是字符串类型
+    recommend_data["cust_code"] = recommend_data["cust_code"].astype(str)
+    cust_ids = recommend_data.set_index("cust_code")
+    
+    # 执行合并操作
+    merge_data = order_data.join(cust_ids, on="cust_code", how="inner")
+    merge_data = merge_data[["cust_code", "cust_name", "product_code", "product_name", "sale_qty", "sale_amt", "推荐序号", "匹配评分"]]
+    return merge_data
         
 if __name__ == "__main__":
-    order_data = get_cust_list_from_history_order("00000000000000000000000011445301", "350139")
-    order_data.to_csv("./data/history.csv", index=False)
+    order_data = get_cust_list_from_history_order("00000000000000000000000011445301", "420202")
+    order_data.to_csv("./data/eval.csv", index=False)