Tempo estimado de leitura: 5 atas
No nosso post anterior sobre como comparar ficheiros CSV para as diferenças mostrámos como podia ver as diferenças, mas e se quisesse ver se um registo era 100% compatível ou não?
Aqui vamos utilizar o SequenceMatcher que é uma classe python que lhe permite comparar ficheiros e devolvê-los se forem combinados como uma percentagem.
Vejamos o código
Importar declarações e ler nos ficheiros CSV. Aqui fazemos o habitual de importar as bibliotecas de que precisamos e lemos nos dois ficheiros CSV que utilizamos:
Note-se que também aqui definimos as definições da moldura de dados, para que possamos ver correctamente a moldura de dados. Ver mais abaixo.
import pandas as pd
import numpy as np
import math
from difflib import SequenceMatcher
pd.set_option('max_columns', None)
pd.set_option('display.width', None)
#Read in the CSV files
df1 = pd.read_csv('CSV1.csv')
df2 = pd.read_csv('CSV2.csv')
Os dois ficheiros CSV têm este aspecto:
CSV1
CSV2
A seguir, vamos criar uma matriz. A criação de uma matriz permite completar a comparação à medida que cria índices, e os índices em cada matriz podem ser comparados e depois a diferença percentual pode ser calculada.
#Create an array for both dataframes
array1 = np.array(df1)
array2 = np.array(df2)
O nosso passo seguinte é transferir as matrizes para uma moldura de dados, alterar todos os valores inteiros para uma cadeia, e depois juntar ambas as moldura de dados numa só.
Neste caso, a mudança de valores para uma cadeia permite que esses valores sejam iterados, caso contrário, receberá um erro " TypeError: o objecto 'int' não é iterável".
#Transfer the arrays to a dataframe
df_CSV_1 = pd.DataFrame(array1, columns=['No1','Film1','Year1','Length1'])
df_CSV_2 = pd.DataFrame(array2, columns=['No2','Film2','Year2','Length2'])
#Change all the values to a string, as numbers cannot be iterated over.
df_CSV_1['Year1'] = df_CSV_1['Year1'].astype('str')
df_CSV_2['Year2'] = df_CSV_2['Year2'].astype('str')
df_CSV_1['Length1'] = df_CSV_1['Length1'].astype('str')
df_CSV_2['Length2'] = df_CSV_2['Length2'].astype('str')
#join the dataframes
df = pd.concat([df_CSV_1,df_CSV_2], axis=1)
Estamos agora a passar à parte principal do programa, que nos dá as respostas de que necessitamos. Aqui criamos uma função que faz os cálculos por nós:
#Create a function to calculate the differences and show as a ratio.
def create_ratio(df, columna, columnb):
return SequenceMatcher(None,df[columna],df[columnb]).ratio()
A seguir calculamos as diferenças e formatamos a saída
#Here we use apply which will pull in the data that needs to be passed to the fuction above.
df['Film_comp'] = df.apply(create_ratio,args=('Film1','Film2'),axis=1)
df['Year_comp'] = df.apply(create_ratio,args=('Year1','Year2'),axis=1)
df['Length_comp'] = df.apply(create_ratio,args=('Length1','Length2'),axis=1)
#This creates the values we are looking for
df['Film_comp'] = round(df['Film_comp'].astype('float'),2)*100
df['Year_comp'] = round(df['Year_comp'].astype('float'),2)*100
df['Length_comp'] = round(df['Length_comp'].astype('float'),2)*100
#this removes the decimal point that is added as a result of using the datatype 'Float'
df['Film_comp'] = df['Film_comp'].astype('int')
df['Year_comp'] = df['Year_comp'].astype('int')
df['Length_comp'] = df['Length_comp'].astype('int')
#Print the output
print(df)
E o resultado final parece ser o seguinte:
Uma explicação da produção
Como se pode ver, as últimas três colunas são as percentagens da correspondência obtida, sendo 100 uma correspondência exacta.
Para o valor de índice 1 há Joker no primeiro ficheiro, mas Jokers está no segundo ficheiro.
O rácio é calculado da seguinte forma:
Joker é comprimento 5, Jokers é comprimento 6 = 11 caracteres
Assim, a lógica olha para a sequência, iterando através da sequência pode ver que os primeiros 10 caracteres estão na mesma ordem, mas o 11º carácter não é, como resultado, calcula o seguinte:
(10/11) * 100 = 90.90
Finalmente, a função redonda define o valor que procuramos para 91.
Na mesma linha, vamos comparar o ano:
2019 e 2008 são um total de oito caracteres.
Na sequência, os dois primeiros de cada partida, e como também são encontrados, dão-nos quatro, e a proporção é a seguinte:
4/8 *100 = 50
Para o Índice 20 também comparámos o nome do filme, no total há 17 caracteres, mas o programa ignora o que eles chamam de lixo, pelo que o espaço não está incluído, por essa razão a proporção apenas calcula mais de dezasseis caracteres.
A fim de melhor compreender isto, compilei o que se segue:
Índice 1 | Índice 1 | ||||||
2019 | 2008 | 8 | Joker | Jokers | 11 | ||
2 | Mancha Correcta | 1 | J | Mancha Correcta | 1 | ||
0 | Mancha Correcta | 1 | o | Mancha Correcta | 1 | ||
1 | Ponto incorrecto | 0 | K | Mancha Correcta | 1 | ||
9 | Ponto incorrecto | 0 | e | Mancha Correcta | 1 | ||
r | Mancha Correcta | 1 | |||||
2 | Encontrado | 1 | J | Encontrado | 1 | ||
0 | Encontrado | 1 | o | Encontrado | 1 | ||
1 | Encontrado | 0 | K | Encontrado | 1 | ||
9 | Encontrado | 0 | e | Encontrado | 1 | ||
r | Encontrado | 1 | |||||
Total em comparação | 8 | Total em comparação | 11 | ||||
Rácio | 0.50 | Rácio | 0.91 | ||||
Índice 20 | |||||||
A Sujeira | O Sujo | 16 | |||||
T | Mancha Correcta | 1 | |||||
H | Mancha Correcta | 1 | |||||
E | Mancha Correcta | 1 | |||||
Mancha Correcta | 1 | ||||||
T | Encontrado | 1 | |||||
H | Encontrado | 1 | |||||
E | Encontrado | 1 | |||||
D | Mancha Correcta | 1 | |||||
I | Mancha Correcta | 1 | |||||
R | Mancha Correcta | 1 | |||||
T | Mancha Correcta | 1 | |||||
D | Encontrado | 1 | |||||
I | Encontrado | 1 | |||||
R | Encontrado | 1 | |||||
T | Encontrado | 1 | |||||
Total em comparação | 16 | ||||||
Rácio | 0.94 |