9 min de leitura
NumPy #5 - Arredondamento, Logaritmos e Somas/Produtos Acumulados
Como arredondar valores com trunc, floor e ceil, calcular logaritmos com log2, log10 e log, e acumular somas e produtos com cumsum, cumprod e diff no NumPy.
No post anterior da série, você viu o que são as ufuncs e como as operações aritméticas básicas (np.add(), np.subtract() e companhia) são só a porta de entrada desse mecanismo. O catálogo de ufuncs do NumPy é bem maior que isso — hoje a gente segue com funções de arredondamento, logaritmos em diferentes bases, e as formas de acumular somas e produtos ao longo de um array.
Arredondando valores
O NumPy tem várias formas de arredondar um número, e cada uma se comporta de um jeito diferente com valores negativos — vale conhecer as quatro antes de escolher qual usar.
np.trunc() e np.fix()
np.trunc() e np.fix() removem a parte decimal, sempre “cortando” na direção do zero — não importa se o número é positivo ou negativo.
import numpy as np
arr = np.array([-3.1666, 3.6667])
print(np.trunc(arr))
print(np.fix(arr))
A saída é:
[-3. 3.]
[-3. 3.]
Repare que -3.1666 virou -3.0, não -4.0 — a truncagem descarta a parte decimal em vez de arredondar pra baixo. Na prática, trunc() e fix() fazem a mesma coisa; fix() existe como um nome alternativo mais alinhado com outras bibliotecas numéricas.
np.around()
np.around() arredonda para o número de casas decimais que você definir, seguindo a regra de arredondamento matemático (pra cima a partir de 5).
arr = np.array([3.1666, 3.6667])
print(np.around(arr, 2))
A saída é:
[3.17 3.67]
Nota:
np.round()(com “o” duplo) existe como um segundo nome pra exatamente a mesma função —np.round is np.arounddáTrue. Os dois aparecem em código por aí; use o que preferir, não há diferença de comportamento entre eles.
np.floor() e np.ceil()
np.floor() sempre arredonda pra baixo (na direção de menos infinito); np.ceil() sempre arredonda pra cima (na direção de mais infinito) — diferente de trunc(), que arredonda na direção do zero.
arr = np.array([-3.1666, 3.6667])
print(np.floor(arr))
print(np.ceil(arr))
A saída é:
[-4. 3.]
[-3. 4.]
Nota: é fácil confundir
floor()/ceil()comtrunc()/fix()porque em números positivos eles dão o mesmo resultado. A diferença só aparece com negativos:floor(-3.16)é-4(desce), enquantotrunc(-3.16)é-3(corta na direção do zero).
Logaritmos
O NumPy oferece ufuncs prontas pras bases de log mais usadas — base 2, base 10 e natural (base e) — e um jeito de calcular logaritmo em qualquer outra base.
np.log2(), np.log10() e np.log()
arr_log2 = np.array([2, 4, 8, 16, 32])
arr_log10 = np.array([1, 10, 100, 1000])
arr_log = np.array([1, np.e, np.e ** 2])
print(np.log2(arr_log2))
print(np.log10(arr_log10))
print(np.log(arr_log))
A saída é:
[1. 2. 3. 4. 5.]
[0. 1. 2. 3.]
[0. 1. 2.]
Escolhi valores que são potências exatas de cada base só pra deixar o resultado redondo e fácil de conferir — na prática, o array de entrada raramente vem tão arrumado assim.
Log em uma base arbitrária
O NumPy não tem uma ufunc pronta pra “log em qualquer base”, mas dá pra criar uma combinando np.frompyfunc() (visto no post anterior) com math.log(), que aceita a base como segundo argumento.
import math
log_generico = np.frompyfunc(math.log, 2, 1)
resultado = log_generico(100, 15)
print(resultado)
A saída é:
1.7005483074552052
math.log(100, 15) calcula o logaritmo de 100 na base 15. Como vimos no post anterior, frompyfunc(func, 2, 1) diz que a função recebe 2 argumentos de entrada e devolve 1 de saída — o que permite aplicar log_generico() em arrays inteiros, não só em números soltos.
Nota: logs de números não positivos não geram erro — o NumPy retorna
-inf(para 0) ounan(para negativos) e emite um aviso (RuntimeWarning) no lugar de interromper o script.
Somas e produtos acumulados
Além da soma elemento a elemento que você já viu com np.add(), o NumPy tem funções pra somar (ou multiplicar) todos os elementos de um array, com ou sem manter o histórico da acumulação.
np.sum() com axis
np.sum() soma todos os elementos de um array (ou de vários arrays) em um único valor.
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
print(np.sum([arr1, arr2]))
print(np.sum([arr1, arr2], axis=1))
A saída é:
21
[ 6 15]
Sem axis, np.sum() soma absolutamente tudo (1+2+3+4+5+6 = 21). Com axis=1, a soma acontece dentro de cada sub-array separadamente — arr1 soma 6, arr2 soma 15. É a mesma lógica de axis que apareceu no post de join/split: axis=0 percorre linhas, axis=1 percorre colunas.
np.cumsum() — soma acumulada
np.cumsum() não colapsa o array em um único número — ele devolve, pra cada posição, a soma de tudo até ali.
arr = np.array([1, 2, 3, 4])
print(np.cumsum(arr))
A saída é:
[ 1 3 6 10]
O primeiro valor é só 1; o segundo é 1+2=3; o terceiro é 1+2+3=6; e assim por diante. É útil, por exemplo, pra calcular um saldo acumulado dia a dia a partir de uma lista de valores diários.
np.prod() e np.cumprod()
np.prod() e np.cumprod() seguem exatamente a mesma lógica de sum()/cumsum(), trocando soma por multiplicação.
arr = np.array([1, 2, 3])
print(np.prod(arr))
print(np.cumprod(arr))
A saída é:
6
[1 2 6]
np.prod() multiplica tudo (1*2*3=6); np.cumprod() devolve o produto acumulado em cada posição (1, 1*2=2, 1*2*3=6).
np.diff() — diferença entre elementos consecutivos
np.diff() faz o caminho inverso de uma soma acumulada: calcula a diferença entre cada elemento e o anterior.
arr = np.array([10, 15, 25, 5])
print(np.diff(arr))
print(np.diff(arr, n=2))
A saída é:
[ 5 10 -20]
[ 5 -30]
A primeira diferença já era esperada (15-10=5, 25-15=10, 5-25=-20). Com n=2, o NumPy repete a operação — calcula a diferença do resultado anterior ([5, 10, -20] vira [10-5, -20-10] = [5, -30]). É uma forma rápida de detectar variação (por exemplo, entre medições de um sensor) sem escrever um loop manual.
Exemplo prático — juros compostos com cumprod()
Uma aplicação bem direta de cumprod() é calcular o crescimento acumulado de um capital sujeito a taxas de retorno diferentes a cada período — em vez de somar percentuais (o que dá resultado errado quando a taxa é composta), você multiplica os fatores de crescimento.
taxas = np.array([1.02, 1.03, 0.98, 1.05])
crescimento_acumulado = np.cumprod(taxas)
print(crescimento_acumulado)
A saída é:
[1.02 1.0506 1.029588 1.081067]
Cada 1.02, 1.03, 0.98 e 1.05 representa um fator de crescimento por período (1.02 = alta de 2%, 0.98 = queda de 2%). Depois de 4 períodos, o capital original ficou multiplicado por 1.081067 — um crescimento acumulado de cerca de 8,1%, mesmo com um período de queda no meio do caminho. Sem cumprod(), você precisaria de um loop multiplicando o resultado anterior a cada iteração.
Arredondamento, logaritmos e acumulação cobrem boa parte do dia a dia com ufuncs, mas ainda faltam alguns clássicos — mínimo múltiplo comum, máximo divisor comum, trigonometria e operações de conjunto. É pra lá que a gente vai no próximo post da série.
Fonte adaptada: NumPy Rounding Decimals, NumPy Logs, NumPy Summations, NumPy Products, NumPy Differences
Comentários