杂记 › 技术笔记 › 两份名单里谁只在其中一份?交集差集并集怎么算

两份名单里谁只在其中一份?交集差集并集怎么算

两份名单对不上的时候,最费劲的不是比,而是"到底在比什么"。去年公司组织体检,行政给了我两份从表格另存出来的 txt:一份是报名接龙的,87 行;一份是人事系统导出的在册名单,92 行。两边人数差 5,谁没报、谁已经不在这份名单里,靠肉眼来回找大概要花整个下午。我第一次用交集差集把这三件事分开算,几分钟就有了结果,但紧接着又冒出一个新问题:算出来的"差异"里有三行其实只是多个一个空格。

先把三个词的方向说清楚

集合运算里最容易搞混的是差集,因为它有方向。A - B 的意思是"在 A 里、但不在 B 里",把 A 和 B 换一个位置,结果就是另外一批人。我当时把报名表当作 A、在册名单当作 B,那么 A - B 就是"报了名但不在册"(外部人员或者已离职),B - A 才是"在册但没报名"(真正要催的那批)。这两个结果完全不同,先想清楚哪一份是基准,再决定方向,顺序搞反了整件事就白做。

交集 A ∩ B 是两边都出现的条目,也就是"确认报了名且确实在册"的人;并集 A ∪ B 是把两份合起来、相同条目只留一个,用来确认"总共涉及多少个人"。并集是最容易被忽略却最能提醒我这几次数据有多乱的一个数——两份名单各 87 和 92 行,并集却是 95,说明实际只有 95 个人,而两边加起来有 179 行。

数字对得上,才算真的算完

我做完以后会做一次减法校验。集合运算的好处是结果之间有固定的数量关系,错了立刻能看出来。体检那次的四个数是自查表里那一栏:

口径数量怎么核对
集合 A(报名接龙)87 行原文件行数,含 2 行重复
集合 B(在册名单)92 行系统导出,无重复
交集 A ∩ B84 个两边都有的条目
差集 A - B3 个应等于 87 减 84
差集 B - A8 个应等于 92 减 84
并集 A ∪ B95 个应等于 87 加 92 减 84

因为 A 里那 2 行重复我在选项里勾选了去重,"报了多少人"其实是 85,而 A - B 里的 3 个是三个人名,两边都对得上我才往下走。这里有个坑:如果不去重,交集会按第一次出现留一个,但你会误以为 A 里 87 个条目都参与了比较,催办名单的长度就对不上。所以我习惯在结果里同时看三个计数——A 的元素数、B 的元素数、结果的元素数,文本集合运算 会把这三个数显示出来,够我用来自证结论。并集还有个副作用我很在意:合并两份来源相同的清单,同时就把重复解决了,比先合并再去重少一步。

假差异才是耗时间的地方

拿到 B - A 那 8 个以后,我发现有 3 个"明明报过名"。原因不复杂,都是文本层面的差异。

第一类是尾随空格。表格另存成 txt 的时候,单元格里手滑多打一个空格很常见,"李雷 " 和 "李雷" 在字符串比较里就是两个东西。我的处理是先把两边都做了首尾空格清理再比——选项里就有这一项。这一步会直接改变结果条数,所以我一定会记下自己有没有打开它:打开了是 8 个差异,没打开可能变 11 个,汇报时说不清是哪个就白比了。

第二类是大小写。邮箱和英文名最容易中招,LiLei@corp.com 和 lilei@corp.com 是同一个人,但如果按区分大小写比较就会被判成两边各有一个。我的做法是:第一轮忽略大小写,把结果当作"真实差异"的候选;第二轮恢复区分大小写,看被抹平的条目有几条,那几条就是书写不规范,得回头统一而不是直接合并。

第三类是不可见字符,制表符、全角空格,还有从别的编码文件粘过来时带的行首标记,肉眼完全看不见,但会让一行"看起来一样"的文本被判成不同。遇到这种我一般先用文本清理类的工具把空白统一处理掉,再做集合运算。

还有一类不是假差异而是真问题:重名。两份名单里都有"张伟",交集会认为这是同一个人——如果工号也一并列出,那这两行本来就不同,比对结果会多出两条"差异"。凡是靠姓名的比对,我都会要求加上一个唯一列,工号或者邮箱都行,不然集合运算帮不上忙。

它和逐行对比不是一件事

顺带说一句容易混的:集合运算不看顺序,也不看重复的位置,只问"这条在不在那一份里"。所以它回答不了"这一版比上一版改了哪一行、第 5 行变成了什么"。那种要保留行序、要看改动位置的比对,我用的是文本对比,两边按行对齐来看红绿差别。而我如果只是想把两份名单里同一人重复出现的条目砍掉、按姓排好,交给文本排序去重更直接。

几份名单能不能一起比?

工具吃的是两个列表,所以三份以上我会分步做:先算前两份的交集,把结果当作新的 A,再和第三份做交集;要找"任何一份里有但别的没有"的,就把两两差集分别算出来再看重合。三次的结果都得留档,否则第二三步就说不清基准是什么。名单再多我就不指望这种链式做法了,那种情况我会改成把所有名单拼成一列、加一列来源标记,回到表格里做。

问得比较多的几件事

Q:为什么我算出来的差集比预期的多好几条?

十有八九是格式差异,不是数据差异。先按顺序排查:首尾空格清没清、大小写忽不忽略、有没有制表符或全角空格、空行是否参与。把这几项统一后再比一次,缩掉的那几条就是格式造成的。

Q:A 减 B 和 B 减 A 应该看哪一个?

看你要处理谁。要"催没做这件事的人",就是全集减已办集,也就是 B - A 那个方向;要"查漏、查外部数据",就是已办集全减。别记"左边减右边",要记谁是基准。

Q:重复条目会不会把结果带偏?

会。重复让计数变得无法解释,你分不清差异是"多出来的人"还是"多出来的行"。我通常在选项里打开去重,同时另外记一份"原始行数"和"去重后条数",两个数差多少本身就是数据质量的指标。

Q:并集有什么用,为什么不直接两份拼起来?

拼起来得到的是行数,并集得到的是不重复的条目数。做统计口径时这两个数差别可能很大,体检那次是 179 行对 95 条。要一份完整人员名单,用并集;要保留每一条原始记录,才用简单拼接。

Q:一份名单里有括号、后缀这类杂信息怎么办?

先在两边做同样的清洗,把杂信息去掉再比,否则两边格式不同,交集会小得离谱。清洗动作要一致这点很重要:只洗 A 不洗 B,本来相同的人也被洗成不同的了。

回到那两份差 5 个人的名单。最后催办的是 8 个,其中 5 个是真没报、3 个是空格造成的假差异——这件事给我的判断标准就留成了两条:一是先确认基准和方向再做差集,二是先统一文本形态(空格、大小写、不可见字符)再算,算完用数量关系回验一次。做到这两条,集合运算的结果可以直接发出去;少一条,就得再花一个下午。

📎 相关文章

排版稿没内容可填?占位文本怎么写才不像模板 2026-10-15 两列数据要列出所有组合?分列合并与笛卡尔积 2026-10-15 测试上传限制要多大文件?按需造一个指定大小的 2026-10-15 几十MB的文本发不出去也打不开?按行数切小 2026-10-15 只要文件名不要路径?几百行路径批量提取 2026-10-15