Paquete gnuplot

De Wikihaskell
Saltar a: navegación, buscar
Paquete gnuplot
Gráficos en 2D y 3D utilizando gnuplot
Lenguaje Haskell
Biblioteca gnuplot
Autores Cristina Jiménez Gavilán
Juan Carlos Ramirez Navarro
Emilio De Torres Fernandez

Graphics.Gnuplot es un paquete que funciona tanto en el interprete GHCi como en Hugs. Hace las funciones de interfaz para interactuar con la aplicación 'gnuplot'.


gnuplot es un programa muy flexible para generar gráficas de funciones y datos. Este programa es compatible con los sistemas operativos más populares (Linux, UNIX, Windows, Mac OS X...). El origen de gnuplot data de 1986. gnuplot puede producir sus resultados directamente en pantalla, así como en multitud de formatos de imagen, como PNG, EPS, SVG, JPEG, etc.

Wikipedia.org


Este paquete se divide básicamente en dos partes:

  • Simple o Graphics.Gnuplot.Simple, su misión no es la de explotar por completo las posibilidades de gnuplot, así como todas sus capacidades. Sin embargo, es un buen módulo para poder empezar a controlarlo y hacer pequeños gráficos de poca complejidad.
  • Advanced o Graphics.Gnuplot.Advanced, es una parte del módulo que nos permite un uso mucho más complejo y avanzado de gnuplot. Esto complica su uso a cambio de un mayor aprovechamiento de la aplicación.


El módulo Gnuplot tiene una serie de dependencias:


Contenido

Instalación

Para Hugs

Descargamos Gnuplot para Haskell y sus dependencias.


Descomprimimos el primer fichero (gnuplot-0.3.3.tar.gz) y copiamos la carpeta gnuplot-0.3.3/src/Graphics en el directorio /usr/lib/hugs/packages

$ sudo cp -r gnuplot-0.3.3/src/Graphics /usr/lib/hugs/packages/.

A continuación, descomprimimos, los dos módulos de dependencia (utility-ht-0.0.5.1.tar.gz y monoid-transformer-0.0.2.tar.gz) y copiamos el directorio utility-ht-0.0.5.1/src/Data y monoid-transformer-0.0.2/src/Data en el directorio /usr/lib/hugs/packages haciendo una 'mezcla' de ellos.

$ sudo cp -r utility-ht-0.0.5.1/src/Data /usr/lib/hugs/packages/.
$ sudo cp -r monoid-transformer-0.0.2/src/Data /usr/lib/hugs/packages/.
$ sudo cp gnuplot-0.3.3/execute/shell/Graphics/Gnuplot/Execute.hs /usr/lib/hugs/packages/Graphics/Gnuplot/.

Una vez todo instalado ya podemos abrir desde una consola $ hugs y cargar alguno de los dos modulos principales. Por ejemplo el Simple.

Hugs> :load Graphics.Gnuplot.Simple

Para GHCi

Aún no existe la versión 6.10 de GHC para Ubuntu así que es necesario realizar su instalación de manera manual. A la hora de descargarnos el archivo para la instalación de ghc-6.10.4 tendremos que seleccionar si nuestra arquitectura es x86 o x86_64.

  • Después de realizar la descarga es necesario descomprimirla:
Si tenemos x86
 $ tar jxvf ghc-6.10.4-i386-unknown-linux-n.tar.bz2
Si tenemos x86_64
 $ tar jxvf ghc-6.10.4-x86_64-unknown-linux-n.tar.bz2
  • Una vez descomprimido entramos en el directorio
$ cd ghc-6.10.4/
  • Ejecutamos
$ sudo ./configure
  • Realizamos la instalación
$ sudo make install


El paquete gnuplot lo vamos a instalar utilizando cabal. Si aún no disponemos de cabal serán necesarios los siguientes pasos:

  • Descargarnos el paquete
  • Descomprimirlo
$ tar -zxf cabal-install-0.6.2.tar.gz
  • Entrar en el directorio
$ cd cabal-install-0.6.2
  • Instalar las dependencias de cabal
$ sudo apt-get install libghc6-parsec-dev libghc6-network-dev libghc6-http-dev libghc6-mtl-dev zlib1g-dev
  • Ejecutar
$ ./bootstrap.sh
  • Copiar la biblioteca
$ sudo cp ~/.cabal/bin/cabal /usr/local/bin/
  • Actualizar la lista de paquetes disponibles para instalar desde cabal
$ cabal update


Ahora ya solo queda instalar el paquete gnuplot

$ cabal install gnuplot

Descripción

Este paquete permite el empleo de Gnuplot con Haskell facilitando la generación de gráficos 2D y 3D en varios formatos (LaTeX, metafont, pdf, svg o png).

El módulo "Graphics.Gnuplot.Simple" está listo para usar en GHCi, luego si comenzamos una sesión con @make ghci@, se carga directamente y ya podemos comenzar a experimentar con la generación de gráficos simples. En el caso de que deseemos trabajar con gráficos más sofisticados, especialmente si se quiere generar gráficos por lotes, será necesario cargar el módulo "Graphics.Gnuplot.Advanced".

Con los flags executePipe y executeShell de Cabal podemos probar otras formas más prácticas, pero seguramente también menos portables, de ejecutar gnuplot.

Anteriormente este paquete se distribuía como parte del paquete htam.

Los principales módulos incluidos en el paquete gnuplot son:

Graphics
Gnuplot
Graphics.Gnuplot.Advanced
Graphics.Gnuplot.Frame
Graphics.Gnuplot.Frame.Option
Graphics.Gnuplot.Frame.OptionSet
Graph
Graphics.Gnuplot.Graph.ThreeDimensional
Graphics.Gnuplot.Graph.TwoDimensional
Graphics.Gnuplot.LineSpecification
Graphics.Gnuplot.MultiPlot
Plot
Graphics.Gnuplot.Plot.ThreeDimensional
Graphics.Gnuplot.Plot.TwoDimensional
Graphics.Gnuplot.Simple
Terminal
Graphics.Gnuplot.Terminal.PNG
Graphics.Gnuplot.Terminal.PostScript
Graphics.Gnuplot.Terminal.SVG
Graphics.Gnuplot.Terminal.X11
Graphics.Gnuplot.Time

También están disponibles otros módulos:

Graphics.Gnuplot.Private.LineSpecification
Graphics.Gnuplot.Private.Plot
Graphics.Gnuplot.Private.Frame
Graphics.Gnuplot.Private.FrameOption
Graphics.Gnuplot.Private.FrameOptionSet
Graphics.Gnuplot.Private.GraphType
Graphics.Gnuplot.Private.Graph
Graphics.Gnuplot.Private.Graph2D
Graphics.Gnuplot.Private.Graph3D
Graphics.Gnuplot.Private.GraphEmpty
Graphics.Gnuplot.Utility

Graphics.Gnuplot.Simple

Se trata de una interfaz simple y monolítica para gnuplot que, como ya se ha mencionado, da soporte a sus características básicas y puede ser usada tanto en GHCi como en Hugs.

A continuación vamos a ver algunas de las funciones y tipos más usados como ejemplo del contenido de este paquete:

  • Size: Define dos tipos de datos para el atributo tamaño.
  data Size =
              Scale    Double
            | SepScale Double Double
  • plot2d: Dibuja una función en dos dimensiones llamando a gnuplot con los atributos especificados.
  plot2d :: [Attribute] -> Plot2D.T -> IO ()
  • epspdfPlot: Redirige la salida de una función a un fichero EPS y además, lo convierte en PDF.
  epspdfPlot :: FilePath -> ([Attribute] -> IO ()) -> IO ()
  -- ([Attribute] -> IO ()) es el tipo de una función de dibujo que recibe ciertos atributos de gnuplot.

Graphics.Gnuplot.Advanced

Se trata de una interfaz modularizada para gnuplot que permite dibujar gráficos más complejos, así como un buen control de sus componentes. Este módulo está diseñado para un uso no interactivo, como podría ser la realización de scripts para pintar estadísticas.

La jerarquía de objetos del módulo avanzado es la siguiente:

  • Graph2D: Una curva (por ejemplo, una curva sinusoidal). Los atributos de la curva con los que se puede trabajar son el tipo de línea, su grosor y el color.
  • Graph3D: Una superficie.
  • Plot: Una superposición de varias curvas.
  • Frame: Añade opciones a plot tales como borde, leyenda, título, etiquetas para los atributos, etc.
  • MultiPlot: Coloca un número determinado de frames en una matriz para su manejo.

La sinopsis del módulo avanzado se resume a la función plot:

  plot :: (C terminal, C gfx) => terminal -> gfx -> IO ExitCode

gfx puede tomar el valor de cualquiera de los tipos: Plot, Frame, MultiPlot.

Como se puede ver, plot devuelve el tipo ExitCode, hecho que facilita la programación siempre y cuando no estemos trabajando en sesiones interactivas con GHCi (en este último caso sería más conveniente utilizar Graphics.Gnuplot.Simple):

  data ExitCode
  = ExitSuccess
  | ExitFailure Int

A continuación se muestra la descripción de sus dos constructores:

  • ExitSuccess: indica que el programa ha terminado con éxito.
  • ExitFailure Int: determina un fallo en el programa y el código de salida asociado, cuyo significado dependerá de cada sistema operativo concreto.

Ejemplo de uso

Vamos a ver un ejemplo de uso de la biblioteca Gnuplot que contiene a su vez tres ejemplos: el primero de ellos imprime un gráfico simple, el segundo, una gráfica multifunción y el tercero, imprime varias gráficas en un mismo marco.

El paquete gnuplot crea los archivos necesarios para generar las gráficas ejecutándolos con Gnuplot. Por ello, es necesario tener instalada esta utilidad, así que el primer paso será el siguiente:

$ sudo apt-get install gnuplot

El código del ejemplo lo vamos a guardar en ejemplo.hs:

module Main where

import qualified Graphics.Gnuplot.Advanced as Plot
import qualified Graphics.Gnuplot.Terminal.X11 as X11
import qualified Graphics.Gnuplot.MultiPlot as MultiPlot
import qualified Graphics.Gnuplot.Frame as Frame
import qualified Graphics.Gnuplot.Frame.Option as Opt
import qualified Graphics.Gnuplot.Frame.OptionSet as Opts
import qualified Graphics.Gnuplot.Plot.ThreeDimensional as Plot3D
import qualified Graphics.Gnuplot.Plot.TwoDimensional as Plot2D
import qualified Graphics.Gnuplot.Graph.TwoDimensional as Graph2D
import Graphics.Gnuplot.Plot.TwoDimensional (linearScale, )
import Data.Array (listArray, )
import Data.Monoid (mappend, )

-- Ejemplo 1
simple2d :: Plot2D.T
simple2d =
   Plot2D.function (linearScale 100 (-10,10::Double)) sin

-- Ejemplo 2
circle2d :: Plot2D.T
circle2d =
   fmap
     (Graph2D.typ Graph2D.points)
     (Plot2D.parameterFunction
        (linearScale 24 (-pi,pi::Double))
        (\t -> (cos t, sin t)))
overlay2d :: Frame.T Graph2D.T
overlay2d =
   Frame.cons (Opts.size 1 0.4 $ Opts.remove Opt.key $ Opts.deflt) $
   Plot2D.function (linearScale 100 (-pi,pi::Double)) cos
   `mappend`
   circle2d

-- Ejemplo 3
multiplot2d :: MultiPlot.T
multiplot2d =
   let opts :: Opts.T graph
       opts =
          Opts.remove Opt.key $
          Opts.deflt
       (prefix,suffix) =
          splitAt 7 $
          map MultiPlot.partFromFrame $
          map (\k ->
             Frame.cons (Opts.xRange (-1,1) opts) $
             Plot2D.parameterFunction
                (linearScale 48 (-pi,pi::Double))
                (\t -> (cos (t + pi/7*fromInteger k), sin (2*t)))) $
          [0..13]
       meshNodes = linearScale 20 (-2,2::Double)
       center =
          MultiPlot.partFromFrame $
          Frame.cons
             (Opts.xRange (-2.5,2.5) $
              Opts.yRange (-2.5,2.5) $
              opts) $
          (Plot3D.function
             meshNodes meshNodes
             (\x y -> cos(x*x+y*y)))
   in  MultiPlot.simpleFromPartArray $
       listArray ((0::Int,0::Int), (2,4)) $
       prefix ++ center : suffix

-- Programa principal
main :: IO ()
main =
   do Plot.plot X11.cons simple2d
      Plot.plot X11.cons overlay2d
      Plot.plot X11.cons multiplot2d
      return ()

Compilamos el archivo:

$ ghc --make ejemplo.hs -o salida

Lo ejecutamos

$ ./salida

Obtenemos las siguientes gráficas:

Explicación del ejemplo

  • El primer ejemplo pinta una gráfica con una única función:
simple2d :: Plot2D.T
simple2d =
   Plot2D.function (linearScale 100 (-10,10::Double)) sin

Plot2D.function recibe una lista de valores imprimibles y una función con prototipo a -> a y dibuja el resultado de aplicar dicha función a la lista de valores. Por tanto, este primer ejemplo representa el seno del intervalo (-10,10).

  • El segundo ejemplo pinta más de una función en una gráfica:
circle2d :: Plot2D.T
circle2d =
   fmap
      (Graph2D.typ Graph2D.points)
      (Plot2D.parameterFunction
         (linearScale 24 (-pi,pi::Double))
         (\t -> (cos t, sin t)))

(Graph2D.typ Graph2D.points) crea un punto en el espacio en dos dimensiones. Plot2D.parameterFunction es idéntica a Plot2D.function, pero la función que recibe como parámetro devuelve una tupla (a->(a,a)). En este caso, se crean 24 valores Double entre -pi y pi y devuelve los pares formados por el coseno y el seno de dichos valores.

overlay2d :: Frame.T Graph2D.T
overlay2d =
   Frame.cons (Opts.size 1 0.4 $ Opts.remove Opt.key $ Opts.deflt) $
   Plot2D.function (linearScale 100 (-pi,pi::Double)) cos
   `mappend`
   circle2d

Frame.cons recibe primero las opciones gráficas. Seguidamente, dibuja la función coseno en el intervalo (-pi, pi) con Plot2D.function. mappend es el valor de Frame.T que indica el final. Por último, ejecuta la función que nos dibuja un círculo en la gráfica con circle2d.

  • El tercer ejemplo pinta más de una gráfica:

Primeramente, se crean las opciones gráficas (opts):

multiplot2d :: MultiPlot.T
multiplot2d =
   let opts :: Opts.T graph
       opts =
          Opts.remove Opt.key $
          Opts.deflt

Seguidamente:

  1. Se crean por separado cada una de las gráficas con su Frame (segundo map).
  2. Se crean los scripts necesarios para mostrar cada una de las gráficas anteriores (primer map).
  3. Se reparten los scripts: los 7 primeros en prefix y los 7 últimos, en suffix (con splitAt).
  4. Especificamos el número de gráficas distintas a mostrar (14 en este caso)
       (prefix,suffix) =
          splitAt 7 $
          map MultiPlot.partFromFrame $
          map (\k ->
             Frame.cons (Opts.xRange (-1,1) opts) $
             Plot2D.parameterFunction
                (linearScale 48 (-pi,pi::Double))
                (\t -> (cos (t + pi/7*fromInteger k), sin (2*t)))) $
          [0..13]
       meshNodes = linearScale 20 (-2,2::Double)

A continuación:

  1. Se crea la gráfica en 3D que aparece en medio de la imagen (con Plot3D.function).
  2. Se coloca la gráfica en un Frame (Frame.cons).
  3. Se obtiene el script para mostrar la imagen 3D (con MultiPlot.partFromFrame).
       center =
          MultiPlot.partFromFrame $
          Frame.cons
             (Opts.xRange (-2.5,2.5) $
              Opts.yRange (-2.5,2.5) $
              opts) $
          (Plot3D.function
             meshNodes meshNodes
             (\x y -> cos(x*x+y*y)))

Por último, se compone la imagen como una matriz 3x5 situando las gráficas de prefix en las posiciones que van desde (0,0) hasta (1,1), las de suffix desde (1,3) hasta (2, 4) y la gráfica 3D en (1,2).

   in  MultiPlot.simpleFromPartArray $
       listArray ((0::Int,0::Int), (2,4)) $
       prefix ++ center : suffix
  • Finalmente, desde el main se llama con plot a las funciones anteriores para representar los tres casos:
main :: IO ()
main =
   do Plot.plot X11.cons simple2d 
      Plot.plot X11.cons overlay2d
      Plot.plot X11.cons multiplot2d
      return ()

Referencias externas

Herramientas personales