Skip to main content

Ingesting NY Taxi Data to Postgres

Generator

En esta sección, vamos a crear un contenedor Docker con PostgreSQL, donde configuraremos una base de datos. Posteriormente, crearemos una tabla para almacenar un archivo Parquet que descargaremos desde un repositorio remoto el cual transformaremos a un CSV previamente.

Este proyecto implica la ingestión de datos del servicio de Taxis de Nueva York en una base de datos PostgreSQL. Esto incluirá la extracción de datos desde el repositorio remoto, su transformación según sea necesario para adaptarse a la estructura de la base de datos, y la carga eficiente de los mismos en PostgreSQL.

El objetivo final es crear una canalización o "pipeline" de datos mediante un script en Python que facilite la extracción de los datos desde el repositorio remoto y su transformación a un CSV y su posterior carga en la base de datos PostgreSQL. Para ello, utilizaremos un contenedor Docker con PostgreSQL y un entorno virtual de Python, donde ejecutaremos un Jupyter Notebook para desarrollar el script python que se encargará de ejecutar esta tarea.

Estructura del Proyecto

Lo primero que haremos es conectarnos a Machine-0 donde abriremos una terminal y donde crearemos 4 carpetas repos Project-2 Module-1 nyc-tlc-data con los siguientes comandos (cada comando debe ejecutarse de manera independiente):

mkdir repos
cd repos
mkdir Project-1
cd Project-1
mkdir Module-1
cd Module-1
mkdir nyc-tlc-data

Generator

La estructura de Project-2 quedará así:

Project-1
└── Module-1
└── nyc-tlc-data

Docker PostgreSQL

Ahora debemos dentro del folder Module-1 ejecutaremos el siguiente comando Docker:

docker run -it \
-e POSTGRES_USER="root" \
-e POSTGRES_PASSWORD="root" \
-e POSTGRES_DB="ny_taxi" \
-v $(pwd)/nyc-tlc-data:/var/lib/postgresql/data \
-p 5432:5432 \
postgres:13

Comando Docker:

  • docker run -it: Inicia un contenedor Docker en modo interactivo.
  • -e POSTGRES_USER="root": Establece el nombre de usuario de PostgreSQL como "root".
  • -e POSTGRES_PASSWORD="root": Establece la contraseña del usuario "root" como "root".
  • -e POSTGRES_DB="ny_taxi": Crea una base de datos llamada "ny_taxi" dentro del contenedor.
  • -v $(pwd)/nyc-tlc-data:/var/lib/postgresql/data: Monta el directorio local nyc-tlc-data (que acabamos de crear) dentro del contenedor en /var/lib/postgresql/data. Esto permite que PostgreSQL pueda acceder y almacenar datos en este directorio local.
  • -p 5432:5432: Mapea el puerto 5432 del contenedor (donde PostgreSQL está escuchando por defecto) al puerto 5432 del sistema host, permitiendo así acceder a la base de datos PostgreSQL desde fuera del contenedor. Imagen utilizada:
  • postgres:13: Utiliza la imagen oficial de PostgreSQL versión 13 para crear el contenedor.

Docker Extension

La extensión de Docker para Visual Studio Code es una herramienta que facilita la gestión de contenedores y aplicaciones Docker directamente desde el entorno de desarrollo, permitiendo a los usuarios construir, ejecutar y depurar contenedores, así como administrar imágenes, redes y volúmenes de manera visual e intuitiva, todo ello integrado en la interfaz de VS Code.

Ahora instalaremos la extensión de Docker en Visual Studio Code, sigue estos pasos:

  1. Abre Visual Studio Code.

  2. Ve a la vista de extensiones:

    • Puedes hacer esto haciendo clic en el ícono de extensiones en la barra lateral izquierda (parece cuatro bloques).
    • O presiona Ctrl + Shift + X en Windows/Linux o Cmd + Shift + X en macOS.
  3. Busca la extensión de Docker:

    • En la barra de búsqueda, escribe "Docker".
  4. Selecciona la extensión:

    • Deberías ver una lista de extensiones relacionadas. Busca la que dice "Docker" (generalmente publicada por Microsoft).
  5. Instala la extensión:

    • Haz clic en el botón de "Instalar" junto a la extensión.
  6. Configura la extensión (opcional):

    • Una vez instalada, puedes acceder a la configuración haciendo clic en el ícono de engranaje junto a la extensión y seleccionando "Configuración".
  7. Verifica la instalación:

    • Deberías ver un nuevo ícono de Docker en la barra lateral, lo que indica que la extensión está activa.

Postgres PSQL

psql es una herramienta de línea de comandos para interactuar con bases de datos PostgreSQL, que permite a los usuarios ejecutar consultas SQL, administrar bases de datos y realizar tareas de mantenimiento de manera eficiente. Proporciona un entorno interactivo que admite la ejecución de comandos SQL, la visualización de resultados en tiempo real y la ejecución de scripts, así como características adicionales como autocompletado y historial de comandos, lo que la convierte en una herramienta esencial para desarrolladores y administradores de bases de datos que trabajan con PostgreSQL.

Realiza un test de psql:

docker ps
docker exec -it <container_id_or_name> bash
psql -h localhost -p 5432 -U root -d ny_taxi
psql -U root -d ny_taxi
\l

Explicación de los comandos

  1. docker ps: Este comando muestra una lista de todos los contenedores Docker que están actualmente en ejecución. Incluye información como el ID del contenedor, su nombre, el estado y los puertos que están expuestos.

  2. docker exec -it <container_id_or_name> bash: Este comando permite acceder a un contenedor en ejecución de manera interactiva. Aquí, <container_id_or_name> debe ser reemplazado por el ID o nombre del contenedor al que deseas acceder. El uso de -it habilita un terminal interactivo y bash inicia una sesión de shell Bash dentro del contenedor.

  3. psql -h localhost -p 5432 -U root -d ny_taxi: Este comando se utiliza para conectarse a una base de datos PostgreSQL usando psql. Aquí, se especifica:

    • -h localhost: indica que la base de datos está en la misma máquina (localhost).
    • -p 5432: especifica el puerto de conexión, que es el puerto por defecto de PostgreSQL.
    • -U root: establece el usuario que se utilizará para la conexión, en este caso, "root".
    • -d ny_taxi: indica el nombre de la base de datos a la que se desea conectar, que en este caso es "ny_taxi".
  4. psql -U root -d ny_taxi: Este comando también conecta a la base de datos "ny_taxi" pero sin especificar el host ni el puerto, lo que implica que se está utilizando la configuración por defecto (localhost y puerto 5432).

  5. \l: Este es un comando de psql que lista todas las bases de datos disponibles en el servidor PostgreSQL al que estás conectado. Te muestra información sobre cada base de datos, como el nombre, propietario, codificación y acceso.

En conjunto, estas líneas permiten ver los contenedores en ejecución, acceder a uno de ellos, y luego conectarse a una base de datos específica en PostgreSQL para realizar consultas o administrar datos.

Generator

Este es ejemplo de cómo interactuar con una base de datos PostgreSQL utilizando el cliente de línea de comandos psql. Vamos a desglosarlo:

  1. Conexión a la base de datos:

    psql -U root -d ny_taxi
    • psql: Es el cliente de línea de comandos para PostgreSQL.
    • -U root: Indica que te estás conectando como el usuario root.
    • -d ny_taxi: Especifica la base de datos a la que te estás conectando, en este caso, ny_taxi.
  2. Listar bases de datos:

    \l

    Este comando se utiliza dentro de psql para listar todas las bases de datos disponibles en el servidor PostgreSQL.

  3. Salida de la lista de bases de datos: La salida muestra varias columnas:

    • Name: Nombre de la base de datos.
    • Owner: Propietario de la base de datos.
    • Encoding: Tipo de codificación de caracteres utilizado en la base de datos (en este caso, UTF8).
    • Collate: Configuración de ordenamiento (también en este caso, en_US.utf8).
    • Ctype: Tipo de caracteres para la base de datos (igualmente, en_US.utf8).
    • Access privileges: Privilegios de acceso.

    En la salida, se ven cuatro bases de datos:

    • ny_taxi: La base de datos que creamos con el comando Docker y a la que estamos conectados actualmente.
    • postgres: La base de datos predeterminada de PostgreSQL.
    • template0 y template1: Plantillas de base de datos que se utilizan para crear nuevas bases de datos.

Cada base de datos tiene el mismo propietario (root) y todas están utilizando la codificación UTF8 con las mismas configuraciones de collation y ctype. Las plantillas también muestran privilegios de acceso para el usuario root.

note

En este ejemplo de PostgreSQL usamos el usuario root por ser un ejemplo sin embargo usar el usuario root o cualquier usuario con privilegios de superusuario para tareas diarias no es una práctica recomendada en la administración de bases de datos.

PGcli

PGcli es una herramienta de línea de comandos para interactuar con bases de datos PostgreSQL de una manera más amigable y eficiente que el cliente estándar de PostgreSQL (psql). Algunas de las características y ventajas de PGcli incluyen:

  1. Interfaz mejorada: PGcli proporciona una interfaz de línea de comandos más intuitiva y fácil de usar en comparación con psql. Tiene características como resaltado de sintaxis, completado automático de consultas y tablas, historial de comandos mejorado, entre otros.

  2. Autocompletado avanzado: Ofrece autocompletado inteligente para nombres de tablas, columnas, palabras clave SQL y más, lo que agiliza la escritura y evita errores tipográficos.

  3. Formato de salida mejorado: PGcli formatea los resultados de las consultas de una manera más legible y estructurada, facilitando la visualización de datos complejos.

  4. Configuración y personalización: Permite configurar varios aspectos de la interfaz de usuario y el comportamiento del cliente según las preferencias del usuario.

  5. Soporte multiplataforma: Funciona en sistemas operativos compatibles con Python (Linux, macOS, Windows), lo que lo hace accesible para una amplia gama de usuarios.

PGcli es una herramienta que mejora la experiencia de trabajar con PostgreSQL a través de su interfaz de línea de comandos optimizada y características adicionales que simplifican la interacción y gestión de bases de datos.

PIP

En Python, pip es el administrador de paquetes estándar que facilita la instalación y gestión de paquetes de software escritos en Python. El nombre "pip" proviene de "Pip Installs Python" o "Pip Installs Packages". Aquí hay algunos puntos clave sobre pip:

  1. Instalación de Paquetes: Pip se utiliza principalmente para instalar paquetes de Python desde el Python Package Index (PyPI) y otros repositorios. PyPI es el repositorio oficial de paquetes para Python, que contiene una gran cantidad de bibliotecas y herramientas disponibles para ser instaladas.

  2. Gestión de Dependencias: Pip resuelve automáticamente las dependencias de los paquetes que estás instalando. Esto significa que si un paquete necesita otros paquetes para funcionar correctamente, pip se encargará de instalar también esas dependencias.

  3. Actualización y Desinstalación: Además de la instalación, pip se puede usar para actualizar paquetes a nuevas versiones y para desinstalar paquetes que ya no se necesiten.

  4. Uso en la Línea de Comandos: Pip se utiliza a través de la línea de comandos. Algunos comandos comunes incluyen:

    • pip install nombre_del_paquete: Instala un paquete específico.
    • pip install -r requirements.txt: Instala todos los paquetes listados en un archivo requirements.txt.
    • pip uninstall nombre_del_paquete: Desinstala un paquete.
    • pip freeze > requirements.txt: Guarda una lista de todos los paquetes instalados en un archivo requirements.txt para reproducir exactamente el mismo entorno en otro lugar.
  5. Compatibilidad: Pip viene incluido por defecto en las versiones modernas de Python (a partir de Python 2.7.9 y Python 3.4).

pip es super esencial para cualquier desarrollador de Python ya que facilita la gestión eficiente de bibliotecas y herramientas externas necesarias para sus proyectos.

Entorno Virtual Python

¿Qué son exactamente?

Un entorno virtual es una herramienta que te permite crear un espacio aislado para tus proyectos en Python. Esto significa que puedes tener diferentes versiones de bibliotecas y dependencias sin que interfieran entre sí.

¿Entonces, por qué es importante usar un entorno virtual? Es importante porque te ayuda a evitar conflictos entre las diferentes versiones de paquetes que podrías estar usando en varios proyectos. Por ejemplo, si un proyecto necesita una versión específica de una biblioteca, puedes tenerla solo en ese entorno sin afectar otros proyectos.

¿Cómo se crea un entorno virtual? Puedes crear un entorno virtual usando el módulo venv que viene incluido en Python. Solo necesitas abrir la terminal y ejecutar python -m venv nombre_del_entorno. Esto creará una carpeta con todo lo necesario.

¿Y cómo activo el entorno una vez que lo he creado? Para activar el entorno, en Windows usarías nombre_del_entorno\Scripts\activate, y en macOS o Linux, sería source nombre_del_entorno/bin/activate. Una vez activado, verás el nombre del entorno en la línea de comandos.

¿y qué sucede cuando quiero instalar paquetes? Una vez que el entorno está activo, puedes usar pip install nombre_del_paquete y se instalará solo en ese entorno. Así, cada proyecto puede tener sus propias dependencias.

¿Hay algo más que debería saber? Solo recuerda desactivar el entorno cuando termines, usando el comando deactivate. Y siempre es buena práctica tener un archivo requirements.txt donde anotes las dependencias de tu proyecto.

Instalación Python VENV y PGcli

Ahora procederemos con la instalación de python venv que

Para la instalación solo debemos ejecutar el siguiente comando

sudo apt update
sudo apt install python3-venv

Para instalar PGcli lo haremos con PIP pero antes asegurémonos de instalar previamente libpq-dev:

sudo apt update
sudo apt-get install libpq-dev

Esto asegurará que psycopg pueda encontrar la biblioteca libpq adecuada durante la instalación y la ejecución.

Crear un entorno virtual venv

Para crear un entorno virtual con python venv, se utiliza típicamente el siguiente comando en la línea de comandos :

python3 -m venv venv

Donde venv es el nombre que se le desea dar al entorno virtual en este caso seleccionamos venv como nombre pero puedes cambiarlo. Una vez creado, se activa el entorno virtual dependiendo del sistema operativo.

Después de haber creado tu entorno virtual procederemos a activarlo

source venv/bin/activate

Generator

Cuando ves admin@machine-0-user1:~/repos/Project-1/Module-1$, eso es el prompt de la terminal. ¿Sabes qué significa cada parte?

Está mostrando el usuario, la máquina y la ruta en la que estas. Ahora, cuando escribes source venv/bin/activate, ¿qué crees que está pasando?

Estás activando un entorno virtual llamado "venv". Pero, ¿para qué sirve eso?

Activar un entorno virtual es importante porque te permite tener un espacio aislado para tus proyectos de Python. Así puedes instalar dependencias específicas sin afectar otros proyectos o tu instalación global de Python.

¿Y qué pasa después de ejecutar ese comando?

Una vez que ejecutas ese comando, ves que cambia el prompt a (venv) admin@machine-0-user1:~/repos/Project-1/Module-1$. Esa "(venv)" al principio indica que estás dentro del entorno virtual. Ahora cualquier paquete que instales se hará solo dentro de ese entorno.

Con esto no tendrás problemas con versiones de paquetes en otros proyectos. Así puedes trabajar en múltiples proyectos sin conflictos.

PIP PGcli

Con el entorno virtual activado procedemos a instalar PGcli

pip install pgcli

Validamos la instalación

pgcli --help

**Conexión a PostgreSQL con PGcli

pgcli -h localhost -p 5432 -u root -d ny_taxi

Ahora podemos hacer un par de pruebas con

select 1;
\dt

Generator

Muy bien, ahora veamos lo que sucede con estos comandos. Estás en el entorno virtual y ves el prompt (venv) admin@machine-0-user1:~/repos/Project-1/Module-1$. Luego, ejecutas pgcli -h localhost -p 5432 -u root -d ny_taxi. ¿Qué estamos haciendo? Estás intentando conectarte a una base de datos PostgreSQL.

pgcli es una herramienta de línea de comandos para interactuar con bases de datos PostgreSQL, pero tiene algunas ventajas sobre psql, que es la herramienta de línea de comandos estándar de PostgreSQL.

¿Cuáles son esas ventajas?

Bueno, primero, pgcli tiene autocompletado. Cuando empiezas a escribir una consulta, puedes presionar la tecla Tab y te sugiere posibles completaciones. Eso puede acelerar mucho el proceso de escribir consultas.

¡Eso suena útil! ¿Y hay algo más?

Sí, también tiene resaltado de sintaxis, lo que hace que las consultas sean más legibles. Esto puede ayudar a identificar errores más fácilmente.

Y luego te pide la contraseña, y ahí es cuando ves Password for root:, necesitas ingresar la contraseña del usuario "root" para acceder a la base de datos. Recuerda que las credenciales, tanto el user como el password, las creamos con anterioridad desde el contenedor Docker de PostgreSQL.

Generator

Una vez que estas dentro, verás root@localhost:ny_taxi>. ¿Qué significa eso?

Esa línea indica que estás conectado a la base de datos "ny_taxi" como el usuario "root". Ahora puedes ejecutar consultas SQL. En resumen, lo mismo que realizaste pero con el comando psql.

Y luego ejecutas select 1;, ¿qué hace eso? Esa consulta simple devuelve el número 1, que es una forma de verificar que todo funciona correctamente.

¿Y qué es \dt?

Esa es una instrucción especial en pgcli que muestra las tablas en la base de datos actual. La salida que ves, con las columnas Schema, Name, Type y Owner, te da información sobre cada tabla disponible. Así puedes ver qué tablas hay en la base de datos exactamente lo mismo que en psql

Aunque también puedes hacer esto en psql, muchos usuarios prefieren pgcli por su interfaz más amigable.

Instalación Jupyter Lab

pip install jupyterlab

Ahora, hablemos sobre otro comando: pip install jupyterlab. ¿Sabes qué hace? Instala JupyterLab. Pero, ¿Qué es exactamente?

JupyterLab es una interfaz de usuario más moderna y flexible para trabajar con Jupyter Notebooks. Permite combinar texto, código y visualizaciones en un solo lugar. Es muy popular entre los científicos de datos y en la ingeniería de datos.

¿Y qué hace que Jupyter sea tan importante para la ingeniería de datos? Primero, Jupyter permite a los usuarios escribir y ejecutar código en tiempo real. Puedes probar diferentes transformaciones de datos, crear visualizaciones y documentar el proceso todo en el mismo lugar.

También es excelente para compartir tu trabajo. Puedes exportar notebooks a diferentes formatos, como HTML o PDF, lo que facilita la presentación de tus análisis a otros. Además, como se puede integrar con diversas bibliotecas de Python, es muy versátil.

Además, muchas veces, en un equipo de datos, los miembros pueden trabajar en el mismo notebook, lo que permite que todos vean el trabajo de los demás y hagan comentarios directamente en el código.

Entonces, al usar pip install jupyterlab, estas instalando una herramienta clave para tu trabajo en ingeniería de datos. JupyterLab te proporcionará un entorno muy poderoso para explorar, analizar y presentar datos.

Ejecutamos el siguiente comando para iniciar el servidor Jupyter Lab

jupyter lab

Ahora que ejecutaste jupyter lab, mira todo el log que se generó. ¿Ves que dice cosas como "extension was successfully linked" y "extension was successfully loaded"?

Generator

Parece que está cargando varias extensiones. ¿Qué significan esas extensiones?

Las extensiones son complementos que mejoran la funcionalidad de JupyterLab. Por ejemplo, jupyter_lsp permite autocompletado y soporte para varios lenguajes, lo que hace más fácil programar. También verás que dice "Serving notebooks from local directory: /home/admin/repos/Project-1/Module-1". Eso significa que JupyterLab está usando esa carpeta.

JupyterLab servirá los notebooks de esa carpeta. Además, te está dando dos URLs para acceder a la interfaz, como http://localhost:8888/lab?token=....

Ahora, Mira el token en la URL. ¿Es necesario para acceder a JupyterLab?

Sí, el token actúa como una forma de autenticación. Cuando accedes a esa URL en tu navegador, necesitas incluir el token para garantizar que solo las personas autorizadas puedan acceder a tu servidor de JupyterLab.

Entonces, si estas usando Visual Studio Code, ¿tienes que preocuparte por el token?

No realmente. Cuando usas la opción de reenvío de puertos de Visual Studio Code, que abre el puerto 8888 por defecto, la conexión se establece automáticamente. Esto significa que deberías poder acceder a JupyterLab directamente sin tener que ingresar el token manualmente.

¿Y cómo puedo hacer eso?

Cuando inicies JupyterLab y veas que está escuchando en el puerto 8888, simplemente utiliza la opción de "Forward Port" en Visual Studio Code. Esto redirigirá el puerto 8888, y podrás acceder a JupyterLab sin preocuparte por el token. Así no tengo que copiar y pegar la URL cada vez.

Solo abre Visual Studio Code y selecciona el puerto 8888. Después, podrás acceder a JupyterLab fácilmente.

requirements.txt

El archivo requirements.txt es comúnmente utilizado en entornos de desarrollo de software en Python. Su propósito principal es listar las dependencias que un proyecto de Python necesita para funcionar correctamente. Aquí hay algunos puntos clave sobre este archivo:

  1. Listado de Dependencias: En requirements.txt, se enumeran todos los paquetes de Python y sus versiones específicas que el proyecto requiere. Cada línea del archivo generalmente contiene el nombre del paquete y, opcionalmente, la versión específica necesaria, separados por == (por ejemplo, requests==2.25.1).

  2. Facilita la Reproducibilidad: Al incluir las versiones específicas de los paquetes, requirements.txt ayuda a asegurar que cualquier persona que trabaje en el proyecto utilice las mismas versiones de software, lo que facilita la reproducibilidad del entorno de desarrollo.

  3. Instalación de Dependencias: Puedes usar requirements.txt junto con herramientas como pip (el gestor de paquetes de Python) para instalar todas las dependencias necesarias con una sola orden. Por ejemplo:

    pip install -r requirements.txt
  4. Gestión de Versiones: Aunque requirements.txt es una práctica común, también existen alternativas más avanzadas y robustas, como pipenv y conda, que proporcionan gestión más sofisticada de entornos virtuales y dependencias en Python.

Flask==2.0.1
requests==2.26.0
numpy==1.22.0

Es un archivo fundamental para proyectos de Python que especifica las dependencias necesarias, facilita la gestión del entorno de desarrollo y asegura que todos los colaboradores del proyecto utilicen las mismas versiones de paquetes.

Instalación requirements.txt

Una vez iniciado procederemos a instalar requirements.txt archivo que crearemos en /Project-2/Module-1. En este archivo agregaremos la siguiente información:

pyarrow
fastparquet
pandas

Para proceder con la instalación del requirements.txt primero debemos detener el servidor Jupyter Lab con la combinación de teclas CTRL+X Después ejecutaremos el siguiente comando:

pip install -r requirements.txt

Las tres bibliotecas que instalaremos son ampliamente utilizadas en el ecosistema de Python para el manejo eficiente de datos, especialmente cuando se trabaja con formatos de almacenamiento de datos como Parquet y operaciones de procesamiento de datos tabulares. Aquí te explico qué hace cada una:

  1. pyarrow:

    • Función principal: pyarrow es una biblioteca optimizada para el manejo de datos columnares, diseñada para ser rápida y eficiente.
    • Uso principal: Se utiliza principalmente para leer, escribir y procesar datos en formatos como Parquet y Feather, que son formatos de almacenamiento columnares que permiten una compresión eficiente y operaciones rápidas en grandes conjuntos de datos.
    • Características clave: Ofrece soporte para tipos de datos complejos, operaciones vectorizadas rápidas y la capacidad de integrarse bien con otras bibliotecas como pandas y NumPy.
    • Ejemplo de uso: Permite leer y escribir archivos Parquet de manera eficiente en pandas mediante pd.read_parquet() y df.to_parquet(), respectivamente.
  2. fastparquet:

    • Función principal: fastparquet es una biblioteca específica para la lectura y escritura rápida de archivos Parquet.
    • Uso principal: Proporciona una implementación eficiente en Python para manipular archivos Parquet, lo que es especialmente útil cuando se trabaja con grandes volúmenes de datos que necesitan ser procesados y almacenados de manera eficiente.
    • Características clave: Está diseñada para ser rápida y eficiente tanto en lectura como en escritura, utilizando estrategias de compresión y almacenamiento columnar optimizadas.
    • Ejemplo de uso: Se puede utilizar directamente para leer y escribir archivos Parquet con opciones de configuración específicas para optimizar el rendimiento y la compresión de datos.
  3. pandas:

    • Función principal: pandas es una biblioteca de análisis de datos de alto nivel, que proporciona estructuras de datos flexibles y herramientas para trabajar con datos tabulares.
    • Uso principal: Es ampliamente utilizado para manipulaciones de datos como carga, limpieza, transformación y análisis de datos tabulares. Se integra bien con otras bibliotecas como NumPy, matplotlib y las mencionadas anteriormente (pyarrow y fastparquet) para operaciones avanzadas de procesamiento de datos.
    • Características clave: Proporciona la estructura de datos DataFrame, que es una tabla bidimensional con etiquetas en filas y columnas, similar a una hoja de cálculo. Ofrece una amplia gama de funciones para manipular datos, incluyendo operaciones de agrupación, fusión, pivote, y más.
    • Ejemplo de uso: Se utiliza para cargar datos desde diferentes fuentes (archivos CSV, Excel, bases de datos, etc.), procesar y analizar esos datos, y luego guardar los resultados en diferentes formatos, como CSV, Excel, HDF5, SQL, y también en Parquet utilizando bibliotecas como pyarrow o fastparquet.
note

Los datos tabulares son un tipo de organización de información que se presenta en forma de tabla, donde los datos se estructuran en filas y columnas. Cada columna representa un atributo o característica de los datos, mientras que cada fila corresponde a una observación o registro específico. Este formato facilita la visualización, análisis y manipulación de grandes volúmenes de información.

Con pyarrow y fastparquet son bibliotecas especializadas para el manejo eficiente de archivos Parquet, mientras que pandas es una biblioteca más general para el análisis y procesamiento de datos tabulares, que también puede interactuar eficientemente con Parquet utilizando pyarrow o fastparquet para operaciones de lectura y escritura.

Con esto instalaremos las dependencias necesarias para realizar los próximos pasos.

Descarga de datos

Ahora procederemos a crear un nuevo folder al que llamaremos data donde descargaremos un archivo Parquet correspondiente a los registros de viajes en taxis amarillos y verdes de la ciudad de nueva york.

mkdir data
cd data
note

Los registros de viajes en taxis amarillos y verdes incluyen campos que capturan fechas y horas de recogida y entrega, ubicaciones de recogida y entrega, distancias del viaje, tarifas detalladas, tipos de tarifas, tipos de pago y recuentos de pasajeros reportados por el conductor. Los datos utilizados en los conjuntos de datos adjuntos fueron recopilados y proporcionados a la Comisión de Taxis y Limusinas de la Ciudad de Nueva York (TLC).

Una vez dentro del directorio data, descargaremos el archivo Parquet con el siguiente comando:

Primero, asegurémonos de tener instalado el programa wget.

sudo apt update
sudo apt install wget

Luego procedemos a descargar el CSV con el comando wget:

wget https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2024-01.parquet 

En este link podremos validar la estructura y descripción de las columnas del Parquet y en este otro link la Tabla de búsqueda de zonas de taxis

Parquet to CSV

Los archivos Parquet son un formato de archivo de almacenamiento de datos de código abierto diseñado para ser eficiente y compatible con el procesamiento en paralelo de grandes conjuntos de datos. Este formato es comúnmente utilizado en entornos de big data y data lakes, especialmente en sistemas que utilizan Apache Hadoop.

Algunas características importantes de los archivos Parquet incluyen:

  • Eficiencia de almacenamiento: Parquet está diseñado para almacenar grandes cantidades de datos de manera eficiente, optimizando el uso de espacio en disco.
  • Eficiencia de procesamiento: Facilita el procesamiento eficiente de datos en sistemas distribuidos mediante el almacenamiento de datos en columnas y la utilización de compresión.
  • Compatibilidad con herramientas de big data: Es compatible con diversas herramientas y frameworks de procesamiento de datos como Apache Spark, Apache Hive, y otras.

Convertir un archivo Parquet a CSV puede ser beneficioso por razones de interoperabilidad y facilidad de uso. CSV es ampliamente compatible y fácil de manipular con herramientas comunes como hojas de cálculo y software de análisis de datos, lo que facilita su integración con sistemas que no soportan directamente Parquet. Además, puede simplificar el proceso de lectura, edición y análisis de datos para usuarios menos familiarizados con entornos de big data. Sin embargo, esta conversión puede implicar pérdidas en eficiencia y tamaño de archivo, debido a que Parquet está optimizado para grandes volúmenes de datos y CSV no ofrece compresión ni almacenamiento por columnas.

Ahora vamos correr de nuevo el comando jupyter lab dentro de la carpeta data Recordemos tener activado nuestro entorno virtual python:

source venv/bin/activate
jupyter lab

DataFrame

Un DataFrame es una estructura de datos bidimensional que se utiliza comúnmente en programación, especialmente en lenguajes como Python (con la biblioteca pandas) y R. Se asemeja a una tabla o a una hoja de cálculo(Excel), donde los datos están organizados en filas y columnas.

Características de un DataFrame:

  1. Estructura: Contiene filas y columnas, donde cada columna puede tener un tipo de dato diferente (números, texto, fechas, etc.).
  2. Etiquetas: Cada columna y fila puede tener etiquetas (nombres), lo que facilita el acceso y la manipulación de los datos.
  3. Manipulación de datos: Permite realizar operaciones como filtrado, agrupamiento, agregación y transformación de datos de manera sencilla.
  4. Compatibilidad: Se integra bien con otras herramientas de análisis de datos y bibliotecas.

Los DataFrames son ideales para el análisis de datos y la ciencia de datos, ya que permiten trabajar con grandes conjuntos de información de manera eficiente.

Proceso-1

El objetivo de este proceso es cargar el conjunto de datos en formato Parquet y convertirlo a un archivo CSV al que llamaremos "nyt.csv" sin incluir el índice, y por último generar un esquema SQL del DataFrame para revisar su estructura.

Desde el Jupyter Notebook crearemos un nuevo Notebook en File/New/Notebook al cual llamaremos 1-parquet-to-csv y agregaremos el siguiente código:

import pandas as pd
df = pd.read_parquet('yellow_tripdata_2024-01.parquet')
df.head(10)
df.to_csv('nyt.csv', index=False)
print(pd.io.sql.get_schema(df, name='yellow_taxi_data'))

En este código usaremos la biblioteca pandas para trabajar con datos tabulares, específicamente con archivos Parquet y CSV.

  1. Importar pandas:

    import pandas as pd

    Aquí se importa la biblioteca pandas bajo el alias pd, que es comúnmente utilizado para abreviar el nombre de pandas en el código.

  2. Leer archivo Parquet:

    df = pd.read_parquet('yellow_tripdata_2024-01.parquet')

    Este comando lee el archivo Parquet yellow_tripdata_2024-01.parquet y carga su contenido en un objeto DataFrame de pandas llamado df. Un DataFrame es una estructura de datos tabular bidimensional con etiquetas en filas y columnas, similar a una hoja de cálculo.

  3. Mostrar las primeras filas:

    df.head(10)

    Este comando imprime las primeras 10 filas del DataFrame df. Es una manera rápida de visualizar cómo se estructuran los datos y qué tipo de información contienen.

  4. Guardar en archivo CSV:

    df.to_csv('nyt.csv', index=False)

    Aquí se guarda el DataFrame df en un archivo CSV llamado nyt.csv. El parámetro index=False indica que no se debe incluir el índice del DataFrame como una columna adicional en el archivo CSV. Esto es útil cuando el índice no representa información relevante en el contexto del archivo CSV.

  5. Esquema de tabla SQL

print(pd.io.sql.get_schema(df, name='yellow_taxi_data'))

Esta función de pandas genera un esquema de tabla SQL basado en las columnas y tipos de datos presentes en el DataFrame df. Esto es útil cuando se desea crear una tabla SQL que refleje la estructura de los datos contenidos en el DataFrame, facilitando así la interoperabilidad entre datos almacenados en pandas y una base de datos relacional.

  • print(...) se utiliza para mostrar esa cadena de texto en la consola o en el entorno donde se esté ejecutando el código.
  • pd.io.sql es un submódulo de pandas que proporciona funciones relacionadas con la interacción entre pandas y bases de datos SQL.
  • get_schema(df, name='yellow_taxi_data') es una función que toma dos parámetros:
    • df: Es el DataFrame de pandas del cual se generará el esquema SQL.
    • name='yellow_taxi_data': Especifica el nombre que se le dará a la tabla SQL resultante. En este caso, la tabla se llamará yellow_taxi_data.

Pandas to SQL más en detalle

Este fragmento de código pandas generará y mostrará un esquema SQL que representa la estructura de una tabla basada en el DataFrame df. El objetivo de este código es mostrar el esquema de una tabla SQL que se puede crear a partir de un DataFrame de pandas (df). El nombre de la tabla SQL se especifica como 'yellow_taxi_data'.

Esquema de Tabla(Schema) Un esquema de tabla o Schema en ingles, en el contexto de bases de datos relacionales y SQL, se refiere a la estructura fundamental de una tabla. Describe cómo está organizada la tabla y qué tipo de datos puede contener cada columna. Aquí están los principales elementos que componen un esquema de tabla:

  1. Nombre de la tabla: Es el nombre único que identifica a la tabla dentro de la base de datos.

  2. Columnas (campos): Cada tabla está compuesta por una o más columnas, donde cada columna tiene un nombre único y un tipo de datos específico (como entero, cadena de caracteres, fecha, etc.).

  3. Tipos de datos: Define el tipo de datos que puede almacenar cada columna (por ejemplo, INTEGER, VARCHAR, DATE).

  4. Restricciones: Son reglas adicionales que se aplican a los datos en una columna, como restricciones de clave primaria, restricciones de clave externa, restricciones de unicidad, etc.

  5. Clave primaria: Una columna o conjunto de columnas que identifican de manera única cada fila en la tabla. La clave primaria garantiza la integridad de los datos y se utiliza como referencia en relaciones con otras tablas.

  6. Índices: Estructuras opcionales que mejoran el rendimiento de las consultas al permitir un acceso rápido a los datos en función de los valores de ciertas columnas.

CREATE TABLE "yellow_taxi_data" ( 
"VendorID" INTEGER,
"tpep_pickup_datetime" TIMESTAMP,
"tpep_dropoff_datetime" TIMESTAMP,
"passenger_count" REAL,
"trip_distance" REAL,
"RatecodeID" REAL,
"store_and_fwd_flag" TEXT,
"PULocationID" INTEGER,
"DOLocationID" INTEGER,
"payment_type" INTEGER,
"fare_amount" REAL,
"extra" REAL,
"mta_tax" REAL,
"tip_amount" REAL,
"tolls_amount" REAL,
"improvement_surcharge" REAL,
"total_amount" REAL,
"congestion_surcharge" REAL,
"Airport_fee" REAL )

Proceso-2

Ahora, crearemos otro nuevo Notebook al que llamaremos 2-upload-data donde crearemos un script python que creará una conexión a PostgreSQL para proceder a guardar los datos del CSV en una nueva tabla en PostgreSQL. Ahora agreguemos el siguiente código:

import pandas as pd
df = pd.read_csv('nyt.csv', nrows=100)
df.head(10)
print(pd.io.sql.get_schema(df, name='yellow_taxi_data'))

Básicamente es lo mismo que hicimos en Proceso-1 solo que ahora nos conectaremos directamente al CSV que exportamos desde el Parquet. Ahora revisemos la salida de esta línea de código print(pd.io.sql.get_schema(df, name='yellow_taxi_data'))

CREATE TABLE "yellow_taxi_data" (
"VendorID" INTEGER,
"tpep_pickup_datetime" TEXT,
"tpep_dropoff_datetime" TEXT,
"passenger_count" REAL,
"trip_distance" REAL,
"RatecodeID" REAL,
"store_and_fwd_flag" TEXT,
"PULocationID" INTEGER,
"DOLocationID" INTEGER,
"payment_type" INTEGER,
"fare_amount" REAL,
"extra" REAL,
"mta_tax" REAL,
"tip_amount" REAL,
"tolls_amount" REAL,
"improvement_surcharge" REAL,
"total_amount" REAL,
"congestion_surcharge" REAL,
"Airport_fee" REAL
)

Este código SQL representa la estructura de la tabla yellow_taxi_data, con columnas que coinciden con los nombres de las columnas del DataFrame de Pandas (df). Cada columna tiene su tipo de datos asociado (INTEGER, TEXT, REAL), que corresponde a los tipos de datos que se almacenarían en una base de datos SQL.

En el contexto del esquema de la tabla yellow_taxi_data, los campos "tpep_pickup_datetime" y "tpep_dropoff_datetime" están definidos como TEXT. Esto significa que se espera que estos campos contengan datos de tipo texto, que generalmente representan fechas y horas en formato de texto.

Si bien es posible almacenar fechas y horas como texto (TEXT), se recomienda utilizar tipos de datos específicos para fechas y horas cuando sea posible, para garantizar una gestión más eficiente y efectiva de estos datos en la base de datos SQL.

Para convertir los campos "tpep_pickup_datetime" y "tpep_dropoff_datetime" de texto (TEXT) a tipos de datos de fecha y hora adecuados para una base de datos SQL, debes agregar las siguientes líneas de código al Jupyter Notebook :

df.tpep_pickup_datetime = pd.to_datetime(df.tpep_pickup_datetime)
df.tpep_dropoff_datetime = pd.to_datetime(df.tpep_dropoff_datetime)

datetime En pandas , pd.to_datetime() en pandas facilita la conversión y manipulación de fechas y tiempos, lo que es esencial para muchas aplicaciones de análisis de datos y manipulación de series temporales.

SQLAlchemy es una biblioteca de Python que facilita la interacción con bases de datos relacionales utilizando un enfoque orientado a objetos. Es muy utilizada en aplicaciones web y otros proyectos donde se necesita una capa de abstracción sobre la base de datos para simplificar el manejo de consultas y transacciones.

Con SQLAlchemy junto con pandas es útil cuando necesitas trabajar con datos almacenados en bases de datos relacionales y deseas aprovechar las capacidades de optimización y gestión de conexiones que ofrece SQLAlchemy, mientras utilizas pandas para el análisis y manipulación de datos en un entorno de análisis de datos en Python.

SQLAlchemy Instalación

Para instalar SQLAlchemy es tan simple como usar el comando:

Desde Jupyter Notebook

!pip install sqlalchemy && pip install psycopg2-binary

Desde el entorno virtual python

pip install sqlalchemy
pip install psycopg2-binary

Una vez hayas instalado podemos actualizar el requirements.txt para poder instalarlo junto con las otras dependencias en proyectos futuros.

pyarrow
fastparquet
pandas
sqlalchemy
psycopg2-binary
jupyterlab

Ahora vamos a crear una conexión a PostgreSQL usando SQLAlchemy pero antes de tambien debemos importar las siguiente bibliotecas

from sqlalchemy import create_engine

Revisemos el código que básicamente importa la función create_engine del módulo sqlalchemy y la hace disponible para su uso en el programa. Aquí tienes una explicación detallada:

  1. Importación del módulo y función: from sqlalchemy import create_engine importa específicamente la función create_engine del paquete sqlalchemy. sqlalchemy es una biblioteca popular de Python para trabajar con bases de datos relacionales de manera flexible y eficiente.

  2. Función create_engine: Esta función se utiliza para crear un objeto que representa una conexión a la base de datos. Toma una cadena de conexión como argumento, que especifica cómo conectarse a la base de datos (por ejemplo, qué tipo de base de datos, ubicación, credenciales, etc.).

  3. Uso común: Después de importar create_engine, se suele usar para establecer una conexión con una base de datos. Por ejemplo:

    # Ejemplo
    engine = create_engine('sqlite:///mydatabase.db')

    Esto crea un motor (engine) que se conecta a una base de datos SQLite llamada mydatabase.db.

Ahora crearemos la conexión a PostgreSQL

engine = create_engine('postgresql://root:root@localhost:5432/ny_taxi')

Con este código se utiliza la función create_engine de SQLAlchemy para crear un objeto engine que representa una conexión a una base de datos PostgreSQL. Aquí tienes una explicación detallada línea por línea:

  1. create_engine: Esta función de SQLAlchemy se utiliza para crear un motor de base de datos, que es esencialmente una interfaz para interactuar con la base de datos PostgreSQL desde Python.

  2. Cadena de conexión: La cadena de conexión 'postgresql://root:root@localhost:5432/ny_taxi' se pasa como argumento a create_engine. Vamos a desglosar esta cadena:

    • postgresql://: Indica que estamos utilizando PostgreSQL como el sistema de gestión de bases de datos.
    • root:root@: El nombre de usuario y la contraseña para la conexión. En este caso, el nombre de usuario es root y la contraseña también es root.
    • localhost: La dirección del servidor de la base de datos. En este caso, se está conectando a la base de datos que se encuentra en la misma máquina en la que se está ejecutando el código (localhost).
    • 5432: El número de puerto en el que PostgreSQL está escuchando para conexiones. Por defecto, PostgreSQL usa el puerto 5432.
    • ny_taxi: El nombre de la base de datos a la que se desea conectar. En este caso, la base de datos se llama ny_taxi.
  3. Asignación a engine: La función create_engine devuelve un objeto Engine de SQLAlchemy que representa la conexión a la base de datos. Este objeto engine se puede utilizar posteriormente para ejecutar consultas SQL y realizar otras operaciones de base de datos.

El objetivo es crear un objeto engine que se conecta a una base de datos PostgreSQL llamada ny_taxi en el servidor local, utilizando el usuario root con contraseña root. Este objeto engine es fundamental para interactuar con la base de datos PostgreSQL desde Python usando SQLAlchemy.

Ahora procederemos a crear la conexión con el parámetro con=engine

print(pd.io.sql.get_schema(df, name='yellow_taxi_data', con=engine))

En este caso hemos agregado el parámetro con=engine al final del código donde este parámetro especifica la conexión a la base de datos que se utilizará para generar el esquema de la tabla. En tu caso, engine es un objeto que representa una conexión a una base de datos PostgreSQL (o cualquier otro tipo de base de datos, dependiendo de cómo se haya configurado inicialmente con create_engine).

Descripción del CSV Hasta el momento, solo hemos explorado algunos aspectos del archivo CSV. Sin embargo, aún desconocemos ciertas propiedades fundamentales del mismo, como la cantidad de columnas o filas. Esto se debe a que hemos estado trabajando con un "dataframe" que solo contiene una muestra del CSV, limitado a 100 registros mediante df = pd.read_csv('nyt.csv', nrows=100). Esta práctica puede ser útil en ciertos contextos, pero si deseamos obtener detalles más precisos, como la cantidad total de columnas y filas, necesitamos crear un nuevo dataframe que procese todos los datos y nos permita realizar un análisis más detallado. Para lograr lo anterior solo debemos quitar el nrows=100 del código:

df1 = pd.read_csv('nyt.csv')

En este caso hemos creado un nuevo dataframe df1. Ahora agregaremos esta otra pieza de código:

num_filas, num_columnas = df1.shape
print(f'El archivo CSV tiene {num_filas} filas y {num_columnas} columnas.')
  • df1.shape: Este es un atributo de los DataFrames de Pandas que devuelve una tupla con el número de filas y columnas del DataFrame df1. La primera posición de la tupla contiene el número de filas y la segunda el número de columnas.
  • num_filas, num_columnas = df1.shape: Utiliza la técnica de desempaquetado de tuplas para asignar los valores de la tupla devuelta por df1.shape a las variables num_filas y num_columnas respectivamente.
  • print(...): Esta es una función de Python que imprime el texto y los valores de las variables que se le pasan como argumento.
  • f'El archivo CSV tiene {num_filas} filas y {num_columnas} columnas.': Esta es una cadena de formato f (format string). Las expresiones entre llaves {} dentro de la cadena f son evaluadas y sus valores se insertan en la cadena en esos lugares. En este caso, se imprimirá el número de filas y columnas del DataFrame df1.

Ahora vamos a probar con otros métodos descriptivos:

df1.describe()

Este método df1.describe() se utiliza típicamente en entornos de análisis de datos como en este caso con Pandas (una biblioteca para manipulación y análisis de datos) cuando df1 es un DataFrame de Pandas.

El método describe() genera un resumen estadístico de las columnas numéricas de df1. Este resumen incluye varias estadísticas descriptivas útiles:

  1. Conteo (count): El número de elementos no nulos en cada columna.

  2. Media (mean): El promedio de los valores en cada columna.

  3. Desviación estándar (std): La medida de dispersión de los valores en cada columna.

  4. Valor mínimo (min): El valor más bajo en cada columna.

  5. Percentiles (25%, 50%, 75%): Los valores que dividen a los datos ordenados en cuatro partes iguales. 50% es el mismo que la mediana.

  6. Valor máximo (max): El valor más alto en cada columna.

Este método es extremadamente útil para obtener una visión rápida de la distribución de datos numéricos en un DataFrame. Por ejemplo, si df1 tiene columnas que representan datos como edad, ingresos, o cualquier medida cuantitativa, df1.describe() te dará un resumen estadístico de estas variables numéricas.

notation científica

Ahora como abras observado los números que estás viendo están en notación científica, lo cual puede hacer que sean difíciles de interpretar directamente. Aquí te explico cómo interpretar y mejorar la legibilidad de estos valores:

  1. Notación científica: Los números están en formato de notación científica para facilitar la lectura de valores grandes o pequeños. Por ejemplo, 1.754204e+00 significa 1.754204 y 1.339281e+00 significa 1.339281.

  2. Mejora de la legibilidad:

    • Para hacer estos números más legibles, puedes redondear los valores según la precisión que necesites.
    • Puedes formatear los números para que muestren un número específico de decimales utilizando el método round() en Python.

    Por ejemplo, si deseas redondear los valores a dos decimales, puedes hacerlo de la siguiente manera:

    df_describe = df1.describe().round(2)
    print(df_describe)

    Esto redondeará todos los valores en el resultado de describe() a dos decimales, lo que facilitará la lectura y comprensión de las estadísticas descriptivas.

  3. Interpretación de estadísticas:

    • count: Cantidad de valores no nulos en cada columna.
    • mean: Promedio de los valores en cada columna.
    • std: Desviación estándar de los valores en cada columna.
    • min: Valor mínimo encontrado en cada columna.
    • 25%, 50%, 75%: Percentiles correspondientes.
    • max: Valor máximo encontrado en cada columna.
  4. Consideraciones adicionales:

    • Si alguna columna tiene muchos valores faltantes (NaN), es posible que no aparezca en todas las secciones de la salida de describe().
    • Asegúrate de verificar que los valores mínimos y máximos sean coherentes con los datos esperados en cada columna. Valores extremadamente grandes o pequeños pueden indicar posibles errores o outliers en tus datos.

Al aplicar estos pasos, deberías poder mejorar la legibilidad de la salida de describe() y obtener una mejor comprensión de las estadísticas descriptivas de tu conjunto de datos.

Iterator

Ahora procederemos a guardar nuestro dataframe df en PostgreSQL. Sin embargo, el CSV es bastante grande, especialmente para entornos de prueba o, como en este caso, un entorno de aprendizaje. Por lo tanto, vamos a aplicar un método para dividir el CSV en lotes más pequeños, utilizando los iteradores de pandas.

Para lograr guardar un dataframe grande (df) desde un archivo CSV en PostgreSQL, dividiéndolo en lotes más pequeños utilizando los iteradores de pandas, puedes seguir estos pasos generales:

df_iter = pd.read_csv('nyt.csv', iterator=True, chunksize=100000)

El código df_iter = pd.read_csv('nyt.csv', iterator=True, chunksize=100000) carga un archivo CSV grande de manera eficiente mediante iteradores y dividirlo en lotes más pequeños (chunks).

Repasemos el código:

  1. pd.read_csv(): Esta es una función de pandas que se utiliza para leer datos desde archivos CSV y cargarlos en un DataFrame. En este caso, el archivo CSV especificado es 'nyt.csv'.

  2. iterator=True: Este parámetro se establece en True para indicar a pandas que queremos crear un iterador en lugar de cargar todo el archivo CSV de una sola vez en memoria. Los iteradores en pandas son útiles cuando trabajamos con archivos grandes porque nos permiten procesar el archivo en partes (chunks) más pequeñas.

  3. chunksize=100000: Este parámetro especifica el número de filas que queremos en cada chunk o lote del DataFrame. En este caso, cada chunk tendrá 100,000 filas del archivo CSV.

  4. Resultado (df_iter): Después de ejecutar esta línea de código, df_iter no es un DataFrame completo, sino un objeto iterable de tipo TextFileReader que nos permite iterar sobre los chunks del archivo CSV. Cada iteración sobre df_iter devolverá un DataFrame que contiene el siguiente chunk de datos del archivo CSV.

El objetivo final de este código es cargar el archivo 'nyt.csv' utilizando pandas, configura pandas para utilizar un iterador y divide el archivo en chunks de 100,000 filas. Esto es útil cuando necesitas procesar grandes volúmenes de datos de manera eficiente y no quieres cargar todo el archivo en memoria de una sola vez.

A continuación agregaremos el código df = next(df_iter) seguido de len(df) que se utiliza cuando estamos trabajando con un iterador generado por pandas (df_iter) que fue configurado previamente para leer un archivo CSV en chunks (lotes).

df = next(df_iter)
len(df)

El código df = next(df_iter) seguido de len(df) se utiliza cuando estamos trabajando con un iterador generado por pandas (df_iter) que fue configurado previamente para leer un archivo CSV en chunks (lotes). Aquí te explico qué hace cada línea:

  1. df = next(df_iter):

    • next() es una función de Python que se utiliza para obtener el próximo elemento de un iterador. En este caso, df_iter es un iterador creado previamente usando pd.read_csv(..., iterator=True, chunksize=100000).
    • Cuando llamamos a next(df_iter), pandas lee el siguiente chunk de datos del archivo CSV especificado ('nyt.csv') y lo carga en un DataFrame (df en este caso).
    • El DataFrame df contendrá las siguientes 100,000 filas del archivo CSV (o menos si es el último chunk).
  2. len(df):

    • Después de cargar el chunk en el DataFrame df, len(df) devuelve el número de filas en ese DataFrame.
    • Esto es útil para verificar cuántas filas se han cargado en cada chunk y para procesar los datos de manera incremental.

Estas dos líneas de código se utilizan para iterar a través de los chunks del archivo CSV cargado en partes más pequeñas utilizando un iterador de pandas. next(df_iter) carga el siguiente chunk del archivo, y len(df) nos da el número de filas en ese chunk específico. Esto es útil cuando necesitamos procesar grandes volúmenes de datos de manera incremental sin cargar todo el archivo en memoria de una sola vez.

Ahora volvemos agregar los pandas datetime para el dataframe modificado:

df.tpep_pickup_datetime = pd.to_datetime(df.tpep_pickup_datetime)
df.tpep_dropoff_datetime = pd.to_datetime(df.tpep_dropoff_datetime)

Ahora agreguemos un código que crea (o reemplaza) una tabla en la base de datos PostgreSQL especificada por engine, utilizando la estructura de columnas del DataFrame df.

df.head(n=0).to_sql(name='yellow_taxi_data', con=engine, if_exists='replace')

Sin embargo, dado que df.head(n=0) devuelve un DataFrame vacío, la tabla se crea sin datos inicialmente. Este enfoque es útil cuando primero queremos establecer la estructura de la tabla antes de insertar los datos reales.

El código df.head(n=0).to_sql(name='yellow_taxi_data', con=engine, if_exists='replace') se utiliza para crear una tabla en una base de datos PostgreSQL (o reemplazar una existente) utilizando los datos contenidos en el DataFrame df.

Revisemos el código:

  1. df.head(n=0):

    • df.head(n=0) es una función de pandas que devuelve un DataFrame vacío con la misma estructura de columnas que el DataFrame df original.
    • El parámetro n=0 significa que no se devolverán filas del DataFrame, solo la estructura de columnas. Esto es útil cuando queremos crear una tabla en la base de datos sin insertar datos inmediatamente.
  2. .to_sql(name='yellow_taxi_data', con=engine, if_exists='replace'):

    • .to_sql() es un método de pandas que permite guardar un DataFrame en una tabla de una base de datos SQL (como PostgreSQL).
    • name='yellow_taxi_data': Especifica el nombre de la tabla en la base de datos donde queremos guardar los datos. En este caso, la tabla se llamará 'yellow_taxi_data'.
    • con=engine: Especifica el motor de conexión a la base de datos. engine debe ser un objeto SQLAlchemy Engine o una cadena de conexión válida que identifique la base de datos y cómo conectarse a ella.
    • if_exists='replace': Especifica qué hacer si la tabla yellow_taxi_data ya existe en la base de datos.
    • 'replace': Reemplaza la tabla existente si ya está presente en la base de datos.

PGcli Tabla

Una vez creada la tabla, validemos que se haya creado correctamente ejecutando el siguiente comando \dt en la terminal de PGcli:

root@localhost:ny_taxi> \dt
+--------+------------------+-------+-------+
| Schema | Name | Type | Owner |
|--------+------------------+-------+-------|
| public | yellow_taxi_data | table | root |
+--------+------------------+-------+-------+

Como podemos observar, la tabla ha sido creada exitosamente. Ahora probemos con este otro comando \d yellow_taxi_data;

root@localhost:ny_taxi> \d yellow_taxi_data;
+-----------------------+-----------------------------+-----------+
| Column | Type | Modifiers |
|-----------------------+-----------------------------+-----------|
| index | bigint | |
| VendorID | bigint | |
| tpep_pickup_datetime | timestamp without time zone | |
| tpep_dropoff_datetime | timestamp without time zone | |
| passenger_count | double precision | |
| trip_distance | double precision | |
| RatecodeID | double precision | |
| store_and_fwd_flag | text | |
| PULocationID | bigint | |
| DOLocationID | bigint | |
| payment_type | bigint | |
| fare_amount | double precision | |
| extra | double precision | |
| mta_tax | double precision | |
| tip_amount | double precision | |
| tolls_amount | double precision | |
| improvement_surcharge | double precision | |
| total_amount | double precision | |
| congestion_surcharge | double precision | |
| Airport_fee | double precision | |
+-----------------------+-----------------------------+-----------+

Con esto validamos que la tabla contiene el esquema(schema) correcto el cual creamos previamente.

Carga de Datos

Ahora insertemos los datos en la tabla SQL existente, y usaremos el %time que mide cuánto tiempo tarda esta operación en ejecutarse.

%time df.to_sql(name='yellow_taxi_data', con=engine, if_exists='append')

El código %time df.to_sql(name='yellow_taxi_data', con=engine, if_exists='append') realiza varias acciones importantes en Python, específicamente utilizando la biblioteca pandas y SQL Alchemy para interactuar con una base de datos SQL.

  1. %time: Esto es un comando mágico en IPython (o Jupyter Notebook) que se utiliza para medir el tiempo que tarda en ejecutarse una única instrucción o una línea de código. %time antes de una línea de código indica que se debe medir el tiempo de ejecución de esa línea en particular.

  2. df.to_sql(...): Este es un método de pandas que se utiliza para escribir los datos de un DataFrame (df) en una tabla SQL. Los parámetros que se utilizan son:

    • name='yellow_taxi_data': Especifica el nombre de la tabla SQL donde se van a insertar los datos del DataFrame.
    • con=engine: Es el objeto de conexión a la base de datos SQLAlchemy, que generalmente se crea previamente con información de conexión a una base de datos SQL específica.
    • if_exists='append': Especifica cómo se manejarán los datos si la tabla ya existe en la base de datos. En este caso, 'append' significa que los datos del DataFrame se agregarán a la tabla existente, si ya está presente.
  3. Interpretación:

    • Cuando se ejecuta %time df.to_sql(...), IPython (o Jupyter) medirá cuánto tiempo tarda el proceso de escritura del DataFrame en la tabla SQL.
    • Este comando es útil para obtener una idea del rendimiento de la operación de escritura en la base de datos, lo cual es crucial cuando se trabaja con grandes volúmenes de datos.

PGcli count

Validemos de nuevo en PGcli y hagamos un conteo:

select COUNT(1) from yellow_taxi_data;
+--------+
| count |
|--------|
| 100000 |
+--------+
SELECT 1
Time: 0.016s

La consulta select COUNT(1) from yellow_taxi_data; es una instrucción SQL que se utiliza para contar el número de filas en la tabla llamada yellow_taxi_data.

  1. SELECT COUNT(1): Esta parte de la consulta indica que queremos seleccionar y contar las filas de la tabla. COUNT(1) es una función de agregación en SQL que cuenta el número de registros. Es común utilizar COUNT(1) o COUNT(*) para contar todas las filas de una tabla. La diferencia entre COUNT(1) y COUNT(*) es principalmente semántica y de rendimiento en algunos sistemas de bases de datos, pero generalmente ambas hacen lo mismo en la práctica.

  2. FROM yellow_taxi_data: Esto especifica la tabla de la cual queremos contar las filas. yellow_taxi_data es el nombre de la tabla en la base de datos de la que queremos obtener el recuento de filas.

  3. ;: El punto y coma al final de la consulta es opcional en muchos sistemas de gestión de bases de datos, pero se utiliza para indicar el final de la instrucción SQL.

Explicación del proceso:

  • Cuando ejecutas select COUNT(1) from yellow_taxi_data; en un entorno SQL (como PostgreSQL, MySQL, etc.), el motor de la base de datos ejecutará la consulta.
  • Contará todas las filas presentes en la tabla yellow_taxi_data.
  • El resultado de esta consulta será un solo número, que es el recuento total de las filas en la tabla yellow_taxi_data.

Uso común:

  • Esta consulta es útil para verificar la cantidad de datos que tenemos en una tabla específica.
  • Puede ser parte de un análisis inicial para entender el tamaño de los conjuntos de datos o para verificar la integridad de la carga de datos después de insertar o actualizar registros.

time

Ahora importemos la función time del módulo time en Python. Esta es una declaración de importación en Python que es útil para medir y gestionar el tiempo en nuestras aplicaciones y scripts.

from time import time
  1. from time: Esto indica que estamos importando algo del módulo time. En Python, time es un módulo estándar que proporciona funciones relacionadas con mediciones de tiempo y manejo de fechas.

  2. import time: Esta parte del código importa todo el módulo time.

Uso común:

  • time() es útil para medir el tiempo transcurrido entre dos puntos en un programa. Por ejemplo, podríamos almacenar el resultado de time() antes y después de una operación para calcular cuánto tiempo tardó esa operación en ejecutarse.
  • Es una función básica pero poderosa que se utiliza en muchos casos para medir el rendimiento de código, sincronizar acciones en programas, y más.

Bucle

A continuación utilizaremos un código python para manejar de manera eficiente grandes volúmenes de datos dividiéndolos en chunks y almacenándolos en una base de datos. La razón de su esto es permitir el procesamiento incremental de datos, facilitando la conversión y almacenamiento de registros en lotes manejables, lo cual es esencial para evitar problemas de memoria y mejorar el rendimiento. Además, incluye manejo de excepciones para asegurar la robustez del proceso y proporcionar información sobre el tiempo que toma cada inserción, lo que ayuda a monitorear y optimizar el rendimiento del sistema.

try:
while True:
t_start = time() # Captura el tiempo de inicio del bucle

try:
df = next(df_iter) # Obtiene el siguiente chunk de datos del iterador df_iter
except StopIteration:
print("No more data to process.")
break

# Convierte las columnas de fechas a tipos datetime si es necesario
df['tpep_pickup_datetime'] = pd.to_datetime(df['tpep_pickup_datetime'])
df['tpep_dropoff_datetime'] = pd.to_datetime(df['tpep_dropoff_datetime'])

try:
# Inserta el chunk actual del DataFrame en la tabla 'yellow_taxi_data' en la base de datos usando SQLAlchemy
df.to_sql(name='yellow_taxi_data', con=engine, if_exists='append')
except Exception as e:
print(f"Error inserting chunk into database: {e}")
continue

t_end = time() # Captura el tiempo al finalizar la inserción del chunk

# Imprime el tiempo que tomó insertar el chunk actual
print(f"Inserted another chunk, took {t_end - t_start:.3f} seconds")

except Exception as e:
print(f"An error occurred during processing: {e}")

Estructura del Código

El código está diseñado para leer datos en "chunks" y almacenarlos en una base de datos. Comencemos desde el principio.

try:
while True:

Manejo de Errores y Bucle Infinito

Comenzamos con un try y un while True. ¿Cuál es el propósito de esto?

El while True crea un bucle infinito, que seguirá ejecutándose hasta que se encuentre con una instrucción break. El try permite manejar errores que puedan surgir durante la ejecución del bucle.

Capturando el Tiempo de Ejecución

¿Qué ocurre al principio del bucle? Al inicio del bucle, se captura el tiempo usando t_start = time(). Esto es útil para medir el tiempo que toma procesar cada chunk de datos.

t_start = time()

Procesando los Datos

Luego, vemos otro try que intenta obtener el siguiente chunk de datos usando next(df_iter). ¿Qué es df_iter? Es un iterador que permite acceder a un conjunto de datos en partes más pequeñas. Si no hay más datos, se genera una excepción StopIteration.

try:
df = next(df_iter)
except StopIteration:
print("No more data to process.")
break

Manejo de Excepciones

¿Y qué pasa si no hay más datos? En ese caso, el código imprime un mensaje y utiliza break para salir del bucle, finalizando el proceso de manera ordenada.

Conversión de Fechas

Si hay datos, ¿qué sucede después? Si hay datos, el código convierte las columnas de fechas a un tipo datetime usando pd.to_datetime(). Esto es esencial para realizar cálculos y análisis correctamente.

df['tpep_pickup_datetime'] = pd.to_datetime(df['tpep_pickup_datetime'])
df['tpep_dropoff_datetime'] = pd.to_datetime(df['tpep_dropoff_datetime'])

Inserción en la Base de Datos

Luego, vemos que intenta insertar el chunk en una base de datos. ¿Cómo se hace eso? Utilizando df.to_sql() para insertar el DataFrame en la tabla yellow_taxi_data de la base de datos.

df.to_sql(name='yellow_taxi_data', con=engine, if_exists='append')

Manejo de Errores en la Inserción

¿Qué pasa si ocurre un error durante la inserción? Hay otro bloque try que captura excepciones durante la inserción. Si hay un error, imprime un mensaje y continúa con el siguiente chunk.

try:
df.to_sql(name='yellow_taxi_data', con=engine, if_exists='append')
except Exception as e:
print(f"Error inserting chunk into database: {e}")
continue

Tiempo de Inserción

¿Y qué hace con t_end? Después de intentar insertar el chunk, se captura el tiempo nuevamente y se imprime el tiempo que tomó la inserción.

t_end = time()
print(f"Inserted another chunk, took {t_end - t_start:.3f} seconds")

Manejo de Errores Globales

Finalmente, veo que hay un try que envuelve todo el bloque. ¿Por qué es necesario? Ese bloque try externo es crucial. Cubre todo el proceso y captura cualquier error que pueda surgir. Si algo falla, imprime un mensaje con la descripción del error.

except Exception as e:
print(f"An error occurred during processing: {e}")

Entonces, este código procesa datos en chunks, los inserta en una base de datos y maneja errores de manera robusta, todo mientras mide el rendimiento. Este es un enfoque muy eficiente para manejar grandes volúmenes de datos.

Código en detalle

Ahora revisemos detalladamente este código para que comprendamos en profundidad que hicimos:

Bucle principal con manejo de excepciones

try:
while True:
t_start = time() # Captura el tiempo de inicio del bucle
  • Inicia un bucle infinito while True.
  • Captura el tiempo de inicio del ciclo para medir la duración de la inserción del chunk.

Obtener el siguiente chunk de datos

        try:
df = next(df_iter) # Obtiene el siguiente chunk de datos del iterador df_iter
except StopIteration:
print("No more data to process.")
break
  • next(df_iter): Obtiene el siguiente chunk de datos del iterador.
  • StopIteration: Excepción que se lanza cuando el iterador df_iter no tiene más datos. Esto se captura para romper el bucle y detener el procesamiento.

Conversión de columnas de fechas

        df['tpep_pickup_datetime'] = pd.to_datetime(df['tpep_pickup_datetime'])
df['tpep_dropoff_datetime'] = pd.to_datetime(df['tpep_dropoff_datetime'])
  • Convierte las columnas tpep_pickup_datetime y tpep_dropoff_datetime a tipo datetime usando pd.to_datetime(). Esto asegura que las fechas se manejen correctamente en pandas.

Inserción de datos en la base de datos

        try:
df.to_sql(name='yellow_taxi_data', con=engine, if_exists='append')
except Exception as e:
print(f"Error inserting chunk into database: {e}")
continue
  • df.to_sql(): Inserta el chunk actual del DataFrame en la tabla yellow_taxi_data de la base de datos usando la conexión engine. El parámetro if_exists='append' asegura que los datos se añadan a la tabla existente.
  • Exception: Captura cualquier excepción que pueda ocurrir durante la inserción y continúa con el siguiente ciclo del bucle.

Captura del tiempo de finalización y reporte

        t_end = time()  # Captura el tiempo al finalizar la inserción del chunk
print(f"Inserted another chunk, took {t_end - t_start:.3f} seconds")
  • Captura el tiempo al finalizar la inserción del chunk.
  • Calcula y muestra el tiempo que tomó insertar el chunk actual en la base de datos.

Manejo general de excepciones

except Exception as e:
print(f"An error occurred during processing: {e}")
  • Captura cualquier excepción que pueda ocurrir durante el bucle principal y muestra un mensaje de error.

Resumen

  • El código procesa iterativamente chunks de datos de un iterador de DataFrame.
  • Convierte columnas de fechas a tipo datetime.
  • Inserta los datos en una base de datos usando SQLAlchemy.
  • Mide y muestra el tiempo de inserción de cada chunk.
  • Maneja excepciones tanto durante la obtención de datos como durante la inserción en la base de datos.
tip

Aprender Python para la ingeniería de datos es fundamental porque permite implementar y automatizar procesos complejos de manipulación y análisis de datos de manera lógica y eficiente. La lógica de programación en Python, con su sintaxis clara y estructuras de control robustas, facilita la creación de scripts y algoritmos que pueden procesar grandes volúmenes de datos, transformar datos en formatos útiles, y extraer información valiosa para la toma de decisiones. Además, su capacidad para integrar fácilmente con bases de datos y otras herramientas de procesamiento de datos hace que la lógica de programación en Python sea una herramienta poderosa para resolver problemas prácticos en ingeniería de datos.

Por ultimo validemos que efectivamente los datos fueron cargados en la tabla PostgreSQL en PGcli

root@localhost:ny_taxi> select COUNT(1) from yellow_taxi_data
+---------+
| count |
|---------|
| 2964624 |
+---------+
SELECT 1
Time: 0.211s

Como puedes observar, efectivamente todos los datos fueron cargados en la tabal PostgreSQL exitosamente.

Git

Ahora viene una de las partes más importantes de todo proyecto moderno y es el uso de Git para gestionar eficazmente el desarrollo de software. Permite a los desarrolladores colaborar en proyectos de manera organizada y controlada, manteniendo un historial completo de todos los cambios realizados en el código. Algunos aspectos clave del uso de Git en el desarrollo de software incluyen el control de versiones detallado mediante commits, que facilita la recuperación de versiones anteriores y proporciona trazabilidad sobre quién realizó cada cambio y cuándo.

Además, la capacidad de Git para gestionar ramas (branches) permite a los equipos trabajar simultáneamente en diferentes funcionalidades o correcciones sin interferencias, y luego fusionar estos cambios de manera ordenada en la rama principal del proyecto.

¿Qué es git?

Git es un sistema de control de versiones distribuido diseñado para manejar desde proyectos pequeños hasta muy grandes con velocidad y eficiencia. Permite a los desarrolladores colaborar en proyectos de software al mantener un historial de cambios, facilitando la coordinación entre diferentes personas que trabajan en el mismo código. Algunas características clave de Git incluyen ramificación (branching) y fusión (merging), lo que permite a los equipos trabajar en paralelo en diferentes características o versiones del software y luego combinar esos cambios de manera controlada.

Git es una herramienta fundamental para el desarrollo de software moderno, que ayuda a mantener el control sobre el código fuente y facilita la colaboración efectiva entre desarrolladores.

Git en la ingeniería de datos

En el contexto de la ingeniería de datos, Git sigue desempeñando un papel crucial, aunque su aplicación puede diferir ligeramente en comparación con su uso en el desarrollo de software tradicional. Aquí te explico cómo Git se utiliza típicamente en la ingeniería de datos:

  1. Control de versiones de scripts y código ETL: En la ingeniería de datos, es común tener scripts y código que realizan extracción, transformación y carga (ETL) de datos. Git se utiliza para controlar versiones de estos scripts, asegurando que todos los cambios estén registrados y sean reversibles si es necesario.

  2. Colaboración en pipelines de datos: Los equipos de ingeniería de datos trabajan en colaboración para construir y mantener pipelines de datos. Git permite a los equipos trabajar en ramas separadas para desarrollar nuevas características o modificar pipelines existentes sin interferir con el trabajo de otros. Luego, los cambios se pueden fusionar de manera controlada.

  3. Documentación y seguimiento de cambios: Git no solo controla el código, sino también la documentación asociada, como especificaciones de pipelines de datos, configuraciones y otros artefactos relevantes. Esto facilita el seguimiento de quién hizo qué cambio y por qué.

  4. Gestión de configuraciones: Git también se puede utilizar para controlar versiones de archivos de configuración utilizados en entornos de ingeniería de datos, como configuraciones de conexión a bases de datos, parámetros de ejecución de pipelines, etc.

  5. Automatización y CI/CD: Integrar Git con sistemas de integración continua (CI) y despliegue continuo (CD) es crucial en la ingeniería de datos moderna. Esto permite automatizar pruebas, validaciones y despliegues de cambios en pipelines de datos de manera controlada y eficiente.

Git en la ingeniería de datos proporciona un control de versiones robusto y facilita la colaboración entre equipos para desarrollar, mantener y desplegar pipelines de datos de manera efectiva y escalable.

Git vs GitHub

Git y GitHub son dos herramientas fundamentales en el ámbito del desarrollo de software moderno, aunque desempeñan roles diferentes pero complementarios:

  1. Git:

    • ¿Qué es Git?: Git es un sistema de control de versiones distribuido. Esto significa que permite a los desarrolladores llevar un registro de los cambios en el código fuente a lo largo del tiempo.
    • Funcionamiento: Git gestiona estos cambios en forma de snapshots (instantáneas) del estado del proyecto en diferentes puntos en el tiempo. Esto se hace mediante la creación de commits, que son como marcadores de tiempo que registran los cambios realizados.
    • Características clave: Ramificación (branching) y fusión (merging) son características esenciales de Git. Las ramas permiten a los desarrolladores trabajar en paralelo en diferentes características o versiones del software, y las fusiones permiten combinar estos cambios de manera controlada.
  2. GitHub:

    • ¿Qué es GitHub?: GitHub es una plataforma de alojamiento de código basada en la nube que utiliza Git para el control de versiones. Permite a los desarrolladores almacenar, colaborar y gestionar sus proyectos de software de manera eficiente.
    • Funcionalidades: Además del control de versiones, GitHub proporciona herramientas para la gestión de proyectos, seguimiento de problemas (issue tracking), colaboración a través de pull requests, revisión de código (code review), integración con servicios de CI/CD, entre otros.
    • Colaboración y comunidad: GitHub fomenta la colaboración abierta y la contribución al código abierto. Permite a los desarrolladores y equipos trabajar de manera colaborativa en proyectos públicos y privados, facilitando la transparencia y el aprendizaje compartido en la comunidad de desarrollo.
  3. Relación entre Git y GitHub:

    • Git es el sistema de control de versiones subyacente utilizado localmente por los desarrolladores para gestionar cambios en el código.
    • GitHub proporciona una plataforma centralizada en la nube donde se puede almacenar y gestionar proyectos Git de forma colaborativa, haciendo que el desarrollo de software sea más social y accesible.
    • Los desarrolladores utilizan Git para realizar commits y gestionar ramas localmente en sus máquinas, y luego sincronizan su código con GitHub para colaborar con otros desarrolladores, realizar seguimiento de problemas, revisar código y desplegar software de manera automatizada.

Git y GitHub forman una combinación poderosa que no solo facilita el desarrollo y la gestión de software, sino que también promueve la colaboración, la transparencia y la comunidad dentro de la industria del desarrollo de software.

GitHub

Lo primero que debemos hacer es crear una cuenta GitHub si aun no la tenemos desde aquí Github Después de haber creado nuestra cuenta o si ya tienes una procederemos a crear un repositorio al que llamaremos Boost. Después de creado el proyecto verás en pantalla una línea de código similar a esta:

echo "# Boost" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin git@github.com:xploiterx/Boost.git
git push -u origin main

Este conjunto de comandos de terminal configura un nuevo repositorio Git local y lo sincroniza con un repositorio remoto en GitHub. Veamos el paso a paso de cada comando:

  1. echo "# Boost" >> README.md

    • Este comando añade un encabezado (# Boost-1) al archivo README.md. El >> se utiliza para redirigir la salida y añadir contenido al final del archivo sin sobrescribir el contenido existente.
  2. git init

    • Inicia un nuevo repositorio Git en el directorio actual. Esto establece un control de versiones local para el proyecto.
  3. git add README.md

    • Agrega el archivo README.md al área de preparación de Git. Esto indica que los cambios en este archivo se incluirán en el próximo commit.
  4. git commit -m "first commit"

    • Crea un commit con los archivos que están en el área de preparación (README.md en este caso). El mensaje -m proporciona una descripción corta y significativa del commit, en este caso, "first commit" (primer commit).
  5. git branch -M main

    • Renombra la rama principal de master a main. Este cambio se está haciendo para alinearse con las prácticas actuales de nombrar la rama principal del repositorio en GitHub.
  6. git remote add origin git@github.com:xploiterx/Boost.git

    • Añade un repositorio remoto llamado origin y especifica la URL del repositorio remoto en GitHub (git@github.com:xploiterx/Boost-1.git). origin es el nombre convencionalmente utilizado para referirse al repositorio remoto principal.
  7. git push -u origin main

    • Sube (push) los cambios locales al repositorio remoto (origin). -u establece la rama local main para realizar un seguimiento de la rama remota main, lo que significa que en futuros git push y git pull, Git sabe a qué rama remota y local hacer referencia.

Estos comandos configuran un nuevo repositorio Git local, preparan un archivo README.md inicial, y sincronizan este repositorio con un repositorio remoto en GitHub en este caso Boost-1.

Copiar Project-2 a Boost Antes de continuar, vamos a copiar nuestro Project-1 a la nueva carpeta Boost. Desde la terminal, nos ubicaremos en la carpeta repos. Si estás ubicado en repos/Project-1, simplemente usa el comando cd ..` para retroceder un nivel.

repos/Project-1
cd ..
repos

Ahora que estas ubicado en repos ejecutaremos el siguiente comando:

sudo cp -r Project-1/ Boost/
sudo chown -R z2h:z2h Boost/

El comando cp -r Project-1/ Boost/ en un entorno en Linux se utiliza para copiar directorios y su contenido de manera recursiva. Veamos:

  • cp: Es el comando utilizado para copiar archivos y directorios.
  • -r: Es una opción que indica que la copia debe ser recursiva, es decir, debe copiar todos los archivos y subdirectorios dentro del directorio de origen.
  • Project-1/: Es el directorio de origen que se desea copiar.
  • Boost/: Es el directorio de destino donde se desea copiar el contenido de Project-2/.

Entonces, cp -r Project-1/ Boost/ copiará el directorio Project-1 y todo su contenido (incluyendo subdirectorios y archivos) dentro del directorio Boost.

Esto es lo que sucede durante la ejecución de este comando:

  1. Si Boost-1 existe:

    • Project-2 y su contenido serán copiados dentro de Boost, creando una nueva subcarpeta llamada Project-1 dentro de Boost`.
    • La estructura de directorios resultante será Boost-1/Project-1/.
  2. Si Boost no existe:

    • Se producirá un error indicando que el destino no existe, ya que cp no crea directorios de destino que no existen.

gitignore

Ahora solo nos falta un último paso antes de comenzar la sincronización del proyecto con GitHub, que es agregar el archivo .gitignore en la carpeta Module-2.

El archivo .gitignore es un archivo de configuración utilizado por Git para especificar qué archivos y carpetas debe ignorar cuando se realiza el seguimiento de cambios en un repositorio. Le indica a Git qué archivos y directorios no deben incluirse en las actualizaciones o en el control de versiones.

Importancia del gitignore

  1. Evita archivos innecesarios en el repositorio: Muchos proyectos contienen archivos que no deben estar bajo control de versiones, como archivos de configuración local, archivos temporales, logs, archivos binarios generados, entre otros. El .gitignore permite evitar que estos archivos se incluyan accidentalmente en el repositorio.

  2. Mantiene el repositorio limpio y organizado: Al especificar qué archivos y carpetas deben ignorarse, se evita la contaminación del repositorio con archivos irrelevantes. Esto ayuda a mantener el repositorio más legible y facilita la colaboración entre desarrolladores.

  3. Evita conflictos y problemas de rendimiento: Ignorar archivos no esenciales reduce la cantidad de datos que Git necesita gestionar y transferir, lo que puede mejorar el rendimiento de las operaciones de Git, especialmente en proyectos grandes.

  4. Personalización según el entorno y herramientas: El .gitignore puede personalizarse según las necesidades del proyecto y las herramientas utilizadas. Por ejemplo, pueden existir diferencias en los archivos a ignorar entre diferentes sistemas operativos, entornos de desarrollo o herramientas específicas.

Cómo funciona

  • Patrones de coincidencia: Los archivos y carpetas se especifican utilizando patrones de coincidencia que pueden incluir caracteres comodín (*, ?), negaciones (!), y otros caracteres para especificar archivos específicos o tipos de archivos.

  • Ubicación: El archivo .gitignore se coloca en la raíz del repositorio Git o en subdirectorios específicos para controlar qué archivos se ignoran en esas ubicaciones.

Gitignore es fundamental para la gestión eficiente de versiones en Git, ayudando a mantener el repositorio limpio, organizado y libre de archivos no deseados, lo cual es crucial para un desarrollo de software efectivo y colaborativo.

Ahora revisemos este Link https://www.toptal.com/developers/gitignore/api/python

En el link encontrarás un archivo .gitignore específico para proyectos Python es extremadamente útil porque excluye archivos y carpetas que normalmente no deben ser versionados ni compartidos con otros colaboradores en un repositorio Git. Aquí te explico algunas secciones importantes y por qué son relevantes:

  1. Byte-compiled / optimized / DLL files:

    • Ignora los archivos generados por Python cuando compila o optimiza código, como __pycache__/, *.pyc, *.pyo, y *$py.class. Estos archivos son específicos de la implementación de Python y no son necesarios en el repositorio.
  2. Distribution / packaging:

    • Excluye directorios y archivos relacionados con la distribución y empaquetado de proyectos Python, como .Python, build/, dist/, eggs/, etc. Estos directorios contienen artefactos generados durante la construcción de paquetes Python y pueden ser regenerados fácilmente.
  3. Unit test / coverage reports:

    • Ignora archivos y directorios generados durante pruebas unitarias y reportes de cobertura, como htmlcov/, .tox/, .coverage, etc. Estos archivos son temporales y específicos de herramientas de testing como pytest, coverage, etc.
  4. Django, Flask, Scrapy, Jupyter, IPython:

    • Excluye archivos y directorios específicos de frameworks y herramientas como Django (*.log, local_settings.py, db.sqlite3), Flask (instance/, .webassets-cache), Scrapy (.scrapy), Jupyter Notebook (.ipynb_checkpoints), y IPython (profile_default/, ipython_config.py). Estos archivos son generados localmente y pueden variar entre entornos de desarrollo.
  5. Environment / Virtual environments:

    • Ignora directorios y archivos relacionados con entornos virtuales (.env, .venv, env/, venv/, etc.). Estos directorios contienen las dependencias específicas del entorno de desarrollo y no deben ser compartidos con otros desarrolladores.
  6. IDE / Editor specific:

    • Excluye configuraciones específicas de ciertos IDEs como PyCharm (.idea/), Spyder (.spyderproject, .spyproject), Rope (.ropeproject), entre otros. Estos archivos y directorios son configuraciones locales de herramientas de desarrollo y no son necesarios para otros colaboradores.

Este archivo .gitignore permite mantener limpio y organizado un repositorio Git, evitando problemas al no versionar archivos innecesarios y garantizando que solo los archivos esenciales del proyecto sean compartidos y controlados adecuadamente en el sistema de control de versiones.

Vamos a crear nuestro .gitignore en la siguiente ruta:


Boost/Project-1/Module-1/.gitignore

Ahora copiamos el contenido del link en este archivo y agregaremos las siguientes líneas de código al final del .gitignore

# End of https://www.toptal.com/developers/gitignore/api/python

data/nyt.csv
data/yellow_tripdata_2024-01.parquet
nyc-tlc-data

Estas líneas que siguen después del comentario son añadidas manualmente o específicas del proyecto, y no forman parte de la configuración estándar que agregamos. Son archivos y directorios específicos incluidos debido a su gran tamaño, ya que GitHub tiene un límite de tamaño para los archivos individuales. Es importante tener en cuenta estas excepciones para asegurar que el repositorio pueda ser clonado y utilizado correctamente por otros colaboradores y desarrolladores.

Una vez completado el paso anterior abramos a la terminal de VSCode y probemos algunos de estos comandos en el siguiente orden:

echo "# Boost-1" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin git@github.com:tu_usuario/Boost-1.git # Reemplaza esta línea
git push -u origin main

git init

Si ves este log la salida de la terminal después de ejecutar el comando git init.

z2h@devbox1-z2h-platform-67958b9b48-fn2ck ~/r/boost-1> git init
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint: git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint: git branch -m <name>
Initialized empty Git repository in /home/z2h/repos/boost-1/.git/

Veamos lo que está sucediendo:

  1. git init: Este comando inicializa un nuevo repositorio Git en el directorio actual (~/r/boost-1 en este caso). Después de ejecutar este comando, Git crea un directorio oculto llamado .git en el directorio ~/r/boost-1, que contiene todos los archivos necesarios para el repositorio Git.

  2. Hint sobre el nombre de la rama inicial: Git muestra un mensaje que indica que está utilizando 'master' como el nombre de la rama inicial por defecto. Este mensaje es una advertencia y sugiere que el nombre de la rama inicial podría cambiar en el futuro. También proporciona información sobre cómo configurar un nombre de rama inicial diferente para todos los nuevos repositorios mediante el comando git config --global init.defaultBranch <name>.

  3. Sugerencias de nombres comunes para la rama inicial: Git ofrece sugerencias sobre nombres comunes que se utilizan en lugar de 'master' para la rama inicial, como 'main', 'trunk' y 'development'. Estos nombres son populares en diferentes proyectos y organizaciones.

  4. Posibilidad de renombrar la rama inicial: Se menciona que la rama recién creada (en este caso, 'master') puede ser renombrada utilizando el comando git branch -m <name>.

  5. Mensaje de confirmación: Finalmente, se confirma que el repositorio Git ha sido inicializado correctamente en el directorio /home/z2h/repos/boost/.git/.

git add . git add . es un comando utilizado en Git para añadir todos los archivos modificados y nuevos en el directorio de trabajo al área de preparación (también conocida como el "staging area"). La "staging area" es un espacio intermedio donde se preparan los cambios antes de confirmarlos definitivamente en el repositorio Git.

Cuando ejecutas git add ., Git examina el directorio actual y subdirectorios para detectar cualquier cambio en los archivos. Esto incluye archivos modificados (cambios en archivos que ya están bajo control de versiones), archivos nuevos (archivos que aún no han sido añadidos al control de versiones) y archivos eliminados (archivos que han sido eliminados del directorio de trabajo).

Después de ejecutar git add ., los archivos detectados se añaden al área de preparación. Esto significa que están listos para ser incluidos en el próximo commit que se realice con git commit.

Es importante tener en cuenta que git add . añade todos los cambios en el directorio actual y sus subdirectorios. Si solo quieres añadir archivos específicos, puedes utilizar git add nombre_del_archivo o git add ruta/al/directorio.

La diferencia entre git add . y git add README.md radica en el alcance de los archivos que se añaden al área de preparación (staging area).

  1. git add .: Este comando agrega al área de preparación todos los archivos modificados y nuevos en el directorio actual y sus subdirectorios. Es útil cuando quieres incluir todos los cambios realizados en múltiples archivos en una sola operación.

  2. git add README.md: En cambio, este comando específico añade únicamente el archivo README.md o el archivo que le indiquemos al área de preparación. Se utiliza cuando deseas preparar un archivo específico para ser incluido en el próximo commit.

**git commit

z2h@devbox1-z2h-platform-67958b9b48-fn2ck ~/r/boost (master)> git commit -m "first commit"
On branch master

Initial commit

Untracked files:
(use "git add <file>..." to include in what will be committed)
Module-1/
README.md

nothing added to commit but untracked files present (use "git add" to track)

git remote

z2h@devbox1-z2h-platform-67958b9b48-fn2ck ~/r/Boost (main)> git push -u origin main
Enumerating objects: 21, done.
Counting objects: 100% (21/21), done.
Delta compression using up to 8 threads
Compressing objects: 100% (16/16), done.
Writing objects: 100% (17/17), 21.13 KiB | 5.28 MiB/s, done.
Total 17 (delta 4), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (4/4), completed with 1 local object.
To https://github.com/carlsvlezx/Boost-1.git
676cd6c..1ab8871 main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.

git push

En este punto es necesario iniciar sesión en nuestra cuenta de GitHub. Puedes hacerlo a través de dos opciones. Una vez iniciada la sesión con tu cuenta de GitHub, podemos hacer el git push.

z2h@devbox1-z2h-platform-67958b9b48-fn2ck ~/r/Boost (main)> git push -u origin main
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 468 bytes | 468.00 KiB/s, done.
Total 5 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To https://github.com/carlsvlezx/Boost.git
1ab8871..127d369 main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.

Con esto hemos publicado el repositorio. Puedes comprobarlo refrescando el navegador, donde podrás ver el contenido añadido al repositorio en GitHub