O Que Temos Em Comum
Como identificar o que temos em comum entre conjuntos de dados
A gente sempre precisa saber quais elementos se repetem entre diferentes tabelas, listas ou bases. Isso parece simples no papel, mas na prática esbarra em problemas de performance, formatos incompatíveis e dados sujos que ninguém limpou antes de jogar no sistema. O conceito central é bem direto: você quer descobrir a interseção entre dois ou mais grupos. A interseção são exatamente os itens que aparecem em todos os conjuntos que você está comparando. Item que aparece em apenas um deles não entra nessa conta. Eu trabalho com isso toda semana. Ultimamente estou lidando com dois bancos de dados de clientes de CRM que precisam ser cruzados para validar cadastros duplicados. Um vem da plataforma legado com campos mal formatados, o outro é uma importação recente via API. As primeiras vezes que fiz esse cruzamento, ignorei a normalização dos campos e o resultado veio com 47% de falsos negativos — básicos que eram idênticos, mas escriturados de formas ligeiramente diferentes, simplesmente não apareciam como correspondência.
O workaround que funcionou foi implementar uma etapa de normalização prévia: remover acentos, converter tudo para minúsculas, stripping de espaços duplos e padronização de máscaras (CPF, telefone, CEP). Depois disso, o recall subiu para algo próximo de 94%. Ainda não é perfeito, mas é muito mais usado do que o resultado cru que eu tinha antes. Existem várias abordagens para resolver isso. A mais básica é usar estruturas de hash set. Você coloca todos os elementos do primeiro conjunto numa tabela de dispersão, faz a mesma coisa com o segundo, e depois intersecta. Em Python, isso é literalmente um comando: `set(a) & set(b)`. Funciona bem quando os dados cabem na memória e têm cardinalidade moderada. Quando os dados são maiores do que a RAM disponível, aí a coisa muda. Você precisa pensar em streaming ou em partições. No meu caso atual, os dois conjuntos têm cerca de 2 milhões de registros cada. Hash set na memória resolve, mas se escalar para dezenas de milhões, a abordagem precise mudar para processamento distribuído ou query em banco relacional com INNER JOIN adequado.
Query SQL com INNER JOIN ou EXISTS é talvez a forma mais transparente para quem já tem acesso a um banco. A desvantagem é que índices mal configurados podem transformar um operation que deveria levar segundos em algo que leva horas. Eu perdi uma tarde inteira tentando cross-match duas tabelas de 15 milhões de linhas porque nenhuma das colunas de junção tinha índice. Colocar um B-tree index nas chaves de comparação reduziu o tempo de 2h15min para cerca de 8 minutos. Para cenários que exigem tolerância a variações — nomes parecidos, endereços levemente diferentes, CPFs com dígitos trocados — interseção exata não basta. Aí você entra em matching aproximado. Métricas como Jaccard similarity, Levenshtein distance ou token sort ratio permitem encontrar correspondências mesmo quando os strings não são idênticos. O trade-off é custo computacional: comparação par-a-par de dois conjuntos de 500 mil registros gera 250 bilhões de comparações. Sem alguma estratégia de blocking ou indexing reduzidor, isso simplesmente não roda em tempo útil. Uma técnica que uso frequentemente é minhash com LSH (Locality Sensitive Hashing). Reduz a complexidade de O(n²) para algo bem mais gerenciável, mantendo uma taxa de false positive controlável. Implementações prontas existem em bibliotecas como datasketch para Python. A desvantagem prática é que você precisa ajustar os thresholds manualmente, e valores muito apertados perdem matches válidos, valores muito frouxos trazem ruído demais.

Há também a questão dos falsos positivos e negativos que todo mundo subestima. Quando você acha que encontrou o que temos em comum entre dois datasets, o resultado nunca é 100% confiável sem validação humana. Eu costumo fazer amostragem aleatória de 200 registros do resultado e revisar manualmente. Se a taxa de erro passar de 5%, preciso ajustar os parâmetros de normalização ou os thresholds de similaridade. Outro ponto que ninguém menciona: dados missing. Campos nulos ou vazios são um pesadelo para intersection. Eles frequentemente acabam sendo tratados como um valor comum invisível, poluindo seus resultados. Sempre filtre NAs antes de qualquer operação de interseção, ou marque-os explicitamente como não-comparáveis. Se o seu cenário envolve mais de dois conjuntos, a lógica se mantém, mas a complexidade cresce. A interseção de três ou mais tabelas exige que um elemento esteja presente em TODAS elas. Diferente da união, onde basta estar em pelo menos uma. Isso é importante porque em validações de integridade de dados, a interseção rigorosa vezes o que importa — você quer saber o que é realmente consistente entre todas as fontes, não o que aparece em pelo menos uma.
Para quem precisa de uma solução prática e rápida, rodar em Python com pandas é suficiente para volumes até algumas centenas de milhares de linhas. Acima disso, considere Spark ou migre a lógica para o banco de dados. A regra geral é: quanto mais dados, mais perto do armazenamento os cálculos devem ocorrer, para evitar mover gigabytes pela rede. Resumindo sem resuminhar: normalise sempre, indexe as colunas de junção, valide com amostragem manual e escolha a ferramenta conforme o volume. Não existe solução única que funcione bem em todos os cenários.
Galeria
-
COMECEMOS COM O QUE TEMOS EM COMUM, JESUS. - PORTAL DA IGREJA DE CRISTO -
O que temos em comum: É que todos somos... Cacio Leite Santana - Pensador -
O que temos em comum em todos os... Werdson M. Well - Pensador -
O que temos em comum com as maiores... Diego Cardoso de Souza - Pensador