Solucionado (ver solução)
Solucionado
(ver solução)
2
respostas

[Dúvida] filtrando e combinando qual vantagem de não fazer com query?

Fizemos assim:

selecao1 = df['Quartos'] == 1
selecao2 = df['Valor'] < 1200
selecao_final = (selecao1) & (selecao2)
df[selecao_final]

mas porque não fazer assim:

df_3 = df.query('Quartos == 1 and Valor < 1200')
df_3

Muito mais claro, tem diferenças significativas de performance ou outro critério?

Fiz um teste com %%timeit para medir a o tempo de execução

A primeira forma é quase 6x mais rápida que a query

Insira aqui a descrição dessa imagem para ajudar na acessibilidade
Numa análise exploratória a segunda forma pode ser mais clara, já se eu tivesse que colocar dentro de um loop por alguma razão a primeira forma faria diferença.

Mas, ainda ficariam mais perguntas pra analisar, aqui o dataset é pequeno , será que num dataset grande teríamos a mesma diferença ou o segundo método pode superar o primeiro de alguma forma? Talvez, nâo necessariamente em performance direta, mas consumo de memória?

2 respostas
solução!

Olá, Marcelo. Como vai?

Sua dúvida é espetacular e o fato de você ter usado o comando mágico %%timeit para testar a performance por conta própria mostra que você tem o DNA de um verdadeiro Cientista de Dados. Parabéns pela postura investigativa!

A sua constatação está perfeitamente correta: para DataFrames pequenos e médios, a filtragem tradicional por máscaras booleanas (a primeira forma) é significativamente mais rápida que o método .query().

Vamos entender os motivos técnicos por trás dessa diferença de tempo, como o comportamento muda quando escalamos para volumes massivos de dados (Big Data) e os impactos na memória.


Por que o .query() é mais lento em datasets pequenos?

A elegância do .query('Quartos == 1 and Valor < 1200') tem um preço de processamento oculto. Quando você executa esse método, o Pandas não roda o filtro imediatamente; ele precisa realizar três etapas antes:

  1. Interpretação da String: O Pandas precisa ler a String que você digitou e traduzi-la em uma expressão lógica válida.
  2. Compilação: Ele utiliza uma biblioteca interna chamada numexpr (ou o próprio interpretador Python) para compilar essa string em código de máquina.
  3. Avaliação: Aí sim, ele aplica o filtro nas colunas.

Toda essa etapa de parsing e compilação gera um custo fixo de processamento (overhead). Na filtragem tradicional (df[selecao_final]), você está conversando diretamente com os arrays do NumPy em baixo nível. Não há tradução de texto; a operação matemática vetorial acontece de forma instantânea na memória.


O jogo vira com datasets gigantescos? (Performance vs Memória)

A sua intuição sobre o tamanho do dataset e o consumo de memória foi cirúrgica. Quando saímos de milhares de linhas e vamos para milhões de linhas, o comportamento muda por conta do motor de execução numexpr que o .query() usa por baixo dos panos.

1. Consumo de Memória

Na filtragem tradicional, quando você faz selecao1 = df['Quartos'] == 1, o Pandas cria um novo array booleano intermediário na memória (uma série de True e False). Em datasets massivos, criar essas variáveis intermediárias (selecao1, selecao2, selecao_final) pode estourar a memória RAM da sua máquina (Out of Memory Error).

O .query(), por utilizar o numexpr, consegue avaliar a expressão inteira de uma vez e otimizar a criação desses blocos temporários diretamente nos caches do processador, gastando muito menos memória RAM.

2. Performance em Big Data

À medida que o tamanho do DataFrame cresce, o tempo gasto para processar as linhas supera o custo fixo de interpretar a String do .query().

Como o numexpr é capaz de realizar computação paralela multithread (dividindo o processamento entre os núcleos da sua CPU), em datasets muito grandes o .query() passa a ser tão rápido ou até mais rápido que a máscara booleana tradicional.


Resumo dos Critérios de Escolha

Para resumir e te dar uma diretriz prática no dia a dia dos seus projetos:

CritérioMáscara Tradicional (df[...])Método .query()
Datasets Pequenos/MédiosMuito mais rápida (Sem overhead de interpretação)Mais lenta devido ao parse da string.
Datasets Gigantes (Milhões de linhas)Alto consumo de memória por criar arrays intermediários.Otimiza a memória e aproveita paralelismo.
LegibilidadePode ficar confusa com muitos operadores (&, `, ~`).
Uso em Loops / FunçõesIdeal. É direta e fácil de passar variáveis nativas.Exige o uso do caractere @ para ler variáveis de fora (ex: @limite).

Sua análise foi cirúrgica e com certeza esse benchmark que você trouxe vai clarear a mente de muitos alunos que ficam na dúvida entre qual sintaxe escolher. Continue com essa mentalidade analítica!

Espero que possa ter lhe ajudado!

Legal, a sua resposta, mas ou menos como pensei. Além disso creio que para dados muito muito grandes não seja a ferramenta ideal, arquivos binários como parquet, feather podem ser mais apropriados, ou uso de outras ferramentas como polars, dask ou spark (distribuido).

a parte que fala sobre cpu cache e paralelismo ficou sensacional.

Obrigado pelo feedback.