Practica 7
Practica 7
Practica 7
Grado en Fı́sica
Curso 2017-18
Este módulo proporciona una manera conveniente y eficiente de manejar vectores numéricos (los
denominaremos arrays a partir de ahora) en Python. Los arrays son homogéneos (a diferencia de
las listas de Python que pueden ser heterogéneas), los elementos de un array pueden ser de tipo
booleano, entero, real o complejo (pero sin mezclarse). Un array puede tener varias dimensiones,
a las que se accede por ı́ndices (ordenados). Además numpy dispone de una serie de rutinas que
permiten operaciones rápidas sobre arrays. Con el fin de entender la potencia y la utilidad del uso
de numpy plantearemos el siguiente ejemplo:
Ejemplo 7.0.1. En el tratamiento de series de datos (temperatura, campo magnético, etc.) se utiliza
a veces la media móvil, es decir, sustituir un elemento de la serie por su media con puntos anteriores
y posteriores.
15
10
t ºC
1
Universidad de Oviedo Departamento de Fı́sica
Si los datos de temperatura los tenemos en el fichero np01.txt cuya estructura es de la forma:
y = mediamovil(x)
# hacemos cuatro medias móviles más
for i in range(4): y = mediamovil(y)
nx = loadtxt("np01.txt")
n, x = nx[:,0], nx[:,1]
y = mediamovil(x)
# hacemos cuatro medias móviles más
for i in range(4): y = mediamovil(y)
En los lenguajes interpretados (como Python) los bucles son lentos. Para realizar cálculo numérico
es mejor que los bucles estén ‘escondidos’ en código compilado (que es mucho más rápido). Ası́
pues, cualquier mecanismo que nos evite los bucles será muy bienvenido.
Numpy nos permite eliminar bucles y eso también nos ahorra lı́neas de código (por tanto es más fácil
de leer) y el código se parece más a la notación matemática. Además, si las rutinas de numpy en
lenguaje compilado están desarrolladas para cálculo en paralelo también nos aprovechamos de los
múltiples procesadores que hubiera en el ordenador.
También, y no menos importante, nos ‘obliga’ a pensar en paralelo: las operaciones son las mismas
sobre diferentes elementos de los vectores. Los programas codificados con lenguajes ‘paralelos’ son
normalmente más cortos, más claros y más cercanos a las ideas cientı́ficas que queremos programar.
Adaptar el código (cuando es posible) para que funcione en paralelo se denomina vectorializar o
vectorizar. En general consiste en eliminar bucles con contadores. Más adelante, cuando conozcamos
la sintaxis de numpy y sus posibilidades, veremos ejemplos de cómo vectorializar código.
También existe la posibilidad de usar la estructura matrix, aunque creemos más recomendable usar
array en su lugar (sobre todo en los comienzos del aprendizaje de numpy) pues su uso es más
homogéneo. Existen muy pocas diferencias entre ambas, la principal es la definición del producto de
dos matrices (pero veremos que es muy sencillo con array).
Para utilizar numpy lo primero que se debe hacer es cargar el módulo de la siguiente manera (por
ejemplo):
from numpy import *
aunque también se puede usar el módulo con nombre:
import numpy as np
pero entonces en la invocación de las funciones tenemos que añadir delante np.
La mayorı́a de las funciones asociadas a numpy que vamos a utilizar a lo largo de este curso poseen
más argumentos opcionales que los indicados. Para más información debe consultarse:
http://docs.scipy.org/doc/numpy/reference/
Creación de arrays.
>>> l = [3, 4, 2]
>>> a = array(l)
>>> print(a)
[3 4 2]
>>> b = asarray(a)
>>> print(b)
[3 4 2]
La operación inversa (convertir arrays en listas) se realiza con la función tolist (OJO tolist(a)
no funciona).
7.4.2. Creación de arrays usando una función que retorna un array con valores
arange, linspace, ones, zeros, eye, copy, concatenate, repeat, roll, logspace, mgrid y
ogrid.
La función arange
Es similar a range, pero en vez de crear una lista crea un array y además se puede usar un espaciado
de tipo float.
>>> a = arange(5)
>>> print(a)
[0 1 2 3 4]
>>> c = arange(1, 2, 0.2)
>>> print(c)
[ 1. 1.2 1.4 1.6 1.8]
La función linspace
Esta función produce un array con un número determinado de elementos linealmente espaciados
entre dos lı́mites:
>>> b = linspace(1, 2, 6)
>>> print(b)
[1. 1.2 1.4 1.6 1.8 2.]
La función ones
Si se pasa como argumento una tupla usa ese número de elementos en cada dimensión:
>>> b = ones((3, 2)) # 3 filas, 2 columnas
>>> print(b)
[[1.0 1.0]
[1.0 1.0]
[1.0 1.0]]
La función zeros
Para más de una dimensión el primer argumento de ones y zeros debe ser una tupla (de ahı́ los
dobles paréntesis).
Crea un array con todo ceros con la misma forma que otro array:
>>> a = array([[1, 2, 3, 4], [4, 5, 6, 7]])
>>> b = zeros like(a) # 2 filas, 4 columnas
>>> print(b)
[[0 0 0 0]
[0 0 0 0]]
También existe la función ones like que crea un array con todos unos.
La función eye
Crea un array con la diagonal principal llena de unos y el resto de los elementos ceros:
>>> b = eye(4, 3) # 4 filas, 3 columnas
>>> print(b)
[[1.0 0.0 0.0]
[0.0 1.0 0.0]
[0.0 0.0 1.0]
[0.0 0.0 0.0]]
También se puede indicar que los unos estén por encima o debajo de la diagonal principal usando
un parámetro k (0 por defecto: diagonal principal) y puede tomar valores positivos (por encima de
la diagonal principal) y negativos (por debajo):
>>> b = eye(5, 4, k = -1)
>>> print(b)
[[ 0. 0. 0. 0.]
[ 1. 0. 0. 0.]
[ 0. 1. 0. 0.]
[ 0. 0. 1. 0.]
[ 0. 0. 0. 1.]]
La función copy
Cuando se asigna una variable a un array preexistente no se copia el array sino que se crea otro
nombre para el array:
>>> x = array([1, 2, 3])
>>> y = x # y es una ’vista’ de x
>>> x[0] = 10 # luego veremos esta orden
>>> print(y)
[10 2 3]
Realmente se puede considerar a y (también a x) como una ventana que mira en el verdadero array
(un objeto en memoria con caracterı́sticas de array).
La función copy realiza una copia a partir de otro array. En su versión más simple (y usual) serı́a:
>>> x = array([1, 2, 3])
>>> y = copy(x)
>>> x[0] = 10
>>> print(y)
[1 2 3]
El nuevo array al que referencia y no es el mismo que el antiguo x.
La función concatenate
La función concatenate((a1, a2, ...), axis=0) concatena arrays según una de las
dimensiones. La forma de los arrays ha de ser la misma excepto según la dimensión correspondiente
a axis (por defecto 0):
>>> a = array([[1, 2], [3, 4]])
>>> b = array([[5, 6]])
>>> concatenate((a, b))
>>> # (OJO con los paréntesis)
array([[1, 2],
[3, 4],
[5, 6]])
La función repeat
La función repeat(a, copias, axis) sirve para extender arrays según una dimensión. En su modo
más simple de funcionamiento conviene indicar la dimensión (‘axis’) en la que queremos repetir el
array:
>>> a = array([[1, 2], [3, 4]])
>>> repeat(a, 2, axis=0)
array([[1, 2],
[1, 2],
[3, 4],
[3, 4]])
>>> repeat(a, 2, axis=1)
array([[1, 1, 2, 2],
[3, 3, 4, 4]])
Si no indicamos el eje:
>>> repeat(a, 2)
array([1, 1, 2, 2, 3, 3, 4, 4])
La función roll
La función roll(a, desplazamiento, axis) desplaza de manera circular los elementos a lo largo
de una dimensión. En su modo más simple de funcionamiento conviene indicar la dimensión (‘axis’):
>>> a = array([[1, 2, 3, 4], [5, 6, 7, 8],[9,10,11,12]])
>>> roll(a, 1, axis=0)
array([[9, 10, 11, 12],
[1, 2, 3, 4],
[5, 6, 7, 8]])
>>> roll(a, 1, axis=1)
array([[4, 1, 2, 3],
[8, 5, 6, 7],
[12, 9, 10, 11]])
>>> roll(a, -1, axis=1)
array([[ 2, 3, 4, 1],
[6, 7, 8, 5 ],
[10, 11, 12, 9]])
Como vemos la rotación a lo largo de un eje puede ser hacia atrás (desplazamiento negativo).
La función logspace
Produce un array con un número determinado de elementos logarı́tmicamente espaciados entre dos
lı́mites:
>>> b = logspace(1, 2, 6)
>>> print(b)
[ 10. , 15.8489319, 25.1188643, 39.8107171,
63.0957345, 100. ]
Se incluyen 6 puntos desde 101 hasta 102 . Como en el caso de linspace, se incluyen los extremos.
La función ogrid
Esta función se usa para generar arrays usados en mallados. Esta función nos devuelve una lista con
dos arrays, el primero con las coordenadas x y el segundo con las y:
>>> p = ogrid[0:2:0.5, 0:2] # no se usa con paréntesis
>>> print(p)
[array([[ 0. ],
[ 0.5],
[ 1. ],
[ 1.5]]), array([[ 0., 1.]])]
La función mgrid
Nos devuelve un array 3D, siendo m[0,:,:] las coordenadas x de los puntos y m[1,:,:] las y.
[[ 0. 1. ]
[ 0. 1. ]
[ 0. 1. ]
[ 0. 1. ]]]
Existen varias funciones para leer datos (preparados previamente de cierta manera) a partir de
ficheros. La función básica es loadtxt. Su sintaxis es la siguiente:
Este ejemplo lee del fichero de texto cuyo nombre está guardado en la variable fname y los incluye
en el array out. Sólo se exponen algunos argumentos que tienen puestos los valores por defecto:
dtype indica el tipo de dato (entero, real, etc.), por defecto float.
comments indica un string por el que empiezan las lı́neas con comentarios (que se saltan entonces).
Por defecto es #.
delimiter indica los caracteres que se usan para separar datos, por defecto espacios.
skiprows indica cuántas filas iniciales se saltan antes de empezar a leer realmente el array (por
defecto 0). Las lı́neas de comentarios se cuentan también.
usecols indica una secuencia de columnas para leer (por defecto todas), por ejemplo
usecols=(1,3,5) lee sólo esas columnas.
Ejemplo 7.4.4. Uso de la función loadtxt. Escriba el siguiente código en un fichero con el nombre
z.txt:
# z.txt ------
1 2 3
# otro comentario
4 5 6
7 8 9
#-------------
Los arrays tienen asociados un número de dimensiones y un tamaño a lo largo de cada dimension
(‘forma’). Se puede acceder a esa información de diversas maneras, usando ndim, size, shape, len
y reshape.
Los datos del array (o sea los valores de sus elementos) son los mismos independientemente de la
forma asociada (puesto que los arrays se guardan de manera secuencial en memoria).
1 2 3
Vista con forma (2,3):
4 5 6
1 2
Vista con forma (3,2): 3 4
5 6
Para determinar la forma, o sea el número de elementos en cada dimensión se utiliza shape:
>>> a = array([[3, 2, 4], [3, 3, 2]])
>>> shape(a)
(2, 3)
>>> a.shape
(2, 3)
Como vemos a.shape es una tupla de enteros no una función.
Se puede cambiar la forma de un array con la función reshape o asignando una nueva tupla a la
variable a.shape:
>>> a = array([[3, 2, 4], [3, 3, 2]])
>>> a.shape
(2, 3)
>>> a.shape = (a.size, )
>>> print(a)
[3 2 4 3 3 2]
Hemos cambiado las dimensiones del array, aunque no su tamaño (o sea 6 en este caso): a.size
no puede cambiar.
En el ejemplo anterior, a partir de este momento el array tiene una sola dimensión y se accede (como
veremos posteriormente) con un solo ı́ndice:
>>> a[0] = 100
>>> a.shape = (2, 3)
>>> print(a)
[[100 2 4]
[ 3 3 2]]
Vemos que las modificaciones realizadas en a se transmiten aunque se cambie de nuevo la forma.
También podemos cambiar la forma recurriendo a la función reshape, en este caso el procedimiento
será el siguiente:
>>> a = array([[1, 2, 3], [4, 5, 6]])
array([[1, 2],
[1, 2, 3],
[4, 5, 6]])
>>> a=a.reshape(3, 2)
>>> print(a)
array([[1, 2],
[3, 4],
[5, 6]])
Si a es un array, transpose(a), a.transpose() y a.T (que hacen lo mismo), nos devuelven una
vista traspuesta del array a:
>>> b = array([[1, 2, 3], [4, 5, 6]])
>>> print(b.T)
[[1 4]
[2 5]
[3 6]]
Se puede indicar el orden exacto de las dimensiones mediante una lista opcional de ejes, para
La función ravel nos devuelve una vista de una única dimensión, mientras que flatten nos devuelve
una copia:
>>> b = array([[3, 2, 4], [3, 3, 2]])
>>> print(b)
[[3 2 4]
[3 3 2]]
>>> c = ravel(b)
>>> print(c)
[3 2 4 3 3 2]
>>> d =b. flatten()
copia de secciones.
Para acceder a los elementos de un array se usan ı́ndices enteros, en lectura o escritura (como a
continuación):
>>> b = array([[3, 2, 4], [3, 3, 2]])
>>> b[1][1] = 10 # una manera
La potencia de los arrays se manifiesta en el manejo de secciones (slices en inglés) de los mismos.
Una sección es un bloque (formado por elementos a veces no contiguos) dentro de un array. El
bloque se define a través de patrones que verifican los ı́ndices (tantos como dimensiones).
00 01 02 03 04
10 11 12 13 14 El primer ı́ndice indica la fila y el segundo la
20 21 22 23 24 columna.
30 31 32 33 34
Patrón: inicio:final:incremento.
El inicio es inclusivo y el final excluyente.
Ejemplo 7.6.1. Acceso a secciones de un array.
Se quiere extraer las columnas impares. Para ello generamos un patrón de fila : (que indica todo el
rango) y uno de columna 0::2.
Se genera una vista de los elementos que están indicados en rojo y negrita:
00 01 02 03 04
10 11 12 13 14
20 21 22 23 24
30 31 32 33 34
00 02 04
10 12 14
20 22 24
30 32 34
Cualquier modificación en una sección del array a se manifiesta en cambios en el propio array a:
>>> a[:, 0::2] = 77 # Nota
>>> print(a)
[[77 1 77 3 77]
[77 11 77 13 77]
[77 21 77 23 77]
[77 31 77 33 77]]
Este array a[:, 0::2] concreto es conformable con cualquier array con forma (4, 3).
Eso significa que puede sustituirse donde se necesite un array de esa forma. Por ejemplo se puede
sumar a un array de forma (4, 3).
Ejercicio 7.6.1. Introduzca en la terminal de Python un array de unos con forma (4, 3) y realice
la suma entre el array de unos y el array a[:, 0::2].
También se puede multiplicar matricialmente (luego lo veremos) con un array de forma (3, ...).
5. Obtenga una vista con los elementos comunes de las lı́neas impares y las columnas impares.
Para generar una copia y no una vista se debe utilizar la función copy acompañada de la selección
de sección correspondiente:
>>> b = copy(a[:, ::2])
>>> print(b)
[[ 0 2 4]
[10 12 14]]
>>> b[0,0] = 1000 # no modifica a[0,0]
Se pueden utilizar también listas como patrones de selección de secciones de arrays. En este caso se
obtiene una copia y no una vista:
>>> c = a[:2:, ::2] # sección
>>> b = a[:2:, [0,2,4]] # copia
# también b = a[:2:, range(0,6,2)]
>>> print(b)
[[ 0 2 4]
[10 12 14]]
>>> b[0,0] = 1000
# no modifica a[0,0]
>>> c[0,0] = 2222
# sı́ modifica a[0,0]
Existen operaciones, algunas elemento a elemento (en inglés elementwise), y otras resultado de
llamadas a funciones que retornan otros arrays o escalares.
Las operaciones básicas son: suma (+), resta (-), multiplicación (*), división (/), potenciación (**) y
las comparaciones. También se aplican elemento a elemento las denominadas funciones universales.
Para poder operar entre sı́ dos arrays deben tener la misma forma. Realmente es algo más complicado,
pero no lo describiremos aquı́. En el argot del cálculo en paralelo se dice que los arrays deben ser
conformables.
Las funciones que devuelven valores son: ‘y‘ lógico (all), ‘o’ lógico (any), máximo (max), mı́nimo
(min), suma (sum), producto (prod), media (mean), desviación estándar o tı́pica (std), producto
matricial (dot), producto vectorial (cross), producto externo (outer).
También se puede utilizar el operador += que no crea un nuevo array sino que modifica el que está
a la izquierda:
Para poder multiplicar (elemento a elemento) dos arrays deben tener la misma forma:
>>> a = array([[3, 2, 4], [3, 3, 2]])
>>> b = array([[-3, 2, -4], [0, 1, 2]])
>>> c = a*b
>>> print(c)
[[ -9 4 -16]
[ 0 3 4]]
Para poder dividir (elemento a elemento) dos arrays deben tener la misma forma:
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> b = array([[-3, 2, -4], [1, 2, 1]])
>>> c = a/b
>>> print(c)
[[-1. 1. -1. ]
[ 3. 1.5 2. ]]
Cuidado con la división entre enteros.
La potencia también se aplica elemento a elemento y los dos arrays deben tener la misma forma:
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> b = array([[3, 2, 4], [1, 2, 1]])
>>> c = a**b # también c = pow(a, b)
>>> print(c)
[[ 27. 4. 256.]
[ 3. 9. 2.]])
También se puede usar **= de manera similar a +=.
Ejercicio 7.7.1. Demuestre que lı́mn→∞ (1 + n1 )n = e. Para hacer esto cree un vector n que tenga
los elementos: 1, 10, 100, 500, 1000, 2000, 4000 y 8000. Seguidamente cree un vector y en el cual
cada elemento será calculado a partir de los elementos de n mediante (1 + n1 )n .
Ejercicio 7.7.2. La profundidad de un pozo, h, en metros se puede determinar a partir del tiempo
que tarda en caer una piedra en su interior (con velocidad inicial 0). Este cálculo viene determinado
por: h = 12 gt2 , donde t es el tiempo en segundos y g = 9.81 m/s2 . Calcule h para t = 1, 2, 3, 4, 5,
6, 7, 8, 9 y 10 s.
7.7.6. Broadcasting
Este término describe cómo trata numpy arrays de diferentes formas en las operaciones aritméticas.
La aplicación en casos generales de las reglas de broadcasting es relativamente compleja para el nivel
de estos apuntes. Para más información, véase la documentación de numpy y también el artı́culo
expuesto en http://www.scipy.org/EricsBroadcastingDoc.
2. Dos dimensiones son compatibles en las operaciones aritméticas cuando son iguales o cuando
una de ellas es 1. Los tamaños de las dimensiones que son 1 se extienden hasta completar el
tamaño del otro array.
Ası́ pues, siempre que se pueda realizar broadcasting según las reglas anteriores, las dimensiones
menos completas de cada array se ‘extienden’ para que los arrays al operar parezcan tener la misma
forma. Todos los cálculos se hacen sin hacer realmente copias. Por tanto, el broadcasting es una
operación computacionalmente muy eficiente.
Como vemos a.shape = (2, 3) y g.shape = (2, 1), y por tanto estamos en el caso expuesto.
El array g se puede ‘extender’ tres veces a lo largo de segunda dimensión, repitiéndose de la manera:
Pasemos al caso 3 con dos arrays de diferente forma (shape). En ese caso se comparan las
dimensiones a lo largo de cada eje (axis) empezando por la derecha. Tienen que coincidir hasta
acabarse el número de dimensiones del array más ‘corto’. Por ejemplo:
Como vemos a.shape = (2, 3) y b.shape = (3,), y por tanto estamos en el caso expuesto: el
array b se puede ‘extender’ a lo largo de una nueva dimensión 0, repitiéndose de la manera:
b = [[10 11 12]
[10 11 12]]
antes de sumarse al array a.
Como vemos z.shape = (2, ) y v.shape = (3, 1). Ambos arrays se ‘extienden’ lo necesario,
repitiéndose de la manera:
Otro caso con más dimensiones serı́a sumar los arrays A y B con la estructura siguiente:
A (4d array): 8 x 1 x 6 x 1
B (3d array): 7 x 1 x 5
A+B (4d array): 8 x 7 x 6 x 5
Vemos cómo se extienden tanto A como B en las dimensiones que son 1, y en la que falta al principio
de B.
Ejercicio 7.7.3. Cree un array a de la forma (8,1,6,1) cuyos elementos sean números enteros y
un array b de la forma (7,1,5) que contenga números en coma flotante y realice las siguientes
operaciones aritméticas: a+b, a-b y a/b
La función where está relacionada con las operaciones condicionales. Cuando se invoca con un
único argumento devuelve una tupla con los ı́ndices de los elementos del array donde se cumple la
condición:
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> d = where (a > 2)
>>> print(d)
(array([0, 0, 1, 1]), array([0, 2, 0, 1]))
Cuando where se invoca con tres argumentos sustituye los valores que cumplen una condición por
un cierto valor y los que no por otros:
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> g = where (a > 2, a**2, -1.0)
>>> print(g)
[[ 9. -1. 16.]
[ 9. 9. -1.]]
Estas funciones, denominadas en ocasiones ‘puras‘ y en el argot de Python ufuncs, no tienen efectos
laterales (no modifican por ejemplo ninguna variable global ni manejan ficheros).
Por ejemplo, sin es una ufunc. Lo son también la mayorı́a de las funciones matemáticas que se
aplican sobre escalares.
Las funciones del módulo math están redefinidas en numpy para que el cálculo sea elemento a
Entre las funciones universales se tienen: absolute, add, arccos, arccosh, arcsin, arcsinh,
arctan, arctan2, arctanh, ceil, conj, cos, cosh, deg2rad, divide, exp, exp2, floor, fmod,
hypot, log, log10, log2, mod, multiply, negative, power, rad2deg, reciprocal, remainder,
rint, sign, sin, sinh, sqrt, square, subtract, tan, tanh, trunc.
En ocasiones necesitamos definir funciones que usen como argumentos arrays. Deben ser funciones
puras. Por ejemplo:
>>> def f (a):
... return a+3*sin(a)*log(a)
...
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> f(a)
array([[ 3.46510853, 3.89083084, 0.8525469 ]
[ 3.46510853, 3.46510853, 3.89083084]])
No obstante, no todas las funciones puras que diseñemos para escalares pueden ser invocadas
directamente con arrays.
Ejemplo 7.8.1. Funciones que no admiten arrays como argumentos.
En el caso anterior se necesita vectorizar la función. Ello se hace con un decorador, que es otra
función que se invoca previamente a la nuestra (y que hace ‘cosas’ con ella). En este caso se puede
poner @vectorize en la lı́nea anterior a def h(a):.
Ejercicio 7.8.1. Repita el ejemplo anterior utilizando el decorador (@vectorize) para que la función
h(a) admita un array como argumento.
Para vectorizar una función ya definida previamente, se puede usar la función decoradora
(vectorize) para crear una nueva función.
Es más, en este caso vectorizar f es contraproducente puesto que tarda más la llamada que en el
caso presente.
Algunas de las funciones que trabajan con arrays y que devuelven valores son:
all, any, min, max, sum, prod, mean, std, dot, cross, outer, diag.
La función all
La función all realiza un ‘y’ lógico (and) con todos los elementos de un array (o con los elementos
a lo largo de un eje):
>>> a = array([[3, 2, 4], [3, 3, 0]])
>>> a.all()
False
>>> b = a.all(axis=1)
>>> print(b)
[ True False]
Recordemos que 0 (y 0.0) se toma como valor False y otro valor como True.
La función any
La función any realiza un ‘o’ lógico (or) con todos los elementos de un array (o con los elementos
a lo largo de un eje):
>>> a = array([[3, 2, 4], [3, 3, 0]])
>>> a.any()
True
>>> c = a.any(axis=0)
>>> print(c)
[ True True True]
Recordemos que 0 (y 0.0) se toma como valor False y otro valor como True.
La función a.min() (OJO min(a) no funciona) nos devuelve el mı́nimo valor del array. a.max()
(OJO max(a) no funciona) nos devuelve el máximo valor del array.
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> a.min()
2.0
>>> a.max()
4.0
Son funciones similares a min, pero devuelven la suma (sum) y el producto (prod) de los elementos
del array:
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> a.sum(axis=0)
array([ 6., 5., 6.])
>>> a.prod(axis=1)
array([ 24., 18.])
La función mean devuelve la media y std la desviación estándar de los elementos del array:
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> a.mean(axis=1)
array([ 3. , 2.66666667])
>>> a.std()
0.68718427093627676
La función dot
La función dot también se usa para realizar el producto de matrices (no confundir con el producto
elemento a elemento visto antes):
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> b = array([2., 0., -1.])
>>> dot(a, b)
array([ 2., 4.])
Para poder multiplicar dos arrays, a y b, como matrices, el número de elementos a lo largo de la
última dimensión de a debe ser igual al número de elementos de la primera dimensión de b:
>>> a.shape
(2, 3)
>>> b.shape
(3,)
La función cross
La función outer
cik = ai bk , i = 0, . . . , M − 1, k = 0, . . . , N − 1.
>>> outer(a, b)
array([[-1, 2],
[-2, 4],
[-3, 6]])
La función diag
7.10. Ejercicios
Ejercicio 7.10.1. Un proyectil se dispara con una velocidad de 750 m/s. Calcule la distancia d a la
que el proyectil alcanza el suelo si el ángulo de lanzamiento θ cambia de 5o a 85o en incrementos de
5o . Utilice operaciones elemento a elemento. Para visualizar los resultados cree una matriz de 17x2
en la cual los elementos de la primera columna sean los ángulos de lanzamiento, y los de la segunda
las correspondientes distancias redondeadas al entero más próximo.
Ejercicio 7.10.2. Dos proyectiles, A y B, se disparan en el mismo instante desde el mismo punto.
El proyectil A se dispara a una velocidad de 680 m/s con un ángulo de 65o , mientras que el proyectil
B se dispara a una velocidad de 780 m/s con una ángulo de 42o . Calcule qué proyectil llega antes
a tierra. Seguidamente, tome el tiempo de vuelo tv de ese proyectil y divı́dalo en diez incrementos,
creando para ello un vector t con 11 elementos igualmente espaciados (el primer elemento será 0 y
el último tv . Calcule la distancia entre los dos proyectiles para cada uno de los 11 valores de t.
Eliminar ı́ndices del código es posible siempre que las expresiones se puedan evaluar a la vez para
cualquier ı́ndice (si no es ası́ no se puede vectorializar el código). Además, siempre que se eliminen del
código ı́ndices que deban ser iterados con un bucle tanto mejor (en cualquier lenguaje interpretado
al menos). Pero eliminar ı́ndices implica utilizar más memoria y repensar el código (lo que a veces
es difı́cil).
Analizaremos varios casos de vectorialización de expresiones, algunos con más contenido fı́sico que
otros.
se codifica:
Se define la integral de Riemann de una función f (x) entre dos abscisas a y b mediante:
Z b
I= f (x)dx.
a
y y
f ( x) f ( x)
I
0 1 i ... n −1
x h x
a b a b
La solución de este problema puede obtenerse numéricamente por el método de los rectángulos,
usando como altura de cada rectángulo el valor de la función en la abscisa media de cada rectángulo:
n−1
X 1 b−a
I≈h f a + h + ih , h= .
2 n
i=0
∂2V ∂2V ρ
2
+ =− ,
∂x ∂y 2 ǫ0
siendo V el potencial eléctrico y ρ la densidad de carga en cada punto del recinto.
Discreticemos un recinto rectangular con un mallado h (tanto en Ox como en Oy) con nx nodos en
Ox y ny nodos en Oy, de tal manera que V (i, k) define el potencial en los puntos i = 0, 1, . . . , nx −1,
k = 0, 1, . . . , ny − 1. El problema de Dirichlet (potencial conocido en la frontera) se puede resolver
iterando en m:
h2
m+1 1 m m m m
Vi,k = Vi+1,k + Vi−1,k + Vi,k+1 + Vi,k−1 + ρi,k
4 ǫ0
i = 1, 2, . . . , n − 2, k = 1, 2, . . . , n − 2.
Esta ecuación se puede discretizar en el espacio usando n intervalos, de tal manera que h = L/n,
y en el tiempo usando un incremento temporal τ . Con i = 1, 2, . . . , n − 2 para los puntos en el
espacio, e indicando m el instante temporal, tendrı́amos:
Dτ m
yim+1 = yim + (y m
+ yi−1 − 2yim ) + Cτ yim .
h2 i+1
y[1:-1] = copy(yn[1:-1])
Las series radiactivas sin bifurcaciones están formadas por radionúclidos que se desintegran unos en
otros: 0 → 1 → 2 → . . . → n, siendo el núclido n estable. Si Ni es el número de núcleos presentes
del radionúclido i se tiene:
Ṅ0 = − λ0 N0 ,
Ṅi = λi−1 Ni−1 − λi Ni .
se puede codificar:
w = zeros like(x)
indices = range(len(x))
for i in indices:
w[i] = sum(abs(x[i]+x))
Este segundo método necesita mucha más memoria que el primero (el array bidimensional aux al
menos), pero es más rápido, ya que no lleva bucles.
n = len(x)
x.shape = (n, 1)
aux = x.repeat(n, axis = 1)
aux = aux-aux.T
w = prod(where(aux != 0, aux, 1.0), axis = 0)
Como vemos, la eliminación de un ı́ndice ‘que se pierde’ (k cuando es igual al i) conlleva la utilización
de la función where, como máscara.
Este caso puede servir de partida, por ejemplo, para codificar el cálculo de la aceleración de los
planetas y el Sol en una simulación del sistema solar (se deja como ejercicio), ya que también hay
un ı́ndice ‘que se pierde’.