Tema 1: Nociones básicas sobre IDL

Anuncio
Tema 1: Nociones básicas sobre IDL
Para entrar en IDL (Interactive Data Language) 1 desde la lı́nea de comandos debe ejecutarse idl o bien idlde (IDL Development Environment). En
estas notas supondremos que entramos tecleando idl pero si prefieres usar
la otra opción será todo muy similar. Si todo va bien, al entrar debe aparecer
finalmente la lı́nea de comandos siguiente:
IDL>
IDL posee ayuda dentro de su propio entorno que se obtiene escribiendo:
IDL>?
No dudes en consultarla cuando sea necesario. Para salir ejecutamos el
comando:
IDL>exit
IDL puede usarse ejecutando programas previamente escritos (lo habitual en cuanto tengamos algo relativamente complicado que programar) o
bien directamente, a través de la lı́nea de comandos. En este primer tema
aprenderemos algunas nociones básicas usando la lı́nea de comandos directamente.
1.
Forma de los comandos
Para mostrar por pantalla el resultado de una operación podemos escri-
bir:
IDL> print,3.1*5.1234
15.8825
Aquı́ print es el nombre del comando (llamado en IDL procedure, procedimiento). Cada comando va siempre seguido de una coma (,). Finalmente
3.1*5.1234 (3.1 multiplicado por 5.1234) es el argumento.
Cuando tengamos más de un argumento los separamos mediante comas:
1
IDL requiere de una licencia comercial. Existe una versión libre compatible (aunque
no 100 %) que funciona bajo LINUX: GDL - GNU Data Language. Puedes descargarla de:
http://gnudatalanguage.sourceforge.net/
1
IDL> print,10^4,cos(0.)
10000
1.00000
Los comandos admiten también opciones denominadas keywords (palabras claves). La forma general que tienen es:
procedure,argumento_1,...,keyword_1=opción,...
En los ejemplos anteriores puede especificarse el formato de salida mediante
la opción format. Si escribimos:
IDL> print,3.1*5.1234,format=’(f7.3)’
15.883
forzamos a que nos muestre el resultado del producto como un número real
(f) de 7 caracteres (5 dı́gitos más el punto más el signo menos si lo hubiera
habido) con 3 decimales. Si usamos el formato (f20.16) veremos que nos
aparecen 16 decimales aunque eso no significa que el resultado tenga esa
precisión; en ningún caso debemos confundir la presentación (que es lo que
nos da la opción format) con la precisión del cálculo.
Ejercicio 1.
Considera la operación:
IDL> print,5./3.
1.66667
¿Cuántos decimales significativos tiene el resultado? 2
Otra forma usual de escribir el resultado es con potencias de diez. El
formato es similar al anterior pero con e en lugar de f. Por ejemplo si
escribimos:
IDL> print,3.1*5.1234,format=’(e11.4)’
1.5883e+01
vemos que el resultado (1.5883 · 101 ), quince y pico, aparece con 4 decimales.
El número 11 representa el número total de caracteres que contiene el resultado, a saber: el signo inicial (en este caso al ser un + deja un blanco), el
dı́gito antes del punto, el propio punto, los cuatro decimales, la letra e (que
significa diez elevado a), el signo del exponente (en este caso un +) y los
dos dı́gitos del exponente. Es decir 7 caracteres más el número de decimales.
Como regla general podemos usar 8+número de decimales que queramos (en
doble precisión el exponente puede tener tres dı́gitos).
2
Puedes consultar la respuesta a los ejercicios al final del documento.
2
2.
Variables
La forma más sencilla de crear una variable es asignándole un valor. Por
ejemplo, si escribimos:
IDL> a=3*5
habremos creado la variable A (no se distingue entre mayúsculas y minúsculas) y asignado el valor 3 × 5.
Podemos usar cualquier combinación de letras y números para los nombres de las variables (también se admite el subı́ndice y el $) pero siempre
deben empezar por una letra. Por ejemplo son válidas las variables: mas,
vec, a1, velo2, x 1, x$a. No es válida 3b.
Las variables tienen dos atributos importantes:
1. Tipo de datos. La variable A anterior es de tipo INTEGER (entero).
También pueden ser de tipo real, doble precisión, complejo, etc.
2. Tipo de estructura. La variable A anterior es un escalar. Hay también
vectores, arrays n x m (matrices), etc.
En cada momento podemos ver el tipo de variable y estructura (en el
caso de los escalares su valor) mediante el comando help. Por ejemplo:
IDL> help,a
A
INT
=
15
Las variables pueden cambiar sus atributos como consecuencia de una
operación. Por ejemplo si hacemos:
IDL> a=sqrt(a)
IDL> help,a
A
FLOAT
=
3.87298
vemos que la variable A pasa a ser un número de tipo FLOAT (floating point
number o número con coma flotante) correspondiente a los números reales de
menor precisión en IDL, concretamente entre 6 y 7 decimales significativos.
Las operaciones anteriores pueden escribirse en una sola lı́nea usando el
sı́mbolo &:
IDL> a=3*5 &
A
a=sqrt(a) & help,a
FLOAT
=
3.87298
En otros lenguajes de programación que probablemente conozcas las variables se definen una vez, digamos al principio del programa, y luego siempre
son del mismo tipo (las variables enteras pueden cambiar su valor pero siempre serán enteras, etc.). En IDL no. El hecho de que las variables se definan
de forma dinámica tiene sus ventajas: en el ejemplo anterior se calcula la
3
raı́z cuadrada de un número como se hace usualmente y en consecuencia
se transforma en real. Pero es importante no olvidarse de las desventajas,
en concreto podemos terminar pensando que siempre va a funcionar como
intuitivamente esperamos y hay excepciones. En este otro ejemplo:
IDL> a=1 & b=3
IDL> print,a/b
0
¡1/3=0! La división de dos números enteros es la parte entera del cociente.
¿Era eso lo que querı́amos? posiblemente no. Si alguno de los números de un
cociente es real el resultado es un número real. Ası́, en el ejemplo anterior,
si no queremos que tome sólo la parte entera podemos escribir:
IDL> a=1. & b=3.
IDL> print, a/b
0.33333
;ahora, al a~
nadir el punto, A y B son reales.
Hemos usado el punto y coma (;) para indicar que lo que sigue es un comentario. Puede estar al principio de la lı́nea o después de un comando. Al
programar, los comentarios son muy importantes.
Más problemas con los INTEGER: En IDL por defecto estos son de 2
bytes: los números positivos llegan hasta el 32767. Si nos pasamos ocurre
esto:
IDL> ent=32760
IDL> i=ent+10 & print,i
-32766
Con un disparate como este es muy posible que el programa termine por
abortar y podamos darnos cuenta del error. No es tan infrecuente pasarnos
de 32767, por ejemplo si la variable corresponde al número de datos de un
fichero. Ası́ que en muchos casos puede ser una buena idea introducir un
LONG INTEGER:
IDL> ent=32760L ;la L del final distingue este tipo de enteros
IDL> i=ent+10 & print,i ;10L estarı́a bien igualmente
32770
IDL> help,i
I
LONG
=
32770
Los números en doble precisión (14 decimales significativos aproximadamente) pueden introducirse de una forma similar, en este caso añadiendo la
letra d:
IDL> print,5.d/3.d,format=’(f17.14)’
1.66666666666667
4
que puede compararse con el resultado del primer ejercicio que se encuentra
al final del documento.
Igual que usamos el comando & para poner dos sentencias en la misma
lı́nea, podemos usar el signo $ para continuar un comando en la siguiente
lı́nea:
IDL> b=1+2-2+$
IDL> 4
IDL> print,b
5
Como indicamos en su momento, el comando $ se puede usar también como
parte del nombre de una variable (por ejemplo var$1); sólo si está al final
de la lı́nea y va después de un operador o similar (consulta la ayuda, ?$,
para más detalles) indica continuación de lı́nea. Si lo pones al principio de
la lı́nea de comandos verás que te envı́a al intérprete de UNIX/LINUX, sin
cerrar la sesión de IDL. Puedes volver a la sesión de IDL con el comando
exit.
3.
Vectores
La forma mas sencilla (pero no la más frecuente) de crear un “vector” o
array de una dimensión es escribiendo sus elementos entre corchetes: 3
IDL> v=[1,2,4,2]
IDL> help,v
V
INT
= Array[4]
Aquı́ INT quiere decir que V es una variable de tipo INTEGER mientras
que Array[4] nos indica que es un array con cuatro componentes.
En IDL muchos operadores y funciones trabajan directamente sobre
arrays. Muchas veces el resultado es el que intuitivamente era de esperar: en
el ejemplo que sigue sumamos a un vector, 2*v, un escalar, 1, entendiendo
que lo que se quiere es sumar el escalar a cada uno de los elementos del
vector.
IDL> w=2*v+1
IDL> print,v,w
1
2
3
5
4
9
2
5
3
En las versiones antiguas de IDL (anteriores a la 5.0) se usaban los paréntesis en
lugar de los corchetes para indicar los elementos de un array. Por compatibilidad, en las
nuevas versiones esto sigue funcionando. Sin embargo, dado que los paréntesis se usan
para indicar los argumentos de las funciones -sqrt(a), cos(a)- esto puede prestarse a
confusión en particular cuando una función y un array poseen el mismo nombre. Por ello
no es recomendable usar paréntesis para indicar componentes de arrays aunque sea una
práctica muy extendida.
5
La suma, el producto y la división entre dos vectores corresponde respectivamente a la suma, el producto y la división entre componentes:
IDL> z=v+w
IDL> print,z
4
7
13
7
IDL> v1=[1.5,2.1,3.2]
& v2=[3.0,4.2,6.5]
IDL> p=v1*v2 & print,p
4.50000
8.82000
20.8000
IDL> d=v1/v2 & print,d
0.500000
0.500000
0.492308
3.1.
Subı́ndices
Aunque la velocidad de cálculo es muy superior si se trabaja con todos
los elementos de un array a la vez, esto no siempre es posible o deseable.
Consideremos el vector:
IDL> t=[1,2,3,4,5]
Para identificar un elemento usamos la notación t[i], siendo i el ı́ndice
buscado (que es de tipo INTEGER o en su caso LONG). Como ejemplo si
escribimos:
IDL> print,t[1]
2
vemos que t[1] es el segundo elemento del vector t. El primero es t[0].
Por otra parte, podemos manejar un conjunto seguido de elementos usando los dos puntos. Por ejemplo:
IDL> print,t[0:2]
1
2
3
corresponde a los elementos de t del 0 al 2.
Conviene recordar que las variables pueden cambiar sus atributos, incluidas sus dimensiones, mediante una operación. Por ejemplo, para añadir
un elemento al final del vector t escribimos:
IDL> t=[t,6] & print,t
1
2
3
4
5
6
y si ahora queremos añadir un 7 como tercer elemento:
IDL> t=[t[0:1],7,t[2:5]] & print,t
1
2
7
3
6
4
5
6
Consideremos por último algunas funciones básicas que nos permiten
crear vectores de forma sencilla (usamos los comandos help y print para
ver lo que hacen estas funciones):
IDL> v1=findgen(4) & print,v1
0.00000
1.00000
2.00000
3.00000
IDL> v2=indgen(4) & print,v2
0
1
2
3
IDL> help,v1,v2
V1
FLOAT
= Array[4]
V2
INT
= Array[4]
IDL> v3=fltarr(4) & print,v3
0.00000
0.00000
0.00000
0.00000
IDL> v4=dblarr(4) & print,v4
0.0000000
0.0000000
0.0000000
0.0000000
IDL> help,v3,v4
V3
FLOAT
= Array[4]
V4
DOUBLE
= Array[4]
Cuando necesites una descripción completa de las funciones que acabamos
de introducir usa la ayuda (por ejemplo ?findgen). Por otra parte recuerda
que la precisión de los números FLOAT y DOUBLE (unos 7 decimales el
primero y unos 14 el segundo) no se corresponde con los que nos muestra el
compando print por defecto.
Ejercicio 2. Crea un vector de 1000 elementos que cubra uniformemente
el intervalo [0, 2.5]. A continuación quı́tale los datos 344 y 567.
4.
Arrays de dos dimensiones
Podemos crear un array 3 x 2 (3 filas x 2 columnas) escribiéndolo columna a columna, de la siguiente forma:
IDL> ar=[[1,2,3],[4,5,6]]
IDL> help,ar
AR
INT
= Array[3, 2]
Vemos que efectivamente se trata de una matriz 3 x 2. Para listarla por pantalla ponemos la transpuesta porque IDL escribe lo que convencionalmente
se llaman filas y columnas al revés:
IDL> print,transpose(ar)
1
4
2
5
3
6
7
Veamos como operar con arrrays. Para ello creamos dos matrices cuadradas a modo de ejemplo. La primera de la siguiente forma:
IDL> a=findgen(9)
IDL> a1=reform(a,3,3)
IDL> help,a,a1
A
FLOAT
A1
FLOAT
= Array[9]
= Array[3, 3]
La función reform(a,m,n) nos transforma el vector A en un array de m filas
x n columnas. Para la segunda matriz tomamos la transpuesta:
IDL> a2=transpose(a1)
Probamos a sumar, multiplicar y dividir los dos arrays (los resultados del
print están quitados pues son demasiado largos):
IDL>
IDL>
IDL>
IDL>
IDL>
print,a1
print,a2
b1=a1+a2 & print,b1
b1=a1*a2 & print,b2
b3=a1/(a2+1.) & print,b3
Vemos que se suma, multiplica y divide elemento a elemento. Estas son operaciones que nos interesará hacer frecuentemente. Cuidado con no confundir
la multiplicación elemento a elemento con la multiplicación de matrices; para
obtener esta última debemos poner:
IDL> b4=a1#a2
Usa el comando print, transpose(b4) para comprobar que obtienes lo
esperado.
4.1.
Subı́ndices
Supongamos que tenemos una matriz AR cuyos elementos se van conociendo secuencialmente. Por ejemplo supongamos que por el momento sólo
sabemos que ar[1,2]=3.5. Entonces para poder asignar un valor a uno sólo
de los elementos del array es necesario tener definida la matriz previamente.
Una forma sencilla de conseguirlo es haciendo inicialmente cero todos sus
elementos. Ası́ escribirı́amos (recuerda la función fltarr):
IDL> ar=fltarr(2,4)
IDL> ar[1,2]=3.5 ;empezando por cero: fila 1, columna 2
IDL> print,transpose(ar)
0.00000
0.00000
0.00000
0.00000
0.00000
0.00000
3.50000
0.00000
8
Nuevamente listamos la transpuesta para que se visualice con la apariencia
estándar.
Es importante recalcar que sólo cuando asignamos el resultado de una
operación a uno o a un conjunto de elementos de un array (es decir cuando
en el miembro izquierdo de una igualdad ponemos la variable con corchetes)
es necesario tenerlo definida previamente. Por el contrario, las matrices b1,
b2, b3 y b4 anteriores las introdujimos directamente pues trabajábamos con
todos sus elementos a la vez.
Una de las cosas que cuesta más acostumbrarse en IDL (y en otros lenguajes de caracterı́sticas similares) es a operar con trozos de un array. A
modo de ejemplo supongamos que tenemos una serie de datos que representan distintas magnitudes (temperatura, presión, densidad). Como por
el momento no sabemos como leer ficheros de datos, vamos a crear unos
números aleatorios que hagan las veces de datos:
IDL>data=randomu(seed,300)
La función randomu devuelve números aleatorios uniformemente distribuidos en el intervalo (0,1). Para conseguir el resultado deseado la variable que
hemos llamado seed puede ser en realidad cualquier variable no definida
previamente. Consulta la ayuda para completar la información. Supongamos que estos números representan los datos indicados anteriormente. Por
tanto tenemos datos de temperatura, presión y densidad agrupados en 100
instantes de tiempo distintos. En concreto supondremos que la temperatura, presión y densidad en el instante inicial corresponde a los tres primeros
datos del vector:4
IDL> print,data[0:2]
0.957516
0.740806
0.483755
Los siguientes 3 datos, data[3:5], son la temperatura, presión y densidad
en el instante siguiente:
IDL> print,data[3:5]
0.304793
0.939899
0.735009
y ası́ sucesivamente.
Nuestro objetivo final, a modo de ejemplo, es pasar la temperatura de
grados centı́grados a Kelvin. En general, si vamos a operar por separado con
cada una de las magnitudes, puede ser una buena idea crearnos una matriz
que nos separe por filas o por columnas cada una de las variables. Para ello
escribimos:
4
Dado que se trata de números aleatorios, los valores concretos que tome el vector data
en cada sesión de IDL pueden ser distintos y no tienen porqué coincidir con los mostrados
aquı́.
9
IDL> tpd=reform(data,3,100)
IDL> help, tpd
TPD
FLOAT
= Array[3, 100]
Vemos que la función reform nos transforma el vector data en un array de 3
filas y 100 columnas de forma que en la primera fila tenemos la temperatura,
en la segunda la presión y en la tercera la densidad. ¿Seguro? ¿o tendrı́amos
que haber tomado 3 columnas y 100 filas? Vamos a comprobar que lo hemos
hecho bien usando el comando print adecuadamente. En concreto la temperatura (primera fila, es decir, fila 0) en los dos primeros instantes (columnas
0 y 1) corresponderá a:
IDL> print,tpd[0,0:1]
0.957516
0.304793
Vemos que efectivamente coinciden con los datos mostrados anteriormente.
Por otra parte, la temperatura, presión y densidad en el instante inicial
corresponderán a la primera columna de la matriz tpd. Entonces le decimos:
filas 0 a 2, columna 0:
IDL> print,tpd[0:2,0]
0.957516
0.740806
0.483755
que efectivamente coincide con lo mostrado al principio. Para indicar “todas”
también podemos usar el sı́mbolo *. La sentencia anterior: “todas las filas,
la primera columna” es equivalente a:
IDL> print,tpd[*,0]
Supongamos que tenemos la temperatura en grados centı́grados y la queremos pasar a Kelvin. Con la separación en filas de los datos que hemos hecho
es muy fácil:
IDL> tpd[0,*]=tpd[0,*]+273.15
es decir sumamos 273.15 a los elementos de la primera fila (la cero) y todas
las columnas (*).
4.2.
Subarrays
Supongamos ahora que tenemos el array tpd pero que nos interesa usar
(por ejemplo para guardar) solo la presión y la densidad entre los instante
50 y 60. Entonces creamos un subarray con las filas 1,2 (presión y densidad
respectivamente) y las columnas desde la 49 a la 59, de la siguiente forma:
IDL> sub_tpd=tpd[1:2 , 49:59]
10
Si, por el contrario, queremos almacenar la presión desde el instante 50 hasta
el final podemos hacer lo siguiente:
IDL> p=tpd[1,49:99]
o bien:
IDL> p=tpd[1,49:*]
donde el * en este contexto significa “hasta el final”. Esto es útil si el número
de datos puede variar lo que ocurrirá frecuentemente.
5.
Gráficas
Veremos en este apartado lo más elemental. A lo largo del curso iremos
aprendiendo algunas cosas más sobre gráficas. Busquemos algo que dibujar.
Por ejemplo la suma de dos sinusoides:
Creamos un vector con 100 datos uniformemente espaciados en el intervalo [0, 6π]:
IDL> x=findgen(100)*6.*!pi/99.
Fı́jate como hemos introducido el número π en precisión simple, en doble
precisión es !dpi. Creamos un vector con la señal (f1 y f2 son las frecuencias;
a1 y a2 las amplitudes; las fases las ponemos igual a 0):
IDL> f1=0.15 & f2=0.2
IDL> a1=4. & a2=2.
IDL> y=a1*sin(2.*!pi*f1*x)+a2*sin(2.*!pi*f2*x)
Dibujamos y(x):
IDL> plot,x,y,xtitle=’tiempo (s)’,ytitle=’velocidad (m/s)’
Aquı́ hemos usado el procedimiento plot con los argumentos x, y junto con
dos opciones para dibujar los carteles en cada eje. Consulta la ayuda (?plot)
cuando sea necesario para ver el resto de opciones. El nombre de las opciones
pueden abreviarse mientras no exista ambigüedad, por ejemplo también es
válido:
IDL> plot,x,y,xtit=’tiempo (s)’,ytit=’velocidad (m/s)’
pero no:
IDL> plot,x,y,xt=’tiempo (s)’,yt=’velocidad (m/s)’
Si queremos dibujar otra función sobre esta gráfica lo podemos hacer de
la siguiente forma:
11
IDL> w=abs(y)
IDL> oplot,x,w,linestyle=2
El procedimiento oplot (overplot) permite pintar encima de lo que hay.
Consulta la ayuda para ver las posibilidades según te vaya haciendo falta.
En el ejemplo anterior el procedimiento plot nos abrió automáticamente
una ventana gráfica. En realidad esto ocurrió porque no tenı́amos ninguna
abierta en ese momento. Si ahora queremos un dibujo en otra ventana debemos especificarlo. Para abrir ventanas podemos usar el comando:
IDL> window,1
donde en lugar de 1 podemos poner cualquier número entero entre 0 y 31. 5
En la cabecera de la ventana puedes ver el número de cada una (el dibujo
anterior está en la 0, ¿no?). Dibujemos algo en la pantalla nueva, por ejemplo:
IDL> z=x*y
IDL> plot,x,z,tit=’NUEVO’
La ventana gráfica no siempre sale en la posición y con el tamaño que
queremos. A parte de usando el cursor de la forma estándar, para cambiar
los valores por defecto podemos usar las siguientes opciones:
IDL> window, 1, xsize=200, ysize=300
IDL> window, 2, xpos=75, ypos=150
;tama~
no en pixeles
;posición origen
A la hora de crear documentos es muy frecuente incluir figuras que muestren los resultados de un cálculo. En general la mejor forma de introducirlos
en los procesadores de texto es guardando previamente las figuras en un
fichero postScript o similar. Supongamos que queremos guardar la gráfica
que generamos en la ventana 0 como un fichero postScript encapsulado en
un fichero de nombre (por ejemplo) figura.prueba.eps. Lo podemos hacer
de la siguiente forma:
IDL>
IDL>
IDL>
IDL>
IDL>
IDL>
set_plot,’ps’
device,filename=’figura.prueba.eps’,/encapsulated
plot,x,y,xtit=’tiempo (s)’,ytit=’velocidad (m/s)’
oplot,x,w,linestyle=2
device,/close
set_plot,’x’
El primer comando pasa de modo ventana a fichero postScript, luego abrimos
el fichero con el nombre que queramos, dibujamos con los mismos comandos
que antes: plot, oplot, etc., cerramos el fichero y por último volvemos al
modo pantalla para los siguientes dibujos (suponiendo que eso sea lo que
nos interese). La Figura 1 muestra el resultado.
5
No existe un lı́mite para el número de ventanas. Para abrir la que tenga el ı́ndice menor
por encima de 31 todavı́a sin usar en la sesión debe escribirse la sentencia window,/free.
12
Figura 1: Este es el resultado de añadir el fichero figura.prueba.eps que
acabamos de generar al procesador de texto (LATEX 2ε concretamente). Para decir verdad la opción /encapsulated que pusimos antes nos permite
cambiar el tamaño de la gráfica a posteriori con el procesador de texto. Entonces si, como suele ocurrir frecuentemente, nos interesa reducir el tamaño
de la gráfica en el texto, conviene aumentar previamente con IDL el tamaño
de las letras y números de los ejes para que se vean bien, esto se hace con
la opción charsize que nos multiplica el tamaño de los caracteres por un
factor, concretamente la figura tiene: plot,...,chars=1.5
6.
Respuesta a los ejercicios
Ejercicio 1: Por defecto el comando print nos enseña un número determinado
de decimales pero la precisión puede ser mayor. Para ver más decimales ponemos:
IDL> print,5./3.,format=’(f15.12)’
1.666666626930
y sabiendo que el resultado debe ser “uno con seis periodo” resultan como mucho
7 decimales significativos.
Ejercicio 2: Al tener decimales, el vector debe ser real. Como el comando
findgen empieza en 0, el último número será el 999. Entonces para que sea 2.5
lo reescalamos adecuadamente. Luego para quitar los puntos tenemos en cuenta
que el elemento 344 es a[343] y el 567 es a[566]. Ası́:
IDL> a=findgen(1000)*2.5/999.
IDL> print, a[0],a[999]
0.00000
2.50000
IDL> b=[a[0:342],a[344:565],a[567:999]]
IDL> help,a,b
A
FLOAT
= Array[1000]
B
FLOAT
= Array[998]
13
Descargar