sábado, 8 de junho de 2024

Usando o difflib do Python para configurar reuniões virtuais

Introdução

O difflib é um módulo da biblioteca padrão do Python que permite comparar sequências, tais como arquivos, listas, strings e outros dados. Ele é especialmente útil para identificar e destacar diferenças entre essas sequências.

Neste post, focaremos na função get_close_matches(). Vale a pena, porém, conhecer outras funções contidas no módulo. 

Estrutura da função

difflib.get_close_matches(word, possibilities, n=3, cutoff=0.6)

A função get_close_matches retorna uma lista das melhores correspondências entre uma sequência de caracteres (geralmente palavras) e uma lista de possibilidades. As n respostas mais prováveis são retornadas em ordem decrescente de semelhança. O parâmetro n deve ser maior que 0, sendo 3 o valor padrão.

O ponto de corte (cutoff) é um valor float do intervalo [0, 1], com padrão em 0.6. Pontuações inferiores ao ponto de corte são ignoradas.

Um caso de uso

Durante a última campanha para Procurador-Geral, precisávamos criar reuniões virtuais no MS Teams com um número variado de colegas, às vezes ultrapassando a centena.

Possuíamos uma planilha Excel que relacionava nomes de colegas a seus respectivos e-mails funcionais. Era um documento oficial, de acesso permitido a qualquer membro da Instituição.

Então, listávamos num arquivo texto os nomes aproximados de quem, deveria ser convidado para as reuniões de campanha (às vezes incompletos ou mesmo com algum erro de grafia) e incumbíamos o Python de nos entregar os e-mails correspondentes aos nomes da lista oficial que mais se assemelhassem  aos fornecidos. O acerto chegava próximo dos 100%! 

O script relacionava os e-mails em arquivo texto, de forma que bastava colá-los no campo próprio do MS Teams para que a reunião já estivesse configurada.

O código

import pandas as pd
import difflib

df = pd.read_excel('emails_dos_membros_2024_01.xlsx')
df['Nome'] = df['Nome'].str.lower()
df['E-mail'] = df['E-mail'].str.lower()

# Relação de nomes para a reunião
arq = open("emails.txt", "r")
nomes_para_a_reuniao = ['antonio carlos']

# Itera sobre cada linha do arquivo
for linha in arq:
# Adiciona a linha à lista
nomes_para_a_reuniao.append(linha.rstrip("\n").lower())

# Fecha o arquivo
arq.close()

# Imprime a lista
print(f'Total de nomes: {len(nomes_para_a_reuniao)}')
relacao_oficial = df['Nome'].to_list()

lista_correspondencia = []

for nome in nomes_para_a_reuniao:
correspondencia = difflib.get_close_matches(nome, relacao_oficial, 1, 0.3)
try:
nome_encontrado = correspondencia[0]
lista_correspondencia.append(nome_encontrado)
print(nome, ">>>", nome_encontrado.strip())
except:
print(f'\nPulamos {nome}!!!\n')

print(f"Nomes encontrados: {len(lista_correspondencia)}")
e_mails = []

for index, row in df.iterrows():
for nome in lista_correspondencia:
if row['Nome']==nome:
e_mails.append(row['E-mail'])

# Saída
nome_arquivo = "saida.txt"

with open(nome_arquivo, 'w') as arquivo:
for e_mail in e_mails:
arquivo.write(e_mail + '\n')


Explicação do código

Primeiro, o código importa as bibliotecas necessárias (pandas para manipulação de dados e difflib para comparação de strings). Em seguida, carrega um arquivo Excel contendo nomes e e-mails, converte os valores das colunas 'Nome' e 'E-mail' para letras minúsculas para garantir a consistência nas comparações, e exibe uma amostra dos dados.

Depois, abre um arquivo de texto contendo nomes adicionais e adiciona esses nomes a uma lista preexistente, convertendo cada nome para minúsculas. Em seguida, imprime o total de nomes nessa lista combinada.

Para cada nome na lista combinada, o código usa difflib.get_close_matches para encontrar a correspondência mais próxima na lista de nomes do DataFrame (lista oficial), com um índice de similaridade mínimo. Se encontrar uma correspondência, adiciona o nome correspondente a uma nova lista e imprime a correspondência. Se não encontrar, imprime uma mensagem indicando que o nome foi pulado, para que se possa fazer a inclusão manual. Após isso, imprime o total de nomes encontrados.

Em seguida, o código percorre cada linha do DataFrame, verificando se o nome na linha corresponde a algum nome na lista de correspondência. Se houver correspondência, adiciona o e-mail correspondente a uma lista de e-mails.

Finalmente, o código escreve os e-mails encontrados em um arquivo de texto, um por linha.

Em conclusão: o código compara listas de nomes (lista oficial e lista fornecida), encontra as correspondências e extrai os e-mails associados.