当前位置: 首页 > news >正文

WPS自定义公式,相似度匹配

借助豆包做的这个相似度匹配公式,vlookup公式的模糊匹配使用起来一言难尽,经测试1500对1500的匹配大概3分钟完成。使用方法:打开空白表,将代码复制到宏编辑器,保存为 .xlam文件,再加载到wps里面后续使用就跟普通公式一样使用了。

/** * 【自定义函数】多粒度精准相似度匹配(跨Sheet + 阈值过滤 + 显示最高匹配度) * 使用方法: * =mlooklup(查找值, 查找列) → 返回最优匹配 * =mlooklup(查找值, 查找列, 0.8) → 大于等于80%匹配度才返回 * =mlooklup(查找值, 查找列, 80%) → 同上 * =mlooklup(查找值, 查找列, 1.2) → 仅完全一致返回 * =mlooklup(查找值, 查找列, 0) → 不限制,返回最优 * @param {Range} target - 查找值所在单元格 * @param {Range} matchRange - 要匹配的列/区域(鼠标直接点选,支持跨Sheet) * @param {number|string} [threshold=null] - 匹配阈值(支持小数/百分比) * @returns {string} 最匹配结果 或 无匹配提示(含最高匹配度) */ function mlooklup(target, matchRange, threshold = null) { // ===================== 1. 获取匹配区域的工作表与列信息(跨Sheet核心) ===================== // 获取用户选中区域所在的工作表(不是当前活动表,保证跨表正常) const lookupSheet = matchRange.Worksheet; // 获取匹配区域所在的列号(自动识别,无需手动输入数字) const matchColNum = matchRange.Column; // 获取该列最后一行非空行号,全空时默认1,避免循环报错 const maxRow = lookupSheet.Cells(lookupSheet.Rows.Count, matchColNum).End(-4162).Row || 1; // ===================== 2. 初始化最优结果变量 ===================== let bestItem = null; // 存储最匹配的文本内容 let bestScore = 0; // 存储最高综合相似度(0~1) // ===================== 3. 处理查找值(格式统一) ===================== // 安全获取查找值,容错空值/无效对象 let targetValue = (target?.Value2 ?? "").toString().trim(); // 查找值为空,直接返回提示 if (!targetValue) return "目标值不能为空"; // 替换罗马数字:II→2,I→1 targetValue = replaceIIandI(targetValue); // 文本清洗:全角转半角 + 小写 + 去空格 const tClean = cleanText(targetValue); // ===================== 4. 遍历匹配列,计算相似度 ===================== // 从第2行开始遍历,跳过表头 for (let row = 2; row <= maxRow; row++) { try { // 获取当前行单元格内容 let cellValue = (lookupSheet.Cells(row, matchColNum).Value2 ?? "").toString().trim(); // 空值跳过 if (!cellValue) continue; // 统一格式:替换罗马数字 + 文本清洗 cellValue = replaceIIandI(cellValue); const cClean = cleanText(cellValue); // 核心:计算4维度综合相似度 const sim = getMultiSimilarity(tClean, cClean); // 更新最高分与最优匹配项 if (sim > bestScore) { bestScore = sim; bestItem = cellValue; } } catch (e) { // 单条数据异常,跳过并打印日志 console.log(`【行${row}】异常:${e.message}`); } } // ===================== 5. 阈值处理 + 最终返回 ===================== // 初始化阈值 let finalThreshold = 0; // 如果传入了阈值,处理格式(支持百分比 80% → 0.8) if (threshold !== null && threshold !== undefined && threshold !== "") { let val = threshold; // 字符串百分比处理 if (typeof val === "string" && val.includes("%")) { val = parseFloat(val.replace("%", "")) / 100; } finalThreshold = Number(val) || 0; } // 把最高相似度格式化为百分比(保留2位小数) const bestPercent = (bestScore * 100).toFixed(2); // 根据阈值规则返回结果 if (finalThreshold > 1) { // 阈值 >1 → 仅完全匹配(100%)才返回 return bestScore === 1 ? bestItem : `无匹配,最高匹配度 ${bestPercent}%`; } else if (finalThreshold <= 0) { // 阈值 ≤0 → 直接返回最优匹配 return bestItem || `无匹配,最高匹配度 ${bestPercent}%`; } else { // 0~1之间 → 达到阈值返回,否则提示无匹配 return bestScore >= finalThreshold ? bestItem : `无匹配,最高匹配度 ${bestPercent}%`; } } /** * 文本清洗函数:统一格式,消除干扰 * @param {string} str - 原始字符串 * @returns {string} 清洗后字符串(半角、小写、去空格) */ function cleanText(str) { // 全角字符转半角 const toHalf = s => s.replace(/[\uff00-\uffff]/g, ch => String.fromCharCode(ch.charCodeAt(0) - 65248)); // 全角转半角 → 小写 → 去首尾空格 return toHalf(str).toLowerCase().trim(); } /** * 替换罗马数字:II→2,I→1 * @param {string} str * @returns {string} */ function replaceIIandI(str) { return str.replace(/II/gi, "2").replace(/I/gi, "1"); } /** * 4维度综合相似度计算(高精度核心) * @param {string} t - 目标清洗后文本 * @param {string} c - 对比清洗后文本 * @returns {number} 综合得分 0~1 */ function getMultiSimilarity(t, c) { const charSim = charSimilarity(t, c); // 单字符匹配 const biSim = ngramSimilarity(t, c, 2); // 双字符组合 const triSim = ngramSimilarity(t, c, 3); // 三字符组合 const editSim = editDistanceSimilarity(t, c); // 编辑距离 // 四个维度等权平均 return (charSim + biSim + triSim + editSim) / 4; } /** * 单字符重合相似度(Jaccard) */ function charSimilarity(a, b) { const setA = new Set(a.split('')); const setB = new Set(b.split('')); const intersect = [...setA].filter(x => setB.has(x)).length; const union = new Set([...setA, ...setB]).size; return union === 0 ? 0 : intersect / union; } /** * N-gram连续子串相似度(2-gram、3-gram) */ function ngramSimilarity(a, b, n) { // 生成连续N字符组合 function getNgrams(s, n) { const ngrams = []; for (let i = 0; i <= s.length - n; i++) { ngrams.push(s.slice(i, i + n)); } return ngrams; } const g1 = getNgrams(a, n); const g2 = getNgrams(b, n); const set1 = new Set(g1); const set2 = new Set(g2); const intersect = [...set1].filter(x => set2.has(x)).length; const union = new Set([...set1, ...set2]).size; return union === 0 ? 0 : intersect / union; } /** * 编辑距离相似度(Levenshtein) * 衡量两个字符串的差异程度 */ function editDistanceSimilarity(a, b) { const lenA = a.length; const lenB = b.length; const maxLen = Math.max(lenA, lenB); if (maxLen === 0) return 1; // 构建动态规划表 const dp = Array.from({ length: lenA + 1 }, () => Array(lenB + 1).fill(0)); for (let i = 0; i <= lenA; i++) dp[i][0] = i; for (let j = 0; j <= lenB; j++) dp[0][j] = j; // 计算最小修改次数 for (let i = 1; i <= lenA; i++) { for (let j = 1; j <= lenB; j++) { const cost = a[i - 1] === b[j - 1] ? 0 : 1; dp[i][j] = Math.min( dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost ); } } // 转为相似度 0~1 return 1 - dp[lenA][lenB] / maxLen; }
http://www.jsqmd.com/news/536129/

相关文章:

  • 亲测复盘|靠谱特殊膳食营养粉销售厂家推荐
  • 百川2-13B-4bits量化对比测试:OpenClaw在消费级显卡上的极限
  • 隐私优先方案:OpenClaw+nanobot本地化邮件处理助手
  • 2026年四川玻璃隔断选购指南:成都智能办公隔断源头工厂直供省钱攻略 - 精选优质企业推荐榜
  • 实战指南:如何用Mask R-CNN在iSAID数据集上提升航空影像分割效果(附调参技巧)
  • 入职培训系统怎么选?从需求到落地看这篇就够了
  • (一)前端,如此简单!---下载Nginx
  • 3个技巧教你解锁《艾尔登法环》帧率与视野:Elden Ring FPS Unlock And More完全指南
  • SEO_ 如何制定有效的SEO优化方案与执行步骤(232 )
  • 安全验证大升级!为什么说它正式迈入 Agentic 时代?
  • 如何利用OpenCode实现高效专业的AI驱动开发工作流?
  • 写作小白救星!9个一键生成论文工具:全行业通用测评+毕业论文+科研写作推荐
  • 实现Atmosphere固件与Switch 19.0.1系统适配:从兼容性评估到部署验证的工程化方案
  • OpenClaw权限管理:Qwen3-VL:30B在飞书中的访问控制实践
  • 万国数据是做什么的?撑起中国 AI 算力底座的算力基建龙头
  • 俄罗斯莫斯科电子烟展:跟团公司高性价比选择策略拆解
  • py每日spider案例之某website影视解析接口
  • 探索多思计组原理虚拟实验室:从全加器到串行进位加法器的实践之旅
  • 2025年SEVC SCI2区,一种基于变量分解与空间压缩的大规模优化算法,深度解析+性能实测
  • 2026.03.25(第一天)
  • 多模态实践:OpenClaw+Qwen3.5-9B实现截图内容分析与归档
  • 2026广东LED柔性灯带模切线路板源头厂家选择指南
  • FlashPatch终极指南:让Flash游戏在浏览器中重获新生
  • 从智能出行到智能家电,探路生态携智能空间全栈产品矩阵亮相AWE
  • 关于前端获取DOM节点的兼容IE6的代码封装
  • 家庭实验室应用:OpenClaw+Qwen3.5-9B管理智能家居
  • 招聘系统AI功能完整性Top10:用星级评级替代虚假分数,还原真实能力格局!
  • C++的std--bit_cast类型双关与字节序列重解释的安全转换
  • 如何用WoWmapper实现魔兽世界控制器完美映射:终极配置指南
  • 【都市小说推荐】草根美女谋房记:《兄弟,爱情》