6.16. Curvas e integración

Las páginas de matemáticas cubrieron operaciones que toman un arreglo de entrada y producen un arreglo (o escalar) de salida: aritmética, reducciones, difusión. Esta página cubre una clase distinta de operaciones: las que tratan el arreglo como una función muestreada y plantean preguntas sobre la función en sí. Interpolar entre muestras, ajustar una curva a ellas, integrar bajo ellas, convolucionarlas con otro búfer.

Todas ellas aceptan entradas ndarray y devuelven o bien un escalar float o un ndarray de floats.

6.16.1. Interpolación

interp() realiza interpolación lineal unidimensional. xp es un arreglo 1-D de valores independientes monótonamente creciente; fp son los valores dependientes correspondientes; x es donde debe evaluarse el interpolante:

xp = np.array([0.0, 1.0, 2.0, 3.0])
fp = np.array([10.0, 20.0, 25.0, 30.0])
x  = np.array([0.5, 1.5, 2.5])

np.interp(x, xp, fp)
# array([15.0, 22.5, 27.5])

Fuera del rango [xp[0], xp[-1]] el resultado se recorta a fp[0] y fp[-1] respectivamente; las palabras clave left= y right= anulan esos extremos.

Se usa en la cámara para remapear una tabla de calibración a posiciones de muestra arbitrarias: una tabla de temperatura a voltaje de un termistor, una curva de respuesta de píxel no lineal, una búsqueda gamma por canal. Una llamada de biblioteca, una pasada sobre las entradas, sin bucle de Python.

6.16.2. Ajuste y evaluación de polinomios

polyfit() ajusta un polinomio de grado deg a los puntos de datos (x, y) por mínimos cuadrados y devuelve los coeficientes (de mayor grado primero):

x = np.array([0.0, 1.0, 2.0, 3.0, 4.0])
y = np.array([0.0, 1.1, 3.9, 9.1, 15.8])

coeffs = np.polyfit(x, y, 2)
# array([1.0, 0.0, 0.0])  (approximately)

Cuando se omite x, se usa range(len(y)): útil para ajustar un polinomio rápido a un búfer muestreado regularmente sin un eje x asociado:

np.polyfit(y, 2)

polyval() evalúa en x el polinomio cuyos coeficientes son p. La entrada x puede ser un escalar (devuelve un float) o un ndarray (devuelve un ndarray):

p = np.polyfit(x, y, 2)
fitted = np.polyval(p, x)

El emparejamiento natural es llamar a polyfit una vez en el momento de la calibración, almacenar los coeficientes y llamar a polyval para evaluar la curva resultante en cada fotograma. El paso de evaluación del polinomio es un puñado de operaciones en float por muestra, lo que resulta barato incluso en las cámaras más pequeñas.

6.16.3. Convolución

convolve() devuelve la convolución lineal discreta de longitud completa de dos arreglos 1-D. Solo se implementa el modo full; la longitud de salida es len(a) + len(v) - 1. Recorta el resultado para obtener el mismo efecto que los modos same y valid que ofrece el numpy de escritorio:

a = np.array([1.0, 2.0, 3.0])
v = np.array([0.5, 0.5])

np.convolve(a, v)
# array([0.5, 1.5, 2.5, 1.5])

Útil para filtros FIR cortos y núcleos de suavizado (caja, triángulo, gaussiano) donde montar una cadena SOS sería excesivo. El tiempo de ejecución es proporcional al producto de las longitudes de los dos arreglos: bien para núcleos cortos, pero rápidamente se vuelve más caro que una convolución por FFT para los largos.

6.16.4. Integración trapezoidal

trapz() integra una función muestreada mediante la regla trapezoidal compuesta:

x = np.linspace(0, np.pi, num=128)
y = np.sin(x)
np.trapz(y, x)             # ~ 2.0

Pasa dx= cuando el espaciado de las muestras es uniforme y solo importa el paso; pasa x= cuando las muestras no están espaciadas uniformemente. Es la llamada adecuada para integrar datos de sensor ya capturados, donde no se dispone de la forma analítica.

Para datos de muestra que han sido limitados en banda (un búfer de audio tras un filtro anti-aliasing, por ejemplo), la regla trapezoidal converge como el cuadrado del número de muestras, lo que significa que duplicar la longitud del búfer reduce el error en un factor de cuatro.

Cuando el integrando no es un búfer de muestras, sino una función de Python que la aplicación puede evaluar en puntos arbitrarios, la llamada adecuada es una familia distinta de solucionadores.