Python · Capítulo 4 · Lección de conceptos · 6 min de lectura

Mirar una tabla antes de confiar en ella

Elegir una columna da una Series, elegir una lista de columnas da un DataFrame, y unas pocas comprobaciones de una línea te dicen qué contiene una tabla antes de calcular nada con ella.

La idea

Por los Fundamentos sabes que un DataFrame es una tabla de columnas con tipo. Este capítulo trata del hábito que lo acompaña: antes de calcular nada, mira. Unas pocas llamadas de una línea te dicen qué tamaño tiene la tabla, qué guarda cada columna, qué valores aparecen y con qué frecuencia. Saltárselas es como se calcula un total sobre la columna equivocada, o sobre una tabla con filas duplicadas, y luego se manda a un VP.

Figura 1

Un corchete da una Series, dos corchetes dan un DataFrame

orders
order_idstatuschannelshipping_fee
1001completedweb0.00
1002completedweb0.00
1003completedmarketplace4.99
1006cancelledmarketplace4.99
orders['status'] (una Series)
status
completed
completed
completed
cancelled
orders[['order_id', 'status']] (un DataFrame)
order_idstatus
1001completed
1002completed
1003completed
1006cancelled

Paso 1 de 3 Partimos de orders, un DataFrame con cuatro columnas. Casi nunca necesitas todas para una pregunta.

  1. Partimos de orders, un DataFrame con cuatro columnas. Casi nunca necesitas todas para una pregunta.
  2. orders['status'], un nombre entre corchetes simples, devuelve una Series: una columna de valores, con las etiquetas de fila pegadas. Tiene métodos para valores, como value_counts().
  3. orders[['order_id', 'status']], una lista dentro de los corchetes, devuelve un DataFrame con solo esas columnas, que sigue siendo una tabla. Los corchetes interiores son una lista de Python con nombres.

Lo primero es tener claro qué tienes en las manos. Un DataFrame es la tabla entera. Una Series es una columna de ella. Corchetes simples con un nombre dan una Series; corchetes dobles con una lista dan un DataFrame. Se imprimen casi igual, pero tienen métodos distintos, y confundirlos es el mensaje de error más común del capítulo.

Por qué existe

Las tablas reales nunca están tan limpias como te dijeron. Una columna documentada como "channel" tiene un valor que nadie mencionó. Una "fecha" está guardada como texto. Una clave que creías única se repite. Estos problemas no cuestan nada al principio y mucho cuando el informe ya salió. Las llamadas del primer vistazo tardan segundos, y su valor es que le haces las mismas preguntas a cada tabla nueva, en el mismo orden, sin tener que pensarlo.

Cómo funciona por dentro

El tamaño y los tipos

orders.shape da (12000, 7): primero filas, luego columnas. len(orders) da 12.000, el número de filas. orders.info() lista cada columna con su dtype y su cuenta de valores no vacíos, así que de un vistazo sabes qué columnas son números, cuáles son texto y cuáles tienen huecos. orders.head() muestra las primeras cinco filas, para que leas unos valores reales junto a los nombres de columna.

Contar los valores

Para una columna de categorías, value_counts() es la primera llamada. Devuelve una Series nueva: los valores distintos como índice y las cuentas como valores, de mayor a menor.

Figura 2

orders['status'].value_counts(): cómo terminaron los 12.000 pedidos

0 3,000 6,000 9,000 12,000 Pedidos completed: 10,849cancelled: 726refunded: 304pending: 121 completedcancelledrefundedpending

Paso 1 de 3 value_counts() cuenta cuántas veces aparece cada valor distinto, de mayor a menor. De 12.000 pedidos, 10.849 son completed: unos 9 de cada 10.

  1. value_counts() cuenta cuántas veces aparece cada valor distinto, de mayor a menor. De 12.000 pedidos, 10.849 son completed: unos 9 de cada 10.
  2. El resto es una cola corta. 726 cancelados y 304 reembolsados son los dos que investigarías; 121 siguen pendientes.
  3. Cuatro valores, cuatro barras, y las cuentas suman 12.000. Un quinto valor, un error como Completed, aparecería aquí como su propia barra. Para eso sirve un primer vistazo.

Con normalize=True da fracciones en vez de cuentas, que es como se dice "el 90% de los pedidos se completó". Para una columna numérica, describe() da la cuenta, la media, la desviación estándar, el mínimo, los cuartiles y el máximo en una tabla. Léela como prueba de cordura: un precio mínimo de 0, o un máximo 100 veces la mediana, merece una mirada antes de confiar en cualquier promedio.

Contar filas frente a contar cosas

Tres llamadas se parecen y responden preguntas distintas. len(df) es el número de filas. series.count() es el número de filas con valor, así que se salta las vacías. series.nunique() es el número de valores diferentes.

Figura 3

count() y nunique() responden preguntas distintas

orders['customer_id'], pedidos 1001 a 1010
order_idcustomer_id
1001125
1002156
100322
100448
100582
1006157
100721
1008157
100938
1010111
Respuestas
callresult
len(orders)10
customer_id.count()10
customer_id.nunique()9

Paso 1 de 3 Diez pedidos. Cada uno tiene un customer_id, pero los clientes pueden pedir más de una vez.

  1. Diez pedidos. Cada uno tiene un customer_id, pero los clientes pueden pedir más de una vez.
  2. count() cuenta los valores que no faltan. Los diez pedidos tienen cliente, así que son 10, igual que len.
  3. El cliente 157 hizo los pedidos 1006 y 1008. nunique() cuenta valores distintos, así que el 157 cuenta una vez, y la respuesta es 9 clientes.

La segunda y la tercera son donde se esconden las respuestas equivocadas. "¿Cuántos clientes pidieron?" es nunique() sobre customer_id; el número de filas contaría cada compra repetida. "¿A cuántos pedidos les falta cupón?" es isna().sum() sobre coupon_code, y count() es su opuesto. unique() da los valores distintos en sí, no solo cuántos, que es la forma más rápida de detectar web y Web en la misma columna.

Cargar un archivo

pd.read_csv lee un archivo en un DataFrame y adivina el dtype de cada columna según lo que ve. Las fechas llegan como texto salvo que digas parse_dates=['order_date']. Una columna numérica con un "n/a" suelto llega como texto. Por eso, después de cargar, dtypes es la primera comprobación, antes de cualquier aritmética. NumPy, sobre el que se construye pandas, aporta los arreglos bajo las columnas, por eso np.mean, cumsum y la aritmética elemento a elemento funcionan directamente sobre ellas.

Una rutina que puedes repetir

Ejecuta la misma rutina corta en cada tabla nueva. Primero shape y head(): ¿cuánto hay y cómo es una fila? Luego info(): ¿los tipos son correctos y dónde están los huecos? Después value_counts() en cada columna de categorías y describe() en las numéricas: ¿los valores y los rangos tienen sentido? Por último, nunique() en cada columna que debería ser una clave: ¿de verdad hay una por fila? Anota todo lo raro, como un estado que no esperabas, un precio de 0 o una fecha guardada como texto. Cinco minutos aquí suelen ahorrar una hora de depuración confusa después, y te dan una lista corta de preguntas para quien sea dueño de los datos.

Errores comunes

"Una Series y un DataFrame con una columna son lo mismo"

Se ven igual al imprimirse y se comportan distinto. Una Series no tiene columnas de las que elegir, así que s[['a', 'b']] significa otra cosa; un DataFrame de una columna sigue siendo una tabla con la que puedes seguir trabajando. Si un método se queja de "no attribute", comprueba cuál de los dos tienes con type(x), y usa corchetes dobles siempre que quieras conservar una tabla.

"count() me dice cuántas filas hay"

count() cuenta los valores presentes. En una columna con datos que faltan es menor que el número de filas, y la diferencia es exactamente el número de vacíos. Para "cuántas filas" usa len o shape[0]. Para "cuántas diferentes" usa nunique(). Elegir mal da un número plausible, lo cual es peor que un error.

"Si los datos cargaron sin errores, están bien"

pandas cargará sin quejarse una columna de fechas como texto, un precio con signo de moneda como texto y una clave con duplicados. Nada de eso lanza un error. Una carga limpia significa que el archivo se pudo leer, no que los datos sean correctos. El primer vistazo es cómo descubres la diferencia.

Ponte a prueba

Una pregunta. Al responderla, la lección queda marcada en el mapa; nunca bloquea un nivel.

orders['customer_id'].nunique() devuelve 1.771 y len(orders) devuelve 12.000. ¿Qué dicen estos números?

Elige qué puede ejecutarse en este sitio. Puedes cambiarlo cuando quieras desde "Configurar cookies", al pie de cada página.