Commit 8095cb00634aae042fa78f01cd2dda629e6f288c

Authored by tangwang
1 parent 5b954396

add cos sim

hot/README.md deleted
... ... @@ -1,85 +0,0 @@
1   -# 热门书籍索引生成项目
2   -
3   -## 项目简介
4   -本项目旨在根据机构的阅读行为数据(reading_time埋点数据)生成热门书籍索引,通过多种方法统计不同维度下的用户访问(UV)数据。项目支持基于机构(tenant)、机构所属行业(tenant_type)及书籍标签(tag)(包括category1和category2,当成tag同等处理)等不同维度进行统计和排名,从而生成热门书籍清单。并带有自动更新的软链接以方便外部访问。
5   -
6   -
7   -## 文件结构
8   -- `index_generation.py`:主程序代码,包含数据加载、UV处理、书单生成和输出等主要功能。
9   -- `logs/`:日志文件存放目录。
10   -- `output/`:程序生成的书单输出目录。
11   -
12   -## 输入数据
13   -### 1. 书籍属性数据 (`all_books.json`)
14   -- **路径**:`CONFIG['books_path']`
15   -- **内容**:每行包含一个书籍的 JSON 数据,主要字段为:
16   - - `id`:书籍ID。
17   - - `merged_tags`:书籍相关的标签列表,用逗号分隔。
18   -
19   -### 2. 机构所属行业数据 (`tenants.json`)
20   -- **路径**:`CONFIG['tenants_path']`
21   -- **内容**:每行包含一个机构的 JSON 数据,主要字段为:
22   - - `id`:机构ID。
23   - - `tenant_type`:机构所属行业类型。
24   -
25   -### 3. 阅读行为数据 (`reading_time.json`)
26   -- **路径**:`CONFIG['base_dir']` 下的文件夹,文件名格式为 `reading_time.json.YYYYMMDD`。
27   -- **内容**:每行包含一个阅读行为的 JSON 数据,主要字段为:
28   - - `user_id`:用户ID。
29   - - `book_id`:书籍ID。
30   - - `tenant_id`:机构ID。
31   -
32   -## 输出数据
33   -输出数据为生成的热门书籍列表,每个文件包含按指定维度统计的前 `N` 个书籍的排名结果:
34   -- 文件输出路径:`CONFIG['output_dir']`
35   -- 文件名格式:`<prefix>_<current_date>.txt`,并生成软链接至 `<prefix>.txt`。
36   -- 输出内容示例:`tenant_id book_id1:uv_count1,book_id2:uv_count2,...`
37   -
38   -### 输出文件类型
39   -1. `tenant_booklist.txt`:按机构(tenant)统计的热门书籍列表。
40   -2. `tenant_type_booklist.txt`:按机构所属行业(tenant_type)统计的热门书籍列表。
41   -3. `tag_booklist.txt`:按标签(tag)统计的热门书籍列表。
42   -
43   -## 配置参数
44   -### `CONFIG` 说明
45   -- `base_dir`:阅读数据文件的目录。
46   -- `books_path`:书籍属性数据文件路径。
47   -- `tenants_path`:机构所属行业数据文件路径。
48   -- `output_dir`:输出目录路径。
49   -- `days`:用于选择最近 `days` 天内的数据文件。
50   -- `top_n`:生成前 `N` 个热门书籍。
51   -- `tenant_type_ratio`:用于在机构数据不足时融合所属行业数据的权重比例。
52   -- `use_simple_uv_processing`:
53   - - `True`:累加每天的 UV。
54   - - `False`:以数据周期内总 UV 统计为准。
55   -
56   -## 计算逻辑
57   -1. **数据加载**
58   - - 使用 `load_books_data()` 和 `load_tenants_data()` 分别加载书籍和机构的基本信息,确保各个 ID 均为字符串。
59   - - 使用 `get_recent_files()` 获取最近 `days` 天的阅读数据文件列表。
60   -
61   -2. **UV 数据处理**
62   - - `process_reading_data()`:简单 UV 统计,每条记录中的用户访问量直接累加。
63   - - `process_reading_data_by_uv()`:用户 UV 去重统计,计算某书籍在一天内的 UV 数量。
64   - - `CONFIG['use_simple_uv_processing']` 用于决定是否使用简单的累加逻辑。
65   -
66   -3. **数据融合**
67   - - 使用 `merge_tenant_uv_with_type_uv()` 将机构的 UV 数据与其所属行业的 UV 数据按比例进行融合,减小数据量较小的机构所带来的统计偏差。
68   -
69   -4. **生成书单**
70   - - `generate_top_booklist()` 根据 UV 统计数据生成指定维度的前 `N` 本热门书籍列表。
71   - - 生成的书单文件分别保存机构、机构所属行业、标签维度的热门书籍排名。
72   -
73   -5. **输出与软链接**
74   - - 使用 `write_output()` 将生成的书单写入指定文件,并更新软链接到最新文件。
75   -
76   -## 日志
77   -程序的所有日志信息输出至 `logs/index_generation.log`,主要记录数据加载、文件处理、UV 统计、文件写入等步骤的成功与错误信息,以便跟踪和排查问题。
78   -
79   -## 运行方法
80   -在终端中执行以下命令来运行主程序:
81   -```bash
82   -python main.py
83   -# 或者
84   -sh run.sh
85   -```
86 0 \ No newline at end of file
hot/main.py deleted
... ... @@ -1,261 +0,0 @@
1   -import os
2   -import json
3   -import glob
4   -import logging
5   -from collections import defaultdict, Counter
6   -from datetime import datetime, timedelta
7   -import shutil
8   -
9   -# 设置日志配置
10   -logging.basicConfig(
11   - filename='logs/index_generation.log',
12   - level=logging.INFO,
13   - format='%(asctime)s - %(levelname)s - %(message)s'
14   -)
15   -
16   -# 配置超参
17   -CONFIG = {
18   - 'base_dir': '../fetch_data/data/',
19   - 'books_path': '../fetch_data/meta_data/all_books.json',
20   - 'tenants_path': '../fetch_data/meta_data/tenants.json',
21   - 'output_dir': './output',
22   - 'days': 30, # 天数,用于获取最近的文件
23   - 'top_n': 1000, # 生成的前 N 个书单
24   - 'tenant_type_ratio': 0.01, # 机构和所属行业融合的比例。可以解决机构的冷启动问题。机构内的行为数据越少,受到行业的影响越大。
25   - 'use_simple_uv_processing': True # 是否使用简单UV处理逻辑
26   - # 配置为True:则book的read UV统计规则为 每一天的UV的累加,
27   - # 配置为False:则book的read UV统计规则为统计范围内所有天的UV,该方法更多的收到运营配置的曝光的影响,
28   - # 默认为True
29   -}
30   -
31   -def load_json_files(path_pattern):
32   - """根据通配符加载 JSON 文件"""
33   - files = glob.glob(path_pattern)
34   - data = []
35   - for file in files:
36   - with open(file, 'r', encoding='utf-8') as f:
37   - for line in f:
38   - line = line.strip()
39   - if not line:
40   - continue
41   - try:
42   - data.append(json.loads(line))
43   - except json.JSONDecodeError:
44   - logging.error(f"Failed to parse JSON line in {file}: {line}")
45   - return data
46   -
47   -def load_books_data(books_path):
48   - """加载书籍属性词典,并将所有ID转换为字符串"""
49   - books_data = {}
50   - with open(books_path, 'r', encoding='utf-8') as f:
51   - for line in f:
52   - line = line.strip()
53   - if not line:
54   - continue
55   - book = json.loads(line)
56   -
57   - tags = book.get('merged_tags', '')
58   - category1 = book.get('category1', '')
59   - category2 = book.get('category2', '')
60   - combined_tags = ','.join(filter(lambda x: x not in [None, ''], [tags, category1, category2]))
61   - books_data[str(book['id'])] = combined_tags # 将book['id']转换为字符串
62   -
63   - logging.info(f"Loaded {len(books_data)} books from {books_path}")
64   - return books_data
65   -
66   -def load_tenants_data(tenants_path):
67   - """加载机构所属行业词典,并将所有ID转换为字符串"""
68   - tenants_data = {}
69   - with open(tenants_path, 'r', encoding='utf-8') as f:
70   - for line in f:
71   - line = line.strip()
72   - if not line:
73   - continue
74   - tenant = json.loads(line)
75   - tenant_type = tenant.get('tenant_type', '')
76   - if not tenant_type:
77   - tenant_type = ''
78   - tenants_data[str(tenant['id'])] = tenant_type # 将tenant['id']转换为字符串
79   - logging.info(f"Loaded {len(tenants_data)} tenants from {tenants_path}")
80   - return tenants_data
81   -
82   -def get_recent_files(base_dir, days=30):
83   - """获取最近 days 天的文件"""
84   - today = datetime.today()
85   - recent_files = []
86   - for i in range(days):
87   - date_str = (today - timedelta(days=i)).strftime('%Y%m%d')
88   - path_pattern = os.path.join(base_dir, f'reading_time.json.{date_str}')
89   - recent_files.extend(glob.glob(path_pattern))
90   - logging.info(f"Found {len(recent_files)} files for the last {days} days")
91   - return recent_files
92   -
93   -def process_reading_data_by_uv(reading_files, books_data, tenants_data):
94   - """使用用户UV数据处理阅读数据"""
95   - tenant_uv = defaultdict(lambda: defaultdict(set)) # 使用集合来去重
96   - tenant_type_uv = defaultdict(lambda: defaultdict(set)) # 使用集合来去重
97   - tag_uv = defaultdict(lambda: defaultdict(set)) # 使用集合来去重
98   -
99   - for file in reading_files:
100   - with open(file, 'r', encoding='utf-8') as f:
101   - for line in f:
102   - try:
103   - record = json.loads(line.strip())
104   - user_id = str(record.get('user_id', '')) # 将user_id转换为字符串
105   - book_id = str(record.get('book_id', '')) # 将book_id转换为字符串
106   - tenant_id = str(record.get('tenant_id', '')) # 将tenant_id转换为字符串
107   -
108   - if not book_id or not tenant_id or not user_id:
109   - continue
110   -
111   - tenant_uv[tenant_id][book_id].add(user_id)
112   - tenant_type = tenants_data.get(tenant_id, '') # tenant_id已经是字符串
113   - tenant_type_uv[tenant_type][book_id].add(user_id)
114   -
115   - tags = books_data.get(book_id, '').split(',')
116   - for tag in tags:
117   - if tag:
118   - tag_uv[tag][book_id].add(user_id)
119   -
120   - except json.JSONDecodeError:
121   - logging.error(f"Failed to parse JSON line in {file}: {line}")
122   -
123   - # 转换为UV数量,即集合中user_id的数量
124   - tenant_uv_count = {tenant: Counter({book: len(users) for book, users in books.items()})
125   - for tenant, books in tenant_uv.items()}
126   - tenant_type_uv_count = {tenant_type: Counter({book: len(users) for book, users in books.items()})
127   - for tenant_type, books in tenant_type_uv.items()}
128   - tag_uv_count = {tag: Counter({book: len(users) for book, users in books.items()})
129   - for tag, books in tag_uv.items()}
130   -
131   - logging.info(f"Processed reading data, total tenants: {len(tenant_uv_count)}, tenant types: {len(tenant_type_uv_count)}, tags: {len(tag_uv_count)}")
132   -
133   - return tenant_uv_count, tenant_type_uv_count, tag_uv_count
134   -
135   -def process_reading_data(reading_files, books_data, tenants_data):
136   - """使用简单的UV累加逻辑处理阅读数据"""
137   - tenant_uv = defaultdict(Counter)
138   - tenant_type_uv = defaultdict(Counter)
139   - tag_uv = defaultdict(Counter)
140   -
141   - for file in reading_files:
142   - with open(file, 'r', encoding='utf-8') as f:
143   - for line in f:
144   - try:
145   - record = json.loads(line.strip())
146   - user_id = str(record.get('user_id', '')) # 将user_id转换为字符串
147   - book_id = str(record.get('book_id', '')) # 将book_id转换为字符串
148   - tenant_id = str(record.get('tenant_id', '')) # 将tenant_id转换为字符串
149   -
150   - if not book_id or not tenant_id:
151   - continue
152   -
153   - tenant_uv[tenant_id][book_id] += 1
154   - tenant_type = tenants_data.get(tenant_id, '') # tenant_id已经是字符串
155   - tenant_type_uv[tenant_type][book_id] += 1
156   -
157   - tags = books_data.get(book_id, '').split(',')
158   - for tag in tags:
159   - if tag:
160   - tag_uv[tag][book_id] += 1
161   -
162   - except json.JSONDecodeError:
163   - logging.error(f"Failed to parse JSON line in {file}: {line}")
164   -
165   - logging.info(f"Processed reading data, total tenants: {len(tenant_uv)}, tenant types: {len(tenant_type_uv)}, tags: {len(tag_uv)}")
166   -
167   - return tenant_uv, tenant_type_uv, tag_uv
168   -
169   -def generate_top_booklist(counter_dict, top_n=1000):
170   - """生成排序后的前 top_n booklist"""
171   - result = {}
172   - for key, counter in counter_dict.items():
173   - top_books = counter.most_common(top_n)
174   - if not key or len(top_books) == 0:
175   - continue
176   - result[key] = ','.join([f'{bid}:{uv}' for bid, uv in top_books])
177   - return result
178   -
179   -def write_output(data, output_dir, prefix, current_date):
180   - """写入输出文件,并生成软链接到 output 目录下"""
181   - try:
182   - output_file_path = os.path.join(output_dir, f'{prefix}_{current_date}.txt')
183   - output_file_link = os.path.join(output_dir, f'{prefix}.txt')
184   -
185   - if not os.path.exists(output_dir):
186   - os.makedirs(output_dir)
187   -
188   - with open(output_file_path, 'w', encoding='utf-8') as f:
189   - for key, booklist in data.items():
190   - key.replace('\t', ' ')
191   - if not key or not booklist:
192   - continue
193   - f.write(f"{key}\t{booklist}\n")
194   -
195   - logging.info(f"Output written to {output_file_path}")
196   -
197   - if os.path.islink(output_file_link) or os.path.exists(output_file_link):
198   - os.remove(output_file_link)
199   -
200   - os.symlink(os.path.basename(output_file_path), output_file_link)
201   - logging.info(f"Symlink created at {output_file_link} pointing to {output_file_path}")
202   -
203   - except Exception as e:
204   - logging.error(f"Error writing output or creating symlink: {str(e)}")
205   -
206   -def merge_tenant_uv_with_type_uv(tenant_uv, tenant_type_uv, tenants_data, ratio=CONFIG['tenant_type_ratio']):
207   - """合并 tenant 的 UV 统计和其所属 tenant_type 的 UV 统计结果
208   -
209   - 融合的目的:通过融合机构所属行业的UV数据,平滑处理小机构数据不足的情况,给予它们更多的行业UV权重 ,避免因数据量小而导致的统计偏差。
210   -
211   - ratio 参数控制行业 UV 统计数据在融合过程中所占的权重比例。较高的比例表示行业数据的影响较大,较低的比例则表示单个机构的数据占主导地位。
212   - """
213   - merged_tenant_uv = defaultdict(Counter)
214   -
215   - for tenant_id, books_counter in tenant_uv.items():
216   - # 获取该 tenant 的 tenant_type
217   - tenant_type = tenants_data.get(tenant_id, '')
218   -
219   - # 获取该 tenant_type 下的 UV 统计
220   - tenant_type_counter = tenant_type_uv.get(tenant_type, Counter())
221   -
222   - # 合并 tenant 自身的 UV 统计和 tenant_type 的 UV 统计结果(乘以比例系数)
223   - for book_id, uv_count in books_counter.items():
224   - tenant_type_uv_adjusted = int(tenant_type_counter.get(book_id, 0) * ratio)
225   - merged_tenant_uv[tenant_id][book_id] = uv_count + tenant_type_uv_adjusted
226   -
227   - logging.info(f"Merged tenant UV with tenant type UV using ratio {ratio}")
228   - return merged_tenant_uv
229   -
230   -def main():
231   - # 获取当前日期
232   - current_date = datetime.today().strftime('%Y%m%d')
233   -
234   - # 加载书籍和机构数据
235   - books_data = load_books_data(CONFIG['books_path'])
236   - tenants_data = load_tenants_data(CONFIG['tenants_path'])
237   -
238   - # 获取最近配置的天数的阅读数据文件
239   - reading_files = get_recent_files(CONFIG['base_dir'], days=CONFIG['days'])
240   -
241   - # 根据配置选择UV处理逻辑
242   - if CONFIG['use_simple_uv_processing']:
243   - tenant_uv, tenant_type_uv, tag_uv = process_reading_data(reading_files, books_data, tenants_data)
244   - else:
245   - tenant_uv, tenant_type_uv, tag_uv = process_reading_data_by_uv(reading_files, books_data, tenants_data)
246   -
247   - # 合并 tenant UV 和 tenant_type UV(使用配置的比例)
248   - merged_tenant_uv = merge_tenant_uv_with_type_uv(tenant_uv, tenant_type_uv, tenants_data, ratio=CONFIG['tenant_type_ratio'])
249   -
250   - # 生成前N本书的书单
251   - tenant_booklist = generate_top_booklist(merged_tenant_uv, top_n=CONFIG['top_n'])
252   - tenant_type_booklist = generate_top_booklist(tenant_type_uv, top_n=CONFIG['top_n'])
253   - tag_booklist = generate_top_booklist(tag_uv, top_n=CONFIG['top_n'])
254   -
255   - # 写入输出文件并生成软链接
256   - write_output(tenant_booklist, CONFIG['output_dir'], 'tenant_booklist', current_date)
257   - write_output(tenant_type_booklist, CONFIG['output_dir'], 'tenant_type_booklist', current_date)
258   - write_output(tag_booklist, CONFIG['output_dir'], 'tag_booklist', current_date)
259   -
260   -if __name__ == '__main__':
261   - main()
hot/run.sh deleted
... ... @@ -1,7 +0,0 @@
1   -mkdir -p output
2   -mkdir -p logs
3   -python3 main.py
4   -
5   -# 清理output目录下365天以前的文件
6   -find output/ -type f -mtime +365 -exec rm -f {} \;
7   -find logs/ -type f -mtime +180 -exec rm -f {} \;
item_sim.py deleted
... ... @@ -1,88 +0,0 @@
1   -import pandas as pd
2   -import math
3   -from collections import defaultdict
4   -from sqlalchemy import create_engine
5   -from db_service import create_db_connection
6   -import argparse
7   -
8   -def clean_text_field(text):
9   - if pd.isna(text):
10   - return ''
11   - # 移除换行符、回车符,并替换其他可能导致CSV格式问题的字符
12   - return str(text).replace('\r', ' ').replace('\n', ' ').replace('"', '""').strip()
13   -
14   -# 数据库连接配置
15   -host = 'selectdb-cn-wuf3vsokg05-public.selectdbfe.rds.aliyuncs.com'
16   -port = '9030'
17   -database = 'datacenter'
18   -username = 'readonly'
19   -password = 'essa1234'
20   -
21   -# 创建数据库连接
22   -engine = create_db_connection(host, port, database, username, password)
23   -
24   -# SQL 查询 - 获取用户点击序列
25   -sql_query = """
26   -SELECT
27   - DATE_FORMAT(se.create_time, '%%Y-%%m-%%d') AS date,
28   - se.anonymous_id AS user_id,
29   - se.item_id,
30   - pgs.name AS item_name
31   -FROM
32   - sensors_events se
33   -LEFT JOIN prd_goods_sku pgs ON se.item_id = pgs.id
34   -WHERE
35   - se.event IN ('contactFactory', 'addToPool', 'addToCart')
36   - AND se.create_time >= '2025-04-01'
37   -ORDER BY
38   - se.anonymous_id,
39   - se.create_time;
40   -"""
41   -
42   -# 执行 SQL 查询并将结果加载到 pandas DataFrame
43   -df = pd.read_sql(sql_query, engine)
44   -
45   -# 处理点击序列,计算共现关系
46   -cooccur = defaultdict(lambda: defaultdict(int))
47   -freq = defaultdict(int)
48   -
49   -# 按用户和日期分组处理点击序列
50   -for (user_id, date), group in df.groupby(['user_id', 'date']):
51   - items = group['item_id'].tolist()
52   - unique_items = set(items)
53   -
54   - # 更新频率统计
55   - for item in unique_items:
56   - freq[item] += 1
57   -
58   - # 更新共现关系
59   - for i in range(len(items)):
60   - for j in range(i + 1, len(items)):
61   - item1, item2 = items[i], items[j]
62   - if item1 != item2:
63   - cooccur[item1][item2] += 1
64   - cooccur[item2][item1] += 1
65   -
66   -# 计算余弦相似度
67   -result = {}
68   -for item1 in cooccur:
69   - sim_scores = []
70   - for item2 in cooccur[item1]:
71   - numerator = cooccur[item1][item2]
72   - denominator = math.sqrt(freq[item1]) * math.sqrt(freq[item2])
73   - if denominator != 0:
74   - score = numerator / denominator
75   - sim_scores.append((item2, score))
76   - sim_scores.sort(key=lambda x: -x[1]) # 按分数排序
77   - result[item1] = sim_scores
78   -
79   -# 创建item_id到name的映射
80   -item_name_map = dict(zip(df['item_id'], df['item_name']))
81   -
82   -# 输出相似商品
83   -for item_id, sims in result.items():
84   - item_name = item_name_map.get(item_id, 'Unknown')
85   - # 只取前8个最相似的商品
86   - top_sims = sims[:8]
87   - sim_str = ','.join([f'{item_name_map.get(sim_id, "Unknown")}:{score:.4f}' for sim_id, score in top_sims])
88   - print(f'{item_name}\t{sim_str}')
tag_sim.py deleted
... ... @@ -1,81 +0,0 @@
1   -import pandas as pd
2   -import math
3   -from collections import defaultdict
4   -from sqlalchemy import create_engine
5   -from db_service import create_db_connection
6   -import argparse
7   -
8   -def clean_text_field(text):
9   - if pd.isna(text):
10   - return ''
11   - # 移除换行符、回车符,并替换其他可能导致CSV格式问题的字符
12   - return str(text).replace('\r', ' ').replace('\n', ' ').replace('"', '""').strip()
13   -
14   -bpms_host = '120.76.244.158'
15   -bpms_port = '3325'
16   -bpms_database = 'bpms'
17   -bpms_username = 'PRD_M1_190311'
18   -bpms_password = 'WTF)xdbqtW!4gwA7'
19   -
20   -# 创建数据库连接
21   -engine = create_db_connection(bpms_host, bpms_port, bpms_database, bpms_username, bpms_password)
22   -
23   -# SQL 查询
24   -sql_query = """
25   -SELECT
26   - sp.code AS `PO单号`,
27   - psm.name AS `区域`,
28   - bb.code AS `客户编码`,
29   - GROUP_CONCAT(pc_1.name) AS `商品信息`,
30   - MIN(spi.order_time) AS `下单货时间`
31   -FROM sale_po sp
32   -INNER JOIN sale_po_item spi ON sp.id = spi.po_id
33   -LEFT JOIN buy_buyer bb ON bb.id = sp.buyer_id
34   -LEFT JOIN prd_goods pg ON pg.id = spi.spu_id
35   -LEFT JOIN prd_category AS pc_1 ON pc_1.id = SUBSTRING_INDEX(SUBSTRING_INDEX(pg.category_id, '.', 2), '.', -1)
36   -LEFT JOIN pub_sale_market_setting psms ON psms.country_code = bb.countries
37   -LEFT JOIN pub_sale_market psm ON psms.sale_market_id = psm.id
38   -WHERE spi.quantity > 0
39   - AND spi.is_delete = 0
40   - AND bb.is_delete = 0
41   -GROUP BY sp.code, psm.name, bb.code;
42   -"""
43   -
44   -# 执行 SQL 查询并将结果加载到 pandas DataFrame
45   -df = pd.read_sql(sql_query, engine)
46   -
47   -# 处理商品信息,分割并去重
48   -cooccur = defaultdict(lambda: defaultdict(int))
49   -freq = defaultdict(int)
50   -
51   -for _, row in df.iterrows():
52   - # Handle None values in 商品信息
53   - if pd.isna(row['商品信息']):
54   - continue
55   - categories = [cat.strip() for cat in str(row['商品信息']).split(',') if cat.strip()]
56   - unique_cats = set(categories)
57   - for c1 in unique_cats:
58   - freq[c1] += 1
59   - for c2 in unique_cats:
60   - if c1 != c2:
61   - cooccur[c1][c2] += 1
62   -
63   -# 计算余弦相似度
64   -result = {}
65   -for c1 in cooccur:
66   - sim_scores = []
67   - for c2 in cooccur[c1]:
68   - numerator = cooccur[c1][c2]
69   - denominator = math.sqrt(freq[c1]) * math.sqrt(freq[c2])
70   - if denominator != 0:
71   - score = numerator / denominator
72   - sim_scores.append((c2, score))
73   - sim_scores.sort(key=lambda x: -x[1]) # 按分数排序
74   - result[c1] = sim_scores
75   -
76   -# 输出相似分类
77   -for cat, sims in result.items():
78   - # 只取前8个最相似的分类
79   - top_sims = sims[:8]
80   - sim_str = ','.join([f'{sim_cat}:{score:.4f}' for sim_cat, score in top_sims])
81   - print(f'{cat}\t{sim_str}')