Testes unitários em grandes volumes de dados com Great Expectations e Spark

Thiago Santos
6 min readMay 30, 2022

--

Anteriormente eu escrevi um post aqui no Medium falando um pouco sobre Qualidade de Dados e sobre o Great Expectations. Nesse post, eu abordei uma introdução de como configurar localmente e como essa ferramenta poderia ser utilizada para suportar processos de Qualidade de Dados. Desta vez, retorno novamente ao uso do Great Expectations, mas sob uma nova ótica, voltada para ótimo funcionamento e integração que esta ferramenta apresenta com a principal ferramenta de processamento de dados distribuída do mundo, o Apache Spark. Para no nosso caso de uso, iremos utilizar o PySpark que basicamente é uma API que faz a interface entre o Python e o Apache Spark, ferramenta esta, que espero que você já possua alguma familiaridade, para poder aproveitar melhor o conteúdo deste post.

O trabalho de um engenheiro de dados não deve limitar-se apenas a processar as informações necessárias para o negócio, mas também deve garantir a veracidade destas informações. Principalmente em um ambiente de Big Data onde o reprocessamento de Terabytes de dados podem atrasar um projeto, causar insatisfação no cliente e causar o aumento de custos. Problemas como: a má qualidade dos conjuntos de dados entregues; falta de colunas de um dataset; valores inesperados fruto de uma regra de negócio mal explicada ou aplicada; carga de dados incompletas dentre outas questões, podem ser minimizados com a implantação de processos de testes unitários nos dados da pipeline.

Mas afinal o que são testes unitários?

No desenvolvimento de software, o teste de unidade é uma técnica de verificação e validação na qual um desenvolvedor testa se métodos e funções individuais usados ​​por seu software são adequados para uso, ou seja, um teste unitário basicamente realiza o teste na menor parte que pode ser testada em um software.

Trazendo esse conceito para o mundo dos dados, podemos pensar que testes unitários em dados deve se concentrar em determinar a qualidade do dado de fato, ao invés de um objeto ou função como ocorre no desenvolvimento de software, iremos testar o dado. São exemplos de testes unitários de dados:

  • Verificar se os dados estão dentro dos intervalos que deveriam (com base em todos os dados anteriores registrados);
  • Examinar lacunas nos dados, valores ausentes, tendências existentes e assim por diante;
  • Validar se o conjunto de dados foi devidamente atualizado;
  • Validar se o dado está com o tipo e o formato correto.

Mãos na massa!

Para realizar uma demonstração de como é simples utilizar o Great Expectations com Pyspark, irei utilizar um dataset que contém dados a cerca do desempenho dos jogadores dos 7 primeiros colocados do campeonato alemão (Bundesliga) da temporada 2020/2021. O desenvolvimento foi feito no Google Colab, mas não existe dificuldade em configurar o Great Expectations em outras plataformas como o Databricks, Amazon EMR e até mesmo o Google Big Query, você pode verificar cada caso na documentação do projeto.

Voltando ao Google Colab, o primeiro passo, logicamente é realizar a instalação dos pacotes que precisaremos utilizar, e se você pensou em instalar o Pyspark e o Great Expectations, parabéns! Estamos em sintonia aqui!

Depois, vamos iniciar a seção spark e aplicar algumas configurações, no meu caso, o meu dataset estava armazenado no Google Drive então também precisei conceder as devidas permissões para o Colab enxergar o arquivo.

Por fim, vamos realizar a leitura dos dados.

Para poder usufruir de todas as comodidades que o Great Expectations nos proporciona no que se refere a validação de dados, precisamos transformar o dataset spark em um SparkDFDataset, e faremos isso desta maneira:

Certo, agora vamos imaginar que o nosso conjunto de dados precisa cumprir a seguinte ordem de requisitos:

  1. O dataset deve obrigatoriamente conter as seguintes colunas: “Name”, “Club”, “Position”, “Matches”, “Mins”, “Goals”, “Assists”, “Yellow_Cards”, e “Red_Cards”;
  2. As colunas do tópico 1 não devem conter valores nulos;
  3. As colunas “Assists” e “Yellow_Cards” devem ser um número inteiro;
  4. A coluna “Name” deve ser uma cadeia de caracteres;
  5. A coluna “Name” não deve conter valores duplicados
  6. A coluna “Club” deve obrigatoriamente um ou mais registros com os seguintes valores: 1, 2, 3, 4, 5, 6 e 7.

Para validar o primeiro primeiro ponto, usaremos o método “expect_column_to_exist”, note que ao executarmos é exibido na console um dicionário com algumas formações. A mais importante para a gente seria o campo “success”.

Existem métodos que retornam um dicionário um pouco mais robusto e com mais informações. Por padrão esse result tem um retorno básico, isso implica dizer que alguma informação pode ser comprimida durante o retorno para alguns métodos. Uma forma de que essas informações não sejam suprimidas é passar como parâmetro o result format como: “result_format={‘result_format’: ‘COMPLETE’}”. Mas por hora não precisamos nos preocupar, já que o “expect_column_to_exist” possui um retorno bem simples e a adição desse parâmetro não afetaria o nosso result.

Se quisermos acelerar esse processo, podemos criar uma lista com o nome das colunas que devem ser obrigatórias. Depois percorremos essa lista com o auxilio de uma estrutura de repetição, passando o nome da coluna como parâmetro.

Para o segundo ponto, vamos reutilizar a lista de colunas anteriores e basicamente repetir a estrutura anterior alterando apenas o método utilizado, que neste caso seria o “expect_column_values_to_not_be_null”

Para o caso 3 e 4, vamos criar uma função que utilizará o método “expect_column_values_to_be_of_type” e passaremos três parâmetros: o SparkDFDataset, a coluna a ser testada e o tipo que esperamos que ela seja.

Para o ponto 5, usaremos o método “expect_column_values_to_be_unique”, esse método do Great Expectations não somente verifica se a coluna está duplicada, mas nos informa também quantos registros duplicados existem e qual o percentual de dados duplicados. E aqui encontramos o primeiro problema do nosso dataset.

Para o ultimo ponto, o 6, usaremos o método “expect_column_values_to_be_in_set”, onde informamos qual a coluna que queremos validar e quais valores essa coluna deve ter, nesse caso eu provoquei uma falha proposital, apenas para vermos o quanto esse método encurtaria tempo quando falamos em valores não esperados em uma coluna. Isso porque eu saberia não somente a quantidade ou o percentual, mas também quais os valores inesperados que existem na coluna, que nesse caso seria os valores “6” e “7”. Lembra do “result_format”, aqui precisei passa-lo como “COMPLETE” para ter acesso a esta lista de valores inesperados.

Bem, aqui finalizamos os nossos pontos de validação. Caso tenha ficado curioso com o que mais pode ser feito você pode acessar este link e descobrir mais uma variedade de métodos que podem ser utilizados em uma outra variedade de casos, e que pode com certeza adicionar uma pitada de qualidade de dados nas suas pipelines.

Referências:

--

--