Apuntes Python
Apuntes Python
Apuntes Python
1 Introducción a Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2 NumPy y SciPy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.1 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.2 Funciones para crear arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
2.3 Slicing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
2.4 Operaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
2.5 Otras operaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
2.6 Lectura de ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
2.7 Búsqueda de información . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
2.8 SciPy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
2.9 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
1
2 ÍNDICE GENERAL
1 Aunque es conveniente saber que existen funciones para avisarnos si alguna parte de
3
4 Tema 1 Introducción a Python
1 1
MANEJO BÁSICO DE PYTHON
1 1 1 La consola IPython
En lugar del intérprete Python habitual existe una consola interactiva
denominada IPython con una serie de características muy interesantes que
facilitan el trabajo con el intérprete. Entre ellas podemos destacar la presencia
de autocompletado, característica que se activa al pulsar la tecla de tabulación
y que nos permite que al teclear las primeras letras de una orden aparezcan
todas las órdenes disponibles que comienzan de esa forma. También existe un
operador ? que puesto al final de una orden nos muestra una breve ayuda
acerca de dicha orden, así como acceso al historial de entradas recientes con
la tecla ↑
3 Esto es lo que se conoce como el shebang, y es el método estándar para poder ejecutar
un programa interpretado como si fuera un binario.
4 Es decir, codificaciones que admiten caracteres acentuados.
6 Tema 1 Introducción a Python
In [1]:
In [2]:
Si como ocurre en este caso, nuestra entrada produce una salida Out[1]:
46, podemos usar la numeración asignada para reutilizar el dato mediante la
variable _1,
In [2]: _1 + 15
Out [2]: 61
In [4]: _2 + _
Out [4]: 183
Además, esta consola pone a nuestra disposición comandos del entorno (cd,
ls, etc.) que nos permiten movernos por el árbol de directorios desde dentro
de la consola, y comandos especiales, conocidos como funciones mágicas que
proveen de funcionalidades especiales a la consola. Estos comandos comienzan
por el carácter % aunque si no interfieren con otros nombres dentro del
sistema se puede prescindir de este carácter e invocar sólo el nombre del
comando. Entre los más útiles está el comando run con el que podemos
ejecutar desde la consola un script de órdenes. Por ejemplo, para ejecutar
el creado anteriormente:
In [5]: run hola.py
Hola Mundo
1.1 Manejo básico de Python 7
IPython Notebook
El IPython Notebook es una variación de la consola IPython, que usa un
navegador web como interfaz y que constituye un entorno de computación
que mezcla la edición de texto con el uso de una consola. Es una forma muy
interesante de trabajar con Python pues auna las buenas características de
la consola IPython, con la posibilidad de ir editando las entradas las veces
que sean necesarias. Para correr el entorno hemos de escribir en una terminal
la orden ipython notebook, lo que nos abrirá una ventana en un navegador
web, con un listado de los notebooks disponibles y la posibilidad de navegar
en un árbol de directorios. Los notebooks son ficheros con extensión .ipynb
que pueden ser importados o exportados con facilidad al entorno web.
Además de las características mencionadas, los notebooks permiten añadir
texto en diversos formatos (LATEX incluisive) con los que diseñar páginas
interactivas con código Python e información.
1 2
ASPECTOS BÁSICOS DEL LENGUAJE
1 2 1 Variables numéricas
Veamos algunos ejemplos:
>>> a=2 # define un entero
>>> b=5. # define un número real
>>> c=3+1j # define un número complejo
>>> d= complex (3 ,2) # define un número complejo
Claramente a+b calcula la suma de los valores de las variables, sin embargo
a/b parece que no calcula correctamente la división. En realidad la respuesta
es correcta dado que ambas variables son enteros, y por tanto se realiza la
división entre enteros, que corresponde a la parte entera de la división. Si lo
que esperamos es obtener la división real debemos escribir al menos uno de
los números en forma real, lo que se hace con el comando float:
1.2 Aspectos básicos del lenguaje 9
1 2 2 Objetos
Python sigue el paradigma de la Programación Orientada a Objetos
(POO). En realidad, todo en Python es un objeto. Podemos entender un
objeto como un tipo especial de variable en la que no sólo se almacena un
valor, o conjunto de valores, sino para el que tenemos disponible también una
serie de características y de funciones concretas, que dependerán del objeto
en cuestión.
Por ejemplo, si creamos un número complejo
>>> a=3+2j
10 Tema 1 Introducción a Python
Los atributos son características de los objetos a las que se accede mediante
el operador . de la forma objeto.atributo.
Cada tipo de objeto suele tener disponible ciertos métodos. Un método es
una función que actúa sobre un objeto con una sintaxis similar a la de un
atributo, es decir, de la forma objeto.método(argumentos). Por ejemplo, la
operación de conjugación es un método del objeto complejo:
>>> a. conjugate ()
(3 -2j)
el intérprete nos indica que se trata de un método del objeto complejo, pero
no proporciona lo esperado.
Pulsando el tabulador después de escribir objeto. en la consola IPython,
ésta nos muestra los atributos y funciones accesibles al objeto:
In [1]: a= complex (3 ,2)
1 2 3 Listas
Las listas son colecciones de datos de cualquier tipo (inclusive listas) que
están indexadas, comenzando desde 0:
>>> a=[1 ,2. ,3+1j]
>>> type(a)
<type 'list '>
>>> a[1]
2.0
>>> a[3]
Traceback (most recent call last):
File "<stdin >", line 1, in <module >
IndexError : list index out of range
1.2 Aspectos básicos del lenguaje 11
Hemos definido una lista encerrando sus elementos (de tipos diversos) entre
corchetes y separándolos por comas. Podemos acceder a cada uno de los
elementos de la lista escribiendo el nombre de la lista y el índice del elemento
entre corchetes, teniendo en cuenta que el primer elemento tiene índice 0.
Nótese que el tercer elemento corresponde al índice 2, y si intentamos acceder
al elemento a[3] obtenemos un error.
Si algún elemento de la lista es otra lista, podemos acceder a los elementos
de esta última usando el corchete dos veces, como en el siguiente ejemplo:
>>> a=[1 ,2. ,3+1j ,[3 ,0]]
>>> a[3]
[3, 0]
>>> a [3][1]
0
Las listas son estructuras de datos muy potentes que conviene aprender a
manejar con soltura. Podemos consultar los métodos a los que tenemos acceso
en una lista usando el autocompletado en IPython:
In [2]: a. # Pulsar <TAB >
a. append a. extend a. insert a. remove a.sort
a. count a. index a.pop a. reverse
Por otra parte, es frecuente que Python utilice los operadores aritméticos
con diferentes tipos de datos y distintos resultados. Por ejemplo, los operadores
suma y multiplicación pueden aplicarse a listas, con el siguiente resultado:
>>> a=[1 ,2 ,3]; b=[10 ,20 ,30]
>>> a*3
[1, 2, 3, 1, 2, 3, 1, 2, 3]
>>> a+b
[1, 2, 3, 10, 20, 30]
Nótese que el uso del método hubiera sido equivalente a escribir a=a+b. En
general, el uso de métodos proporciona mejor rendimiento que el uso de otras
acciones, pero hemos de ser conscientes de que el objeto sobre el que se aplica
puede quedar modificado al usar un método.
1 2 4 Cadenas de caracteres
Las cadenas no son más que texto encerrado entre comillas:
>>> a="Hola"; b='mundo '
>>> print a
Hola
>>> print b
mundo
>>> type(a)
<type 'str '>
en las que se puede comprobar que da igual definirlas con comillas simples o
dobles, lo que es útil si queremos cadenas que incluyan estos caracteres:
>>> a="Esto es una 'string '"
>>> print a
Esto es una 'string '
pero las cadenas son inmutables, esto es, no es posible alterar sus elementos.
Eso significa que cualquier transformación que llevemos a cabo con un método,
no alterará la cadena original.
Hay una gran cantidad de métodos para manejar strings que permiten
cambiar la capitalización, encontrar caracteres dentro de una cadena o separar
cadenas en trozos en función de un caracter dado. Emplazamos al lector a usar
la ayuda en línea del intérprete para aprender el funcionamiento de éstos y
otros métodos.
1 2 5 Diccionarios
En algunas ocasiones es interesante disponer de listas que no estén inde-
xadas por números naturales, sino por cualquier otro elemento (strings ha-
bitualmente). Python dispone de los diccionarios para manejar este tipo de
listas:
>>> colores ={ 'r': 'rojo ', 'g': 'verde ', 'b': 'azul '}
>>> type( colores )
<type 'dict '>
>>> colores ['r']
'rojo '
>>> colores ['k']= 'negro ' # añadimos un nuevo elemento
14 Tema 1 Introducción a Python
>>> colores
{'k': 'negro ', 'r': 'rojo ', 'b': 'azul ', 'g': 'verde '}
Observar que el orden dentro de los elementos de la lista es irrelevante, pues
la indexación no es numerada.
El objeto que se usa como índice se denomina clave. Podemos pensar
entonces en un diccionario como un conjunto no ordenado de pares, clave:
valor donde cada clave ha de ser única (para ese diccionario). Podemos
acceder a ellas usando los métodos adecuados:
>>> colores .keys () # claves
['k', 'r', 'b', 'g']
>>> colores . values () # valores
['negro ', 'rojo ', 'azul ', 'verde ']
Entre los diversos métodos accesibles para un diccionario, disponemos del
método pop que permite eliminar una entrada en el diccionario:
>>> colores .pop('b')
'azul '
>>> colores
{'k': 'negro ', 'r': 'rojo ', 'g': 'verde '}
1 2 6 Tuplas
Para terminar con los tipos de datos principales haremos mención a las
tuplas. Las tuplas son un tipo de dato similar a las listas (es decir, una
colección indexada de datos) pero que no pueden alterarse una vez definidas
(son inmutables):
>>> a=(1 ,2. ,3+1j,"hola")
>>> type(a)
<type 'tuple '>
>>> a[2]
(3+1j)
>>> a [0]=10. # error : no se puede modificar una tupla
Traceback (most recent call last):
File "<stdin >", line 1, in <module >
TypeError : 'tuple ' object does not support item assignment
La definición es similar a la de una lista, salvo que se usan paréntesis en lugar
de corchetes, y el acceso a los elementos es idéntico. Pero como podemos
observar, no es posible alterar sus elementos ni tampoco añadir otros nuevos.
La tuplas son muy útiles para empaquetar y desempaquetar datos, como
puede verse en el siguiente ejemplo:
>>> a=1,2,'hola ' # creamos una tupla
>>> type(a)
<type 'tuple '>
>>> a
1.3 Módulos 15
1 3
MÓDULOS
Para conocer todas las funciones a las que tenemos acceso dentro del
módulo disponemos de la orden dir5
>>> dir(math)
['__doc__ ', '__name__ ', '__package__ ', 'acos ', 'acosh ', 'asin '
, 'asinh ', 'atan ', 'atan2 ', 'atanh ', 'ceil ', 'copysign ', '
cos ', 'cosh ', 'degrees ', 'e', 'erf ', 'erfc ', 'exp ', 'expm1
', 'fabs ', 'factorial ', 'floor ', 'fmod ', 'frexp ', 'fsum ',
'gamma ', 'hypot ', 'isinf ', 'isnan ', 'ldexp ', 'lgamma ', '
log ', 'log10 ', 'log1p ', 'modf ', 'pi ', 'pow ', 'radians ', '
sin ', 'sinh ', 'sqrt ', 'tan ', 'tanh ', 'trunc ']
definidas.
1.3 Módulos 17
ejemplo, es posible que haya más de un módulo que use la misma función,
como es el caso de la raíz cuadrada, que aparece tanto en el módulo math
como en el módulo cmath. De manera que podemos encontrarnos situaciones
como la siguiente:
>>> import math
>>> import cmath
>>> math.sqrt (-1)
Traceback (most recent call last):
File "<stdin >", line 1, in <module >
ValueError : math domain error
>>> cmath .sqrt (-1)
1j
Como vemos, hemos cargado los módulos math y cmath y calculado la raíz
cuadrada de −1 con la función sqrt que posee cada módulo. El resultado es
bien distinto: la función raíz cuadrada del módulo math no permite el uso de
números negativos, mientras que la función sqrt del módulo cmath sí. Si en
lugar de cargar los módulos como en el último ejemplo los hubiésemos cargado
así:
>>> from cmath import *
>>> from math import *
nos deja a nuestra disposición la función raíz cuadrada del módulo cmath y
las funciones trigonométricas seno y coseno del módulo math. Es importante
señalar que con este método de importación no tenemos acceso a ninguna
otra función de los módulos que no hubiera sido previamente importada. Esta
última opción es de uso más frecuente en los scripts, debido a que con ella
cargamos exclusivamente las funciones que vamos a necesitar y de esa forma
mantenemos el programa con el mínimo necesario de recursos.
En el uso de la consola interactiva es más frecuente cargar el módulo
al completo, y es aconsejable hacerlo sin el uso de *. De hecho, hay una
posibilidad adicional que nos evita tener que escribir el nombre del módulo
al completo, seguido del punto para usar una función. Si realizamos una
importación del módulo como sigue.
>>> import math as m
>>> m.cos(m.pi)
-1.0
Módulo Descripción
math Funciones matemáticas
cmath Funciones matemáticas con complejos
fractions Números racionales
os Funcionalidades del sistema operativo
sys Funcionalidades del intérprete
re Coincidencia en patrones de cadenas
datetime Funcionalidades de fechas y tiempos
pdb Depuración
random Números aleatorios
ftplib Conexiones FTP
MySQLdb manejo de bases de datos MySQL
smtplib Envío de e-mails
1 4
CONTROL DE FLUJO
1 4 1 Bucles
Una característica esencial de Python es que la sintaxis del lenguaje impone
obligatoriamente que escribamos con cierta claridad. Así, los bloques de código
deben ser obligatoriamente sangrados:
>>> for i in range (3):
... print i
...
0
1
2
1.4 Control de flujo 19
1 4 2 Condicionales
La escritura de sentencias condicionales es similar a la de los bucles for,
usando el sangrado de línea para determinar el bloque:
>>> if 5 %3 == 0:
... print "5 es divisible entre 3"
... elif 5 %2 == 0:
... print "5 es divisible por 2"
... else:
... print "5 no divisible ni por 2 ni por 3"
...
5 no es divisible ni por 2 ni por 3
>>>
1 4 3 Bucles condicionados
Obsérvese este ejemplo con la sentencia while:
1 >>> a,b=0 ,1 # Inicialización de la sucesión de Fibonacci
2 >>> while b <20:
3 ... print b,
4 ... a,b=b,a+b
5 ...
6 1 1 2 3 5 8 13
Es interesante analizar un poco este breve código que genera unos cuantos
términos de la sucesión de Fibonacci. En especial, hemos de prestar atención
a cómo usamos tuplas para las asignaciones múltiples que realizamos en las
líneas 1 y 4; en la primera hacemos a=0 y b=1 y en la segunda se realiza la
asignación a=b y b=a+b, en la que debemos notar que antes de realizar la
asignación se evalúa el lado derecho (de izquierda a derecha).
También disponemos de las sentencias break y continue para terminar
o continuar, respectivamente, los bucles for o while. Asimismo, la sentencia
else tiene sentido en un bucle y se ejecuta cuando éste ha terminado, en
1.5 Funciones definidas por el usuario 21
1 5
FUNCIONES DEFINIDAS POR EL USUARIO
Las funciones son trozos de código que realizan una determinada tarea.
Vienen definidas por la orden def y a continuación el nombre que las define
seguido de dos puntos. Siguiendo la sintaxis propia de Python, el código de
la función está sangrado. La principal característica de las funciones es que
permiten pasarles argumentos de manera que la tarea que realizan cambia en
función de dichos argumentos.
>>> def fibo(n): # sucesión de Fibonacci hasta n
... a,b=0 ,1
... while b<n:
... print b,
22 Tema 1 Introducción a Python
... a,b=b,a+b
...
>>> fibo (100) # llamada a la función con n=100
1 1 2 3 5 8 13 21 34 55 89
>>> fibo (1000) # llamada a la función con n=1000
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
if cos(x)!=0:
return sin(x)/cos(x)
else:
print "La tangente es infinita "
return
x= tangente (pi)
print 'La tangente de pi es ',x
tendremos todas las funciones presentes en el módulo (salvo las que comiencen
por _) sin necesidad de anteponer el nombre, pero como podemos observar
en el ejemplo, podemos alterar las variables propias que tengamos definidas,
razón por la cual no recomendamos este tipo de importación.
Finalmente si realizamos la importación selectiva:
>>> from tang import tangente
24 Tema 1 Introducción a Python
la función tangente está disponible, pero nada más. Aun así, obsérvese que
se ha ejecutado el módulo en el momento de la importación. Para evitar que
el código se ejecute cuando importamos podemos separar la función del resto
del código del siguiente modo:
from math import sin ,cos ,pi
def tangente (x):
if cos(x)!=0:
return sin(x)/cos(x)
else:
print "La tangente es infinita "
return
Documentando funciones
Se puede documentar una función en Python añadiendo una cadena de
documentación justo detrás de la definición de función:
def mifuncion ( parametro ):
1.5 Funciones definidas por el usuario 25
Absolutamente nada
"""
pass
En tal caso, la función help(mifuncion) o escribiendo mifuncion? en IPyt-
hon nos mostrará la documentación de la función.
1 5 2 Argumentos de entrada
Es posible definir funciones con un número variable de argumentos. Una
primera opción consiste en definir la función con valores por defecto:
def fibonacci (n,a=0,b=1):
sucesion =[]
while b<n:
sucesion . append (b)
a,b=b,b+a
return sucesion
La llamada a esta función puede realizarse de diversos modos.
Usando sólo el argumento posicional: fibonacci(100).
Pasando sólo alguno de los argumentos opcionales (junto con el posicio-
nal): fibonacci(100,3), en cuyo caso a=3 y b=1.
Pasando todos los argumentos: fibonacci(10,2,3).
También es posible realizar la llamada usando los nombres de las variables
por defecto, así
fibonacci (100 ,b=3) # equivale a fibonacci (100 ,0 ,3)
fibonacci (100 ,b=4,a=2) # equivale a fibonacci (100 ,2 ,4)
fibonacci (b=4,a=2) # error : falta el argumento n
1 5 3 Funciones lambda
Las funciones lambda son funciones anónimas de una sola línea que pueden
ser usadas en cualquier lugar en el que se requiera una función. Son útiles para
reducir el código, admiten varios parámetros de entrada o salida y no pueden
usar la orden return:
>>> f = lambda x,y: (x+y, x**y)
>>> f(2 ,3)
(5, 8)
1 6
ENTRADA Y SALIDA DE DATOS
Es habitual que nuestros programas necesiten datos externos que deben ser
pasados al intérprete de una forma u otra. Típicamente, para pasar un dato
a un programa interactivamente mediante el teclado (la entrada estándar)
podemos usar la función raw_input
>>> raw_input ('Introduce algo\n') # \n salta de línea
Introduce algo
hola 2 # introducido por teclado
'hola 2'
import sys
print sys.argv
Como vemos, sys.argv almacena en una lista todos los datos de la llamada,
de manera que es fácil manejarlos teniendo presente que son considerados
como cadenas de caracteres, por lo que es probable que tengamos que realizar
una conversión de tipo para tratarlos correctamente.
Por ejemplo, si tenemos el archivo fibo.py con el siguiente contenido
# -*- coding : utf -8 -*-
import sys
El método read del objeto archivo lee el archivo al que referencia el objeto
creado. Si se le proporciona un argumento numérico entonces lee un número
determinado de bytes del archivo;6 cuando es llamado sin argumento, lee el
archivo al completo. Cada llamada a read mueve el puntero de lectura del
archivo la último lugar leído, de manera que la llamada siguiente lee a partir
de ahí. Por eso:
>>> f.read ()
''
produce una cadena vacía, pues el puntero se halla al final del archivo.
Podemos usar el método seek para situarnos en cualquier posición del archivo.
>>> f.seek (0) # Vamos al inicio del archivo
>>> f.read ()
'# coding : utf -8\ nprint " Primera l\xc3\ xadnea "\ nprint " Segunda
l\xc3\ xadnea "'
>>> f. readline ()
'print " Segunda l\xc3\ xadnea "'
o con f.readlines() que almacena cada una de las líneas del archivo en una
lista:
>>> f.seek (0)
>>> for x in f. readlines ():
6 Un byte puede considerarse un carácter, aunque hay caracteres que ocupan más de uno.
1.6 Entrada y salida de datos 33
... print x,
...
# coding : utf -8
print " Primera línea "
print " Segunda línea "
También disponemos del método tell que nos indica con un entero la posición
en la que se encuentra el puntero del archivo.
La escritura con write también se puede hacer en pantalla si la usamos
como un objeto tipo archivo con el atributo stdout del módulo sys:
>>> import sys
>>> g=sys. stdout
>>> g. write ('Hola mundo \n')
Hola mundo
lo que puede ser útil para programar las salidas por pantalla durante la
elaboración de código con write, de modo que cuando queramos direccionar
la salida a un archivo sólo necesitamos cambiar la salida.
1 7
MÁS SOBRE ESTRUCTURAS DE DATOS
1 7 1 Slicing
Una de las formas más interesantes de acceder a los elementos de una lista
es mediante el operador de corte o slicing, que permite obtener una parte de
los elementos de una lista:
>>> a= range (10); a. reverse () # creamos una lista
>>> a
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[2:5] # accedemos a los elementos 2,3,4
[7, 6, 5]
Como vemos, el slicing [n:m] accede a los elementos de la lista desde n hasta
m (el último sin incluir). Admite un parámetro adicional, y cierta flexibilidad
en la notación:
>>> a [1:7:2] # desde 1 hasta 6, de 2 en 2
[8, 6, 4]
>>> a[:3] # al omitir el primero se toma desde el inicio
[9, 8, 7]
>>> a[6:] # al omitir el último se toma hasta el final
[3, 2, 1, 0]
1.7 Más sobre estructuras de datos 35
una forma de escribir código que usa las características propias y peculiares del lenguaje
Python que auna sencillez, brevedad, elegancia y potencia.
1.7 Más sobre estructuras de datos 37
No hemos creado un nuevo objeto, sino una nueva referencia para el mismo
objeto, por lo que tienen el mismo identificador. Lo podemos comprobar con
la orden is:
>>> x is y
True
y ahora:
>>> x is y
False
38 Tema 1 Introducción a Python
Con las listas pasa algo similar, salvo que ahora está permitido modificar
el objeto (porque son mutables), por lo que las referencias hechas al objeto
continuarán apuntado a él:
>>> a=[1 ,2]
>>> id(a) # identificador del objeto
3074955372 L
>>> a[0]=3 # modificamos el primer elemento
>>> a
[3, 2]
>>> id(a) # el identificador no cambia
3074955372 L
La L del identificador se refiere al tipo de entero que devuelve (long).
La consecuencia de que las listas sean mutables se puede ver en el siguiente
ejemplo:
>>> x= range (3)
>>> y=x # y referencia lo mismo que x
>>> x[:0]= x # modificamos x
>>> y # y también se modifica
[0, 1, 2, 0, 1, 2]
Sin embargo,
>>> x=x+[3] # nuevo objeto
>>> y
[0, 1, 2, 0, 1, 2]
¿Por qué no se ha modificado y en este caso? La respuesta es que se ha creado
un nuevo objeto, al que se referencia con x, por lo que x ya no apunta al objeto
anterior, pero sí y. ¿Cómo podemos entonces copiar una lista? Podemos para
ello usar el slicing:
>>> x = range (10)
>>> y = x[:] # copiamos x a y
>>> id(x), id(y)
(3074955372L, 3075028972 L)
1 8
EXCEPCIONES
1 9
EJERCICIOS
(a) a = complex(3,1)
(c) a = 3+j
(d) a = 3+(2-1)j
(a) [10, 8, 6, 4, 2, 0]
∑
100
1
E.6 Calcula √
i=1
i
E.7 Calcula la suma de los números pares entre 1 y 100.
E.8 Explica el resultado del siguiente código:
s=[0]
for i in range (100):
s. append (s[i -1]+i)
print s
[0, 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91, 105]
1.9 Ejercicios 41
E.10 Utiliza la función randint del módulo random para crear una lista de
100 números aleatorios entre 1 y 10.
E.11 Construye el vigésimo elemento de la siguiente sucesión definida por
recurrencia:
un+3 = un+2 − un+1 + 3un , u0 = 1, u1 = 2, u3 = 3.
E.12 Calcular la suma de todos los múltiplos de 3 o 5 que son menores que
1000. Por ejemplo, la suma de todos los múltiplos de 3 o 5 que son menores
que 10 es: 3 + 5 + 6 + 9 = 23
E.13 Escribir una función reverse que acepte una cadena de caracteres y
devuelva como resultado la cadena de caracteres invertida en orden, es decir,
el resultado de reverse('hola') debe ser 'aloh'.
E.14 Usando el Ejercicio E.13, escribir una función is_palindromo que
indentifique palíndromos, (esto es, palabras que leídas en ambas direcciones
son iguales, por ejemplo radar). Es decir, is_palindromo('radar') debería
dar como resultado True.
E.15 El Documento Nacional de Identidad en España es un número finaliza-
do con una letra, la cual se obtiene al calcular el resto de la división de dicho
número entre 23. La letra asignada al número se calcula según la siguiente
tabla:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
TRWAGMYFPD X B N J Z S Q V H L C K E
Escribe un programa que lea el número por teclado y muestre la letra asignada.
Por ejemplo, al número 28594978 le debe corresponder la letra K. Tres líneas
de código debería ser suficientes.
E.16 Usando un bucle while escribe un programa que pregunte por un
número por pantalla hasta que sea introducido un número impar múltiplo de
3 y de 7 que sea mayor que 100.
E.17 Crea una lista aleatoria de números enteros (usar Ejercicio E.10) y
luego escribe un programa para que elimine los valores impares de dicha lista.
E.18 Escribir una función histograma que admita una lista aleatoria de
números enteros entre 1 y 5 (usar Ejercicio E.10) y elabore un diagrama de
frecuencias de aparición de cada uno de los elementos de la lista según el
formato que sigue: por ejemplo, si la lista es
[1, 4, 2, 1, 4, 4, 5, 5, 5, 3]
entonces la función debe imprimir:
1 **
2 *
3 *
4 ***
5 ***
42 Tema 1 Introducción a Python
E.20 Escribir una función que acepte como parámetros dos valores a y b, y
una función f tal que f (a)f (b) < 0 y que, mediante el método de bisección,
devuelva un cero de f . La aproximación buscada será controlada por un
parámetro ε que por defecto ha de ser 10−4 .
E.21 Considera la siguiente función:
def media (* valores ):
if len( valores ) == 0: # len = longitud de la tupla
return 0
else:
sum = 0
for x in valores :
sum += x
return sum / len( valores )
varios (2,5,1,a=2,b=3)
Escribe el código de la función.
E.23 Crea un fichero de nombre hist.py que incluya la función histograma
del Ejercicio E.18. Luego crea un nuevo archivo Python de manera que acepte
un número n como entrada, cree una lista aleatoria de números enteros entre 1
y 5 de longitud n, y llame a la función histograma para imprimir el recuento
de cifras.
2 NumPy y SciPy
2 1
ARRAYS
45
46 Tema 2 NumPy y SciPy
2 2
FUNCIONES PARA CREAR ARRAYS
2 3
SLICING
Por supuesto, al igual que con las listas, podemos acceder a los elementos
de un array mediante slicing, que en NumPy además admite más sofisticación.
Por ejemplo, la asignación sobre un trozo de una lista funciona de diferente
modo que en los arrays:
>>> a= range (5)
>>> a [1:4]=[6] # eliminamos elementos en la lista
>>> a
[0, 6, 4]
50 Tema 2 NumPy y SciPy
2 4
OPERACIONES
entonces, podemos usarla para trabajar sobre arrays del siguiente modo:
>>> vfunc = np. vectorize ( myfunc )
>>> a = np. arange (4)
>>> vfunc(a)
array ([ 0. , 0.84147098 , 0.90929743 , 0.14112001])
2 4 1 Álgebra Lineal
El módulo NumPy posee un submódulo, linalg para realizar operaciones
matemáticas comunes en Álgebra Lineal, como el cálculo de determinantes,
inversas, autovalores y solución de sistemas. Vemos unos ejemplos a continua-
ción:
>>> a = np. random . randint (0 ,5 ,(3 ,3)). astype ('float ')
>>> a
array ([[ 2., 0., 0.] ,
[ 4., 4., 1.] ,
[ 2., 3., 1.]])
>>> np. linalg .det(a) # determinante
1.9999999999999998
>>> val , vec = np. linalg .eig(a) # autovalores y autovectores
>>> val
array ([ 0.20871215 , 4.79128785 , 2. ])
>>> vec
array ([[ 0. , 0. , 0.4472136 ],
[ 0.25504011 , -0.78419037 , -0.53665631] ,
[ -0.96693047 , -0.62052031 , -0.71554175]])
>>> np. linalg .inv(a) # inversa
array ([[ 0.5, 0. , 0. ],
[-1. , 1. , -1. ],
[ 2. , -3. , 4. ]])
>>> b = np. random . randint (0 ,5 ,3). astype ('float ')
>>> b
array ([ 4., 3., 3.])
>>> x = np. linalg .solve (a,b) # resolución del sistema ax=b
>>> x
array ([ 2., -4., 11.])
>>> np.dot(a,x) - b # comprobación
array ([ 0., 0., 0.])
2 4 2 Concatenación
Otra de las operaciones comunes que se realizan con arrays es la concate-
nación de varios arrays. Funciona así:
2.4 Operaciones 55
2 4 3 Broadcasting
Es evidente que cuando las dimensiones de los arrays no concuerdan, no
podemos realizar las operaciones:
>>> a=np.array ([1 ,2 ,3] , float )
>>> b=np.array ([2 ,4] , float )
>>> a+b
Traceback (most recent call last):
File "<stdin >", line 1, in <module >
ValueError : operands could not be broadcast together with
shapes (3) (2)
Sin embargo, cuando los arrays no concuerden en dimensiones, Python tratará
de proyectarlos de manera que pueda realizarse la operación en cuestión. Este
procedimiento es conocido como broadcasting. Obsérvese el siguiente ejemplo
>>> a=np. arange (1. ,7). reshape (3 ,2)
56 Tema 2 NumPy y SciPy
La ventaja que nos aporta NumPy es que es posible realizar esta misma
operación sin necesidad del bucle. Nótese que queremos multiplicar tres vec-
tores columna, por los correspondientes tres vectores filas. Esto es, vamos a
realizar 3 operaciones de vectores 2 × 1 con vectores 1 × 2. De este modo, de-
bemos considerar a A como 3 vectores columna (lo que corresponde a tamaño
(3,2,1))
2.5 Otras operaciones 59
[[ 3],
[ 9]] ,
[[ 5],
[11]]])
>>> A[np. newaxis ].T. shape
(3, 2, 1)
[[ 6, 8]] ,
[[10 , 12]]])
>>> B[:,np. newaxis ]. shape
(3, 1, 2)
Así,
>>> A[np. newaxis ].T*B[:,np. newaxis ]
array ([[[ 2, 4],
[ 14, 28]] ,
[[ 18, 24] ,
[ 54, 72]] ,
[[ 50, 60] ,
[110 , 132]]])
2 5
OTRAS OPERACIONES
13249600
>>> a.min () # menor elemento
1
>>> a.max () # mayor elemento
169
>>> a. argmin () # índice del menor elemento
0
>>> a. argmax () # índice del mayor elemento
4
>>> a.clip (10 ,50) # si a<10 , ponemos 10; si a >50 ponemos 50
array ([10 , 16, 49, 50, 50])
Para arrays multidimensionales, la opción axis permite hacer las opera-
ciones anteriores (excepto clip) a lo largo de ejes diferentes:
>>> a = np. arange (12). reshape (3 ,4)
>>> a
array ([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> a.sum ()
66
>>> a.sum(axis =0)
array ([12 , 15, 18, 21])
>>> a.sum(axis =1)
array ([ 6, 22, 38])
También existe la función where que crea un nuevo array a partir de una
comparación:
>>> a
array ([ 2., 3., 0., 5.])
>>> np.where (a!=0. , 1/a, -1)
array ([ 0.5 , 0.33333333 , -1. , 0.2 ])
2 5 1 Indexación sofisticada
Además del slicing que hemos visto, los arrays de NumPy se pueden
indexar a través de arrays con enteros o variables booleanas, denominados
máscaras. Veamos algunos ejemplos:
62 Tema 2 NumPy y SciPy
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>> a[2 ,[0 ,3]] # tercera fila , columnas primera y cuarta
array ([ 8, 11])
>>> a[1:3 ,[0 ,3]] # mismas columnas , filas segunda y tercera
array ([[ 4, 7],
[ 8, 11]])
>>> a[[1 ,2] ,[0 ,3]] # ! igual que antes ?
array ([ 4, 11])
Es claro que con a[2,[0,3]] accedemos a los elementos de la tercera fila y
columas primera y última. Si queremos acceder a las filas segunda y tercera,
con las mismas columnas, podemos usar el slicing a[1:3,[0,3]. Sin embargo,
si tratamos de realizar el mismo acceso, pero con una lista de índices en lugar
del slicing empleado antes, el resultado no es el esperado. La razón detrás de
este comportamiento está en la forma en la que NumPy almacena y accede a
los datos de un array, y no trataremos de explicarla aquí. Simplemente debe-
remos tener en cuenta que el acceso simultáneo a un array multidimensional
con índices sin emplear slicing no funciona correctamente: NumPy interpreta
un acceso a los elementos [1,0] y [2,3]. Para obtener lo esperado usando
sólo índices hemos de usar la función ix_:
>>> a[np.ix_ ([1 ,2] ,[0 ,3])]
array ([[ 4, 7],
[ 8, 11]])
2 5 2 Slicing y operaciones
Un problema muy habitual en computación científica es el cálculo de
derivadas de funciones discretas. Por ejemplo, si tenemos una función u
aproximada por sus valores en un conjunto discreto de puntos xi , de tal forma
que ui ≡ u(xi ), para calcular la derivada discreta de dicha función se usan los
típicos cocientes incrementales:
ui+1 − ui
u′ (xi ) ≈
xi+1 − xi
Si disponemos de los valores de la función u y de los puntos xi en sendos
arrays, un sencillo bucle nos proporcionaría el cálculo buscado:
>>> x=np. linspace (0 ,1 ,10)
>>> u=np. random .rand (10)
>>> up = np.zeros (u. shape [0] -1) # reserva de memoria
>>> for i in range (len(x) -1):
... up[i] = (u[i+1] -u[i]) /(x[i+1] -x[i])
...
>>> up
array ([ 0.82173555 , -5.53656072 , 1.32509937 , -1.36740571 ,
-0.8901319 ,
64 Tema 2 NumPy y SciPy
2 5 3 Acceso a elementos
El rendimiento en el acceso a los elementos de un array puede verse
mejorado con el uso de los métodos item e itemset en lugar del acceso
mediante índices:
>>> a=np. random . randint (0 ,10 ,8). reshape (2 ,4)
>>> a
array ([[2 , 3, 1, 1],
[0, 8, 5, 1]])
>>> a.item (5) # muestra el quinto elemento
8
>>> a. itemset (5 ,3) # pone el quinto elemento igual a 3
>>> a
array ([[2 , 3, 1, 1],
[0, 3, 5, 1]])
>>> a. itemset (1,1 ,-1) # elemento 1,1 igual a -1
>>> a
array ([[ 2, 3, 1, 1],
[ 0, -1, 5, 1]])
Aunque en casos concretos no se nota la mejora de rendimiento, sí que es
relevante cuando manejamos arrays de gran tamaño.
2 6
LECTURA DE FICHEROS
2 1
4 3
6 5
8 7
10 9
entonces lo podemos leer de la forma:
>>> d=np. loadtxt ('fichero .dat ')
>>> d
array ([[ 2., 1.] ,
[ 4., 3.],
[ 6., 5.],
[ 8., 7.],
[ 10. , 9.]])
De forma similar tenemos la orden savetxt para guardar arrays de forma
rápida. La siguiente orden
c=d**2
np. savetxt ('nuevo .dat ',c)
crea un fichero de nombre nuevo.dat con el siguiente contenido
4.000000000000000000 e+00 1.000000000000000000 e+00
1.600000000000000000 e+01 9.000000000000000000 e+00
3.600000000000000000 e+01 2.500000000000000000 e+01
6.400000000000000000 e+01 4.900000000000000000 e+01
1.000000000000000000 e+02 8.100000000000000000 e+01
No obstante es importante resaltar que estos mecanismos no son los más
eficientes para manejar una gran cantidad de datos.
2 7
BÚSQUEDA DE INFORMACIÓN
...
"""
v = asarray (v)
s = v.shape
if len(s) == 1:
n = s[0]+ abs(k)
res = zeros ((n, n), v. dtype )
if k >= 0:
i = k
else:
i = (-k) * n
res [:n-k]. flat[i::n+1] = v
return res
elif len(s) == 2:
return v. diagonal (k)
else:
raise ValueError ("Input must be 1- or 2-d.")
aunque es necesario advertir que puede que dicho código no esté disponible
para algunas funciones.
Por último, cuando no concemos el nombre exacto de la función, o su
posible existencia, podemos realizar una búsqueda por concepto para ver qué
funciones en NumPy están relacionadas con ese tema. Por ejemplo, si estamos
buscando algún tipo de descomposición matricial y queremos ver qué posee
2.7 Búsqueda de información 67
2 8
SCIPY
Submódulo Descripción
cluster Algoritmos de agrupamiento
constants Constantes físicas y matemáticas
fftpack Rutinas para la Transformada Rápida de Fourier
integrate Integración de ecuaciones diferenciales ordinarias
interpolate Interpolación y splines regulares
io Entrada y salida de datos
linalg Álgebra lineal
ndimage Procesamiento de imágenes
odr Regresión ortogonal
optimize Optimización y cálculo de raíces
signal Procesamiento de señales
sparse Matrices dispersas
spatial Estructuras de datos espaciales
special Funciones especiales
stats Funciones estadísticas
weave Integración con C/C++
y a partir de éstos veremos las ideas básicas para aprender a usar cualquiera
de los otros.
2 8 1 El submódulo optimize
Este módulo proporciona una serie de algoritmos para la resolución de
problemas de optimización y el cálculo de raíces. En estas notas se ha usado
la versión 0.13.3 de SciPy, por lo que es posible que si el lector usa una versión
anterior algunas funciones no estén disponibles.
El módulo permite resolver problemas de optimización con y sin restric-
ciones para funciones de una o varias variables, usando diversos algoritmos
que incluyen optimización global, métodos de mínimos cuadrados, métodos
quasi-Newton, programación secuencial cuadrática, entre otros. Dado que no
es nuestra intención ser muy exhaustivos, veremos sólo algún ejemplo sencillo
del uso de alguno de estos métodos. En cualquier caso, la elección del método
más adecuado al problema a resolver precisa de un conocimiento general de
los problemas de optimización que se escapa del alcance de estas notas.
Como primer ejemplo consideraremos la función de Rosenbrook de N
variables, definida por
∑ −1
N
[ ]
f (x0 , . . . , xN −1 ) = 100(xi − x2i−1 )2 + (1 − xi−1 )2
i=1
siendo el resultado:
Optimization terminated successfully .
Current function value : 0.000000
70 Tema 2 NumPy y SciPy
Iterations : 26
Function evaluations : 2904
resultando:
status : 0
success : True
direc: array ([[ 1.00000000 e+00 , 0.00000000 e+00,
0.00000000 e+00,
0.00000000 e+00 , 0.00000000 e+00, 0.00000000 e+00,
0.00000000 e+00] ,
[ -1.16178090e-06 , -1.38844961e -06, 5.31919603e-06,
1.18135619e -05 , 4.32066364e -06 , -1.96578047e-06,
9.91290351e -06] ,
[ 1.87511323e -08, 4.17923168e-08, 6.17959385e-07,
6.04935481e -07 , 9.14081580e -07 , 1.69213885e-06,
2.60044460e -06] ,
[ -1.33951405e-03 , -2.63439121e -03, -6.99973806e-03,
-9.52271263e -03, -2.15130721e-02, -4.47290037e-02,
-9.44484188e -02] ,
[ 0.00000000 e+00 , 0.00000000 e+00, 0.00000000 e+00,
0.00000000 e+00 , 1.00000000 e+00, 0.00000000 e+00,
0.00000000 e+00] ,
[ -1.11668317e-01 , -1.97386502e -01, -1.16436208e-01,
-6.49484932e -02, -1.82185447e-02, 2.81508404e-03,
8.48222299e -03] ,
[ -6.41674658e-10 , -1.70815040e -09, -2.51554421e-09,
-6.56574489e -09, -1.31350719e-08, -2.62921083e-08,
-5.15242505e -08]])
nfev: 2904
fun: 2.2173970636557304e -21
x: array ([ 1., 1., 1., 1., 1., 1., 1.])
message : 'Optimization terminated successfully .'
nit: 26
Minimizar f (x)
sujeto a hi (x) = 0, 1 ≤ i ≤ n,
gj (x) ≥ 0, 1 ≤ j ≤ m,
lk ≤ xk ≤ uk , 1 ≤ k ≤ N,
constr2 = {'type ':'ineq ', 'fun ': lambda x: x[1] -1, 'jac ':
lambda x: np. array ([0. ,1.])}
constr = (constr1 , constr2 )
La optimización se lleva a cabo con una llamada similar a las anteriores
res = minimize (fobj , [ -1.0 ,1.0] , jac=grad_fobj , constraints =
constr , method ='SLSQP ',options ={ 'disp ': True })
en el que el punto inicial es (−1, 1). El resultado es
Optimization terminated successfully . (Exit mode 0)
Current function value : -1.00000018311
Iterations : 9
Function evaluations : 14
Gradient evaluations : 9
El objeto res contiene toda la información del proceso:
print res
status : 0
success : True
njev: 9
nfev: 14
fun: -1.0000001831052137
x: array ([ 1.00000009 , 1. ])
message : 'Optimization terminated successfully .'
jac: array ([ -1.99999982 , 1.99999982 , 0. ])
nit: 9
2 8 3 El submódulo interpolate
El módulo interpolate proporciona diversos métodos y funciones para la
interpolación de datos en una o varias dimensiones. Por simplicidad en estas
notas sólo veremos cómo se utiliza la función interp1d para la interpolación
de datos en una dimensión. La llamada a este método devuelve una función que
puede ser evaluada en cualquier punto. Dicha función es obtenida mediante
interpolación sobre un conjunto de datos proporcionado en una serie de
puntos.
Por ejemplo, supongamos que tenemos el siguiente conjunto de puntos:
(0.00, 0.00), (0.20, 0.56), (0.40, 0.93), (0.60, 0.97), (0.80, 0.68), (1.00, 0.14)
y ′′ + γy ′ + ωy = 0
omega = 2.
gamma = 0.5
y0 = [2.5 , 0] # condiciones iniciales
2 9
EJERCICIOS
E.2 Escribe una función tenga como argumento de entrada una matriz y
devuelva 1 si es simétrica, −1 si es antisimétrica y 0 en caso contrario.
E.3 Crear una función para construir una matriz de tamaño n con una
78 Tema 2 NumPy y SciPy
E.10 Crear una función para construir una matriz de tamaño n con una
estructura como la siguiente:
1 2 3 ··· n−1 n
1 3 3 ··· n−1 n
1 2 5 ··· n−1 n
. . . ..
. . . .. ..
. . . . . .
1 2 3 ··· 2n − 3 n
1 2 3 ··· n − 1 2n − 1
E.13 Construye una función para calcular, en función del tamaño, los
autovalores de las matrices del tipo siguiente y comprueba que su producto
coincide con el determinante:
2 1 0 0 ··· 0
1 2 1 0 · · · 0
0 1 2 1 · · · 0
. . . . . ..
. . . . . . .
. . . .
0 0 0 0 ··· 2
E.14 Construye una función tal que, dado un array de longitud cualquiera,
construya el conocido como determinante de Vandermonde.
1 1 1 ··· 1
x1 x2 x3 ··· xn
x2 x22 x23 ··· x2n
1
. .. .. ..
. ..
. . . . .
xn−1 xn−1 xn−1 · · · xn−1
1 2 3 n
¿Podrías escribir el código para calcular esta fórmula usando un solo bucle?
¿Y sin bucles?
E.15 Crea un array aleatorio de enteros entre 0 y 100 bidimensional, de
tamaño 1000 × 1000. Cuenta el número de múltiplos de 5 que hay en cada fila
y luego calcula la media de los valores obtenidos. Repite el proceso 100 veces.
Indicación: usa el método mean.
E.16 Considera un conjunto de n puntos uniformemente espaciados en el
intervalo (0, 1), x0 < x1 < · · · < xn−1 . Dada una función u, sabemos que una
2.9 Ejercicios 81
Maximizar xexy
sujeto a x2 + y = 0
82 Tema 2 NumPy y SciPy
y ′′ − xy = 0
1 1
y(0) = √ , y ′ (0) = − √
3
32 Γ( 23 )
3
3Γ( 13 )
3 1
GRÁFICOS INTERACTIVOS
83
84 Tema 3 Gráficos con Matplotlib
El Backend
El uso diverso que permite Matplotlib (embeber gráficos en GUIs, creación
de ficheros gráficos en algún formato específico, etc.) precisa de una comuni-
cación adecuada con el entorno en el que se va a generar el gráfico. El backend
es la herramienta que hace el trabajo detrás del telón, y necesita ser definido
de forma correcta. En general, una instalación estándar de Matplotlib habrá
seleccionado correctamente el backend, en cuyo caso el usuario no ha de preo-
cuparse de este tema. Sin embargo, si el funcionamiento de los gráficos no es el
adecuado, es posible que sea necesario modificar el backend por defecto. Éste
se puede seleccionar en el momento de la apertura de la consola, por ejemplo:
$ ipython --pylab ='qt '
3 1 1 Interactividad
Una vez cargado el módulo pylab, tendremos a nuestra disposición, tanto
el módulo NumPy como Matplotlib. Lo primero que haremos será saber si la
sesión es o no interactiva, para lo cual usamos la función:
In [1]: isinteractive ()
Out [1]: True
3 1 2 Figuras y gráficos
La orden figure() crea una ventana con título Figure más un número
entero que irá incrementándose sucesivamente. Es posible hacer la llamada
de la forma figure(num), bien para crear la figura con la numeración que
deseemos, o bien, si dicha figura existe, para hacerla activa. En lugar de un
número entero es posible pasar un string como argumento, que se convertirá
en el título de la ventana creada.
La orden
In [3]: plot ([1 ,3 ,2])
Out [3]: [< matplotlib .lines. Line2D at 0xb65a110c >]
In [5]: y=x**2
In [6]: plot(x,y)
Out [6]: [< matplotlib .lines . Line2D at 0xac346ac >]
El resultado puede verse en la Figura 3.3. Nótese que hemos usado la función
arange de NumPy (recuérdese que pylab importa éste módulo) y que por
tanto, x e y son arrays. Obsérvese también cómo el gráfico es reescalado para
poder mostrar la nueva línea en el eje que ya estaba creado.
Podríamos haber hecho que el nuevo comando plot borrara el dibujo
anterior, en lugar de añadirse al existente. La función hold es la encargada
de activar o desactivar el estado de concurrencia, esto es, si los sucesivos
dibujos se mostrarán junto a los anteriores, o bien éstos serán borrados
y sustituidos por el último. Se puede cambiar de estado invocándola sin
3.1 Gráficos interactivos 87
Si lo que queremos es borrar los gráficos de la figura activa sin cerrar la ventana
disponemos de
cla () # borra los gráficos pero mantiene el eje
clf () # borra los ejes pero mantiene la ventana de la figura
3 1 3 Subplots
El posible tener varios ejes distintos en la misma ventana gráfica, para lo
cual usaremos la orden:
subplot (n,m,k)
abre una ventana como la de la Figura 3.4a y selecciona dicho eje como el
eje activo. Nótese que la figura es dividida en dos filas de tres columnas cada
88 Tema 3 Gráficos con Matplotlib
(a) (b)
3 1 4 Axes
Es posible organizar los ejes creados en una figura de forma no estructurada
con el comando axes:
In [1]: axes ()
Out [1]: <matplotlib .axes. AxesSubplot at 0xa8aa5ac >
crea unos ejes por defecto, que equivale a hacer subplot(111). Si a continua-
ción escribimos:
90 Tema 3 Gráficos con Matplotlib
3 2
AÑADIENDO OPCIONES
normalizadas [0.125,0.1,0.775,0.8].
3.2 Añadiendo opciones 91
x= linspace (0 ,1 ,30)
y=sqrt(x)
z=x**2
(a) plot(x,y,'r-o',x,z,'g:')
(b) plot(x,y,'m--s',x,z,'bx')
3 3
CONFIGURANDO VARIOS ELEMENTOS DEL GRÁFICO
Títulos y leyendas
Podemos incluir un título al eje del gráfico a dibujar con el comando
title (cadena)
También es posible distinguir cada uno de las líneas trazadas con plot me-
diante una leyenda, usando una etiqueta definida por el argumento opcional
label. La leyenda se activa con el comando legend, que entre otros argu-
mentos permite situar la leyenda en posiciones predeterminadas con la opción
3.3 Configurando varios elementos del gráfico 93
loc, el estilo de fuente, etc. Una vez más la ayuda del comando proporciona
todas las posibilidades. La Figura 3.9 muestra el resultado de las siguientes
órdenes:
x= linspace (0 ,1 ,20)
y=x**2
z=x**3
plot(x,y, linewidth =2, label ='$x ^2$',color =(0 ,1 ,0))
plot(x,z, linestyle ='dashed ',color =(1 ,0 ,1) ,label ='$x ^3$')
title ('Un par de funciones ',fontsize =14)
legend (loc =0)
Nótese que en las cadenas de caracteres que conforman las etiquetas para la
leyenda se ha usado notación LATEX. También se han usado otras opciones del
comando plot. La leyenda debe ser activada después de los comandos plot y
recogerá todas las etiquetas en función del orden.
Texto y anotaciones
La Figura 3.10 ha sido generada con el siguiente código:
axes( axisbg =(0.35 ,0.25 ,0.75) )
x= linspace (0 ,1 ,20)
y=x**2
plot(x,y,'b-o')
text(x[12] -0.22 ,y[12] ,u'Texto aquí ',fontsize = 12,
horizontalalignment ='right ')
arrow (x[12] -0.2 ,y[12] ,0.2 ,0. , color ='yellow ',
length_includes_head = "True", width =0.008 , head_width
=0.02)
94 Tema 3 Gráficos con Matplotlib
Como podemos ver, este gráfico ha sido generado con la orden scatter
que en lugar de dibujar líneas, dibuja un conjunto de puntos (desordenados)
cuyas coordenadas vienen dadas por dos listas (en nuestro caso, dos arrays
aleatorios). El resto de órdenes establece leyendas para los ejes (con xlabel e
ylabel), los límites que determinan los ejes del gráfico (con xlim e ylim), y las
marcas que se muestran en cada eje (con xticks e yticks), que son definidas
a través de una lista o un array. Por último, la orden minorticks_on() activa
las marcas de subdivisión en ambos ejes.
96 Tema 3 Gráficos con Matplotlib
3 4
GRÁFICOS Y OBJETOS
Crearemos una figura, la cual asignamos a una variable para acceder adecua-
damente a los métodos disponibles.
fig = plt. figure ()
Los objetos gráficos tienen su propia jerarquía, por ejemplo, en una figura
podemos incluir varios ejes (tal y como hacíamos con subplot):
ax1 = fig. add_subplot (211)
ax2 = fig. add_subplot (212)
Ahora la variable a es una lista que contiene dos objetos gráficos de tipo
Line2D, y b es un sólo objeto gráfico de este tipo. Obsérvese el uso de la coma
para almacenar el objeto gráfico y no la lista.5 Ahora podemos acceder a las
diversas propiedades de cada uno de los objetos gráficos usando métodos:
a[0]. set_linewidth (2)
a[1]. set_color ('magenta ')
b. set_label (r'$\sin x$ ')
b. set_linestyle ('--')
ax2. legend ()
b. set_marker ('s')
b. set_markerfacecolor ('r')
b. set_markersize (3)
plt.draw ()
El resultado puede verse en la Figura 3.12. Las distintas opciones para el objeto
Line2D pueden consultarse en la ayuda. Por supuesto, también se pueden
emplear las opciones del mismo modo que en la secciones anteriores.
Figura 3.12
# banda de resaltado
band = ax. axvspan (2* np.pi /5 ,3* np.pi /5)
band. set_color ('red ') # ponemos color
band. set_alpha (0.2) # ponemos transparencia
Figura 3.13
3 5
GRÁFICOS 3D
Podemos dibujar curvas con el método plot vinculado a este tipo de ejes,
usando tres listas o arreglos que proporcionan las coordenadas de los puntos
de la curva. Por ejemplo,
t = np. linspace (-4* np.pi ,4* np.pi ,100)
z = np. linspace ( -2 ,2 ,100)
r = z**2+1
x = r*np.sin(t)
y = r*np.cos(t)
b = ax.plot(x,y,z, linewidth =2)
El parámetro zdir señala el eje sobre el que se dibujan los contornos, mientras
que offset señala el nivel en el que se muestran (si este parámetro no aparece,
se dibuja cada contorno en su nivel correspondiente). Nótese la selección de
transparencia sobre el objeto cset con el parámetro alpha y el comando setp.
102 Tema 3 Gráficos con Matplotlib
3 6
EJERCICIOS
E.6 Considera los puntos (0, 0), (1, 3), (2, −1), (3, 2), (4, 2) y, (5, −1).
Dibújalos usando triángulos de color verde. A continuación, calcula la función
interpoladora lineal, el spline cúbico y el polinomio interpolador de Lagrange.
Dibuja cada uno de ellos en un color distinto y etiquétalos para que aparezca
una leyenda.
E.7 Considera el siguiente código que genera tres líneas l1 , l2 y l3 :
from pylab import *
t1 = linspace (0.0 , 2.0, 20)
t2 = linspace (0.0 , 2.0, 100)
f = figure (1)
ax = f. add_subplot (111)
l1 , = ax.plot(t2 , exp(-t2))
l2 , l3 = ax.plot(t2 , sin (2* pi*t2), t1 ,log (1+ t1))
Realiza las siguientes modificaciones añadiendo nuevas líneas al código:
(c) 100 ptos. en el círculo unida�d (d) 100 ptos. en el círculo unidad
105
106 Tema 4 Programación Orientada a Objetos
5
2 3
3
1 4
2
0 . 1
0
4 1
DEFINIENDO CLASES
Las clases se definen con la palabra clave class seguida del nombre asig-
nado a la clase y que define el tipo de objeto. Es una convención ampliamente
usada nombrar las clases definidas por el usuario con la inicial en mayúsculas.
También es muy conveniente documentar adecuadamente la clase.
Como es habitual en Python, la sangría marcará el fragmento de código
correspondiente a la clase. A continuación, aunque en Python no es obliga-
torio, aparece el denominado constructor. Se trata del método __init__ que
se ejecuta cuando la clase se instancia. El proceso de instanciación no es más
que la definición de un objeto perteneciente a esta clase.
Puesto que __init__ es un método, esto es, una función, se define como ya
vimos con la palabra clave def. Los argumentos de los métodos de una clase
son un poco peculiares pues el primero de ellos siempre es self, que se refiere
al propio objeto.2 El resto de argumentos deberá aparecer en el momento de
instanciar al objeto, y los podemos entender como los datos de entrada en la
creación del objeto.
De este modo, para definir un objeto punto, instanciamos su clase del
siguiente modo:
>>> p = Point (2. ,3.)
En principio no hay mucho más que hacer con un objeto de este tipo. Podemos
acceder a sus atributos o bien modificarlos:
>>> p.x
2.0
>>> p.y
3.0
>>> p.x = 5.
>>> print p
<__main__ . Point instance at 0 x7fb7f0235128 >
sólo obtenemos información sobre el mismo. Esto es debido a que no hemos
especificado cómo imprimir adecuadamente el objeto. Para hacer esto se define
el método __str__ dentro de la clase:
def __str__ (self):
return "({0} ,{1})". format (self.x,self.y)
Ahora (habrá que volver a ejecutar la clase),
>>> p = Point (2. ,3.)
>>> print p
(2.0 ,3.0)
Vamos ahora a construir los objetos de tipo nodo. Básicamente este objeto
no es más que un punto junto con un identificador. Una primera opción podría
ser esta:
class Nodo:
"""
describe un nodo mediante identificador y punto
"""
def __init__ (self ,n,a,b):
self.id = n
self.p = Point (a,b)
Entonces,
>>> a = Nodo (0 ,1. ,3.)
>>> a.id
0
>>> print a.p
(1.0 ,3.0)
>>> b.p.x
1.0
Sin embargo, dado que hay una gran similitud entre los objetos tipo punto
y los objeto tipo nodo, otra opción consiste en apoyarse en el concepto de
herencia, que no es más que el establecimiento de una relación entre dos
clases, de manera que los atributos y métodos de una puedan ser usados en
la otra. En nuestro caso es evidente que los atributos x e y de la clase Point
deberán ser heredados por la clase Node que vamos a crear:
class Node(Point ):
"""
clase que hereda de la clase Point
"""
def __init__ (self ,n,xx ,yy):
self.id = n
self.x = xx
self.y = yy
4.1 Definiendo clases 109
# construcción de nodos
hx = (p2.x - p1.x) / float (nx)
hy = (p2.y - p1.y) / float (ny)
k = 0
for j in range (ny +1):
yy = p1.y + float (j*hy)
for i in range (nx +1):
xx = p1.x + float (i*hx)
self.nodos . append (Node(k,xx ,yy))
k += 1
# construcción de barras
for j in range (ny):
for i in range (nx):
n1 = i+ j*( nx +1)
n2 = n1 + 1
n3 = n1 + nx + 1
n4 = n3 + 1
# barras en cada elemento
b1 = Bar(self.nodos [n1],self. nodos[n2])
b2 = Bar(self.nodos [n1],self. nodos[n3])
b3 = Bar(self.nodos [n1],self. nodos[n4])
4.1 Definiendo clases 111
Nótese que hemos definido un par de listas: nodos y barras en las que
almacenar los elementos que nos interesan. Tal y como está construido, el
identificador de cada nodo coincide con el índice que ocupa en la lista nodos,
lo que nos simplifica la búsqueda de los nodos. Una alternativa hubiera sido
escribir:
self.nodos[k] = Node(k,xx ,yy)
[ 1., 1.]])
>>> m. get_conection ()
array ([[0 , 1],
[0, 2],
[0, 3],
[1, 2],
[1, 3],
[2, 3]])
Es evidente que podríamos haber creado una función que tuviera como
entrada las coordenadas de los puntos del rectángulo y el número de nodos a
usar en cada dimensión, y cuya salida fuera precisamente los dos arrays que
hemos obtenido; posiblemente hubiera sido incluso más sencillo de implemen-
tar. Sin embargo, como ahora veremos, es más conveniente el uso de clases
porque nos va a permitir una flexibilidad aun mayor.
4 1 1 Modificando clases
Supongamos que ahora queremos dibujar la estructura obtenida. Si hubié-
ramos implementado una función tendríamos dos opciones: o bien modifica-
mos la función creada para añadirle la parte gráfica, o bien implementamos
la parte gráfica en una función aparte, que reciba los arrays que definen la
estructura y los dibuje.
La primera opción puede resultar un engorro, pues cada vez que ejecutemos
la función obtendremos los arrays y el gráfico y habrá ocasiones en las que
queramos crear sólo la información de la estructura y otras en las que sólo
queramos dibujar. La segunda opción nos obliga a llamar primero a la función
para obtener los arrays de coordenadas y conexiones, y luego pasarlos a la
nueva función para dibujar.
Sin embargo, implementar un nuevo método dentro de la clase para que
construya el gráfico es mucho más cómodo, pues podremos invocarlo indepen-
dientemente de que construyamos o no los arrays de coordenadas y conexiones.
Podríamos añadir a la clase Truss algo así:
def plotting (self):
plt.ion ()
fig=plt. figure ()
bx = fig. add_subplot (111)
De este modo, una vez creada una estructura, nos bastará con invocar al
método plotting para obtener el gráfico correspondiente.
4.1 Definiendo clases 113