Comenzaremos por importar los datos para trabajar en esta clase
# Importamos la librería necesaria
import pandas as pd
cadena = "https://assets.datacamp.com/production/repositories/4412/datasets/61bb27bf939aac4344d4f446ce6da1d1bf534174/vt_tax_data_2016.csv"
# guardamos los datos en un dataframe
data = pd.read_csv(cadena)
podemos ver cuántas filas y cuántas columnas tiene la tabla que importamos en el dataframe data
# num de filas y columnas
print(data.shape)
# primeras 5 filas del dataframe
print(data.head(5))
(1476, 147) STATEFIPS STATE zipcode agi_stub N1 mars1 MARS2 MARS4 PREP \ 0 50 VT 0 1 111580 85090 14170 10740 45360 1 50 VT 0 2 82760 51960 18820 11310 35600 2 50 VT 0 3 46270 19540 22650 3620 24140 3 50 VT 0 4 30070 5830 22190 960 16060 4 50 VT 0 5 39530 3900 33800 590 22500 N2 ... N10300 A10300 N85530 A85530 N85300 A85300 N11901 \ 0 130630 ... 53660 50699 0 0 0 0 10820 1 132950 ... 74340 221146 0 0 0 0 12820 2 91870 ... 44860 266097 0 0 0 0 10810 3 71610 ... 29580 264678 0 0 0 0 7320 4 103710 ... 39170 731963 40 24 0 0 12500 A11901 N11902 A11902 0 9734 88260 138337 1 20029 68760 151729 2 24499 34600 90583 3 21573 21300 67045 4 67761 23320 103034 [5 rows x 147 columns]
con lo cual el dataframe tiene 1476 filas y 147 columnas. A veces, toda la información que importemos no es necesaria para nuestro fin, de tal manera podemos limitarla. Por ejemplo, podemos sólo importar la información de las primeras 5 columnas utilizando usecols
dentro de la importación
# opción 1: colocando el número de columna
data = pd.read_csv(cadena, usecols=[0,1,2,3,4])
# opción 2: colocando el nombre de las columnas
# nanme_cols = ["STATEFIPS","zipcode", "agi_stub", "N1", "mars1"]
# data = pd.read_csv(cadena, usecols=[])
# veamos que
data.head(5)
STATEFIPS | STATE | zipcode | agi_stub | N1 | |
---|---|---|---|---|---|
0 | 50 | VT | 0 | 1 | 111580 |
1 | 50 | VT | 0 | 2 | 82760 |
2 | 50 | VT | 0 | 3 | 46270 |
3 | 50 | VT | 0 | 4 | 30070 |
4 | 50 | VT | 0 | 5 | 39530 |
De tal manera limitamos la información por columnas. También podemos limitar la información por filas, por ejemplo, sólo considerar las primeras 500 filas
data = pd.read_csv(cadena, nrows=500)
o considerar 476 filas a partir de la fila 1000 (es decir que se estarían cosniderando las filas del 1001 al 1476):
data2 = pd.read_csv(cadena, usecols=[0,1,2,3,4], nrows=476, skiprows=1000)
# veamos
data2
50 | VT | 05730 | 4 | 20 | |
---|---|---|---|---|---|
0 | 50 | VT | 5730 | 5 | 30 |
1 | 50 | VT | 5730 | 6 | 0 |
2 | 50 | VT | 5732 | 1 | 200 |
3 | 50 | VT | 5732 | 2 | 120 |
4 | 50 | VT | 5732 | 3 | 70 |
... | ... | ... | ... | ... | ... |
471 | 50 | VT | 99999 | 2 | 2010 |
472 | 50 | VT | 99999 | 3 | 1070 |
473 | 50 | VT | 99999 | 4 | 650 |
474 | 50 | VT | 99999 | 5 | 750 |
475 | 50 | VT | 99999 | 6 | 180 |
476 rows × 5 columns
list(data)
['STATEFIPS', 'STATE', 'zipcode', 'agi_stub', 'N1']
Por otro lado, cuando hacemos la importación, por default se interpreta que la primer fila corresponde a las etiquetas correspondientes de las columnas. Así, podemos escribir también header=None
para especificar que la tabla no tiene nombres de columnas, por lo cual Pandas hará que los nombres de las columnas sea una enumeración comenzando por el cero.
Podemos combinar lo anterior y agregar names=
para colocar el nombre de las columnas manualmente. Por ejemplo, de la tabla anterior vemos como Pandas tomó los valores de la primer fila que encontró de acuerdo a nuestras especificaciones y los colocó como nombre de las columnas de dicha tabla. Podemos modificar lo anterir como sigue
data3 = pd.read_csv(cadena, usecols=[0,1,2,3,4], nrows=476, skiprows=1000, header=None,
names=list(data))
# Veamos el resultado
data3
STATEFIPS | STATE | zipcode | agi_stub | N1 | |
---|---|---|---|---|---|
0 | 50 | VT | 5730 | 4 | 20 |
1 | 50 | VT | 5730 | 5 | 30 |
2 | 50 | VT | 5730 | 6 | 0 |
3 | 50 | VT | 5732 | 1 | 200 |
4 | 50 | VT | 5732 | 2 | 120 |
... | ... | ... | ... | ... | ... |
471 | 50 | VT | 99999 | 1 | 2710 |
472 | 50 | VT | 99999 | 2 | 2010 |
473 | 50 | VT | 99999 | 3 | 1070 |
474 | 50 | VT | 99999 | 4 | 650 |
475 | 50 | VT | 99999 | 5 | 750 |
476 rows × 5 columns
donde list(data)
nos arroja una lista del nombre de las columnas de la tabla que tenemos en el dataframe data
; por lo que estos nombres fueron asignados al nombre de las columnas de este nuevo dataframe.
En estos podríamos tener:
Comencemos por ver el tipo de dato de las columnas del dataframe data
data.dtypes
STATEFIPS int64 STATE object zipcode int64 agi_stub int64 N1 int64 dtype: object
donde object es el equivalente de string en Pandas. Tenemos que zipcode
está almacenado como un número entero, pero en realidad queremos que este sea una cadena de texto. Para lograr lo anterior escribimos
data4 = pd.read_csv(cadena, usecols=[0,1,2,3,4], nrows=476, skiprows=1000, header=None,
names=list(data),
dtype={"zipcode": str})
data4.dtypes
STATEFIPS int64 STATE object zipcode object agi_stub int64 N1 int64 dtype: object
donde cambiamos el tipo de dato de la columna zipcode
a tipo string (u object) utilizando un diccionario en el valor dtype=
a la hora de la importación.
De forma automática, Pandas puede identificar los valores nulos o NA, sin embargo, puede ocurrir que dichos valores estén representados de otra forma en los registros del dataframe. Por ejemplo, supangamos que el valor 0 en la columna zipcode
es en realidad un valor nulo. Podemos indicar lo anterior a Pandas escribiendo
data5 = pd.read_csv(cadena, usecols=[0,1,2,3,4],
dtype={"zipcode": str},
na_values={"zipcode": "0"})
utilizando nuevamente un diccionario para especificar que en la columana zipcode
el valor de 0 (en cadena de texto) debe considerarse como una valor nulo. Así, podemos considerar en el dataframe data5
sólo las filas donde tengamos valores nulos en la columna zipcode
escribiendo
# filtramos en la columna zipcode
data5[data5.zipcode.isna()]
STATEFIPS | STATE | zipcode | agi_stub | N1 | |
---|---|---|---|---|---|
0 | 50 | VT | NaN | 1 | 111580 |
1 | 50 | VT | NaN | 2 | 82760 |
2 | 50 | VT | NaN | 3 | 46270 |
3 | 50 | VT | NaN | 4 | 30070 |
4 | 50 | VT | NaN | 5 | 39530 |
5 | 50 | VT | NaN | 6 | 9620 |
# acá no filtramos para comparar
data5
STATEFIPS | STATE | zipcode | agi_stub | N1 | |
---|---|---|---|---|---|
0 | 50 | VT | NaN | 1 | 111580 |
1 | 50 | VT | NaN | 2 | 82760 |
2 | 50 | VT | NaN | 3 | 46270 |
3 | 50 | VT | NaN | 4 | 30070 |
4 | 50 | VT | NaN | 5 | 39530 |
... | ... | ... | ... | ... | ... |
1471 | 50 | VT | 99999 | 2 | 2010 |
1472 | 50 | VT | 99999 | 3 | 1070 |
1473 | 50 | VT | 99999 | 4 | 650 |
1474 | 50 | VT | 99999 | 5 | 750 |
1475 | 50 | VT | 99999 | 6 | 180 |
1476 rows × 5 columns
Finalmente está el caso en que Pandas no puede analizar la información de la importación. Para ello tenemos
error_bad_lines=False
: que hace que Pandas salte las líneas que no puede reconocer y cargue las que sí.warn_bad_lines=True
: que hace que Pandas muestre un mensaje si es que omitió líneas que no pudo analizar.Por defecto está configurado que error_bad_lines=True
por lo que Pandas no saltará aquellas líneas que no puede reconocer y por ende arrojará un error.
De tal manera, podemos escribir algo como
data8 = pd.read_csv(cadena, error_bad_lines=False, warn_bad_lines=True)
para saltar aquellas líneas que Pandas no puede reconocer y arrojar un mensaje especificando ésto.