1.3.3. Arreglos más elaborados

1.3.3.1. Más tipos de datos

1.3.3.1.1. Conversión de tipos

El tipo “más grande” prevalece en las operaciones de tipo mixto

>>> np.array([1, 2, 3]) + 1.5
array([ 2.5, 3.5, 4.5])

La asignación no cambia el tipo!

>>> a = np.array([1, 2, 3])
>>> a.dtype
dtype('int32')
>>> a[0] = 1.9 # <-- número de punto flotante truncado a número entero
>>> a
array([1, 2, 3])

Conversión forzada

>>> a = np.array([1.7, 1.2, 1.6])
>>> b = a.astype(int) # <-- truncado a entero
>>> b
array([1, 1, 1])

Redondeo

>>> a = np.array([1.2, 1.5, 1.6, 2.5, 3.5, 4.5])
>>> b = np.around(a)
>>> b # sigue siendo de punto flotante
array([ 1., 2., 2., 2., 4., 4.])
>>> c = np.around(a).astype(int)
>>> c
array([1, 2, 2, 2, 4, 4])

1.3.3.1.2. Tamaños de tipos de datos

Enteros (con signo):

int8 8 bits
int16 16 bits
int32 32 bits (el mismo int en platforma 32-bit)
int64 64 bits (el mismo int en platforma 64-bit)
>>> np.array([1], dtype=int).dtype
dtype('int64')
>>> np.iinfo(np.int32).max, 2**31 - 1
(2147483647, 2147483647)
>>> np.iinfo(np.int64).max, 2**63 - 1
(9223372036854775807, 9223372036854775807L)

Enteros sin signo:

uint8 8 bits
uint16 16 bits
uint32 32 bits
uint64 64 bits
>>> np.iinfo(np.uint32).max, 2**32 - 1
(4294967295, 4294967295)
>>> np.iinfo(np.uint64).max, 2**64 - 1
(18446744073709551615L, 18446744073709551615L)

Números de punto flotante:

float16 16 bits
float32 32 bits
float64 64 bits (el mismo float)
float96 96 bits, dependiente de la plataforma (el mismo np.longdouble)
float128 128 bits, dependiente de la plataforma (el mismo np.longdouble)
>>> np.finfo(np.float32).eps
1.1920929e-07
>>> np.finfo(np.float64).eps
2.2204460492503131e-16
>>> np.float32(1e-8) + np.float32(1) == 1
True
>>> np.float64(1e-8) + np.float64(1) == 1
False

Números complejos de punto flotante:

complex64 dos números de punto flotante 32-bit
complex128 dos números de punto flotante 64-bit
complex192 dos números de punto flotante 96-bit, dependiente de la plataforma
complex256 dos números de punto flotante 128-bit, dependiente de la plataforma

Tipos de datos pequeños

Si usted no sabe si necesita los tipos de datos especiales, es probable que no los conozca.

Comparación sobre el uso de float32 en vez de float64:

  • La mitad del tamaño en la memoria y en el disco

  • La mitad del ancho de banda de memoria necesaria (puede ser un poco más rápido en algunas operaciones )

    In [1]: a = np.zeros((1e6,), dtype=np.float64)
    
    In [2]: b = np.zeros((1e6,), dtype=np.float32)
    In [3]: %timeit a*a
    1000 loops, best of 3: 1.78 ms per loop
    In [4]: %timeit b*b
    1000 loops, best of 3: 1.07 ms per loop
  • Pero: con grandes errores de redondeo — a veces en lugares sorprendentes (es decir, no lo utilice a menos que realmente lo necesite)

1.3.3.2. Tipos de datos estructurados

>>> muestras = np.zeros((6,), dtype=[('codigo_sensor', 'S4'),
... ('posicion', float), ('valor', float)])
>>> muestras.ndim
1
>>> muestras.shape
(6,)
>>> muestras.dtype.names
('codigo_sensor', 'posicion', 'valor')
>>> muestras[:] = [('ALFA', 1, 0.37), ('BETA', 1, 0.11), ('TAU', 1, 0.13),
... ('ALFA', 1.5, 0.37), ('ALFA', 3, 0.11), ('TAU', 1.2, 0.13)]
>>> muestras
array([('ALFA', 1.0, 0.37), ('BETA', 1.0, 0.11), ('TAU', 1.0, 0.13),
('ALFA', 1.5, 0.37), ('ALFA', 3.0, 0.11), ('TAU', 1.2, 0.13)],
dtype=[('codigo_sensor', 'S4'), ('posicion', '<f8'), ('valor', '<f8')])

Para acceder a los campos se debe indexar los nombres de los campos

>>> muestras['codigo_sensor']
array(['ALFA', 'BETA', 'TAU', 'ALFA', 'ALFA', 'TAU'],
dtype='|S4')
>>> muestras['valor']
array([ 0.37, 0.11, 0.13, 0.37, 0.11, 0.13])
>>> muestras[0]
('ALFA', 1.0, 0.37)
>>> muestras[0]['codigo_sensor']
>>> 'ALFA'
>>> muestras[0]['codigo_sensor'] = 'TAU'
>>> samples[0]
('TAU', 1.0, 0.37)

Campos múltiples a la vez

>>> muestras[['posicion', 'valor']]
array([(1.0, 0.37), (1.0, 0.11), (1.0, 0.13), (1.5, 0.37), (3.0, 0.11),
(1.2, 0.13)],
dtype=[('posicion', '<f8'), ('valor', '<f8')])

Usando indexado fancy, en la forma usual

>>> muestras[muestras['codigo_sensor'] == 'ALFA']
array([('ALFA', 1.5, 0.37), ('ALFA', 3.0, 0.11)],
dtype=[('codigo_sensor', 'S4'), ('posicion', '<f8'), ('valor', '<f8')])

Nota

Hay un montón de otras sintaxis para construir arreglos estructurados, consulte aquí y aquí.

1.3.3.3. maskedarray: tratando con datos que faltan

  • Para números de punto flotante se podría utilizar NaN , pero las máscaras funcionan para todos los tipos

    >>> x = np.ma.array([1, 2, 3, 4], mask=[0, 1, 0, 1])
    
    >>> x
    masked_array(data = [1 -- 3 --],
    mask = [False True False True],
    fill_value = 999999)
    >>> y = np.ma.array([1, 2, 3, 4], mask=[0, 1, 1, 1])
    >>> x + y
    masked_array(data = [2 -- -- --],
    mask = [False True True True],
    fill_value = 999999)
  • Versión enmascarada de funciones comunes

    >>> np.ma.sqrt([1, -1, 2, -2])
    
    masked_array(data = [1.0 -- 1.41421356237 --],
    mask = [False True False True],
    fill_value = 1e+20)

Nota

Hay otros útiles array siblings


Este tema esta fuera de los capítulos sobre Numpy, tomemos un momento para recordar las buenas prácticas de codificación, lo que realmente vale la pena a largo plazo:

Buenas prácticas

  • Nombres de variables explícitos (sin necesidad de un comentario que explique lo que está en la variable)
  • Estilo: espacio después de las comas, antes y despues de =, etc.

Un cierto número de reglas para escribir código hermoso (y más importante, el uso de las mismas convenciones para todos los demás!) están en Style Guide for Python Code y Docstring Conventions (manejo de cadenas de ayuda).

  • Excepto en algunos casos raros, los nombres de variables y comentarios en inglés.