今天介绍一个实际问题的解决,这个问题来自于一个真实的案例。在解决过程中,我们使用了一个VLOOKUP函数的新技巧。 我们进行数据匹配的场景可以分为三类:
其中,前两种都很简单,只是VLOOKUP函数(或其他LOOKUP函数)最后一个参数的设置问题。 但是第三种匹配没有类似的简单办法。 比如,我们今天需要处理的问题就是这样的: 左表是客户简称,右边是客户名称,需要将客户简称和客户名称对应起来。 对应的原则是不言而明的,就像上图中相同颜色对应的条目一样。 但是仔细分析就会发现,这些能对应上的条目各有各的特殊情况:
如果仔细分析完整的数据,还可以看到更多的细分情况,比如错别字,漏字等等。 在上面的各种情况中,除了第一种情况可以使用VLOOKUP的通配符匹配外,其余的都没有办法直接解决。 一般来说,我们面临的所有现实问题都是这样的: 看上去是个匹配问题,应该用VLOOKUP之类的函数,但是你会的函数肯定解决不了。 其实,基本上现实问题都需要我们从两个方向努力,一方面对问题进行分解和变形,希望能用上我们已经掌握的技术,另一方面需要我们调动我们的知识积累,组合这些知识,看看是否可以将其用于解决分解和变形之后的问题。 根据上面的分析,我们可以将前两类归为一类:简称在全称中完全出现,但是不一定是连续出现。 后两类可以归为一类:简称在全称中部分出现。 对于“博众宏业 - 湖北省博众宏业商贸有限公司”类型的匹配来说,我们可以使用通配符匹配找到类似的结果: =VLOOKUP(“*” & A2 & “*”, $F$2:$F$20, 1, 0) 那么,如果变成“博众宏业 - 博众湖北省宏业商贸有限公司”的匹配模式,我们能否用通配符匹配的方式来完成呢? 更进一步,我们能否在完成这种匹配的同时,也可以支持“博众宏业 - 湖北省博众宏业商贸有限公司”类型的匹配呢? 可以的!!! 只要我们将“博众宏业”变成“博*众*宏*业”,就完全可以使用通配符匹配了。 这就要求我们使用公式完成从“博众宏业”到“博*众*宏*业”的转换。 只要看过我们前面讲过的各种LET函数的案例文章,或者函数式编程的文章和视频,这都不在话下。 完整公式如下: =LET( name1, A2, names2, $F$2:$F$20, name1arr, MID(name1, SEQUENCE(LEN(name1)),1), name1exp,"*"& TEXTJOIN("*",1, name1arr) & "*", IFERROR(VLOOKUP(name1exp, names2,1,0),"")) 其中,
这是匹配后的结果,可以看到匹配成功了很多条目,这些条目完全符合我们的预期。 对于那些没有匹配成功的条目,一定是简称没有全部在全称里出现。此时我们一定要了解,再好的算法也只能做到尽可能匹配成功,而且,最终必须依靠人工才更加准确。我们能做的就是在人工判断时尽可能地提供方便。 所以,我们将第二匹配尝试定位为: 推荐匹配结果 因为我们无法确认匹配出的结果是否是真正的结果。所以作为推荐供人工选择确认。 至于方法,就用到了我们前面介绍的LCS(最长公共子串,具体请参见Excel这个函数功能竟然暗合孙子兵法 - 详说递归函数:什么是递归?递归能干什么?递归怎么做?): 对于给定的简称name1,计算其与全称列表中的每一个全称ns1的LCS长度,并返回具有最长LCS的那个全称ns1。 比如,对于“绵阳永贞”,
其中LCS最长为2,所以返回“四川永贞商贸有限公司”作为推荐结果。 =LET( name1, A2, names2, $F$2:$F$498, matchname, REDUCE("",names2, LAMBDA(acc,a, IF( LCSLENGTH(a,name1)>LCSLENGTH(acc,name1), a, acc ) ) ), matchname) 其中,
结果如下: 可以看到,第二匹配实际完全包含了第一匹配。可见第二匹配从原理上涵盖了VLOOKUP模糊匹配的结果。 既然第二匹配涵盖了第一匹配,能否只使用第二匹配呢? 一般来说,这么做是不合适的。原因有二:
我们可以使用下面的公式: =IF(B7<>"","", LET( name1, A7, names2, $F$2:$F$498, matchname, REDUCE("",names2, LAMBDA(acc,a, IF( LCSLENGTH(a,name1)>LCSLENGTH(acc,name1), a, acc ) ) ), matchname )) 只是在前一个公式外面套用了IF,结果如下: 只要人工浏览第二匹配并删除不确定的即可。 关于这个问题,最后再说几点:
详细解释请看视频 |
|