Pandera Column checks with another column as conditional

27 Views Asked by At

I'm trying to create a DataFrameSchema to validate a DataFrame. This DataFrameSchema is going to check multiple columns (as usual), but two of them need to be checked together. The columns 'Quantidade' and 'Financeiro' need to be unique in the row; either 'Quantidade' is filled and 'Financeiro' is not, or vice-versa.

I'm using the current DataFrame to test the DataFrameSchema:

DataFrame(
    [{'Basket': False, 'id': 895912122, 'Created': Timestamp('2024-03-20 16:31:34'), 'Creator': '[email protected]', 'Código do Broker': 1234, 'Código do Cliente': 1234, 'Operação': 'Compra', 'Código do Ativo': 'KJNT11', 'Quantidade': 500, 'Financeiro': 0.0, 'Incentivo': 1.0, 'Deságio': 0.5, 'Corretagem': 0.0, 'Estado': '100'},
    {'Basket': False, 'id': 895912327, 'Created': Timestamp('2024-03-20 16:31:54'), 'Creator': '[email protected]', 'Código do Broker': 1234, 'Código do Cliente': 1234, 'Operação': 'Venda', 'Código do Ativo': 'KJNT11', 'Quantidade': 500, 'Financeiro': 0.0, 'Incentivo': 1.0, 'Deságio': 0.5, 'Corretagem': 0.0, 'Estado': '100'},
    {'Basket': False, 'id': 895951726, 'Created': Timestamp('2024-03-20 17:29:23'), 'Creator': '[email protected]', 'Código do Broker': 1234, 'Código do Cliente': 1234, 'Operação': 'Compra', 'Código do Ativo': 'KJNT11', 'Quantidade': 0, 'Financeiro': 500.0, 'Incentivo': 1.0, 'Deságio': 0.5, 'Corretagem': 0.5, 'Estado': '100'},
    {'Basket': False, 'id': 895952012, 'Created': Timestamp('2024-03-20 17:29:41'), 'Creator': '[email protected]', 'Código do Broker': 1234, 'Código do Cliente': 1234, 'Operação': 'Venda', 'Código do Ativo': 'KJNT11', 'Quantidade': 0, 'Financeiro': 500.0, 'Incentivo': 1.0, 'Deságio': 0.5, 'Corretagem': 0.5, 'Estado': '100'},
    {'Basket': False, 'id': 0, 'Created': Timestamp('2021-07-01 00:00:00'), 'Creator': '[email protected]', 'Código do Broker': 1234, 'Código do Cliente': 1234, 'Operação': 'Compra', 'Código do Ativo': 'KJNT11', 'Quantidade': 500, 'Financeiro': 0.0, 'Incentivo': 0.0, 'Deságio': 0.0, 'Corretagem': 0.0, 'Estado': '100'},
    {'Basket': False, 'id': 0, 'Created': Timestamp('2021-07-01 00:00:00'), 'Creator': '[email protected]', 'Código do Broker': 1234, 'Código do Cliente': 1234, 'Operação': 'Compra', 'Código do Ativo': 'KJNT11', 'Quantidade': 500, 'Financeiro': 0.0, 'Incentivo': 0.0, 'Deságio': 1.0, 'Corretagem': 2.0, 'Estado': '100'},
    {'Basket': False, 'id': 0, 'Created': Timestamp('2021-07-01 00:00:00'), 'Creator': '[email protected]', 'Código do Broker': 1234, 'Código do Cliente': 1234, 'Operação': 'Compra', 'Código do Ativo': 'KJNT11', 'Quantidade': 500, 'Financeiro': 500.0, 'Incentivo': 0.0, 'Deságio': 1.0, 'Corretagem': 0.0, 'Estado': '100'},
    {'Basket': True, 'id': 895857873, 'Created': Timestamp('2024-03-20 14:38:48'), 'Creator': '[email protected]', 'Código do Broker': 1234, 'Código do Cliente': 1234, 'Operação': 'Compra', 'Código do Ativo': 'KJNT11', 'Quantidade': 500, 'Financeiro': 0.0, 'Incentivo': 0.0, 'Deságio': 0.0, 'Corretagem': 0.5, 'Estado': '100'},
    {'Basket': True, 'id': 895857873, 'Created': Timestamp('2024-03-20 14:38:48'), 'Creator': '[email protected]', 'Código do Broker': 1234, 'Código do Cliente': 1234, 'Operação': 'Venda', 'Código do Ativo': 'KJNT11', 'Quantidade': 500, 'Financeiro': 0.0, 'Incentivo': 0.0, 'Deságio': 0.0, 'Corretagem': 0.5, 'Estado': '100'}]
)

And this is my working DataFrameSchema:

SchemaFatoBoletaCetipados = pa.DataFrameSchema(
    columns = {
        'Basket': pa.Column(bool, nullable = False, required = True)
        ,'id': pa.Column("int32", nullable = False, required = True)
        ,'Created': pa.Column("datetime64[ns]", nullable = False, required = True)
        ,'Creator': pa.Column(str, nullable = False, required = True, checks = pa.Check.str_matches(r"^.+@manchesterinvest\.com\.br$"))
        ,'Código do Broker': pa.Column("int32", nullable = False, required = True, checks = pa.Check.gt(0))
        ,'Código do Cliente': pa.Column("int32", nullable = False, required = True, checks = pa.Check.gt(0))
        ,'Operação': pa.Column(str, nullable = False, required = True, checks = pa.Check.isin(["Compra", "Venda"]))
        ,'Código do Ativo': pa.Column(str, nullable = False, required = True) # ! Adicionar validação de ativo
        ,'Quantidade': pa.Column("int32", nullable = False, required = True)
        ,'Financeiro': pa.Column("float64", nullable = False, required = True)
        ,'Incentivo': pa.Column("float64", nullable = False, required = True, checks = [pa.Check.ge(0), pa.Check.le(1.5)])
        ,'Deságio': pa.Column("float64", nullable = False, required = True, checks = [pa.Check.ge(0), pa.Check.le(1.5)])
        ,'Corretagem': pa.Column("float64", nullable = False, required = True, checks = [pa.Check.ge(0), pa.Check.le(1.5)])
        ,'Estado': pa.Column(str, nullable = False, required = True)
    }
    ,checks = pa.Check(
        # ? Validação considerando colunas múltiplas https://stackoverflow.com/questions/73828200/pandera-validation-based-on-multiple-columns
        # ? Validação considerando colunas múltiplas https://stackoverflow.com/questions/77758630/pandera-checks-for-multiple-columns
        name = "preenchimento_quantidade_financeiro"
        ,description = "Quantidade e Financeiro não podem estarem ambos preenchidos"
        ,check_fn = lambda FatoBoletaCetipado: (
            ((FatoBoletaCetipado["Quantidade"] == 0) & (FatoBoletaCetipado["Financeiro"] != 0)) |
            ((FatoBoletaCetipado["Quantidade"] != 0) & (FatoBoletaCetipado["Financeiro"] == 0))
        )
    )
)

The problem? The last argument (checks) of DataFrameSchema creates WideCheks on DataFrameSchema-level, and when I'm treating the errors with the function below

try:
    SchemaFatoBoletaCetipados.validate(FatoBoletaCetipados, lazy = True)
except pa.errors.SchemaErrors as ErroDeValidacao:
    ErrosDeValidacao = ErroDeValidacao

The ErrosDeValidacao.failure_cases property returns a DataFrame with all my columns presenting errors and not only "Quantidade" or "Financeiro", as presented bellow:

DataFrame(
    [{'schema_context': 'DataFrameSchema','column': 'Basket','check': 'preenchimento_quantidade_financeiro','check_number': 0,'failure_case': False,'index': 6},
    {'schema_context': 'DataFrameSchema', 'column': 'id', 'check': 'preenchimento_quantidade_financeiro', 'check_number': 0, 'failure_case': 0, 'index': 6},
    {'schema_context': 'DataFrameSchema', 'column': 'Created', 'check': 'preenchimento_quantidade_financeiro', 'check_number': 0, 'failure_case': Timestamp('2021-07-01 00:00:00'), 'index': 6},
    {'schema_context': 'DataFrameSchema', 'column': 'Creator', 'check': 'preenchimento_quantidade_financeiro', 'check_number': 0, 'failure_case': '[email protected]', 'index': 6},
    {'schema_context': 'DataFrameSchema', 'column': 'Código do Broker', 'check': 'preenchimento_quantidade_financeiro', 'check_number': 0, 'failure_case': 1234, 'index': 6},
    {'schema_context': 'DataFrameSchema', 'column': 'Código do Cliente', 'check': 'preenchimento_quantidade_financeiro', 'check_number': 0, 'failure_case': 5311, 'index': 6},
    {'schema_context': 'DataFrameSchema', 'column': 'Operação', 'check': 'preenchimento_quantidade_financeiro', 'check_number': 0, 'failure_case': 'Compra', 'index': 6},
    {'schema_context': 'DataFrameSchema', 'column': 'Código do Ativo', 'check': 'preenchimento_quantidade_financeiro', 'check_number': 0, 'failure_case': 'KJNT11', 'index': 6},
    {'schema_context': 'DataFrameSchema','column': 'Quantidade','check': 'preenchimento_quantidade_financeiro','check_number': 0,'failure_case': 500,'index': 6},
    {'schema_context': 'DataFrameSchema', 'column': 'Financeiro', 'check': 'preenchimento_quantidade_financeiro', 'check_number': 0, 'failure_case': 500.0, 'index': 6},
    {'schema_context': 'DataFrameSchema', 'column': 'Incentivo', 'check': 'preenchimento_quantidade_financeiro', 'check_number': 0, 'failure_case': 0.0, 'index': 6},
    {'schema_context': 'DataFrameSchema', 'column': 'Deságio', 'check': 'preenchimento_quantidade_financeiro', 'check_number': 0, 'failure_case': 1.0, 'index': 6},
    {'schema_context': 'DataFrameSchema', 'column': 'Corretagem', 'check': 'preenchimento_quantidade_financeiro', 'check_number': 0, 'failure_case': 0.0, 'index': 6},
    {'schema_context': 'DataFrameSchema', 'column': 'Estado', 'check': 'preenchimento_quantidade_financeiro', 'check_number': 0, 'failure_case': '100', 'index': 6},
    {'schema_context': 'Column', 'column': 'Corretagem', 'check': 'less_than_or_equal_to(1.5)', 'check_number': 1, 'failure_case': 2.0, 'index': 5}]
)

I want the errors to be presented like the last line, Explicit column that has the error. And yes I've already searched StackOverflow (See my comments in my code) to get on this half solution, and I tried passing the Check inside the pa.Column(checks=check), any thoughts?

0

There are 0 best solutions below