Interpolación lineal en Ruby
Alfonso Egio
La interpolación lineal de datos
es una tarea llevada a cabo de forma intensiva en cualquier
tipo de análisis numérico como los llevados
a cabo en laboratorios, consultorías de estudios financieros,
bolsa y etc ...
El problema pasa por partir de una serie
de valores tabulados sobre los cuales pretendemos estimar
valores intermedios que se encuentran a caballo entre dos
datos registrados. La manera más inmediata de conseguirlo es
la de hacer pasar una recta por los puntos precedente y posterior
al valor sobre el cual se pretende la estimación y extraer
de ésta última el valor estimado.
Por ejemplo y para clarificar ideas
supongamos que tenemos un fichero llamado "data.dat"
en el que constan los siguientes valores tabulados:
1.0 3.0
2.0 9.0
3.0 4.0
4.0 -3.0
5.0 10.0
Si ahora deseamos conocer el valor de la estimación
construida a partir de dichos datos para x = 2.5 podemos
proceder según ilustra el gráfico adjunto para obtener
la respuesta estimada de forma lineal: yaproximada = 6.5.
Interpolación lineal en Ruby de los datos contenidos
en el fichero "data.dat"
Código Ruby
class NumericalData
attr_accessor :data
def initialize
@data = []
end
def interpolate(x)
if ( pnt = @data.select { |d| x == d[:x] }.shift )
return pnt[:y]
end
prv = @data.select { |d| x > d[:x] }.pop
nxt = @data.select { |d| x < d[:x] }.shift
if (prv and nxt)
a = (nxt[:y] - prv[:y])/(nxt[:x] - prv[:x])
return a*(x-prv[:x]) + prv[:y]
end
nil
end
def read(filename,column)
@data = []
File.open(filename).each do |l|
t,*arr = l.split(/\s+/).collect { |n| n.to_f }
@data.push({ :x => t , :y => arr[column-2]})
end
self
end
end
f = NumericalData::new()
f.read("data.dat",2)
puts f.interpolate(2.5)
He implementado algoritmos similares
en muchas ocasiones y lenguajes de programación distintos,
no obstante, estoy iniciándome en
Ruby y me
ha llamado la atención el hecho de que no se hace necesario
ningún tipo de loop o bucle para realizar la interpolación
lineal descrita anteriormente. El uso abstracto de bloques a partir
de filtros como:
- select (selecciona valores de una lista)
- shift (extrae el primer elemento de una lista)
- pop (extrae el último elemento de una lista)
- collect (opera y acumula sobre elementos de una lista)
es suficiente para conseguir nuestro objetivo
de forma muy compacta y natural revelándonos algunos aspectos
de la verdadera potencia de Ruby gracias a su tratamiento generalizado
de lo que conocemos como bloques de
código en lenguajes como C o Perl.