acsdata.data.gz
) y crear un conjunto de datos analítico (un archivo derivado enfocado específicamente en análisis en mano).Importa las bibliotecas necesarias y crea objetos Path
(ruta de archivo). Esto grarantiza reproducibilidad en distintos sistemas operativos (Windows utiliza \
en lugar de /
para separar los nombres de archivos.
Necesitamos:
pandas
para trabajar con los datos.pathlib
, y más específicamente su objeto Path
, para trabajar con rutas de archivos. Esto asegura que nuestro código funcione en Windows (que utiliza \
en sus rutas) y MacOS/Linux (los cuales utilizan /
).datetime
- tip: Existen sistemas de control de versiones para datos pero etiquetar tus archivos de datos (cuando no son masivos) no es un mal primer paso si estás comenzando.# Prepara tu entorno de trabajo
import _____ as pd
from _____ import Path
from datetime import datetime as dt
hoy = __.today().strftime("%d-%m-%y")
print(hoy)
06-05-19
nota: Aunque estés utilizando Windows puedes escribir /
en tus rutas de archivo.
# Directorio de datos y rutas
RUTA_DATOS_EN_BRUTO = ____("../datos/brutos/")
RUTA_DATOS_________ = ____("../datos/interinos/")
RUTA_______________ = ____("../datos/procesados/")
___________________ = ____("../datos/finales/")
NOTA: He incluido un script de python
llamado herramientas.py
con la función arbol
la cual muestra el árbol de directorios. La obtuvé de el tutorial de RealPython's sobre el módulo pathlib
).
de nuestro script herramientas importa arbol para utilizarla
arbol(________)
Con pandas
leer archivos de datos es tan fácil como escribir .read_csv(RUTA_AL_ARCHIVO_CSV)
y esto es suficiente la mayoría del tiempo. La función read_csv()
de Pandas
es tan poderosa que incluso lee archivos comprimidos sin tener que especificar algún otro parametro. Prueba lo siguiente:
datos = pd.read_csv(RUTA_DATOS_EN_BRUTO / 'acs_data.csv.gz')
datos.head()
Asegurate de cambiar RUTA_DE_DATOS_EN_BRUTO
a lo que seaque hayas llamado tu variable a la que asignaste el objeto Path
con la ruta a tus datos en bruto.
IPUMS ofrece algunos formatos de datos que pueden ser aún más útiles [docs]:
Además del archivo de datos ASCII, el sistema crea un archivo de sintaxis de paquetes estadísticos para acompañar cada extracto. El archivo de sintaxis está diseñado para leer los datos ASCII mientras se aplican las variables de valor y las etiquetas apropiadas. SPSS, SAS y Stata son compatibles. Debe descargar el archivo de sintaxis con el extracto o no podrá leer los datos. El archivo de sintaxis requiere una edición menor para identificar la ubicación del archivo de datos en su computadora local.
En este caso, usaremos un archivo Stata (.dta
). La razón principal es que los archivos .dta
pueden almacenar etiquetas de valor quepandas
luego puede leer y convertir columnas en columnas Categóricas
en nuestro marco de datos de pandas. Esto 1) ahorra memoria, y 2) es una buena práctica porque ciertas ciencias sociales en serio, en serio en serio, y en serio en serio *en serio* aman a Stata, por lo que sus conjuntos de datos interesantes son probablemente archivos .dta
.
Sin embargo, pandas
no puede leer.dta
comprimido directamente como lo hace con los archivos .csv
. Afortunadamente, IPUMS utiliza el formato comprimido gzip y python
incluye un módulogzip
en su biblioteca estándar.
Importa gzip y prueba lo siguiente:
with gzip.open(RUTA_DATOS_EN_BRUTO / 'acs_data.dta.gz') as archivo:
datos = pd.read_stata(archivo)
y muestra las primeras cinco filas de tu datos
DataFrame.
# importa gzip y carga tus datos
# muestra las primeras 5 filas
Ya hemos visto .head()
- el método de pandas
que mostrará las primeras 5 filas de tu DataFrame. Esto te da una idea de cómo se ven sus datos. Sin embargo, hay mucho más .info()
que puede salir de su marco de datos. También puede simplemente pedirle al DataFrame si los .describe()
...
# averigua más info de tu DataFrame
datos.____()
# describe tus datos
datos.____()
Comprueba la 'forma' de tus datos con su atributo .shape
. Note la falta de paréntesis.
datos._____
En este momento estás trabajando con tu archivo maestro: un conjunto de datos que contiene todo lo que podría necesita para su análisis. Realmente no deseas modificar este conjunto de datos porque podría estar usándolo para otros análisis. Por ejemplo, vamos a analizar el acceso a Internet de alta velocidad en un estado de tu elección, pero la próxima semana es posible que desees realizar el mismo análisis en otro estado o tal vez solo en un condado específico. Para asegurarse de que puedas reutilizar tus datos y tu código más adelante, creamos un archivo de análisis, un conjunto de datos que contiene solo los datos necesarios para este análisis específico a mano.
Primero, solo estamos interesados en encontrar la _ "Brecha Digital" de un estado en este momento. El archivo maestro contiene datos de los 50 estados y el distrito de Columbia.
Lo que quieres hacer es encontrar todas las filas donde el statefip
coincide con el nombre de tu estado. Esto se llama indexación booleana.
Prueba lo siguiente:
datos['statefip'] == 'ohio'
Nota: Tu puedes cambiar 'ohio' a cualquiera de los 50 estados o 'district of columbia' para DC.
# Prueba indexación booleanda
___['______'] == '_______'
Esto devolverá un pandas.Series
de booleanos (Trues y Falses) que luego puede usar para filtrar las filas innecesarias.
Es una buena práctica guardar esta Serie como una variable al principio de tu código (si la conoces de antemano) o justo antes de usarla en caso de que use estas condiciones en más de un lugar. Esto te ahorrará tiempo si decide cambiar el valor que está comparando, 'ohio'
para' california'
por ejemplo.
mascara_estado = (datos['statefip'] == 'ohio')
datos[mascara_estado].head()
# hazlo tu
mascara_estado = (________________________ == _______)
datos[mascara_estado].____()
Guardemoslo en una variable con un nombre más útil:
datos_estatales = datos[mascara_estado].copy()
Tienes que utilizar .copy()
para crear copias reales de los datos. Si ejecutas:
datos_estatales = datos[mascara_estado]
datos_estatales
sería lo que la documentación de pandas
se refiere como una vista de DataFrame datos
. Esto puede tener consecuencias más adelante si modificas estos DataFrames. Muchas veces recibirás solo una advertencia y tu código se ejecutará tal como lo esperabas - pero, ¿para qué tomar riesgos?
# asigna tu nuevo DataFrame a la variable datos_estatales
datos_estatales = __________.copy()
Ahora, veamos que .columns
tenemos en nuestro DataFrame. Puedes acceder esta información de la misma manera que encontramos la "forma" hace un rato.
datos_estatales._____
¿Hay columnas de las cuales tienes seguridad que no vas a necesitar?
Si no estás 90% seguro de que no vas a necesitar una columna, no te deshagas de ella.
Eliminar columnas es tan fácil como utilizar .drop()
en tu DataFrame.
datos_estatales.drop(columns = ['lista', 'de', 'columnas', 'pa', 'eliminar'])
Si hay columnas que tu crees que no vas a necesitar pero no tienes mucha seguridad de que ese es el caso, deberías explorarlas.
Las columnas de los DataFrames de pandas
son pandas.Series
y tienen métodos y atributos como los DataFrames.
Exploremos la columna gq
que representa los valores de la variable Group Quarters
. La documentación de IPUMS define Group Quarters así:
Group quarters son en su mayoría instituciones y otras viviendas para grupos, por ejemplo, barracas militares y dormitorios.
Exploremos que valores unicos (.unique()
) tiene la Serie datos_estatales['gq']
...
Tambien podemos ver las cuentas totales de los sus valores (.value_counts()
) lo que nos daría una mejor idea de que tan útil esta columna podría ser.
Por ejemplo, si una columna tiene 2 valores pero 99% de sus observaciones tiene un valor y el resto tiene otro - en este caso, podrías deshacerte de esa columna ya que no agregaría mucho valor a tu análisis.
Hay algunas variables donde el 100% de sus filas tienen el mismo valor *tos* *tos* datos_estatales['year']
De la documentación de IPUMS:
Hay tres definiciones un poco diferentes de group quarters en IPUMS. Para el periodo 1940-1970 (excluyendo el conjunto de datos 100% de 1940), group quarters eran unidades de vivienda con 5 o más individuos sin relación a la cabeza de la vivienda. Antes de 1940 y en 1980-1990, unidades con 10 o más individuos sin relación a la cabeza de la vivienda eran considerados group quarters. En el censo del 2000, 2010, la ACS y la PRCS, ningún límite fue aplicado; para que un hogar fuera considerado group quarter, tenía que estar en una lista de group quarters que el Censo mantiene continuamente. En años pasados, una lista similar fue utilizada, con la regla sobre personas-sin-relación immpuesta como seguridad.
Por esta razón, y por el hecho de que la gran mayoría de nuestras observaciones caen bajo la definición de 1970 y 1990, estas son a las que nos apegaremos para nuestro análisis.
Creemos otra mascara para filtrar todos los hogares que no caen bajo nuestra definición
Para evaluar multiples condiciones utilizamos los operadores &
y |
("Y" y "O", respectivamente).
mascara_hogar = ( PRIMERA CONDICION ) | ( SEGUNDA CONDICION )
mascara_hogar = (datos_estatales['gq'] <= 'additional households under 1990 definition')
datos_estatales = datos_estatales[mascara_hogar].______()
Para este momento ya estas muy cerca de crear un datos_analisis
DataFrame. Hasta ahora:
Nuestra pregunta de investigación 1 es: "¿Qué porcentage de hogares en el estado X tiene acceso a internet de alta velocidad?"
Matemáticamente, $$ \frac{hogares\ con\ internet\ de\ alta\ velocidad}{hogares\ en\ el\ estado}$$
Tu conjunto de datos datos_estatales
ahora contiene todo lo que necesitas para encontrar la respuesta.
Ahora que haz recordado tu archivo maestro en un conjunto de datos enfocado para tu análisis, deberías guardarlo.
Hemos estado trabajando con un archivo .dta
y sale mejor mantenerlo así.
Prueba lo siguiente:
datos_estatales.to_stata(RUTA_DATOS_INTERINOS / f'datos_estatales-{hoy}.dta', write_index = False)
Un par de cosas:
f-strings
para etiquetar nuestro archivo de datos con la fecha de hoy.write_index
para que al grabar nuestro archivo no se le agregue una columna "index" (índice). Es nuestro conjunto de datos el índice no tiene valor. En otros análisis puede que tengas un índice que tenga algún valor o significado - en esos casos no vas a querer apagar esta bandera.
¿Qué tal si cambiaramos nuestra pregunta de investigación un poco? de
"¿Qué porcentaje de hogares en el estado X tienen acceso a internet de alta velocidad?"
a
"¿Qué porcentaje de hogares con niños y niñas de edad escolar en el estado X tiene acceso a internet de alta velocidad?"
Esto sería una estadística interecante para los creadores de políticas públicas, especialmente si encontramos diferencias entre grupos demográficos (pregunta de investigación 2).
El reto aquí es que nuestra unidad de observación en el archivo datos_estatales
es una persona (ponderada) y que ahora queremos filtrar aquellos hogares sin niños o niñas de edad escolar. Esto puede sonar un poco complicado a primera vista pero solo requiere modificar nuestro flujo de trabajo un poco.
Necesitamos hacer unas cuantas cosas:
'serial'
)La mayoría de las personas concordamos que edad escolar (de primaria a preparatoria) es de 6 a 17 años. Algunas personas están mas interesadas en Kindergarden - Preparatoria (de 5 a 17 o 18 años). Algunas más no incluirían personas de 18 años. Lo importante es saber debes poder defender el porque de que cualquier rango.
Para este análisis, yo sugeriría que usemos 5 - 18 años (Kinder a Preparatoria) pero tu puedes escoger cualquier rango que desees. ¿Qué tal edad de preparatoria (14-18)? Eso sería interesante, lo más probable es que necesites acceso a internet de alta velocidad en la preparatoria más que cuando estas en kindergarden.
mascara_ninxs = (datos_estatales['age'] >= ___) & (___________________ <= )
Ahora que tenemos nuestra máscara podemos utilizarla para crear una lista de hogares con niños/niñas.
Anteriormente, aplicamos una máscara a un DataFrame y la guardamos en una variable. Ahora, vamos a dar un paso más y tomar una sola columna del marco de datos filtrado.
Intentalo tu primero.
hogares_con_ninxs = _________________________________________
hogares_con_ninxs.head()
¿Recuerdas como eliminamos (drop
) columnas al inicio?
¿Cómo crees que podemos eliminar duplicados?
.drop_duplicates()
Ahora que tienes una lista de valores únicos de los hogares con niños/niñas, solo tienes que revisar si un valor de la columna serial
de nuestro marco de datos datos_estatales
también en nuestra series hogares_con_ninxs
.
Grabemos esto como datos_para_analisis
en nuestra computadora.
datos_para_analisis = _____________________
datos_para_analisis.to_stata(RUTA_DATOS_INTERINOS / f'datos_para_analisis-{hoy}.dta', write_index = False)