4.3. Introdução ao Schema: Uma Biblioteca Python para Validar seus Dados#
Foto por Nonsap Visuals no Unsplash
4.3.1. O que é o Schema?#
Nas duas últimas seções, aprendemos como validar um DataFrame Pandas. No entanto, às vezes você pode querer validar estruturas de dados Python em vez de um DataFrame Pandas. É aí que o Schema se faz útil.
Schema é uma biblioteca para validação de estruturas de dados em Python.
Instale o Schema com:
pip install schema
ou, se estiver usando o Poetry:
poetry add schema
4.3.2. Valide Tipos de Dados#
Imagine que esses dados apresentam as informações sobre seus amigos.
data = [
{'name': 'Norma Fisher', 'city': 'South Richard', 'closeness (1-5)': 4, 'extrovert': True, 'favorite_temperature': -45.74},
{'name': 'Colleen Taylor', 'city': 'North Laurenshire', 'closeness (1-5)': 4, 'extrovert': False, 'favorite_temperature': 93.9},
{'name': 'Melinda Kennedy', 'city': 'South Cherylside', 'closeness (1-5)': 1, 'extrovert': True, 'favorite_temperature': 66.33}
]
Podemos usar o Schema para validar tipos de dados:
from schema import Schema
schema = Schema([{'name': str,
'city': str,
'closeness (1-5)': int,
'extrovert': bool,
'favorite_temperature': float}])
schema.validate(data)
Como a saída não gera erros, sabemos que nossos dados são válidos.
Vejamos o que acontece se os tipos de dados não forem como o que esperamos:
schema = Schema([{'name': int,
'city': str,
'closeness (1-5)': int,
'extrovert': bool,
'favorite_temperature': float}])
schema.validate(data)
SchemaError: Or({'name': <class 'int'>, 'city': <class 'str'>, 'closeness (1-5)': <class 'int'>, 'extrovert': <class 'bool'>, 'favorite_temperature': <class 'float'>}) did not validate {'name': 'Norma Fisher', 'city': 'South Richard', 'closeness (1-5)': 3, 'extrovert': True, 'favorite_temperature': -45.74}
Key 'name' error:
'Norma Fisher' should be instance of 'int'
A partir do erro, sabemos exatamente qual coluna e valor dos dados são diferentes do que esperamos. Assim, podemos voltar aos dados para corrigir ou excluir esse valor.
Se tudo o que importa é se os dados são válidos ou não, use:
schema.is_valid(data)
Isso retornará True
se os dados estiverem conforme o esperado ou False
caso contrário.
4.3.3. Valide o Tipo de Dado de Algumas Colunas Enquanto Ignora o Resto#
Mas e se não nos importarmos com os tipos de dados de todas as colunas, mas apenas nos preocuparmos com o valor de algumas colunas? Podemos especificar isso com str: object
schema = Schema([{'name': str,
'city': str,
'favorite_temperature': float,
str: object
}])
schema.is_valid(data)
Output: True
Como você pode ver, tentamos validar os tipos de dados ‘name’, ‘city’ e ‘favorite_temperature’, ignorando os tipos de dados do restante dos recursos em nossos dados.
Os dados são válidos porque os tipos de dados dos 3 recursos especificados estão corretos.
4.3.4. Valide com uma Função#
E se quisermos determinar se os dados em uma coluna atendem a uma condição específica, como o intervalo dos valores em uma coluna?
O Schema permite que você use uma função para especificar a condição para os seus dados.
Se quisermos verificar se os valores na coluna ‘closeness (1-5)’ estão entre 1 e 5, podemos usar lambda
como abaixo:
schema = Schema([{'name': str,
'city': str,
'favorite_temperature': float,
'closeness (1-5)': lambda n : 1 <= n <= 5,
str: object
}])
schema.is_valid(data)
Output: True
Como você pode ver, especificamos n
, o valor em cada linha da coluna ‘closeness (1-5)’, entre 1 e 5 com lambda n: 1 <= n <=5
. Legal!
4.3.5. Valide Vários Schemas#
4.3.5.1. And#
E se você quiser garantir que sua coluna “closeness (1-5)” esteja entre 1 e 5 e o tipo de dado seja um número inteiro?
É quando And
pode ser usado:
from schema import And
schema = Schema([{'name': str,
'city': str,
'favorite_temperature': float,
'closeness (1-5)': And(lambda n : 1 <= n <= 5, float),
str: object
}])
schema.is_valid(data)
Output: False
Embora todos os valores estejam entre 1 e 5, o tipo de dado não é flutuante (decimal). Como uma das condições não foi satisfeita, os dados são inválidos.
4.3.5.2. Or#
Se quisermos que os dados da coluna sejam válidos se qualquer uma das condições for satisfeita, podemos usar Or
.
Por exemplo, se quisermos que o nome da cidade contenha 1 ou 2 palavras, podemos usar:
from schema import Or
schema = Schema([{'name': str,
'city': Or(lambda n: len(n.split()) == 2, lambda n: len(n.split()) == 1),
'favorite_temperature': float,
'closeness (1-5)': int,
str: object
}])
schema.is_valid(data)
Output: True
4.3.5.3. Combinação de And e Or#
E se quisermos que o tipo de dado da coluna ‘city’ seja string, mas o comprimento pode ser 1 ou 2? Felizmente, isso pode ser tratado facilmente combinando And
e Or
:
schema = Schema([{'name': str,
'city': And(str, Or(lambda n: len(n.split()) == 2, lambda n: len(n.split()) == 1)),
'favorite_temperature': float,
'closeness (1-5)': int,
str: object
}])
schema.is_valid(data)
Output: True
4.3.6. Optional#
E se não tivermos as informações detalhadas sobre alguns de seus amigos?
data = [
{'name': 'Norma Fisher', 'city': 'South Richard', 'closeness (1-5)': 4, 'detailed_info': {'favorite_color': 'Pink', 'phone number': '7593824219489'}},
{'name': 'Emily Blair', 'city': 'Suttonview', 'closeness (1-5)': 4, 'detailed_info': {'favorite_color': 'Chartreuse', 'phone number': '9387784080160'}},
{'name': 'Samantha Cook', 'city': 'Janeton', 'closeness (1-5)': 3}
]
Como o ‘detailed_info’ de Samantha Cook não está disponível para todos os seus amigos, queremos tornar esta coluna opcional. Schema nos permite definir essa condição com Optional
.
from schema import Optional
schema = Schema([{'name': str,
'city': str,
'closeness (1-5)': int,
Optional('detailed_info'): dict}])
schema.is_valid(data)
Output: True
4.3.7. Forbidden#
Às vezes, também podemos querer garantir que um determinado tipo de dado não esteja em nossos dados, como informações privadas. Podemos especificar qual coluna é proibida com Forbidden
:
from schema import Forbidden
schema = Schema([{'name': str,
'city':str,
'closeness (1-5)': int,
Forbidden('detailed_info'): dict
}])
schema.validate(data)
Forbidden key encountered: 'detailed_info' in {'name': 'Norma Fisher', 'city': 'South Richard', 'closeness (1-5)': 4, 'detailed_info': {'favorite_color': 'Pink', 'phone number': '7593824219489'}}
Agora estamos cientes da existência da coluna proibida toda vez que o esquema lança um erro!
4.3.8. Dicionário Aninhado#
Até agora, o esquema nos permitiu realizar muitas validações sofisticadas em várias linhas de código. Mas na realidade, podemos lidar com uma estrutura de dados mais sofisticada do que o exemplo acima.
Podemos usá-lo para dados com uma estrutura mais complicada? Como um dicionário dentro de um dicionário? Sim, nós podemos.
Imagine que nossos dados se parecem abaixo:
data = [
{'name': 'Norma Fisher', 'city': 'South Richard', 'closeness (1-5)': 4, 'detailed_info': {'favorite_color': 'Pink', 'phone number': '7593824219489'}},
{'name': 'Emily Blair', 'city': 'Suttonview', 'closeness (1-5)': 4, 'detailed_info': {'favorite_color': 'Chartreuse', 'phone number': '9387784080160'}}
]
Podemos validar com um dicionário aninhado:
schema = Schema([{'name': str,
'city':str,
'closeness (1-5)': int,
'detailed_info': {'favorite_color': str, 'phone number': str}
}])
schema.is_valid(data)
Output: True
A sintaxe é direta! Só precisamos escrever outro dicionário e especificar o tipo de dado para cada chave.
4.3.9. Converter Tipo de Dado#
O Schema pode não somente ser usado para validar dados, mas também para converter o tipo de dado se não for o que esperávamos!
Por exemplo, podemos converter a string ‘123’ para o inteiro 123 com Use(int)
:
from schema import Use
Schema(Use(int)).validate('123')
123