Saltar al contenido

Indice / NumPy / 3.1

El array, y por qué no una lista

Un ndarray es una lista con dos promesas: todos los elementos son del mismo tipo, y la forma se conoce de antemano. Todo lo que NumPy hace rápido sale de esas dos promesas.

Para qué sirve

NumPy es la base sobre la que está construido pandas: una columna de un DataFrame es un array. Aprenderlo primero significa que en la unidad 4 la mitad de las cosas ya se van a entender - el dtype, las máscaras booleanas, el NaN que contamina.

Y para el trabajo de esta materia, NumPy resuelve directamente lo que se hace sobre una grilla de números: estadística por eje, normalización, detección de atípicos.

ELI5Explicado facil

Una lista de Python es un cajón con etiquetas: cada elemento vive donde quiera y la lista guarda una anotación de dónde está. Por eso puede mezclar números, texto y otras listas.

Un array es una fila de casilleros iguales, todos del mismo tamaño y uno pegado al otro. No admite mezclas, y a cambio la computadora puede recorrerlos sin ir a buscar nada.

Los cinco atributos

EjemploEl array: mismo tipo, forma conocida
import numpy as np

notas = np.array([9.7, 4.7, 8.6, 6.0, 3.8])
print("notas       :", notas)
print("  dtype     :", notas.dtype, "  <- el tipo de TODOS sus elementos")
print("  shape     :", notas.shape, "  <- 5 elementos en 1 dimension")
print("  ndim      :", notas.ndim)
print("  size      :", notas.size)
print("  itemsize  :", notas.itemsize, "bytes por elemento")
print("  nbytes    :", notas.nbytes, "bytes en total")

print()
grilla = np.array([[9.7, 4.7, 8.6],
                   [6.0, 3.8, 4.2],
                   [8.1, 3.6, 3.8]])
print("una grilla de 3x3:")
print(grilla)
print("  shape:", grilla.shape, "| ndim:", grilla.ndim, "| size:", grilla.size)
print("  shape es (filas, columnas), y ese orden no cambia nunca.")

print()
print("La homogeneidad no es opcional: si le das tipos mezclados, NumPy")
print("elige uno solo para todos y convierte. Se llama upcasting.")
for lista in ([1, 2, 3], [1, 2.5, 3], [1, 2, "tres"], [True, False, 1]):
    a = np.array(lista)
    print(f"   {str(lista):<22} -> dtype {str(a.dtype):<8} {a}")

print()
print("El tercero es el peligroso: un solo texto convirtio los numeros en")
print("texto. El dtype <U21 significa 'cadena de hasta 21 caracteres'.")
mezclado = np.array([1, 2, "tres"])
print("   dtype:", mezclado.dtype, "| el 1 ahora es", repr(mezclado[0]))
try:
    mezclado.sum()
except TypeError as error:
    print("   .sum() ->", type(error).__name__)
print("   NumPy 2 se niega a sumar cadenas, y eso es una suerte: avisa.")
print("   pandas, sobre la misma columna, las CONCATENA sin quejarse.")
print("   Los dos casos vienen del mismo origen: un solo valor de texto")
print("   en una columna numerica, que es el defecto mas comun del corpus.")

print()
print("Y el dtype se puede pedir explicitamente, que es lo que conviene")
print("cuando ya sabes que estas leyendo:")
enteros = np.array([1, 2, 3], dtype=np.int64)
flotantes = np.array([1, 2, 3], dtype=np.float64)
print("   dtype=int64   ->", enteros, enteros.dtype)
print("   dtype=float64 ->", flotantes, flotantes.dtype)
notas       : [9.7 4.7 8.6 6.  3.8]
  dtype     : float64   <- el tipo de TODOS sus elementos
  shape     : (5,)   <- 5 elementos en 1 dimension
  ndim      : 1
  size      : 5
  itemsize  : 8 bytes por elemento
  nbytes    : 40 bytes en total

una grilla de 3x3:
[[9.7 4.7 8.6]
 [6.  3.8 4.2]
 [8.1 3.6 3.8]]
  shape: (3, 3) | ndim: 2 | size: 9
  shape es (filas, columnas), y ese orden no cambia nunca.

La homogeneidad no es opcional: si le das tipos mezclados, NumPy
elige uno solo para todos y convierte. Se llama upcasting.
   [1, 2, 3]              -> dtype int64    [1 2 3]
   [1, 2.5, 3]            -> dtype float64  [1.  2.5 3. ]
   [1, 2, 'tres']         -> dtype <U21     ['1' '2' 'tres']
   [True, False, 1]       -> dtype int64    [1 0 1]

El tercero es el peligroso: un solo texto convirtio los numeros en
texto. El dtype <U21 significa 'cadena de hasta 21 caracteres'.
   dtype: <U21 | el 1 ahora es np.str_('1')
   .sum() -> TypeError
   NumPy 2 se niega a sumar cadenas, y eso es una suerte: avisa.
   pandas, sobre la misma columna, las CONCATENA sin quejarse.
   Los dos casos vienen del mismo origen: un solo valor de texto
   en una columna numerica, que es el defecto mas comun del corpus.

Y el dtype se puede pedir explicitamente, que es lo que conviene
cuando ya sabes que estas leyendo:
   dtype=int64   -> [1 2 3] int64
   dtype=float64 -> [1. 2. 3.] float64
Lo que se le pregunta a un array antes de hacer nada
AtributoQué diceEjemplo
dtypeEl tipo de todos sus elementosfloat64, int64, <U21
shapeLa forma, como tupla (filas, columnas)(5,), (10, 24)
ndimCuántas dimensiones1, 2
sizeCuántos elementos en total240 para (10, 24)
nbytesCuánta memoria ocupasize × itemsize
Un solo texto convierte todo el array en texto

np.array([1, 2, "tres"]) tiene dtype <U21: «cadena de hasta 21 caracteres». El 1 pasó a ser '1'. Se llama upcasting, y es silencioso.

Después, .sum() sobre ese array levanta TypeError - NumPy 2 se niega a sumar cadenas, y eso es una suerte porque avisa. pandas, sobre la misma columna, las concatena y devuelve '12tres'. Los dos casos vienen del mismo origen: un valor de texto en una columna numérica, que es el defecto más común del corpus.

Contra una lista

EjemploPor que un array y no una lista
import sys
import timeit

import numpy as np

N = 100_000
lista = list(range(N))
arreglo = np.arange(N, dtype=np.int64)

print("MEMORIA, para", f"{N:,}".replace(",", ".") , "enteros:")
print("   lista de Python:", sys.getsizeof(lista), "bytes solo los punteros")
print("   ...mas cada objeto int, que pesa", sys.getsizeof(0), "bytes aparte")
print("   array de NumPy :", arreglo.nbytes, "bytes, y eso es todo")
print("   El array guarda los numeros pegados en memoria; la lista guarda")
print("   direcciones a objetos que estan cada uno por su lado.")

print()
print("VELOCIDAD, sumando 1 a cada elemento:")
con_lista = timeit.timeit(lambda: [x + 1 for x in lista], number=20) / 20
con_array = timeit.timeit(lambda: arreglo + 1, number=20) / 20
# Los milisegundos exactos cambian en cada corrida y en cada maquina,
# asi que esta pagina publica la comparacion, no el cronometro.
print("   [x + 1 for x in lista] : el bucle de Python")
print("   arreglo + 1            : una sola operacion de NumPy")
print("   ¿el array tarda menos de la decima parte?",
      con_array * 10 < con_lista)
print("   En esta maquina la diferencia es de uno a dos ordenes de")
print("   magnitud, y crece con el tamano: el bucle paga por elemento,")
print("   el array no.")

print()
print("Y la diferencia que mas se nota al escribir codigo:")
print("   lista * 2   ->", [1, 2, 3] * 2, "  <- repite la lista")
print("   array * 2   ->", np.array([1, 2, 3]) * 2, "      <- duplica cada valor")
print("   lista + 1   -> TypeError")
try:
    [1, 2, 3] + 1
except TypeError as error:
    print("     ", error)
print("   array + 1   ->", np.array([1, 2, 3]) + 1)

print()
print("Esa es la idea entera de NumPy: la operacion se escribe una vez y se")
print("aplica a todos los elementos. Se llama vectorizacion, y es la")
print("leccion 3.2.")

print()
print("Lo que se pierde a cambio:")
print("   - el array tiene UN tipo: no se pueden mezclar numeros y texto")
print("   - el tamano es fijo: no hay append barato")
print("   - se puede indexar y rebanar, pero no insertar en el medio")
print("Para una tabla de datos, esas tres perdidas no molestan nunca.")
MEMORIA, para 100.000 enteros:
   lista de Python: 800056 bytes solo los punteros
   ...mas cada objeto int, que pesa 28 bytes aparte
   array de NumPy : 800000 bytes, y eso es todo
   El array guarda los numeros pegados en memoria; la lista guarda
   direcciones a objetos que estan cada uno por su lado.

VELOCIDAD, sumando 1 a cada elemento:
   [x + 1 for x in lista] : el bucle de Python
   arreglo + 1            : una sola operacion de NumPy
   ¿el array tarda menos de la decima parte? True
   En esta maquina la diferencia es de uno a dos ordenes de
   magnitud, y crece con el tamano: el bucle paga por elemento,
   el array no.

Y la diferencia que mas se nota al escribir codigo:
   lista * 2   -> [1, 2, 3, 1, 2, 3]   <- repite la lista
   array * 2   -> [2 4 6]       <- duplica cada valor
   lista + 1   -> TypeError
      can only concatenate list (not "int") to list
   array + 1   -> [2 3 4]

Esa es la idea entera de NumPy: la operacion se escribe una vez y se
aplica a todos los elementos. Se llama vectorizacion, y es la
leccion 3.2.

Lo que se pierde a cambio:
   - el array tiene UN tipo: no se pueden mezclar numeros y texto
   - el tamano es fijo: no hay append barato
   - se puede indexar y rebanar, pero no insertar en el medio
Para una tabla de datos, esas tres perdidas no molestan nunca.
listndarray
TiposCualquiera, mezcladosUno solo
Memoria (100.000 enteros)800 KB de punteros más 28 bytes por objeto800 KB y nada más
Sumar 1 a todosUn bucle, que paga por elementoUna operación, uno o dos órdenes de magnitud más rápida
x * 2Repite la listaDuplica cada valor
x + 1TypeErrorSuma 1 a cada valor
TamañoCrece con appendFijo

Las tres cosas que se pierden - mezclar tipos, crecer barato, insertar en el medio - no hacen falta nunca para una tabla de datos. Lo que se gana, arreglo + 1 en vez de un bucle, es la idea entera de NumPy y el tema de la lección 3.2.

Convertir

Ejemploastype: convertir tiene consecuencias
import warnings

import numpy as np

notas = np.array([9.7, 4.7, 8.6, 6.0, 3.8])

print("astype convierte y devuelve un array NUEVO:")
enteros = notas.astype(np.int64)
print("   original:", notas, notas.dtype)
print("   astype  :", enteros, enteros.dtype)
print("   TRUNCA, no redondea: 9.7 -> 9, no 10.")
print("   Si queres redondear: np.round(notas).astype(int) ->",
      np.round(notas).astype(np.int64))

print()
print("Y no toca el original:", notas)

print()
print("El caso que hay que conocer: un NaN convertido a entero.")
con_hueco = np.array([9.7, np.nan, 8.6])
print("   con_hueco:", con_hueco, con_hueco.dtype)
with warnings.catch_warnings(record=True) as avisos:
    warnings.simplefilter("always")
    convertido = con_hueco.astype(np.int64)
print("   .astype(int64) ->", convertido)
print("   avisos:", [a.category.__name__ for a in avisos])
print()
print("   NO levanta excepcion: emite un RuntimeWarning y devuelve")
print("   -9223372036854775808, que es el entero mas chico que existe.")
print("   Ese numero entra al calculo como si fuera un dato.")
print()
print("   pandas, sobre lo mismo, se niega:")
print("     IntCastingNaNError: Cannot convert non-finite values to integer")
print("   NaN es un float y solo existe en float. En pandas hay un tipo")
print("   entero que admite ausentes (Int64, con I mayuscula); en NumPy")
print("   puro no: o hay float, o no hay huecos.")

print()
print("El otro caso que muerde: el desborde, que NO siempre avisa.")
print("   np.int8 admite de -128 a 127.")
try:
    np.array([200], dtype=np.int8)
except OverflowError as error:
    print("   crear el array con 200 ->", type(error).__name__, "  <- avisa")
grandes = np.array([200, 300])
print("   pero .astype(np.int8) NO avisa:",
      grandes, "->", grandes.astype(np.int8))
al_borde = np.array([127], dtype=np.int8)
print("   y sumar tampoco:", al_borde, "+ 1 ->", al_borde + np.int8(1))
print("   Da la vuelta y sigue. Por eso los tipos chicos se usan solo")
print("   cuando la memoria importa de verdad y se sabe el rango.")

print()
print("Y el redondeo de NumPy no es el que se aprende en la escuela:")
medios = np.array([0.5, 1.5, 2.5, 3.5, 4.5])
print("   np.round([0.5 1.5 2.5 3.5 4.5]) ->", np.round(medios))
print("   Redondea al PAR mas cercano, no siempre hacia arriba. Es el")
print("   estandar IEEE 754 y lo mismo hace round() de Python.")
print("   round(2.5) de Python ->", round(2.5))
print("   Va a volver en la leccion 7.4, cuando dos archivos correctos")
print("   difieran en un centavo.")

print()
print("Resumen de lo que hay que mirar antes de un astype:")
print("   ¿hay NaN?            -> a entero, no se puede")
print("   ¿hay decimales?      -> a entero, se truncan")
print("   ¿el tipo entra?      -> int8 no aguanta 200")
print("   ¿es una copia?       -> si, siempre: astype no modifica")
astype convierte y devuelve un array NUEVO:
   original: [9.7 4.7 8.6 6.  3.8] float64
   astype  : [9 4 8 6 3] int64
   TRUNCA, no redondea: 9.7 -> 9, no 10.
   Si queres redondear: np.round(notas).astype(int) -> [10  5  9  6  4]

Y no toca el original: [9.7 4.7 8.6 6.  3.8]

El caso que hay que conocer: un NaN convertido a entero.
   con_hueco: [9.7 nan 8.6] float64
   .astype(int64) -> [                   9 -9223372036854775808                    8]
   avisos: ['RuntimeWarning']

   NO levanta excepcion: emite un RuntimeWarning y devuelve
   -9223372036854775808, que es el entero mas chico que existe.
   Ese numero entra al calculo como si fuera un dato.

   pandas, sobre lo mismo, se niega:
     IntCastingNaNError: Cannot convert non-finite values to integer
   NaN es un float y solo existe en float. En pandas hay un tipo
   entero que admite ausentes (Int64, con I mayuscula); en NumPy
   puro no: o hay float, o no hay huecos.

El otro caso que muerde: el desborde, que NO siempre avisa.
   np.int8 admite de -128 a 127.
   crear el array con 200 -> OverflowError   <- avisa
   pero .astype(np.int8) NO avisa: [200 300] -> [-56  44]
   y sumar tampoco: [127] + 1 -> [-128]
   Da la vuelta y sigue. Por eso los tipos chicos se usan solo
   cuando la memoria importa de verdad y se sabe el rango.

Y el redondeo de NumPy no es el que se aprende en la escuela:
   np.round([0.5 1.5 2.5 3.5 4.5]) -> [0. 2. 2. 4. 4.]
   Redondea al PAR mas cercano, no siempre hacia arriba. Es el
   estandar IEEE 754 y lo mismo hace round() de Python.
   round(2.5) de Python -> 2
   Va a volver en la leccion 7.4, cuando dos archivos correctos
   difieran en un centavo.

Resumen de lo que hay que mirar antes de un astype:
   ¿hay NaN?            -> a entero, no se puede
   ¿hay decimales?      -> a entero, se truncan
   ¿el tipo entra?      -> int8 no aguanta 200
   ¿es una copia?       -> si, siempre: astype no modifica
Cuatro cosas que mirar antes de un astype
PreguntaQué pasa si no
¿Hay decimales?A entero se truncan, no se redondean: 9.7 → 9
¿Hay NaN?No levanta excepción. Un RuntimeWarning y el valor queda en -9223372036854775808
¿El tipo entra?[200, 300].astype(int8) da [-56, 44], sin avisar
¿Modifica el original?No: astype siempre devuelve una copia
El NaN convertido a entero es el peor de los cuatro

NumPy devuelve -9223372036854775808 - el entero más chico que existe - y ese número entra al cálculo como si fuera un dato. pandas, sobre lo mismo, se niega con IntCastingNaNError.

La regla: NaN solo existe en float. Si hace falta una columna entera con huecos, pandas tiene Int64 (con I mayúscula); NumPy puro no tiene equivalente.

El redondeo al par

np.round([0.5, 1.5, 2.5, 3.5, 4.5]) da [0, 2, 2, 4, 4]: redondea al par más cercano, no siempre hacia arriba. Es el estándar IEEE 754, y round() de Python hace lo mismo (round(2.5) es 2).

Va a volver en la lección 7.4, cuando dos archivos correctos difieran en un centavo.

Crear y leer

EjemploCrear arrays, y leer la grilla de sensores
import numpy as np

print("Las cinco formas de crear un array sin escribir los valores:")
print("   np.zeros(5)      ->", np.zeros(5))
print("   np.ones(3)       ->", np.ones(3))
print("   np.full(4, -1)   ->", np.full(4, -1))
print("   np.arange(0, 10, 2) ->", np.arange(0, 10, 2))
print("   np.linspace(0, 1, 5) ->", np.linspace(0, 1, 5))
print()
print("arange va de a pasos y NO incluye el final, como range().")
print("linspace va por cantidad de puntos y SI incluye el final.")
print("   np.arange(0, 1, 0.25) ->", np.arange(0, 1, 0.25))
print("   np.linspace(0, 1, 5)  ->", np.linspace(0, 1, 5))

print()
print("Y en dos dimensiones se le pasa la forma como tupla:")
print("   np.zeros((2, 3)) ->")
print(np.zeros((2, 3)))

print()
print("=" * 62)
print("Ahora el archivo real: numpy_sensores.csv")
print("=" * 62)

# genfromtxt lee texto a un array. skip_header saltea la cabecera y
# usecols elige las 24 columnas horarias, que son las numericas.
lecturas = np.genfromtxt("numpy_sensores.csv", delimiter=",",
                         skip_header=1, usecols=range(2, 26))

with open("numpy_sensores.csv", encoding="utf-8") as f:
    cabecera = f.readline().strip().split(",")
sensores = np.genfromtxt("numpy_sensores.csv", delimiter=",", skip_header=1,
                         usecols=(0,), dtype=str)

print("shape :", lecturas.shape, " <- 10 sensores x 24 horas")
print("dtype :", lecturas.dtype)
print("size  :", lecturas.size, "lecturas")
print("horas :", cabecera[2], "...", cabecera[-1])
print("sensores:", sensores)

print()
print("primeras 4 horas de los 3 primeros sensores:")
print(lecturas[:3, :4])

print()
print("Las celdas vacias del archivo llegaron como NaN, sin pedirlo:")
huecos = np.isnan(lecturas)
print("   cuantos NaN:", int(huecos.sum()))
filas, columnas = np.nonzero(huecos)
for fila, columna in zip(filas, columnas):
    print(f"     {sensores[fila]}  {cabecera[columna + 2]}")

print()
print("Y ojo con el maximo:")
print("   lecturas.max()    ->", lecturas.max(), " <- un solo NaN contamina")
print("   np.nanmax()       ->", np.nanmax(lecturas))
posicion = np.nanargmax(lecturas)
print("   ...y ese maximo esta en", sensores[posicion // 24],
      cabecera[posicion % 24 + 2])
print()
print("El 999.9 sigue ahi porque es un numero perfectamente valido: ni")
print("genfromtxt ni el dtype lo detectan. Solo lo encuentra un chequeo")
print("de rango, que es codigo que hay que escribir (leccion 3.7).")
print("Y lo del NaN que arruina la reduccion es la leccion 3.6.")
Las cinco formas de crear un array sin escribir los valores:
   np.zeros(5)      -> [0. 0. 0. 0. 0.]
   np.ones(3)       -> [1. 1. 1.]
   np.full(4, -1)   -> [-1 -1 -1 -1]
   np.arange(0, 10, 2) -> [0 2 4 6 8]
   np.linspace(0, 1, 5) -> [0.   0.25 0.5  0.75 1.  ]

arange va de a pasos y NO incluye el final, como range().
linspace va por cantidad de puntos y SI incluye el final.
   np.arange(0, 1, 0.25) -> [0.   0.25 0.5  0.75]
   np.linspace(0, 1, 5)  -> [0.   0.25 0.5  0.75 1.  ]

Y en dos dimensiones se le pasa la forma como tupla:
   np.zeros((2, 3)) ->
[[0. 0. 0.]
 [0. 0. 0.]]

==============================================================
Ahora el archivo real: numpy_sensores.csv
==============================================================
shape : (10, 24)  <- 10 sensores x 24 horas
dtype : float64
size  : 240 lecturas
horas : h00 ... h23
sensores: ['S01' 'S02' 'S03' 'S04' 'S05' 'S06' 'S07' 'S08' 'S09' 'S10']

primeras 4 horas de los 3 primeros sensores:
[[20.8 19.8 19.2 19. ]
 [18.5 17.1 16.6 16.1]
 [17.8 16.6 16.6 15.5]]

Las celdas vacias del archivo llegaron como NaN, sin pedirlo:
   cuantos NaN: 4
     S03  h07
     S03  h08
     S06  h13
     S09  h21

Y ojo con el maximo:
   lecturas.max()    -> nan  <- un solo NaN contamina
   np.nanmax()       -> 999.9
   ...y ese maximo esta en S05 h11

El 999.9 sigue ahi porque es un numero perfectamente valido: ni
genfromtxt ni el dtype lo detectan. Solo lo encuentra un chequeo
de rango, que es codigo que hay que escribir (leccion 3.7).
Y lo del NaN que arruina la reduccion es la leccion 3.6.
Crear un array sin escribir los valores
ExpresiónDevuelveOjo con
np.zeros(5)Cinco ceros floatEl dtype por defecto es float64
np.zeros((2, 3))Una grilla 2×3La forma va como tupla
np.ones(3), np.full(4, -1)Rellenosfull toma el dtype del valor
np.arange(0, 10, 2)De a pasosNo incluye el final, como range
np.linspace(0, 1, 5)Por cantidad de puntosSí incluye el final
np.genfromtxt(ruta, delimiter=",")Un array desde un CSVLas celdas vacías llegan como NaN sin pedirlo

Sobre numpy_sensores.csv, genfromtxt con skip_header=1 y usecols=range(2, 26) devuelve una grilla (10, 24) de float64: 10 sensores por 24 horas. Las 4 celdas vacías del archivo llegan como NaN, y el 999.9 llega como 999.9, porque es un número perfectamente válido.

numpy_sensores.csv (1.4 kB)

Dónde se rompe

ValueError: could not convert string to float: 'S01'

Causa: genfromtxt intentó leer una columna de texto como número.

Arreglo: usecols= para elegir solo las numéricas, o dtype=str si querés el texto.

(sin error) el array quedó con dtype <U21

Causa: uno de los valores era texto y arrastró a todos.

Arreglo: print(a.dtype) apenas se crea. Si dice <U algo, hay un valor no numérico adentro.

TypeError: the resolved dtypes are not compatible with add.reduce

Causa: .sum() sobre un array de cadenas.

Arreglo: el problema no es la suma, es el dtype: hay que convertir antes, decidiendo qué hacer con lo que no convierte.

RuntimeWarning: invalid value encountered in cast

Causa: astype(int) sobre un array con NaN.

Arreglo: filtrar los NaN primero, o quedarse en float. Es un warning: si no lo mirás, el programa sigue con un número basura adentro.

ValueError: setting an array element with a sequence

Causa: las filas que le pasaste no tienen todas el mismo largo.

Arreglo: un array 2D es rectangular. Si las filas son irregulares, no es un array: es una lista de listas.

Recetario

El array
ExpresiónQué haceCuándo
np.array(lista)Crea el array e infiere el dtypeDesde datos que ya tenés
np.array(lista, dtype=np.float64)Fuerza el tipoCuando sabés lo que estás leyendo
a.shape / a.dtypeForma y tipoLo primero, siempre
a.astype(np.int64)Copia convertidaCon las cuatro precauciones de arriba
np.round(a).astype(int)Redondear y convertirCuando truncar no es lo que querés
np.arange(inicio, fin, paso)Secuencia por pasosÍndices, rangos regulares
np.linspace(inicio, fin, cuantos)Secuencia por cantidadCuando el final tiene que estar
np.zeros((f, c))Grilla de cerosReservar antes de llenar
np.genfromtxt(r, delimiter=",", skip_header=1)CSV → arrayGrillas numéricas
np.isnan(a)Máscara de ausentesAntes de cualquier cuenta
np.nonzero(mascara)Las posiciones que son TruePara saber dónde están

Ponete a prueba

1. ¿Qué devuelve a.shape para un array de 10 sensores × 24 horas?Nivel 1 · Recordar
2. ¿Qué diferencia hay entre [1, 2, 3] * 2 y np.array([1, 2, 3]) * 2?Nivel 1 · Recordar
3. np.array([1, 2, "tres"]). ¿Qué dtype tiene y cuánto vale su primer elemento?Nivel 2 · Predecir la salida
4. np.array([9.7, np.nan, 8.6]).astype(np.int64). ¿Qué pasa?Nivel 2 · Predecir la salida
5. Tenés que leer las 24 columnas horarias de numpy_sensores.csv, que además tiene sensor_id y ubicacion. Las cuatro corren: ¿cuál conviene?Nivel 3 · Elegir entre dos casi correctas
6. ¿Por qué un array de 100.000 enteros ocupa menos memoria que la lista equivalente?Nivel 3 · Elegir entre dos casi correctas
7. Este código promedia una columna de stock y devuelve un número negativo de dieciocho cifras. ¿Por qué?
datos = np.genfromtxt("stock.csv", delimiter=",", skip_header=1)
enteros = datos.astype(np.int64)
print(enteros.mean())
Nivel 4 · Encontrar el defecto