En este nuevo articulo,se realiza una introduccion al data science en python,gracias a un repost de los notebooks de la gente de Python Mallorca, en la Pycon 2018. Van desde 0 hasta un nivel muy aceptable.
En esta primera entrega, empezamos tocando uno de los clasicos para gestionar matrices.

Ahora empezemos con la salsa!
Notebook de Numpy en Python
import sys
import numpy as np
Numpy¶
Numpy proporciona un nuevo contenedor de datos a Python, los ndarray
s, además de funcionalidad especializada para poder manipularlos de forma eficiente.
Hablar de manipulación de datos en Python es sinónimo de Numpy y prácticamente todo el ecosistema científico de Python está construido sobre Numpy. Digamos que Numpy es el ladrillo que ha permitido levantar edificios tan sólidos como Pandas, Matplotlib, Scipy, scikit-learn,...
Índice
¿Por qué un nuevo contenedor de datos?¶
En Python, disponemos, de partida, de diversos contenedores de datos, listas, tuplas, diccionarios, conjuntos,..., ¿por qué añadir uno más?.
¡Por conveniencia!, a pesar de la pérdida de flexibilidad. Es una solución de compromiso.
- Uso de memoria más eficiente: Por ejemplo, una lista puede contener distintos tipos de objetos lo que provoca que Python deba guardar información del tipo de cada elemento contenido en la lista. Por otra parte, un
ndarray
contiene tipos homogéneos, es decir, todos los elementos son del mismo tipo, por lo que la información del tipo solo debe guardarse una vez independientemente del número de elementos que tenga elndarray
.
(imagen por Jake VanderPlas y extraída de GitHub).
- Más rápido: Por ejemplo, en una lista que consta de elementos con diferentes tipos Python debe realizar trabajos extra para saber si los tipos son compatibles con las operaciones que estamos realizando. Cuando trabajamos con un
ndarray
ya podemos saber eso de partida y podemos tener operaciones más eficientes (además de que mucha funcionalidad está programada en C, C++, Cython, Fortran).
- Operaciones vectorizadas
- Funcionalidad extra: Muchas operaciones de álgebra lineal, transformadas rápidas de Fourier, estadística básica, histogramas,...
- Acceso a los elementos más conveniente: Indexación más avanzada que con los tipos normales de Python
- ...
Uso de memoria
# AVISO: SYS.GETSYZEOF NO ES FIABLE
lista = list(range(5_000_000))
arr = np.array(lista, dtype=np.uint32)
print("5 millones de elementos")
print(sys.getsizeof(lista))
print(sys.getsizeof(arr))
print()
lista = list(range(100))
arr = np.array(lista, dtype=np.uint8)
print("100 elementos")
print(sys.getsizeof(lista))
print(sys.getsizeof(arr))
Velocidad de operaciones
a = list(range(1000000))
%timeit sum(a)
print(sum(a))
a = np.array(a)
%timeit np.sum(a)
print(np.sum(a))
Operaciones vectorizadas
# Suma de dos vectores elemento a elemento
a = [1, 1, 1]
b = [3, 4, 3]
print(a + b)
print('Fail')
# Suma de dos vectores elemento a elemento
a = np.array([1, 1, 1])
b = np.array([3, 4, 3])
print(a + b)
print('\o/')
Funcionalidad más conveniente
# suma acumulada
a = list(range(100))
print([sum(a[:i+1]) for i in a])
a = np.array(a)
print(a.cumsum())
Acceso a elementos más conveniente
a = [[11, 12, 13],
[21, 22, 23],
[31, 32, 33]]
print('acceso a la primera fila: ', a[0])
print('acceso a la primera columna: ', a[:][0], ' Fail!!!')
a = np.array(a)
print('acceso a la primera fila: ', a[0])
print('acceso a la primera columna: ', a[:,0], ' \o/')
...
Recapitulando un poco.
Los ndarray
s son contenedores multidimensionales, homogéneos con elementos de tamaño fijo, de dimensión predefinida.
Tipos de datos¶
Como los arrays deben ser homogéneos tenemos tipos de datos. Algunos de ellos se pueden ver en la siguiente tabla:
Data type | Descripción |
---|---|
bool_ | Booleano (True o False) almacenado como un Byte |
int_ | El tipo entero por defecto (igual que el long de C; normalmente será int64 o int32 ) |
intc | Idéntico al int de C (normalmente int32 o int64 ) |
intp | Entero usado para indexación (igual que ssize_t en C; normalmente int32 o int64 ) |
int8 | Byte (de -128 a 127) |
int16 | Entero (de -32768 a 32767) |
int32 | Entero (de -2147483648 a 2147483647) |
int64 | Entero (de -9223372036854775808 a 9223372036854775807) |
uint8 | Entero sin signo (de 0 a 255) |
uint16 | Entero sin signo (de 0 a 65535) |
uint32 | Entero sin signo (de 0 a 4294967295) |
uint64 | Entero sin signo (de 0 a 18446744073709551615) |
float_ | Atajo para float64 . |
float16 | Half precision float: un bit para el signo, 5 bits para el exponente, 10 bits para la mantissa |
float32 | Single precision float: un bit para el signo, 8 bits para el exponente, 23 bits para la mantissa |
float64 | Double precision float: un bit para el signo, 11 bits para el exponente, 52 bits para la mantissa |
complex_ | Atajo para complex128 . |
complex64 | Número complejo, represantedo por dos floats de 32-bits |
complex128 | Número complejo, represantedo por dos floats de 64-bits |
Es posible tener una especificación de tipos más detallada, pudiendo especificar números con big endian o little endian. No vamos a ver esto en este momento.
El tipo por defecto que usa numpy
al crear un ndarray es np.float_
, siempre que no específiquemos explícitamente el tipo a usar.
Por ejemplo, un array de tipo np.uint8
puede tener los siguientes valores:
import itertools
for i, bits in enumerate(itertools.product((0, 1), repeat=8)):
print(i, bits)
Es decir, puede contener valores que van de 0 a 255 ($2^8$).
¿Cuántos bytes tendrá un ndarray
de 10 elementos cuyo tipo de datos es un np.int8
?
a = np.arange(10, dtype=np.int8)
print(a.nbytes)
print(sys.getsizeof(a))
a = np.repeat(1, 100000).astype(np.int8)
print(a.nbytes)
print(sys.getsizeof(a))
Creación de numpy arrays¶
Podemos crear numpy arrays de muchas formas.
- Rangos numéricos
np.arange
, np.linspace
, np.logspace
- Datos homogéneos
np.zeros
, np.ones
- Elementos diagonales
np.diag
, np.eye
- A partir de otras estructuras de datos ya creadas
np.array
- A partir de otros numpy arrays
np.empty_like
- A partir de ficheros
np.loadtxt
, np.genfromtxt
,...
- A partir de un escalar
np.full
, np.tile
,...
- A partir de valores aleatorios
np.random.randint
, np.random.randint
, np.random.randn
,...
...
a = np.arange(10) # similar a range pero devuelve un ndarray en lugar de un objeto range
print(a)
a = np.linspace(0, 1, 101)
print(a)
a_i = np.zeros((2, 3), dtype=np.int)
a_f = np.zeros((2, 3))
print(a_i)
print(a_f)
a = np.eye(3)
print(a)
a = np.array(
(
(1, 2, 3, 4, 5, 6),
(10, 20, 30, 40, 50, 60)
),
dtype=np.float
)
print(a)
np.full((5, 5), -999)
np.random.randint(0, 50, 15)
Practicando
Recordad que siempre podéis usar help
, ?
, np.lookfor
,..., para obtener más información.
help(np.sum)
np.rad2deg?
np.lookfor("create array")
Ved un poco como funciona np.repeat
, np.empty_like
,...
# Play area
%load ../../solutions/03_01_np_array_creacion.py
Operaciones disponibles más típicas¶
a = np.random.rand(5, 2)
print(a)
a.sum()
a.sum(axis=0)
a.sum(axis=1)
a.ravel()
a.reshape(2, 5)
a.T
a.transpose()
a.mean()
a.mean(axis=1)
a.cumsum(axis=1)
Referencias:
Practicando
Mirad más métodos de un ndarray
y toquetead. Si no entendéis algo, preguntad:
dir(a)
# Play area
%load ../../solutions/03_02_np_operaciones_tipicas.py
Metadatos y anatomía de un ndarray
¶
En realidad, un ndarray
es un bloque de memoria con información extra sobre como interpretar su contenido. La memoria dinámica (RAM) se puede considerar como un 'churro' lineal y es por ello que necesitamos esa información extra para saber como formar ese ndarray
, sobre todo la información de shape
y strides
.
Esta parte va a ser un poco más esotérica para los no iniciados pero considero que es necesaria para poder entender mejor nuestra nueva estructura de datos y poder sacarle mejor partido.
a = np.random.randn(5000, 5000)
El número de dimensiones del ndarray
a.ndim
El número de elementos en cada una de las dimensiones
a.shape
El número de elementos
a.size
El tipo de datos de los elementos
a.dtype
El número de bytes de cada elemento
a.itemsize
El número de bytes que ocupa el ndarray
(es lo mismo que size
por itemsize
)
a.nbytes
El buffer que contiene los elementos del ndarray
a.data
Pasos a dar en cada dimensión cuando nos movemos entre elementos
a.strides
(imagen extraída de GitHub).
Más cosas
a.flags
Pequeño ejercicio, ¿por qué tarda menos en sumar elementos en una dimensión que en otra si es un array regular?
%timeit a.sum(axis=0)
%timeit a.sum(axis=1)
Pequeño ejercicio, ¿por qué ahora el resultado es diferente?
aT = a.T
%timeit aT.sum(axis=0)
%timeit aT.sum(axis=1)
print(aT.strides)
print(aT.flags)
print(np.repeat((1,2,3), 3))
print()
a = np.repeat((1,2,3), 3).reshape(3, 3)
print(a)
print()
print(a.sum(axis=0))
print()
print(a.sum(axis=1))
Indexación¶
Si ya has trabajado con indexación en estructuras de Python, como listas, tuplas o strings, la indexación en Numpy te resultará muy familiar.
Por ejemplo, por hacer las cosas sencillas, vamos a crear un ndarray
de 1D:
a = np.arange(10, dtype=np.uint8)
print(a)
print(a[:]) # para acceder a todos los elementos
print(a[:-1]) # todos los elementos menos el último
print(a[1:]) # todos los elementos menos el primero
print(a[::2]) # el primer, el tercer, el quinto,..., elemento
print(a[3]) # el cuarto elemento
print(a[-1:-5:-1]) # ¿?
# Practicad vosotros
Para ndarrays de una dimensión es exactamente igual que si usásemos listas o tuplas de Python:
- Primer elemento tiene índice 0
- Los índices negativos empiezan a contar desde el final
- slices/rebanadas con
[start:stop:step]
Con un ndarray
de más dimensiones las cosas ya cambian con respecto a Python puro:
a = np.random.randn(10, 2)
print(a)
a[1] # ¿Qué nos dará esto?
a[1, 1] # Si queremos acceder a un elemento específico hay que dar su posición completa en el ndarray
a[::3, 1]
Si tenemos dimensiones mayores a 1 es parecido a las listas pero los índices se separan por comas para las nuevas dimensiones.
(imagen extraída de aquí)
a = np.arange(40).reshape(5, 8)
print(a)
a[2, -3]
Para obtener más de un elemento hacemos slicing para cada eje:
(imagen extraída de aquí)
a[:3, :5]
¿Cómo podemos conseguir los elementos señalados en esta imagen?
(imagen extraída de aquí)
a[x:x ,x:x]
¿Cómo podemos conseguir los elementos señalados en esta imagen?
(imagen extraída de aquí)
a[x:x ,x:x]
¿Cómo podemos conseguir los elementos señalados en esta imagen?
(imagen extraída de aquí)
a[x:x ,x:x]
¿Cómo podemos conseguir los elementos señalados en esta imagen?
(imagen extraída de aquí)
a[x:x ,x:x]
Fancy indexing
Con fancy indexing podemos hacer cosas tan variopintas como:
(imágenes extraídas de aquí)
Es decir, podemos indexar usando ndarray
s de booleanos ó usando listas de índices para extraer elementos concretos de una sola vez.
WARNING: En el momento que usamos fancy indexing nos devuelve un nuevo ndarray que no tiene porque conservar la estructura original.
Por ejemplo, en el siguiente caso no devuelve un ndarray de dos dimensiones porque la máscara no tiene porqué ser regular y, por tanto, devuelve solo los valores que cumplen el criterio en un vector (ndarray de una dimensión).
a = np.arange(10).reshape(2, 5)
print(a)
bool_indexes = (a % 2 == 0)
print(bool_indexes)
a[bool_indexes]
Sin embargo, sí que lo podríamos usar para modificar el ndarray original en base a un criterio y seguir manteniendo la misma forma.
a[bool_indexes] = 999
print(a)
Manejo de valores especiales¶
numpy
provee de varios valores especiales: np.nan
, np.Inf
, np.Infinity
, np.inf
, np.infty
,...
a = 1 / np.arange(10)
print(a)
a[0] == np.inf
a.max() # Esto no es lo que queremos
a.mean() # Esto no es lo que queremos
a[np.isfinite(a)].max()
a[-1] = np.nan
print(a)
a.mean()
np.isnan(a)
np.isfinite(a)
np.isinf(a) # podéis mirar también np.isneginf, np.isposinf
numpy
usa el estándar IEEE de números flotantes para aritmética (IEEE 754). Esto significa que Not a
Number no es equivalente a infinity. También, positive infinity no es equivalente a negative infinity. Pero infinity es equivalente a positive infinity.
1 < np.inf
1 < -np.inf
1 > -np.inf
1 == np.inf
1 < np.nan
1 > np.nan
1 == np.nan
Subarrays, vistas y copias¶
¡IMPORTANTE!
Vistas y copias: numpy
, por defecto, siempre devuelve vistas para evitar incrementos innecesarios de memoria. Este comportamiento difiere del de Python puro donde una rebanada (slicing) de una lista devuelve una copia. Si queremos una copia de un ndarray
debemos obtenerla de forma explícita:
a = np.arange(10)
b = a[2:5]
print(a)
print(b)
b[0] = 222
print(a)
print(b)
Este comportamiento por defecto es realmente muy útil, significa que, trabajando con grandes conjuntos de datos, podemos acceder y procesar piezas de estos conjuntos de datos sin necesidad de copiar el buffer de datos original.
A veces, es necesario crear una copia. Esto se puede realizar fácilmente usando el método copy
de los ndarrays. El ejemplo anterior usando una copia en lugar de una vista:
a = np.arange(10)
b = a[2:5].copy()
print(a)
print(b)
b[0] = 222
print(a)
print(b)
¿Cómo funcionan los ejes en un ndarray
?¶
Por ejemplo, cuando hacemos a.sum()
, a.sum(axis=0)
, a.sum(axis=1)
.
¿Qué pasa si tenemos más de dos dimensiones?
Vamos a ver ejemplos:
a = np.arange(10).reshape(5,2)
a.shape
a.sum()
a.sum(axis=0)
a.sum(axis=1)
(imagen extraída de aquí)
a = np.arange(9).reshape(3, 3)
print(a)
print(a.sum(axis=0))
print(a.sum(axis=1))
(imagen extraída de aquí)
a = np.arange(24).reshape(2, 3, 4)
print(a)
print(a.sum(axis=0))
print(a.sum(axis=1))
print(a.sum(axis=2))
Por ejemplo, en el primer caso, axis=0
, lo que sucede es que cogemos todos los elementos del primer índice y aplicamos la operación para cada uno de los elementos de los otros dos ejes. Hecho de uno en uno sería lo siguiente:
print(a[:,0,0].sum(), a[:,0,1].sum(), a[:,0,2].sum(), a[:,0,3].sum())
print(a[:,1,0].sum(), a[:,1,1].sum(), a[:,1,2].sum(), a[:,1,3].sum())
print(a[:,2,0].sum(), a[:,2,1].sum(), a[:,2,2].sum(), a[:,2,3].sum())
Sin contar el eje que estamos usando, las dimensiones que quedan son 3 x 4 (segunda y tercera dimensiones) por lo que el resultado son 12 elementos.
Para el caso de axis=1
:
print(a[0,:,0].sum(), a[0,:,1].sum(), a[0,:,2].sum(), a[0,:,3].sum())
print(a[1,:,0].sum(), a[1,:,1].sum(), a[1,:,2].sum(), a[1,:,3].sum())
Sin contar el eje que estamos usando, las dimensiones que quedan son 2 x 4 (primera y tercera dimensiones) por lo que el resultado son 8 elementos.
Para el caso de axis=2
:
print(a[0,0,:].sum(), a[0,1,:].sum(), a[0,2,:].sum())
print(a[1,0,:].sum(), a[1,1,:].sum(), a[1,2,:].sum())
Sin contar el eje que estamos usando, las dimensiones que quedan son 2 x 3 (primera y segunda dimensiones) por lo que el resultado son 3 elementos.
Reformateo de ndarray
s¶
Podemos cambiar la forma de los ndarray
s usando el método reshape
. Por ejemplo, si queremos colocar los números del 1 al 9 en un grid $3 \times 3$ lo podemos hacer de la siguiente forma:
a = np.arange(1, 10).reshape(3, 3)
Para que el cambio de forma no dé errores hemos de tener cuidado en que los tamaños del ndarray
inicial y del ndarray
final sean compatibles.
# Por ejemplo, lo siguiente dará error?
a = np.arange(1, 10). reshape(5, 2)
Otro patrón común de cambio de forma sería la conversion de un ndarray
de 1D en uno de 2D añadiendo un nuevo eje. Lo podemos hacer usando, nuevamente, el método reshape
o usando numpy.newaxis
.
# Por ejemplo un array 2D de una fila
a = np.arange(3)
a1_2D = a.reshape(1,3)
a2_2D = a[np.newaxis, :]
print(a1_2D)
print(a1_2D.shape)
print(a2_2D)
print(a2_2D.shape)
# Por ejemplo un array 2D de una columna
a = np.arange(3)
a1_2D = a.reshape(3,1)
a2_2D = a[:, np.newaxis]
print(a1_2D)
print(a1_2D.shape)
print(a2_2D)
print(a2_2D.shape)
Broadcasting¶
Es poible realizar operaciones en ndarrays de diferentes tamaños. En algunos casos numpy
puede transformar estos ndarrays automáticamente de forma que todos tienen la misma forma. Esta conversión automática se llama broadcasting.
Normas del Broadcasting
Para determinar la interacción entre dos ndarray
s en Numpy se sigue un conjunto de reglas estrictas:
- Regla 1: Si dos
ndarray
s difieren en su número de dimensiones la forma de aquel con menos dimensiones se rellena con 1's a su derecha. - Regla 2: Si la forma de dos
ndarray
s no es la misma en ninguna de sus dimensiones, elndarry
con forma igual a 1 en esa dimensión se 'alarga' para tener simulares dimensiones que los del otrosndarray
. - Regla 3: Si en cualquier dimensión el tamaño no es igual y ninguno de ellos es igual a 1 entonces obtendremos un error.
Resumiendo, cuando se opera en dos ndarrays, numpy
compara sus formas (shapes) elemento a elemento. Empieza por las dimensiones más a la izquierda y trabaja hacia las siguientes dimensiones. Dos dimensiones son compatibles cuando
ambas son iguales o
una de ellas es 1
Si estas condiciones no se cumplen se lanzará una excepción ValueError: frames are not aligned
indicando que los ndarrays tienen formas incompatibles. El tamaño del ndarray resultante es el tamaño máximo a lo largo de cada dimensión de los ndarrays de partida.
De forma más gráfica:
(imagen extraída de aquí)
a: 4 x 3 a: 4 x 3 a: 4 x 1
b: 4 x 3 b: 3 b: 3
result: 4 x 3 result: 4 x 3 result: 4 x 3
Intentemos reproducir los esquemas de la imagen anterior.
a = np.repeat((0, 10, 20, 30), 3).reshape(4, 3)
b = np.repeat((0, 1, 2), 4).reshape(3,4).T
print(a)
print(b)
print(a + b)
a = np.repeat((0, 10, 20, 30), 3).reshape(4, 3)
b = np.array((0, 1, 2))
print(a)
print(b)
print(a + b)
a = np.array((0, 10, 20, 30)).reshape(4,1)
b = np.array((0, 1, 2))
print(a)
print(b)
print(a + b)
ndarrays
estructurados y recarray
s¶
Antes hemos comentado que los ndarray
s deben ser homogéneos pero era un poco inexacto, en realidad, podemos tener ndarray
s que tengan diferentes tipos. Estos se llaman ndarray
s estructurados y recarray
s.
Veamos ejemplos:
nombre = ['paca', 'pancracio', 'nemesia', 'eulogio']
edad = [72, 68, 86, 91]
a = np.array(np.zeros(4), dtype=[('name', '<S10'), ('age', np.int)])
a['name'] = nombre
a['age'] = edad
print(a)
Podemos acceder a las columnas por nombre
a['name']
A todos los elementos menos el primero
a['age'][1:]
Un recarray
es similar pero podemos acceder a los campos con notación de punto (dot notation).
ra = a.view(np.recarray)
ra.name
Esto introduce un poco de overhead para acceder ya que se realizan algunas operaciones de más.
Concatenación y partición de ndarrays
¶
Podemos combinar múltiples ndarrays en uno o separar uno en varios.
Para concatenar podemos usar np.concatenate
, np.hstack
, np.vstack
, np.dstack
. Ejemplos:
a = np.array([1, 1, 1, 1])
b = np.array([2, 2, 2, 2])
Podemos concatenar esos dos arrays usando np.concatenate
:
np.concatenate([a, b])
No solo podemos concatenar ndarrays de una sola dimensión:
np.concatenate([a.reshape(2, 2), b.reshape(2, 2)])
Podemos elegir sobre qué eje concatenamos:
np.concatenate([a.reshape(2, 2), b.reshape(2, 2)], axis=1)
Podemos concatenar más de dos arrays:
c = [3, 3, 3, 3]
np.concatenate([a, b, c])
Si queremos ser más explícitos podemos usar np.hstack
o np.vstack
. La h
y la v
son para horizontal y vertical, respectivamente.
np.hstack([a, b])
np.vstack([a, b])
Podemos concatenar en la tercera dimensión usamos np.dstack
.
De la misma forma que podemos concatenar, podemos partir ndarrays usando np.split
, np.hsplit
, np.vsplit
, np.dsplit
.
# Intentamos entender como funciona la partición probando...
Funciones matemáticas, funciones universales ufuncs y vectorización¶
¿Qué es eso de ufunc?
De la documentación oficial de Numpy:
A universal function (or ufunc for short) is a function that operates on ndarrays in an element-by-element fashion, supporting array broadcasting, type casting, and several other standard features. That is, a ufunc is a “vectorized” wrapper for a function that takes a fixed number of scalar inputs and produces a fixed number of scalar outputs.
Una ufunc es una Universal function o función universal que actúa sobre todos los elementos de un ndarray
, es decir aplica la funcionalidad sobre cada uno de los elementos del ndarray
. Esto se conoce como vectorización.
Por ejemplo, veamos la operación de elevar al cuadrado una lista en python puro o en numpy
:
# En Python puro
a_list = list(range(10000))
%timeit [i ** 2 for i in a_list]
# En numpy
an_arr = np.arange(10000)
%timeit np.power(an_arr, 2)
a = np.arange(10)
np.power(a, 2)
La función anterior eleva al cuadrado cada uno de los elementos del ndarray
anterior.
Dentro de numpy
hay muchísimas ufuncs y scipy
(no lo vamos a ver) dispone de muchas más ufuns mucho más especializadas.
En numpy
tenemos, por ejemplo:
- Funciones trigonométricas:
sin
,cos
,tan
,arcsin
,arccos
,arctan
,hypot
,arctan2
,degrees
,radians
,unwrap
,deg2rad
,rad2deg
# juguemos un poco con ellas
- Funciones hiperbólicas:
sinh
,cosh
,tanh
,arcsinh
,arccosh
,arctanh
# juguemos un poco con ellas
- Redondeo:
around
,round_
,rint
,fix
,floor
,ceil
,trunc
# juguemos un poco con ellas
- Sumas, productos, diferencias:
prod
,sum
,nansum
,cumprod
,cumsum
,diff
,ediff1d
,gradient
,cross
,trapz
# juguemos un poco con ellas
- Exponentes y logaritmos:
exp
,expm1
,exp2
,log
,log10
,log2
,log1p
,logaddexp
,logaddexp2
# juguemos un poco con ellas
- Otras funciones especiales:
i0
,sinc
# juguemos un poco con ellas
- Trabajo con decimales:
signbit
,copysign
,frexp
,ldexp
# juguemos un poco con ellas
- Operaciones aritméticas:
add
,reciprocal
,negative
,multiply
,divide
,power
,subtract
,true_divide
,floor_divide
,fmod
,mod
,modf
,remainder
# juguemos un poco con ellas
- Manejo de números complejos:
angle
,real
,imag
,conj
# juguemos un poco con ellas
- Miscelanea:
convolve
,clip
,sqrt
,square
,absolute
,fabs
,sign
,maximum
,minimum
,fmax
,fmin
,nan_to_num
,real_if_close
,interp
...
# juguemos un poco con ellas
Referencias:
Estadística¶
- Orden:
amin
,amax
,nanmin
,nanmax
,ptp
,percentile
,nanpercentile
- Medias y varianzas:
median
,average
,mean
,std
,var
,nanmedian
,nanmean
,nanstd
,nanvar
- Correlacionando:
corrcoef
,correlate
,cov
- Histogramas:
histogram
,histogram2d
,histogramdd
,bincount
,digitize
...
# juguemos un poco con ellas
Ordenando, buscando y contando¶
- Ordenando:
sort
,lexsort
,argsort
,ndarray.sort
,msort
,sort_complex
,partition
,argpartition
- Buscando:
argmax
,nanargmax
,argmin
,nanargmin
,argwhere
,nonzero
,flatnonzero
,where
,searchsorted
,extract
- Contando:
count_nonzero
...
# juguemos un poco con ellas
Polinomios¶
- Series de potencias:
numpy.polynomial.polynomial
- Clase Polynomial:
np.polynomial.Polynomial
- Básicos:
polyval
,polyval2d
,polyval3d
,polygrid2d
,polygrid3d
,polyroots
,polyfromroots
- Ajuste:
polyfit
,polyvander
,polyvander2d
,polyvander3d
- Cálculo:
polyder
,polyint
- Álgebra:
polyadd
,polysub
,polymul
,polymulx
,polydiv
,polypow
- Miscelánea:
polycompanion
,polydomain
,polyzero
,polyone
,polyx
,polytrim
,polyline
- Otras funciones polinómicas:
Chebyshev
,Legendre
,Laguerre
,Hermite
...
# juguemos un poco con ellas
Álgebra lineal¶
Lo siguiente que se encuentra dentro de numpy.linalg
vendrá precedido por LA
.
- Productos para vectores y matrices:
dot
,vdot
,inner
,outer
,matmul
,tensordot
,einsum
,LA.matrix_power
,kron
- Descomposiciones:
LA.cholesky
,LA.qr
,LA.svd
- Eigenvalores:
LA.eig
,LA.eigh
,LA.eigvals
,LA.eigvalsh
- Normas y otros números:
LA.norm
,LA.cond
,LA.det
,LA.matrix_rank
,LA.slogdet
,trace
- Resolución de ecuaciones e inversión de matrices:
LA.solve
,LA.tensorsolve
,LA.lstsq
,LA.inv
,LA.pinv
,LA.tensorinv
Dentro de scipy
tenemos más cosas relacionadas.
# juguemos un poco con ellas
Manipulación de ndarrays
¶
tile
, hstack
, vstack
, dstack
, hsplit
, vsplit
, dsplit
, repeat
, reshape
, ravel
, resize
,...
# juguemos un poco con ellas
Módulos de interés dentro de numpy
¶
Dentro de numpy
podemos encontrar módulos para:
- Usar números aleatorios:
np.random
- Usar FFT:
np.fft
- Usar masked arrays:
np.ma
- Usar polinomios:
np.polynomial
- Usar álgebra lineal:
np.linalg
- Usar matrices:
np.matlib
- ...
Toda esta funcionalidad se puede ampliar y mejorar usando scipy
.
Cálculo matricial¶
a1 = np.repeat(2, 9).reshape(3, 3)
a2 = np.tile(2, (3, 3))
a3 = np.ones((3, 3), dtype=np.int) * 2
print(a1)
print(a2)
print(a3)
b = np.arange(1,4)
print(b)
print(a1.dot(b))
print(np.dot(a2, b))
print(a3 @ b) # only python version >= 3.5
Lo anterior lo hemos hecho usando ndarrays pero numpy
también ofrece una estructura de datos matrix
.
a_mat = np.matrix(a1)
a_mat
b_mat = np.matrix(b)
a_mat @ b_mat
a_mat @ b_mat.T
Como vemos, con los ndarrays no hace falta que seamos rigurosos con las dimensiones, en cambio, si usamos np.matrix
como tipos hemos de realizar operaciones matriciales válidas (por ejemplo, que las dimensiones sean correctas).
A efectos prácticos, en general, los ndarrays se pueden usar como matrix
conociendo estas pequeñas cosas.
Tambien te recormendamos, visitar los siguientes articulos sobre portfolios en python: este y este