Filtrar o data usando ID == ‘string’ no Pandas é algo que o senhor deve evitar, pois o operador scalar_compare leva a gargalos de desempenho. Há muitas maneiras de contornar isso, por exemplo, particionando o quadro Data em um dicionário usando o ID em questão como chave.
Introdução | Filtros de string no pandas
A simplificação do gerenciamento de hardware, proporcionada pelas soluções cloud, faz com que cada vez mais nos afastemos dos problemas de otimização de código.

Mas o aumento de escala e aumentar a capacidade de computação nem sempre é a solução pois isso leva a um aumento dos custos e a capacidade de computação não é infinita.
Ao me desafiar a encaixar toda a minha preparação para o data em um contêiner simples, percebi rapidamente que, com o conhecimento de alguns truques, o senhor pode fazer o que quiser, otimizar seu código às vezes pode ser tão simples quanto instanciar uma instância maior.
Neste artigo, gostaria de voltar a uma única alteração de código que me permitiu reduzir drasticamente o tempo gasto no cálculo dos recursos durante a fase de desenvolvimento de um modelo de propensão. Essa alteração é comum o suficiente para ser aplicada a muitas outras situações.

Ele não tem como objetivo ser a solução mais ideal, mas procura ser uma opção rápida para diminuir o tempo de computação de forma eficiente, no espírito do Princípio de Pareto.
Contexto
Durante essa missão, fui encarregado de automatizar o processo de preparação do data e a previsão de modelos desenvolvidos por nossa equipe de cientistas do Data. Para simplificar o fluxo do data, o data transacional era carregado todos os dias e tinha de passar por uma primeira Pré-processamento seguida de uma segunda etapa de Recurso Computação antes de chegar à última etapa do Previsão de modelo usando o modelo treinado.

O Computação de recursos é a fase que levou mais tempo para ser executada: na verdade, muitos recursos foram computados no nível do cliente, o que resultou na execução recorrente de uma linha surpreendentemente demorada no código:

Essa única linha foi cronometrada em 18 ms, o que significa que, com meus 33.717 clientes para avaliar diariamente, eu estava gastando cerca de 10 minutos de tempo bruto de computação por recursos de nível de cliente, reduzido para 1 minuto e 16 segundos por recurso graças à paralelização da operação em minhas 8 CPUs disponíveis.
Como estávamos trabalhando com o B2B data, foi necessário computar os recursos no nível do cliente, pois um cliente representava, na verdade, uma empresa com, às vezes, vários pedidos por dia.
Experimentação | Filtros de string no pandas
Depois de investigar um pouco usando o %%prun Consegui identificar a origem desse gargalo de processamento: o pandas._libs.ops.scalar_compare que estava subotimizado na minha versão do Pandas (1.3.1).

Simplesmente substituindo esse “==” pelo operador “isin”, o que não é muito intuitivo, pois eu estava comparando uma única string, já dividi o tempo de computação por 2,5 vezes, passando de 18ms por operação para 7,95ms.

Ainda em busca de otimização, deparei-me com um Stackoverflow post promovendo o uso de Tipo categórico para melhorar ainda mais a operação.

Essa última implementação me permitiu dividir o tempo de computação em mais de 36 vezes. No entanto, pude observar uma nuance nesse truque, pois o tipo de categoria não se comporta como um str clássico durante todas as operações (veja o exemplo abaixo ao usar .groupby() no pandas), então tive que convertê-lo de volta para str em um ponto.

Hipótese
Mas por que isso acontece? Como pode um simples == entre duas strings leva mais tempo do que uma operação de isin() comparando listas, ou uma operação categórica um ?
Bem, para responder à primeira pergunta, precisaríamos definitivamente descobrir o código por trás da execução do scalar_compare que usa principalmente Cython e compará-lo com o código por trás do isin() método.
Felizmente, a resposta para a segunda parte parece ser mais intuitiva: ao comparar dois valores de cadeia de caracteres, estamos comparando um número infinito de possibilidades juntas, enquanto que ao comparar duas categorias, o o número de opções é definido pelas diferentes categorias exclusivas que existem. Parece muito é mais fácil comparar duas entidades quando nosso número de opções é fixo.
Como minha sede por otimização ainda não estava saciada, decidi dar um passo atrás em relação ao método atual. Criei uma nova abordagem: Particionar meu Dataframe em um dicionário que usarei para filtrar meus clientes ao calcular meus recursos.
Em termos de código, isso se traduziu simplesmente nas seguintes linhas:
A criação desse dicionário me custou 32 segundos de tempo de computação, mas usando esse quadro Data particionado, consegui filtrar meu data em alguns nanossegundos.

Conclusão | Filtros de string no pandas
Depois de passar algumas horas na fase de experimentação, fiquei satisfeito com o resultado:

O tempo inicial de computação por filtragem de cliente foi agora dividido 348 000 vezes, indo de 18ms a 51,7ns, ou de 10min a 2,65ms por recurso calculado no meu caso, levando em conta o tempo gasto no particionamento.

Imediatamente, o impacto dessa pequena mudança me permitiu reduzir o tempo de cálculo da minha fase completa de computação de recursos em 90%, de 40’49” para 7’27”. Usando um método de estimativa de CO2eq que detalharei em meu próximo artigo, essa modificação economizou pelo menos 170$/ano + 22kgCO2/ano e potencialmente muito mais com a crescente lista de clientes e a implementação do projeto em outros países.

BLOG







