<?xml version="1.0" encoding="UTF-8" standalone="no"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" version="2.0">

<channel>
	<title>Marcos Torregrosa</title>
	<atom:link href="https://n4gash.com/feed/" rel="self" type="application/rss+xml"/>
	<link>https://n4gash.com/</link>
	<description>Data Engineering, design and hobby!</description>
	<lastBuildDate>Fri, 17 Apr 2026 00:12:29 +0000</lastBuildDate>
	<language>es</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://i0.wp.com/n4gash.com/wp-content/2022/11/cropped-android-chrome-512x512-1.png?fit=32%2C32&amp;ssl=1</url>
	<title>Marcos Torregrosa</title>
	<link>https://n4gash.com/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">111643484</site>	<xhtml:meta content="noindex" name="robots" xmlns:xhtml="http://www.w3.org/1999/xhtml"/><item>
		<title>Data Mesh: gestión de datos descentralizada</title>
		<link>https://n4gash.com/arquitectura-data-mesh-gestion-de-datos-descentralizada/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=arquitectura-data-mesh-gestion-de-datos-descentralizada</link>
		
		<dc:creator><![CDATA[Marcos Torregrosa]]></dc:creator>
		<pubDate>Mon, 23 Oct 2023 23:05:58 +0000</pubDate>
				<category><![CDATA[Cloud Data]]></category>
		<category><![CDATA[Data Architecture]]></category>
		<category><![CDATA[Data Engineering]]></category>
		<category><![CDATA[Data Mesh]]></category>
		<category><![CDATA[Data virtualization]]></category>
		<guid isPermaLink="false">https://www.n4gash.com/?p=3834</guid>

					<description><![CDATA[<p>El paradigma Data Mesh está ganando adeptos como un nuevo enfoque para la gestión de datos en las organizaciones. A diferencia de las arquitecturas monolíticas como Data Warehouse o Data Lake, el Data Mesh se enfoca en la descentralización, democratización y distribución de la propiedad de los datos entre las áreas de la organización. Las áreas de negocio se responsabilizan del ciclo de vida completo de sus propios datos, así como exponerlos al resto de la organización, sin depender de un equipo técnico central. Este nuevo enfoque facilita que se superen las principales dolencias de las arquitecturas de datos centralizadas:…</p>
<p>La entrada <a href="https://n4gash.com/arquitectura-data-mesh-gestion-de-datos-descentralizada/">Data Mesh: gestión de datos descentralizada</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>El paradigma <strong>Data Mesh</strong> está ganando adeptos como un nuevo enfoque para la gestión de datos en las organizaciones. A diferencia de las arquitecturas monolíticas como<em> Data Warehouse </em>o<em> Data Lake, </em>el <em>Data Mesh</em> se enfoca en la <strong>descentralización, democratización y distribución de la propiedad de los datos entre las áreas de la organización</strong>.  Las áreas de negocio se responsabilizan del ciclo de vida completo de sus propios datos, así como exponerlos al resto de la organización, sin depender de un equipo técnico central. Este nuevo enfoque facilita que se superen las principales dolencias de las arquitecturas de datos centralizadas: cuellos de botella, dependencia de terceros y pérdida de oportunidades por falta de conocimiento. </p>



<p>Cuando hablamos de <em>Data mesh</em> podemos decir que se ha trasladado la filosofía de la arquitectura de microservicios de software al mundo de los datos. Al igual que en el desarrollo software los microservicios son entidades independientes que se exponen para ser consumidos sin mostrar sus entresijos, en <em>Data mesh</em> los<em> <strong>Data as a Product</strong> (DaaP)</em> se convierten en microservicios de datos para su consumo por otros equipos en la organización en forma de APIs o BBDD. Podríamos decir que los <strong>Data products </strong>son datamarts vitaminados con dos grandes diferencias respecto a los tradicionales: los encargados de crearlos y mantenerlos son las unidades de negocio a las que pertenecen los datos, no equipos técnicos centrales; y por otro lado, son entidades autónomas que integran la seguridad, reglas de calidad, gobierno, consumo, etc. Para asumir esta nueva responsabilidad, los áreas de negocio se dotan de recursos técnicos con conocimientos en data capaces de crear y mantener los productos de datos.</p>



<p>El Data Mesh se inspira en principios similares al <strong>«Domain Driven Design» </strong>DDD al enfatizar la importancia de comprender a fondo el dominio o área de negocio que se está tratando de abordar. En lugar de tratar exclusivamente los datos como un recurso aislado, el Data Mesh reconoce la necesidad de involucrar a expertos en el dominio y diseñar soluciones de datos que se ajusten a las necesidades y desafíos específicos de ese dominio. Data Mesh se alinea con una perspectiva «domain-driven», pero no es una implementación directa del Domain Driven Design.</p>



<figure class="wp-block-pullquote has-palette-color-2-color has-text-color" style="border-style:none;border-width:0px;border-radius:0px;font-style:normal;font-weight:100"><blockquote><p><strong>Data Mesh propone una gestión de datos descentralizada</strong> federando la  propiedad de los mismos a las unidades de negocio a través de «dominios de datos»</p></blockquote></figure>



<p>La gestión de datos ha experimentado una gran evolución en las últimas décadas, y esto ha sido posible gracias a la creciente cantidad de información generada y al surgimiento de nuevas tecnologías.</p>



<p><strong>Data Warehouse</strong> <strong>(finales de 1980)</strong> fue uno de los primeros modelos de arquitectura de datos en surgir. Se trata de un sistema centralizado diseñado para almacenar y gestionar grandes cantidades de datos relacionales, permitiendo la consolidación y el análisis de información de diferentes fuentes. Sin embargo, este modelo se enfrenta a limitaciones, como la complejidad y el tiempo requerido para la integración de nuevos datos.</p>



<p>El surgimiento de <strong>Data Lake (2000) </strong>trajo un nuevo enfoque en la arquitectura de datos. Este modelo se basa en el almacenamiento no estructurado de datos en su formato original, lo que permite una mayor flexibilidad y escalabilidad. Además, un <em>Data Lake </em>permite un procesamiento en paralelo de grandes cantidades de datos, mejorando la velocidad de procesamiento y la eficiencia. La evolución dentro de este paradigma fue trasladar la infraestructura <em>on-premise</em> a la nube, por lo que hablaríamos de <strong>Cloud Data Lake (2011)</strong>.</p>



<p>El concepto de <strong>Data Lakehouse</strong> <strong>(finales del 2010)</strong> surge como híbrido entre los <em>Data Warehouse</em> y <em>Data Lake.</em> Este modelo combina la capacidad de gestión, estructura, gobierno y análisis de datos relacionales de los <em>Data Warehouse</em> con la capacidad, flexibilidad y escalabilidad de los <em>Data Lake</em>. Se crea una capa semántica entre los datos estructurados y no-estructurados almacenados en un repositorio (<em>data lake</em>) para consumirlos como si se tratase de una DWH.</p>



<p><strong>Data Mesh</strong> <strong>(2019)</strong> es un enfoque más reciente en la gestión de datos que se basa en la descentralización y la colaboración. Este modelo promueve la gestión de datos como un producto, con equipos de negocio responsables de sus propios conjuntos de datos, otorgando mayor flexibilidad, escalabilidad en la gestión de datos y reduciendo el<em> time-to-market</em> de las iniciativas de datos al desaparecer las dependencias con otros actores (equipos centrales de datos). A finales de esta década también surge otro paradigma, <strong><a href="https://www.n4gash.com/2023/que-es-una-arquitectura-data-fabric/">Data Fabric</a></strong>, que a diferencia de <em>Data Mesh</em>, da continuidad a la arquitectura de datos centralizada pero poniendo foco en la tecnología que facilita el consumo de los datos implementando una capa de abstracción por encima de todos los orígenes de datos (virtualización de datos). </p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/06/image.png?ssl=1"><img data-recalc-dims="1" fetchpriority="high" decoding="async" width="1024" height="405" data-attachment-id="5409" data-permalink="https://n4gash.com/arquitectura-data-mesh-gestion-de-datos-descentralizada/image-103/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/06/image.png?fit=1510%2C597&amp;ssl=1" data-orig-size="1510,597" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/06/image.png?fit=1024%2C405&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/06/image-1024x405.png?resize=1024%2C405&#038;ssl=1" alt="Evolución arquitectura de datos hasta Data Mesh y Data Fabric" class="wp-image-5409" title="Evolución arquitectura de datos hasta Data Mesh y Data Fabric" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/06/image.png?resize=1024%2C405&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/06/image.png?resize=300%2C119&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/06/image.png?resize=768%2C304&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/06/image.png?w=1510&amp;ssl=1 1510w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Evolución arquitectura de datos hasta Data Mesh y Data Fabric</figcaption></figure>



<p>En conclusión, la arquitectura de datos ha evolucionado desde modelos centralizados y estructurados hasta enfoques más flexibles y descentralizados. Cada modelo tiene sus fortalezas y limitaciones, y la elección del modelo adecuado dependerá de las necesidades y objetivos específicos de cada organización. Sin embargo, es seguro decir que la arquitectura de datos continuará evolucionando a medida que surjan nuevas tecnologías y desafíos.</p>



<h2 class="wp-block-heading">Data Mesh</h2>



<p>En un <strong>Data Mesh</strong> los datos se organizan en <strong>Data Domains</strong> que representan áreas de negocio específicas, cada uno representa un conjunto de datos relacionados con un área de negocio o función específica de la organización. Por ejemplo, en un banco, podríamos tener <em>Data Domains</em> para transacciones bancarias, préstamos, seguridad, marketing, etc. Cada dominio es gestionado por un<strong> equipo de dominio </strong>autónomo, responsable de la calidad y disponibilidad de los datos compuesto por expertos en el área y responsables de la recopilación, transformación y gestión de los datos dentro de ese dominio. Estos equipos son independientes y tienen la propiedad de sus datos. Tienen la responsabilidad de garantizar la calidad y la disponibilidad de los datos en su dominio. Crean <strong>Data Products</strong>, subconjuntos de datos listos para su consumo que se presentan en forma de APIs, bases de datos, o servicios. Los <em>Data Products</em> son diseñados para ser utilizados por otros equipos en la organización. Para facilitar el acceso y su consumo se pueden implementar capas de <strong>servicios de datos.</strong> Estos servicios proporcionan una interfaz estandarizada para acceder a los datos en los<em> Data Domains. </em>Los equipos de dominio pueden exponer sus Data Products a través de estos servicios con una <strong>infraestructura </strong>que puede incluir tecnologías como Data Lakes, Data Warehouses, sistemas de streaming y es compartida y administrada de manera centralizada. La <strong>gobernanza </strong>y seguridad de los datos se aplican de manera uniforme en toda la organización para garantizar la integridad y privacidad de los datos.</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://learn.microsoft.com/es-es/azure/cloud-adoption-framework/scenarios/cloud-scale-analytics/architectures/reference-architecture-data-mesh"><img data-recalc-dims="1" decoding="async" width="1024" height="647" data-attachment-id="3837" data-permalink="https://n4gash.com/arquitectura-data-mesh-gestion-de-datos-descentralizada/image-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/01/image-3.png?fit=1118%2C706&amp;ssl=1" data-orig-size="1118,706" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-3" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/01/image-3.png?fit=1024%2C647&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/01/image-3-1024x647.png?resize=1024%2C647&#038;ssl=1" alt="Diagrama de arquitectura Data Mesh" class="wp-image-3837" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/01/image-3.png?resize=1024%2C647&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-3.png?resize=300%2C189&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-3.png?resize=768%2C485&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-3.png?w=1118&amp;ssl=1 1118w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Diagrama de arquitectura Data Mesh</figcaption></figure>



<p><em>Por ejemplo, en una empresa de comercio electrónico divide sus datos en Data Domains, como historiales de compras, preferencias de los clientes, inventario, y análisis de marketing. Cada Data Domain es gestionado por un equipo de dominio compuesto por expertos en esas áreas, quienes se encargan de recopilar, cocinar y gestionar los datos en su dominio. Estos equipos crean Data Products, como recomendaciones personalizadas para los clientes o análisis de tendencias de compras, que son compartidos a través de Data Services estandarizados para su uso en toda la organización. La infraestructura de datos subyacente se gestiona centralmente para garantizar la eficiencia y la seguridad, y las políticas de gobernanza y seguridad de datos se aplican de manera consistente en toda la empresa para proteger la integridad y la privacidad de la información.</em></p>



<h3 class="wp-block-heading has-medium-font-size" style="font-style:normal;font-weight:100">Principios de Data Mesh</h3>



<p>Una arquitectura <em>Data Mesh</em> orbita alrededor de estos cuatro principios:</p>



<ol class="wp-block-list">
<li><strong>Propiedad de datos</strong> <strong>descentralizada</strong>: Las áreas de negocio <em>aka </em><strong>dominios </strong>tienen la propiedad y responsabilidad de los datos que generan y utilizan, lo que les permite tomar decisiones informadas sobre su uso y gestión. Influenciado por el concepto de Diseño orientado al Dominio o <em>Domain-driven design</em> (<em>DDD</em>). </li>



<li><strong>Diseño basado en productos: </strong>Los datos son tratados como productos <em>aka </em><strong>Data as a Product (DaaP)</strong>, con equipos de desarrollo de datos responsables de su calidad, seguridad y valor. Los <em>DaaPs </em>se exponen al resto de la organización para su consumo.</li>



<li><strong>Plataforma de auto servicio:</strong> infraestructura transversal que articula la democratización del uso de los <em>DaaPs</em>, así como la gestión del ciclo de vida completo de cada uno. Este framework de componentes técnicos se gestiona desde un equipo central para facilitar la adopción del Data Mesh a las unidades de negocio.</li>



<li><strong>Gobierno federado: </strong>crea una capa de gobierno federada entre los dominios con un equipo formado por representantes de cada uno. Equilibra entre la autonomía y agilidad de los dominios frente a la interoperabilidad del propio <em>data mesh</em>.</li>
</ol>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/01/image-41.png?ssl=1"><img data-recalc-dims="1" decoding="async" width="1024" height="201" data-attachment-id="4110" data-permalink="https://n4gash.com/arquitectura-data-mesh-gestion-de-datos-descentralizada/image-41/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/01/image-41.png?fit=1434%2C281&amp;ssl=1" data-orig-size="1434,281" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-41" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/01/image-41.png?fit=1024%2C201&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/01/image-41-1024x201.png?resize=1024%2C201&#038;ssl=1" alt="Principios de un Data Mesh" class="wp-image-4110" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/01/image-41.png?resize=1024%2C201&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-41.png?resize=300%2C59&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-41.png?resize=768%2C150&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-41.png?w=1434&amp;ssl=1 1434w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Principios de un Data Mesh</figcaption></figure>



<h2 class="wp-block-heading" id="daap">¿Que es un Data as a Product? </h2>



<p>En lugar de tratar los datos como un recurso interno, los datos se entregan como un producto independiente por parte de cada <em>data domain</em> dedicado a su desarrollo y mantenimiento. Como adelantábamos en la introducción del artículo, se trata de microservicios de datos y se denominan <strong>Data as a Product</strong> o <strong>DaaP</strong>. Para que un conjunto de datos se considere un <em>producto de datos</em> debe cumplir los siguientes atributos:</p>



<ol class="wp-block-list">
<li><strong>Secure</strong>: Los datos deben estar protegidos contra accesos no autorizados y cumplir con las regulaciones y estándares de seguridad aplicables.</li>



<li><strong>Discoverable</strong>: Los datos deben ser fácilmente descubribles y accesibles para los usuarios autorizados. Cualquier usuario debe ser capaz de poder buscar y encontrar los Data as a Product creados en la organización para identificar cual de ellos debe utilizar, o si se diera el caso, solicitar al <em>data domain</em> propietario de los datos que necesita, la creación de uno nuevo.</li>



<li><strong>Addressable</strong>: Los datos deben tener una identidad única y una dirección para facilitar su acceso y uso. </li>



<li><strong>Understandable</strong>: Los datos deben ser comprensibles para los usuarios y contener metadatos para facilitar su uso.</li>



<li><strong>Trustworthy</strong>: Los datos deben ser precisos y confiables. Se pueden definir SLAs o como se refiere Zhamak en su literatura, SLOs (objetivos de nivel de servicio, o en inglés <em>service level objectives</em>) para determinar el nivel de disponibilidad y calidad de un Data as a Product. Por ejemplo, según la propia definición de Google, un <a href="https://cloud.google.com/service-mesh/docs/observability/design-slo" target="_blank" rel="noreferrer noopener">SLO </a>puede medir la latencia y la disponibilidad.</li>



<li><strong>Natively accessible</strong>: Los datos deben ser fácilmente accesibles en su formato nativo, sin la necesidad de transformaciones previas.</li>



<li><strong>Interoperable</strong>: Los datos deben ser interoperables con otros sistemas y formatos. Además, muchos Data as a Product consumidores se generan como DaaP derivados cruzando varios DaaP, por lo que es necesario que las claves o identificadores sean cross a toda la organización.</li>



<li><strong>Valuable</strong>: Los datos deben tener un valor comprobado para la organización y ser utilizados para tomar decisiones y mejorar los procesos de negocio.</li>
</ol>



<h2 class="wp-block-heading">Comparativa entre arquitecturas de datos</h2>



<figure class="wp-block-table is-style-stripes"><table class="has-light-green-cyan-background-color has-background"><thead><tr><th>Data Mesh vs Data Lake vs Data Warehouse</th><th>Data Mesh vs Data Fabric</th></tr></thead><tbody><tr><td>En las arquitecturas centralizadas (<strong>Data Warehouse, Data Lake, Data Lakehouse</strong><em>)</em>, los datos se almacenan y administran bajo el mismo paraguas. Esto significa que todas las decisiones sobre los datos, desde la integración hasta el análisis, son tomadas por un único equipo o departamento (habitualmente el área de Data de la organización). En cambio, en una arquitectura de <strong>Data Mesh</strong>, la responsabilidad y la decisión sobre los datos se divide entre múltiples unidades de negocio o <em>data domains</em> independientes, cada uno enfocado en sus propios datos.<br /><br />Mientras que <em>Data Warehouse</em>, <em>Data Lake </em>o <em>Data Lakehouse </em>son soluciones centralizadas de gestión de datos para el análisis y toma de decisiones, <em>Data Mesh</em> se enfoca en la descentralización y la entrega de valor a los usuarios finales.</td><td><em>Data Mesh</em> y <em><a href="https://www.n4gash.com/2023/que-es-una-arquitectura-data-fabric/">Data Fabric</a></em> son dos enfoques para la gestión de datos que comparten objetivos similares pero difieren en su enfoque y filosofía.<br /><br /><strong>Data Mesh </strong>es un modelo de gestión de datos enfocado en la entrega de valor a los usuarios finales y en la independencia de los equipos (dominios). Se basa en la idea de que los datos deben ser tratados como productos independientes con equipos propios responsables de ellos. Por otro lado, <strong>Data Fabric</strong> se refiere a una red de tecnologías y procesos que permiten la integración y acceso a datos a través una única capa a diferentes fuentes y sistemas. En este enfoque, los datos se gestionan de forma centralizada y se busca una visibilidad completa y un control total sobre los mismos.<br /><br />En resumen, <em>Data Mesh</em> se enfoca en la descentralización y la entrega de valor a los usuarios, mientras que <em>Data Fabric</em> se enfoca en la integración y la centralización de los datos.</td></tr></tbody></table><figcaption class="wp-element-caption">Comparativa entre modelos de gestión de datos: data mesh, data warehouse, data lake y data fabric</figcaption></figure>



<h2 class="wp-block-heading">Infraestructura de un Data Mesh</h2>



<p>La plataforma donde se almacenan, transforman y distribuyen los datos entre los <em>data domains</em> es administrada de forma central por un equipo técnico. Los <em>Data Domains</em> solo deben preocuparse de explorar y trabajar los datos, no de cómo persistirlos o distribuirlos.</p>



<ul class="wp-block-list">
<li><strong>Almacenamiento de datos:</strong> Los datos se almacenan en múltiples instancias independientes, cada una enfocada en un dominio específico. Estas instancias pueden ser bases de datos (con esquemas o datamarts específcos por dominio), data lakes (distribución por directorios) o sistemas de archivos.</li>



<li><strong>Servicios de datos:</strong> Cada instancia de datos proporciona servicios de datos independientes, como integración, seguridad y acceso, a través de una interfaz de programación de aplicaciones (API). Estos servicios pueden ser desarrollados y mantenidos por los equipos de datos correspondientes.</li>



<li><strong>Virtualización de datos:</strong> Se puede utilizar una capa de <em>virtualización de datos </em>para aislar los servicios de datos de los sistemas operativos subyacentes y permitir un acceso independiente a los datos. Esta capa puede ser proporcionada por herramientas como Denodo.</li>



<li><strong>Red de servicios:</strong> Los diferentes servicios de datos se comunican y colaboran entre sí a través de una red de servicios. Esta red puede ser implementada utilizando tecnologías como Kubernetes o Docker.</li>



<li><strong>Automatización</strong>: Se utilizan herramientas y procesos automatizados para garantizar la seguridad, calidad, cumplimiento y escalabilidad de la arquitectura. </li>
</ul>



<p>Sin embargo, implementar un <em>Data Mesh</em> también presenta algunos desafíos. Uno de los principales es la necesidad de establecer una estrategia clara para la gestión de datos en la organización, establecer procesos y herramientas para garantizar la seguridad, calidad y cumplimiento, y asegurar una comunicación y colaboración efectiva entre los diferentes equipos de datos.</p>



<h2 class="wp-block-heading">Desafíos de la arquitectura Data Mesh</h2>



<p>Implementar un <em>Data Mesh</em> presenta varios desafíos, algunos de los cuales son:</p>



<ul class="wp-block-list">
<li><strong>Cambio en la cultura organizacional:</strong> implica un cambio en la forma en que se gestionan los datos y en la estructura de la organización. Es importante involucrar a todos los departamentos y equipos en el proceso de cambio para garantizar su éxito. Se debe dedicar mucho esfuerzo a la gestión del cambio.</li>



<li><strong>Comunicación y colaboración entre equipos</strong>: La descentralización de los datos y la responsabilidad de su gestión entre múltiples equipos independientes requiere una buena comunicación y colaboración entre ellos. Es necesario establecer procesos y herramientas para garantizar una comunicación y colaboración efectiva. </li>



<li><strong>Seguridad y cumplimiento</strong>: Es importante establecer procesos y herramientas para garantizar la seguridad y cumplimiento de los datos en una arquitectura descentralizada.</li>



<li><strong>Escalabilidad</strong>: Es importante garantizar que la arquitectura sea escalable para manejar grandes cantidades de datos y poder crecer con las necesidades de la organización.</li>



<li><strong>Integración de los datos: </strong>Un Data Mesh implica la integración de múltiples fuentes de datos, lo que puede ser desafiante debido a la variedad de formatos y sistemas de origen de los datos.</li>



<li><strong>Capacitación y habilidades:</strong> Es necesario capacitar al equipo en las nuevas tecnologías y procesos necesarios para implementar un Data Mesh. Al distribuir la responsabilidad end-to-end de los datos a los dominios, éstos se deben dotar de recursos técnicos.</li>



<li><strong>Monitoreo y medición:</strong> Es importante establecer procesos y herramientas para monitorear y medir el rendimiento y la disponibilidad de los servicios de datos, así como detectar problemas.</li>



<li><strong>Cambio en los procesos de negocio:</strong> puede requerir cambios en los procesos de negocio existentes para aprovechar al máximo los servicios de datos independientes.</li>
</ul>



<h2 class="wp-block-heading">Implementación Data Mesh</h2>



<p>Una cosa es la literatura de Zhamak en <strong><a href="https://amzn.to/3Xyut9w" target="_blank" rel="noreferrer noopener">Data Mesh: Deliverig Data-Driven value at scale</a></strong> y otra la realidad. Os recomiendo esta <a href="https://medium.com/google-cloud/10-reasons-why-you-should-not-adopt-data-mesh-7a0b045ea40f" target="_blank" rel="noreferrer noopener">lectura </a>donde se desarrollan los motivos por los que una organización no estaría preparada para implantar una arquitectura <em>Data Mesh</em>. Como tener un <em>greenfield </em>para implementar una arquitectura <em>Data Mesh</em> es utópico, el único camino para no ahogarse en el intento es definir un <em>roadmap </em>e ir adaptando poco a poco el entorno. En nuestro caso, nos hemos juntado varios equipos técnicos y de negocio para diseñar un framework que sea el habilitador a la adopción de la nueva cultura de datos al resto de la organización. Va a nacer acoplado a los sistemas que ya hay implantados para ir poco a poco evolucionando a herramientas o servicios independientes e interoperables para todos los dominios. </p>



<p>Por ahora los DaaP se están generando y manteniendo desde este pequeño grupo, con la idea de que a medida que los dominios de datos asuman su gestión se doten de recursos técnicos especializados para asumir el mantenimiento y desarrollo. Tenemos hitos alcanzables, el objetivo no es hacer un <em>big bang</em>; queremos ir implementando cambios poco a poco y llevar de la mano a las demás áreas. Además del trabajo técnico y de diseño, esta transformación lleva consigo una inmensa tarea evangelizadora. Esto implica que a corto y medio plazo se tenga que asumir mucha deuda técnica. Algunos comentarios sobre cómo lo estamos abordando:</p>



<ul class="wp-block-list">
<li>Definición clara del <strong>roadmap</strong>, tanto en el corto plazo como el objetivo final ¿qué queremos conseguir? ¿cómo? y ¿cuándo?</li>



<li>Esfuerzo inicial en la <strong>definición de un DaaP,</strong> qué consideramos un <em>Data as a product </em>y las propiedades que debe cumplir (ver<em> </em><a href="#daap"><em>¿Qué es un data as a produc</em>t?</a>). Para ello hemos desarrollado las 6 características principales <em>(seguro, descubrible, accesible, entendible, confiable </em>e<em> interoperable</em>), dejando de lado las de <em>valioso </em>y <em>nativamente accesible</em>, ya que son inherentes al objetivo a su creación. Se ha trabajado en definir qué significa para nosotros cada uno de estos atributos y como llevarlo al mundo real. Por ejemplo, como organización ¿qué necesitamos para que un DaaP sea seguro? ¿qué mecanismos podemos construir para conseguirlo? en nuestro caso, hemos creado dos capas de seguridad definidas por roles de nuestro LDAP y sobre la BD donde se almacenan los data products como esquemas de base de datos.</li>



<li>Se ha comenzado con DaaP <strong>pilotos</strong>. Es necesario partir el elefante en trocitos, por lo que se han identificado cuáles son los que podríamos empezar por resultar más sencillos de implementar y tener un impacto en negocio. Se han aprovechado las nuevas necesidades de datos en la organización para que nazcan ya como data products.</li>



<li>Hemos definido varios tipos de DaaP en función de cuál es el origen y como se persisten los datos en base a nuestra arquitectura beta:
<ul class="wp-block-list">
<li>Tabla de BBDD</li>



<li>Modelo de estrella en BBDD (tabla de hechos y sus dimensiones)</li>



<li>Vista de BBDD</li>



<li>Entidades virtualizadas </li>
</ul>
</li>



<li>Seguimos manteniendo equipos de data <strong>centralizados </strong>encargados de hacer nacer los primeros DaaP y construir el framework de la plataforma de auto servicio.</li>



<li>Los <strong>propietarios </strong>de los data products siguen siendo los equipos de desarrollo que están empujando la iniciativa. Los dominios aun no están lo suficientemente maduros para asumir el desarrollo, mantenimiento y en general el ciclo de vida de los DaaP. </li>



<li>Los <strong>DaaPs se consumen </strong>a través de esquemas de BBDD (como si fueran datamarts). El objetivo a largo plazo no está claro, podría ser crear una capa de API REST como propone Zhamak. No sólo los propios datos, todo el ecosistema que define un DaaP podría ser accesible o consumible por microservicios. Por ejemplo, la gobernanza, metadatos, monitorización, etc. Se está explorando la <strong>virtualización de datos</strong> para resolver este paradigma.</li>



<li>La <strong>seguridad </strong>de acceso a datos, la interoperabilidad, etc. por ahora se gestiona a nivel dominio activo y de configuración de base de datos (roles) ya que los datos están almacenados en datamarts de BBDD.</li>



<li>Con el fin de dar servicio al <em>descubrimiento</em>, el <strong>catálogo </strong>de <em>Data as a Product</em>s o <strong>data martketplace </strong>se está construyendo aprovechando las herramientas que ya están implementadas en la organización. En el catálogo se describe cada DaaP en detalle con las evidencias a los atributos descritos previamente: qué datos contiene, SLA/SLO, ejemplos, linaje, ownership, etc.</li>
</ul>
<p>La entrada <a href="https://n4gash.com/arquitectura-data-mesh-gestion-de-datos-descentralizada/">Data Mesh: gestión de datos descentralizada</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3834</post-id>	</item>
		<item>
		<title>Data Engineering Zoomcamp – Semana 6</title>
		<link>https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=data-engineering-zoomcamp-semana-6-stream-processing</link>
		
		<dc:creator><![CDATA[Marcos Torregrosa]]></dc:creator>
		<pubDate>Fri, 10 Mar 2023 06:26:10 +0000</pubDate>
				<category><![CDATA[Cloud Data]]></category>
		<category><![CDATA[Data Engineering]]></category>
		<category><![CDATA[Apache Kafka]]></category>
		<category><![CDATA[Confluent]]></category>
		<category><![CDATA[Stream processing]]></category>
		<guid isPermaLink="false">https://www.n4gash.com/?p=5094</guid>

					<description><![CDATA[<p>Stream processing con Apache Kafka Llegamos a la última semana del bootcamp para Data Engineers organizado por DataTalksClub. Si la semana pasada vimos el procesamiento de datos por lotes, en esta vamos a abordar el procesamiento en tiempo real con Apache Kafka. En primer lugar analizando cómo funciona internamente para luego practicar levantando un clúster Spark y Kafka en local con Docker o utilizando el SaaS de Confluence que ejecuta un clúster de Kafka en cloud. Prerequisitos Stream processing (procesamiento de datos en tiempo real) Apache Kafka Productores y Consumidores Brokers Clúster de Kafka KRaft (sustituto de Zookeeper) Topics Particiones…</p>
<p>La entrada <a href="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/">Data Engineering Zoomcamp – Semana 6</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">Stream processing con Apache Kafka</h2>



<p>Llegamos a la última semana del <a href="https://www.n4gash.com/2023/data-engineering-zoomcamp/" target="_blank" rel="noreferrer noopener">bootcamp para Data Engineers</a> organizado por <a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/tree/main/week_6_stream_processing" target="_blank" rel="noreferrer noopener">DataTalksClub</a>. Si la semana pasada vimos el procesamiento de datos por lotes, en esta vamos a abordar el procesamiento en tiempo real con <strong>Apache Kafka</strong>. En primer lugar analizando cómo funciona internamente para luego practicar levantando un clúster <strong>Spark </strong>y <strong>Kafka </strong>en local con <strong>Docker </strong>o utilizando el SaaS de <strong>Confluence </strong>que ejecuta un clúster de Kafka en cloud.</p>



<ul class="wp-block-list">
<li><strong>Repo Bootcamp:</strong> <a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/tree/main/week_6_stream_processing" target="_blank" rel="noreferrer noopener">https://github.com/DataTalksClub/data-engineering-zoomcamp/tree/main/week_6_stream_processing</a></li>



<li><strong>Repo personal con ejercicios:</strong> <a href="https://github.com/hegdehog/data-engineering-zoomcamp-2023/tree/main/week_6_streaming_processing" target="_blank" rel="noreferrer noopener">https://github.com/hegdehog/data-engineering-zoomcamp-2023</a></li>
</ul>



<div class="wp-block-group has-palette-color-8-color has-text-color has-background is-layout-constrained wp-block-group-is-layout-constrained" style="background-color:#7bdbb547">
<div class="wp-block-aioseo-table-of-contents">
<ul>
<li><a href="#prerequisitos">Prerequisitos</a></li>
<li><a href="#stream-processing">Stream processing (procesamiento de datos en tiempo real)</a></li>
<li><a href="#apache-kafka">Apache Kafka</a>
        <ul>
                    <li><a href="#kafka-producers-consumers">Productores y Consumidores</a></li>
                    <li><a href="#kafka-brokers">Brokers</a></li>
                    <li><a href="#kafka-cluster">Clúster de Kafka</a></li>
                    <li><a href="#kafka-kraft-zookeeper">KRaft (sustituto de Zookeeper)</a></li>
                    <li><a href="#kafka-topics">Topics</a>
                        <ul>
                            <li><a href="#kafka-particiones">Particiones</a></li>
                            <li><a href="#kafka-replicacion">Replicación (Replication)</a></li>
<li><a href="#kafka-politica-retencion">Política de retención (retention policy)</a></li>
                            <li><a href="#kafka-offset">Offsets</a></li>
                            <li><a href="#kafka-timestamps">Timestamps</a></li>
                        </ul>
                    </li>
                    <li><a href="#schema-registry">Schema Registry</a>
<ul><li><a href="#kafka-avro">Avro</a></li></ul></li>
                    <li><a href="#kafka-connect">Kafka connect</a></li>
                </ul>
            
    </li>
<li><a href="#kafka-streams">Kafka Streams</a>
<ul>
<li><a href="#kafka-stream">Stream</a></li>
<li><a href="#kafka-stream-processor-topology">Processor Topology</a></li>
<li><a href="#kafka-stream-kstream-ktable">KStreams vs KTables</a></li>
<li><a href="#kafka-stream-stateless-stateful">Transformaciones Stateful y Stateless</a></li>
<li><a href="#kafka-stream-aggregations">Agregaciones</a></li>
<li><a href="#kafka-stream-join">Uniones (join)</a></li>
<li><a href="#kafka-stream-windowing">Funciones de ventana (windowing)</a></li>
</ul>
</li>
<li><a href="#ejecutar-kafka-spark-docker">Ejecutar Spark y cluster Kafka en Docker</a></li>
<li><a href="#pyspark-structured-streaming">PySpark Structured Streaming </a></li>
</ul>
</div>
</div>



<p class="has-text-align-center has-small-font-size"><em>Última actualización: 18/03/202</em>3</p>



<h2 class="wp-block-heading" id="prerequisitos">Prerequisitos</h2>



<p>En mi caso estoy trabajando en Windows, os recomiendo seguir la <a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/blob/main/week_5_batch_processing/setup/windows.md" target="_blank" rel="noreferrer noopener">guía</a> de instalación de los prerequesitos. Es importante tener en cuenta que si estamos usando <strong>Git bash </strong>o <strong>MINGW64</strong> el catálogo de variables de entorno no se replica en Windows, por lo que debemos arrancar los cuadernos de <em>jupyter </em>desde el <em>command shell </em>de unix para que coja correctamente los <em>path </em>configurados.</p>



<ul class="wp-block-list">
<li><strong>Apache Kafka</strong>: podemos instalarlo de forma manual siguiendo las instrucciones de la <a href="https://kafka.apache.org/quickstart#quickstart_kafkaconnect" target="_blank" rel="noreferrer noopener">documentación </a>oficial o bien levantar un contenedor de Docker preconfigurado. Puedes consultar cómo configurarlo y desplegarlo más <a href="#ejecutar-kafka-spark-docker" target="_blank" rel="noreferrer noopener">adelante</a>.</li>



<li>Revisa las <strong>variables de entorno</strong> en Windows, además de tenerlas declaradas de forma independiente, es necesario añadirlas al <code>PATH</code>:
<ul class="wp-block-list">
<li><code>HADOOP_HOME</code>=C:\tools\hadoop-3.2.0</li>



<li><code>JAVA_HOME</code>=C:\Program Files\Java\jdk-11.0.17</li>



<li><code>SPARK_HOME</code>=C:\tools\spark-3.3.2-bin-hadoop3</li>



<li><code>PYSPARK_PYTHON</code>=python</li>
</ul>
</li>
</ul>



<h2 class="wp-block-heading" id="stream-processing">Stream processing (procesamiento de datos en tiempo real)</h2>



<p>El<strong> stream processing </strong>(procesamiento de datos en tiempo real) es un método que implica el procesamiento continuo de datos en tiempo real a medida que se generan y se transmiten a través de un flujo (<em>stream</em>) en lugar de procesarlos como un lote (<a href="https://www.n4gash.com/2023/data-engineering-zoomcamp/">batch</a>) estático.</p>



<p>Los datos se procesan a medida que se generan y se transmiten, lo que significa que se pueden analizar y tomar decisiones en tiempo real. Esto es especialmente útil en aplicaciones donde la velocidad y la precisión son críticas, como el monitoreo de transacciones financieras, la detección de fraudes, la gestión de inventario, análisis de redes sociales y seguimiento de la cadena de suministro, entre otros.</p>



<p>La mayoría de los sistemas de stream processing están diseñados para ser escalables, tolerantes a fallos y distribuidos, lo que permite el procesamiento de grandes cantidades de datos en tiempo real en múltiples nodos o servidores. Algunas herramientas populares de stream processing incluyen <strong>Apache Kafka</strong>, <em>Apache Flink, Apache Storm, Spark Streaming</em>, y <em>AWS Kinesis.</em></p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-38.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1112" height="104" data-attachment-id="5119" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-38-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-38.png?fit=1112%2C104&amp;ssl=1" data-orig-size="1112,104" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-38" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-38.png?fit=1024%2C96&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-38.png?resize=1112%2C104&#038;ssl=1" alt="Stream processing " class="wp-image-5119" title="Stream processing " srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-38.png?w=1112&amp;ssl=1 1112w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-38.png?resize=300%2C28&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-38.png?resize=1024%2C96&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-38.png?resize=768%2C72&amp;ssl=1 768w" sizes="auto, (max-width: 1112px) 100vw, 1112px" /></a><figcaption class="wp-element-caption">Stream processing </figcaption></figure>



<figure class="wp-block-table aligncenter is-style-stripes, tab-head is-style-stripes"><table class="has-fixed-layout"><thead><tr><th class="has-text-align-left" data-align="left">Ventajas</th><th class="has-text-align-left" data-align="left">Desventajas</th></tr></thead><tbody><tr><td class="has-text-align-left" data-align="left">Su arquitectura distribuida permite que se pueda <strong>escalar </strong>horizontalmente según las necesidades de la empresa.</td><td class="has-text-align-left" data-align="left">Tecnología <strong>compleja </strong>y puede requerir conocimientos especializados para configurarlo correctamente.</td></tr><tr><td class="has-text-align-left" data-align="left">Es capaz de procesar y entregar los datos en tiempo real, lo que significa que los usuarios pueden tomar decisiones más <strong>rápidamente</strong>.</td><td class="has-text-align-left" data-align="left">Puede requerir una cantidad significativa de <strong>recursos de hardware</strong> para funcionar correctamente, especialmente si se trata de grandes volúmenes de datos.</td></tr><tr><td class="has-text-align-left" data-align="left">Altamente <strong>tolerante a fallos</strong> y ofrece una alta durabilidad de los datos. Los datos se almacenan en múltiples nodos, lo que significa que si un nodo falla, los datos se pueden recuperar fácilmente.</td><td class="has-text-align-left" data-align="left">Aunque <em>Kafka </em>es una tecnología de código abierto, puede haber <strong>costos </strong>asociados con su implementación y mantenimiento.</td></tr><tr><td class="has-text-align-left" data-align="left">Compatible con múltiples lenguajes de programación y sistemas operativos, lo que lo hace fácilmente <strong>integrable </strong>en diferentes sistemas.</td><td class="has-text-align-left" data-align="left">Se puede usar en conjunto con otras tecnologías, lo que puede crear <strong>dependencias </strong>en la infraestructura tecnológica de la empresa.</td></tr></tbody></table><figcaption class="wp-element-caption"> Pros y contras de procesamiento en tiempo real (streaming)</figcaption></figure>



<h2 class="wp-block-heading" id="apache-kafka">Apache Kafka</h2>



<p><strong>Apache Kafka</strong> es una plataforma de streaming de datos de código abierto desarrollada por la <em>Apache Software Foundation</em>. Se utiliza para la transmisión de datos en tiempo real a través de diferentes aplicaciones y sistemas, lo que permite a los usuarios procesar, analizar y almacenar grandes cantidades de datos en tiempo real.</p>



<p><em>Kafka </em>se basa en el <strong>modelo de publicación-suscripción</strong>, donde los datos son enviados por los <strong>productores <em>(producers)</em> </strong>y recibidos por los <strong>consumidores <em>(consumers)</em> </strong>en tiempo real. Está diseñado para ser escalable y tolerante a fallos, lo que significa que es capaz de manejar grandes volúmenes de datos y puede continuar operando incluso si algunos nodos fallan. Ofrece una arquitectura distribuida, lo que significa que se puede implementar en múltiples servidores y se puede escalar horizontalmente según las necesidades. Además, cuenta con una API, lo que facilita su integración con diferentes sistemas y lenguajes de programación.</p>



<h3 class="has-palette-color-2-color has-text-color has-medium-font-size wp-block-heading" id="kafka-producers-consumers" style="font-style:normal;font-weight:100">Productores y Consumidores</h3>



<p>Los <strong>productores </strong>y los <strong>consumidores </strong>son los componentes principales de <em>Kafka</em>. Los <em>productores </em>son los encargados de enviar los datos a Kafka, mientras que los <em>consumidores </em>los reciben y los procesan. Es como si los productores fueran los que hablan y los consumidores los que escuchan. Los consumidores se pueden agrupar en <strong>grupos de consumidores</strong> (<code>consumer.group.id</code>) para leer un mismo <strong><em>topic</em></strong>. </p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-48.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1064" height="301" data-attachment-id="5147" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-48-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-48.png?fit=1064%2C301&amp;ssl=1" data-orig-size="1064,301" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-48" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-48.png?fit=1024%2C290&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-48.png?resize=1064%2C301&#038;ssl=1" alt="Apache Kafka Producers y Consumers" class="wp-image-5147" title="Apache Kafka Producers y Consumers" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-48.png?w=1064&amp;ssl=1 1064w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-48.png?resize=300%2C85&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-48.png?resize=1024%2C290&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-48.png?resize=768%2C217&amp;ssl=1 768w" sizes="auto, (max-width: 1064px) 100vw, 1064px" /></a><figcaption class="wp-element-caption">Apache Kafka Producers y Consumers</figcaption></figure>



<p><code>Acks</code> (<em>confirmaciones</em>) es una configuración de <em>Apache Kafka</em> que se refiere a la cantidad de réplicas de un <em>mensaje </em>que deben confirmar la recepción antes de que se considere que el mensaje ha sido correctamente procesado y se envíe una respuesta de confirmación al <em>productor</em>.</p>



<p>Cuando un <em>productor </em>envía un <em>mensaje </em>a un <em>topic</em> en <em>Kafka</em>, se envía a los <em>brokers </em>que son responsables de ese <em>topic</em> que tienen una o más réplicas del mismo. La configuración <code>acks</code> indica cuántas réplicas deben confirmar la recepción del <em>mensaje </em>antes de que el productor reciba una respuesta de confirmación. Hay tres valores posibles:</p>



<ul class="wp-block-list">
<li><code>0</code>: El <em>productor </em>no espera confirmación de recepción. Esto significa que el mensaje se envía al <em>broker </em>y se considera enviado.</li>



<li><code>1</code>: El <em>productor </em>espera la confirmación de recepción de al menos un <em>broker</em>. Si uno confirma la recepción del <em>mensaje</em> se envía una respuesta de confirmación al <em>productor</em>.</li>



<li><code>all</code>» o <code>-1</code>: El <em>productor </em>espera la confirmación de recepción de todas las réplicas. Cuando han confirmado la recepción del <em>mensaje</em>, se envía una respuesta de confirmación al productor.</li>
</ul>



<p>La configuración <code>acks</code> es importante porque afecta la disponibilidad y la durabilidad de los datos en Kafka. Si se establece en <code>0</code> se corre el riesgo de perder <em>mensajes </em>si se produce una incidencia en el <em>broker </em>antes de que se hayan replicado los datos. Si se establece en <code>all</code> se asegura la durabilidad de los datos, pero impacta en la entrega del <em>mensaje </em>debido a la necesidad de que todas las réplicas confirmen la recepción.</p>



<h3 class="has-palette-color-2-color has-text-color has-medium-font-size wp-block-heading" id="kafka-brokers" style="font-style:normal;font-weight:100">Brokers</h3>



<p>Los <strong>brokers </strong>son los nodos de <em>Kafka </em>que se encargan de almacenar y distribuir los datos. Cada <em>broker </em>es capaz de almacenar uno o más <em>topics </em>y es responsable de asegurarse de que los datos sean entregados correctamente a los consumidores. Si un <em>broker </em>falla, los otros <em>brokers </em>pueden continuar operando sin interrupciones.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-52.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1063" height="455" data-attachment-id="5154" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-52-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-52.png?fit=1063%2C455&amp;ssl=1" data-orig-size="1063,455" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-52" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-52.png?fit=1024%2C438&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-52.png?resize=1063%2C455&#038;ssl=1" alt="Apache Kafka Producers, Consumers y Brokers" class="wp-image-5154" title="Apache Kafka Producers, Consumers y Brokers" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-52.png?w=1063&amp;ssl=1 1063w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-52.png?resize=300%2C128&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-52.png?resize=1024%2C438&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-52.png?resize=768%2C329&amp;ssl=1 768w" sizes="auto, (max-width: 1063px) 100vw, 1063px" /></a><figcaption class="wp-element-caption">Apache Kafka Producers, Consumers y Brokers</figcaption></figure>



<h3 class="has-palette-color-2-color has-text-color has-medium-font-size wp-block-heading" id="kafka-cluster" style="font-style:normal;font-weight:100">Clúster de Kafka</h3>



<p>Un <strong>clúster</strong> <strong>de Kafka </strong>es un conjunto de servidores o <strong>brokers </strong>de Kafka que trabajan juntos para proporcionar una plataforma de streaming de datos distribuida y tolerante a fallos. Permite escalar la plataforma horizontalmente para procesar grandes cantidades de datos en tiempo real. Cada <em>broker </em>en el <em>clúster de Kafka</em> es responsable de almacenar y distribuir un subconjunto de los datos del clúster. Los <strong>productores </strong>envían mensajes a los <strong>brokers</strong>, que los almacenan en <strong>particiones </strong>y los distribuyen a los <strong>consumidores</strong>.</p>



<p>Uno de los <em>broker </em>se convierte en el <strong>controlador </strong>(<em><strong>controller</strong></em>) para coordinar la asignación de <em>particiones </em>a todos los brokers. El <em>controlador </em>supervisa el estado de los <em>brokers </em>y las <em>particiones </em>y se asegura de que las réplicas de las <em>particiones </em>estén distribuidas de manera uniforme entre los <em>brokers</em>. Por otro lado, utiliza <strong>grupos de consumidores </strong>(<strong><em>consumer groups</em></strong>) para equilibrar la carga de lectura de mensajes. Los <em>consumidores </em>de un grupo se asignan a diferentes <em>particiones </em>de un <strong>topic </strong>para leer los mensajes de forma paralela y procesarlos.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-51.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1063" height="552" data-attachment-id="5152" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-51-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-51.png?fit=1063%2C552&amp;ssl=1" data-orig-size="1063,552" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-51" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-51.png?fit=1024%2C532&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-51.png?resize=1063%2C552&#038;ssl=1" alt="Apache Kafka Producers, Consumers, Brokers y Clúster" class="wp-image-5152" title="Apache Kafka Producers, Consumers, Brokers y Clúster" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-51.png?w=1063&amp;ssl=1 1063w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-51.png?resize=300%2C156&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-51.png?resize=1024%2C532&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-51.png?resize=768%2C399&amp;ssl=1 768w" sizes="auto, (max-width: 1063px) 100vw, 1063px" /></a><figcaption class="wp-element-caption">Apache Kafka Producers, Consumers, Brokers y Clúster</figcaption></figure>



<h3 class="has-palette-color-2-color has-text-color has-medium-font-size wp-block-heading" id="kafka-kraft-zookeeper" style="font-style:normal;font-weight:100">KRaft (sustituto de Zookeeper)</h3>



<p><strong>KRaft </strong>es un protocolo de consenso de <em>Apache Kafka</em> que fue introducido en la versión 2.4.0 (aunque se liberó oficialmente en la 3.1.1)  y que tiene como objetivo reemplazar a <strong>ZooKeeper </strong>para la gestión de los metadatos del <em>clúster </em>de <em>Kafka</em>. <em>ZooKeeper </em>gestiona el estado del clúster y almacena los metadatos de los diferentes componentes, como los <em>topics</em>, <em>particiones</em>, <em>brokers</em>, etc. con la misión de gestionar su estado estado y coordinar los consumidores. Sin embargo, puede ser una fuente de problemas, ya que su arquitectura centralizada lo hace propenso a fallos y cuellos de botella.</p>



<p>Con el objetivo de mejorar la fiabilidad y escalabilidad del sistema, <em>Kafka </em>introdujo el protocolo <em>KRaft</em>, que permite <strong>almacenar los metadatos en los propios <em>brokers </em>de <em>Kafka </em>a través de un <em>topic</em> en lugar de depender de <em>ZooKeeper</em></strong>. <em>Kraft </em>utiliza un sistema distribuido de almacenamiento de metadatos que permite a los <em>brokers </em>mantener una copia de los metadatos del <em>clúster</em>, lo que mejora la disponibilidad y elimina la necesidad de un sistema de gestión de estado centralizado como <em>ZooKeeper</em>.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-47.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1175" height="330" data-attachment-id="5145" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-47-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-47.png?fit=1175%2C330&amp;ssl=1" data-orig-size="1175,330" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-47" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-47.png?fit=1024%2C288&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-47.png?resize=1175%2C330&#038;ssl=1" alt="Diferencia entre Zookeeper y KRaft en Apache Kafka" class="wp-image-5145" title="Diferencia entre Zookeeper y KRaft en Apache Kafka" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-47.png?w=1175&amp;ssl=1 1175w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-47.png?resize=300%2C84&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-47.png?resize=1024%2C288&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-47.png?resize=768%2C216&amp;ssl=1 768w" sizes="auto, (max-width: 1175px) 100vw, 1175px" /></a></figure>



<p>Históricamente, el plano de control de <em>Kafka </em>se gestionaba a través de un servicio de consenso externo llamado <strong>ZooKeeper</strong>. Uno de los <em>brokers </em>se designa como <em>controlador</em> y es responsable de comunicarse con <em>ZooKeeper </em>y los demás <em>brokers </em>del <em>clúster</em>. Los metadatos del clúster se almacenan en <em>ZooKeeper</em>.  En <strong>KRaft</strong> se designa un grupo de <em>brokers </em>como <em>controladores</em>, que proporcionan los servicios de consenso que solían ser proporcionados por <em>ZooKeeper</em>. Todos los metadatos del clúster se almacenan en <em>topics </em>de <em>Kafka </em>y se gestionan internamente.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-53.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1064" height="552" data-attachment-id="5156" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-53-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-53.png?fit=1064%2C552&amp;ssl=1" data-orig-size="1064,552" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-53" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-53.png?fit=1024%2C531&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-53.png?resize=1064%2C552&#038;ssl=1" alt="Apache Kafka Producers, Consumers, Brokers, Clúster y KRaft" class="wp-image-5156" title="Apache Kafka Producers, Consumers, Brokers, Clúster y KRaft" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-53.png?w=1064&amp;ssl=1 1064w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-53.png?resize=300%2C156&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-53.png?resize=1024%2C531&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-53.png?resize=768%2C398&amp;ssl=1 768w" sizes="auto, (max-width: 1064px) 100vw, 1064px" /></a><figcaption class="wp-element-caption">Apache Kafka Producers, Consumers, Brokers, Clúster y KRaft</figcaption></figure>



<h3 class="has-palette-color-2-color has-text-color has-medium-font-size wp-block-heading" id="kafka-topics" style="font-style:normal;font-weight:100">Topics</h3>



<p>Un <strong>topic </strong>es una categoría o flujo de <strong>mensajes </strong>que se almacenan en un <strong>clúster de Kafka</strong>. Los <strong>productores </strong>envían <em>mensajes </em>a un <em>topic </em>específico y los <strong>consumidores </strong>pueden leerlos. Cada <em>topic </em>se compone de uno o más <strong>logs</strong>, que son la persistencia física de los <em>mensajes </em>en una secuencia ordenada. Cada <em>log </em>se almacena en uno o más <em>brokers</em>. Un <em>topic</em> divide los <em>mensajes </em>en múltiples <strong>particiones</strong>, lo que permite distribuir la carga de datos entre los nodos del clúster (<em>brokers</em>). Cada <em>mensaje </em>tiene asignado un par clave-valor, un <code>timestamp</code> y se identifica con un <strong>offset</strong>, número único que indica la posición del mensaje en la <em>partición</em>. Los <em>consumidores </em>utilizan los <em>offsets </em>para <em>saber </em>qué mensajes ya han sido procesados y cuáles están pendientes.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-60.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1290" height="799" data-attachment-id="5193" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-60-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-60.png?fit=1499%2C928&amp;ssl=1" data-orig-size="1499,928" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-60" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-60.png?fit=1024%2C634&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-60.png?resize=1290%2C799&#038;ssl=1" alt="Arquitectura Apache Kafka" class="wp-image-5193" title="Arquitectura Apache Kafka" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-60.png?w=1499&amp;ssl=1 1499w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-60.png?resize=300%2C186&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-60.png?resize=1024%2C634&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-60.png?resize=768%2C475&amp;ssl=1 768w" sizes="auto, (max-width: 1290px) 100vw, 1290px" /></a><figcaption class="wp-element-caption">Arquitectura Apache Kafka</figcaption></figure>



<h4 class="has-cyan-bluish-gray-color has-text-color has-medium-font-size wp-block-heading" id="kafka-particiones" style="font-style:normal;font-weight:100">Particiones</h4>



<p>Cada <em>topic </em>puede tener una o varias <strong>particiones</strong>, que son secuencias ordenadas y duraderas de <em>mensajes </em>que se almacenan en los <em>brokers </em>de <em>Kafka</em>. Cada <em>partición </em>se asigna a un único <em>broker </em>y se denomina <strong>líder de la partición</strong>, que es el responsable de recibir y servir las solicitudes de L/E de los <em>productores </em>y <em>consumidores </em>para esa partición. Una <em>partición </em>sólo se puede asignar a un <em>consumidor</em>, por lo que lo ideal es que haya tantas particiones como <em>consumidores</em>. </p>



<p>Los <em>mensajes </em>se asignan a las <em>particiones </em>en un <em>topic</em> mediante su <strong>clave</strong>: las claves de los mensajes se hashean y se dividen a partes iguales por las particiones. Las <em>particiones </em>mejoran el rendimiento y son una herramienta de escalabilidad de los <em>productores </em>y <em>consumidores </em>de <em>Kafka</em>. Al tener varias <em>particiones</em>, se pueden escribir varios <em>mensajes </em>de forma concurrente entre varios <em>brokers</em>.</p>



<figure class="wp-block-image aligncenter size-full is-resized"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-58.png?ssl=1"><img data-recalc-dims="1" decoding="async" data-attachment-id="5188" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-58-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-58.png?fit=1330%2C434&amp;ssl=1" data-orig-size="1330,434" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-58" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-58.png?fit=1024%2C334&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-58.png?w=1058&#038;ssl=1" alt="Particiones en Apache Spark" class="wp-image-5188"  title="Particiones en Apache Spark" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-58.png?w=1330&amp;ssl=1 1330w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-58.png?resize=300%2C98&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-58.png?resize=1024%2C334&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-58.png?resize=768%2C251&amp;ssl=1 768w" sizes="(max-width: 1290px) 100vw, 1290px" /></a><figcaption class="wp-element-caption">Particiones en Apache Spark</figcaption></figure>



<p>El <strong>Consumer Group id</strong> es un parámetro clave en <em>Kafka</em>, ya que determina cómo se distribuyen los <em>mensajes </em>entre los <em>consumidores</em>. Si varios <em>consumidores </em>pertenecen al mismo grupo y comparten la misma <em>suscripción </em>al mismo <em>topic</em>, <em>Kafka </em>garantiza que cada <em>mensaje </em>se entregue a un solo <em>consumidor</em> en el grupo, lo que ayuda a distribuir la carga de procesamiento de <em>mensajes </em>entre los <em>consumidores </em>y asegura que cada <em>mensaje </em>se procese exactamente una vez.</p>



<p>La elección de la clave de partición adecuada en <em>Kafka </em>es una tarea crítica para el rendimiento y la escalabilidad del sistema. A continuación, se presentan algunos factores a considerar:</p>



<ol class="wp-block-list">
<li><strong>Cardinalidad</strong>: la cardinalidad de la clave de <em>partición </em>es importante porque afecta la capacidad de distribuir uniformemente los mensajes a través de los diferentes <em>consumidores</em>. Si la clave de <em>partición </em>tiene baja cardinalidad, los mensajes se agruparán en un conjunto reducido de <em>particiones</em>, lo que puede generar un desequilibrio en la carga y un bajo rendimiento del sistema. Si la clave de <em>partición </em>tiene alta cardinalidad, los mensajes se distribuirán uniformemente en diferentes particiones.</li>



<li><strong>Ordenación</strong>: si el sistema requiere que los <em>mensajes </em>se procesen en un orden específico, la clave de partición debe elegirse de tal manera que garantice que se entreguen en el orden correcto.</li>



<li><strong>Distribución de carga:</strong> si el sistema tiene muchos <em>consumidores </em>, es importante elegir una clave de partición que permita una distribución uniforme de la carga a través de éstos. Si la carga está desequilibrada, algunos <em>consumidores </em>pueden estar sobrecargados, mientras que otros pueden estar inactivos.</li>



<li><strong>Reutilización de particiones</strong>: es posible que se puedan reutilizar particiones para diferentes flujos de <em>mensajes</em>. En este caso, la clave de partición debe elegirse de tal manera que permita la reutilización de particiones en diferentes flujos de mensajes.</li>
</ol>



<h4 class="has-cyan-bluish-gray-color has-text-color has-medium-font-size wp-block-heading" id="kafka-replicacion" style="font-style:normal;font-weight:100">Replicación (Replication)</h4>



<p><em>Kafka</em> utiliza la replicación de particiones para garantizar la tolerancia a fallos y la alta disponibilidad de los datos. Cada partición tiene una o varias réplicas (<strong>seguidores </strong>o <strong>followers</strong>), que son copias exactas de la partición <strong>líder </strong>en otros <em>brokers</em> de Kafka. Las réplicas garantizan que los datos de la partición estén disponibles incluso si el <em>broker</em> donde está ubicado el <em>líder </em>de la partición falla, en tal caso, uno de los <em>seguidores </em>en otro <em>broker</em> se convierte en el nuevo <em>líder</em>.</p>



<p>El<strong> factor de replicación</strong> se configura a nivel de <em>topic</em> y configura la cantidad de copias de una <em>partición </em>que se deben almacenar en diferentes <em>brokers </em>para garantizar la tolerancia a fallos y la disponibilidad de los datos en caso de que falle un <em>broker</em>. Por ejemplo, si un <em>topic</em> tiene un factor de replicación de 2, cada partición tendrá un líder y dos réplicas. Si el líder falla, una de las réplicas se elegirá como el nuevo líder y continuará gestionando las L/E en la partición. </p>



<p>Un factor de replicación 1 implica que no habrá copias de las <em>particiones</em>, es decir, solo habrá una instancia de cada <em>partición </em>en el <em>broker </em>líder que la aloja. Si falla, esa partición no estará disponible hasta que se restaure el <em>broker </em>o se cree un nuevo líder para esa <em>partición </em>en otro. Esta casuística puede provocar una interrupción de servicio y pérdida de datos en caso de que la <em>partición </em>no esté respaldada en otra ubicación. En general, <strong>se recomienda utilizar un factor de replicación mayor que 1</strong> para garantizar la tolerancia a fallos y la disponibilidad de los datos en caso de que falle un broker.</p>



<p>Es importante tener en cuenta que un factor de replicación muy alto significa que se necesita más espacio de almacenamiento y ancho de banda de red para replicar los datos en los diferentes <em>brokers</em>, lo que también impacta en el rendimiento y velocidad.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-62.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1290" height="494" data-attachment-id="5198" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-62-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-62.png?fit=1648%2C631&amp;ssl=1" data-orig-size="1648,631" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-62" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-62.png?fit=1024%2C392&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-62.png?resize=1290%2C494&#038;ssl=1" alt="Replicación en Apache Kafka" class="wp-image-5198" title="Replicación en Apache Kafka" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-62.png?w=1648&amp;ssl=1 1648w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-62.png?resize=300%2C115&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-62.png?resize=1024%2C392&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-62.png?resize=768%2C294&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-62.png?resize=1536%2C588&amp;ssl=1 1536w" sizes="auto, (max-width: 1290px) 100vw, 1290px" /></a><figcaption class="wp-element-caption">Replicación en Apache Kafka</figcaption></figure>



<h4 class="has-cyan-bluish-gray-color has-text-color has-medium-font-size wp-block-heading" id="kafka-politica-retencion" style="font-style:normal;font-weight:100">Política de retención (retention policy)</h4>



<p>La <strong>política de retención</strong> en <em>Kafka</em> determina cómo se manejan los registros en un <em>topic</em> después de cierto tiempo o tamaño. En otras palabras, es la configuración que determina cuánto tiempo se deben mantener los <em>mensajes</em> antes de ser eliminados.</p>



<p>Hay dos tipos de políticas de retención en Kafka: basadas en tiempo y basadas en tamaño.</p>



<ul class="wp-block-list">
<li><strong>Retención basada en tiempo</strong>: Los logs se eliminan después de un período de tiempo especificado, independientemente de su tamaño. Para habilitar esta política, se establece la propiedad de configuración <code>log.retention.ms</code> en el archivo <strong>de configuración de Kafka.</strong></li>



<li><strong>Retención basada en tamaño</strong>: Los registros se eliminan del <em>topic</em> después de que se haya alcanzado un tamaño máximo especificado. Para habilitar esta política, se establece la propiedad de configuración <code>log.retention.bytes</code>.</li>
</ul>



<p>Otros parámetros que podemos configurar de la política de retención son:</p>



<ul class="wp-block-list">
<li><code>retention.ms</code>: este parámetro de configuración controla el tiempo máximo de retención de un log antes de que sea eliminado para liberar espacio. Si se indica a -1  no se aplica límite de tiempo (no se borran nunca los logs).</li>



<li><code>cleanup.policy</code>: determina la política de retención de los logs. Por defecto es <code>delete </code>y borra todos los logs una vez superado el tiempo designado en la política de retención. Otra opción es configurarlo como <code>compact</code> para comprimirlos. La última opción combinar ambas separadas por coma <code>delete, compact</code>, que elimina los logs antiguos y comprime los logs retenidos.</li>
</ul>



<h4 class="has-cyan-bluish-gray-color has-text-color has-medium-font-size wp-block-heading" id="kafka-offset" style="font-style:normal;font-weight:100">Offsets</h4>



<p><code>__consumer_offset<em>s</em></code> es un <em>topic </em>interno que se utiliza para almacenar el estado de compensación de los consumidores de Kafka. Este topic almacena los <em>offsets </em>de los mensajes usados por cada <em>consumidor </em>de <em>Kafka </em>en un grupo de consumidores determinado.</p>



<p>Los <em>offsets </em>son números que indican la posición del último mensaje que un consumidor ha leído en un <em>topic </em>determinado. Almacenar los <em>offsets </em>en <code>__consumer_offsets</code> permite que los <em>consumidores </em>de <em>Kafka </em>puedan reanudar la lectura de los <em>mensajes </em>desde el último punto en que se detuvieron, incluso después de un reinicio del <em>consumidor </em>o un fallo del mismo. También se utiliza para mantener la coordinación entre los miembros de un<em> grupo de consumidores </em>de <em>Kafka</em>. Cuando un <em>consumidor </em>se une o abandona un grupo, se actualiza la información en <code>__consumer_offsets</code> para asegurar que los <em>offsets </em>de los mensajes se mantengan consistentes entre todos los miembros del grupo.</p>



<figure class="wp-block-image aligncenter size-full is-resized"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-57.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" data-attachment-id="5186" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-57-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-57.png?fit=967%2C396&amp;ssl=1" data-orig-size="967,396" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-57" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-57.png?fit=967%2C396&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-57.png?resize=840%2C344&#038;ssl=1" alt="Offset en Apache Kafka" class="wp-image-5186" width="840" height="344" title="Offset en Apache Kafka" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-57.png?w=967&amp;ssl=1 967w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-57.png?resize=300%2C123&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-57.png?resize=768%2C315&amp;ssl=1 768w" sizes="auto, (max-width: 840px) 100vw, 840px" /></a><figcaption class="wp-element-caption">Offset en Apache Kafka</figcaption></figure>



<p><code>auto.offset.reset</code> es una configuración en<em> Apache Kafka </em>que especifica qué sucede cuando un <em>consumido</em>r se une a un <em>grupo de consumidores </em>para leer <em>mensajes </em>de un <em>topic</em> y no tiene un <em>offset </em>válido para comenzar a leerlos (porque es la primera vez que se conecta o porque ha perdido su posición de lectura). Puede tener uno de los siguientes valores:</p>



<ul class="wp-block-list">
<li><code>earliest</code>: Si no hay offset <em>válido </em>para el <em>consumidor</em>, el <em>consumidor </em>comenzará a leer mensajes desde el comienzo del <em>topic</em>.</li>



<li><code>latest</code>: el <em>consumidor </em>comenzará a leer <em>mensajes </em>desde el final del <em>topic</em>.</li>



<li><code>none</code>: se lanzará una excepción.</li>
</ul>



<p>Es importante tener en cuenta que la configuración <code>auto.offset.reset</code> solo se aplica cuando un <em>consumidor </em>se une a un grupo de <em>consumidores</em>. Si un <em>consumidor </em>está leyendo desde un <em>topic</em> sin unirse a un grupo de <em>consumidores</em>, debe especificar el <em>offset </em>inicial de forma explícita.</p>



<h4 class="has-cyan-bluish-gray-color has-text-color has-medium-font-size wp-block-heading" id="kafka-timestamps" style="font-style:normal;font-weight:100">Timestamps</h4>



<p>Los <strong>timestamps</strong> se utilizan para registrar la hora en que se produce un evento en un <em>topic</em> de Kafka. Son importantes porque permiten ordenar los eventos en función del tiempo en que se produjeron, fundamental para procesar flujos de datos en tiempo real, ya que es necesario garantizar que los eventos se procesen en el orden correcto para generar resultados precisos, por ejemplo, si usamos <a href="#kafka-windowing">funciones de ventana (windowing)</a>.</p>



<p>En Kafka, existen dos tipos de <em>timestamps</em>:</p>



<ul class="wp-block-list">
<li><strong>Timestamp del productor:</strong> se asigna al evento cuando se escribe en un <em>topi</em>c. Por defecto, <em>Kafka </em>utiliza el <em>timestamp</em> del sistema en el momento en que se escribe el evento, pero se puede especificar uno personalizado si es necesario.</li>



<li><strong>Timestamp del registro</strong>: se asigna al evento cuando se lee del <em>topic</em>. Por defecto, <em>Kafka </em>utiliza el del productor, pero se puede configurar para que utilice el del sistema en el momento en que se lee el evento.</li>
</ul>



<p>Por último, existen tres conceptos de tiempo importantes: </p>



<ul class="wp-block-list">
<li><strong>Event time</strong>: momento en que un evento realmente ocurrió en el mundo real. Se puede incluir como un campo en el registro de <em>Kafka </em>o se puede inferir a partir de otros campos, como la marca de tiempo del sistema del dispositivo que generó el evento.</li>



<li><strong>Processing time</strong>: momento en que un evento es procesado por una aplicación o un sistema de <em>Kafka</em>. Es decir, es el tiempo en que se procesó el evento. Es determinado por el sistema que está procesando el evento y se puede incluir como un campo en el registro de <em>Kafka</em>.</li>



<li><strong>Ingestion time</strong>: momento en que un evento es recibido por un sistema de <em>Kafka</em>, es decir, es el tiempo en que el evento llega al <em>broker</em> y se escribe en un <em>topic</em>. Se puede incluir como un campo en el registro de <em>Kafka </em>o se puede inferir a partir de otros campos, como la marca de tiempo del sistema del <em>broker</em> que recibió el evento.</li>
</ul>



<h3 class="has-palette-color-2-color has-text-color has-medium-font-size wp-block-heading" id="schema-registry" style="font-style:normal;font-weight:100"><br />Schema Registry</h3>



<p>El <strong>Schema Registry</strong> es un componente opcional de <em>Kafka </em>que se utiliza para gestionar los esquemas en formato JSON de los datos que se envían (<em>productores)</em> y reciben (<em>consumidores</em>). Usar esquemas en <em>Kafka</em> es como tener una plantilla para el tipo de datos. Esto ayuda a prevenir errores y garantiza que los datos sean consistentes y estén en el formato correcto. Además, permite agregar nuevos campos o cambiar los tipos de datos sin romper la compatibilidad con aplicaciones antiguas. Usar esquemas en <em>Kafka </em>hace que sea más fácil enviar y recibir datos de manera confiable entre diferentes tecnologías y aplicaciones.</p>



<p>Cuando un <em>productor </em>envía un mensaje a un <em>topic</em>,<em> Kafka </em>puede validar el mensaje utilizando el esquema asociado. Si el mensaje es válido según el esquema, se procesa normalmente. Si el mensaje no cumple con el esquema, se rechaza o envia una notificación de error. Cuando un <em>consumidor </em>lee un mensaje de un tema, <em>Kafka </em>puede recuperar el esquema asociado y utilizarlo para deserializar el <em>mensaje</em>. Esto ayuda a garantizar que el mensaje se procese correctamente y que los datos estén en el formato correcto.</p>



<p>El <strong>Schema Registry</strong> también puede manejar la evolución de los esquemas. Si se actualiza un esquema, <em>Kafka </em>puede almacenar la versión anterior y garantizar que los <em>consumidores </em>que utilizan el esquema anterior aún puedan procesar los mensajes. Esto permite una evolución más flexible de los datos a medida que cambian con el tiempo.</p>



<h4 class="has-cyan-bluish-gray-color has-text-color has-medium-font-size wp-block-heading" id="kafka-avro" style="font-style:normal;font-weight:100">Avro</h4>



<p><strong>Avro</strong> es un formato de serialización de datos que se utiliza para estructurar y almacenar datos. La serialización es el proceso de convertir un objeto o estructura de datos en un formato que se pueda transmitir o almacenar, y que luego se pueda volver a convertir en el objeto original. Es similar a <strong>JSON </strong>en el sentido de que ambos son formatos de texto que se pueden leer y escribir en varios lenguajes de programación. Sin embargo, hay algunas diferencias clave entre los dos. En primer lugar, <em>Avro </em>es más compacto que <em>JSON</em>, lo que significa que utiliza menos espacio de almacenamiento y ancho de banda. Además, <em>Avro </em>admite la serialización de datos complejos, como registros y uniones, mientras que <em>JSON </em>solo admite tipos de datos simples, como cadenas y números.</p>



<p>Una de las mayores ventajas de <em>Avro </em>es que se puede utilizar en diferentes lenguajes de programación. Esto significa que un mensaje serializado en <em>Avro </em>en un lenguaje de programación se puede deserializar en otro lenguaje sin problemas de compatibilidad. En otras palabras, si estás usando <em>Java </em>para enviar un mensaje a través de <em>Kafka </em>y otro equipo está usando <em>Python </em>para recibir ese mensaje, ambos equipos pueden leer el mensaje en <em>Avro </em>sin ningún problema. Otra ventaja de <em>Avro </em>es que se integra bien con el <strong>Schema Registry</strong> para garantizar que todos los <em>mensajes </em>que se envían a través de <em>Kafka</em> se ajusten a un esquema específico. </p>



<p>Cuando se utiliza <em>Avro </em>para serializar datos, es posible que los esquemas cambien con el tiempo, lo que puede causar problemas si no se manejan correctamente. La <strong>evolución de <em>Avro</em></strong><em> </em>gestiona los cambios en los esquemas de datos. Básicamente, <em>Avro </em>tiene un sistema que permite manejar estos cambios de manera eficiente, sin afectar la capacidad de los clientes para leer registros más antiguos. Por ejemplo, cuando se serializa un registro en <em>Avro</em> este incluye su propio esquema. Si éste cambia en el futuro, <em>Avro </em>utilizará el esquema almacenado en el registro para interpretar los datos, lo que garantiza que los datos se lean correctamente.</p>



<p>Además, Avro permite agregar nuevos campos a los esquemas sin romper la compatibilidad hacia atrás. Esto significa que se pueden agregar nuevos campos a los registros sin afectar la capacidad de los clientes más antiguos para leer registros más antiguos. Sin embargo, si se eliminan campos, esto puede romper la compatibilidad hacia atrás y puede requerir una conversión de esquema.</p>



<ul class="wp-block-list">
<li><strong>La compatibilidad hacia atrás (backward compatibility)</strong> es la capacidad de nuevos <em>consumidores </em>de leer datos generados por <em>productores </em>antiguos. En otras palabras, si un <em>consumidor</em> más nuevo puede leer los datos que fueron producidos por un <em>productor</em> más antiguo sin problemas, entonces se dice que existe compatibilidad hacia atrás.</li>



<li><strong>La compatibilidad hacia adelante (forward compatibility) </strong>se refiere a la capacidad de <em>productores</em> más nuevos para producir datos que pueden ser leídos por <em>consumidores</em> más antiguos. Es decir, si un <em>productor</em> más nuevo puede producir datos que un <em>consumidor</em> más antiguo puede leer sin problemas, entonces se dice que existe compatibilidad hacia adelante.</li>



<li><strong>La compatibilidad mixta (mixed compatibility)</strong> se refiere a la capacidad de sistemas antiguos y nuevos para interactuar juntos de manera adecuada. </li>
</ul>



<h3 class="has-palette-color-2-color has-text-color has-medium-font-size wp-block-heading" id="kafka-connect" style="font-style:normal;font-weight:100">Kafka Connect</h3>



<p><strong>Kafka Connect</strong> es un framework que se utiliza para conectar <em>Kafka </em>con otros sistemas de forma que los usuarios pueden enviar y recibir datos desde y hacia <em>Kafka </em>utilizando conectores preconstruidos. Utiliza una arquitectura de plugin que permite a los usuarios conectar diferentes sistemas de origen y destino. Los <strong>plugins de origen</strong> son responsables de extraer los datos de la fuente y enviarlos a Kafka, mientras que los <strong>plugins de destino</strong> son responsables de recibir los datos y enviarlos al sistema de destino.</p>



<p>Proporciona una amplia gama de características que mejoran la usabilidad, escalabilidad y flexibilidad de la integración de datos:</p>



<ol class="wp-block-list">
<li><strong>Conectores preconstruidos: </strong>tiene una variedad de conectores preconstruidos para integrar diferentes sistemas, se incluyen conectores para bases de datos, sistemas de archivos, servicios web y más.</li>



<li><strong>Configuración sencilla:</strong> utiliza una configuración simple en formato JSON para los conectores, lo que facilita el proceso y la personalización de los conectores.</li>



<li><strong>Escalabilidad</strong>: altamente escalable y puede manejar grandes volúmenes de datos de manera eficiente. Además, admite la configuración de múltiples conectores para la misma tarea, lo que permite una mayor escalabilidad.</li>



<li><strong>Tolerancia a fallos:</strong> está diseñado para ser tolerante a fallos. Si un proceso de <em>Kafka Connect</em> falla, otro proceso lo reemplaza automáticamente sin interrupción del flujo de datos.</li>



<li><strong>Integración con Kafka:</strong> está diseñado para integrarse sin problemas con <em>Kafka </em>y utiliza el mismo sistema de particiones y replicación que Kafka para garantizar la tolerancia a fallos y la alta disponibilidad de los datos.</li>
</ol>



<h2 class="wp-block-heading" id="kafka-streams">Kafka Streams</h2>



<p><strong>Kafka Streams</strong> es una herramienta <em>open source</em> para procesar y analizar datos en tiempo real mientras almacenados en <em>Apache Kafka</em>, donde se consumen datos de un <em>topic</em> de <em>Kafka </em>y son enviados a otro <em>topic</em> para su procesamiento.  Permite realizar realizar operaciones de transformación, filtrado, combinación (join) y hacer cálculos en los flujos de datos en tiempo real para detectar patrones y tendencias. Es muy útil para aplicaciones que requieren análisis en tiempo real de grandes volúmenes de datos, como el análisis de datos de IoT, la detección de fraude, migración de datos o monitoreo y alertas.</p>



<p>Imagina <strong>Kafka Streams </strong>es como un cocinero que recibe datos de uno o varios lugares (<em>producers</em>), los mezcla y los prepara de una manera especial para después enviarlos a otro lugar (<em>consumer</em>). En este caso, los datos son como los ingredientes, los <em>topics </em>de <em>Kafka </em>son los lugares donde se encuentran estos ingredientes, y <em>Kafka Streams</em> es el chef que usa los ingredientes, los mezcla y transforma en algo diferente y sabroso para luego enviarlo a otro lugar donde pueda ser utilizado. </p>



<p>En<em> Kafka Streams</em> un <strong>thread</strong> es como un trabajador que se encarga de procesar datos de <em>Kafka </em>en tiempo real. Cada <em>thread</em> puede procesar varias tareas llamadas <strong>tasks</strong>» que son como paquetes de trabajo que contienen datos de una parte del <em>topic </em>de <em>Kafka</em>. Para procesar grandes cantidades de datos en tiempo real, se dividen las tareas en subconjuntos más pequeños y se distribuyen en varios <em>threads</em>. De esta manera, se pueden procesar varias tareas al mismo tiempo y de manera eficiente.</p>



<p>El número de <em>threads </em>y <em>tasks</em> se puede ajustar según las necesidades de procesamiento de la aplicación y los recursos disponibles. De esta manera, se puede asegurar que <em>Kafka Streams</em> tenga suficientes <em>workers</em> para procesar grandes cantidades de datos en tiempo real de manera rápida y eficiente.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-66.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1290" height="438" data-attachment-id="5277" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-66-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-66.png?fit=1810%2C615&amp;ssl=1" data-orig-size="1810,615" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-66" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-66.png?fit=1024%2C348&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-66.png?resize=1290%2C438&#038;ssl=1" alt="Kafka Streams" class="wp-image-5277" title="Kafka Streams" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-66.png?w=1810&amp;ssl=1 1810w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-66.png?resize=300%2C102&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-66.png?resize=1024%2C348&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-66.png?resize=768%2C261&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-66.png?resize=1536%2C522&amp;ssl=1 1536w" sizes="auto, (max-width: 1290px) 100vw, 1290px" /></a><figcaption class="wp-element-caption">Kafka Streams</figcaption></figure>



<p>Algunas de las principales funciones de Kafka Streams incluyen:</p>



<ul class="wp-block-list">
<li><strong>Transformación de datos</strong>: transformar, filtrar y enriquecer flujos de datos en tiempo real.</li>



<li><strong>Procesamiento de ventanas de tiempo: </strong>analizar datos en ventanas de tiempo definidas, lo que facilita la detección de patrones y tendencias en los flujos de datos.</li>



<li><strong>Join</strong>: combinar flujos de datos de múltiples fuentes para obtener información más completa y precisa.</li>



<li><strong>Agregación</strong>: realizar cálculos en los flujos de datos en tiempo real, como el recuento de eventos o la sumatoria de valores.</li>
</ul>



<h3 class="has-medium-font-size wp-block-heading" id="kafka-stream" style="font-style:normal;font-weight:100">Stream</h3>



<p>Un <strong>stream</strong> es una corriente continua de datos que se actualiza constantemente y cuyo tamaño es desconocido o ilimitado. Es como un río que fluye y nunca se detiene. En <em>Kafka Streams</em>, una <em>stream </em>puede estar compuesto por una o más <strong>particiones </strong>que son secuencias de datos ordenados, inmutables, y que se pueden reproducir y recuperar en caso de fallos.</p>



<p>Cada registro de datos en un <em>Stream </em>es un par de valores llamado (<strong>key-value</strong>) donde <strong>key</strong> es una etiqueta que se utiliza para identificar el registro y <strong>value</strong> es el valor real que contiene el registro.</p>



<h3 class="has-medium-font-size wp-block-heading" id="kafka-stream-processor-topology" style="font-style:normal;font-weight:100">Processor Topology</h3>



<p>El <strong>processor topology</strong> es un grafo formado por <strong>Streams </strong><em>(nodos)</em> conectados por <strong>Streams processors </strong><em>(aristas)</em> o la <strong>State Store<em> </em></strong>que representa las distintas etapas de transformación de los datos en tiempo real de un <em>topic </em>de <em>Kafka</em>. </p>



<p>Los <strong>stream processors</strong> procesan un flujo de datos de entrada y producen uno de salida. Pueden realizar diversas operaciones en los datos, como filtrar, transformar, combinar o agregar. Cada <em>stream processor</em> puede recibir y generr uno o varios <em>streams</em>, lo que le permite realizar múltiples operaciones de procesamiento en los datos. Hay dos tipos<strong><em> </em></strong>de <em>stream processors:</em></p>



<ul class="wp-block-list">
<li><strong>Source processor:</strong> Es un <em>stream processor </em>especial que no tiene ningún nodo anterior, es el primer procesador que lee datos directamente de uno o varios <em>topic</em>s.</li>



<li><strong>Sink processor: </strong>por contra, este tipo de <em>stream processor</em> sería el último de la <em>topología</em>. Se encarga de enviar todos los datos recibidos de los <em>stream processor </em>previos a un <em>topic </em>de <em>Kafka</em>.</li>
</ul>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-65.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="860" height="460" data-attachment-id="5275" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-65-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-65.png?fit=860%2C460&amp;ssl=1" data-orig-size="860,460" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-65" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-65.png?fit=860%2C460&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-65.png?resize=860%2C460&#038;ssl=1" alt="Kafka Stream - Processor topology" class="wp-image-5275" title="Kafka Stream - Processor topology" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-65.png?w=860&amp;ssl=1 860w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-65.png?resize=300%2C160&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-65.png?resize=768%2C411&amp;ssl=1 768w" sizes="auto, (max-width: 860px) 100vw, 860px" /></a><figcaption class="wp-element-caption">Kafka Stream &#8211; Processor topology</figcaption></figure>



<p>Para trabajar con una <em>topología </em>de procesamiento de datos podemos utilizar la <a href="https://docs.confluent.io/platform/current/streams/developer-guide/processor-api.html#kstreams-processor-api" target="_blank" rel="noreferrer noopener">Processor API</a> para trabajar a bajo nivel con mayor flexibilidad con los <em>streams</em> e interactuar con las <em>state stores</em>. Sin embargo, la opción más recomendada es utilizar la API <a href="https://docs.confluent.io/platform/current/streams/developer-guide/dsl-api.html#streams-dsl" target="_blank" rel="noreferrer noopener">Kafka Streams DSL (Domain Specific Language)</a>, que construida sobre el Processor API permite trabajar con los <em>streams </em>de una forma mucho más sencilla en muy pocas líneas de código, aunque menos personalizada.</p>



<h3 class="has-medium-font-size wp-block-heading" id="kafka-stream-kstream-ktable" style="font-style:normal;font-weight:100">KStreams vs KTables</h3>



<p>La principal diferencia entre un <strong>kStream </strong>y una <strong>KTable </strong>(o <strong><em>State</em></strong>) es que el primero representa un flujo continuo de datos en tiempo real, mientras que una <em>tabla </em>es una vista instantánea de los datos almacenados en un momento dado. En otras palabras, los <em>streams </em>permiten procesar datos a medida que fluyen, mientras que las <em>tablas </em>permiten realizar consultas y análisis sobre los datos almacenados en un momento específico en el tiempo. Los <em>streams </em>pueden ser infinitos, lo que significa que pueden procesar y transmitir datos continuamente, mientras que las <em>tablas </em>tienen un tamaño finito y se actualizan en tiempo real a medida que fluyen los datos.</p>



<div class="wp-block-group has-palette-color-6-background-color has-background is-layout-constrained wp-block-group-is-layout-constrained">
<p>Supongamos que tienes una aplicación de chat en tiempo real y deseas proporcionar una funcionalidad de búsqueda de mensajes. En primer lugar crearemos un <em>stream </em>que recibe todos los mensajes enviados en el chat y los procesa en tiempo real. Por otro lado, podemos crear una <em>tabla </em>que almacene los mensajes en un formato estructurado y les asigne una marca de tiempo. Esta <em>tabla </em>se actualiza automáticamente a medida que llegan nuevos mensajes, lo que te permite buscar mensajes antiguos con facilidad.</p>



<p>En este caso, el <em><strong>stream </strong></em>representa el <strong>flujo continuo de mensajes del chat en tiempo real</strong>, mientras que la <strong>tabla </strong>es una<strong> vista materializada de los datos </strong>almacenados en un momento dado (los mensajes antiguos). Ambos se utilizan juntos para proporcionar una funcionalidad de búsqueda de mensajes en la aplicación de chat.</p>
</div>



<p>Las <strong>KTables </strong>se almacenan en la <strong>state store</strong>, una caché en memoria que se utiliza para proporcionar un acceso más rápido y eficiente a los datos. Cada instancia de la aplicación de <em>Kafka Streams</em> tiene su propia copia del <em>state store </em>en la memoria, lo que permite que cada instancia procese su propia partición de los datos. También admite la persistencia de <em>KTables </em>en un almacenamiento externo como un sistema de archivos o una base de datos, con lo que incluso podríamos acceder a los datos aunque nuestra aplicación ya no esté corriendo.</p>



<h3 class="has-medium-font-size wp-block-heading" id="kafka-stream-stateless-stateful" style="font-style:normal;font-weight:100">Transformaciones Stateful y Stateless</h3>



<p>Algunas aplicaciones de <em>stream processing</em> no requieren estado (<strong></strong><strong>stateless</strong>), lo que significa que el procesamiento de un mensaje es independiente del resto mensajes y no necesitan almacenar datos adicionales en <em>Kafka</em>. Un ejemplo sería cuando sólo necesitas transformar un único mensaje a la vez o filtrar mensajes en función de alguna condición.</p>



<p>Por otro lado, nuestra aplicación necesitará <em>estado</em> (<strong>stateful</strong>) cuando realiza operaciones como unir, agregar o dividir registros que necesitan información de los registros previos, de forma que los datos del <em>Stream</em> son almacenados en <em>kafka</em>.</p>



<h3 class="has-medium-font-size wp-block-heading" id="kafka-stream-join" style="font-style:normal;font-weight:100">Uniones (join)</h3>



<p>Los <strong>joins</strong> de <em>streams </em>son muy similares a los <em>joins</em> de tablas en bases de datos relacionales. Sin embargo, hay algunas diferencias importantes debido a la naturaleza de los flujos de datos en <em>Kafka</em>. En Kafka Streams, los <strong>joins</strong> de <em>streams </em>se realizan mediante la unión de dos o más <em>streams </em>que se producen en tiempo real a través de las claves de los eventos.</p>



<p>Los <em>joins</em> en <em>Kafka Streams</em> utilizan un <strong>modelo de tiempo de evento</strong>, es decir, los eventos se procesan en orden de llegada, en lugar de utilizar un <em>modelo de tiempo de procesamiento</em>. Esto garantiza que los <em>joins</em> se realicen de manera precisa y coherente a medida que llegan los eventos. Los <em>joins</em> de <em>streams </em>también pueden ser realizados con <strong>ventanas de tiempo (windowed joins)</strong>, lo que permite unir <em>streams </em>en ventanas de tiempo específicas en lugar de en todo el flujo. Esto es útil en situaciones donde se quiere hacer uniones con datos históricos o en eventos que ocurrieron en un período de tiempo específico.</p>



<p>En general, hay dos tipos de <strong>join</strong>s de <em>streams </em>en<em> Kafka Streams</em>:</p>



<ol class="wp-block-list">
<li><strong>Stream-Stream Join</strong>: Este tipo de <em>join</em> se utiliza para combinar dos o más <em>streams </em>en uno nuevo. Para realizar esta unión<em> </em>los <em>streams</em> deben estar particionados por las mismas claves. Se puede utilizar una <strong>ventana de tiempo</strong> para limitar el alcance del <em>join</em> y unir solamente los eventos que caen dentro ésta. Puede ser útil para limitar la cantidad de eventos que se procesan y reducir el costo de procesamiento.</li>



<li><strong>Table-Stream Join</strong>: se utiliza para combinar un <em>stream </em>con una <em>tabl</em>a utilizando una clave común. El resultado es un nuevo <em>stream</em> enriquecido con la información de la tabla. La <strong>ventana de tiempo </strong>se puede utilizar para definir el período de tiempo durante el cual se debe realizar el <em>join</em> entre la tabla y el <em>stream</em>. Esto permite realizar uniones en la <em>tabla </em>basados en un período de tiempo específico, en lugar de en la <em>tabla </em>completa.</li>



<li><strong>Table-table join: </strong>unión similar a la de dos tablas relacionales que se combinan cruzando por la misma clave. Este tipo de unión no admite ventanas de tiempo.</li>
</ol>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">from pyspark.sql.functions import from_json, col, struct
from pyspark.sql.types import StructType, StructField, IntegerType, StringType, DoubleType

# Configuración de Kafka
kafka_bootstrap_servers = "localhost:9092"
stream1_topic = "stream1"
stream2_topic = "stream2"
stream1_starting_offsets = "earliest"
stream2_starting_offsets = "earliest"

# Definición del esquema de los datos
stream1_schema = StructType([
    StructField("id", IntegerType()),
    StructField("producto", StringType()),
    StructField("cantidad", IntegerType())
])

stream2_schema = StructType([
    StructField("producto", StringType()),
    StructField("precio", DoubleType())
])

# Creación de los DataFrames que representan los streams
stream1_df = spark \
    .readStream \
    .format("kafka") \
    .option("kafka.bootstrap.servers", kafka_bootstrap_servers) \
    .option("subscribe", stream1_topic) \
    .option("startingOffsets", stream1_starting_offsets) \
    .load() \
    .selectExpr("CAST(value AS STRING)") \
    .select(from_json("value", stream1_schema).alias("data")) \
    .select("data.*")

stream2_df = spark \
    .readStream \
    .format("kafka") \
    .option("kafka.bootstrap.servers", kafka_bootstrap_servers) \
    .option("subscribe", stream2_topic) \
    .option("startingOffsets", stream2_starting_offsets) \
    .load() \
    .selectExpr("CAST(value AS STRING)") \
    .select(from_json("value", stream2_schema).alias("data")) \
    .select("data.*")
</code></pre>



<p>Una vez que tenemos los DataFrames, podemos hacer el join utilizando la función <code>join</code> de PySpark:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python"># Realizamos el join entre los dos streams
joined_df = stream1_df.join(stream2_df, "producto")

# Escribimos el resultado en la consola para visualizarlo
query = joined_df \
    .writeStream \
    .outputMode("append") \
    .format("console") \
    .start()

query.awaitTermination()
</code></pre>



<h3 class="has-medium-font-size wp-block-heading" id="kafka-stream-windowing" style="font-style:normal;font-weight:100">Funciones de ventana (windowing)</h3>



<p>Las<strong> funciones de ventana (windowing) </strong>en <em>Kafka Streams</em> son una herramienta poderosa que permiten procesar flujos de datos en ventanas de tiempo discretas. Básicamente, una función de ventana divide el flujo de datos en segmentos de tiempo llamados ventanas, y aplica una operación de agregación (como sumar, contar, promediar) sobre los eventos que caen dentro de cada ventana.</p>



<p>Las funciones de ventana son muy útiles para realizar análisis en tiempo real de flujos de datos, como calcular promedios móviles, contar eventos por hora o sumar valores por día. Al procesar datos en ventanas de tiempo, se pueden obtener resultados más precisos y oportunos que al procesar datos en tiempo real sin una ventana de tiempo definida. Utilizan cinco unidades de tiempo: DD (día), HH (hora), MI (minuto), SS (segundo) y MS (milisegundo).</p>



<p>En <em>Kafka Streams</em>, existen varios tipos de funciones de ventana, entre las que se incluyen:</p>



<p><strong>Tumbling windows</strong><br />Son ventanas de tiempo fijas y no solapadas. Por ejemplo, una ventana de 10 segundos que inicia en los segundos 0, 10, 20, etc.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-68.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1000" height="148" data-attachment-id="5321" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-68-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-68.png?fit=1000%2C148&amp;ssl=1" data-orig-size="1000,148" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-68" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-68.png?fit=1000%2C148&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-68.png?resize=1000%2C148&#038;ssl=1" alt="Tumbling window Kafka" class="wp-image-5321" title="Tumbling window Kafka" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-68.png?w=1000&amp;ssl=1 1000w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-68.png?resize=300%2C44&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-68.png?resize=768%2C114&amp;ssl=1 768w" sizes="auto, (max-width: 1000px) 100vw, 1000px" /></a><figcaption class="wp-element-caption">Tumbling window Kafka</figcaption></figure>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">SELECT count(*) FROM demo GROUP BY ID, TUMBLINGWINDOW(ss, 10);</code></pre>



<p></p>



<p><br /><strong>Hopping windows</strong><br />Son ventanas de tiempo que se solapan. Por ejemplo, una ventana de 10 segundos que se mueve cada 5 segundos, lo que resulta en ventanas que cubren los segundos 0-10, 5-15, 10-20, etc. Un evento puede estar en dos ventanas de tiempo.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-69.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="990" height="372" data-attachment-id="5324" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-69-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-69.png?fit=990%2C372&amp;ssl=1" data-orig-size="990,372" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-69" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-69.png?fit=990%2C372&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-69.png?resize=990%2C372&#038;ssl=1" alt="Hoping window Kafka" class="wp-image-5324" title="Hoping window Kafka" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-69.png?w=990&amp;ssl=1 990w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-69.png?resize=300%2C113&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-69.png?resize=768%2C289&amp;ssl=1 768w" sizes="auto, (max-width: 990px) 100vw, 990px" /></a><figcaption class="wp-element-caption">Hoping window Kafka</figcaption></figure>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">SELECT count(*) FROM demo GROUP BY ID, HOPPINGWINDOW(ss, 10, 5);</code></pre>



<p></p>



<p><br /><strong>Sliding windows</strong><br />Se desplazan a lo largo del tiempo. Por ejemplo, una ventana de 10 segundos que se desplaza cada segundo, lo que resulta en ventanas que cubren los segundos 0-10, 1-11, 2-12, etc. Un evento puede pertenecer a más de una ventana. Solo generan un output si se produce un evento y cada ventana tiene al menos uno. Os recomiendo esta lectura de <a href="https://docs.aws.amazon.com/es_es/kinesisanalytics/latest/dev/sliding-window-concepts.html" target="_blank" rel="noreferrer noopener">Amazon AWS</a>.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-72.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="985" height="369" data-attachment-id="5334" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-72-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-72.png?fit=985%2C369&amp;ssl=1" data-orig-size="985,369" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-72" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-72.png?fit=985%2C369&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-72.png?resize=985%2C369&#038;ssl=1" alt="Sliding window Kafka" class="wp-image-5334" title="Sliding window Kafka" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-72.png?w=985&amp;ssl=1 985w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-72.png?resize=300%2C112&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-72.png?resize=768%2C288&amp;ssl=1 768w" sizes="auto, (max-width: 985px) 100vw, 985px" /></a><figcaption class="wp-element-caption">Sliding window Kafka</figcaption></figure>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">SELECT count(*) FROM demo GROUP BY ID, SLIDINGWINDOW(mi, 1);</code></pre>



<p><br /><strong>Session windows</strong><br />Se definen en función de una brecha de tiempo entre eventos. Si no se reciben eventos durante un período de tiempo determinado, se cierra la ventana actual y se abre una nueva ventana. Una ventana de sesión comienza cuando ocurre el primer evento. Si otro evento ocurre dentro del timeout especificado desde la ingesta del último evento, la ventana se amplía para incluirlo.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-71.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="994" height="255" data-attachment-id="5329" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-71-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-71.png?fit=994%2C255&amp;ssl=1" data-orig-size="994,255" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-71" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-71.png?fit=994%2C255&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-71.png?resize=994%2C255&#038;ssl=1" alt="Session window Kafka" class="wp-image-5329" title="Session window Kafka" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-71.png?w=994&amp;ssl=1 994w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-71.png?resize=300%2C77&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-71.png?resize=768%2C197&amp;ssl=1 768w" sizes="auto, (max-width: 994px) 100vw, 994px" /></a><figcaption class="wp-element-caption">Session window Kafka</figcaption></figure>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">SELECT count(*) FROM demo GROUP BY ID, SESSIONWINDOW(mi, 2, 1);</code></pre>



<p>Cada tipo de ventana tiene sus propias características y se utiliza en diferentes situaciones. Además, en <em>Kafka Streams </em>se pueden configurar ventanas con diferentes duraciones y períodos de desplazamiento según las necesidades del caso de uso. Os recomiendo <a href="https://learn.microsoft.com/en-us/azure/stream-analytics/stream-analytics-window-functions" target="_blank" rel="noreferrer noopener">esta lectura</a> de la certificación Data Engineer Associate de Azure.</p>



<h2 class="wp-block-heading" id="ejecutar-kafka-spark-docker">Ejecutar Spark y Cluster Kafka en docker (PySpark Streaming)</h2>



<p>Vamos a seguir las <a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/blob/main/week_6_stream_processing/python/streams-example/pyspark/README.md" target="_blank" rel="noreferrer noopener">instrucciones </a>del bootcamp para levantar un contenedor Docker con Kafka. </p>



<p>1. Si aun no lo has hecho, clona la repo del <a href="https://github.com/DataTalksClub/data-engineering-zoomcamp">bootcamp </a>en local.</p>



<p>2. Arrancamos <em>Docker desktop</em></p>



<p>3. Ejecutamos el shell <code>week_6_streaming_processing\docker\spark\<strong>build.sh</strong></code> para descargar las imágenes de <em>docker </em>que vamos a necesitar para construir el contendor de <em>Spark</em> (spark-master, spark-worker y jupyterlab). Tardará un ratito ;).</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">bash build.sh</code></pre>



<p><br />4. Creamos la red para que los contenedores <em>Docker </em>de <em>Kafka </em>y <em>Spark </em>que vamos a crear tengan conectividad entre sí:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">docker network  create kafka-spark-network

docker volume create --name=hadoop-distributed-file-system</code></pre>



<p>5. Arrancamos los contenedores de <em>Kafka </em>y <em>Spark </em>(están ubicados en las carpetas con este nombre de la repo que hemos clonado en el primer paso).</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">docker compose up -d</code></pre>



<p>6. Comprobamos si se han levantado todos los servicios accediendo a los frontales web:</p>



<ul class="wp-block-list">
<li><strong>JupyterLab</strong>&nbsp;en <a href="http://localhost:8888/" target="_blank" rel="noreferrer noopener">localhost:8888</a></li>



<li><strong>Spark master</strong>&nbsp;en <a href="http://localhost:8080/" target="_blank" rel="noreferrer noopener">localhost:8080</a></li>



<li><strong>Spark worker I</strong>&nbsp;en <a href="http://localhost:8083/" target="_blank" rel="noreferrer noopener">localhost:8083</a></li>



<li><strong>Spark worker II</strong>&nbsp;en&nbsp;<a href="http://localhost:8084" target="_blank" rel="noreferrer noopener">localhost:8084</a></li>



<li><strong>Confluent Control center</strong> en <a href="http://localhost:9021" target="_blank" rel="noreferrer noopener">localhost:9021</a> (kafka)</li>
</ul>



<p>7. Para nuestra primera demo de PySpark y Kafka vamos a usar la repo del <a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/tree/main/week_6_stream_processing/python" target="_blank" rel="noreferrer noopener">bootcamp</a>, si la clonas, realmente usaremos los ficheros ubicados en los directorios <em>streams-example</em> y <em>resources</em>. Creamos un nuevo entorno de Python y nos ayudamos de un<strong> requirements.txt </strong>para instalar las librerías necesarias:</p>



<p>Creamos el nuevo entorno de Python:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">virtualenv venv-kafka
virtualenv/scripts/activate</code></pre>



<p>El fichero <strong>requirements.txt</strong> lo puedes copiar en la carpeta raíz del proyecto:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">kafka-python
confluent_kafka
requests
avro
pyspark</code></pre>



<p>Instalamos todas las librerías:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">pip install -r requirements.txt</code></pre>



<p>Iniciamos las pruebas lanzando el script del productor <code>producer.py</code>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">python producer.py</code></pre>



<p>Y observamos como se empiezan a generar topics!!</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-73.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1243" height="141" data-attachment-id="5348" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-73-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-73.png?fit=1243%2C141&amp;ssl=1" data-orig-size="1243,141" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-73" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-73.png?fit=1024%2C116&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-73.png?resize=1243%2C141&#038;ssl=1" alt="Kafka producer en Python" class="wp-image-5348" title="Kafka producer en Python" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-73.png?w=1243&amp;ssl=1 1243w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-73.png?resize=300%2C34&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-73.png?resize=1024%2C116&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-73.png?resize=768%2C87&amp;ssl=1 768w" sizes="auto, (max-width: 1243px) 100vw, 1243px" /></a></figure>



<p>Si lanzamos el script del <code>consumer.py</code> observamos cómo se cargan:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">python consumer.py</code></pre>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-74.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1222" height="146" data-attachment-id="5351" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-74-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-74.png?fit=1222%2C146&amp;ssl=1" data-orig-size="1222,146" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-74" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-74.png?fit=1024%2C122&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-74.png?resize=1222%2C146&#038;ssl=1" alt="Kafka consumer en Python" class="wp-image-5351" title="Kafka consumer en Python" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-74.png?w=1222&amp;ssl=1 1222w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-74.png?resize=300%2C36&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-74.png?resize=1024%2C122&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-74.png?resize=768%2C92&amp;ssl=1 768w" sizes="auto, (max-width: 1222px) 100vw, 1222px" /></a></figure>



<p>Podemos comprobar desde el <strong>Confluent Control Center</strong> (<a href="http://localhost:9021/">http://localhost:9021/</a>)el comportamiento de nuestro topic:</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-75.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1277" height="474" data-attachment-id="5362" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-75-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-75.png?fit=1277%2C474&amp;ssl=1" data-orig-size="1277,474" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-75" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-75.png?fit=1024%2C380&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-75.png?resize=1277%2C474&#038;ssl=1" alt="" class="wp-image-5362" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-75.png?w=1277&amp;ssl=1 1277w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-75.png?resize=300%2C111&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-75.png?resize=1024%2C380&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-75.png?resize=768%2C285&amp;ssl=1 768w" sizes="auto, (max-width: 1277px) 100vw, 1277px" /></a><figcaption class="wp-element-caption">Confluent Control Center</figcaption></figure>



<h3 class="has-medium-font-size wp-block-heading" style="font-style:normal;font-weight:100">Producer.py</h3>



<p>Este script se encarga de conectarse al fichero de datos de origen (<code>rides.csv</code>) con los datos y alimentar el <em>topic </em>con <em>mensajes</em>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">import csv
from time import sleep
from typing import Dict
from kafka import KafkaProducer

from settings import BOOTSTRAP_SERVERS, INPUT_DATA_PATH, PRODUCE_TOPIC_RIDES_CSV


def delivery_report(err, msg):
    if err is not None:
        print("Delivery failed for record {}: {}".format(msg.key(), err))
        return
    print('Record {} successfully produced to {} [{}] at offset {}'.format(
        msg.key(), msg.topic(), msg.partition(), msg.offset()))


class RideCSVProducer:
    def __init__(self, props: Dict):
        self.producer = KafkaProducer(**props)
        # self.producer = Producer(producer_props)

    @staticmethod
    def read_records(resource_path: str):
        records, ride_keys = [], []
        i = 0
        with open(resource_path, 'r') as f:
            reader = csv.reader(f)
            header = next(reader)  # skip the header
            for row in reader:
                # vendor_id, passenger_count, trip_distance, payment_type, total_amount
                records.append(f'{row[0]}, {row[1]}, {row[2]}, {row[3]}, {row[4]}, {row[9]}, {row[16]}')
                ride_keys.append(str(row[0]))
                i += 1
                if i == 5:
                    break
        return zip(ride_keys, records)

    def publish(self, topic: str, records: [str, str]):
        for key_value in records:
            key, value = key_value
            try:
                self.producer.send(topic=topic, key=key, value=value)
                print(f"Producing record for &lt;key: {key}, value:{value}&gt;")
            except KeyboardInterrupt:
                break
            except Exception as e:
                print(f"Exception while producing record - {value}: {e}")

        self.producer.flush()
        sleep(1)


if __name__ == "__main__":
    config = {
        'bootstrap_servers': [BOOTSTRAP_SERVERS],
        'key_serializer': lambda x: x.encode('utf-8'),
        'value_serializer': lambda x: x.encode('utf-8')
    }
    producer = RideCSVProducer(props=config)
    ride_records = producer.read_records(resource_path=INPUT_DATA_PATH)
    print(ride_records)
    producer.publish(topic=PRODUCE_TOPIC_RIDES_CSV, records=ride_records)
</code></pre>



<p>En este caso, se ha generado un <code>settings.py</code> para guardar toda la configuración:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">import pyspark.sql.types as T

INPUT_DATA_PATH = 'resources/fhv_tripdata_2019-01.csv'
BOOTSTRAP_SERVERS = 'localhost:9092'

TOPIC_WINDOWED_VENDOR_ID_COUNT = 'vendor_counts_windowed'

PRODUCE_TOPIC_RIDES_CSV = CONSUME_TOPIC_RIDES_CSV = 'fhv_csv'

RIDE_SCHEMA = T.StructType(
    [T.StructField("dispatching_base_num", T.StringType()),
     T.StructField('pickup_datetime', T.TimestampType()),
     T.StructField('dropOff_datetime', T.TimestampType()),
     T.StructField("PUlocationID", T.IntegerType()),
     T.StructField("DOlocationID", T.FloatType()),
     T.StructField("SR_Flag", T.IntegerType()),
     T.StructField("Affiliated_base_number", T.FloatType()),
     ])
</code></pre>



<h3 class="has-medium-font-size wp-block-heading" style="font-style:normal;font-weight:100">Consumer.py</h3>



<p>Por otro lado, desde este script vamos a ir recibiendo los <em>mensajes </em>del <em>topic</em>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">import argparse
from typing import Dict, List
from kafka import KafkaConsumer

from settings import BOOTSTRAP_SERVERS, CONSUME_TOPIC_RIDES_CSV


class RideCSVConsumer:
    def __init__(self, props: Dict):
        self.consumer = KafkaConsumer(**props)

    def consume_from_kafka(self, topics: List[str]):
        self.consumer.subscribe(topics=topics)
        print('Consuming from Kafka started')
        print('Available topics to consume: ', self.consumer.subscription())
        while True:
            try:
                # SIGINT can't be handled when polling, limit timeout to 1 second.
                msg = self.consumer.poll(1.0)
                if msg is None or msg == {}:
                    continue
                for msg_key, msg_values in msg.items():
                    for msg_val in msg_values:
                        print(f'Key:{msg_val.key}-type({type(msg_val.key)}), '
                              f'Value:{msg_val.value}-type({type(msg_val.value)})')
            except KeyboardInterrupt:
                break

        self.consumer.close()


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Kafka Consumer')
    parser.add_argument('--topic', type=str, default=CONSUME_TOPIC_RIDES_CSV)
    args = parser.parse_args()

    topic = args.topic
    config = {
        'bootstrap_servers': [BOOTSTRAP_SERVERS],
        'auto_offset_reset': 'earliest',
        'enable_auto_commit': True,
        'key_deserializer': lambda key: int(key.decode('utf-8')),
        'value_deserializer': lambda value: value.decode('utf-8'),
        'group_id': 'consumer.group.id.csv-example.1',
    }
    csv_consumer = RideCSVConsumer(props=config)
    csv_consumer.consume_from_kafka(topics=[topic])
</code></pre>



<h2 class="wp-block-heading" id="pyspark-structured-streaming">PySpark Structured Streaming </h2>



<p>La última sección de la semana es sobre <strong>PySpark structured streaming</strong>, módulo de la biblioteca <em>PySpark </em>que permite el procesamiento en tiempo real de grandes conjuntos de datos estructurados. Utiliza una estructura de datos en forma de tabla, lo que significa que puedes procesar y analizar los datos con mucha más facilidad y profundidad. Además, es muy resistente a fallos y escalable, por lo que puede manejar grandes volúmenes de datos sin problemas.</p>



<p>Ha sido la parte en la que más he sufrido para poder lanzar el script <a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/tree/main/week_6_stream_processing/python/streams-example/pyspark" target="_blank" rel="noreferrer noopener">streaming.py</a> con <code>spark-submit</code>. Tras reinstalar Python y reconfigurar las variables de entorno, he logrado ejecutarlo en Windows 10 con <em><strong>Powershell</strong>.</em> A continuación indico las instrucciones:</p>



<p>1. Revisa las variables de entorno en Windows, además de tenerlas declaradas de forma independiente, es necesario añadirlas al PATH:</p>



<ul class="wp-block-list">
<li><code>HADOOP_HOME</code>=C:\tools\hadoop-3.2.0</li>



<li><code>JAVA_HOME</code>=C:\Program Files\Java\jdk-11.0.17</li>



<li><code>SPARK_HOME</code>=C:\tools\spark-3.3.2-bin-hadoop3</li>



<li><code>PYSPARK_PYTHON</code>=python</li>
</ul>



<p>2. Arranca los contenedores de Docker que hemos comentado previametne con el <em>clúster </em>de <strong>Kafka </strong>y el<strong> Standalone Spark</strong> (ubicados en las carpetas correspondietnes).</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">docker compose up -d</code></pre>



<p>3. Ahora sí, dirígete a la carpeta donde está ubicado el script<code> streaming.py</code> (<em>\week_6_streaming_processing\streams-example\pyspark</em>) y ejecuta el spark-submit:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">spark-submit --master spark://localhost:7077 --num-executors 2 --executor-memory 512M --executor-cores 1  --packages org.apache.spark:spark-sql-kafka-0-10_2.12:3.3.1,org.apache.spark:spark-avro_2.12:3.3.1,org.apache.spark:spark-streaming-kafka-0-10_2.12:3.3.1 streaming.py</code></pre>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-76.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1200" height="733" data-attachment-id="5382" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-76-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-76.png?fit=1200%2C733&amp;ssl=1" data-orig-size="1200,733" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-76" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-76.png?fit=1024%2C625&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-76.png?resize=1200%2C733&#038;ssl=1" alt="PySpark Structured Streaming " class="wp-image-5382" title="PySpark Structured Streaming " srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-76.png?w=1200&amp;ssl=1 1200w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-76.png?resize=300%2C183&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-76.png?resize=1024%2C625&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-76.png?resize=768%2C469&amp;ssl=1 768w" sizes="auto, (max-width: 1200px) 100vw, 1200px" /></a><figcaption class="wp-element-caption">PySpark Structured Streaming </figcaption></figure>



<div class="wp-block-group has-background is-layout-constrained wp-block-group-is-layout-constrained" style="border-radius:5px;background-color:#f0cece">
<pre class="wp-block-code has-background" style="background-color:#f3e3e3"><code lang="bash" class="language-bash">WARN TaskSchedulerImpl: Initial job has not accepted any resources; check your cluster UI to ensure that workers are registered and have sufficient resources</code></pre>



<p>Tras pelearme mucho con este error finalmente lo he solucionado añadiendo un nuevo <em>worker </em>al clúster de <em>Spark</em>. Como no tengo claro por qué con los dos <em>workers </em>que creamos en el contenedor no es suficiente, estoy pendiente de la respuesta en <em>Slack</em>. En este <a href="https://medium.com/@karthik.jayaraman1/apache-spark-cluster-with-sufficient-resources-does-not-accept-new-jobs-5d29183bbbf9" target="_blank" rel="noreferrer noopener">artículo</a> indican dos opciones para resolverlo, pero ninguna me ha funcionado. El error indica que no hay memoria o <em>cores </em>suficientes ejecutar un nuevo job en el clúster, aunque si revisamos el <em>Spark UI </em>vemos que sí.</p>



<p><strong>Solución</strong></p>



<p>Crear un nuevo <em>worker </em>desde línea de comandos. Nos ubicamos en la carpeta donde hemos descomprimido <em>spark </em>(C:\tools\spark-3.3.2-bin-hadoop3) y ejecutamos:</p>



<pre class="wp-block-code has-background" style="background-color:#f3e3e3"><code lang="bash" class="language-bash">./bin/spark-class org.apache.spark.deploy.worker.Worker spark://localhost:7077</code></pre>



<p>Vemos en <em>Spark UI</em> cómo se ha creado el nuevo worker:</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-77.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1290" height="162" data-attachment-id="5388" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-77-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-77.png?fit=1470%2C185&amp;ssl=1" data-orig-size="1470,185" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-77" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-77.png?fit=1024%2C129&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-77.png?resize=1290%2C162&#038;ssl=1" alt="Spark Worker" class="wp-image-5388" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-77.png?w=1470&amp;ssl=1 1470w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-77.png?resize=300%2C38&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-77.png?resize=1024%2C129&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-77.png?resize=768%2C97&amp;ssl=1 768w" sizes="auto, (max-width: 1290px) 100vw, 1290px" /></a><figcaption class="wp-element-caption">Spark Worker</figcaption></figure>



<p>Y como devuelve los resultados de las dos operaciones de streaming:</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-78.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1123" height="487" data-attachment-id="5389" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/image-78-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-78.png?fit=1123%2C487&amp;ssl=1" data-orig-size="1123,487" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-78" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-78.png?fit=1024%2C444&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-78.png?resize=1123%2C487&#038;ssl=1" alt="PySpark structured streaming" class="wp-image-5389" title="PySpark structured streaming" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-78.png?w=1123&amp;ssl=1 1123w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-78.png?resize=300%2C130&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-78.png?resize=1024%2C444&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-78.png?resize=768%2C333&amp;ssl=1 768w" sizes="auto, (max-width: 1123px) 100vw, 1123px" /></a><figcaption class="wp-element-caption">PySpark structured streaming</figcaption></figure>
</div>
<p>La entrada <a href="https://n4gash.com/data-engineering-zoomcamp-semana-6-stream-processing/">Data Engineering Zoomcamp – Semana 6</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">5094</post-id>	</item>
		<item>
		<title>Data Engineering Zoomcamp – Semana 5</title>
		<link>https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=data-engineering-zoomcamp-semana-5-batch-spark</link>
					<comments>https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/#comments</comments>
		
		<dc:creator><![CDATA[Marcos Torregrosa]]></dc:creator>
		<pubDate>Fri, 24 Feb 2023 10:17:43 +0000</pubDate>
				<category><![CDATA[Cloud Data]]></category>
		<category><![CDATA[Data Engineering]]></category>
		<category><![CDATA[Apache Spark]]></category>
		<category><![CDATA[Batch processing]]></category>
		<category><![CDATA[BigQuery]]></category>
		<category><![CDATA[Dataproc]]></category>
		<category><![CDATA[ETL]]></category>
		<category><![CDATA[GCP]]></category>
		<category><![CDATA[Google Cloud Platform]]></category>
		<guid isPermaLink="false">https://www.n4gash.com/?p=4678</guid>

					<description><![CDATA[<p>Batch Processing con Spark Entramos en la recta final del bootcamp para Data Engineers. Las dos semanas que quedan están dedicadas al procesamiento de datos, comenzando esta con el batch o procesamiento por lotes empleando tecnología spark y la semana que viene con el procesamiento de datos en tiempo real o streaming con Apache Kafka. Prerequisitos Procesamiento de datos Batch processing (procesamiento de datos por lotes) Apache Spark ¿Qué es Apache Spark? Arquitectura Spark Ecosistema Spark PySpark (Python + Apache Spark) SparkSession Spark DataFrames con CSV y Parquet Partitions Transformaciones y Acciones de PySpark Funciones de Spark Trabajando con DataFrames…</p>
<p>La entrada <a href="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/">Data Engineering Zoomcamp – Semana 5</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">Batch Processing con Spark</h2>



<p>Entramos en la recta final del <a href="https://www.n4gash.com/2023/data-engineering-zoomcamp/" target="_blank" rel="noreferrer noopener">bootcamp para Data Engineers</a>. Las dos semanas que quedan están dedicadas al procesamiento de datos, comenzando esta con el <strong><em>batch </em></strong>o <strong>procesamiento por lotes</strong> empleando tecnología <strong>spark</strong> y la semana que viene con el <strong>procesamiento de datos en tiempo real</strong> o <em><strong>streaming </strong></em>con<em> Apache Kafka</em>.</p>



<ul class="wp-block-list">
<li><strong>Repo Bootcamp:</strong> <a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/tree/main/week_5_batch_processing" target="_blank" rel="noreferrer noopener">https://github.com/DataTalksClub/data-engineering-zoomcamp/tree/main/week_5_batch_processing</a></li>



<li><strong>Repo personal con ejercicios:</strong> <a href="https://github.com/hegdehog/data-engineering-zoomcamp-2023/tree/main/week_5_batch_processing" target="_blank" rel="noreferrer noopener">https://github.com/hegdehog/data-engineering-zoomcamp-2023</a></li>
</ul>



<div class="wp-block-group has-palette-color-8-color has-text-color has-background is-layout-constrained wp-block-group-is-layout-constrained" style="background-color:#7bdbb547">
<div class="wp-block-aioseo-table-of-contents">
<ul>
<li><a href="#prerequisitos">Prerequisitos</a></li>
<li><a href="#procesamiento-datos">Procesamiento de datos</a>
<ul><li><a href="#batch-processing">Batch processing (procesamiento de datos por lotes)</a></li></ul>
</li>
<li><a href="#apache-spark">Apache Spark</a>
<ul><li><a href="#que-es-apache-spark">¿Qué es Apache Spark?</a></li>
<li><a href="#arquitectura-spark">Arquitectura Spark</a>
</li>
<li><a href="#ecosistema-spark">Ecosistema Spark</a></li>
<li><a href="#pyspark-python-apache-spark">PySpark (Python + Apache Spark)</a>
<ul>
<li><a href="#SparkSession">SparkSession</a></li>
<li><a href="#spark-dataframes">Spark DataFrames con CSV y Parquet</a></li>
<li><a href="#spark-partitions">Partitions</a></li>
<li><a href="#spark-transformations-actions">Transformaciones y Acciones de PySpark</a></li>
<li><a href="#spark-functions">Funciones de Spark</a></li>
<li><a href="#trabajando-dataframes-spark-sql">Trabajando con DataFrames y Spark SQL</a>
<ul><li><a href="#spark-groupby">GroupBy (shuffle)</a></li>
<li><a href="#spark-join">Join (broadcasting)</a></li></ul>
</li>
</ul>
</li>
<li><a href="#spark-rdd">Resilient Distributed Datasets (RDDs)</a>
<ul><li><a href="#spark-rdd-ejemplos">Trabajando con RDDs en Spark</a></li></ul>
</li>
<li><a href="#spark-standalone-cluster">Spark Standalone Mode</a><ul>
<li><a href="#spark-submit">Spark Submit</a></li>
</ul></li>
<li><a href="#google-cloud-platform-dataproc-spark">Spark en Google Cloud Platform: Dataproc</a>
</li><li><a href="#spark-job-dataproc">Ejecutar job Spark en Dataproc</a>
<ul>
<li><a href="#spark-job-gcp-ui">Ejecutar job Spark desde Google Cloud Patform UI</a></li>
<li><a href="#spark-job-gcloud-sdk">Ejecutar job Spark desde gcloud SDK</a></li>
</ul>
</li><li><a href="#job-spark-dataproc-biquery">Job Spark + Dataproc + BigQuery</a></li>

</ul></li>


</ul>
</div>
</div>



<p class="has-text-align-center has-small-font-size"><em>Última actualización: 07/03/202</em>3</p>



<h2 class="wp-block-heading" id="prerequisitos">Prerequisitos</h2>



<p>En mi caso estoy trabajando en Windows, os recomiendo seguir la <a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/blob/main/week_5_batch_processing/setup/windows.md" target="_blank" rel="noreferrer noopener">guía</a> de instalación de los prerequesitos. Es importante tener en cuenta que si estamos usando <strong>Git bash </strong>o <strong>MINGW64</strong> el catálogo de variables de entorno no se replica en Windows, por lo que debemos arrancar los cuadernos de <em>jupyter </em>desde el <em>command shell </em>de unix para que coja correctamente los <em>path </em>configurados.</p>



<ul class="wp-block-list">
<li><strong>Java</strong>: Mínimo la versión 11 del JDK de java desde su <a href="https://jdk.java.net/archive/" target="_blank" rel="noreferrer noopener">página oficial</a> para trabajar con Spark. Recuerda que además de instalarlo, es necesario añadir a la variable de entorno <code>PATH</code> el <code>JAVA_HOME</code> con la ubicación de Java.</li>



<li><strong>Apache Hadoop:</strong> Descargamos los binarios de la versión 3.2 desde <a href="https://github.com/cdarlint/winutils/tree/master/hadoop-3.2.0" target="_blank" rel="noreferrer noopener">aquí</a>. Es necesario añadir también en el <code>PATH</code> la ubicación del directorio donde extraigamos los archivos y crear la variable <code>HADOOP_HOME</code>. </li>



<li><strong>Apache Spark:</strong> vamos a descargar la versión 3.3.2 de Spark desde su <a href="https://dlcdn.apache.org/spark/" target="_blank" rel="noreferrer noopener">repositorio</a>. Si estás usando <strong>Git bash </strong>en Windows y no tienes instalado <code>wget</code> para descargar paquetes, debes hacerlo desde este <a href="https://eternallybored.org/misc/wget/" target="_blank" rel="noreferrer noopener">enlace</a> y copiar el archivo .exe en la ruta bin donde tengas instalado <em>git bash</em> (<em><strong>C:\Program Files\Git\mingw64\bin</strong></em>). Puedes acceder al GUI de Spark publicado en el puerto 4040: <a href="http://localhost:4040/jobs/" target="_blank" rel="noreferrer noopener">http://localhost:4040/jobs/</a>. Creamos la variable de entorno <code>SPARK_HOME</code> y la añadimos también al <code>PATH</code> de Windows.</li>



<li><strong>PySpark</strong>: librería de Python para trabajar con Spark. Podemos seguir esta <a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/blob/main/week_5_batch_processing/setup/pyspark.md" target="_blank" rel="noreferrer noopener">guía</a> para su instalación. Si nos da problemas, específicamente el error <em>Module not found</em> cuando añadimos al PATH la ruta de <em>PySpark</em>, revisa la nota indicada en la repo del bootcamp (hay que cambiar el nombre del fichero en la ruta <code>PATH</code> si hemos descargado otra versión). Añadimos la variable de entorno <code>PYTHONPATH</code> según las instrucciones.</li>
</ul>



<h2 class="wp-block-heading" id="procesamiento-datos">Procesamiento de datos</h2>



<p>El <strong>procesamiento de datos</strong> es el conjunto de operaciones y técnicas utilizadas para transformar datos brutos o sin procesar en información útil y significativa. Esta información puede ser utilizada para tomar decisiones, realizar análisis, predicciones o automatizar procesos. Existen distintas técnicas o enfoques para procesar los datos donde destacan los procesos <strong>batch </strong>(por lotes) y <strong>streaming </strong>(en tiempo real), que a su vez pueden clasificarse en procesamiento distribuido, paralelo, online, transaccional o analítico. Esta semana del bootcamp está centrada en el batch, por lo que vamos a dejar por aquí una breve pincelada del procesamiento s<em>treaming </em>donde profundizaremos la semana que viene.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column has-background is-layout-flow wp-block-column-is-layout-flow" style="background-color:#e7edc6;padding-top:var(--wp--preset--spacing--50);padding-right:var(--wp--preset--spacing--50);padding-bottom:var(--wp--preset--spacing--50);padding-left:var(--wp--preset--spacing--50)">
<h2 class="has-text-align-center has-large-font-size wp-block-heading" style="font-style:normal;font-weight:100">Batch</h2>



<p>El procesamiento por<strong> lotes</strong> <strong>(batch)</strong> se utiliza para realizar periódicamente trabajos de datos repetitivos y de gran tamaño. Las tareas de transformación, filtrado y clasificación pueden ser intensivas en cálculo e ineficientes si se ejecutan en transacciones individuales. En su lugar se procesan estas tareas en lotes, a menudo en momentos de menor actividad cuando los recursos informáticos están más disponibles, como al final del día o durante la noche. </p>



<p>Consideremos una tienda online que recibe pedidos durante todo el día. En lugar de procesar cada pedido a medida que ocurre, el sistema podría recopilar todos los pedidos al final de cada día y compartirlos en un bloque con el equipo de cumplimiento de pedidos.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-91.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="439" height="125" data-attachment-id="4684" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-91/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-91.png?fit=439%2C125&amp;ssl=1" data-orig-size="439,125" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-91" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-91.png?fit=439%2C125&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-91.png?resize=439%2C125&#038;ssl=1" alt="Streaming processing" class="wp-image-4684" title="Streaming processing" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-91.png?w=439&amp;ssl=1 439w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-91.png?resize=300%2C85&amp;ssl=1 300w" sizes="auto, (max-width: 439px) 100vw, 439px" /></a><figcaption class="wp-element-caption">Batch processing</figcaption></figure>
</div>



<div class="wp-block-column has-background is-layout-flow wp-block-column-is-layout-flow" style="background-color:#e7edc6;padding-top:var(--wp--preset--spacing--50);padding-right:var(--wp--preset--spacing--50);padding-bottom:var(--wp--preset--spacing--50);padding-left:var(--wp--preset--spacing--50)">
<h3 class="has-text-align-center has-large-font-size wp-block-heading" style="font-style:normal;font-weight:100"> Streaming</h3>



<p>El procesamiento de datos en <strong>tiempo real</strong> o<strong> streaming</strong> (<em>streaming processing</em> / <em>real-time processing</em>) es un método que se realiza en tiempo real, a medida que se generan o se reciben los datos. Cuando la cantidad de datos es desconocida o infinita es preferible aplicar el procesamiento por <em>streaming </em>en lugar de <em>batch</em>.</p>



<p>Este enfoque es adecuado para tareas que requieren una respuesta rápida, como la detección de fraudes, la monitorización de sistemas en línea, el análisis de datos de sensores (IoT) o de logs.</p>



<p>El procesamiento de datos en <em>streaming </em>permite tomar decisiones rápidas basadas en la información más reciente disponible.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-93.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="439" height="118" data-attachment-id="4687" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-93/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-93.png?fit=439%2C118&amp;ssl=1" data-orig-size="439,118" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-93" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-93.png?fit=439%2C118&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-93.png?resize=439%2C118&#038;ssl=1" alt="Batch processing" class="wp-image-4687" title="Batch processing" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-93.png?w=439&amp;ssl=1 439w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-93.png?resize=300%2C81&amp;ssl=1 300w" sizes="auto, (max-width: 439px) 100vw, 439px" /></a><figcaption class="wp-element-caption">Streaming processing</figcaption></figure>
</div>
</div>



<h3 class="has-medium-font-size wp-block-heading" id="batch-processing" style="font-style:normal;font-weight:100">Batch processing (procesamiento de datos por lotes)</h3>



<p>Como hemos indicado en la introducción, el procesamiento <strong>batch </strong>o por lotes trabaja con conjuntos de datos que procesa a intervalos de tiempo. Por ejemplo, si un banco o aseguradora debe cumplir sus compromisos regulatorios hacia entidades de control de forma mensual, existirá un proceso <strong>batch mensual</strong> que agrupe toda la información, la transforme, agregue y cocine para generar el modelo de datos del <em>reporting</em>. Habitualmente los procesos <em>batch </em>tienen intervalos:</p>



<ul class="wp-block-list">
<li>Mensual (más habitual)</li>



<li>Semanal (más habitual)</li>



<li>Diario (más habitual)</li>



<li>Cada hora</li>



<li>3 por hora</li>



<li>Cada 5 minutos</li>



<li>&#8230;</li>
</ul>



<p>A nivel de tecnología podemos crear un proceso batch en cualquier lenguaje de programación (por ejemplo, Python, Java, Scala&#8230;), modelar los datos con <a href="https://www.n4gash.com/2023/data-engineering-zoomcamp/">dbt</a> y orquestar los scripts con <em>Apache Airflow</em>, <a href="https://www.n4gash.com/2023/data-engineering-zoomcamp/"><em>Prefect</em></a>, <em>Control-M</em>, etc.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-96.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1028" height="132" data-attachment-id="4699" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-96/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-96.png?fit=1028%2C132&amp;ssl=1" data-orig-size="1028,132" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-96" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-96.png?fit=1024%2C131&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-96.png?resize=1028%2C132&#038;ssl=1" alt="Flujo de procesamiento batch" class="wp-image-4699" title="Flujo de procesamiento batch" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-96.png?w=1028&amp;ssl=1 1028w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-96.png?resize=300%2C39&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-96.png?resize=1024%2C131&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-96.png?resize=768%2C99&amp;ssl=1 768w" sizes="auto, (max-width: 1028px) 100vw, 1028px" /></a><figcaption class="wp-element-caption">Flujo de procesamiento batch</figcaption></figure>



<figure class="wp-block-table aligncenter is-style-stripes, tab-head is-style-stripes"><table class="has-fixed-layout"><thead><tr><th class="has-text-align-left" data-align="left">Ventajas</th><th class="has-text-align-left" data-align="left">Desventajas</th></tr></thead><tbody><tr><td class="has-text-align-left" data-align="left">Muy <strong>eficiente </strong>para tareas repetitivas y de gran tamaño. En lugar de procesar cada transacción de datos individualmente se trabaja con grandes cantidades de una sola vez.</td><td class="has-text-align-left" data-align="left">Como los procesos batch se realizan en intervalos de tiempo específico, los datos tienen <strong>retraso en estar disponibles</strong>.</td></tr><tr><td class="has-text-align-left" data-align="left">Consume menos recursos que el procesamiento en tiempo real, lo que implica <strong>menores costes </strong>de infraestructura y capacidad de cómputo. </td><td class="has-text-align-left" data-align="left">Si ocurre un error durante el procesamiento por lotes se puede<strong> perder información </strong>que obliga a reprocesar.</td></tr><tr><td class="has-text-align-left" data-align="left">Se aprovechan los períodos de menor actividad de máquina (fines de semana u horas nocturnas) para procesar grandes cantidades de datos de manera <strong>más rápida</strong>.</td><td class="has-text-align-left" data-align="left">El procesamiento por lotes puede ser <strong>complejo de implementar</strong> y mantener, especialmente cuando se trata de programar y garantizar la disponibilidad de recursos adecuados.</td></tr><tr><td class="has-text-align-left" data-align="left">Facilita la <strong>escalabilidad, </strong>en caso necesario se puede aprovisionar mayor capacidad de cómputo (<em>clusters </em>de <em>spark</em>)</td><td class="has-text-align-left" data-align="left"></td></tr><tr><td class="has-text-align-left" data-align="left">Un <em>batch</em> <em>job </em>se puede relanzar tantas veces sean necesarias.</td><td class="has-text-align-left" data-align="left"></td></tr><tr><td class="has-text-align-left" data-align="left">Existen en el mercado multitud de herramientas y tecnologías para<strong> facilitar la gestión</strong> una malla batch.</td><td class="has-text-align-left" data-align="left"></td></tr></tbody></table><figcaption class="wp-element-caption">Pros y contras de procesamiento por lotes (batch)</figcaption></figure>



<h2 class="wp-block-heading" id="apache-spark">Apache Spark</h2>



<h3 class="has-medium-font-size wp-block-heading" id="que-es-apache-spark" style="font-style:normal;font-weight:100">¿Qué es Apache Spark?</h3>



<p><strong>Apache Spark</strong> es un motor de procesamiento de datos de código abierto utilizado para realizar análisis y transformación de grandes volúmenes de datos en clústeres de servidores distribuidos (paralelizando el procesamiento en distintos nodos). Fue desarrollado originalmente en la Universidad de California, Berkeley, y ahora es mantenido por la Apache Software Foundation. Básicamente lo que hace <em>Spark</em> es dividir una carga de trabajo en varias porciones que distribuye entre distintos nodos o máquinas para que trabajen de forma paralela y cuando finalizan, agrupan los resultados y lo devuelven.</p>



<p>Spark es conocido por su velocidad, ya que puede procesar grandes conjuntos de datos mucho más rápido que otras herramientas con el mismo objetivo, como <em>Hadoop MapReduce</em>. <em>Spark </em>también admite múltiples lenguajes de programación, como<em> Scala, Java, Python</em> y<em> R</em>. Además, proporciona una variedad de bibliotecas y herramientas para diferentes tareas de procesamiento de datos, como <em>batch, streaming, </em>procesamiento de gráficos y aprendizaje automático (<em>ML</em> o <em>machine learning</em>). </p>



<p>Dentro de un ecosistema <em>Data Lake</em> utilizar <strong>Spark </strong>nos va a ayudar en el proceso de <strong>transformación de los datos</strong>. En un <em>Data lake</em> los datos se almacenan como ficheros, habitualmente <em>csv</em> o <em>parquet</em>, que podemos consultar como si fuera un modelo de datos SQL mediante herramientas tales como <em>Hive, Presto </em>o<em> Athena</em> (en cloud <em>Amazon AWS</em>), o <em>BigQuery </em>(en <em>Google Cloud Platform</em>). En el caso de que la lógica sea más compleja y no podamos resolverla mediante SQL, entra en juego <strong>Spark</strong>. En un mismo flujo de trabajo podemos combinar ambas opciones, cuando los datos puedan transformarse por SQL usaremos este camino, y cuando sean transformaciones complejas lo haremos con <em>Spark</em>.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-98.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1286" height="427" data-attachment-id="4709" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-98/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-98.png?fit=1286%2C427&amp;ssl=1" data-orig-size="1286,427" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-98" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-98.png?fit=1024%2C340&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-98.png?resize=1286%2C427&#038;ssl=1" alt="Workflow de transformación de datos con Spark o Hive / BigQuery" class="wp-image-4709" title="Workflow de transformación de datos con Spark o Hive / BigQuery" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-98.png?w=1286&amp;ssl=1 1286w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-98.png?resize=300%2C100&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-98.png?resize=1024%2C340&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-98.png?resize=768%2C255&amp;ssl=1 768w" sizes="auto, (max-width: 1286px) 100vw, 1286px" /></a><figcaption class="wp-element-caption">Workflow de transformación de datos con Spark o Hive / BigQuery</figcaption></figure>



<h3 class="has-medium-font-size wp-block-heading" id="arquitectura-spark" style="font-style:normal;font-weight:100">Arquitectura Spark</h3>



<p><strong>Spark </strong>se basa en una arquitectura de procesamiento distribuido, lo que significa que utiliza un clúster o grupo de ordenadores para procesar datos. Consta de varios componentes que se comunican entre sí para ejecutar las tareas. </p>



<p>Un clúster de <em>Spark </em>consiste en un proceso <strong>Driver </strong>que se ejecuta dentro de un <strong>nodo Master </strong>y procesos <strong>Executor </strong>que se ejecutan dentro de cada uno de los <strong>nodos Worker</strong>. Cuando se envía un trabajo a <em>Spark</em>, el <em>Driver </em>particiona y distribuye el trabajo en forma de tareas a los procesos <em>Executor </em>(en diferentes <em>nodos Worker</em>) para su procesamiento adicional. A medida que se ejecuta el trabajo de la aplicación, los <em>Executor </em>informan al <em>Driver </em>sobre el estado de la tareas, y así éste mantiene el estado general del trabajo de la aplicación. Cada <em>Worker </em>tiene su propia memoria y CPU, y está conectado a otros <em>Workers</em> a través de una red de alta velocidad. Pueden ser añadidos o eliminados del clúster según sea necesario para ajustar la capacidad de procesamiento</p>



<p>¿Cómo sabe el proceso <em>Driver </em>qué <em>Executors </em>están disponibles para el procesamiento y a quién distribuir las tareas? gracias al <strong>Administrador de Clúster</strong> <strong>(Cluster Manager)</strong>. Realiza un seguimiento del estado de los recursos del clúster (qué procesos <em>Executor </em>en qué <em>nodos Worker</em> están disponibles, etc.). El <em>Driver </em>está conectado con el <em>Cluster Manager</em> a través de una <em>SparkSession </em>o un <em>SparkContext</em> (<em>SparkSession </em>estaría por encima del <em>SparkContext</em>).</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-8.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1111" height="344" data-attachment-id="4888" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-8-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-8.png?fit=1111%2C344&amp;ssl=1" data-orig-size="1111,344" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-8" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-8.png?fit=1024%2C317&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-8.png?resize=1111%2C344&#038;ssl=1" alt="Arquitectura Spark" class="wp-image-4888" title="Arquitectura Spark" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-8.png?w=1111&amp;ssl=1 1111w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-8.png?resize=300%2C93&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-8.png?resize=1024%2C317&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-8.png?resize=768%2C238&amp;ssl=1 768w" sizes="auto, (max-width: 1111px) 100vw, 1111px" /></a><figcaption class="wp-element-caption">Arquitectura Spark</figcaption></figure>



<p>La arquitectura de Apache Spark cosiste principalmente en dos capas de abstracción:</p>



<p id="-resilient-distributed-datasets-rdd--it-is-a-key-tool-for-data-computation-it-enables-you-to-recheck-data-in-the-event-of-a-failure-and-it-acts-as-an-interface-for-immutable-data-it-helps-in-recomputing-data-in-case-of-failures-and-it-is-a-data-structure-there-are-two-methods-for-modifying-rdds-transformations-and-actions"><strong>Resilient Distributed Datasets (RDD):</strong><br />Es la célula del ecosistema Spark, el elemento básico para trabajar con los datos. Se caracterizan porque son <strong>inmutables </strong>(los datos no pueden cambiarse una vez se crean), <strong>distribuidos </strong>(siguiendo el patrón de Spark, se dividen en particiones entre los nodos del clúster) y <strong>resilientes </strong>(automáticamente es capaz de regenerar una partición que se haya perdido). Hay dos operaciones que pueden realizarse sobre los RDDs: <em>transformaciones </em>y <em>acciones</em>.</p>



<p id="-directed-acyclic-graph-dag--the-driver-converts-the-program-into-a-dag-for-each-job-the-apache-spark-eco-system-includes-various-components-such-as-the-api-core-spark-sql-streaming-and-real-time-processing-mlib-and-graph-x-a-sequence-of-connection-between-nodes-is-referred-to-as-a-driver-as-a-result-you-can-read-volumes-of-data-using-the-spark-shell-you-can-also-use-the-spark-context--cancel-run-a-job-task-work-and-job-computation-to-stop-a-job"><strong>Directed Acyclic Graph (DAG):</strong><br />El driver convierte cada tarea en un job de tipo DAG (<em>grafo acíclico dirigido</em>) formado por vértices (RDD) y aristas (sus transformaciones). En lenguaje coloquial, cada tarea es un trabajo dividido en etapas (vértices) que siguen un secuencia lineal (acíclico). Las etapas están construidas con alguno de los componentes de Spark <em>(API Core, Spark SQL, Streaming, real-time processing, MLlIB </em>o<em> GraphX)</em>.</p>



<h3 class="has-medium-font-size wp-block-heading" id="ecosistema-spark" style="font-style:normal;font-weight:100">Ecosistema Spark</h3>



<p>El ecosistema de <em>Spark </em>lo forman los siguientes elementos:</p>



<ul class="wp-block-list">
<li><strong>Spark Core</strong>: Es el componente principal de Spark y proporciona las funcionalidades básicas, como el procesamiento distribuido, la programación paralela y la tolerancia a fallos. Es la API para el procesamiento batch.</li>



<li><strong>Spark SQL</strong>: Proporciona una API para trabajar con datos estructurados o semiestructurados usando SQL. Nos ofrece tres vías para ello:








































<ul class="wp-block-list">
<li><strong>DataFrames</strong>: estructura de datos distribuida que se organiza en columnas con nombres y tipos de datos (similar a una tabla relacional). Se pueden crear a partir de archivos de datos estructurados como CSV o JSON, o mediante la lectura de datos de una base de datos relacional utilizando Spark SQL. Los DataFrames también se pueden transformar mediante operaciones de filtrado, agregación y unión para realizar tareas de análisis de datos.</li>



<li><strong>Datasets</strong>:  es una API más segura y fuertemente tipada que se encuentra en la parte superior de los DataFrames. Los Datasets permiten trabajar con datos estructurados de manera más fácil y natural, ya que se definen los esquemas de los datos de forma estática. Se generan a partir de ficheros CSV, JSON, bases de datos relacionales, etc. También se pueden transformar mediante operaciones de filtrado, agregación y unión.</li>
</ul>



<ul class="wp-block-list">
<li>Lenguaje <strong>SQL </strong>a través de una API SQL pata trabajar sobre <em>DataFrames </em>y <em>Datasets</em>. Admite una amplia gama de funciones SQL como SELECT, FROM, WHERE, JOIN, GROUP BY, ORDER BY, etc.</li>
</ul>
</li>



<li><strong>Spark Streaming</strong>: Es un componente que permite procesar datos en tiempo real, como publicaciones de <em>Twitter </em>o <em>Facebook</em>. Procesa los datos en lotes y utiliza la misma API que <em>Spark Core</em>.</li>



<li><strong>Spark MLlib</strong>: Proporciona algoritmos de aprendizaje automático para realizar tareas como clasificación, regresión y agrupamiento de datos en modo distribuido.</li>



<li><strong>Spark GraphX</strong>: Proporciona herramientas para trabajar con datos gráficos y realizar análisis de redes y grafos.</li>
</ul>



<h3 class="has-medium-font-size wp-block-heading" id="pyspark-python-apache-spark" style="font-style:normal;font-weight:100">PySpark (Python + Apache Spark)</h3>



<p><strong>PySpark </strong>es una librería Python para desarrollar aplicaciones que explotan todas las capacidades de <strong>Apache Spark</strong> (procesamiento distribuido paralelizando las cargas de trabajo entre nodos) ideal para proyectos de datos a gran escala y machine learning (ML). Necesitamos descargar la librería <code>pyspark</code> bien por <code>pip</code> o siguiendo las <a href="#prerequisitos">instrucciones </a>del bootcamp.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">import pyspark</code></pre>



<h4 class="has-medium-font-size wp-block-heading" id="SparkSession" style="font-style:normal;font-weight:100">SparkSession</h4>



<p><strong>SparkSession </strong>es una clase en <strong>PySpark </strong>que se utiliza para trabajar con <em>Spark </em>y que proporciona una interfaz única para interactuar con diferentes tipos de datos en Spark, como RDD, DataFrames y DataSet. <strong>SparkSession </strong>se utiliza para crear y configurar <em>SparkContext</em>, <em>SQLContext </em>y <em>HiveContext </em>en una sola sesión.</p>



<p>Para instanciar una <strong>SparkSession</strong> debemos invocar el constructor y pasarle varios parámetros, aunque en vamos a trabajar sólo con los dos primeros:</p>



<ul class="wp-block-list">
<li><code>appName</code>: Nombre de la aplicación de Spark, por ejemplo «<em>test</em>«</li>



<li><code>master</code>: especifica la dirección del clúster de <em>Spark </em>en el que se ejecutará la aplicación. Puede ser una URL de un cluster de Spark independiente o  ejecución local:
<ul class="wp-block-list">
<li><code>local</code>: Especifica el modo de ejecución local, es decir, que se ejecutará en una única máquina como un proceso local.</li>



<li><code>local[N]</code>: Especifica el modo de ejecución local con N hilos.</li>



<li><code>local[*]</code>: Especifica el modo de ejecución local con tantos hilos como núcleos de CPU disponibles.</li>



<li><code>yarn</code>: Especifica el modo de ejecución en un cluster de YARN.</li>



<li><code>mesos</code>: Especifica el modo de ejecución en un cluster de Mesos.</li>



<li><code>spark://HOST:PORT</code>: Especifica la URL de un cluster de Spark independiente.</li>



<li><code>k8s://https://HOST:PORT</code>: Especifica la URL del API server de Kubernetes en el que se ejecutará la aplicación.</li>
</ul>
</li>



<li><code>config</code>: Configuraciones adicionales de Spark.
<ul class="wp-block-list">
<li><code>spark.executor.memory</code>: Cantidad de memoria asignada a cada ejecutor.</li>



<li><code>spark.driver.memory</code>: Cantidad de memoria asignada al driver.</li>



<li><code>spark.sql.shuffle.partitions</code>: Número de particiones utilizadas por las operaciones de shuffle en SQL.</li>



<li><code>spark.serializer</code>: Serializador utilizado para serializar/deserializar objetos.</li>



<li><code>spark.ui.port</code>: Puerto utilizado por la interfaz de usuario web de Spark.</li>
</ul>
</li>
</ul>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">from pyspark.sql import SparkSession

spark = SparkSession.builder \
    .appName("test") \
    .master("local[*]") \
    .getOrCreate()</code></pre>



<p>Para acceder al Spark UI podemos consultar la URL <a href="http://localhost:4040" target="_blank" rel="noreferrer noopener">http://localhost:4040</a> (si no hemos especificado otro puerto en la configuración). Si deseamos crear otra <em>SparkSession </em>para otro notebook podemos especificar un nuevo puerto distinto al de por defecto 4040 en el <code>.config()</code>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">from pyspark.sql import SparkSession

spark = SparkSession.builder \
    .master("local[*]") \
    .appName('test') \
    .config("spark.ui.port", "4041") \
    .getOrCreate()</code></pre>



<h4 class="has-medium-font-size wp-block-heading" id="spark-dataframes" style="font-style:normal;font-weight:100">Spark DataFrames con CSV y Parquet</h4>



<p>Como hemos mencionado previamente, un <em>DataFrame </em>es un set de datos con tipado y organizado en columnas. A continuación vamos a ver cómo cargar en un DataFrame de <strong>PySpark </strong>el contenido de un <em>CSV </em>o un <em>Parquet</em>. Como breve pincelada del formato <em>Parquet</em><strong> </strong>indicar que es un tipo de archivo muy utilizado en el mundo de datos al tener un alto grado de compresión (1:10 respecto a CSV) gracias a que almacena los datos de forma columnar, no por filas, lo que permite ajustar el tipado y por tanto el espacio necesario. Otra de las ventajas es que el esquema de los datos está incluido en el propio fichero, por lo que no es necesario inferirlo o asignarlo.</p>



<p><strong>CSV</strong><br />Para cargar un <strong>CSV </strong>utilizaremos el método <code>read.csv()</code>. Podemos especificar algunos parámetros de configuración, por ejemplo <code>header=True</code> indica que la primera fila es la cabecera y <code>inferSchema=True</code> va a inferir esquema en base a los datos de cada columna.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df = spark.read.csv('taxi+_zone_lookup.csv', header=True, inferSchema=True)</code></pre>



<p></p>



<p><strong>Parquet</strong><br />En el caso de de <strong>Parquet </strong>usaremos el método <code>read.parquet()</code>. La ventaja de cargar un archivo <em>Parquet </em>en lugar de otro formato es que estos archivos suelen tener un tamaño más pequeño y una mayor eficiencia de procesamiento debido a su estructura columnar y compresión de datos.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df = spark.read.parquet('taxi+_zone_lookup.parquet')</code></pre>



<h4 class="has-medium-font-size wp-block-heading" id="spark-partitions" style="font-style:normal;font-weight:100">Partitions</h4>



<p>En <strong>PySpark</strong>, las particiones se utilizan para dividir un conjunto de datos en fragmentos más pequeños y distribuirlos a través de un clúster de Spark. Cada partición es procesada en paralelo por un <strong><em>executor </em></strong>en el <em>clúster</em>, lo que permite procesar grandes conjuntos de datos de manera más eficiente y escalable. Por ejemplo, si deseamos procesar un fichero <em>CSV </em>o <em>parquet </em>de 1GB, podríamos segmentarlo en 10 particiones para que cada una sea trabajada de forma paralela en 10 nodos del clúster.</p>



<p>Las particiones son una de las unidades fundamentales de procesamiento en Spark y se utilizan en varios tipos de objetos de datos, como RDD, DataFrames y DataSets. En general, se recomienda tener un número adecuado de particiones para un conjunto de datos dado, ya que demasiadas particiones pueden provocar un exceso de sobrecarga en la comunicación entre los ejecutores y demasiado pocas particiones pueden resultar en una utilización ineficiente de los recursos del clúster.</p>



<p>Las particiones se pueden especificar al crear un RDD o al leer un conjunto de datos en un DataFrame o DataSet. Por ejemplo, al leer un archivo CSV en un DataFrame, se puede especificar el número de particiones mediante el parámetro <code>numPartitions</code>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df = spark.read.csv("path/to/csv/file.csv" \
        , header=True \
        , inferSchema=True \
        , numPartitions=8)</code></pre>



<p>También es posible ajustar el número de particiones de un RDD o DataFrame existente utilizando el método <code>repartition</code> o <code>coalesce</code>. El método <code>repartition</code> redistribuirá los datos aleatoriamente a través del clúster y creará el número especificado de particiones, mientras que <code>coalesce</code> fusionará particiones adyacentes para crear el número especificado de particiones. </p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df = df.repartition(24)</code></pre>



<p>Si queremos persistir el <em>DataFrame </em>en un fichero <em>parquet</em>, por ejemplo, usaremos el método <code>write.parquet</code> pasándole como parámetro el directorio para la salida:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df = df.repartition(24)
df.write.parquet('fhvhv/2021/01/')</code></pre>



<p>Si consultamos en la Spark UI (<a href="http://localhost:4040" target="_blank" rel="noreferrer noopener">http://localhost:4040</a>) el progreso del trabajo podemos ver que está dividido en dos etapas: en primer lugar <em>Spark </em>crea tantas particiones como <em>cores </em>tenga nuestra CPU (recuerda que hemos creado el <em>SparkSession </em>con <code>Local[*]</code>), por lo que si tenemos 4, va a dividir el <em>DataFrame </em>en 4 particiones. Cada partición genera una <em>task</em>. Cuando hayan finalizado las 4 tareas de esta etapa, se genera el particionado que hayamos especificado. En el ejemplo, se generarán 10 <em>tasks</em>, cada una para cada partición. Como nuestra CPU sólo tiene 4 <em>cores </em>las <em>tasks </em>se van a ir encolando y ejecutando según se vayan quedando disponibles. Cada una de las particiones se almacena en un fichero con el sufijo <code>_snappy.parquet</code>, que es el formato de compresión de alta velocidad por defecto de <em>parquet</em>.</p>



<p>Para ilustrar el ejemplo, mi equipo tiene 12 cores y vamos a crear 24 particiones del fichero <em>parquet</em>. </p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="886" height="589" data-attachment-id="4810" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-102/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image.png?fit=886%2C589&amp;ssl=1" data-orig-size="886,589" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image.png?fit=886%2C589&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image.png?resize=886%2C589&#038;ssl=1" alt="CPU y cores para calcular tasks de Spark" class="wp-image-4810" title="CPU y cores para calcular tasks de Spark" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image.png?w=886&amp;ssl=1 886w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image.png?resize=300%2C199&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image.png?resize=768%2C511&amp;ssl=1 768w" sizes="auto, (max-width: 886px) 100vw, 886px" /></a><figcaption class="wp-element-caption">CPU y cores para calcular tasks de Spark</figcaption></figure>



<p>Al lanzar el comando <code>.write.parquet()</code> <em>Spark </em>va a generar dos etapas: la primera de 12 <em>tasks </em>que se corresponden con los 12 cores de mi CPU y la segunda con las 24 <em>tasks </em>correspondientes a las 24 particiones que he especificado.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-1.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1290" height="460" data-attachment-id="4812" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-1-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-1.png?fit=1296%2C462&amp;ssl=1" data-orig-size="1296,462" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-1" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-1.png?fit=1024%2C365&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-1.png?resize=1290%2C460&#038;ssl=1" alt="Spark UI y Spark partitions" class="wp-image-4812" title="Spark UI y Spark partitions" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-1.png?w=1296&amp;ssl=1 1296w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-1.png?resize=300%2C107&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-1.png?resize=1024%2C365&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-1.png?resize=768%2C274&amp;ssl=1 768w" sizes="auto, (max-width: 1290px) 100vw, 1290px" /></a><figcaption class="wp-element-caption">Spark UI y Spark partitions</figcaption></figure>



<p>Si consultamos el directorio podremos observar como se han generado 24 ficheros de tipo <code>.snappy.parquet</code>:</p>



<figure class="wp-block-image aligncenter size-large wp-duotone-ffffff-000000-1"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-2.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="374" data-attachment-id="4820" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-2-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-2.png?fit=1046%2C382&amp;ssl=1" data-orig-size="1046,382" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-2" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-2.png?fit=1024%2C374&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-2-1024x374.png?resize=1024%2C374&#038;ssl=1" alt="Ficheros snappy.parquet de Spark partitions " class="wp-image-4820" title="Ficheros snappy.parquet de Spark partitions " srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-2.png?resize=1024%2C374&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-2.png?resize=300%2C110&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-2.png?resize=768%2C280&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-2.png?w=1046&amp;ssl=1 1046w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Ficheros snappy.parquet de Spark partitions </figcaption></figure>



<h4 class="has-medium-font-size wp-block-heading" id="spark-transformations-actions" style="font-style:normal;font-weight:100">Transformaciones y Acciones de PySpark</h4>



<p>En PySpark, las operaciones se dividen en dos categorías: <em>Transformaciones </em>(<em>Transformations</em>) y <em>Acciones </em>(<em>Actions</em>).</p>



<p>Las <strong>transformaciones </strong>son operaciones que toman un <em>DataFrame </em>como entrada, aplican una transformación y generan un nuevo <em>DataFrame </em>como resultado. Son operaciones «<em>perezosas</em>» (<em>lazy</em>), lo que significa que no se ejecutan inmediatamente sino que se almacenan en el grafo de transformación hasta que se requiere una acción.</p>



<ul class="wp-block-list">
<li><code>select()</code>: selecciona columnas específicas.</li>



<li><code>filter()</code>: filtra filas que satisfacen una condición específica.</li>



<li><code>groupBy()</code>: agrupa filas por una o más columnas.</li>



<li><code>join()</code>: une dos <em>DataFrames </em>en función de una o más columnas comunes.</li>



<li><code>distinct()</code>: devuelve un nuevo <em>DataFrame </em>que contiene solo valores distintos.</li>



<li><code>orderBy()</code>: ordena las filas en función de una o más columnas.</li>



<li><code>withColumn()</code>: agrega una nueva columna o reemplaza una columna existente con una nueva.</li>



<li><code>drop()</code>: elimina una o más columnas.</li>
</ul>



<p>Por otro lado, las <strong>acciones </strong>son operaciones que toman un <em>DataFrame </em>como entrada y producen un resultado que se almacena o se muestra. Las acciones son operaciones que «activan» el grafo de transformación y hacen que se ejecuten las transformaciones almacenadas.</p>



<ul class="wp-block-list">
<li><code>show()</code>: muestra una vista previa de un número determinado de filas.</li>



<li><code>count()</code>: cuenta el número de filas.</li>



<li><code>collect()</code>: recopila todos los datos en la memoria del driver.</li>



<li><code>write()</code>: escribe en un archivo o fuente de datos externa.</li>



<li><code>first()</code>: devuelve la primera fila.</li>



<li><code>max()</code> y <code>min()</code>: devuelve el valor máximo o mínimo en una columna numérica.</li>



<li><code>sum()</code>: devuelve la suma de los valores en una columna numérica.</li>



<li><code>mean()</code>: devuelve la media de los valores en una columna numérica.</li>



<li><code>pivot()</code>: crea una tabla dinámica a partir.</li>
</ul>



<p>En general, se recomienda minimizar el número de acciones en <em>PySpark </em>y maximizar el uso de transformaciones, ya que éstas son más eficientes y permiten una mejor optimización del flujo de procesamiento de datos.</p>



<h4 class="has-medium-font-size wp-block-heading" id="spark-functions" style="font-style:normal;font-weight:100">Funciones de Spark</h4>



<p>Dentro del ecosistema <em>Spark</em> podemos encontrar dos tipos de funciones: un grupo de <a href="https://spark.apache.org/docs/latest/api/sql/index.html">funciones <em>built-in</em></a> incluidas por defecto para realizar distintas operaciones y las <em>User-defined functions (UDF)</em> que son funciones personalizadas que podemos desarrollar a nuestro gusto. </p>



<p><strong>Funciones built-in</strong><br />La forma de importarlas es la siguiente. Para ver todas las opciones disponibles basta con escribir en una celda de un cuaderno F. y pulsar el tabulador.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">from pyspark.sql import functions as F</code></pre>



<p>Siguiendo el ejemplo del curso, podemos convertir un <code>DATETIME</code> a <code>DATE</code> utilizando la función <code>to_date()</code>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df \
    .withColumn('pickup_date', F.to_date(df.pickup_datetime)) \
    .withColumn('dropoff_date', F.to_date(df.dropoff_datetime)) \
    .select('pickup_date', 'dropoff_date', 'PULocationID', 'DOLocationID') \
    .show()</code></pre>



<p></p>



<p><strong>User-defined functions (UDF)</strong><br />Una UDF (User-Defined Function) en Spark es una función definida por el usuario que se puede usar para realizar transformaciones de datos personalizadas en un <em>DataFrame </em>o <em>RDD</em>. Las UDF se definen en Python, Java, Scala o R, y se pueden aplicar en PySpark, Spark SQL y Spark Streaming. Para definir una UDF en PySpark, podemos usar la función <code>udf()</code> de la biblioteca <code>pyspark.sql.functions</code>. A continuación se muestra un ejemplo de cómo definir una UDF en PySpark para calcular el cuadrado de un número:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">from pyspark.sql import functions as F
from pyspark.sql.types import DoubleType

def square(x):
    return x*x

square_udf = F.udf(square, DoubleType())
</code></pre>



<p>En este ejemplo, se define una función <code>square()</code> que calcula el cuadrado de un número, y luego se usa la función F.<code>udf()</code> para convertirla en una UDF. La UDF se define para que tome un argumento de entrada de tipo <em>double </em>y devuelva un valor de tipo <em>double</em>.</p>



<p>Una vez que se ha definido una UDF, se puede aplicar a una columna de un DataFrame mediante la función <code>withColumn()</code> de <em>PySpark</em>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df \
    .withColumn('square_trip_miles', square_udf(df.trip_miles)) \
    .select("hvfhs_license_num","trip_miles","square_trip_miles") \
    .show(10)</code></pre>



<h4 class="has-medium-font-size wp-block-heading" id="trabajando-dataframes-spark-sql" style="font-style:normal;font-weight:100">Trabajando con DataFrames y Spark SQL</h4>



<p>Podemos trabajar con un <em>DataFrame </em>utilizando los métodos de <em>PySpark </em>o bien aprovechar uno de los componentes clave de <em>Spark:</em><strong> API SQL</strong>. Gracias al cual podemos consultar <em>DataFrames </em>como si fueran tablas relacionales y lanzar queries en SQL estándar. Vamos a verlo con dos ejemplos del mismo caso de uso. Vamos a cargar un parquet con las ventas de una tienda online. El fichero tiene tres columnas (<code>producto</code>, <code>cantidad </code>y <code>fecha</code>):</p>



<p><strong>Consulta de DataFrame con PySpark</strong><br />Para explotar un <em>DataFrame </em>con <em>PySpark </em>tenemos a nuestra los <a href="#spark-transformations-actions">operadores de transformación</a> que comentamos más arriba. Todos ellos se apilan sobre el mismo DataFrame por puntos » . «:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("ConsultaVentas").getOrCreate()

df = spark.read.parquet("ruta/ventas.parquet")

fecha = "2022-02-28"
df_productos_vendidos= df.filter(df.fecha == fecha).groupBy("producto").sum("cantidad")

df_productos_vendidos.show()</code></pre>



<p></p>



<p><strong>Consulta de DataFrame con Spark SQL</strong><br />Al utilizar el API de Spark SQL vamos a poder escribir SQL estándar y consultar el DataFrame como si se tratase de una tabla relacional utilizando el método <code>spark.sql()</code>. Como paso previo es necesario crear una <strong>tabla temporal</strong> a partir del <em>DataFrame</em> utilizando la función <code>createOrReplaceTempView()</code> y pasándole como parámetro el nombre de la tabla, en nuestro caso <code>ventas</code>.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("ConsultaVentas").getOrCreate()

df = spark.read.parquet("ruta/ventas.parquet")

df.createOrReplaceTempView("ventas")

fecha = "2022-02-28"
df_productos_vendidos = spark.sql(f"SELECT producto, SUM(cantidad) FROM ventas WHERE fecha = '{fecha}' GROUP BY producto")

df_productos_vendidos.show()</code></pre>



<p></p>



<p><strong>DataFrames de NY Taxis</strong><br />En los ejemplos del bootcamp vamos a utilizar los datos de las carreras de taxis de NY <em>green </em>y <em>yellow </em>de 2020 y 2021. Podemos descargarlos de forma manual, desde un notebook de Jupyter y con el shell script que han preparado en el curso (<a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/blob/main/week_5_batch_processing/code/download_data.sh" target="_blank" rel="noreferrer noopener">download_data.sh</a>) y lanzando los comandos:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">bash download_data.sh yellow 2020
bash download_data.sh yellow 2021
bash download_data.sh green 2020
bash download_data.sh green 2021</code></pre>



<p><br />1. Vamos a generar un <em>DataFrame</em> agrupando todos los ficheros mensuales por cada tipo de taxi (<em>green</em> y <em>yellow</em>). </p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df_green = spark.read.csv('data/raw/green/*/*', header=True, inferSchema=True)
df_yellow = spark.read.csv('data/raw/yellow/*/*', header=True, inferSchema=True)
</code></pre>



<p><br />2. Como el objetivo es unirlos en un único <em>DataFrame</em> debemos asegurarnos de que tienen el mismo esquema. Podemos consultarlo con <code>df_yellow.schema()</code> y <code>df_green.schema()</code>. Vamos a realizar varias acciones para crear el nuevo df con las columnas que tienen en común ambos y además, añadir una nueva que identifique el origen:</p>



<ul class="wp-block-list">
<li>Añadir una nueva columna en cada <em>df</em> para identificar el origen usando el operador de transformación <code>.withColumn(nombre_columna, valor)</code> y la función <code>F.lit()</code> de las librería built-in de Spark que nos permite especificar un valor literal: <code>.withColumn('service_type', F.lit('green'))</code></li>



<li>Columnas que no están en ambos <em>DataFrames</em>. La forma rápida es convirtiendo la lista de columnas a <code>SET</code> de <em>python </em>y combinarlos, pero perdemos el orden de las columnas, por lo que tenemos que usar un bucle <code>FOR</code> para recorrer comparar ambas listas y generar una nueva con las que tienen en común.</li>



<li>Columnas con las fechas de subida y bajada (<code>xy_pickup _datetime</code><em> </em>y <code>xy_dropoff_datetime</code>) tienen distinto nombre. Vamos a renombrarlas en ambos <em>df</em>  con el operador de transformación <code>.withColumnRenamed(columna, nueva_columna)</code>. </li>
</ul>



<pre class="wp-block-code has-palette-color-5-background-color has-background"><code lang="python" class="language-python">from pyspark.sql import functions as F

# renombramos columnas de fecha:

df_green = df_green \
    .withColumnRenamed('lpep_pickup_datetime', 'pickup_datetime') \
    .withColumnRenamed('lpep_dropoff_datetime', 'dropoff_datetime')

df_yellow = df_yellow \
    .withColumnRenamed('tpep_pickup_datetime', 'pickup_datetime') \
    .withColumnRenamed('tpep_dropoff_datetime', 'dropoff_datetime')

# generamos lista con columnas en común entre ambos DataFrames
common_colums = []

yellow_columns = set(df_yellow.columns)

for col in df_green.columns:
    if col in yellow_columns:
        common_colums.append(col)

# generamos los nuevos df por cada tipo sólo con las columnas en común y añadiendo el service_type para identificar el tipo de taxi:
df_green_sel = df_green \
    .select(common_colums) \
    .withColumn('service_type', F.lit('green'))

df_yellow_sel = df_yellow \
    .select(common_colums) \
    .withColumn('service_type', F.lit('yellow'))</code></pre>



<p><br />3. Por último vamos a combinar ambos <em>DataFrames </em>que ahora sí tienen el mismo esquema y una nueva columna para identificar el tipo de taxi.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df_trips_data = df_green_sel.unionAll(df_yellow_sel)</code></pre>



<p>Podemos comprobar el número de registros por tipo de taxi usando PySpark:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df_trips_data.groupBy('service_type').count().show()</code></pre>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-3.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="149" data-attachment-id="4848" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-3-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-3.png?fit=1111%2C162&amp;ssl=1" data-orig-size="1111,162" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-3" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-3.png?fit=1024%2C149&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-3-1024x149.png?resize=1024%2C149&#038;ssl=1" alt="Comprobamos con PySpark la distribución de datos en el nuevo DataFrame" class="wp-image-4848" title="Comprobamos con PySpark la distribución de datos en el nuevo DataFrame" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-3.png?resize=1024%2C149&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-3.png?resize=300%2C44&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-3.png?resize=768%2C112&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-3.png?w=1111&amp;ssl=1 1111w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Comprobamos con PySpark la distribución de datos en el nuevo DataFrame</figcaption></figure>



<p>Si observamos el DAG generado por el job Spark vemos que se leen los dos CSV por separado y se unen en único RDD:</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-4.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="745" height="1024" data-attachment-id="4849" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-4-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-4.png?fit=786%2C1080&amp;ssl=1" data-orig-size="786,1080" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-4" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-4.png?fit=745%2C1024&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-4-745x1024.png?resize=745%2C1024&#038;ssl=1" alt="DAG de job de Spark" class="wp-image-4849" title="DAG de job de Spark" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-4.png?resize=745%2C1024&amp;ssl=1 745w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-4.png?resize=218%2C300&amp;ssl=1 218w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-4.png?resize=768%2C1055&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-4.png?w=786&amp;ssl=1 786w" sizes="auto, (max-width: 745px) 100vw, 745px" /></a><figcaption class="wp-element-caption">DAG de job de Spark</figcaption></figure>



<h4 class="has-palette-color-1-color has-text-color has-medium-font-size wp-block-heading" id="spark-groupby" style="font-style:normal;font-weight:100">GroupBy en Spark</h4>



<p>Vamos a analizar cómo gestiona internamente Spark el operador <strong>GroupBy</strong>. Podemos probar con <em>Spark SQL</em> o con <em>PySpark</em>. Para nuestro ejemplo vamos a calcular el beneficio y cantidad de viajes por hora y zona de los taxis.</p>



<p>Si lo queremos calcular con <em>PySpark</em>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">import pyspark
from pyspark.sql import SparkSession
from pyspark.sql import functions as F

spark = SparkSession.builder \
    .master("local[*]") \
    .appName('test_groupby') \
    .config("spark.ui.port", "4042") \
    .getOrCreate()

df_green = spark.read.csv('data/raw/green/*/*', header=True, inferSchema=True)

df_green_revenue = df_green.filter("lpep_pickup_datetime &gt;= '2020-01-01 00:00:00'") \
    .withColumn("hour", F.date_trunc("hour", "lpep_pickup_datetime")) \
    .groupBy("hour", "PULocationID") \
    .agg({"total_amount": "sum", "*": "count"}) \
    .withColumnRenamed("sum(total_amount)", "amount") \
    .withColumnRenamed("count(1)", "number_records") \
    .orderBy("hour","PULocationID")</code></pre>



<p><br />O bien con <em>Spark SQL</em>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">import pyspark
from pyspark.sql import SparkSession

spark = SparkSession.builder \
    .master("local[*]") \
    .appName('test_groupby') \
    .config("spark.ui.port", "4042") \
    .getOrCreate()

df_green = spark.read.csv('data/raw/green/*/*', header=True, inferSchema=True)

df_green.createOrReplaceTempView("green")

df_green_revenue = spark.sql("""
SELECT 
    date_trunc('hour', lpep_pickup_datetime) AS hour, 
    PULocationID AS zone,

    SUM(total_amount) AS amount,
    COUNT(1) AS number_records
FROM
    green
WHERE
    lpep_pickup_datetime &gt;= '2020-01-01 00:00:00'
GROUP BY
    1, 2  
""")

df_green_revenue.show()</code></pre>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-5.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="438" data-attachment-id="4862" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-5-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-5.png?fit=1117%2C478&amp;ssl=1" data-orig-size="1117,478" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-5" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-5.png?fit=1024%2C438&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-5-1024x438.png?resize=1024%2C438&#038;ssl=1" alt="Resultado del group by sobre el DataFrame de Spark" class="wp-image-4862" title="Resultado del group by sobre el DataFrame de Spark" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-5.png?resize=1024%2C438&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-5.png?resize=300%2C128&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-5.png?resize=768%2C329&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-5.png?w=1117&amp;ssl=1 1117w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Resultado del group by sobre el DataFrame de Spark</figcaption></figure>



<p>Vamos a persistir la salida en un <em>parquet </em>y analizar cómo realiza la tarea <em>Spark</em>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df_green_revenue.write.parquet('data/report/revenue/green', mode="overwrite")</code></pre>



<p><br />Al tratarse de procesamiento distribuido se<em> </em>dividen los datos en particiones que son enviadas a cada no de los <em>executors</em>. Para combinar los resultados de cada uno se realiza la operación de <strong>shuffle</strong><em>.</em></p>



<p>Si observamos en la Spark UI, vemos que la tarea se ha dividido en dos <em>stages</em>, el primero para preparar el <strong>groupBy </strong>(genera las agrupaciones intermedias de cada <em>executor</em>) y el segundo genera el resultado final combinándolas (<strong>shuffle</strong>). <em>Spark </em>no sólo ha generado dos <em>stages </em>para la tarea, si no que además ha ejecutado cada <em>stage </em>en un job independiente. El motivo es que la operación de <em>shuffle </em>es muy costosa. Para agilizar el procesamiento el primer job almacena los resultados intermedios en la caché que luego usará el <em>shuffl</em>e, por eso en el segundo <em>job </em>aparece el primer <em>stage </em>como <strong><em>Skipped</em></strong>, que ya ha sido procesado en el primero. Más info en la <a href="https://spark.apache.org/docs/1.5.0/programming-guide.html#performance-impact">documentación de Shuffle</a> de Spark. </p>



<figure class="wp-block-image aligncenter size-large is-resized"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-7.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" data-attachment-id="4872" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-7-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-7.png?fit=1288%2C220&amp;ssl=1" data-orig-size="1288,220" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-7" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-7.png?fit=1024%2C175&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-7-1024x175.png?resize=1024%2C175&#038;ssl=1" alt="Tarea de groupBy en Spark se divide en dos jobs y stages" class="wp-image-4872" width="1024" height="175" title="Tarea de groupBy en Spark se divide en dos jobs y stages" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-7.png?resize=1024%2C175&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-7.png?resize=300%2C51&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-7.png?resize=768%2C131&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-7.png?w=1288&amp;ssl=1 1288w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Tarea de groupBy en Spark se divide en dos jobs y stages</figcaption></figure>



<p>Si vemos el DAG de cada job:</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-6.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="973" height="729" data-attachment-id="4867" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-6-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-6.png?fit=973%2C729&amp;ssl=1" data-orig-size="973,729" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-6" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-6.png?fit=973%2C729&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-6.png?resize=973%2C729&#038;ssl=1" alt="DAG de groupBy en Spark" class="wp-image-4867" title="DAG de groupBy en Spark" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-6.png?w=973&amp;ssl=1 973w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-6.png?resize=300%2C225&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-6.png?resize=768%2C575&amp;ssl=1 768w" sizes="auto, (max-width: 973px) 100vw, 973px" /></a><figcaption class="wp-element-caption">DAG de groupBy en Spark</figcaption></figure>



<p>Si analizamos cada uno de los <em>stages </em>en detalle, en el primero los datos se dividen en particiones y cada una es enviada a un <em>executor</em>, donde se realizan las agrupaciones y operaciones necesarias (<em>filter</em> y <em>groupBy</em>). Esto genera las agrupaciones o <strong>resultados intermedios</strong>, donde H es la hora, Z la zona y los dos KPIs con el beneficio y total de carreras.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-17.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1109" height="452" data-attachment-id="4907" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-17-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-17.png?fit=1109%2C452&amp;ssl=1" data-orig-size="1109,452" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-17" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-17.png?fit=1024%2C417&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-17.png?resize=1109%2C452&#038;ssl=1" alt="Spark GroupBy" class="wp-image-4907" title="Spark GroupBy" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-17.png?w=1109&amp;ssl=1 1109w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-17.png?resize=300%2C122&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-17.png?resize=1024%2C417&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-17.png?resize=768%2C313&amp;ssl=1 768w" sizes="auto, (max-width: 1109px) 100vw, 1109px" /></a></figure>



<p>En el segundo <em>stage</em> se realiza la operación de <strong>shuffle </strong>para combinar todos los <em>resultados intermedios</em> y agrupar los datos con la misma clave en la misma partición. Spark identifica como clave las columnas del <code><em>GroupB</em>y</code> (en nuestro ejemplo <code>hour</code> y <code>zone</code>). Por último, se realiza un nuevo <code>GroupBy</code> sobre las nuevas particiones para realizar reducir los datos agrupándolos por la clave. Es posible que en alguna de las particiones generadas por la operación <em>shuffle</em> haya datos de distintas claves, pero en la última operación de agrupación se reparten en las particiones correspondientes.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-18.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1102" height="455" data-attachment-id="4909" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-18-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-18.png?fit=1102%2C455&amp;ssl=1" data-orig-size="1102,455" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-18" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-18.png?fit=1024%2C423&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-18.png?resize=1102%2C455&#038;ssl=1" alt="Spark GroupBy" class="wp-image-4909" title="Spark GroupBy" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-18.png?w=1102&amp;ssl=1 1102w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-18.png?resize=300%2C124&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-18.png?resize=1024%2C423&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-18.png?resize=768%2C317&amp;ssl=1 768w" sizes="auto, (max-width: 1102px) 100vw, 1102px" /></a></figure>



<h4 class="has-palette-color-1-color has-text-color has-medium-font-size wp-block-heading" id="spark-join" style="font-style:normal;font-weight:100">Join en Spark</h4>



<p>En Spark podemos combinar dos <em>DataFrames </em>como si se tratasen de dos tablas relacionales mediante el operador <code>.join(df, on, how)</code>, donde:</p>



<ol class="wp-block-list">
<li><code>df</code>: El <em>DataFrame </em>que se unirá con el <em>DataFrame </em>principal. Debe especificarse como un objeto <em>DataFrame </em>de <em>Pyspark</em>.</li>



<li><code>on</code>: Una o varias columnas comunes a ambas tablas que se utilizarán para unir los <em>df</em>. Puede especificarse como una cadena que contenga el nombre de la columna, o una lista de cadenas que contengan los nombres de las columnas (<code>["id","nombre"]</code>).</li>



<li><code>how</code>: El tipo de unión que se realizará. Puede tomar uno de los siguientes valores:
<ul class="wp-block-list">
<li><code>'inner'</code>: Realiza una unión interna, es decir, devuelve sólo los registros que tienen coincidencias en ambas tablas.</li>



<li><code>'outer'</code> o <code>'full'</code>: Realiza una unión externa completa, es decir, devuelve todos los registros de ambas tablas, incluso si no tienen una coincidencia en la otra tabla.</li>



<li><code>'left'</code> o <code>'left_outer'</code>: Realiza una unión externa izquierda, es decir, devuelve todos los registros del DataFrame izquierdo y los registros coincidentes del DataFrame derecho. Si no hay coincidencias en el DataFrame derecho, los valores para las columnas del DataFrame derecho serán <code>null</code>.</li>



<li><code>'right'</code> o <code>'right_outer'</code>: Realiza una unión externa derecha, es decir, devuelve todos los registros del DataFrame derecho y los registros coincidentes del DataFrame izquierdo. Si no hay coincidencias en el DataFrame izquierdo, los valores para las columnas del DataFrame izquierdo serán <code>null</code>.</li>



<li><code>'left_semi'</code>: Realiza una unión semijoin izquierda, es decir, devuelve sólo los registros del DataFrame izquierdo que tienen una coincidencia en el DataFrame derecho.</li>



<li><code>'left_anti'</code>: Realiza una unión anti-izquierda, es decir, devuelve sólo los registros del DataFrame izquierdo que no tienen una coincidencia en el DataFrame derecho.</li>
</ul>
</li>
</ol>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">from pyspark.sql.functions import *

df = df1.join(df2, on='id', how='left')</code></pre>



<p>En el bootcamp profundizan en cómo se comporta Spark internamente cuando debe realizar operaciones de <em>join</em> entre tablas muy grandes o cuando una de las tablas es muy grande y la otra pequeña. En el primer caso, el comportamiento es muy similar al del <a href="#groupby">GroupBy</a>. Spark particiona los DataFrames, hace un <strong>shuffle </strong>para reorganizarlos por la clave y finalmente los agrupa en particiones por clave. </p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-25.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1090" height="446" data-attachment-id="4957" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-25-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-25.png?fit=1090%2C446&amp;ssl=1" data-orig-size="1090,446" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-25" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-25.png?fit=1024%2C419&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-25.png?resize=1090%2C446&#038;ssl=1" alt="Join Spark" class="wp-image-4957" title="Join Spark" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-25.png?w=1090&amp;ssl=1 1090w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-25.png?resize=300%2C123&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-25.png?resize=1024%2C419&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-25.png?resize=768%2C314&amp;ssl=1 768w" sizes="auto, (max-width: 1090px) 100vw, 1090px" /></a></figure>



<p>En el ejemplo observamos como hacemos un <strong>outer join</strong> entre los <em>DataFrames </em>de carreras de taxis <em>yellow </em>y <em>green</em> para quedarnos todos los registros de ambas tablas. Esto a a generar un nuevo DataFrame con las columnas de la clave (<code>hour</code> y <code>zone</code>) y las columnas de <code>amount</code> y <code>number_records</code> de <em>yellow </em>y <em>green</em>. Para facilitar el trabajo se renombran. Podéis consultar el <a href="https://github.com/hegdehog/data-engineering-zoomcamp-2023/blob/main/week_5_batch_processing/code/07_spark_groupby_join.ipynb" target="_blank" rel="noreferrer noopener">notebook completo en mi repo</a>.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df_green_revenue_tmp = df_green_revenue \
    .withColumnRenamed('amount', 'green_amount') \
    .withColumnRenamed('number_records', 'green_number_records')

df_yellow_revenue_tmp = df_yellow_revenue \
    .withColumnRenamed('amount', 'yellow_amount') \
    .withColumnRenamed('number_records', 'yellow_number_records')

df_join = df_green_revenue_tmp.join(df_yellow_revenue_tmp, on=['hour', 'PULocationID'], how='outer')

df_join.write.parquet('data/report/revenue/total', mode='overwrite')</code></pre>



<p>Si comprobamos en el Spark UI observamos que la operación se ha dividido en tres <em>jobs </em>que procesan un <em>stage </em>cada uno. Los dos primeros se encargan de la lectura de cada uno de los DataFrames y el último (con 12 tasks) es el encargado de realizar el <em>join</em>.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-21.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1290" height="199" data-attachment-id="4937" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-21-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-21.png?fit=1292%2C199&amp;ssl=1" data-orig-size="1292,199" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-21" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-21.png?fit=1024%2C158&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-21.png?resize=1290%2C199&#038;ssl=1" alt="Stages de join Spark" class="wp-image-4937" title="Stages de join Spark" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-21.png?w=1292&amp;ssl=1 1292w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-21.png?resize=300%2C46&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-21.png?resize=1024%2C158&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-21.png?resize=768%2C118&amp;ssl=1 768w" sizes="auto, (max-width: 1290px) 100vw, 1290px" /></a><figcaption class="wp-element-caption">Stages de join Spark</figcaption></figure>



<p>Si entramos a ver el DAG del último job observamos que los dos primeros <em>stages </em>están <em>skipped </em>(se han ejecutado previamente). En el <em>Stage 87</em> se encarga de realizar el <em>shuffle </em>y por último agrupar el resultado (<em>reduces</em>).</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-20.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="974" height="663" data-attachment-id="4935" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-20-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-20.png?fit=974%2C663&amp;ssl=1" data-orig-size="974,663" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-20" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-20.png?fit=974%2C663&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-20.png?resize=974%2C663&#038;ssl=1" alt="DAG de join en Spark" class="wp-image-4935" title="DAG de join en Spark" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-20.png?w=974&amp;ssl=1 974w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-20.png?resize=300%2C204&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-20.png?resize=768%2C523&amp;ssl=1 768w" sizes="auto, (max-width: 974px) 100vw, 974px" /></a><figcaption class="wp-element-caption">DAG de join en Spark</figcaption></figure>



<p>En el segundo caso, vamos a cruzar una tabla <em>lookup </em>pequeña (<code>zones</code>) con una muy grande (el <em>DataFrame </em>que hemos acabamos de generar combinando las carreras de ambos tipos de taxi). </p>



<p>En <em>Spark</em> cuando se realiza una operación de <code>join</code> entre dos <em>DataFrames</em> el proceso de unión puede ser costoso en términos de recursos computacionales, especialmente si uno de ellos es mucho más grande que el otro. Una forma de optimizar este proceso de unión es utilizar el <strong>broadcasting</strong>.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-24.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1260" height="363" data-attachment-id="4955" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-24-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-24.png?fit=1260%2C363&amp;ssl=1" data-orig-size="1260,363" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-24" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-24.png?fit=1024%2C295&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-24.png?resize=1260%2C363&#038;ssl=1" alt="Arquitectura de Broadcasting en Spark" class="wp-image-4955" title="Arquitectura de Broadcasting en Spark" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-24.png?w=1260&amp;ssl=1 1260w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-24.png?resize=300%2C86&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-24.png?resize=1024%2C295&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-24.png?resize=768%2C221&amp;ssl=1 768w" sizes="auto, (max-width: 1260px) 100vw, 1260px" /></a><figcaption class="wp-element-caption">Arquitectura de Broadcasting en Spark</figcaption></figure>



<p>En lugar de enviar el <em>DataFrame </em>pequeño a través de la red y unirlo con el <em>DataFrame </em>grande en los <em>nodos de trabajo (wokers)</em>, <em>Spark </em>envía el <em>DataFrame </em>pequeño a todos los nodos de trabajo que procesan el <em>DataFrame </em>grande. De esta manera, cada <em>worker</em> puede realizar la unión localmente sin necesidad de realizar una operación de red costosa.</p>



<p>El <strong>broadcasting </strong>de Spark se puede utilizar en operaciones de <em>join </em>cuando se cumplen las siguientes condiciones:</p>



<ul class="wp-block-list">
<li>Uno de los <em>DataFrames </em>es significativamente más pequeño que el otro, por lo general, cuando el tamaño del <em>DataFrame </em>pequeño es menor que el tamaño de la memoria disponible en cada nodo del clúster.</li>



<li>El tamaño del <em>DataFrame </em>pequeño es menor que el parámetro <code>spark.sql.autoBroadcastJoinThreshold</code>. Este parámetro define el tamaño máximo que un <em>DataFrame </em>puede tener para que Spark utilice la técnica de <em>broadcasting</em>.</li>
</ul>



<p>Para el ejemplo los nombres de columna no son iguales en ambos <em>df</em> en lugar de especificar el parámetro <code>on</code> vamos a indicar directamente las columnas clave de cada uno <code>df_join.PU</code><code>LocationID</code> <code>== df_zones.LocationID</code>. La operación de <em>join </em>se lleva todas las columnas al nuevo <em>DataFrame</em>. Como en este caso la clave tiene un nombre distinto en cada <em>df</em>, en el nuevo estarán las dos columnas de <em>Location</em>. Como buena práctica de limpieza es recomendable borrar una de ellas <code>.drop('LocationID')</code>.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">!wget https://s3.amazonaws.com/nyc-tlc/misc/taxi+_zone_lookup.csv

df_zones = spark.read \
    .option("header", "true") \
    .csv('taxi+_zone_lookup.csv')

df_result = df_join.join(df_zones, df_join.PULocationID == df_zones.LocationID)

df_result.drop('LocationID').write.parquet('tmp/revenue-zones')</code></pre>



<p>Vamos a volver a Spark UI a ver cómo ha generado la orden de trabajo. Observamos que se ha dividido en 4 <em>jobs </em>con una <em>stage</em> cada uno. Si prestamos atención, vemos que el job con id 69 con sólo 3&#8243; de duración habría cargado el <em>DataFrame </em>pequeño de zonas, a continuación el job 70 realiza la operación de <strong>broadcast</strong>, el 71 carga el segundo <em>DataFrame</em> y por último el job que realiza el join.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-23.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1290" height="261" data-attachment-id="4943" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-23-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-23.png?fit=1296%2C262&amp;ssl=1" data-orig-size="1296,262" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-23" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-23.png?fit=1024%2C207&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-23.png?resize=1290%2C261&#038;ssl=1" alt="Spark UI broadcasting DataFrames" class="wp-image-4943" title="Spark UI broadcasting DataFrames" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-23.png?w=1296&amp;ssl=1 1296w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-23.png?resize=300%2C61&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-23.png?resize=1024%2C207&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-23.png?resize=768%2C155&amp;ssl=1 768w" sizes="auto, (max-width: 1290px) 100vw, 1290px" /></a><figcaption class="wp-element-caption">Spark UI broadcasting DataFrames</figcaption></figure>



<h3 class="wp-block-heading" id="spark-rdd" style="font-style:normal;font-weight:100">Resilient Distributed Datasets (RDDs)</h3>



<p>Imagina que <em>Spark </em>es como una gran caja de herramientas para trabajar con datos en un grupo de ordenadores (clúster). En esa caja, una de las herramientas más importantes es el <strong>RDD</strong>, que es un conjunto de objetos distribuidos entre todos los nodos del clúster. Los RDD son muy útiles porque se pueden procesar en paralelo y procesar datos de manera más rápida.</p>



<p>Pero para trabajar con datos estructurados, como si fueran una tabla de una base de datos, se necesita una herramienta un poco diferente. Esa herramienta es el <strong>DataFrame</strong>, que es como una tabla que se puede manipular con facilidad. La ventaja de los <em>DataFrames </em>es que son más fáciles de usar que los RDDs y se pueden procesar de manera más eficiente.</p>



<p>Aunque los <em>DataFrames </em>son más fáciles de usar, siguen utilizando los RDDs en segundo plano para hacer todo el trabajo duro. Cuando se convierte un RDD en un <em>DataFrame</em>, se están organizando los datos en una tabla fácil de usar, y cuando se convierte un <em>DataFrame </em>en un RDD, se están sacando los datos de esa tabla para que se puedan procesar de manera más eficiente en paralelo. En general, se recomienda utilizar <em>DataFrames </em>siempre que sea posible debido a que son más fáciles de usar y optimizados para consultas SQL y de tipo tabla. Sin embargo, hay algunas situaciones donde puede ser útil utilizar RDD, como por ejemplo, si se necesita realizar operaciones más complejas o cálculos de bajo nivel que no son posibles con <em>DataFrames</em>. Además, si estamos trabajando con tipos de datos no estructurados, como archivos de registro, los RDD pueden ser una opción más adecuada.</p>



<p>Los datos en un RDD están organizados en un tipo de objeto llamado <strong>Row,</strong> que sería un concepto similar al de fila o tupla de una base de datos relacional o estructurada. Podemos acceder a los valores de cada columna en esa fila, ya sea por su índice (como un número de posición) o por su nombre (como un título). </p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-26.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="976" height="263" data-attachment-id="5008" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-26-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-26.png?fit=976%2C263&amp;ssl=1" data-orig-size="976,263" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-26" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-26.png?fit=976%2C263&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-26.png?resize=976%2C263&#038;ssl=1" alt="Rows de Spark RDD" class="wp-image-5008" title="Rows de Spark RDD" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-26.png?w=976&amp;ssl=1 976w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-26.png?resize=300%2C81&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-26.png?resize=768%2C207&amp;ssl=1 768w" sizes="auto, (max-width: 976px) 100vw, 976px" /></a><figcaption class="wp-element-caption">Rows de Spark RDD</figcaption></figure>



<p>Dentro de RDD, se pueden utilizar tres operaciones principales: <code>Map()</code>, <code>Filter()</code> y <code>ReduceByKey()</code></p>



<p><strong>Map</strong><br />Se utiliza para transformar cada elemento de un RDD en otro elemento. Por ejemplo, se puede aplicar una función a cada elemento del RDD para convertirlo en una cadena o para realizar una operación. La función <code>map </code>crea un nuevo RDD que contiene los elementos resultantes de aplicar la transformación a cada elemento del RDD original.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python"># Creamos un RDD con una lista de números
numeros = sc.parallelize([1, 2, 3, 4, 5])

# Utilizamos la función map para multiplicar cada número por 2
numeros_por_dos = numeros.map(lambda x: x * 2)

# Imprimimos el nuevo RDD con los elementos resultantes
print(numeros_por_dos.collect())  # Resultado: [2, 4, 6, 8, 10]
</code></pre>



<p><br /><strong>Filter</strong><br />se utiliza para seleccionar un subconjunto de elementos de un RDD que cumple una determinada condición. Crea un nuevo RDD que contiene los elementos seleccionados.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python"># Creamos un RDD con una lista de palabras
palabras = sc.parallelize(['hola', 'adios', 'gato', 'perro', 'casa'])

# Utilizamos la función filter para seleccionar las palabras con más de 5 letras
palabras_largas = palabras.filter(lambda x: len(x) &gt; 5)

# Imprimimos el nuevo RDD con los elementos seleccionados
print(palabras_largas.collect())  # Resultado: ['adios']
</code></pre>



<p><br /><strong>ReduceByKey</strong><br />sirve para reducir los valores de un RDD que tienen la misma <strong>clave</strong>. Por ejemplo, se puede aplicar una función a cada valor de un RDD para agregarlos y, a continuación, utilizar la función <code>reduceByKey</code> para agregar los valores que tienen la misma clave. Crea un nuevo RDD que contiene las claves y los valores resultantes de aplicar la función de agregación.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python"># Creamos un RDD con una lista de tuplas que representan ventas de productos
ventas = sc.parallelize(
[('norte', 'producto1', 100), 
('sur', 'producto1', 200), 
('norte', 'producto2', 50), 
('sur', 'producto2', 150)])

# Utilizamos la función reduceByKey para sumar las ventas de cada producto por región
ventas_por_region = ventas.reduceByKey(lambda x, y: x + y)

# Imprimimos el nuevo RDD con los resultados de las ventas por región
print(ventas_por_region.collect())  # Resultado: [('norte', 150), ('sur', 350)]
</code></pre>



<p></p>



<p><strong>MapPartition</strong><br />El método <code>mapPartitions</code> es útil cuando necesitas aplicar una función a un conjunto de datos que está dividido en particiones, especialmente en proyectos ML. Digamos que tienes una gran cantidad de datos que <em>Spark</em> divide en múltiples particiones para procesarlos más rápido. Esta función es recomendable usarla cuando deseas aplicar una transformación a cada partición de un conjunto de datos en lugar de aplicarla a cada registro individualmente (que para eso usaríamos <code>.map()</code>).</p>



<p>Por ejemplo, imagina que tienes un conjunto de datos que contiene información de ventas de diferentes tiendas en todo el país. Cada partición contiene información de ventas de una región geográfica específica. Si deseas agregar la cantidad de ventas de cada región, en lugar de aplicar una función a cada registro, puedes usar <code>mapPartitions</code> para aplicar la función a cada partición de datos y luego sumar los resultados.</p>



<pre class="wp-block-code"><code class=""># Crear un RDD de números
rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3) # Dividir en 3 particiones

# Definir una función que devuelve el número máximo en cada partición
def max_partition(iterator):
    yield max(iterator)

# Aplicar la función a cada partición usando mapPartitions
max_per_partition = rdd.mapPartitions(max_partition).collect()

# Imprimir el número máximo en cada partición
print(max_per_partition)
</code></pre>



<p>En este ejemplo creamos un RDD con 10 números y lo dividimos en 3 particiones usando el argumento <code>numSlices</code> en la función <code>parallelize</code>. Luego, definimos una función llamada <code>max_partition</code> que toma un iterador de números y devuelve el número máximo en esa partición. Usamos <code>yield</code> en lugar de <code>return</code> en la función porque estamos generando un único valor para cada partición.</p>



<p>Finalmente, usamos <code>mapPartitions</code> para aplicar la función <code>max_partition</code> a cada partición y obtener el número máximo en cada una de ellas. El resultado es una lista con el número máximo de cada partición, que imprimimos usando la función <code>print</code>.</p>



<h4 class="has-medium-font-size wp-block-heading" id="spark-rdd-ejemplos" style="font-style:normal;font-weight:100">Trabajando con RDDs en Spark</h4>



<p>Vamos a realizar el ejercicio de generar la consulta de Spark SQL que hemos visto previamente con RDD en lugar de DataFrames. Esta es la consulta original:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df_green_revenue = spark.sql("""
SELECT 
    date_trunc('hour', lpep_pickup_datetime) AS hour, 
    PULocationID AS zone,

    SUM(total_amount) AS amount,
    COUNT(1) AS number_records
FROM
    green
WHERE
    lpep_pickup_datetime &gt;= '2020-01-01 00:00:00'
GROUP BY
    1, 2  
""")</code></pre>



<p></p>



<p>Vamos a utilizar los datos de los taxis <em>green</em> que hemos descargado previamente generando el <em>DataFrame </em>df_green con todos los CSVs correspondientes. Para convertir un <em>DataFrame </em>a RDD simplemente utilizamos el operador <code>.rdd</code>. Vamos a comenzar convirtiendo el <strong>SELECT </strong>a RDD con el mismo operador de transformación <code>.select()</code> (líneas 18-20). </p>



<p>El <strong>WHERE </strong>lo vamos a generar utilizando el operador <code>.filter()</code>, para lo que se crea la función <em>filter_outliers(row)</em> que recibe como parámetro una <strong>Row</strong> que el propio RDD va iterando (como las funciones lambda cuando las usamos para transformar DataFrames). La función simplemente realiza la comprobación de que el la fecha de recogida (<em>lpep_pickup_datetime </em>) sea a partir del 1.1.2022. </p>



<p>Para realizar las operaciones de agregación <strong>SUM </strong>y <strong>COUNT</strong> del <strong>GROUPBY</strong> necesitamos crear dos funciones para preparar los datos y agruparlos. En primer lugar vamos a generar un nuevo RDD con los resultados intermedios (con una Key y los valores) usando el operador <code>.map()</code>. Vamos a dividir cada <em>Row </em>en dos tuplas (clave(x,y) , valor(k,z)). La función <em>prepare_for_grouping</em> genera las tuplas key(hour, zone) y value (amount, count). El count es 1 por cada <em>Row </em>para que cuando se sumen todas nos de el número total de Rows, es decir, un <code>count(1)</code>.</p>



<p>Por otro lado, vamos a crear otra función (<em>calculate_revenue</em>) para realizar el cálculo de agregación (sumatorio del <em>amount </em>y el conteo del nº de carreras) y vamos a invocarla desde <code>reduceByKey()</code>. Este operador infiere cuál es la clave del RDD original (columnas con los mismos valores), por lo que en cada iteración va a agrupar dos <em>Rows</em> con la misma <em>Key</em> sumando los valores <em>amount </em>y <em>count</em>. </p>



<p>El método unwrap lo vamos a usar para mostrar los resultados como si fueran una fila y no un conjunto de tuplas, para después generar el DataFrame de una forma más higiénica con el operador <code>.toDF()</code>. Si lo deseamos, podemos pasarle como parámetro el esquema de los datos.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python line-numbers">from datetime import datetime
from collections import namedtuple
from pyspark.sql import types

import pyspark
from pyspark.sql import SparkSession

spark = SparkSession.builder \
    .master("local[*]") \
    .appName('test') \
    .getOrCreate()


start = datetime(year=2020, month=1, day=1)

df_green = spark.read.csv('data/raw/green/*/*', header=True, inferSchema=True)

rdd = df_green \
    .select('lpep_pickup_datetime', 'PULocationID', 'total_amount') \
    .rdd

def filter_outliers(row):
    return row.lpep_pickup_datetime &gt;= start

def prepare_for_grouping(row): 
    hour = row.lpep_pickup_datetime.replace(minute=0, second=0, microsecond=0)
    zone = row.PULocationID
    key = (hour, zone)
    
    amount = row.total_amount
    count = 1
    value = (amount, count)

    return (key, value)

def calculate_revenue(left_value, right_value):
    left_amount, left_count = left_value
    right_amount, right_count = right_value
    
    output_amount = left_amount + right_amount
    output_count = left_count + right_count
    
    return (output_amount, output_count)

def unwrap(row):
    return RevenueRow(
        hour=row[0][0], 
        zone=row[0][1],
        revenue=row[1][0],
        count=row[1][1]
    )



result_schema = types.StructType([
    types.StructField('hour', types.TimestampType(), True),
    types.StructField('zone', types.IntegerType(), True),
    types.StructField('revenue', types.DoubleType(), True),
    types.StructField('count', types.IntegerType(), True)
])

df_result = rdd \
    .filter(filter_outliers) \
    .map(prepare_for_grouping) \
    .reduceByKey(calculate_revenue) \
    .map(unwrap) \
    .toDF(result_schema) 

df_result.write.parquet('tmp/green-revenue')</code></pre>



<div class="wp-block-group has-background is-layout-constrained wp-block-group-is-layout-constrained" style="border-radius:5px;background-color:#f0cece">
<pre class="wp-block-code has-background" style="background-color:#f3e3e3"><code lang="bash" class="language-bash">PicklingError: Could not serialize object: IndexError: tuple index out of range</code></pre>



<p>Si intentamos realizar una acción con el RDD (por ejemplo <code>.take()</code>, <code>.filter()</code>, etc) y nos arroja este error, es necesario utilizar una versión de Python inferior a la 3.11. Podemos crear un nuevo entorno de Python con Anaconda, a mano, o si resulta más sencillo, utilizar un <a href="https://colab.research.google.com/drive/1uDqG12vwP4fzQxG0_Q0Gdp3qicQUJBLP?usp=sharing" target="_blank" rel="noreferrer noopener">colab de Google</a> que por defecto instala Python 3.8.</p>
</div>



<h2 class="wp-block-heading" id="spark-standalone-cluster">Spark Standalone Mode</h2>



<p><strong>Spark Standalone Mode </strong>es un modo de ejecución de <em>Apache Spark</em> que permite ejecutar aplicaciones en un clúster dedicado. En este modo, <em>Spark </em>actúa como un administrador de clúster y controla la asignación de recursos y la planificación de tareas. El modo <em>Standalone </em>es una de las opciones de implementación de <em>Spark</em>, junto con <em>YARN (Yet Another Resource Negotiator) </em>y <em>Mesos</em>. Podemos consultar la documentación de <a href="https://spark.apache.org/docs/latest/spark-standalone.html" target="_blank" rel="noreferrer noopener">Spark Standalone Mode</a> para más información.</p>



<p>En el modo <em>Standalone</em>, se puede iniciar un clúster Spark simplemente ejecutando una serie de comandos para arrancar el nodo maestro (<em>master</em>) y varios nodos de trabajo (<em>workers</em>). El nodo maestro coordina el clúster y administra los recursos disponibles, mientras que los nodos de trabajo ejecutan tareas.</p>



<p>Para arrancar el <strong>Spark Standalone</strong> <strong>mode</strong> <strong>en Windows 10</strong> por línea de comandos CMD debemos seguir los siguientes pasos. Antes de empezar, recuerda que tienes que tener definida la variable de entorno SPARK_HOME  (si lo has hecho por Git Bash, no está en Windows). Para declararla desde línea de comandos:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="powershell" class="language-powershell">setx SPARK_HOME "c:\tools\spark-3.3.2-bin-hadoop3"</code></pre>



<p><br />1. Abrimos un prompt de CMD y nos dirigimos a la ruta donde hemos descargado <em>Spark</em>. </p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">cd %SPARK_HOME%\bin</code></pre>



<p><br />2. Ejecutamos el comando el siguiente comando para arrancar el nodo master. Nos va a generar una URL tipo <code>spark://192.168.0.38:7077</code> que usaremos para arrancar el <em>worker</em>. Por otro lado, también arranca el<strong> Spark UI</strong> que por defecto se despliega en el puerto 8080 (<a href="http://http//localhost:8080" target="_blank" rel="noreferrer noopener">http//localhost:8080</a>).</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">spark-class org.apache.spark.deploy.master.Master </code></pre>



<p><br />3. Abrimos otro prompt de CMD y arrancamos un <em>worker</em> con el siguiente comando. Fíjate en que se le pasa como parámetro la URL del master que hemos iniciado en el paso preivo:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">spark-class org.apache.spark.deploy.worker.Worker spark://192.168.0.38:7077</code></pre>



<p><br />4. Creamos un nuevo cuaderno de <em>jupyter </em>y conectamos la <strong>SparkSession</strong> al nodo <em>master</em> que acabamos de levantar indicando la URL <code>spark://192.168.0.38:7077</code>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">
spark = SparkSession.builder \
    .master("spark://192.168.0.38:7077") \
    .appName('test') \
    .getOrCreate()</code></pre>



<p><br />5. Nos dirigimos a la Spark UI para ver cómo se han arrancado los nodos master y worker, y la app:</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-27.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1290" height="639" data-attachment-id="5018" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-27-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-27.png?fit=1308%2C648&amp;ssl=1" data-orig-size="1308,648" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-27" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-27.png?fit=1024%2C507&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-27.png?resize=1290%2C639&#038;ssl=1" alt="Spark UI de Spark Standalone Mode" class="wp-image-5018" title="Spark UI de Spark Standalone Mode" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-27.png?w=1308&amp;ssl=1 1308w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-27.png?resize=300%2C149&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-27.png?resize=1024%2C507&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-27.png?resize=768%2C380&amp;ssl=1 768w" sizes="auto, (max-width: 1290px) 100vw, 1290px" /></a><figcaption class="wp-element-caption">Spark UI de <strong>Spark Standalone Mode</strong></figcaption></figure>



<div class="wp-block-group has-background is-layout-constrained wp-block-group-is-layout-constrained" style="border-radius:5px;background-color:#f0cece">
<pre class="wp-block-code has-background" style="background-color:#f3e3e3"><code lang="bash" class="language-bash">failed to launch: nice -n 0 /c/tools/spark-3.3.2-bin-hadoop3/bin/spark-class org.apache.spark.deploy.master.Master --host --port 7077 --webui-port 8080
  ps: unknown option -- o
  Try `ps --help' for more information.
  Error: Could not find or load main class org.apache.spark.launcher.Main
  Caused by: java.lang.ClassNotFoundException: org.apache.spark.launcher.Main
  /c/tools/spark-3.3.2-bin-hadoop3/bin/spark-class: line 96: CMD: bad array subscript
full log in /c/tools/spark-3.3.2-bin-hadoop3/logs/spark--org.apache.spark.deploy.master.Master-1-FORGEWORLD.out</code></pre>



<p>Este error lo lanza <strong>Git bash</strong> al intentar arrancar Spark con el comando <code>start-master.sh</code>. Al parecer, el lanzador <code>%SPARK_HOME%\sbin</code> no está soportado en Windows.</p>
</div>



<p></p>



<p>¡Bien! ya tenemos arrancado nuestro propio clúster Spark en local, vamos a probar a crear scripts python que corran sobre el mismo. Para nuestra prueba vamos a aprovechar uno de los notebooks que hemos realizado previamente y vamos a convertirlo en un script python con <code>jupyter nbconvert</code> desde <em>Git Bash</em>. Una vez generado el fichero debemos limpiarlo de código autogenerado por Jupyter.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">jupyter nbconvert --to=script 06_spark_sql.ipynb
</code></pre>



<p>Vamos a sustituir los nombres de los ficheros y los directorios que están puestos a fuego por parámetros, para lo que usaremos la librería <code>argparse</code>. Podéis consultar el fichero en la <a href="https://github.com/hegdehog/data-engineering-zoomcamp-2023/tree/main/week_5_batch_processing">repo de Github</a>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">import argparse

import pyspark
from pyspark.sql import SparkSession
from pyspark.sql import functions as F

parser.add_argument('--input_green', required=True)
parser.add_argument('--input_yellow', required=True)
parser.add_argument('--output', required=True)

input_green = args.input_green
input_yellow = args.input_yellow
output = args.output</code></pre>



<p>Para lanzar el script Python en el clúster local de Spark que hemos levantado, desde la terminal ejecutamos:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">python my_script.py \
    --input_green=data/pq/green/2020/*/ \
    --input_yellow=data/pq/yellow/2020/*/ \
    --output=data/report-2020</code></pre>



<h3 class="has-medium-font-size wp-block-heading" id="spark-submit" style="font-style:normal;font-weight:100">Spark submit</h3>



<p><strong>Spark submit</strong> es una herramienta de línea de comandos en <em>Apache Spark</em> que permite enviar y ejecutar aplicaciones Spark en un cluster de Spark. Para enviar una aplicación Spark con <code>spark-submit</code> simplemente indicamos el script <em>python </em>y los parámetros necesarios. También proporciona opciones para especificar el número de cores y memoria que se asignarán a la aplicación en el clúster de <em>Spark</em>. Además, puede ser utilizado para enviar aplicaciones a diferentes modos de ejecución de Spark, incluyendo modo local, modo cluster y modo cliente.</p>



<p>Para utilizarlo con nuestros ejemplos, dentro del mismo script <em>Python </em>en lugar de conectarnos al <strong>SparkSession</strong> indicando el master, vamos a eliminar este parámetro dejándolo así:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">spark = SparkSession.builder \
    .appName('test') \
    .getOrCreate()</code></pre>



<p><br />Lo que vamos a hacer es ejecutarlo desde <code>spark-submit</code> y pasarle como parámetro la URL del master de <em>Spark </em>que hemos levantado:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">spark-submit \
    --master="spark://192.168.0.38:7077" \
    my_script.py \
        --input_green=data/pq/green/2020/*/ \
        --input_yellow=data/pq/yellow/2020/*/ \
        --output=data/report-2020</code></pre>



<h2 class="wp-block-heading" id="google-cloud-platform-dataproc-spark" style="font-style:normal;font-weight:100">Spark en Google Cloud Platform: Dataproc</h2>



<p><strong>Dataproc </strong>es un servicio de <em>Google Cloud</em> que te permite ejecutar tareas de procesamiento de datos a gran escala de manera fácil y rápida. En otras palabras, es una herramienta que te permite procesar grandes cantidades de datos en paralelo usando el poder de la nube.</p>



<p>Con <em>Dataproc</em>, puedes crear un clúster de computación en la nube y ejecutar tareas de procesamiento de datos como procesamiento por lotes, análisisy aprendizaje automático. El servicio está diseñado para ser fácil de usar, escalable y rentable, lo que lo hace ideal para empresas y organizaciones que necesitan procesar grandes cantidades de datos de manera eficiente. Además, <em>Dataproc</em> está integrado con otras herramientas de <em>Google Cloud</em>, como <em>BigQuery, Cloud Storage</em> y <em>Cloud Dataprep</em>, lo que te permite integrar y analizar datos de diferentes fuentes de manera eficiente.</p>



<p>Vamos a seguir los siguientes pasos para ejecutar nuestro script <em>Python </em>en <strong>Dataproc</strong>:</p>



<p>1. En primer lugar vamos a otorgar permisos sobre <em>Dataproc </em>al <strong>service account </strong>que estamos usando en el bootcamp. Desde el IAM lo seleccionamos y añadimos privilegios sobre el rol <em>Dataproc Administrator</em>.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-30.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="562" height="110" data-attachment-id="5080" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-30-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-30.png?fit=562%2C110&amp;ssl=1" data-orig-size="562,110" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-30" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-30.png?fit=562%2C110&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-30.png?resize=562%2C110&#038;ssl=1" alt="Dataproc role en IAM" class="wp-image-5080" title="Dataproc role en IAM" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-30.png?w=562&amp;ssl=1 562w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-30.png?resize=300%2C59&amp;ssl=1 300w" sizes="auto, (max-width: 562px) 100vw, 562px" /></a><figcaption class="wp-element-caption">Dataproc role en IAM</figcaption></figure>



<p>2. Para crear una instancia de <em>Dataproc </em>simplemente buscamos desde en la barra de búsqueda por <em>Dataproc.</em> La primera vez que accedamos nos solicitará habilitar la API. </p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-28.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="769" height="347" data-attachment-id="5075" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-28-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-28.png?fit=769%2C347&amp;ssl=1" data-orig-size="769,347" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-28" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-28.png?fit=769%2C347&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-28.png?resize=769%2C347&#038;ssl=1" alt="Dataproc API" class="wp-image-5075" title="Dataproc API" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-28.png?w=769&amp;ssl=1 769w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-28.png?resize=300%2C135&amp;ssl=1 300w" sizes="auto, (max-width: 769px) 100vw, 769px" /></a><figcaption class="wp-element-caption">Dataproc API</figcaption></figure>



<p>3. Creamos un nuevo cluster de Dataproc en modo <strong>Compute Engine:</strong></p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-29.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="445" height="297" data-attachment-id="5078" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-29-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-29.png?fit=445%2C297&amp;ssl=1" data-orig-size="445,297" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-29" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-29.png?fit=445%2C297&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-29.png?resize=445%2C297&#038;ssl=1" alt="Cluster Dataproc Compute Engine" class="wp-image-5078" title="Cluster Dataproc Compute Engine" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-29.png?w=445&amp;ssl=1 445w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-29.png?resize=300%2C200&amp;ssl=1 300w" sizes="auto, (max-width: 445px) 100vw, 445px" /></a><figcaption class="wp-element-caption">Cluster Dataproc Compute Engine</figcaption></figure>



<p>3. Vamos a definir un nombre, región (debe ser la misma que nuestro Bucket (GCS)). En los siguientes pasos nos permite especificar el número de nodos (la versión <strong>Single Node</strong> es ideal para hacer pruebas, sólo incluye un nodo y sale más económico). Por último, podemos especificar que el clúster de Dataproc incluya ciertos componentes (Docker, Anaconda, Jupyter Notebook&#8230;).</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-31.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="906" height="247" data-attachment-id="5082" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-31-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-31.png?fit=906%2C247&amp;ssl=1" data-orig-size="906,247" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-31" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-31.png?fit=906%2C247&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-31.png?resize=906%2C247&#038;ssl=1" alt="Dataproc" class="wp-image-5082" title="Dataproc" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-31.png?w=906&amp;ssl=1 906w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-31.png?resize=300%2C82&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-31.png?resize=768%2C209&amp;ssl=1 768w" sizes="auto, (max-width: 906px) 100vw, 906px" /></a><figcaption class="wp-element-caption">Dataproc</figcaption></figure>



<h3 class="has-medium-font-size wp-block-heading" id="spark-job-dataproc" style="font-style:normal;font-weight:100">Ejecutar job Spark en Dataproc</h3>



<p>Hay varias formas de ejecutar un job de <em>Spark </em>en <em>Dataproc</em>. En el curso vemos desde el <em>UI </em>de la <em>GCP</em> y por línea de comandos con la <em>SDK gcloud</em>, pero también podríamos lanzarlo a través del API, <em>node.js</em>, etc.. Más info en la <a href="https://cloud.google.com/dataproc/docs/guides/submit-job?hl=es-419#dataproc-submit-job-gcloud">documentación de Google</a>.</p>



<h4 class="has-palette-color-3-color has-text-color has-medium-font-size wp-block-heading" id="spark-job-gcp-ui" style="font-style:normal;font-weight:100">Ejecutar job Spark desde Google Cloud Patform UI</h4>



<p>Ejecutamos un job desde el UI simplemente entrando en el <strong>Dataproc Cluster </strong>y pulsando en el botón <strong>Submit job</strong> en el menú superior. Se abrirá un blade donde podemos configurar las propiedades del mismo: </p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-32.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1290" height="632" data-attachment-id="5085" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/image-32-3/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-32.png?fit=1297%2C635&amp;ssl=1" data-orig-size="1297,635" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-32" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-32.png?fit=1024%2C501&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/03/image-32.png?resize=1290%2C632&#038;ssl=1" alt="Nuevo job en Cluster Dataproc" class="wp-image-5085" title="Nuevo job en Cluster Dataproc" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/03/image-32.png?w=1297&amp;ssl=1 1297w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-32.png?resize=300%2C147&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-32.png?resize=1024%2C501&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/03/image-32.png?resize=768%2C376&amp;ssl=1 768w" sizes="auto, (max-width: 1290px) 100vw, 1290px" /></a><figcaption class="wp-element-caption">Nuevo job en Cluster Dataproc</figcaption></figure>



<ul class="wp-block-list">
<li><strong>Job ID:</strong> identificador del job</li>



<li><strong>Job Type</strong>: lenguaje de programación del script, en nuestro caso, PySpark.</li>



<li><strong>Main python file: </strong>ubicación física del script Python (porque hemos seleccionado <em>PySpark </em>como <em>type</em>). Vamos a utilizar nuestro <em>Bucket</em> para alojar el script, para subirlo podemos hacerlo a mano desde el UI o utilizando el <a href="https://www.n4gash.com/2023/data-engineering-zoomcamp/">SDK gcloud</a> que instalamos en la primera semana del curso. Recuerda que no debemos especificar en el <em>SparkSession </em>el clúster master, debemos utilizar el script del ejemplo de <a href="#spark-submit">spark-submit</a>. Por tanto, introducimos la ruta física del script en nuestro bucket: <code>gs://dtc_data_lake_digital-aloe-375022/code/06_spark_sql_big.py</code>. Para copiarlo de local al <em>Bucket</em>:</li>
</ul>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">gsutil cp 06_spark_sql.py gs://dtc_data_lake_digital-aloe-375022/code/06_spark_sql_big.py</code></pre>



<ul class="wp-block-list">
<li><strong>Jar files: </strong>para este ejemplo no es necesario, ya que sólo ejecutamos un script Python.</li>



<li><strong>Arguments: </strong>aquí especificamos los parámetros que espera recibir el script, recuerda que son tres. Se añaden uno por uno (como si fueran tags) incluyendo el prefijo de «&#8211;«. Revisa la ubicación de los ficheros en tu bucket.</li>
</ul>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">--input_green=data/green/2020/*/
--input_yellow=data/yellow/2020/*/ 
--output=data/report-2020</code></pre>



<p></p>



<p>3. Pulsamos sobre el botón <em>submit </em>y comienza a ejecutarse de inmediato.</p>



<h4 class="has-palette-color-3-color has-text-color has-medium-font-size wp-block-heading" id="spark-job-gcloud-sdk" style="font-style:normal;font-weight:100">Ejecutar job Spark desde gcloud SDK</h4>



<p>Otra forma de lanzar un job de Spark en Dataproc es usando el gcloud SDK. Abrimos una terminal y lanzamos el siguiente comando. Como nuestro script es un <em>Python </em>con <em>PySpark</em>, indicamos <code>pyspark</code> en <code>job-command</code>. Especificamos el nombre del clúster Dataproc que hemos instanciado, la región y por último</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">gcloud dataproc jobs submit pyspark \
    --cluster=de-zoomcamp-cluster \
    --region=europe-west6 \
    gs://dtc_data_lake_de-zoomcamp-nytaxi/code/06_spark_sql.py \
    -- \
        --input_green=gs://dtc_data_lake_de-zoomcamp-nytaxi/pq/green/2020/*/ \
        --input_yellow=gs://dtc_data_lake_de-zoomcamp-nytaxi/pq/yellow/2020/*/ \
        --output=gs://dtc_data_lake_de-zoomcamp-nytaxi/report-2020</code></pre>



<p><br />Ejemplo:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">gcloud dataproc jobs submit pyspark \
    --cluster=&lt;your-cluster-name&gt; \
    --region=europe-west6 \
    gs://&lt;url-of-your-script&gt; \
    -- \
        --param1=&lt;your-param-value&gt; \
        --param2=&lt;your-param-value&gt;</code></pre>



<h3 class="has-medium-font-size wp-block-heading" id="job-spark-dataproc-biquery" style="font-style:normal;font-weight:100">Job Spark + Dataproc + BigQuery</h3>



<p>Por último, vemos cómo ejecutar  un job en <em>Dataproc </em>para que ingeste y transforme los datos para finalmente guardarlos en una tabla de <strong>BigQuery </strong>en lugar de ficheros parquet. </p>



<p>1. Vamos a crear otro script de Python copia de <a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/blob/main/week_5_batch_processing/code/06_spark_sql.py">06_spark_sql.py</a> donde modificaremos dónde queremos persistir el output. En lugar de un fichero <em>parquet</em>, vamos a guardarlos en una tabla de <em>BigQuery</em>. Modificamos la última línea del <code>.write()</code>. En <code>.option()</code> indicamos que e<em>s </em>una tabla y la variable<em> output </em>es uno de los parámetros que se recibe al ejecutar el job.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df_result.write.format('bigquery') \
    .option('table', output) \
    .save()</code></pre>



<p><br />2. Subimos el nuevo script al <em>Bucket </em>de Google:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">gsutil cp 06_spark_sql_big_query.py gs://dtc_bucket/code/06_spark_sql_big_query.py</code></pre>



<p><br />3. Vamos a crear un nuevo job de <em>Spark </em>donde además de seguir la misma configuración del ejemplo anterior, vamos a añadir la librería <code>gs://spark-lib/bigquery/spark-bigquery-latest_2.12.jar</code> en el apartado de <code>jar files</code>. Para más información, consulta la <a href="https://cloud.google.com/dataproc/docs/tutorials/bigquery-connector-spark-example?hl=es-419" target="_blank" rel="noreferrer noopener">documentación</a>.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">gcloud dataproc jobs submit pyspark \
    --cluster=de-zoomcamp-cluster \
    --region=europe-west6 \
    --jars=gs://spark-lib/bigquery/spark-bigquery-latest_2.12.jar \
    gs://dtc_data_lake_de-zoomcamp-nytaxi/code/06_spark_sql_big_query.py \
    -- \
        --input_green=gs://dtc_data_lake_de-zoomcamp-nytaxi/pq/green/2020/*/ \
        --input_yellow=gs://dtc_data_lake_de-zoomcamp-nytaxi/pq/yellow/2020/*/ \
        --output=trips_data_all.reports-2020</code></pre>



<p>4. Lanzamos el job y observamos cómo se ha creado la tabla en nuestro esquema de <em>BigQuery</em>:</p>
<p>La entrada <a href="https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/">Data Engineering Zoomcamp – Semana 5</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://n4gash.com/data-engineering-zoomcamp-semana-5-batch-spark/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4678</post-id>	</item>
		<item>
		<title>Data Engineering Zoomcamp – Semana 4</title>
		<link>https://n4gash.com/data-engineering-zoomcamp-semana-4/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=data-engineering-zoomcamp-semana-4</link>
					<comments>https://n4gash.com/data-engineering-zoomcamp-semana-4/#comments</comments>
		
		<dc:creator><![CDATA[Marcos Torregrosa]]></dc:creator>
		<pubDate>Tue, 14 Feb 2023 08:32:08 +0000</pubDate>
				<category><![CDATA[Cloud Data]]></category>
		<category><![CDATA[Data Engineering]]></category>
		<category><![CDATA[Data Visualization]]></category>
		<category><![CDATA[Analytics Engineer]]></category>
		<category><![CDATA[DBT]]></category>
		<category><![CDATA[GCP]]></category>
		<category><![CDATA[Google Data Studio]]></category>
		<guid isPermaLink="false">https://www.n4gash.com/?p=4353</guid>

					<description><![CDATA[<p>Modelización de datos con dbt Cada vez se pone más interesante el bootcamp para Data Engineers. La cuarta semana está dedicada al modelaje de datos y los Analytics Engineer como pieza intermedia entre los Data Engineers y los Data Scientists. Su labor es hacer de catalizador entre ambos roles participando en el modelado de datos para lo que hemos explorado dbt (Data build tool). El último capitulo está centrado en la visualización de datos con Google Data Studio, disponible dentro del ecosistema de GCP (Google Cloud Platform). Prerequisitos Analytics Engineer Modelización de Data Warehouse: Kimball, Inmon y Data Vault. Data…</p>
<p>La entrada <a href="https://n4gash.com/data-engineering-zoomcamp-semana-4/">Data Engineering Zoomcamp – Semana 4</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">Modelización de datos con dbt</h2>



<p>Cada vez se pone más interesante el <a href="https://www.n4gash.com/2023/data-engineering-zoomcamp/" target="_blank" rel="noreferrer noopener">bootcamp para Data Engineers</a>. La cuarta semana está dedicada al modelaje de datos y los <strong><em>Analytics Engineer </em></strong>como pieza intermedia entre los <em>Data Engineers </em>y los <em>Data Scientists.</em> Su labor es hacer de catalizador entre ambos roles participando en el <strong>modelado de datos</strong> para lo que hemos explorado <a href="https://www.getdbt.com/" target="_blank" rel="noreferrer noopener">dbt </a>(Data build tool). El último capitulo está centrado en la <strong>visualización de datos </strong>con <strong>Google Data Studio</strong>, disponible dentro del ecosistema de GCP (Google Cloud Platform).</p>



<ul class="wp-block-list">
<li><strong>Repo Bootcamp:</strong> <a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/tree/main/week_4_analytics_engineering" target="_blank" rel="noreferrer noopener">https://github.com/DataTalksClub/data-engineering-zoomcamp/tree/main/week_4_analytics_engineering</a></li>



<li><strong>Repo personal con proyecto dbt:</strong> <a href="https://github.com/hegdehog/zoomcamp-2023-dbt" target="_blank" rel="noreferrer noopener">https://github.com/hegdehog/zoomcamp-2023-dbt</a></li>



<li><strong>Repo personal con ejercicios:</strong> <a href="https://github.com/hegdehog/data-engineering-zoomcamp-2023/tree/main/week_4_analytics_engineering" target="_blank" rel="noreferrer noopener">https://github.com/hegdehog/data-engineering-zoomcamp-2023</a></li>
</ul>



<div class="wp-block-group has-palette-color-8-color has-text-color has-background is-layout-constrained wp-block-group-is-layout-constrained" style="background-color:#7bdbb547">
<div class="wp-block-aioseo-table-of-contents">
<ul>
<li><a href="#prerequisitos">Prerequisitos</a></li>
<li><a href="#analytics-engineer">Analytics Engineer</a></li>
<li><a href="#modelizacion-datawarehouse">Modelización de Data Warehouse: Kimball, Inmon y Data Vault.</a></li>
<li><a href="#data-build-tool-dbt">Data Build Tool (dbt)</a>
<ul><li><a href="#como-empezar-dbt">¿Cómo empezar con dbt?</a></li>
<li><a href="#como-instalar-dbt-pip">Instalar dbt Core con PIP</a></li>
<li><a href="#instalar-dbt-core-docker">Instalar dbt Core con una imagen de Docker</a></li>
</ul></li>
<li><a href="#crear-proyecto-dbt">Crear proyecto en DTB Cloud</a>
<ul><li><a href="#crear-service-account-bigquery-dbt">Crear service account + JSON Key para Big Query</a></li>
<li><a href="#crear-repositorio-github-dbt">Crear repositorio en Github</a></li></ul>
</li>
<li><a href="#dbt-model">Dbt model</a>
<ul>
<li><a href="#dbt-from">La cláusula FROM en un modelo DBT</a>
<ul><li><a href="#dbt-sources">Sources</a></li>
<li><a href="#dbt-seeds">Seeds</a></li></ul>
</li>
<li><a href="#dbt-macros">Macros</a></li>
<li><a href="#dbt-variables">Variables</a></li>
<li><a href="#dbt-packages">Packages</a></li>
</ul>
</li><li><a href="#dbt-case-use">Caso de uso: crear dbt models para proyecto tripdata</a></li>
<li><a href="#automatic-test-dbt">Testing en dbt</a></li>
<li><a href="#dbt-documentacion&quot;">Documentación automática</a></li>
<li><a href="#dbt-deployment">Despliegue de un model dbt</a>
<ul>
<li><a href="#dbt-jobs">Jobs en dbt</a></li>
<li><a href="#dbt-integracion-continua-ci">Integración continua (CI)</a></li>
</ul>
</li>
<li><a href="#google-data-studio">Google Data Studio</a>
<ul>
<li><a href="#google-data-studio-data-source">Fuente de datos (Data Source)</a></li>
<li><a href="#google-data-studio-report">Informes en Google Data Studio (reports)</a></li>
</ul></li>
</ul></div>
</div>



<p class="has-text-align-center has-small-font-size"><em>Última actualización: 20/02/202</em>3</p>



<h2 class="wp-block-heading" id="prerequisitos">Prerequisitos</h2>



<p>Vamos a seguir utilizando los recursos que hemos desplegado en GCP (BigQuery y GCS Bucket). La novedad esta semana es la introducción de una nueva herramienta para el modelado de datos: </p>



<ul class="wp-block-list">
<li><strong>dbt (Data Build Tool)</strong>. Podemos utilizarla de dos formas, bien mediante el SaaS gratuito para usuarios individuales registrándonos en la web <a href="https://www.getdbt.com/signup/" target="_blank" rel="noreferrer noopener">dbt Cloud</a> o descargando el paquete para trabajar en local y con la consola CLI. Se puede <a href="https://docs.getdbt.com/docs/get-started/installation" target="_blank" rel="noreferrer noopener">descargar </a>de varias formas, la más sencilla a mi parecer es con <em>pip </em>en nuestro entorno de Python. Es muy interesante el <a href="https://courses.getdbt.com/courses/fundamentals" target="_blank" rel="noreferrer noopener">curso de introducción oficial</a> (duración: 5 horas).</li>



<li><strong>Google Data Studio</strong>. Nuevo recurso que probamos en Google Cloud Platform, simplemente lo desplegamos. Podemos acceder desde <a href="https://lookerstudio.google.com/" target="_blank" rel="noreferrer noopener">aquí</a>.</li>
</ul>



<p>Mas allá de software y herramientas, vamos a necesitar cargar en nuestra base de datos de BigQuery los datasets de los ejercicios:</p>



<ul class="wp-block-list">
<li><strong>Datasets de las carreras de taxis:</strong> he dejado en <a href="https://github.com/hegdehog/data-engineering-zoomcamp-2023/tree/main/week_4_analytics_engineering" target="_blank" rel="noreferrer noopener">mi repositorio de github</a> un script de <em>Prefect </em>para cargar todos los ficheros en GCS Bucket para luego trabajar con ellos en dbt y persistirlos en la base de datos BigQuery.
<ul class="wp-block-list">
<li>Yellow taxi data &#8211; Años 2019 y 2020</li>



<li>Green taxi data &#8211; Años 2019 y 2020</li>



<li>fhv data &#8211; Año 2019</li>
</ul>
</li>



<li><strong>Dataset de zonas para lookup</strong>: puedes descargarlo desde el repositorio original <a href="https://s3.amazonaws.com/nyc-tlc/misc/taxi+_zone_lookup.csv" target="_blank" rel="noreferrer noopener">aquí</a></li>



<li><strong>Schema, staging models y macros</strong> para generar los primeros modelos en dbt. Están publicados en la repo del bootcamp (<a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/tree/main/week_4_analytics_engineering/taxi_rides_ny/macros" target="_blank" rel="noreferrer noopener">macros </a>y <a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/tree/main/week_4_analytics_engineering/taxi_rides_ny/models/staging" target="_blank" rel="noreferrer noopener">schema-models</a>)</li>
</ul>



<h2 class="wp-block-heading" id="analytics-engineer">Analytics Engineer</h2>



<p>Según el estudio <a href="https://www.gartner.com/en/doc/what-are-the-essential-roles-for-data-and-analytics" target="_blank" rel="noreferrer noopener">What Are the Essential Roles for Data and Analytics?</a> de Gartner publicado en el año 2022 los perfiles de D&amp;A (<em>Data and analytics</em>) llevan varios años en continúo desarrollo y para este 2023 se espera que continúen surgiendo nuevos perfiles que respondan a necesidades más concretas:</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-27.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="755" height="495" data-attachment-id="4369" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-27-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-27.png?fit=755%2C495&amp;ssl=1" data-orig-size="755,495" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-27" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-27.png?fit=755%2C495&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-27.png?resize=755%2C495&#038;ssl=1" alt="Gráfico del estudio de roles D&amp;A de Gartner" class="wp-image-4369" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-27.png?w=755&amp;ssl=1 755w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-27.png?resize=300%2C197&amp;ssl=1 300w" sizes="auto, (max-width: 755px) 100vw, 755px" /></a><figcaption class="wp-element-caption">Gráfico del estudio de roles D&amp;A de Gartner</figcaption></figure>



<p>Dentro de este extenso e imparable abanico de roles, los <strong>Analytics Engineers</strong> son un híbrido a medio camino entre los <strong>Data Engineers</strong> y los <strong>Data Scientists.</strong> Se encargan de diseñar, construir y mantener sistemas de análisis y visualización para facilitar la toma de decisiones basadas en datos. Similar a un ingeniero de datos, se encarga de crear canalizaciones y visualizaciones, pero principalmente para modelos de IA/ML y equipos de ciencia de datos.</p>



<p>En comparación, los <em>data engineers </em>se enfocan en la recopilación, integración y almacenamiento de datos, mientras que los <em>data scientists </em>se enfocan en la investigación y modelado de datos para el descubrimiento de patrones y tendencias. La tarea principal de un <em>Analytics Engineer</em> es proporcionar una plataforma de análisis de datos sólida y confiable para que los <em>data scientists</em> y otros perfiles puedan realizar análisis complejos. </p>



<p>Es importante que los <em>Analytics Engineer</em> estén familiarizados con una amplia gama de tecnologías, ya que la capacidad de trabajar con diferentes herramientas y plataformas es clave para la solución efectiva de problemas de análisis de datos. El stack tecnológico necesario puede variar dependiendo de los requisitos específicos del proyecto y la organización, pero algunas tecnologías comunes que se utilizan en este campo incluyen:</p>



<ul class="wp-block-list">
<li><strong>Almacenamiento de datos</strong>: Data warehousing, sistemas de almacenamiento en la nube como Azure Blob Storage, Amazon S3 o Google Cloud Storage.</li>



<li><strong>Procesamiento de datos</strong>: Apache Hadoop, Apache Spark, Apache Flink.</li>



<li><strong>Lenguajes de programación</strong>: Python, SQL, Java.
<ul class="wp-block-list">
<li><strong>Bibliotecas de análisis de datos</strong>: Pandas, Numpy, Matplotlib, etc.</li>
</ul>
</li>



<li><strong>Visualización de datos</strong>: Tableau, Power BI, QlikView, Looker.</li>



<li><strong>Colaboración y gestión de proyectos</strong>: Azure DevOps, Github, JIRA, Confluence, Asana.</li>



<li><strong>Integración de datos</strong>: Apache NiFi, Apache Airflow, Prefect, Talend.</li>
</ul>



<h2 class="wp-block-heading" id="modelizacion-datawarehouse">Modelización de Data Warehouse: Kimball, Inmon y Data Vault</h2>



<p>Existen varias metodologías de <em>Data Warehouse</em> basadas en la arquitectura de la modelización, las más extendidas son las de <strong>Kimball</strong>, <strong>Inmon </strong>y más recientemente, <strong>Data Vault</strong>.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column has-background is-layout-flow wp-block-column-is-layout-flow" style="background-color:#e7edc6;padding-top:var(--wp--preset--spacing--50);padding-right:var(--wp--preset--spacing--50);padding-bottom:var(--wp--preset--spacing--50);padding-left:var(--wp--preset--spacing--50)">
<h2 class="has-text-align-center has-large-font-size wp-block-heading" style="font-style:normal;font-weight:100">Kimball</h2>



<p>Integra los datamarts en un <em>Data Warehouse </em>centralizado. Se basa en el concepto <em>Ciclo de Vida Dimensional del Negocio (Business Dimensional Lifecycle)</em>:</p>



<ul class="wp-block-list">
<li><strong>Estructura de dimensiones y hechos</strong> del conglomerado de <em>datamarts </em>que forman parte del DWH. La <strong>bus structure</strong> se encarga de unir estas entidades entre los <em>datamarts</em> a través de las <strong>dimensiones conformadas</strong>. </li>



<li><strong>Separación</strong> entre el procesamiento de datos y la presentación de informes (datos históricos).</li>



<li><strong>Enfoque iterativo:</strong> permite mejorar y ajustar el <em>Data Warehouse</em> a medida que se obtiene más información y se identifican nuevos requisitos de negocios. Arranque muy rápido, pero cada nueva iteración requiere mucho esfuerzo.</li>



<li><strong>Prioriza la entrega de datos</strong> al control de la redundancia de datos (3FN) </li>
</ul>
</div>



<div class="wp-block-column has-background is-layout-flow wp-block-column-is-layout-flow" style="background-color:#e7edc6;padding-top:var(--wp--preset--spacing--50);padding-right:var(--wp--preset--spacing--50);padding-bottom:var(--wp--preset--spacing--50);padding-left:var(--wp--preset--spacing--50)">
<h3 class="has-text-align-center has-large-font-size wp-block-heading" style="font-style:normal;font-weight:100"> Inmon</h3>



<p>Los datos deben ser integrados y consolidados desde todos los orígenes en un<em> data warehouse </em>central para proporcionar una <strong>visión única de los datos</strong>. Un sistema <em>Inmon </em>debe cumplir cuatro normas:</p>



<ul class="wp-block-list">
<li><strong>Temática</strong>: Todos los datos relacionados con el mismo tema se almacenan juntos.</li>



<li><strong>Integración</strong>: se almacena en un <em>data warehouse </em>central la información de todos los sistemas de origen gestionando la relación entre ellos.</li>



<li><strong>No volátil</strong>: Los datos se escriben en piedra y nunca se borran.</li>



<li><strong>Tiempo variante</strong>: se mantiene un histórico de los datos para poder consultar una foto con los datos reales en ese momento.</li>
</ul>



<p>El enfoque <em>Inmon </em>prioriza un <em>Data Warehouse</em> preciso y coherente, por lo que el mayor esfuerzo se realiza en la última capa.</p>
</div>



<div class="wp-block-column has-background is-layout-flow wp-block-column-is-layout-flow" style="background-color:#e7edc6;padding-top:var(--wp--preset--spacing--50);padding-right:var(--wp--preset--spacing--50);padding-bottom:var(--wp--preset--spacing--50);padding-left:var(--wp--preset--spacing--50)">
<h3 class="has-text-align-center has-large-font-size wp-block-heading" style="font-style:normal;font-weight:100"> Data Vault</h3>



<p>Pretende solucionar las debilidades de los dos enfoques previos centrándose en la flexibilidad ante los cambios en los sistemas fuente. Busca ser un modelo eficiente, rápido de implementar y muy dinámico.</p>



<ul class="wp-block-list">
<li><strong>Orientación al detalle: </strong>máximo nivel de detalle de la información.</li>



<li><strong>Histórico</strong>: se almacenan todos los cambios que experimentan los datos.</li>



<li><strong>Set de tablas normalizadas</strong>: El modelo está construido en base a tres tipos de tabla fundamentalmente: 
<ul class="wp-block-list">
<li><strong>Hub</strong>: entidades de interés para el negocio. Contienen las claves de negocio y sus metadatos.</li>



<li><strong>Link</strong>: relaciones entre Hubs.</li>



<li><strong>Satellite</strong>: almacén histórico de la información de los Hubs.</li>
</ul>
</li>



<li><strong>Hard y Soft Rules: </strong>las reglas de negocio tienen dos capas, las de interpretación (hard) que son inmutables  y las técnicas (soft) que facilitan los cambios.</li>
</ul>
</div>
</div>



<figure class="wp-block-image alignfull size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-31.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="356" data-attachment-id="4380" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-31-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-31.png?fit=1639%2C570&amp;ssl=1" data-orig-size="1639,570" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-31" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-31.png?fit=1024%2C356&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-31-1024x356.png?resize=1024%2C356&#038;ssl=1" alt="Metodologías Data Warehouse (Kimball, Inmon y Data Vault)" class="wp-image-4380" title="Metodologías Data Warehouse (Kimball, Inmon y Data Vault)" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-31.png?resize=1024%2C356&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-31.png?resize=300%2C104&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-31.png?resize=768%2C267&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-31.png?resize=1536%2C534&amp;ssl=1 1536w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-31.png?w=1639&amp;ssl=1 1639w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Metodologías Data Warehouse (Kimball, Inmon y Data Vault)</figcaption></figure>



<figure class="wp-block-table is-style-stripes" style="margin-right:0;padding-top:0;padding-right:0;padding-bottom:0;padding-left:0"><table class="has-fixed-layout" style="border-style:none;border-width:0px"><thead><tr><th>Kimball</th><th>Inmon</th><th>Data Vault</th></tr></thead><tbody><tr><td>Multidimensional</td><td>Relacional</td><td>Relacional</td></tr><tr><td>Modelo de estrella: hechos + dimensiones</td><td>Modelo copo de nieve: entidad-relación</td><td>Modelo de estrella en la última capa <em>Mart</em></td></tr><tr><td>Proceso <em>Bottom-Up</em>: el<em> Data Warehouse</em> central es la agregación de distintos <em>datamarts </em>con sus verdades ya calculadas. Primero se generan los <em>datamarts </em>y luego el DWH.</td><td>Proceso <em>Top-Down</em>: una única verdad de los datos gestionada en el <em>Data Warehouse</em> central y distribuida a los distintos <em>datamarts</em>.</td><td>Proceso Top-Down: los datos pasan por varias capas (data source, data lake, staging y finalmente data vault).</td></tr><tr><td>Dimensiones de datos conformadas: para conectar los datos entre los <em>datamarts</em> cargados en el <em>DWH </em>se utilizan tablas de dimensiones idénticas<em> </em>o una centralizada en el DWH para mantener la coherencia de los datos.</td><td>Datos con el máximo nivel de detalle</td><td>Datos con el máximo nivel de detalle</td></tr><tr><td>Los datos históricos se almacenan en una ubicación distinta del DWH central.</td><td>Uso de SCD (<em>slowing changing dimension</em>) para control de datos históricos</td><td>Uso de SCD2 (<em>slowing changing dimension</em>) en las tablas de tipo <em>Satellite</em>.</td></tr><tr><td>Desnormalizado</td><td>Normalizado</td><td>Normalizado</td></tr><tr><td>Sí permite la redundancia de datos con el fin de optimizar el procesamiento de datos</td><td>No redundancia de datos</td><td>No redundancia de datos</td></tr></tbody></table><figcaption class="wp-element-caption">Comparativa entre metodologías Kimball vs Inmon vs Data Vault</figcaption></figure>



<h2 class="wp-block-heading" id="data-build-tool-dbt">Data Build Tool (dbt)</h2>



<p><strong>dbt (Data Build Tool) </strong>es una librería Python open-source que agiliza la construcción de modelos de datos permitiendo a los desarrolladores definir, orquestar y ejecutar transformaciones en un almacén de datos moderno como <em>BigQuery, Snowflake, Redshift</em>, etc. Podríamos decir que se trata de una herramienta de gobierno focalizado en la «T» de un proceso ETL/ELT, nos permite centralizar y construir todas las transformaciones de datos en SQL organizándolas como módulos reutilizables (<em>models</em>). Por otro lado, al estar inspirado en las prácticas de la ingeniería software, podemos crear test de validación e implementar todo el ciclo de CI/CD en nuestras canalizaciones de datos. En paralelo al conocimiento que aporta el Bootcamp, es muy interesante el <a href="https://courses.getdbt.com/courses/fundamentals" target="_blank" rel="noreferrer noopener">curso de introducción oficial</a> (duración: 5 horas).</p>



<p>En mi periodo profesional anterior la lógica estaba repartida en procedimientos almacenados en el servidor de <em>BBDD SQL Server</em>, queries SQL en las ETL (<em>Azure Data Factory, SSIS </em>y <em>Taled</em>) e incluso en los modelos de carga de las herramientas de visualización (<em>Qlikview </em>y<em> Power BI</em>). No estaba documentada en ningún lado. Lo que aporta <em>dbt </em>en este paradigma es una capa de gobierno o control que facilita el mantenimiento y documentación de la lógica, el linaje, aumentar la resiliencia y colaboración gracias a un control de versiones y por último, facilitaría la integración continua / entrega o despliegue continuo <a href="https://www.getdbt.com/blog/adopting-ci-cd-with-dbt-cloud/" target="_blank" rel="noreferrer noopener">CI/CD</a>.</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/dbt_data_build_tool.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="375" data-attachment-id="4393" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/dbt_data_build_tool/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/dbt_data_build_tool.png?fit=1200%2C439&amp;ssl=1" data-orig-size="1200,439" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="dbt_data_build_tool" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/dbt_data_build_tool.png?fit=1024%2C375&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/dbt_data_build_tool-1024x375.png?resize=1024%2C375&#038;ssl=1" alt="Arquitectura data integrado DBT (Data Build Tool) como software de Transformación" class="wp-image-4393" title="Arquitectura data integrado DBT (Data Build Tool) como software de Transformación" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/dbt_data_build_tool.png?resize=1024%2C375&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/dbt_data_build_tool.png?resize=300%2C110&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/dbt_data_build_tool.png?resize=768%2C281&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/dbt_data_build_tool.png?w=1200&amp;ssl=1 1200w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Arquitectura data integrado dbt (Data Build Tool) como software de Transformación</figcaption></figure>



<p>Algunas de las características principales de dbt.</p>



<ol class="wp-block-list">
<li><strong>Reutilización de código</strong>: Permite la definición de modelos de datos y la organización de transformaciones en paquetes.</li>



<li><strong>Énfasis en los controles de calidad</strong>: fomenta el uso de pruebas automatizadas para garantizar la calidad de los datos y prevenir errores en las transformaciones.</li>



<li><strong>Control de versiones y colaboración:</strong> está diseñado para trabajar con sistemas de control de versiones como Git, Bitbucket, etc., lo que facilita el seguimiento de los cambios y la colaboración en el desarrollo de pipelines de datos.</li>



<li><strong>Escalabilidad</strong>: diseñado para trabajar con almacenes de datos modernos como BigQuery, Snowflake, Redshift, etc., permite escalar fácilmente el procesamiento de grandes volúmenes de datos.</li>
</ol>



<h3 class="has-medium-font-size wp-block-heading" id="como-empezar-dbt" style="font-style:normal;font-weight:100">¿Cómo empezar con dbt?</h3>



<p>Hay dos formas de utilizar dbt de forma gratuita:</p>



<ul class="wp-block-list">
<li><strong>dbt Core: </strong>versión de código abierto (open-source) que se instala localmente o en un servidor propio. La interacción es por consola CLI.</li>



<li><strong>dbt Cloud: </strong>plataforma alojada en la nube (SaaS) que ofrece funcionalidades adicionales a la versión Core (programación de ejecuciones, integraciones con servicios de BI, monitoreo y alertas). Es más fácil de utilizar al disponer de un GUI. Además de los planes de pago, ofrece una versión gratuita limitada para desarrolladores.</li>
</ul>



<h3 class="has-medium-font-size wp-block-heading" id="como-instalar-dbt-pip" style="font-style:normal;font-weight:200">Instalar dbt Core con PIP</h3>



<p>Tenemos varias opciones para instalar dbt Core en nuestro equipo o servidor local, la forma más sencilla es mediante <code>pip</code> en un entorno de python.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">pip install dbt-core</code></pre>



<p>Después instalaremos el <strong>adapter </strong>o conector del motor de base de datos que vayamos a utilizar. Tenemos a nuestra disposición un catálogo oficial y otro complementario de la comunidad, puedes consultar todos los conectores disponibles desde <a href="https://docs.getdbt.com/docs/supported-data-platforms" target="_blank" rel="noreferrer noopener">aquí</a>. En nuestro caso vamos a hacer la instalación del <em>adapter</em> de <em>BigQuery.</em></p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">pip install dbt-bigquery</code></pre>



<div class="wp-block-group has-background is-layout-constrained wp-block-group-is-layout-constrained" style="border-radius:5px;background-color:#f0cece">
<pre class="wp-block-code has-background" style="background-color:#f3e3e3"><code lang="bash" class="language-bash">error: Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools":
https://visualstudio.microsoft.com/visual-cpp-build-tools/
      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for pandas
Failed to build pandas
ERROR: Could not build wheels for pandas, which is required to install pyproject.toml-based projects</code></pre>



<p>Al intentar instalar el <em>adapter</em> de <em>BigQuery</em> me arrojó un error por una dependencia con la librería <em>Pandas</em> que necesita como mínimo la versión 14.0 del runtime de <em>Microsoft Visual C++</em>. Descargamos <em>Build Tools</em> desde <a href="https://visualstudio.microsoft.com/visual-cpp-build-tools/" target="_blank" rel="noreferrer noopener">aquí </a>o el enlace facilitado en el mensaje de error<em>.</em> Lo ejecutamos y pulsamos en <em>Modificar</em> para añadir el paquete que nos falta:</p>



<figure class="wp-block-image alignfull size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-32.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="271" data-attachment-id="4427" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-32-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-32.png?fit=1236%2C327&amp;ssl=1" data-orig-size="1236,327" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-32" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-32.png?fit=1024%2C271&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-32-1024x271.png?resize=1024%2C271&#038;ssl=1" alt="Instalar runtime Microsoft Visual C++ desde Build Tools para Pandas" class="wp-image-4427" title="Instalar runtime Microsoft Visual C++ desde Build Tools para Pandas" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-32.png?resize=1024%2C271&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-32.png?resize=300%2C79&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-32.png?resize=768%2C203&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-32.png?w=1236&amp;ssl=1 1236w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Instalar runtime Microsoft Visual C++ desde Build Tools para Pandas</figcaption></figure>



<p>Volvemos a instalar el <em>adapter </em>de <em>BigQuery</em> y debería terminar con éxito.</p>
</div>



<div class="wp-block-group has-background is-layout-constrained wp-block-group-is-layout-constrained" style="border-radius:5px;background-color:#f0cece">
<pre class="wp-block-code has-background" style="background-color:#f3e3e3"><code lang="bash" class="language-bash">ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
prefect-gcp 0.2.3 requires google-api-python-client&gt;=2.20.0, but you have google-api-python-client 1.7.11 which is incompatible.</code></pre>



<p>En mi caso, me arrojó otro error al intentar instalar el paquete de BigQuery para dbt. Simplemente actualicé el paquete con una versión no compatible:</p>



<pre class="wp-block-code has-background" style="background-color:#f3e3e3"><code lang="bash" class="language-bash">pip install google-api-python-client --upgrade </code></pre>



<p>Si continúa dando problemas, lo más fácil es crear un nuevo entorno de python limpio:</p>



<pre class="wp-block-code has-background" style="background-color:#f3e3e3"><code lang="bash" class="language-bash">python -m venv PythonDbt
PythonDbt\Scripts\activate</code></pre>
</div>



<p>Creamos un nuevo proyecto ejecutando el comando <code>dbt init</code>. Lo que hace este comando es clonar en la carpeta desde donde lo hayamos ejecutado el <strong><a href="https://github.com/dbt-labs/dbt-starter-project" target="_blank" rel="noreferrer noopener">starter project</a></strong> de dbt que contiene todos los ficheros y directorios necesarios para arrancar nuestro proyecto</p>



<ul class="wp-block-list">
<li><strong>dbt_project.yml:</strong> fichero de configuración del proyecto dbt (nombre, <em>profile</em> que identifica el motor de base de datos que vamos a utilizar, como <em>PostgreSQL </em>o <em>BigQuery</em> y variables globales). Si vas a usar dbt  de forma local es necesario que asegurar que el <em>profile</em> indicado en este fichero de configuración coincida con el de la instalación (~/.dbt/profiles.yml).</li>



<li><strong>Readme.MD:</strong> fichero para literatura en la repo</li>



<li>Directorios <strong>analysis, data, macros, models, snapshots</strong> y <strong>tests</strong></li>
</ul>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">dbt init</code></pre>



<div class="wp-block-group has-background is-layout-constrained wp-block-group-is-layout-constrained" style="border-radius:5px;background-color:#f0cece">
<p>Si cuando ejecutamos <code>dbt init</code> nos arroja el siguiente error, es que no se ha instalado correctamente el <em>adapter </em>o conector a la base de datos:</p>



<pre class="wp-block-code has-background" style="background-color:#f3e3e3"><code lang="bash" class="language-bash">$ dbt init
21:47:00  Running with dbt=1.4.1
No adapters available. Go to https://docs.getdbt.com/docs/available-adapters</code></pre>
</div>



<p>Si todo ha ido bien, podremos configurar nuestro proyecto a través de la consola (CLI) para generar el fichero <strong>profiles.yml</strong>:</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-45.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="535" data-attachment-id="4452" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-45-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-45.png?fit=1299%2C679&amp;ssl=1" data-orig-size="1299,679" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-45" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-45.png?fit=1024%2C535&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-45-1024x535.png?resize=1024%2C535&#038;ssl=1" alt="Instalar DBT Core en local" class="wp-image-4452" title="Instalar DBT Core en local" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-45.png?resize=1024%2C535&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-45.png?resize=300%2C157&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-45.png?resize=768%2C401&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-45.png?w=1299&amp;ssl=1 1299w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Instalar dbt Core en local</figcaption></figure>



<p>Comprobamos que se han generado todos los ficheros y directorios del proyecto dbt en la ruta en la que hemos ejecutado el comando:</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-50.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="775" height="270" data-attachment-id="4517" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-50/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-50.png?fit=775%2C270&amp;ssl=1" data-orig-size="775,270" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-50" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-50.png?fit=775%2C270&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-50.png?resize=775%2C270&#038;ssl=1" alt="Proyecto dbt core en local" class="wp-image-4517" title="Proyecto dbt core en local" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-50.png?w=775&amp;ssl=1 775w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-50.png?resize=300%2C105&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-50.png?resize=768%2C268&amp;ssl=1 768w" sizes="auto, (max-width: 775px) 100vw, 775px" /></a><figcaption class="wp-element-caption">Proyecto dbt core en local</figcaption></figure>



<h3 class="has-medium-font-size wp-block-heading" id="instalar-dbt-core-docker" style="font-style:normal;font-weight:100">Instalar dbt Core con una imagen de Docker</h3>



<p>Os recomiendo <a href="https://github.com/dbt-labs/dbt-core/tree/main/docker" target="_blank" rel="noreferrer noopener">esta lectura</a> para profundizar más en este paso. Las imágenes disponibles para montar un contenedor de dbt con Docker son:</p>



<ul class="wp-block-list">
<li><code>dbt-core</code>&nbsp;<em>(no tiene soporte de base de datos)</em></li>



<li><code>dbt-postgres</code></li>



<li><code>dbt-redshift</code></li>



<li><code>dbt-bigquery</code></li>



<li><code>dbt-snowflake</code></li>



<li><code>dbt-spark</code></li>



<li><code>dbt-third-party</code>&nbsp;</li>



<li><code>dbt-all</code>&nbsp;<em>(instala todas las imágenes en una sola)</em></li>
</ul>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">docker build --tag my-dbt  --target dbt-bigquery .</code></pre>



<p>Una vez creada la imagen arrancamos el contenedor:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">docker run \
--network=host
--mount type=bind,source=path/to/project,target=/usr/app \
--mount type=bind,source=path/to/profiles.yml,target=/root/.dbt/profiles.yml \
my-dbt \
ls</code></pre>



<h2 class="wp-block-heading" id="crear-proyecto-dbt">Crear proyecto en DTB Cloud</h2>



<p>Previo a crear nuestro primer proyecto en <strong>dbt Cloud</strong> debemos reunir los ingredientes necesarios: crear una service account, generar la JSON Key para otorgar acceso a dbt a nuestra instancia de <em>BigQuery </em>y crear una repo vacía en <em>Github </em>donde se almacenarán los ficheros y directorios del proyecto:</p>



<h3 class="has-medium-font-size wp-block-heading" id="crear-service-account-bigquery-dbt" style="font-style:normal;font-weight:100">Crear service account + JSON Key para Big Query</h3>



<p>Como en nuestro caso vamos a utilizar <strong>BigQuery</strong>, la autenticación se realiza por <a href="https://docs.getdbt.com/docs/dbt-cloud/cloud-configuring-dbt-cloud/cloud-setting-up-bigquery-oauth">BigQuery OAuth</a>. Debemos crear un <strong>service account </strong>desde <em>GCP </em>y descargar la <strong>JSON</strong> <strong>key </strong>para otorgar acceso a dbt.</p>



<ol class="wp-block-list">
<li>Accedemos a la consola de <a href="https://cloud.google.com/gcp" target="_blank" rel="noreferrer noopener">Google Cloud Platform</a> y nos dirigimos a <em>IAM and admin &gt; Service accounts</em> para crear un nuevo <strong>service account</strong> con permisos <em>BigQuery Admin</em> y <em>Storage Object Viewer</em>. </li>



<li>Descargamos la JSON Key a nuestro equipo (más tarde la subiremos en dbt en el proceso de configuración del proyecto).</li>
</ol>



<h3 class="has-medium-font-size wp-block-heading" id="crear-repositorio-github-dbt" style="font-style:normal;font-weight:100">Crear repositorio en Github</h3>



<p>Simplemente creamos un repo vacía en <em>Github </em>y pulsamos en <strong>Git Clone</strong> para copiar la clave SSH con la que vincularemos el proyecto de <em>dbt</em>. La conexión con Github se hace en dos partes, por ahora sólo nos interesa la key, más adelante veremos cómo configurar la <em>deploy key</em> generada desde <em><strong>dbt</strong></em>.</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-36.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="228" data-attachment-id="4431" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-36-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-36.png?fit=1275%2C284&amp;ssl=1" data-orig-size="1275,284" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-36" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-36.png?fit=1024%2C228&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-36-1024x228.png?resize=1024%2C228&#038;ssl=1" alt="Copiamos la key SSH de nuestra repo de Github para conectarla con DBT" class="wp-image-4431" title="Copiamos la key SSH de nuestra repo de Github para conectarla con DBT" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-36.png?resize=1024%2C228&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-36.png?resize=300%2C67&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-36.png?resize=768%2C171&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-36.png?w=1275&amp;ssl=1 1275w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Copiamos la key SSH de nuestra repo de Github para conectarla con dbt</figcaption></figure>



<p>¡Ahora sí tenemos los ingredientes! Nos registramos en <strong>dbt cloud</strong> con la opción gratuita para un usuario desde este <a href="https://www.getdbt.com/pricing/" target="_blank" rel="noreferrer noopener">enlace</a>. Una vez verificado el email podremos crear nuestro proyecto eligiendo en primer lugar el almacenamiento de datos. </p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-34.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="396" data-attachment-id="4429" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-34-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-34.png?fit=1844%2C714&amp;ssl=1" data-orig-size="1844,714" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-34" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-34.png?fit=1024%2C396&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-34-1024x396.png?resize=1024%2C396&#038;ssl=1" alt="Creación de nuevo proyecto en DBT (Data Build Tool)" class="wp-image-4429" title="Creación de nuevo proyecto en DBT (Data Build Tool)" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-34.png?resize=1024%2C396&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-34.png?resize=300%2C116&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-34.png?resize=768%2C297&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-34.png?resize=1536%2C595&amp;ssl=1 1536w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-34.png?w=1844&amp;ssl=1 1844w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Creación de nuevo proyecto en dbt (Data Build Tool)</figcaption></figure>



<p>En el siguiente paso cargamos la JSON Key que hemos generado con el <strong>service account</strong> de <em>BigQuery </em>y automáticamente se cargan todos los parámetros. Es recomendable crear el dataset en <em>BigQuery </em>de forma manual para evitar problemas de incompatibilidad entre regiones. En mi caso, he usado el nombre que ha generado por defecto y creado el <em>dataset </em>directamente:</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-37.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="370" data-attachment-id="4432" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-37-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-37.png?fit=1791%2C647&amp;ssl=1" data-orig-size="1791,647" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-37" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-37.png?fit=1024%2C370&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-37-1024x370.png?resize=1024%2C370&#038;ssl=1" alt="Confguración de dataset de BigQuery en DBT" class="wp-image-4432" title="Confguración de dataset de BigQuery en DBT" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-37.png?resize=1024%2C370&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-37.png?resize=300%2C108&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-37.png?resize=768%2C277&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-37.png?resize=1536%2C555&amp;ssl=1 1536w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-37.png?w=1791&amp;ssl=1 1791w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Configuración de dataset de BigQuery en dbt</figcaption></figure>



<p>Hacemos el test de conexión y si todo ha ido bien ¡al paso siguiente!</p>



<figure class="wp-block-image aligncenter size-large" id="Prueba-de-conexión-de-dbt-con-Google-BigQuery"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-38.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="110" data-attachment-id="4433" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-38-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-38.png?fit=1782%2C192&amp;ssl=1" data-orig-size="1782,192" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-38" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-38.png?fit=1024%2C110&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-38-1024x110.png?resize=1024%2C110&#038;ssl=1" alt="Prueba de conexión de dbt con Google BigQuery" class="wp-image-4433" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-38.png?resize=1024%2C110&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-38.png?resize=300%2C32&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-38.png?resize=768%2C83&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-38.png?resize=1536%2C165&amp;ssl=1 1536w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-38.png?w=1782&amp;ssl=1 1782w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Prueba de conexión de dbt con Google BigQuery</figcaption></figure>



<p>Ahora es el turno de configurar el repositorio Github que hemos creado previamente y realizar el segundo paso que teníamos pendiente. Seleccionamos <strong>Git Clone</strong> y pegamos la Key SSH que hemos copiado antes. Pulsamos el botón de <strong>Import.</strong></p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-39.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="222" data-attachment-id="4434" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-39-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-39.png?fit=1793%2C389&amp;ssl=1" data-orig-size="1793,389" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-39" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-39.png?fit=1024%2C222&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-39-1024x222.png?resize=1024%2C222&#038;ssl=1" alt="Configuración Github con DBT" class="wp-image-4434" title="Configuración Github con DBT" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-39.png?resize=1024%2C222&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-39.png?resize=300%2C65&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-39.png?resize=768%2C167&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-39.png?resize=1536%2C333&amp;ssl=1 1536w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-39.png?w=1793&amp;ssl=1 1793w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Configuración Github con dbt</figcaption></figure>



<p>Generará una <strong>clave de despliegue </strong>que debemos copiar en la configuración del repositorio de Github:</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-41.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="142" data-attachment-id="4436" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-41-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-41.png?fit=1780%2C246&amp;ssl=1" data-orig-size="1780,246" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-41" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-41.png?fit=1024%2C142&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-41-1024x142.png?resize=1024%2C142&#038;ssl=1" alt="Clave de despliegue generada en dbt para conectar con Github" class="wp-image-4436" title="Clave de despliegue generada en dbt para conectar con Github" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-41.png?resize=1024%2C142&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-41.png?resize=300%2C41&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-41.png?resize=768%2C106&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-41.png?resize=1536%2C212&amp;ssl=1 1536w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-41.png?w=1780&amp;ssl=1 1780w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Clave de despliegue generada en dbt para conectar con Github</figcaption></figure>



<p>Volviendo a nuestro repositorio de Github, pulsamos en <strong>Settings </strong> y en la sección de <strong>Security</strong> pulsamos en <strong>Deploy Keys </strong>para añadirla. Es necesario marcar la opción <strong>Allow write access:</strong></p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-42.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="578" data-attachment-id="4437" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-42-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-42.png?fit=1139%2C643&amp;ssl=1" data-orig-size="1139,643" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-42" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-42.png?fit=1024%2C578&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-42-1024x578.png?resize=1024%2C578&#038;ssl=1" alt="Configuración deploy Key en Github" class="wp-image-4437" title="Configuración deploy Key en Github" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-42.png?resize=1024%2C578&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-42.png?resize=300%2C169&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-42.png?resize=768%2C434&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-42.png?w=1139&amp;ssl=1 1139w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Configuración deploy Key en Github</figcaption></figure>



<p>¡Listo!</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-43.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="838" height="221" data-attachment-id="4438" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-43-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-43.png?fit=838%2C221&amp;ssl=1" data-orig-size="838,221" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-43" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-43.png?fit=838%2C221&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-43.png?resize=838%2C221&#038;ssl=1" alt="Deploy Keys en Github" class="wp-image-4438" title="Deploy Keys en Github" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-43.png?w=838&amp;ssl=1 838w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-43.png?resize=300%2C79&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-43.png?resize=768%2C203&amp;ssl=1 768w" sizes="auto, (max-width: 838px) 100vw, 838px" /></a><figcaption class="wp-element-caption">Deploy Keys en Github</figcaption></figure>



<p>Si damos al botón <strong>Next</strong> en la configuración del proyecto de dbt ya habremos terminado:</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-44.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1001" height="615" data-attachment-id="4439" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-44-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-44.png?fit=1001%2C615&amp;ssl=1" data-orig-size="1001,615" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-44" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-44.png?fit=1001%2C615&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-44.png?resize=1001%2C615&#038;ssl=1" alt="Configurar proyecto dbt" class="wp-image-4439" title="Configurar proyecto dbt" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-44.png?w=1001&amp;ssl=1 1001w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-44.png?resize=300%2C184&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-44.png?resize=768%2C472&amp;ssl=1 768w" sizes="auto, (max-width: 1001px) 100vw, 1001px" /></a><figcaption class="wp-element-caption">Configurar proyecto dbt</figcaption></figure>



<p>Accedemos a <strong>Develop</strong> y debemos inicializar nuestro proyecto en dbt cloud (similar al comando <code>dbt init</code> que ejecutaríamos en <em>dbt core</em>):</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-49.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="529" height="308" data-attachment-id="4498" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-49/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-49.png?fit=529%2C308&amp;ssl=1" data-orig-size="529,308" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-49" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-49.png?fit=529%2C308&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-49.png?resize=529%2C308&#038;ssl=1" alt="Inicializamos el proyecto dbt" class="wp-image-4498" title="Inicializamos el proyecto dbt" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-49.png?w=529&amp;ssl=1 529w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-49.png?resize=300%2C175&amp;ssl=1 300w" sizes="auto, (max-width: 529px) 100vw, 529px" /></a><figcaption class="wp-element-caption">Inicializamos el proyecto dbt</figcaption></figure>



<p>Tras esperar unos segundos, se crean todos los ficheros de yml, SQL y los directorios del proyecto dbt. Debemos pulsar sobre <em>Commit and sync</em> para hacer un push a nuestra repo de Github.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-47.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="548" height="723" data-attachment-id="4468" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-47/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-47.png?fit=548%2C723&amp;ssl=1" data-orig-size="548,723" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-47" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-47.png?fit=548%2C723&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-47.png?resize=548%2C723&#038;ssl=1" alt="Proyecto dbt recién creado" class="wp-image-4468" title="Proyecto dbt recién creado" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-47.png?w=548&amp;ssl=1 548w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-47.png?resize=227%2C300&amp;ssl=1 227w" sizes="auto, (max-width: 548px) 100vw, 548px" /></a><figcaption class="wp-element-caption">Proyecto dbt recién creado</figcaption></figure>



<p>Recuerda que al estar vinculado a una repo en github, si quieres trabajar sobre el GUI de dbt cloud es necesario que crees primero una rama. Para ejecutar cualquier comando desde GUI, podemos usar la consola que tenemos en el pie:</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-63.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="299" data-attachment-id="4585" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-63/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-63.png?fit=1319%2C385&amp;ssl=1" data-orig-size="1319,385" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-63" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-63.png?fit=1024%2C299&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-63-1024x299.png?resize=1024%2C299&#038;ssl=1" alt="Consola en dbt cloud" class="wp-image-4585" title="Consola en dbt cloud" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-63.png?resize=1024%2C299&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-63.png?resize=300%2C88&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-63.png?resize=768%2C224&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-63.png?w=1319&amp;ssl=1 1319w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Consola en dbt cloud</figcaption></figure>



<h2 class="wp-block-heading" id="dbt-model">dbt model</h2>



<p>Un <strong>modelo</strong> en dbt es la unidad mínima con la que vamos a trabajar, se trata simplemente de una fichero SQL con una sentencia <code>SELECT</code>. En el siguiente ejemplo se va a crear una tabla en la BBDD de destino con los datos del SQL:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">{{      config(materialized='table')    }}

SELECT *
FROM staging.source_table
WHERE record_state = 'ACTIVE'</code></pre>



<p>Además de la query, un  modelo <em>dbt </em>incluye al principio un bloque de código bajo la notación <a href="https://jinja.palletsprojects.com/en/3.0.x/" target="_blank" rel="noreferrer noopener">Jinja</a> que reconoceremos por los doble brackets <code>{{ }}</code>. Dentro de este bloque se llama a la función <code>config()</code> de dbt que habitualmente se usa para especificar la estrategia de persistencia del modelo dbt en la base de datos de destino. Por defecto hay cuatro formas de materializar las consultas, aunque es posible crear las nuestras propias:</p>



<ul class="wp-block-list">
<li><code>table</code>: los datos del modelo se persisten en una tabla sobre el warehouse</li>



<li><code>view</code>: ídem al anterior, pero en lugar de una tabla se trata de una vista</li>



<li><code>incremental</code>: estos modelos permiten a dbt insertar y/o actualizar registros en una <code>table </code>si han cambiado desde la última vez que se ejecutó.</li>



<li><code>ephemeral</code>: no generan un objeto directamente en la base de datos, crea un CTE (Common Table Expression), que viene a ser una subquery temporal para utilizar en otras consultas (como el <code>WITH </code>de SQL Server).</li>
</ul>



<p>Para construir el modelo que persiste la query en una tabla de nuestra base de datos, debemos ejecutar el comando <code>dbt build</code>. Si no añadimos ningún parámetro, compilará y construirá todos los models. Para especificar que sólo queremos construir uno en concreto añadimos el parámetro <code>--select</code>; veámoslo con dos ejemplos:</p>



<p>Ejecutamos y construimos todos los modelos:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">dbt run</code></pre>



<p>Para construir sólo el model <em>myModel.sql</em> corremos este comando:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">dbt run --select myModel.sql</code></pre>



<p>Cuando ejecutamos el modelo, <em>dbt </em>lo compila en la siguiente query:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">CREATE TABLE my_schema.my_model AS (
    SELECT *
    FROM staging.source_table
    WHERE record_state = 'ACTIVE'
)</code></pre>



<h3 class="has-medium-font-size wp-block-heading" id="dbt-from" style="font-style:normal;font-weight:100">La cláusula FROM en un modelo dbt</h3>



<p>Además de indicar directamente esquema y nombre de la tabla, en un modelo dbt hay disponibles dos formas de configurar el origen de los datos en la cláusula FROM: <strong>sources </strong>y <strong>seeds</strong>.</p>



<div class="wp-block-group is-layout-constrained wp-block-group-is-layout-constrained">
<h4 class="has-light-green-cyan-color has-text-color has-medium-font-size wp-block-heading" id="dbt-sources" style="font-style:normal;font-weight:100"><em>Sources</em></h4>



<p>Se utilizan cuando el origen es una tabla de base de datos (BigQuery, PostgreSQL&#8230;). La conexión se configura en un fichero<strong> <em>schema</em>.<em>yml</em></strong><em> </em>que debemos crear en el mismo directorio donde está ubicado el modelo. Es posible indicar si queremos que se compruebe periódicamente si la conexión está operativa o no (<em>source freshness).</em> A la hora de construir el modelo, en la cláusula <code>FROM</code> sustituimos el <em>«esquema . nombre de la tabla»</em> por una macro en notación <em>jinja </em>que va a buscar estos datos al fichero yml de configuración. Por ejemplo, la macro <em><code>source()</code></em> contiene el nombre del <em>source</em> indicado en el yml y el nombre de la tabla.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">{{ config(materialized='table') }}

SELECT *
FROM {{ source('staging','green_tripdata') }}</code></pre>



<p>El fichero <strong><em>schema.yml</em></strong> que creamos en el mismo directorio donde está ubicado el modelo contiene la versión, nombre del origen, la base de datos, esquema y tablas. Una de las ventajas que ofrece tener la configuración en un fichero aparte de los modelos, es que nos permite cambiar la conexión para todos ellos desde un único lugar:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="yaml" class="language-yaml">version: 1

sources:
    - name: staging
      database: dbt_mtorregrosa
      schema: trips_data_all

      tables:
          - name: green_tripdata
          - name: yellow_tripdata</code></pre>
</div>



<p>Por tanto, en nuestra carpeta de<em> /models/ </em>de dbt tendremos un fichero <strong>.sql</strong> para cada modelo y un <strong>schema.yml </strong>con la configuración de los sources.</p>



<h4 class="has-light-green-cyan-color has-text-color has-medium-font-size wp-block-heading" id="dbt-seeds" style="font-style:normal;font-weight:100"><em>Seeds</em></h4>



<p>Similar a las <em>External Tables</em> de BigQuery o <em>Azure Synapse</em>, podemos referenciar cualquier fichero CSV almacenado en el repositorio dentro del directorio <code>/<em>seeds</em></code>, Al estar almacenado en una repo podemos aprovechar su control de versiones. Se recomiendo usar <em>seeds </em>para datos que no cambian con frecuencia (tablas de dimensiones paramétricas, como <em>provincias</em>). </p>



<p>En lugar de usar la macro <code>source()</code> como hemos visto antes para una tabla de BBDD, para los <em>seeds</em> se usa la macro <code>ref()</code> que recibe como parámetro el nombre del fichero y dbt automáticamente detecta las dependencias y su ubicación. La macro <code>ref()</code> también se puede usar para referenciar tablas o vistas de BBDD del mismo modo, sólo pasando por parámetro el nombre de la tabla. Para crear un <em>seed </em>simplemente subimos un fichero CSV al directorio /s<em>eeds </em>de nuestra repo y ejecutamos el comando <code>dbt seed taxi_zone_lookup.csv</code>. Si ejecutasemos <code>dbt seed</code> se cargarían en la base de datos todos los CSVs del directorio. Este comando genera una tabla en nuestra bbdd, en BigQuery:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">dbt seed taxi_zone_lookup.csv</code></pre>



<p>Si accedemos a BigQuery observamos que se ha creado una tabla con nombre <em>taxi_zone_lookup</em> que podremos consultar con <code>ref()</code> en cualquier dbt model.</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-57.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="466" data-attachment-id="4554" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-57/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-57.png?fit=1071%2C487&amp;ssl=1" data-orig-size="1071,487" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-57" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-57.png?fit=1024%2C466&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-57-1024x466.png?resize=1024%2C466&#038;ssl=1" alt="dbt seed que genera una tabla en BigQuery" class="wp-image-4554" title="dbt seed que genera una tabla en BigQuery" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-57.png?resize=1024%2C466&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-57.png?resize=300%2C136&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-57.png?resize=768%2C349&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-57.png?w=1071&amp;ssl=1 1071w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">dbt seed que genera una tabla en BigQuery</figcaption></figure>



<p>Ahora podemos crear un dbt model que referencie al seed recién creado:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">{{ config(materialized='table') }}

SELECT *
FROM {{ ref('<em>taxi_zone_lookup</em>') }}</code></pre>



<p>Si quisiéramos cambiar los tipos de datos del seed que por defecto a inferido del CSV, debemos modificar el fichero de configuración del proyecto <code>dbt_project.yml</code> y añadir el bloque de <em>seeds:</em></p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="yaml" class="language-yaml">seeds:
    taxi_riders_ny:
       taxi_zone_lookup:
          +column_types:
              locationid: numeric</code></pre>



<p>Si queremos recrear el seed debemos utilizar el comando <code>dbt seed micsv.csv --full-refresh</code>.</p>



<h3 class="has-medium-font-size wp-block-heading" id="dbt-macros" style="font-style:normal;font-weight:100">Macros</h3>



<p>Una macro en dbt es similar a una función en cualquier otro lenguaje redactadas en notación <em>jinja</em>. Se generan en ficheros independientes <em>.sql </em>en el directorio <em>/macros</em> del proyecto dbt. Como ya hemos comentado, por defecto dbt dispone de varias macros como <code>source()</code>, <code>ref()</code> y <code>config()</code>, pero podemos crear una nueva que atienda a nuestras necesidades. Algunas características de las macros:</p>



<ul class="wp-block-list">
<li>Permite añadir lógica con bucles <code>FOR</code> y sentencias <code>IF</code></li>



<li>Pueden utilizar variables de entorno definidas a nivel de proyecto en dbt</li>



<li>Permite reutilizar código entre distintos models</li>



<li>Utiliza en una consulta el resultado de otra subconsulta </li>
</ul>



<p>Podemos utilizar tres tipos de bloques jinja distintos dentro de una <a href="https://docs.getdbt.com/docs/build/jinja-macros" target="_blank" rel="noreferrer noopener">macro</a>:</p>



<ul class="wp-block-list">
<li><strong>Expresiones </strong><code>{{  }}</code>: cuando queremos devolver una cadena. Se pueden utilizar expresiones para referenciar variables o llamar a otras macros.</li>



<li><strong>Sentencias </strong><code>{%  %}</code>: se utilizan para el control del flujo, por ejemplo, para loops <code>FOR </code>o sentencias <code>IF</code>.</li>



<li><strong>Comentarios </strong><code>{#  #}</code>: el texto del comentario no se compila, nos permite indicar notas.</li>
</ul>



<p>Por ejemplo, en el siguiente código vamos a crear la <strong>macro</strong> <em>get_payment_type_description</em> que recibe como parámetro un valor en <em>payment_type</em> y resuelve el <code>CASE WHEN</code> devolviendo el valor correspondiente:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">{# This macro returns the description of the payment_type #}

{% macro get_payment_type_description(payment_type) %}

    case {{ payment_type }}
        when 1 then 'Credit card'
        when 2 then 'Cash'
        when 3 then 'No charge'
        when 4 then 'Dispute'
        when 5 then 'Unknown'
        when 6 then 'Voided trip'
    end

{% endmacro %}</code></pre>



<p>Usamos la macro en nuestro modelo dbt:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">{{ config(materialized='table') }}

SELECT 
    get_payment_type_description(payment_type)
FROM{{ source('staging','green_tripdata') }}
WHERE vendorid is not null
</code></pre>



<p>Cuando ejecutamos el modelo, dbt compila el código de forma que la tabla generada ha traducido la macro a un <code>CASE WHEN</code> en SQL:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">SELECT 
    case payment_type
        when 1 then 'Credit card'
        when 2 then 'Cash'
        when 3 then 'No charge'
        when 4 then 'Dispute'
        when 5 then 'Unknown'
        when 6 then 'Voided trip'
    end as payment_type_description
FROM {{ source('staging','green_tripdata') }}
WHERE vendorid is not null</code></pre>



<h3 class="has-medium-font-size wp-block-heading" id="dbt-packages" style="font-style:normal;font-weight:100">Packages</h3>



<p>Nos permite reutilizar macros entre distintos proyectos, similar a las librerías o módulos en otros lenguajes de programación. Para utilizar un <strong>package </strong>en nuestro proyecto debemos crear un fichero de configuración <strong>packages.yml</strong> en el directorio raíz de nuestro proyecto dbt.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="yaml" class="language-yaml">packages:
  - package: dbt-labs/dbt_utils
    version: 0.8.0</code></pre>



<p>Después debemos instalarlos ejecutando el comando <code>dbt deps</code> que se encarga de descargar todas las dependencias y ficheros del paquete dentro de nuestro proyecto. Una vez haya terminado se creará el directorio <em>dbt_packages/dbt_utils</em> en nuestro proyecto.</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-48.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="260" data-attachment-id="4488" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-48/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-48.png?fit=1294%2C329&amp;ssl=1" data-orig-size="1294,329" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-48" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-48.png?fit=1024%2C260&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-48-1024x260.png?resize=1024%2C260&#038;ssl=1" alt="Instalación de package en dbt" class="wp-image-4488" title="Instalación de package en dbt" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-48.png?resize=1024%2C260&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-48.png?resize=300%2C76&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-48.png?resize=768%2C195&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-48.png?w=1294&amp;ssl=1 1294w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Instalación de package en dbt</figcaption></figure>



<p>Podemos utilizar las macros del package recién instalado en cualquier model de nuestro proyecto. Por ejemplo, el package <strong><a href="https://hub.getdbt.com/" target="_blank" rel="noreferrer noopener">dbt-utils</a></strong> incluye la macro <code>surrogate_key </code>para crear una clave subrogada (id secuencial interno a la tabla).</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">{{ config(materialized='table') }}

SELECT
    {{ dbt_utils.surrogate_key(['vendorid', 'lpep_pickup_datetime']) }} as tripid,
    vendorid
FROM {{ source('staging','green_tripdata') }}
WHERE vendorid is not null</code></pre>



<p>Dbt compila este modelo creando una clave subrogada con una función hash:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">SELECT
    to_hex(md5(cast(coalesce(cast(vendorid as string), '') || '-' 
        || coalesce(cast(lpep_pickup_datetime as string), '') as string))) as tripid,
    vendorid
FROM {{ source('staging','green_tripdata') }}
WHERE vendorid is not null</code></pre>



<h3 class="has-medium-font-size wp-block-heading" id="dbt-variables" style="font-style:normal;font-weight:100">Variables</h3>



<p>Similar a las variables de cualquier lenguaje de programación. Con la misma finalidad, nos permite guardar un valor y reutilizarlo en cualquier parte del proyecto. Se pueden crear de dos formas:</p>



<ul class="wp-block-list">
<li>En el fichero de configuración del proyecto dbt (<strong>dbt_project.yml</strong>) ubicado en el directorio raíz añadiendo:</li>
</ul>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">vars:
    payment_type_values: [1, 2, 3, 4, 5, 6]</code></pre>



<ul class="wp-block-list">
<li>Por línea de comandos cuando construimos los modelos:</li>
</ul>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">dbt build --var 'is_test_run: false'</code></pre>



<p>Para utilizar una variable debemos usar la macro <code>var()</code> dentro de un modelo:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">{{ config(materialized='table') }}

SELECT *
FROM {{ source('staging','green_tripdata') }}
{% if var('is_test_run', default=true) %}

    limit 100

{% endif %}</code></pre>



<h2 class="wp-block-heading" id="dbt-case-use">Caso de uso: crear dbt models para proyecto tripdata</h2>



<p>Vamos a cargar en nuestro proyecto dbt los datasets del bootcamp y crear todos los artefactos (models, macros y schema). Repito los <a href="#prerequisitos">prerequesitos </a>de este post:</p>



<p>1. Cargamos en nuestro GCS Bucket todos los datasets comprimidos en formato csv.gz:</p>



<ul class="wp-block-list">
<li><strong>Datasets de las carreras de taxis:</strong> he dejado en <a href="https://github.com/hegdehog/data-engineering-zoomcamp-2023/tree/main/week_4_analytics_engineering" target="_blank" rel="noreferrer noopener">mi repositorio de github</a> un script de <em>Prefect </em>para cargar todos los ficheros en GCS Bucket para luego trabajar con ellos en dbt y persistirlos en la base de datos BigQuery.
<ul class="wp-block-list">
<li>Yellow taxi data &#8211; Años 2019 y 2020</li>



<li>Green taxi data &#8211; Años 2019 y 2020</li>



<li>fhv data &#8211; Año 2019</li>
</ul>
</li>



<li><strong>Dataset de zonas para lookup</strong>: puedes descargarlo desde el repositorio original <a href="https://s3.amazonaws.com/nyc-tlc/misc/taxi+_zone_lookup.csv" target="_blank" rel="noreferrer noopener">aquí</a></li>
</ul>



<p>3. Una vez hemos cargado en nuestro <em>GCS Bucket</em> todos los ficheros csv.gz de <em>tripdata</em>, vamos a crear las <strong>external tables</strong> para consumirlos como tablas de <em>BigQuery</em>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">CREATE OR REPLACE EXTERNAL TABLE trips_data_all.fhv_tripdata
OPTIONS (
  format = 'CSV',
  uris = ['gs://tripdata_n4gash/data/fhv/*.csv.gz']
);

CREATE OR REPLACE EXTERNAL TABLE trips_data_all.green_tripdata
OPTIONS (
  format = 'CSV',
  uris = ['gs://tripdata_n4gash/data/green/*.csv.gz']
);

CREATE OR REPLACE EXTERNAL TABLE trips_data_all.yellow_tripdata
OPTIONS (
  format = 'CSV',
  uris = ['gs://tripdata_n4gash/data/yellow/*.csv.gz']
);

CREATE OR REPLACE EXTERNAL TABLE trips_data_all.zones_tripdata
OPTIONS (
  format = 'CSV',
  uris = ['gs://tripdata_n4gash/data/taxi+_zone_lookup.csv']
);</code></pre>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-53.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="914" height="597" data-attachment-id="4538" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-53/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-53.png?fit=914%2C597&amp;ssl=1" data-orig-size="914,597" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-53" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-53.png?fit=914%2C597&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-53.png?resize=914%2C597&#038;ssl=1" alt="External tables en BigQuery" class="wp-image-4538" title="External tables en BigQuery" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-53.png?w=914&amp;ssl=1 914w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-53.png?resize=300%2C196&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-53.png?resize=768%2C502&amp;ssl=1 768w" sizes="auto, (max-width: 914px) 100vw, 914px" /></a><figcaption class="wp-element-caption">External tables en BigQuery</figcaption></figure>



<p>3. Creamos en dbt el <strong>schema, staging models y macros</strong>. Están publicados en la repo del bootcamp (<a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/tree/main/week_4_analytics_engineering/taxi_rides_ny/macros" target="_blank" rel="noreferrer noopener">macros </a>y <a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/tree/main/week_4_analytics_engineering/taxi_rides_ny/models/staging" target="_blank" rel="noreferrer noopener">schema-models</a>), simplemente copiamos el código en nuestra repo de github o directamente en dbt clout y compilamos:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">dbt build</code></pre>



<div class="wp-block-group has-background is-layout-constrained wp-block-group-is-layout-constrained" style="border-radius:5px;background-color:#f0cece">
<pre class="wp-block-code has-background" style="background-color:#f3e3e3"><code lang="bash" class="language-bash">Compilation Error in test accepted_values_stg_green_tripdata_Payment_type__False___var_payment_type_values_ (models/staging/schema.yml)
  'NoneType' object is not iterable
  
  &gt; in macro test_accepted_values (tests/generic/builtin.sql)
  &gt; called by test accepted_values_stg_green_tripdata_Payment_type__False___var_payment_type_values_ (models/staging/schema.yml)
</code></pre>



<p>Este error nos aparecerá si no hemos añadido en el fichero de configuración del proyecto <code>dbt_project.yml</code> las variables. Recuerda que debes añadir:</p>



<pre class="wp-block-code has-background" style="background-color:#f3e3e3"><code lang="bash" class="language-bash">vars:
  payment_type_values: [1, 2, 3, 4, 5, 6]</code></pre>
</div>



<div class="wp-block-group has-background is-layout-constrained wp-block-group-is-layout-constrained" style="border-radius:5px;background-color:#f0cece">
<pre class="wp-block-code has-background" style="background-color:#f3e3e3"><code lang="bash" class="language-bash">BigQuery adapter: 404 Not found: Dataset was not found in location EU</code></pre>



<p>No hemos configurado correctamente la location del conector a BigQuery en dbt. Para revisar la configuración en dbt nos dirigimos a <em>Account settings &gt; Project Analytics &gt;  BigQuery</em> &gt; bajamos hasta abajo del todo en <em><strong>Location </strong></em>y escribimos exactamente la <em>location </em>que vemos en GCP (EU):</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-51.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="841" height="456" data-attachment-id="4529" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-51/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-51.png?fit=841%2C456&amp;ssl=1" data-orig-size="841,456" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-51" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-51.png?fit=841%2C456&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-51.png?resize=841%2C456&#038;ssl=1" alt="Location de BigQuery" class="wp-image-4529" title="Location de BigQuery" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-51.png?w=841&amp;ssl=1 841w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-51.png?resize=300%2C163&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-51.png?resize=768%2C416&amp;ssl=1 768w" sizes="auto, (max-width: 841px) 100vw, 841px" /></a><figcaption class="wp-element-caption">Location de BigQuery</figcaption></figure>
</div>



<p>Si todo ha ido bien, vemos que al ejecutar el comando <code>dbt build</code> va a compilar y generar todos los modelos:</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-54.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="508" data-attachment-id="4543" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-54/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-54.png?fit=1163%2C577&amp;ssl=1" data-orig-size="1163,577" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-54" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-54.png?fit=1024%2C508&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-54-1024x508.png?resize=1024%2C508&#038;ssl=1" alt="Dbt build" class="wp-image-4543" title="Dbt build" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-54.png?resize=1024%2C508&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-54.png?resize=300%2C149&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-54.png?resize=768%2C381&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-54.png?w=1163&amp;ssl=1 1163w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Dbt build</figcaption></figure>



<p>Y si comprobamos en <em>BigQuery</em>, podemos ver que se han creado dos vistas (green y yellow):</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-56.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="647" data-attachment-id="4547" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-56/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-56.png?fit=1061%2C670&amp;ssl=1" data-orig-size="1061,670" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-56" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-56.png?fit=1024%2C647&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-56-1024x647.png?resize=1024%2C647&#038;ssl=1" alt="Vistas creadas en BigQuery con dbt" class="wp-image-4547" title="Vistas creadas en BigQuery con dbt" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-56.png?resize=1024%2C647&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-56.png?resize=300%2C189&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-56.png?resize=768%2C485&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-56.png?w=1061&amp;ssl=1 1061w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Vistas creadas en BigQuery con dbt</figcaption></figure>



<p>4. Creamos un seed de la tabla lookup de zonas. Podemos subir el <a href="https://s3.amazonaws.com/nyc-tlc/misc/taxi+_zone_lookup.csv" target="_blank" rel="noreferrer noopener">CSV </a>directamente al directorio <em>/seeds </em>en dbt o crear un fichero y copiar el contenido. Después ejecutamos el comando <code>dbt seed</code> para crear la <em>external table</em> en BigQuery y observamos cómo se ha creado en nuestro dataset la tabla <strong>taxi_zones_lookup</strong>.</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-57.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="466" data-attachment-id="4554" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-57/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-57.png?fit=1071%2C487&amp;ssl=1" data-orig-size="1071,487" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-57" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-57.png?fit=1024%2C466&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-57-1024x466.png?resize=1024%2C466&#038;ssl=1" alt="dbt seed que genera una tabla en BigQuery" class="wp-image-4554" title="dbt seed que genera una tabla en BigQuery" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-57.png?resize=1024%2C466&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-57.png?resize=300%2C136&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-57.png?resize=768%2C349&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-57.png?w=1071&amp;ssl=1 1071w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">dbt seed que genera una tabla en BigQuery</figcaption></figure>



<p>5. Creamos un <em>dbt model</em> del <em>seed </em>de zonas en la carpeta <em><code>models/core</code></em> con nombre <strong>dim_zones.sql</strong>: </p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">{{ config(materialized='table') }}

select 
    locationid, 
    borough, 
    zone, 
    replace(service_zone,'Boro','Green') as service_zone
from {{ ref('taxi_zone_lookup') }}</code></pre>



<p>6. Vamos a unir las tablas de hechos (<em>stg_green_tripdata </em>y <em>stg_yellow_tripdata</em>) y la el seed lookup (dim_zones) en una nueva tabla usando el script <a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/blob/main/week_4_analytics_engineering/taxi_rides_ny/models/core/fact_trips.sql" target="_blank" rel="noreferrer noopener">fact_trips.sql</a> que encontramos en la repo del bootcamp. Creamos un nuevo dbt model en la carpeta <code>models/core</code> con nombre <strong>fact_table.sql</strong> con el contenido. Si lo copiáis de la repo, recordad que debéis revisar los nombres de las tablas en vuestro propio proyecto. Lanzamos el comando <code>dbt build</code>. Si todo ha ido bien vemos que cada uno de los pasos se ha ejecutado correctamente:</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-58.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="628" data-attachment-id="4559" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-58/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-58.png?fit=1167%2C716&amp;ssl=1" data-orig-size="1167,716" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-58" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-58.png?fit=1024%2C628&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-58-1024x628.png?resize=1024%2C628&#038;ssl=1" alt="Ejecución de dbt model con éxito" class="wp-image-4559" title="Ejecución de dbt model con éxito" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-58.png?resize=1024%2C628&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-58.png?resize=300%2C184&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-58.png?resize=768%2C471&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-58.png?w=1167&amp;ssl=1 1167w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Ejecución de dbt model con éxito</figcaption></figure>



<p>7. Comprobamos el linaje del dbt model <strong>fact_trips</strong> y observamos cómo se identifican todas las dependencias desde la capa de origen, a staging y por último la tabla final. </p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-59.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="663" height="135" data-attachment-id="4561" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-59/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-59.png?fit=663%2C135&amp;ssl=1" data-orig-size="663,135" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-59" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-59.png?fit=663%2C135&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-59.png?resize=663%2C135&#038;ssl=1" alt="Linaje de dbt model" class="wp-image-4561" title="Linaje de dbt model" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-59.png?w=663&amp;ssl=1 663w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-59.png?resize=300%2C61&amp;ssl=1 300w" sizes="auto, (max-width: 663px) 100vw, 663px" /></a><figcaption class="wp-element-caption">Linaje de dbt model</figcaption></figure>



<p>8. Por último, vemos cómo se ha creado la tabla <strong>fact_trips </strong>en BigQuery:</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-60.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="656" data-attachment-id="4567" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-60/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-60.png?fit=1053%2C675&amp;ssl=1" data-orig-size="1053,675" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-60" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-60.png?fit=1024%2C656&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-60-1024x656.png?resize=1024%2C656&#038;ssl=1" alt="Tabla fact_table en BigQuery:" class="wp-image-4567" title="Tabla fact_table en BigQuery:" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-60.png?resize=1024%2C656&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-60.png?resize=300%2C192&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-60.png?resize=768%2C492&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-60.png?w=1053&amp;ssl=1 1053w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Tabla <strong>fact_table </strong>en BigQuery:</figcaption></figure>



<h2 class="wp-block-heading" id="automatic-test-dbt">Testing en dbt</h2>



<p>Una de las herramientas de las que disponemos los desarrolladores para certificar que nuestro código es correcto, hace lo que se espera y no afecta a lo que hubiera consolidado antes, es pasar varios <strong>test</strong>s <strong>automáticos</strong>. Pueden realizar distintas operaciones, por ejemplo, si nuestro proyecto está compuesto por varios métodos podríamos crear un test por cada una de las posibles salidas de cada método. Esto nos permite comprobar si todo sigue funcionando con normalidad y la nueva funcionalidad aporta el valor esperado. Si alguno de los test automáticos no pasa satisfactoriamente, el código no se despliega en el siguiente entorno.</p>



<p>En el mundo de datos el <em>testing </em>es más ortopédico en el sentido de que las validaciones que podemos construir son suposiciones en base a los datos: conteos, sumatorios, cálculo de KPIs, etc. Es decir, se trataría de sentencias <code>SELECT </code>cuyo resultado vamos a comparar con un valor esperado. En función del proyecto, podemos encontrarnos con frameworks de <strong>data quality </strong>o simplemente un puñado de queries que se ejecutan antes de desplegar los cambios en produccion.</p>



<p>En dbt podemos crear <a href="https://docs.getdbt.com/reference/resource-properties/tests" target="_blank" rel="noreferrer noopener">tests automáticos</a> de forma sencilla usando sentencias <code>SELECT</code>. Cuando lanzamos un test en dbt, éste compila el SQL y nos devuelve el número de registros <strong>que no lo han cumplido</strong>. Podemos usar tres tipos de tests en dbt:</p>



<ul class="wp-block-list">
<li><strong>Singular test:</strong> es la forma más sencilla de crear un test en dbt. Simplemente es una query donde definimos exactamente lo que deseamos comprobar para un caso de uso concreto. Esto implica que no se puede reutilizar, sólo nos sirve para un modelo. Se crea como un fichero .sql y se almacena en la carpeta de tests <code>/tests</code> o en la que hayamos definido en <code>dbt-project.yml</code>. Podemos asignarle el nombre que deseemos, ya que se trata de un test único para un sólo <em>model</em>.</li>



<li><strong>Generic test:</strong> como su nombre indica, se trata de tests genéricos que podemos reutilizar para cualquier <em>model</em>. Se trata de queries parametrizadas que aceptan parámetros de forma que podamos usarla para cualquier caso de uso.</li>



<li><strong>Out-of-the-box test:</strong> de forma predeterminada dbt dispone de 4 tests genéricos:
<ul class="wp-block-list">
<li><code>unique</code>: la columna tiene todos los valores únicos</li>



<li><code>Not null</code>: comprueba si tiene valor nulo (sin informar)</li>



<li><code>accepted_values</code>: similar a los <em>check constrings</em> en una BBDD, se trata de una lista de valores que puede contener la columna. Se construye como un array de <em>strings</em>: <code>values: ['placed', 'shipped', 'completed', 'returned']</code></li>



<li><code>relationships</code>: comprueba la IR del modelo. Por ejemplo, cada valor de la columna debe existir como PK en otra tabla.</li>
</ul>
</li>
</ul>



<p>Los tests se definen en un fichero yml en la carpeta models, por ejemplo, <code>schema.yml</code>, donde definimos las reglas de validación para cada columna. Por ejemplo, usando los tests <em>out-of-the-box</em>. Para utilizar un test que hayamos creado nosotros, debemos usar el nombre del .sql del mismo. Por cada test podemos indicar la severidad: <code>error </code>o <code>warn</code>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="yaml" class="language-yaml">columns:
          - name: tripid
            description: Primary key for this table, generated with a concatenation of vendorid+pickup_datetime
            tests:
                - unique:
                    severity: warn
                - not_null:
                    severity: warn</code></pre>



<p>Podemos ejecutar todos los tests del proyecto con el comando <code>dbt_test</code>. Al igual que para construirlos, si indicamos en el parámetro <code>--select</code> el nombre del model, sólo se ejecutarán los correspondientes.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="bash" class="language-bash">dbt test --select stg_green_tripdata</code></pre>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-62.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="403" data-attachment-id="4583" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-62/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-62.png?fit=1167%2C459&amp;ssl=1" data-orig-size="1167,459" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-62" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-62.png?fit=1024%2C403&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-62-1024x403.png?resize=1024%2C403&#038;ssl=1" alt="Ejecución de test con dbt test" class="wp-image-4583" title="Ejecución de test con dbt test" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-62.png?resize=1024%2C403&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-62.png?resize=300%2C118&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-62.png?resize=768%2C302&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-62.png?w=1167&amp;ssl=1 1167w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Ejecución de test con dbt test</figcaption></figure>



<h2 class="wp-block-heading" id="dbt-documentacion">Documentación automática</h2>



<p>Podemos configurar nuestro proyecto dbt para que genere de forma automática la documentación en formato web y publicada en dbt cloud. Incluye información del proyecto:</p>



<ul class="wp-block-list">
<li><strong>Models</strong>: script de los ficheros .sql y el compilado. Incluye sus dependencias con otros objetos en dbt (models, seeds&#8230;)</li>



<li><strong>Sources</strong>: tablas de base de datos origen</li>



<li><strong>DAG (Directed acyclic graph)</strong> de las macros <code>ref()</code> y <code>source()</code> son diagramas directos acíclicos dirigidos, una forma de representar el flujo de datos en una ETL donde la información viaja desde el punto A hasta el punto B.</li>



<li><strong>Descripciones </strong>ubicadas en los ficheros .yml </li>



<li><strong>Tests</strong></li>



<li>Información sobre los datos de las BBDD origen (nombre de columnas y tipo de datos, estadísticas de tablas, etc.)</li>
</ul>



<h2 class="wp-block-heading" id="dbt-deployment">Despliegue de un model dbt</h2>



<p>Con dbt podemos configurar <strong>CI/CD</strong> (integración continua y despliegue/entrega continua) para desplegar nuestros <em>models </em>en producción. Separar los entornos de desarrollo y despliegue nos permite construir modelos y probarlos sin afectar a producción. En función del proyecto podemos encontrar que el entorno de despliegue o producción está ubicado en otra máquina o simplemente en otro esquema y usuario. El flujo habitual de desarrollo-despliegue sería:</p>



<ol class="wp-block-list">
<li>Creamos una nueva rama en la repo</li>



<li>Abrimos una <em>pull request</em> para unir nuestra rama a la principal</li>



<li>Unimos (merge) nuestra rama con la principal</li>



<li>Ejecutamos los nuevos models en el entorno de producción</li>



<li>Programamos la ejecución de los models usando <strong>jobs</strong>.</li>
</ol>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/dbtOverview-1024x640-1.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="640" data-attachment-id="4592" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/dbtoverview-1024x640-1/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/dbtOverview-1024x640-1.png?fit=1024%2C640&amp;ssl=1" data-orig-size="1024,640" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="dbtOverview-1024&amp;#215;640-1" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/dbtOverview-1024x640-1.png?fit=1024%2C640&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/dbtOverview-1024x640-1.png?resize=1024%2C640&#038;ssl=1" alt="Despliegues en dbt" class="wp-image-4592" title="Despliegues en dbt" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/dbtOverview-1024x640-1.png?w=1024&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/dbtOverview-1024x640-1.png?resize=300%2C188&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/dbtOverview-1024x640-1.png?resize=768%2C480&amp;ssl=1 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Despliegues en dbt</figcaption></figure>



<h3 class="has-medium-font-size wp-block-heading" id="dbt-jobs" style="font-style:normal;font-weight:100">Jobs en dbt</h3>



<p>Dbt incluye un programador donde configurar jobs para que ejecuten los modelos en producción. Los jobs se pueden programar o ejecutar manualmente y cada uno puede tener uno o varios comandos (construir varios modelos, lanzar tests, etc). </p>



<p>Previo a crear el job, lo primero que tendríamos que hacer es configurar un<strong> entorno de producción</strong> para así tener diferenciados el de desarrollo del de despliegue. Para crearlo nos dirigimos a <strong><em>Deploy</em> &gt; <em>Environments</em></strong> y pulsamos sobre el botón <strong>Create Environment</strong>. En <strong>Deployment credentials</strong> debemos indicar el dataset de BigQuery de destino. Podemos crearlo previamente desde GCP o escribir un nombre y dbt automáticamente lo creará:</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-69.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="736" data-attachment-id="4602" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-69/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-69.png?fit=1212%2C871&amp;ssl=1" data-orig-size="1212,871" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-69" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-69.png?fit=1024%2C736&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-69-1024x736.png?resize=1024%2C736&#038;ssl=1" alt="Creación nuevo entorno de producción en dbt" class="wp-image-4602" title="Creación nuevo entorno de producción en dbt" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-69.png?resize=1024%2C736&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-69.png?resize=300%2C216&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-69.png?resize=768%2C552&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-69.png?w=1212&amp;ssl=1 1212w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Creación nuevo entorno de producción en dbt</figcaption></figure>



<p>Ahora sí podríamos crear un job para que publique los cambios de desarrollo en producción. Para ello, vamos a pulsar en <strong><em>Deploy &gt; Jobs</em></strong> y en el botón <strong>Create Job</strong>. Vamos a especificar un nombre:</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-66.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="762" data-attachment-id="4597" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-66/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-66.png?fit=1210%2C901&amp;ssl=1" data-orig-size="1210,901" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-66" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-66.png?fit=1024%2C762&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-66-1024x762.png?resize=1024%2C762&#038;ssl=1" alt="Crear nuevo job en dbt" class="wp-image-4597" title="Crear nuevo job en dbt" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-66.png?resize=1024%2C762&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-66.png?resize=300%2C223&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-66.png?resize=768%2C572&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-66.png?w=1210&amp;ssl=1 1210w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Crear nuevo job en dbt</figcaption></figure>



<p>Marcamos que genere la documentación y añadimos los comandos: <code>dbt seed</code> (crea el seed del CSV de zonas), <code>dbt run</code> (ejecuta y compila los models) y <code>dbt build</code> (para construirlos).</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-67.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="666" data-attachment-id="4598" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-67/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-67.png?fit=1202%2C782&amp;ssl=1" data-orig-size="1202,782" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-67" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-67.png?fit=1024%2C666&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-67-1024x666.png?resize=1024%2C666&#038;ssl=1" alt="Configuración job en dbt" class="wp-image-4598" title="Configuración job en dbt" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-67.png?resize=1024%2C666&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-67.png?resize=300%2C195&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-67.png?resize=768%2C500&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-67.png?w=1202&amp;ssl=1 1202w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Configuración job en dbt</figcaption></figure>



<p>Por último, configuramos la programación en el apartado de <strong>triggers:</strong></p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-68.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="529" data-attachment-id="4599" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-68/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-68.png?fit=1204%2C622&amp;ssl=1" data-orig-size="1204,622" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-68" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-68.png?fit=1024%2C529&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-68-1024x529.png?resize=1024%2C529&#038;ssl=1" alt="Configuración de trigger de job dbt" class="wp-image-4599" title="Configuración de trigger de job dbt" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-68.png?resize=1024%2C529&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-68.png?resize=300%2C155&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-68.png?resize=768%2C397&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-68.png?w=1204&amp;ssl=1 1204w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Configuración de trigger de job dbt</figcaption></figure>



<p>Una vez ejecutado el job, comprobamos que todos los pasos han terminado correctamente y se ha generado la documentación:</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-71.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="953" data-attachment-id="4608" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-71/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-71.png?fit=1219%2C1134&amp;ssl=1" data-orig-size="1219,1134" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-71" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-71.png?fit=1024%2C953&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-71-1024x953.png?resize=1024%2C953&#038;ssl=1" alt="Job ejecutado con éxito en dbt" class="wp-image-4608" title="Job ejecutado con éxito en dbt" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-71.png?resize=1024%2C953&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-71.png?resize=300%2C279&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-71.png?resize=768%2C714&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-71.png?w=1219&amp;ssl=1 1219w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Job ejecutado con éxito en dbt</figcaption></figure>



<p>Y que en BigQuery se han creado las tablas y vistas que teníamos en el dataset de staging:</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-70.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="701" data-attachment-id="4604" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-70/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-70.png?fit=1094%2C749&amp;ssl=1" data-orig-size="1094,749" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-70" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-70.png?fit=1024%2C701&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-70-1024x701.png?resize=1024%2C701&#038;ssl=1" alt="Entorno de producción en BigQuery" class="wp-image-4604" title="Entorno de producción en BigQuery" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-70.png?resize=1024%2C701&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-70.png?resize=300%2C205&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-70.png?resize=768%2C526&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-70.png?w=1094&amp;ssl=1 1094w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Entorno de producción en BigQuery</figcaption></figure>



<h3 class="has-medium-font-size wp-block-heading" id="dbt-integracion-continua-ci" style="font-style:normal;font-weight:100">Integración continua (CI)</h3>



<p>La integración continua (CI) es una práctica de desarrollo de software que se utiliza para garantizar que el código se integra y se prueba automáticamente de manera regular y frecuente.</p>



<p>En CI, los desarrolladores envían su código a un repositorio compartido varias veces al día, lo que desencadena una serie de procesos automatizados que incluyen compilación, prueba y análisis estático de código. Estos procesos se ejecutan automáticamente en un entorno de prueba aislado, que se crea y destruye para cada ciclo de integración, lo que garantiza que el código se pruebe en un entorno limpio y repetible.</p>



<p>El objetivo de la integración continua es detectar y solucionar problemas en el código de manera temprana, lo que ayuda a reducir el tiempo y el costo de corrección de errores más adelante en el ciclo de desarrollo. Además, al integrar y probar regularmente el código, se mejora la calidad del mismo y se facilita el proceso de entrega de software.</p>



<p>Podemos emplear la<strong> integración continua (CI) </strong>en un proyecto dbt en las pull request (cuando solicitamos unir nuestra rama a la principal) usando webhooks de <strong><em>Github</em></strong>, <strong>Azure DevOps</strong> o <strong><em>Gitlab</em></strong>. Cuando una PR está aprobada se envía un <em>webhook </em>a <strong><em>dbt cloud</em></strong> que encola una nueva ejecución del job correspondiente. La ejecución del job se realiza sobre un esquema temporal que se crea y autodestruye. La PR no realizará el merge hasta que finalice la ejecución del job. Vamos a hacer una prueba (puedes consultar <a href="https://docs.getdbt.com/docs/deploy/cloud-ci-job" target="_blank" rel="noreferrer noopener">aquí </a>toda la documentación):</p>



<p>1. Creamos un nuevo un job cuyo <strong><em>trigger</em></strong> sea Continious Integration (CI) y activamos la opción de <strong>Run on Pull Requests?</strong></p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-77.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="557" data-attachment-id="4620" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-77/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-77.png?fit=1186%2C645&amp;ssl=1" data-orig-size="1186,645" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-77" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-77.png?fit=1024%2C557&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-77-1024x557.png?resize=1024%2C557&#038;ssl=1" alt="Dbt job con integración continua (CI)" class="wp-image-4620" title="Dbt job con integración continua (CI)" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-77.png?resize=1024%2C557&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-77.png?resize=300%2C163&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-77.png?resize=768%2C418&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-77.png?w=1186&amp;ssl=1 1186w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Dbt job con integración continua (CI)</figcaption></figure>



<div class="wp-block-group has-background is-layout-constrained wp-block-group-is-layout-constrained" style="border-radius:5px;background-color:#f0cece">
<p>Si no nos aparece el check <strong><em>Run on Pull Requests?</em> </strong>debemos reconfigurar la conexión a <em>Github </em>y usar la<strong> conexión nativa </strong>desde <em>dbt</em>. Es necesario seguir los siguientes <a href="https://docs.getdbt.com/docs/deploy/cloud-ci-job#reconnecting-your-dbt-project-to-use-dbt-clouds-native-integration-with-github-gitlab-or-azure-devops" target="_blank" rel="noreferrer noopener">pasos</a>:</p>



<ol class="wp-block-list">
<li>Conecta tu cuenta de dbt con <em>Github </em>y otorga permisos de lectura/escritura sobre el repositorio que vayas a utilizar. Desde <em><strong>Profile Settings &gt; Linked Accounts</strong> </em>seleccionamos <em>Github</em> y en el botón <em>Configure integration with Github.</em> Más info en esta <a href="https://docs.getdbt.com/docs/collaborate/git/connect-github" target="_blank" rel="noreferrer noopener">nota de dbt</a>.</li>



<li>Desconecta en el proyecto la configuración actual de <em>Github </em>por SSH desde <strong><em>Account Setting</em></strong><em><strong>s &gt; Projects (analytics)</strong> <strong>&gt; Github connection </strong></em>pulsamos en editar y abajo a la izquierda aparece el botón <strong>Disconnect</strong>.</li>



<li>SI volvemos a la pantalla de configuración del proyecto y pulsamos de nuevo en <strong>Repository Details </strong>podemos seleccionar de nuevo el proveedor de repositorios. Esta vez en lugar de clonar, vamos a conectarnos directamente a <strong><em>Github</em></strong> y seleccionar un repositorio:</li>
</ol>



<figure class="wp-block-image aligncenter size-large is-resized"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-78.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" data-attachment-id="4622" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-78/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-78.png?fit=1342%2C1182&amp;ssl=1" data-orig-size="1342,1182" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-78" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-78.png?fit=1024%2C902&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-78-1024x902.png?resize=504%2C444&#038;ssl=1" alt="" class="wp-image-4622" width="504" height="444" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-78.png?resize=1024%2C902&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-78.png?resize=300%2C264&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-78.png?resize=768%2C676&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-78.png?w=1342&amp;ssl=1 1342w" sizes="auto, (max-width: 504px) 100vw, 504px" /></a><figcaption class="wp-element-caption">Conectar dbt a github</figcaption></figure>
</div>



<p>2. Actualizamos nuestro proyecto con la última foto de la repo si es necesario (<em>pull</em>) y creamos una nueva rama (<em>create new branch</em>):</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-73.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="862" height="554" data-attachment-id="4613" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-73/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-73.png?fit=862%2C554&amp;ssl=1" data-orig-size="862,554" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-73" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-73.png?fit=862%2C554&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-73.png?resize=862%2C554&#038;ssl=1" alt="Nueva rama en proyecto dbt para probar integración continua (CI)" class="wp-image-4613" title="Nueva rama en proyecto dbt para probar integración continua (CI)" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-73.png?w=862&amp;ssl=1 862w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-73.png?resize=300%2C193&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-73.png?resize=768%2C494&amp;ssl=1 768w" sizes="auto, (max-width: 862px) 100vw, 862px" /></a><figcaption class="wp-element-caption">Nueva rama en proyecto dbt para probar integración continua (CI)</figcaption></figure>



<p>3. Hacemos cualquier cambio en alguno de los models. Hacemos commit del cambio y finalmente una PR:</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-75.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1001" height="566" data-attachment-id="4615" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-75/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-75.png?fit=1001%2C566&amp;ssl=1" data-orig-size="1001,566" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-75" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-75.png?fit=1001%2C566&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-75.png?resize=1001%2C566&#038;ssl=1" alt="" class="wp-image-4615" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-75.png?w=1001&amp;ssl=1 1001w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-75.png?resize=300%2C170&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-75.png?resize=768%2C434&amp;ssl=1 768w" sizes="auto, (max-width: 1001px) 100vw, 1001px" /></a></figure>



<p>4. Desde <em>Github </em>aprobamos la PR.</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-76.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="783" data-attachment-id="4616" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-76/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-76.png?fit=1154%2C882&amp;ssl=1" data-orig-size="1154,882" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-76" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-76.png?fit=1024%2C783&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-76-1024x783.png?resize=1024%2C783&#038;ssl=1" alt="Desde Github aprobamos el PR" class="wp-image-4616" title="Desde Github aprobamos el PR" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-76.png?resize=1024%2C783&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-76.png?resize=300%2C229&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-76.png?resize=768%2C587&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-76.png?w=1154&amp;ssl=1 1154w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Desde Github aprobamos el PR</figcaption></figure>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-79.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="538" data-attachment-id="4624" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-79/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-79.png?fit=1198%2C629&amp;ssl=1" data-orig-size="1198,629" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-79" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-79.png?fit=1024%2C538&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-79-1024x538.png?resize=1024%2C538&#038;ssl=1" alt="Se ejecuta el merge en la repo" class="wp-image-4624" title="Se ejecuta el merge en la repo" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-79.png?resize=1024%2C538&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-79.png?resize=300%2C158&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-79.png?resize=768%2C403&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-79.png?w=1198&amp;ssl=1 1198w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Se ejecuta el merge en la repo</figcaption></figure>



<p>Si nos dirigimos a <em>dbt </em>podemos ver como se ha encolado una nueva ejecución del job:</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-80.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="470" data-attachment-id="4626" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-80/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-80.png?fit=1139%2C523&amp;ssl=1" data-orig-size="1139,523" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-80" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-80.png?fit=1024%2C470&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-80-1024x470.png?resize=1024%2C470&#038;ssl=1" alt="Job ejecutado en dbt por pull request de github" class="wp-image-4626" title="Job ejecutado en dbt por pull request de github" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-80.png?resize=1024%2C470&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-80.png?resize=300%2C138&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-80.png?resize=768%2C353&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-80.png?w=1139&amp;ssl=1 1139w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Job ejecutado en dbt por pull request de github</figcaption></figure>



<p>Si revisamos los pasos del job podemos ver que se ha disparado desde una PR y que se crea un esquema temporal en nuestro dataset de <em>BigQuery </em>con nombre <em>dbt_cloud_pr_219037_6</em>. Este esquema se autodestruye cuando finaliza el job.</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-81.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="984" data-attachment-id="4629" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-81/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-81.png?fit=1234%2C1186&amp;ssl=1" data-orig-size="1234,1186" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-81" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-81.png?fit=1024%2C984&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-81-1024x984.png?resize=1024%2C984&#038;ssl=1" alt="Ejecución de job dbt desde PR de Github" class="wp-image-4629" title="Ejecución de job dbt desde PR de Github" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-81.png?resize=1024%2C984&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-81.png?resize=300%2C288&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-81.png?resize=768%2C738&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-81.png?w=1234&amp;ssl=1 1234w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Ejecución de job dbt desde PR de Github</figcaption></figure>



<h2 class="wp-block-heading" id="google-data-studio">Google Data Studio</h2>



<p><a href="https://lookerstudio.google.com/" target="_blank" rel="noreferrer noopener">Google Data Studio</a> es una herramienta gratuita basada en <em>Looker Studio</em> de visualización de datos y creación de informes que permite a los usuarios conectarse a múltiples fuentes de datos, como Google Analytics, Google Ads, Google Sheets, bases de datos y más, para crear informes personalizados y visualizaciones interactivas.</p>



<p>La plataforma ofrece una interfaz gráfica fácil de usar que permite a los usuarios diseñar y personalizar informes con diferentes tipos de gráficos, tablas, indicadores clave de rendimiento (KPIs) y otras visualizaciones, y compartirlos con otros usuarios de forma segura y fácilmente.</p>



<p>Básicamente tenemos dos tipos de elementos: <strong>informes </strong>y<strong> fuentes de datos</strong>. Los primeros son los cuadros de mando con las visualizaciones y los segundos son los conectores con las tablas de los sistemas fuente. El primer paso para generar un <em>dashboard </em>es configurar las fuentes de datos.</p>



<h3 class="has-medium-font-size wp-block-heading" id="google-data-studio-data-source" style="font-style:normal;font-weight:100">Fuente de datos (Data Source)</h3>



<p>Una fuente de datos es una conexión a una tabla de nuestro sistema origen (BigQuery en el caso del ejemplo). Por defecto infiere la estructura de datos de la base de datos, pero podemos modificarla o crear nuevos elementos como campos y parámetros:</p>



<ul class="wp-block-list">
<li><strong>Campos</strong>: podemos crear nuevos KPI o campos derivados a partir de otros de la tabla utilizando el catálogo de funciones disponible en la <a href="https://support.google.com/looker-studio/table/6379764?hl=en" target="_blank" rel="noreferrer noopener">documentación de Google Data Studio.</a></li>



<li><strong>Parámetros</strong>: los parámetros permiten interactuar de forma dinámica al usuario con los datos del informe. Mediante entrada de datos, podemos por ejemplo realizar estimaciones de algún cálculo en base al valor que haya introducido el usuario.</li>
</ul>



<p>Seguimos los siguientes pasos para configurar una fuente de datos:</p>



<p>1. Pulsamos en <strong>Crear</strong> y seleccionamos <strong>fuente de datos</strong>. En el nuevo panel buscamos por el conector de <strong>BigQuery:</strong></p>



<figure class="wp-block-image aligncenter size-full is-resized"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-82.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" data-attachment-id="4639" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-82/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-82.png?fit=1018%2C525&amp;ssl=1" data-orig-size="1018,525" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-82" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-82.png?fit=1018%2C525&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-82.png?resize=764%2C394&#038;ssl=1" alt="Conectar Google Data Studio con BigQuery" class="wp-image-4639" width="764" height="394" title="Conectar Google Data Studio con BigQuery" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-82.png?w=1018&amp;ssl=1 1018w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-82.png?resize=300%2C155&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-82.png?resize=768%2C396&amp;ssl=1 768w" sizes="auto, (max-width: 764px) 100vw, 764px" /></a><figcaption class="wp-element-caption">Conectar Google Data Studio con BigQuery</figcaption></figure>



<p>2. Autorizamos el acceso de Google Data Studio a nuestro BigQuery:</p>



<figure class="wp-block-image aligncenter size-large is-resized"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-83.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" data-attachment-id="4641" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-83/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-83.png?fit=1267%2C735&amp;ssl=1" data-orig-size="1267,735" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-83" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-83.png?fit=1024%2C594&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-83-1024x594.png?resize=768%2C446&#038;ssl=1" alt="Autorización de Google Data Studio a BigQuery" class="wp-image-4641" width="768" height="446" title="Autorización de Google Data Studio a BigQuery" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-83.png?resize=1024%2C594&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-83.png?resize=300%2C174&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-83.png?resize=768%2C446&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-83.png?w=1267&amp;ssl=1 1267w" sizes="auto, (max-width: 768px) 100vw, 768px" /></a><figcaption class="wp-element-caption">Autorización de Google Data Studio a BigQuery</figcaption></figure>



<p>3. Seleccionamos la tabla que queremos usar como origen de datos:</p>



<figure class="wp-block-image aligncenter size-large is-resized"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-84.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" data-attachment-id="4645" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-84/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-84.png?fit=1422%2C791&amp;ssl=1" data-orig-size="1422,791" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-84" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-84.png?fit=1024%2C570&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-84-1024x570.png?resize=768%2C428&#038;ssl=1" alt="Configuración de datos de orige en Google Data Studio" class="wp-image-4645" width="768" height="428" title="Configuración de datos de orige en Google Data Studio" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-84.png?resize=1024%2C570&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-84.png?resize=300%2C167&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-84.png?resize=768%2C427&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-84.png?w=1422&amp;ssl=1 1422w" sizes="auto, (max-width: 768px) 100vw, 768px" /></a><figcaption class="wp-element-caption">Configuración de datos de origen en Google Data Studio</figcaption></figure>



<p>4. El último paso para la fuente de datos es revisar y confirmar la estructura de datos que ha inferido del sistema origen. En este punto también podemos realizar tareas de transformación y crear nuevos <strong>campos </strong>y <strong>parámetros</strong>. Por ejemplo, aunque podemos hacerlo al vuelo mientras diseñamos un informe, desde este punto podríamos crear el campo <code>month_pickup</code> que segmente los datos por mes con la fórmula <code>month(pickup_datetime)</code>:</p>



<figure class="wp-block-image aligncenter size-large is-resized"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-85.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" data-attachment-id="4649" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-85/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-85.png?fit=1271%2C908&amp;ssl=1" data-orig-size="1271,908" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-85" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-85.png?fit=1024%2C732&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-85-1024x732.png?resize=768%2C549&#038;ssl=1" alt="" class="wp-image-4649" width="768" height="549" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-85.png?resize=1024%2C732&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-85.png?resize=300%2C214&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-85.png?resize=768%2C549&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-85.png?w=1271&amp;ssl=1 1271w" sizes="auto, (max-width: 768px) 100vw, 768px" /></a><figcaption class="wp-element-caption">Panel de edición de estructura de fuente de datos</figcaption></figure>



<p>O en tiempo de diseño, desde el <em>blade </em>de datos:</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-86.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="638" data-attachment-id="4651" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-86/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-86.png?fit=1493%2C930&amp;ssl=1" data-orig-size="1493,930" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-86" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-86.png?fit=1024%2C638&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-86-1024x638.png?resize=1024%2C638&#038;ssl=1" alt="Añadir nuevo campo personalizado" class="wp-image-4651" title="Añadir nuevo campo personalizado" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-86.png?resize=1024%2C638&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-86.png?resize=300%2C187&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-86.png?resize=768%2C478&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-86.png?w=1493&amp;ssl=1 1493w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Añadir nuevo campo personalizado</figcaption></figure>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-87.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="380" data-attachment-id="4655" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-87/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-87.png?fit=1577%2C585&amp;ssl=1" data-orig-size="1577,585" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-87" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-87.png?fit=1024%2C380&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-87-1024x380.png?resize=1024%2C380&#038;ssl=1" alt="Añadir nuevo campo personalizado" class="wp-image-4655" title="Añadir nuevo campo personalizado" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-87.png?resize=1024%2C380&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-87.png?resize=300%2C111&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-87.png?resize=768%2C285&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-87.png?resize=1536%2C570&amp;ssl=1 1536w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-87.png?w=1577&amp;ssl=1 1577w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Añadir nuevo campo personalizado</figcaption></figure>



<h3 class="has-medium-font-size wp-block-heading" id="google-data-studio-report" style="font-style:normal;font-weight:100">Informes en Google Data Studio (reports)</h3>



<p>Crear un informe en <strong>Google Data Studio </strong>es muy sencillo, siguiendo la tendencia actual de herramientas como <em>Power BI</em> de Microsoft, <em>Qlik Sense</em> o <em>MicroStrategy Visual Insights. </em>Tenemos a nuestra disposición un lienzo en blanco sobre el que vamos a construir visualizaciones en base al set de datos que hemos configurado: seleccionamos el control, el gráfico sobre el que configuramos sus dimensiones y métricas y ¡listo! Veamos qué son estos dos tipos de elementos:</p>



<ul class="wp-block-list">
<li><strong>Controles</strong>: objetos que nos permiten interactuar con los datos de las visualizaciones, por ejemplo, filtros de selección, cajas para introducir texto, listas desplegables con todos los valores de una dimensión, etc.</li>



<li><strong>Gráficos</strong>: o visualizaciones, son todo tipo de gráficos estadísticos que podemos utilizar para analizar y presentar la información: gráficos de tartas, barras, línea, burbujas, etc. En función del gráfico seleccionado debemos elegir una o varias dimensiones y una o varias métricas.</li>
</ul>



<p>Tras unos minutos de esfuerzo podemos disfrutar de nuestro primer <em>report </em>en <strong>Google DAta Studio</strong>:</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-88.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="766" data-attachment-id="4657" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-4/image-88/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-88.png?fit=1483%2C1109&amp;ssl=1" data-orig-size="1483,1109" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-88" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-88.png?fit=1024%2C766&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-88-1024x766.png?resize=1024%2C766&#038;ssl=1" alt="Primer report en Google Data Studio" class="wp-image-4657" title="Primer report en Google Data Studio" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-88.png?resize=1024%2C766&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-88.png?resize=300%2C224&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-88.png?resize=768%2C574&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-88.png?w=1483&amp;ssl=1 1483w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Primer report en Google Data Studio</figcaption></figure>
<p>La entrada <a href="https://n4gash.com/data-engineering-zoomcamp-semana-4/">Data Engineering Zoomcamp – Semana 4</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://n4gash.com/data-engineering-zoomcamp-semana-4/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4353</post-id>	</item>
		<item>
		<title>Python: Optimizar uso de memoria con Pandas</title>
		<link>https://n4gash.com/python-optimizar-uso-de-memoria-con-pandas/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=python-optimizar-uso-de-memoria-con-pandas</link>
		
		<dc:creator><![CDATA[Marcos Torregrosa]]></dc:creator>
		<pubDate>Mon, 13 Feb 2023 12:25:57 +0000</pubDate>
				<category><![CDATA[Data Engineering]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://www.n4gash.com/?p=4334</guid>

					<description><![CDATA[<p>Si estás trabajando con dataframes de Pandas en Python te propongo varias formas de optimizar el consumo de memoria RAM. Para nuestro ejemplo vamos a usar el siguiente dataset: En primer lugar, para analizar el consumo de memoria de cada columna de un dataframe usamos el método de Pandas memory_usage. Vamos a usar dos parámetros con este método: Como el resultado lo representa en bytes, podemos dividir dos veces por 1024 para tener el dato en MB. Nos devuelve el peso en bytes del índice y cada una de las columnas. Si comprobamos el tipo de datos inferido del dataset:…</p>
<p>La entrada <a href="https://n4gash.com/python-optimizar-uso-de-memoria-con-pandas/">Python: Optimizar uso de memoria con Pandas</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Si estás trabajando con <em>dataframes </em>de <em><a href="https://pandas.pydata.org/" target="_blank" rel="noreferrer noopener">Pandas </a></em>en Python te propongo varias formas de optimizar el consumo de memoria RAM. Para nuestro ejemplo vamos a usar el siguiente dataset:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">import pandas as pd
data = [
        {'name' : 'John Connor', 'world' : 'Earth', 'age' : 16, 'survive' : 1},
        {'name' : 'Max Rockatansky', 'world' : 'Earth', 'age' : 31, 'survive' : 1},
        {'name' : 'Ender', 'world' : 'Albion' , 'age' : 6, 'survive' : 1},
        {'name' : 'Anakin Skywalker', 'world' : 'Tatooine', 'age' : 8, 'survive' : 0},
        {'name' : 'Ellen Ripley', 'world' : 'Earth', 'age' : 37, 'survive' : 0},
        {'name' : 'Willow Ufgood', 'world' : 'Earth', 'age' : 25, 'survive' : 1}
]
df = pd.DataFrame(data)
df.dtypes</code></pre>



<p>En primer lugar, para analizar el consumo de memoria de cada columna de un <em>dataframe</em> usamos el método de <em>Pandas </em><code>memory_usage</code>. Vamos a usar dos  parámetros con este método:</p>



<ul class="wp-block-list">
<li><code>deep = True</code> : estima el uso de memoria más preciso a nivel de fila y tipo de dato. </li>



<li><code>index = False</code> : si no indicamos nada, por defecto nos indica el consumo de RAM del índice del <em>dataframe </em>además de cada columna.</li>
</ul>



<p>Como el resultado lo representa en bytes, podemos dividir dos veces por 1024 para tener el dato en MB.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code class="">df.memory_usage(deep = True, index = False)</code></pre>



<p>Nos devuelve el peso en bytes del índice y cada una de las columnas.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">Index      128
name       414
world      376
age         48
survive     48
dtype: int64</code></pre>



<p>Si comprobamos el tipo de datos inferido del dataset:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df.dtypes</code></pre>



<p>Vemos que no es lo más óptimo. Las columnas de tipo <em>string</em> las ha tipado como <code>OBJECT</code> y las numéricas <code>INT64</code>, veamos qué podemos hacer:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">name       object
world      object
age         int64
survive     int64
dtype: object</code></pre>



<h2 class="wp-block-heading">Filtro de datos categóricos (CATEGORY)</h2>



<p>Las columnas de datos categóricos de tipo <code>STRING</code> o <code>DATE</code> podemos convertirlos en <code>CATEGORY</code>. Es importante tener en cuenta la <strong>cardinalidad de los datos</strong> (cantidad de valores distintos). Vamos a conseguir reducir el consumo de memoria siempre que tengan baja o media cardinalidad, si convertimos a <em>category</em> una columna con una cardinalidad muy alta probablemente necesite más memoria que si no lo hiciéramos. Lo que hace el tipo <code>CATEGORY</code> es crear un diccionario de todos los valores distintos de una columna, sustituyéndolos por punteros al diccionario. Vamos a probar con nuestro ejemplo, primero vamos a observar el consumo de memoria de las columnas <strong><em>name </em></strong>y <strong><em>world</em></strong> según están definidas como tipo <code>OBJECT</code> con <code>df.memory_usage(deep = True, index = False)</code>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">name       414
world      376
dtype: int64</code></pre>



<p>Aplicamos la optimización cambiando el tipo a <code>CATEGORY</code>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df["name"] = df["name"].astype("category")
df["world"] = df["world"].astype("category")</code></pre>



<p>Y volvemos a observar el uso de memoria:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">name       592
world      304
dtype: int64</code></pre>



<p>La columna <strong><em>world</em> </strong>logra <strong>reducir el peso un 19% </strong>(de 375 bytes a 304), sin embargo ¿qué ha pasado con <em><strong>name</strong></em>? ¡ha aumentado el consumo de memoria! este comportamiento se debe a que la cardinalidad de la columna es muy alta (un dataset con 5 registros y 5 valores distintos), por lo que al generar el diccionario de la categoría necesita más memoria.</p>



<h2 class="wp-block-heading">Optimizar el tipado de los datos</h2>



<p>En este paso vamos a cambiar el tipo de cada columna para intentar ahorrar costes. Las columnas de texto a <code>STRING</code> y las numéricas que por defecto asigna como <code>INT64</code>, podríamos convertirlas a <code>INT8</code>, <code>INT16 </code>o <code>INT32</code>. Siguiendo el ejemplo del post:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">name       object
world      object
age         int64
survive     int64
dtype: object</code></pre>



<p>Vamos a analizar cómo cambia el consumo de memoria al convertir las columnas <code>OBJECT </code>a <code>STRING</code> y las numéricas a <code>INT8 </code>(nos vale un entero de 8 bits porque abarca valores de -128 a 127). Primero observamos el consumo de memoria antes de hacer la conversión:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">name       414
world      376
age         48
survive     48
dtype: int64</code></pre>



<p>Y aplicamos las conversiones:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">df["name"] = df["name"].astype("string")
df["world"] = df["world"].astype("string")
df["age"] = df["age"].astype("int8")
df["survive"] = df["survive"].astype("int8")</code></pre>



<p>¿Cómo cambia?</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">name       414
world      376
age          6
survive      6
dtype: int64</code></pre>



<p>Observamos que las columnas numéricas <em>age </em>y<em> survive</em> <strong>han reducido su peso 87,5%</strong> (de 48 bytes a 8). En las columnas de texto no apreciamos cambios. Lo ideal es asignar el tipado al crear el <em>dataframe</em>, pero depende de lo que estemos usando como origen. Por ejemplo, al cargar un fichero <em>Parquet </em>se carga el esquema definido en el propio fichero. Si se trata de un CSV o XML que cargamos con los métodos de <em>Pandas </em>read_xml y read_csv podemos especificar un tipo común a todas las columnas (<code>dtype="string"</code>) o bien pasarle un diccionario con el tipo de cada columna  (<code>dtype={"col1" : "string"}</code>) :</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python"># Asignamos el tipo String a todas las columnas
df = pd.DataFrame(data, dtype = "string")

# Especificamos el tipo por columna gracias a un diccionario
dictTypes = {"name":"string", "world":"string", "age":"int8", "survive":"int8"}
df = pd.DataFrame(data, dtype = dictTypes )</code></pre>



<p>Y observamos los tipos con <code>dtypes</code>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">name       string
world      string
age          int8
survive      int8
dtype: object</code></pre>



<h2 class="wp-block-heading">Reducir número de columnas que cargamos en un dataframe</h2>



<p>Es la optimización más sencilla y lógica, la forma más fácil de reducir el consumo de recursos es utilizar sólo los datos que necesitamos. A la hora de cargar un <em>dataframe </em>seleccionamos sólo las columnas necesarias. Para este caso vamos a imaginar que cargamos el dataset desde un CSV con <code>READ_CSV</code>, podemos pasarle en el parámetro <code>USECOLS</code> un listado de las columna que deseamos cargar. </p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="python" class="language-python">import pandas as pd

fields = ['name','world']
df = pd.read_csv('dataset.csv', usecols = fields)
print(df)</code></pre>
<p>La entrada <a href="https://n4gash.com/python-optimizar-uso-de-memoria-con-pandas/">Python: Optimizar uso de memoria con Pandas</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4334</post-id>	</item>
		<item>
		<title>Data Engineering Zoomcamp – Semana 3</title>
		<link>https://n4gash.com/data-engineering-zoomcamp-semana-3/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=data-engineering-zoomcamp-semana-3</link>
					<comments>https://n4gash.com/data-engineering-zoomcamp-semana-3/#comments</comments>
		
		<dc:creator><![CDATA[Marcos Torregrosa]]></dc:creator>
		<pubDate>Mon, 06 Feb 2023 14:40:15 +0000</pubDate>
				<category><![CDATA[Cloud Data]]></category>
		<category><![CDATA[Data Engineering]]></category>
		<category><![CDATA[BigQuery]]></category>
		<category><![CDATA[GCP]]></category>
		<category><![CDATA[Google Cloud Platform]]></category>
		<guid isPermaLink="false">https://www.n4gash.com/?p=4209</guid>

					<description><![CDATA[<p>La tercera semana del bootcamp para Data Engineers se ha centrado en Data warehouse y en concreto, el servidio de Google Cloud Platform dedicado a tal fin: BigQuery. Hemos abordado conceptos como particionamiento y clustering para optimizar el consumo de datos. Última actualización 06/02/2023 Prerequisitos Esta semana no hemos dado grandes pasos en la suite tecnológica. Para seguir el temario y realizar los ejercicios continuaremos con la cuenta de Google Cloud Platform y la instancia de BigQuery que creamos a inicio del bootcamp. Google BigQuery Dentro de la suite de servicios cloud que ofrece la GCP (Google Cloud Platform) encontramos…</p>
<p>La entrada <a href="https://n4gash.com/data-engineering-zoomcamp-semana-3/">Data Engineering Zoomcamp &#8211; Semana 3</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>La tercera semana del <a href="https://www.n4gash.com/2023/data-engineering-zoomcamp/" target="_blank" rel="noreferrer noopener">bootcamp para Data Engineers</a> se ha centrado en <em>Data warehouse</em> y en concreto, el servidio de <em><strong>Google Cloud Platform</strong></em> dedicado a tal fin: <strong>BigQuery</strong>. Hemos abordado conceptos como particionamiento y <em>clustering </em>para optimizar el consumo de datos.</p>



<ul class="wp-block-list">
<li><strong>Repo Bootcamp: </strong><a href="https://github.com/DataTalksClub/data-engineering-zoomcamp/tree/main/week_3_data_warehouse" target="_blank" rel="noreferrer noopener">https://github.com/DataTalksClub/data-engineering-zoomcamp/tree/main/week_3_data_warehouse</a></li>



<li><strong>Repo personal con ejercicios:</strong> <a href="https://github.com/hegdehog/data-engineering-zoomcamp-2023/tree/main/week_3_data_warehouse/1_homework" target="_blank" rel="noreferrer noopener">https://github.com/hegdehog/data-engineering-zoomcamp-2023</a></li>
</ul>



<div class="wp-block-group has-palette-color-8-color has-text-color has-background is-layout-constrained wp-block-group-is-layout-constrained" style="background-color:#7bdbb547">
<div class="wp-block-aioseo-table-of-contents"><ul><li><a href="#aioseo-prerequisitos">Prerequisitos</a></li><li><a href="#aioseo-prefect">Google BigQuery</a><ul><li><a href="#aioseo-precios-de-bigquery">Precios de BigQuery</a></li><li><a href="#aioseo-external-tables">External tables</a></li></ul></li><li><a href="#aioseo-optimizacion-de-google-bigquery">Optimización de Google BigQuery</a><ul><li><a href="#aioseo-partitioning">Particionado (Partitioning)</a></li><li><a href="#aioseo-agrupacion-clustering">Agrupación (Clustering)</a></li><li><a href="#aioseo-comparativa-bigquery-partitioning-vs-clustering">BigQuery Partitioning vs Clustering</a></li></ul></li><li><a href="#aioseo-buenas-practicas-sql-para-optimizar-consultas">Buenas prácticas SQL para optimizar consultas</a></li><li><a href="#aioseo-arquitectura-google-bigquery">Arquitectura Google BigQuery</a><ul><li><a href="#aioseo-borg-orquestador-de-contenedores">Borg: orquestador de contenedores</a></li><li><a href="#aioseo-jupyter-red">Jupyter: red</a></li><li><a href="#aioseo-dremel-motor-de-ejecucion">Dremel: motor de ejecución</a></li><li><a href="#aioseo-colossus-almacenamiento-distribuido">Colossus: almacenamiento distribuido</a></li></ul></li><li><a href="#aioseo-almacenamiento">Almacenamiento</a><ul><li><a href="#aioseo-column-oriented-vs-record-oriented-storage">Column-oriented vs record-oriented</a></li><li><a href="#aioseo-datos-anidados-y-repetidos-nested-fields">Campos anidados y repetidos (repeated &amp; nested fields)</a></li></ul></li><li><a href="#aioseo-machine-learning-with-bigquery">BigQuery y Machine Learning</a></li></ul></div>
</div>



<p class="has-text-align-center has-small-font-size"><em>Última actualización 06/02/2023 </em></p>



<h2 class="wp-block-heading" id="aioseo-prerequisitos">Prerequisitos</h2>



<p>Esta semana no hemos dado grandes pasos en la suite tecnológica. Para seguir el temario y realizar los ejercicios continuaremos con la cuenta de <em>Google Cloud Platform</em> y la instancia de <em>BigQuery</em> que creamos a inicio del bootcamp.</p>



<h2 class="wp-block-heading" id="aioseo-prefect">Google BigQuery</h2>



<p>Dentro de la suite de servicios cloud que ofrece la <strong><em>GCP (Google Cloud Platform) </em></strong>encontramos <em>BigQuery</em> como solución de almacenamiento y análisis de datos. Como todo servicio cloud, destaca por la escalabilidad, alta disponibilidad y pago por uso. Entre sus características, destacan:</p>



<ol class="wp-block-list">
<li><strong>Serverless</strong>: al ser un PaaS el usuario no debe preocuparse por el mantenimiento software/hardware, sólo de su configración y explotación.</li>



<li><strong>Escalabilidad:</strong> BigQuery es altamente escalable, lo que significa que puede manejar un aumento en el volumen de datos sin interrupciones.</li>



<li><strong>Almacenamiento en la nube:</strong> BigQuery permite almacenar y procesar grandes volúmenes de datos en la nube de Google, lo que significa que los usuarios no tienen que preocuparse por la infraestructura de hardware.</li>



<li><strong></strong><strong>Análisis en tiempo real</strong>: BigQuery permite realizar consultas en tiempo real sobre grandes cantidades de datos.</li>



<li><strong>Integración con herramientas de Google</strong>: BigQuery se integra con otras herramientas de Google, como Google Data Studio, Google Cloud Storage y Google Cloud Dataproc.</li>



<li><strong>Consultas SQL:</strong> BigQuery permite realizar consultas SQL en tiempo real sobre grandes cantidades de datos. El SQL es compatible con la semántica de transacción de la base de datos (<a href="https://en.wikipedia.org/wiki/ACID">ACID</a>)</li>



<li><strong>Pago por uso (pay-as-you-go): </strong>BigQuery es un servicio de pago por uso, lo que significa que los usuarios solo pagan por el almacenamiento y el análisis de datos que utilizan.</li>



<li><strong>Seguridad y cumplimiento normativo: </strong>BigQuery ofrece una amplia gama de características de seguridad y cumplimiento normativo para ayudar a proteger los datos de los usuarios.</li>



<li><strong>Compatibilidad con diferentes formatos de datos</strong>: BigQuery es compatible con diferentes formatos de datos, como CSV, JSON y Avro, y permite a los usuarios importar y exportar datos de y hacia otras plataformas.</li>
</ol>



<h3 class="has-medium-font-size wp-block-heading" id="aioseo-precios-de-bigquery" style="font-style:normal;font-weight:100">Precios de BigQuery</h3>



<p>La facturación de BigQuery consta de dos partes: el <strong>precio del análisis </strong>y el del <strong>almacenamiento.</strong> Más información en el <a href="https://cloud.google.com/bigquery/pricing?hl=es#bigquery-pricing" target="_blank" rel="noreferrer noopener">pricing de GCP BigQuery</a>.</p>



<ul class="wp-block-list">
<li>El&nbsp;<strong>precio de análisis</strong>: coste de procesar consultas (SQL, funciones definidas por el usuario UDF, scripts DML y DDL). Sin entrar en mucho detalle, hay dos modelos de precios disponibles para el análisis: <strong>bajo demanda </strong>(5$ por TB) o <strong>tarifa plana</strong> (el precio varía en función del tipo ranura, mensual o anual)</li>



<li>El&nbsp;<strong>precio de almacenamiento</strong>&nbsp;coste de almacenar físicamente los datos. El almacenamiento es más barato y encontramos dos tipos (similar a los <em>tiers</em> del los <em>Storage Account </em>de <em>Azure</em>): 
<ul class="wp-block-list">
<li>Activo: 0,020$/GB (los primeros 10 GB/MES son gratis). Automáticamente para las tablas o particiones con cambios en los últimos 90 días.</li>



<li>Largo plazo: 50% de descuento: 0,010$/GB para aquellas tablas y particiones que no se hayan tocado en los últimos 90 días.</li>
</ul>
</li>
</ul>



<h3 class="has-medium-font-size wp-block-heading" id="aioseo-external-tables" style="font-style:normal;font-weight:100">External tables</h3>



<p>Al igual que en otras soluciones cloud como en <em>Azure Synapse,</em> desde <em>Google BigQuery</em> podemos crear <strong>tablas externas</strong> (<em>external tables)</em> para consultar conjuntos de datos como si fueran una tabla SQL que no estén almacenados en BigQuery (archivos en un<em> Cloud Storage,</em> otra base de datos fuera de GCP, etc). Este tipo de objeto almacena en BQ únicamente el esquema, que infiere del fichero a la hora de crear el objeto, pero no los datos. Las tablas externas tienen las mismas características que una tabla estándar en BigQuery, con sus propiedades, administración de acceso, metadatos, etc. La única diferencia es que son una cáscara, los datos están en otra ubicación.</p>



<p>Por ejemplo, en lugar de ingestar un CSV en una tabla de la BBDD BigQuery, vamos a crear una<em> external table </em>para acceder directamente a los datos sin persistir:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">CREATE OR REPLACE EXTERNAL TABLE 'my_database.yellow_tripdata_external_table'
OPTIONS (
  format = 'CSV',
  uris = ['gs://data/trip data/yellow_tripdata_2020-01.csv']
);</code></pre>



<h2 class="wp-block-heading" id="aioseo-optimizacion-de-google-bigquery">Optimización de Google BigQuery</h2>



<p>A diferencia de una base de datos relacional, BigQuery no soporta índices para agilizar las consultas SQL. En su lugar, ofrece dos alternativas: <strong><a href="https://cloud.google.com/bigquery/docs/partitioned-tables" target="_blank" rel="noreferrer noopener">particionado</a></strong> (<em>partitioning</em>) y <strong><a href="https://cloud.google.com/bigquery/docs/clustered-tables?hl=es-419" target="_blank" rel="noreferrer noopener">agrupación</a></strong><em> (clustering)</em>. Estas opciones no son recomendables cuando nuestro volumen de datos es &lt; 1GB.</p>



<h3 class="has-medium-font-size wp-block-heading" id="aioseo-partitioning" style="font-style:normal;font-weight:100">Particionado (Partitioning)</h3>



<p>Una tabla particionada es una tabla dividida en segmentos <em>aka </em><strong>particiones</strong> basados en los valores de una columna. Dividir una tabla en porciones agiliza enormemente las consultas ya que los datos que necesita recuperar son mucho más reducidos que si tuviera que leer toda la tabla. <strong>BigQuery </strong>ofrece tres tipos de particiones:</p>



<ul class="wp-block-list">
<li><strong>Partición por rangos de números enteros</strong>: se crean particiones en base al valor numérico de una columna de tipo <code>INTEGER</code>. Por ejemplo, por el código de país.</li>



<li><strong>Partición de columnas por unidad de tiempo</strong>: la partición más habitual, se particiona la tabla por una columna&nbsp;de tipo <code>DATE</code>,&nbsp;<code>TIMESTAMP&nbsp;</code>o&nbsp;<code>DATETIME</code>.</li>



<li><strong>Partición por tiempo de ingesta</strong>: <em>BigQuery </em>asigna de forma automática filas a las particiones en función del momento en que <em>BigQuery </em>transfiere los datos. Puedes elegir el nivel de detalle por hora, día, mes o año para las particiones. Tiene un límite de 4k particiones. Se añade la columna <code>_PARTITIONTIME</code> y en cada tupla se asigna el valor del momento en el que se ha almacenado el dato.</li>
</ul>



<p>En el siguiente ejemplo creamos una nueva tabla a partir de una consulta <code>SELECT</code> y añadimos el particionado por la columna <code>tpep_pickup_datetime</code></p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">CREATE OR REPLACE TABLE my_database.yellow_tripdata_partitoned
PARTITION BY
  DATE(tpep_pickup_datetime) AS
SELECT * FROM my_database.yellow_tripdata;</code></pre>



<p>Cuando lanzas una query en BigQuery sobre una tabla particionada no es necesario que lo especifiques, internamente lo identifica y realiza la consulta sobre la partición que cumpla el criterio del predicado (o filtro). Diferencia importante respecto a SQL Server u Oracle, donde sí se debe especificar la partición en la sentencia <code>SELECT</code>.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">SELECT DISTINCT(VendorID)
FROM my_database.yellow_tripdata_non_partitoned
WHERE DATE(tpep_pickup_datetime) BETWEEN '2021-01-01' AND '2021-02-28';</code></pre>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-13.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="870" height="525" data-attachment-id="4251" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-3/image-13-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-13.png?fit=870%2C525&amp;ssl=1" data-orig-size="870,525" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-13" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-13.png?fit=870%2C525&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-13.png?resize=870%2C525&#038;ssl=1" alt="Detalle tabla particionada en BigQuery en data Engineering" class="wp-image-4251" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-13.png?w=870&amp;ssl=1 870w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-13.png?resize=300%2C181&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-13.png?resize=768%2C463&amp;ssl=1 768w" sizes="auto, (max-width: 870px) 100vw, 870px" /></a><figcaption class="wp-element-caption">Detalle tabla particionada en BigQuery</figcaption></figure>



<h3 class="has-medium-font-size wp-block-heading" id="aioseo-agrupacion-clustering" style="font-style:normal;font-weight:100">Agrupación (Clustering)</h3>



<p>El <strong>clustering </strong>reordena los datos de la tabla en base a una o más columnas (hasta 4). Características de la agrupación de columnas en BigQuery:</p>



<ul class="wp-block-list">
<li>El orden de las columnas agrupadas es relevante para determinar la prioridad de las columnas</li>



<li>Mejora el rendimiento en consultas que utilicen predicados o funciones de agregación</li>



<li>Funciona mucho mejor con columnas con una alta cardinalidad (email, categorías, nombres)</li>



<li>Se pueden hacer agrupaciones en columnas de tipo:
<ul class="wp-block-list">
<li><code>DATE</code></li>



<li><code>BOOL</code></li>



<li><code>GEOGRAPHY</code></li>



<li><code>INT64</code></li>



<li><code>NUMERIC</code></li>



<li><code>BIGNUMERIC</code></li>



<li><code>STRING</code></li>



<li><code>TIMESTAMP</code></li>



<li><code>DATETIME</code></li>
</ul>
</li>



<li>Límite de como máximo 4 columnas agrupadas (<em>clusters</em>) por tabla.</li>
</ul>



<p>Podemos crear clusters al mismo tiempo que las particiones. Tomando como ejemplo la query anterior, vamos a añadir una columna grupada o cluster por el campo <code>VendorID</code>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">CREATE OR REPLACE TABLE my_database.yellow_tripdata_partitoned_clustered
PARTITION BY DATE(tpep_pickup_datetime)
CLUSTER BY VendorIDKey AS
SELECT * FROM my_database.external_yellow_tripdata;</code></pre>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-14.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="934" height="621" data-attachment-id="4252" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-3/image-14-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-14.png?fit=934%2C621&amp;ssl=1" data-orig-size="934,621" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-14" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-14.png?fit=934%2C621&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-14.png?resize=934%2C621&#038;ssl=1" alt="Tabla particionada y agrupada por columna en BigQuery en data Engineering" class="wp-image-4252" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-14.png?w=934&amp;ssl=1 934w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-14.png?resize=300%2C199&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-14.png?resize=768%2C511&amp;ssl=1 768w" sizes="auto, (max-width: 934px) 100vw, 934px" /></a><figcaption class="wp-element-caption">Tabla particionada y agrupada por columna en BigQuery</figcaption></figure>



<h3 class="has-medium-font-size wp-block-heading" id="aioseo-comparativa-bigquery-partitioning-vs-clustering" style="font-style:normal;font-weight:100">BigQuery Partitioning vs Clustering</h3>



<p>Podemos utilizar ambas opciones de optimización juntas y aprovechar las sinergias que generan, o en función de la necesidad elegir una estrategia u otra. Normalmente usaremos particionado cuando nuestras consultas filtran o agregan datos sobre un único campo con baja cardinalidad (año, mes, ciudad, departamento&#8230;), mientras que si es por varios podemos <em>particionar </em>por el que tenga menor cardinalidad (fecha, por ejemplo) y aplicar <em>clustering </em>por el resto (hasta un máximo de 4 columnas). </p>



<figure class="wp-block-table aligncenter is-style-stripes, tab-head is-style-stripes"><table class="has-fixed-layout"><thead><tr><th class="has-text-align-left" data-align="left">Partitioning</th><th class="has-text-align-left" data-align="left">Clustering</th></tr></thead><tbody><tr><td class="has-text-align-left" data-align="left">El coste de la query es conocido. BigQuery puede estimar la cantidad de datos que va a recuperar antes de ejecutar la query.</td><td class="has-text-align-left" data-align="left">Se desconoce el coste de la query ya que no puede estimar la cantidad de datos.</td></tr><tr><td class="has-text-align-left" data-align="left">Granularidad baja. Sólo se puede aplicar partición por una columna.</td><td class="has-text-align-left" data-align="left">Granularidad alta. Se pueden utilizar varias columnas para reordenar la tabla (hasta un máximo de 4)</td></tr><tr><td class="has-text-align-left" data-align="left">Enfocada para queries que filtran o agregan datos por una sóla columna.</td><td class="has-text-align-left" data-align="left">Enfocada para queries que filtran o agregan por varias columnas.</td></tr><tr><td class="has-text-align-left" data-align="left">Límite de 4K particiones de una columna, lo que implica que sólo se puede utilizar con campos con baja cardinalidad (o hasta 4K).</td><td class="has-text-align-left" data-align="left">No hay límite de clusters, por lo que soporta columnas con una alta cardinalidad.</td></tr></tbody></table><figcaption class="wp-element-caption">Comparativa entre Partitioning y Clustering en BigQuery</figcaption></figure>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-11.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="467" data-attachment-id="4218" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-3/image-11-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-11.png?fit=1712%2C780&amp;ssl=1" data-orig-size="1712,780" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-11" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-11.png?fit=1024%2C467&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-11-1024x467.png?resize=1024%2C467&#038;ssl=1" alt="Optimización de tabla BigQuery con partitioning + clustering en data Engineering" class="wp-image-4218" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-11.png?resize=1024%2C467&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-11.png?resize=300%2C137&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-11.png?resize=768%2C350&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-11.png?resize=1536%2C700&amp;ssl=1 1536w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-11.png?w=1712&amp;ssl=1 1712w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Optimización de tabla BigQuery con partitioning + clustering</figcaption></figure>



<p>El propio motor de <em>BigQuery</em> es el encargado de actualizar automáticamente los <em>clusters </em>cuando se insertan nuevos datos en la tabla de forma transparente para el usuario. En el caso de tablas particionadas, los <em>clusters </em>se actualizan de forma independiente en cada partición.</p>



<h2 class="wp-block-heading" id="aioseo-buenas-practicas-sql-para-optimizar-consultas">Buenas prácticas SQL para optimizar consultas</h2>



<p>La mayoría no sólo aplican a&nbsp;<em>Google BigQuery</em>, se trata de recomendaciones para queries ejecutadas sobre cualquier motor de BBDD:</p>



<ul class="wp-block-list">
<li>Evitar usar&nbsp;<code>SELECT *</code>, lo ideal es recuperar sólo las columnas que necesitemos o vayamos a utilizar.</li>



<li>Evalúa el coste de ejecutar la query antes de lanzarla. Esto es especialmente útil en entornos cloud donde la facturación seleccionada sea por ejecución (se paga por cada ejecución), que normalmente es más caro que si se selecciona una capacidad o paquete.</li>



<li>Aplicar la optimización por&nbsp;<em>partitioning&nbsp;</em>y/o&nbsp;<em>clustering</em></li>



<li>En los casos de real-time debemos prestar atención y ser cuidadosos con los&nbsp;<code>INSERT</code> de datos (<a href="https://cloud.google.com/bigquery/docs/streaming-data-into-bigquery?hl=es-419" target="_blank" rel="noreferrer noopener">insertAll</a>)</li>



<li>Crear vistas materializadas como pasos intermedios cuando la query debe manejar un gran volumen de datos. Debemos tener en cuenta que&nbsp;<em>BigQuery&nbsp;</em>también cachea los resultados de las columnas.</li>



<li>Aplicar filtros por columnas de partición o agrupación (<em>clusters</em>)</li>



<li>Desnormalizar los datos bajando las formas normales a lo mínimo, en otras palabras, destruir la integridad referencial manteniendo todos los datos en una única tabla para evitar joins entre varias. Se recomienda usar campos anidados o repetidos (<strong>nested fields</strong>) con <code>STRUCT </code>o <code>ARRAY</code>. Aunque tiene ciertas desventajas (mayor almacenamiento por repetir datos y se pierde la integridad de los datos), es la forma más óptima de explotar grandes volúmenes de datos.</li>



<li>Intenta utilizar funciones de agregación aproximada&nbsp;<em><strong><a href="https://cloud.google.com/bigquery/docs/reference/standard-sql/hll_functions?hl=es-419" target="_blank" rel="noreferrer noopener">HyperLogLog++</a></strong>&nbsp;o&nbsp;<strong>HLL++</strong></em>. Necesita menos memoria que las funciones de agregación exacta, como&nbsp;<code>COUNT(DISTINCT)</code>, pero generan incertidumbre estadística. Son muy útiles para grandes volúmenes de datos en los que el uso de la memoria lineal es poco práctico teniendo en cuenta que el dato que nos devuelve es una aproximación estadística, no el valor exacto.</li>



<li>Evitar usar funciones propias SQL o JavaScript&nbsp;<a href="https://cloud.google.com/bigquery/docs/reference/standard-sql/user-defined-functions?hl=es-419" target="_blank" rel="noreferrer noopener">UDF</a></li>



<li>Cuando cruces varias tablas organiza el&nbsp;<code>JOIN</code> ubicando la más voluminosa primero. Será la que <em>BigQuery </em>use en primer lugar para distribuirla por los nodos y las siguientes tablas las irá repartiendo por cada uno. Además, intenta reducir el tamaño de las subqueries o vistas materializadas antes de hacer los cruces.</li>
</ul>



<h2 class="wp-block-heading" id="aioseo-arquitectura-google-bigquery">Arquitectura Google BigQuery</h2>



<p>La arquitectura de <em>BigQuery </em>desvincula el almacenamiento de la computación (motor de análisis), lo que permite escalar cada recurso de forma independiente. Esta flexibilidad favorece un control de costes mucho más granularizado ¿Qué piezas encontramos dentro de BigQuery? <strong>Dremel, Colossus, Jupiter</strong> y <strong>Borg</strong>:</p>



<figure class="wp-block-image alignfull size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-12.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="574" data-attachment-id="4244" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-3/image-12-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-12.png?fit=1412%2C792&amp;ssl=1" data-orig-size="1412,792" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-12" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-12.png?fit=1024%2C574&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-12-1024x574.png?resize=1024%2C574&#038;ssl=1" alt="Arquitectura Google BigQuery" class="wp-image-4244" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-12.png?resize=1024%2C574&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-12.png?resize=300%2C168&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-12.png?resize=768%2C431&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-12.png?w=1412&amp;ssl=1 1412w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Arquitectura Google BigQuery</figcaption></figure>



<h3 class="has-medium-font-size wp-block-heading" id="aioseo-borg-orquestador-de-contenedores" style="font-style:normal;font-weight:100">Borg: orquestador de contenedores</h3>



<p>Orquestador de contenedores propio de Google que se encarga de proveer del hardware necesario para que operen los <em>slots </em>y <em>mixers </em>del motor <em>Dremel</em>.</p>



<h3 class="has-medium-font-size wp-block-heading" id="aioseo-jupyter-red" style="font-style:normal;font-weight:100">Jupyter: red</h3>



<p>Como la estructura de BigQuery es desacoplada (separa físicamente el almacenamiento del motor de cómputo) necesita un artefacto que conecte ambas entidades: <strong>Jupyter</strong>. Ofrece suficiente ancho de banda como para permitir la comunicación entre 100K máquinas a una velocidad ultra rápida de 10Gbs/s.</p>



<h3 class="has-medium-font-size wp-block-heading" id="aioseo-dremel-motor-de-ejecucion" style="font-style:normal;font-weight:100">Dremel: motor de ejecución</h3>



<p>Se trata del motor de consultas de alta velocidad de BigQuery que usa Google en su propio buscador. Orquesta las consultas segmentándolas en pequeñas porciones que se distribuyen por nodos y cuando finalizan se agrupan para devolver el resultado; la definición de procesamiento distribuido. <strong>Dremel </strong>convierte una consulta SQL en un árbol de ejecución donde encontramos <strong>slots </strong>y <strong>mixers</strong>, todos ejecutados sobre <em>Borg</em> (ver más adelante). El propio motor asigna de forma dinámica slots a las consultas que van entrando:</p>



<ul class="wp-block-list">
<li><strong>Slots</strong>: serían las hojas del árbol y se encargan de la parte más pesada: leer datos en Colossus y realizar operaciones de cómputo.</li>



<li><strong>Mixers</strong>: las ramas. Se encargan de las operaciones de agregación</li>
</ul>



<h3 class="has-medium-font-size wp-block-heading" id="aioseo-colossus-almacenamiento-distribuido" style="font-style:normal;font-weight:100">Colossus: almacenamiento distribuido</h3>



<p>Sistema de almacenamiento distribuido de última generación de Google. Gestiona las réplicas, recuperación (cuando los discos fallan) y administración distribuida (lo que mitiga el impacto en caso de caída). <strong>Colossus </strong>utiliza el formato columnar y de compresión <em>ColumnIO</em> capaz de manejar fácilmente petabytes de datos.</p>



<h2 class="wp-block-heading" id="aioseo-almacenamiento">Almacenamiento</h2>



<h3 class="has-medium-font-size wp-block-heading" id="aioseo-column-oriented-vs-record-oriented-storage" style="font-style:normal;font-weight:100">Column-oriented vs record-oriented </h3>



<p>Tradicionalmente el almacenamiento de datos tabular está orientado a filas (<em>record-oriented</em> o <em>row-oriented</em>). De esta forma, los datos se almacenan y consultan fila por fila y después se accede a cada una de las columnas. <em>BigQuery </em>en su lugar, utiliza almacenamiento orientado a columnas (<em>column-oriented)</em>. Este tipo de almacenamiento es ideal para grandes volúmenes de datos ya que permite reducir la consulta sólo a las columnas deseadas, sin necesidad de recuperar todos los datos de una fila para luego quedarnos con lo que nos interesa.</p>



<p>El motor de ejecución <em>Dremel</em> guarda los datos en <em>Colossus </em>en formato columnar, es decir, divide un registro o fila por los valores de sus columnas y almacena cada valor en un volumen distinto. En las base de datos tradiciones normalmente se almacena el registro completo en el mismo volumen. Cuando se lanza una consulta sobre BQ, <em>Dremel </em>crea un árbol de ejecución distribuyendo la query en porciones a través de los <em>mixers </em>que a su vez se distribuyen en paquetes más pequeños sobre la malla de <em>slots</em> que acceden a la capa de almacenamiento <em>Colossus</em> (la siguiente sección incluye un diagrama al final donde se visualiza esta explicación). </p>



<figure class="wp-block-image alignfull size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-18.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="126" data-attachment-id="4268" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-3/image-18-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-18.png?fit=1403%2C173&amp;ssl=1" data-orig-size="1403,173" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-18" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-18.png?fit=1024%2C126&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-18-1024x126.png?resize=1024%2C126&#038;ssl=1" alt="Almacenamiento en orientado a filas vs orientado a columnas " class="wp-image-4268" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-18.png?resize=1024%2C126&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-18.png?resize=300%2C37&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-18.png?resize=768%2C95&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-18.png?w=1403&amp;ssl=1 1403w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Almacenamiento en orientado a filas vs orientado a columnas </figcaption></figure>



<h3 class="has-medium-font-size wp-block-heading" id="aioseo-datos-anidados-y-repetidos-nested-fields" style="font-style:normal;font-weight:100">Campos anidados y repetidos (repeated &amp; nested fields)</h3>



<p>Una de las buenas prácticas para modelar una base de datos analítica es <strong>desnormalizar </strong>con el fin de reducir los cruces entre tablas (hechos + dimensiones). Se almacenan todos los datos en una única tabla, de forma que la información (como la ciudad) se repite y almacena en la misma tabla, no es necesario cruzar con una tabla de dimensión para recuperar los valores. <em>BigQuery </em>soporta el uso de estructuras repetidas o anidadas mediante las operadores <code>STRUCT</code> y <code>ARRAY</code>. Esto nos permite representar la información de forma natural además de agilizar las consultas. Podemos combinar ambas estrategias para el almacenamiento de datos</p>



<div class="wp-block-group is-layout-constrained wp-block-group-is-layout-constrained">
<ul class="wp-block-list">
<li><strong><code>STRUCT</code></strong>: subconjunto de datos (columna) formado por varios campos ordenados con nombre y tipo. Se usa notación de puntos para consultar los campos anidados de una columna, por ejemplo: <strong><em>cliente.nombre</em></strong> donde <em>nombre </em>es el campo y <em>cliente </em>la columna.</li>
</ul>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-20.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="731" height="155" data-attachment-id="4274" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-3/image-20-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-20.png?fit=731%2C155&amp;ssl=1" data-orig-size="731,155" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-20" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-20.png?fit=731%2C155&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-20.png?resize=731%2C155&#038;ssl=1" alt="BigQuery Nested Field STRUCT" class="wp-image-4274" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-20.png?w=731&amp;ssl=1 731w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-20.png?resize=300%2C64&amp;ssl=1 300w" sizes="auto, (max-width: 731px) 100vw, 731px" /></a><figcaption class="wp-element-caption">Nested Field STRUCT</figcaption></figure>
</div>



<div class="wp-block-group is-layout-constrained wp-block-group-is-layout-constrained">
<ul class="wp-block-list">
<li><strong><code>ARRAY</code></strong>: lista ordenada con 0 o más elementos del mismo tipo. Por ejemplo, <strong>producto </strong>es un <code>ARRAY </code>de <code>STRUCTs</code>. Podemos usar <code>UNNEST()</code> para aplanar los elementos de la lista por cada fila o usar <code>OFFSET/ORDINAL</code> para acceder a cada elemento de forma individual (más adelante hay ejemplos de estas consultas).</li>
</ul>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-22.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="755" height="239" data-attachment-id="4278" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-3/image-22-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-22.png?fit=755%2C239&amp;ssl=1" data-orig-size="755,239" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-22" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-22.png?fit=755%2C239&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-22.png?resize=755%2C239&#038;ssl=1" alt="" class="wp-image-4278" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-22.png?w=755&amp;ssl=1 755w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-22.png?resize=300%2C95&amp;ssl=1 300w" sizes="auto, (max-width: 755px) 100vw, 755px" /></a><figcaption class="wp-element-caption">Repeated field ARRAY</figcaption></figure>
</div>



<p>El motor <em>Dremel</em> debe su velocidad, entre otras cosas, al almacenamiento columnar de los <a href="https://cloud.google.com/bigquery/docs/best-practices-performance-nested?hl=es-419" target="_blank" rel="noreferrer noopener">campos anidados</a> (<em>nested fields)</em>. Todos los valores de un campo  anidado (por ejemplo A.B.C) se almacenan de forma contigua, por lo que permite que se recupere sólamente A.B.C sin necesidad de leer A.E, A.B.D, etc.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-19.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="983" height="637" data-attachment-id="4271" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-3/image-19-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-19.png?fit=983%2C637&amp;ssl=1" data-orig-size="983,637" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-19" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-19.png?fit=983%2C637&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-19.png?resize=983%2C637&#038;ssl=1" alt="Dremel almacenamiento columnar-oriented " class="wp-image-4271" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-19.png?w=983&amp;ssl=1 983w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-19.png?resize=300%2C194&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-19.png?resize=768%2C498&amp;ssl=1 768w" sizes="auto, (max-width: 983px) 100vw, 983px" /></a><figcaption class="wp-element-caption">Dremel almacenamiento columnar-oriented </figcaption></figure>



<p>En la siguiente query vamos a crear la tabla my_database.tabla_array_struct con cuatro elementos: dos columnas estándar de tipo string y date, y dos campos anidados: cliente de tipo <code>ARRAY</code> y producto que sería un <code>ARRAY</code> o colección de <code>STRUCT</code>.</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">CREATE TABLE `my_dataset.tabla_array_struct`
(
  ID_PEDIDO int64,
  FECHA date,
  CLIENTE  ARRAY&lt;string&gt;,  
  PRODUCTOS ARRAY&lt;STRUCT&lt;SKU int64, CANTIDAD int64, PRECIO numeric&gt;&gt;,
);</code></pre>



<p>Poblamos la tabla de la siguiente forma:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">INSERT INTO `my_dataset.tabla_array_struct` (ID_PEDIDO, FECHA, CLIENTE, PRODUCTOS)
VALUES (
    1,
    CAST('2021-01-01' AS date), 
    ['231', 'Marcos Torregrosa', 'Madrid, Spain'],
    [
      STRUCT(6633 as SKU, 2 as CANTIDAD, cast(3.99 as numeric) as PRECIO),
      STRUCT(1667, 1, 1.99),
      STRUCT(8871, 1, 16.99)
    ] 
    );</code></pre>



<p>Si observamos el esquema vemos que BQ trata estas columnas como <code>RECORD</code> y modo <code>REPEATED</code>.</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/BIGQUERY_nested_fields_01.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="364" data-attachment-id="4296" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-3/bigquery_nested_fields_01/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/BIGQUERY_nested_fields_01.png?fit=1024%2C364&amp;ssl=1" data-orig-size="1024,364" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="BIGQUERY_nested_fields_01" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/BIGQUERY_nested_fields_01.png?fit=1024%2C364&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/BIGQUERY_nested_fields_01.png?resize=1024%2C364&#038;ssl=1" alt="Esquema de tabla BigQuery con ARRAY y STRUCT" class="wp-image-4296" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/BIGQUERY_nested_fields_01.png?w=1024&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/BIGQUERY_nested_fields_01.png?resize=300%2C107&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/BIGQUERY_nested_fields_01.png?resize=768%2C273&amp;ssl=1 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Esquema de tabla BigQuery con ARRAY y STRUCT</figcaption></figure>



<p>Echando un ojo a los datos:</p>



<figure class="wp-block-image aligncenter size-full"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/BIGQUERY_nested_fields_02.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="437" data-attachment-id="4298" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-3/bigquery_nested_fields_02/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/BIGQUERY_nested_fields_02.png?fit=1024%2C437&amp;ssl=1" data-orig-size="1024,437" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="BIGQUERY_nested_fields_02" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/BIGQUERY_nested_fields_02.png?fit=1024%2C437&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/BIGQUERY_nested_fields_02.png?resize=1024%2C437&#038;ssl=1" alt="Ejemplo de conjunto de datos en BigQuery con ARRAY y STRUCT" class="wp-image-4298" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/BIGQUERY_nested_fields_02.png?w=1024&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/BIGQUERY_nested_fields_02.png?resize=300%2C128&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/BIGQUERY_nested_fields_02.png?resize=768%2C328&amp;ssl=1 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Ejemplo de conjunto de datos en BigQuery con ARRAY y STRUCT</figcaption></figure>



<p>Con <code>OFFSET </code>u <code>ORDINAL </code>podemos acceder directamente por el índice a cualquier elemento de una columna de tipo <code>ARRAY</code>:</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">SELECT cliente, cliente[OFFSET(1)] as offset_1, cliente[ORDINAL(1)] as ordinal_1
FROM my_dataset.tabla_array_struct</code></pre>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-25.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="303" data-attachment-id="4302" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-3/image-25-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-25.png?fit=1208%2C358&amp;ssl=1" data-orig-size="1208,358" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-25" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-25.png?fit=1024%2C303&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-25-1024x303.png?resize=1024%2C303&#038;ssl=1" alt="" class="wp-image-4302" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-25.png?resize=1024%2C303&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-25.png?resize=300%2C89&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-25.png?resize=768%2C228&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-25.png?w=1208&amp;ssl=1 1208w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Operador OFFSET con nested fields en BigQuery</figcaption></figure>



<p>Con el operador <code>UNNEST</code> podemos aplanar un <code>ARRAY</code> mostrar una tabla con una fila para cada elemento en ese array.&nbsp;</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">select * from my_dataset.tabla_array_struct, UNNEST(cliente) as clientes
where cliente[OFFSET(1)] like "Marcos%"</code></pre>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-26.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="425" data-attachment-id="4305" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-3/image-26-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-26.png?fit=1185%2C492&amp;ssl=1" data-orig-size="1185,492" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-26" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-26.png?fit=1024%2C425&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-26-1024x425.png?resize=1024%2C425&#038;ssl=1" alt="Operador UNNEST con nested fields en BigQuery" class="wp-image-4305" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-26.png?resize=1024%2C425&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-26.png?resize=300%2C125&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-26.png?resize=768%2C319&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-26.png?w=1185&amp;ssl=1 1185w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Operador UNNEST con nested fields en BigQuery</figcaption></figure>



<h2 class="wp-block-heading" id="aioseo-machine-learning-with-bigquery">BigQuery y Machine Learning</h2>



<p>Desde <em>BigQuery </em>podemos ejecutar modelos de <strong>machine learning</strong> de forma sencilla y ágil usando simplemente SQL estándar sin necesidad de plataformas específicas, movimiento de datos o conocimientos de programación (python, scala, etc). Los algoritmos de ML disponibles nativamente dentro de BigQuery pueden consultarse en la <a href="https://cloud.google.com/bigquery-ml/docs/introduction?hl=es-419" target="_blank" rel="noreferrer noopener">documentación </a>oficial.</p>



<p>Por ejemplo, para crear un modelo de <strong>regresión lineal</strong> con el objetivo de predecir la propina (<em>tip_amount</em>) de una carrera en taxi usamos la sentencia <code>CREATE MODEL</code> (recomendable ver la <a href="https://cloud.google.com/bigquery-ml/docs/reference/standard-sql/bigqueryml-syntax-create" target="_blank" rel="noreferrer noopener">documentación </a>con todas las opciones):</p>



<pre class="wp-block-code has-palette-color-6-background-color has-background"><code lang="sql" class="language-sql">CREATE OR REPLACE MODEL `my_database.nytaxi.tip_model`
OPTIONS (
  model_type='linear_reg',
  input_label_cols=['tip_amount'],
  DATA_SPLIT_METHOD='AUTO_SPLIT'
) AS
SELECT
  *
FROM
  `my_database.nytaxi.yellow_tripdata`
WHERE
  tip_amount IS NOT NULL;</code></pre>



<ul class="wp-block-list">
<li><code>CREATE OR REPLACE MODEL</code>&nbsp;es la sentencia para crear nuestro modelo</li>



<li>Dentro de las <code>OPTIONS()</code>&nbsp;vamos a indicar los parámetros y configuración del modelo:
<ul class="wp-block-list">
<li><code>MODELE_TYPE='linear_reg'</code> en nuestro ejemplo vamos a crear un modelo de regresión lineal. Podríamos usar cualquiera de los disponibles en BQ (como <code>KMEANS </code>para crear clústers de datos o <code>RANDOM_FOREST_CLASSIFIER</code> para crear un modelo de clasificación)</li>



<li><code>INPUT_LABEL_COLS=['tip_amount']</code>&nbsp;Array de columnas separadas por comas que vamos a utilizar para entrenar y usar el modelo. </li>



<li><code>DATA_SPLIT_METHOD='AUTO_SPLIT'</code>&nbsp;especificamos que queremos dividir automáticamente el conjunto de datos en dos partes, una de entrenamiento y otra de comprobación (training/test).</li>
</ul>
</li>



<li>El <code>SELECT</code>&nbsp;especifica la fuente de datos, así como el predicado si lo hubiera (filtro).</li>
</ul>



<p>El modelo que hemos creado se almacena dentro del conjunto de datos en <em>BigQuery </em>como un nuevo objeto:</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-17.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="698" data-attachment-id="4264" data-permalink="https://n4gash.com/data-engineering-zoomcamp-semana-3/image-17-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-17.png?fit=1250%2C852&amp;ssl=1" data-orig-size="1250,852" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-17" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-17.png?fit=1024%2C698&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-17-1024x698.png?resize=1024%2C698&#038;ssl=1" alt="Model de ML en BigQuery" class="wp-image-4264" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-17.png?resize=1024%2C698&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-17.png?resize=300%2C204&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-17.png?resize=768%2C523&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-17.png?w=1250&amp;ssl=1 1250w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Model de ML en BigQuery</figcaption></figure>



<p>BQ nos ofrece una serie de sentencias para analizar y explotar el modelo. Más información en la <a href="https://cloud.google.com/bigquery-ml/docs/reference" target="_blank" rel="noreferrer noopener">documentación </a>oficial. </p>



<ul class="wp-block-list">
<li><code>ML.FEATURE_INFO</code>: muestra estadísticas de cada columna del conjunto de datos (valores mínimo y máximo, medias, etc). Similar a ejecutar el comando <code>describe()</code> de en Pandas (python).</li>



<li><code>ML.EVALUATE</code>: muestra las métricas de un modelo, ideal para comprobar con un nuevo conjunto de datos cómo respondería el modelo. las métricas que ofrece son las mismas que podemos consultar viendo el detalle del <em>model </em>creado desde el GUI de GCP.</li>



<li><code>ML.PREDICT</code>: nos permite ejecutar el modelo sobre un conjunto de datos y generar las predicciones para las que se haya configurado.</li>



<li><code>ML.EXPLAIN_PREDICT</code>: añade información a la sentencia anterior sobre cuáles de las columnas o features son las que más colaboran para el cálculo de la predicción.</li>
</ul>
<p>La entrada <a href="https://n4gash.com/data-engineering-zoomcamp-semana-3/">Data Engineering Zoomcamp &#8211; Semana 3</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://n4gash.com/data-engineering-zoomcamp-semana-3/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4209</post-id>	</item>
		<item>
		<title>Data literacy o alfabetización de los datos</title>
		<link>https://n4gash.com/data-literacy-o-alfabetizacion-de-los-datos/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=data-literacy-o-alfabetizacion-de-los-datos</link>
		
		<dc:creator><![CDATA[Marcos Torregrosa]]></dc:creator>
		<pubDate>Mon, 06 Feb 2023 08:48:17 +0000</pubDate>
				<category><![CDATA[Data Architecture]]></category>
		<category><![CDATA[Data Engineering]]></category>
		<category><![CDATA[Data literacy]]></category>
		<category><![CDATA[Data-Driven]]></category>
		<guid isPermaLink="false">https://www.n4gash.com/?p=4202</guid>

					<description><![CDATA[<p>A consecuencia de un mundo empresarial cada vez más impulsado por los datos en los últimos años se viene hablando de la data literacy o alfabetización de datos. Se trata de una metodología de trabajo que busca desarrollar la capacidad de todas las personas de la organización en interpretar, analizar y utilizar los datos para tomar decisiones informadas aplicando habilidades de pensamiento crítico. No sólo busca extender el conocimiento, si no cuestionarse el significado de los datos, evaluar su relevancia, fiabilidad y validez antes de utilizarlos para tomar decisiones. Que una organización se defina data-driven está estrechamente relacionado con el…</p>
<p>La entrada <a href="https://n4gash.com/data-literacy-o-alfabetizacion-de-los-datos/">Data literacy o alfabetización de los datos</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>A consecuencia de un mundo empresarial cada vez más impulsado por los datos en los últimos años se viene hablando de la <strong>data literacy</strong> o <strong>alfabetización de datos</strong>. Se trata de una metodología de trabajo que busca <strong>desarrollar la capacidad de todas las personas de la organización en interpretar, analizar y utilizar los datos para tomar decisiones informadas aplicando habilidades de pensamiento crítico</strong>. No sólo busca extender el conocimiento, si no cuestionarse el significado de los datos, evaluar su relevancia, fiabilidad y validez antes de utilizarlos para tomar decisiones.</p>



<p>Que una organización se defina <strong><em>data-driven</em></strong> está estrechamente relacionado con el nivel de <em>data literacy </em>implementado en toda su estructura. Si las decisiones empresariales deben estar basadas en los datos, se hace imprescindible desarrollar los niveles de comprensión y análisis de la información, esto es, de <em>data literacy</em>. En los informes de <em>Gartner </em>se puede observar como uno de los principales bloqueos identificados en la obtención de resultados de los datos es una <strong>pobre alfabetización de datos.</strong></p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-7.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="946" data-attachment-id="4203" data-permalink="https://n4gash.com/data-literacy-o-alfabetizacion-de-los-datos/image-7-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-7.png?fit=1367%2C1263&amp;ssl=1" data-orig-size="1367,1263" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-7" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-7.png?fit=1024%2C946&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-7-1024x946.png?resize=1024%2C946&#038;ssl=1" alt="Informe Gartner valor Data Literacy para explotación de datos" class="wp-image-4203" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-7.png?resize=1024%2C946&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-7.png?resize=300%2C277&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-7.png?resize=768%2C710&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-7.png?w=1367&amp;ssl=1 1367w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Informe Gartner valor Data Literacy para explotación de datos</figcaption></figure>



<p>Compañero en este camino surge la necesidad de desarrollar un<strong> lenguaje común</strong> entre todos los individuos. Según el informe <em>Getting Started With Data Literacy</em> publicado por <em>Gartner </em>en 2018, este lenguaje común favorece:</p>



<ul class="wp-block-list">
<li>Los líderes de negocio internalizan la importancia de crear una cultura de compartir datos y recursos</li>



<li>Comprender el valor fundamental de mantener la calidad de los datos</li>



<li>Crear soluciones basadas en datos gracias a una alta colaboración y creatividad</li>



<li>Todos los empleados hablan el mismo lenguaje lo que permite abrir debates en búsqueda de nuevas métricas y análisis</li>
</ul>



<h2 class="wp-block-heading">Cómo medir la data literacy </h2>



<p>Según <a href="https://www.gartner.com/smarterwithgartner/a-data-and-analytics-leaders-guide-to-data-literacy" target="_blank" rel="noreferrer noopener">Gartner</a>, para medir el nivel de data literacy en una organización podemos responder a las siguientes preguntas:</p>



<ul class="wp-block-list">
<li>¿Cuántas personas de la organización pueden interpretar gráficos o KPI?</li>



<li>¿Cuántos miembros de dirección serían capaces de construir un caso de negocio basado en cifras concretas, precisas y relevantes?</li>



<li>¿Cuántos miembros de dirección pueden explicar los resultados de los procesos de sus áreas?</li>



<li>¿Cuántos data scientists pueden explicar los resultados de los algoritmos de ML que ejecutan?</li>



<li>¿Cuántos clientes pueden realmente apreciar e internacionalizar la esencia de los datos que se comparten con ellos?</li>
</ul>



<p>Si las simplificamos y traducimos a las necesidades del día a día a nivel de individuo, podríamos encontrar:</p>



<ul class="wp-block-list">
<li>Cuando te enfrentas a los datos ¿cuál es tu sensación? ¿aburrimiento, miedo o interés?</li>



<li>¿Utilizas los datos para realizar o mejorar tu trabajo?</li>



<li>¿Crees que la comprensión de los datos es tan importante como leer o escribir?</li>



<li>¿Tomas decisiones basadas en tu instinto?</li>



<li>¿Son accesibles foros donde se fomente la discusión de soluciones en torno a los datos?</li>



<li>¿Has consultado o eres consciente de los datos de tu área?</li>
</ul>



<p>Las respuestas a estas preguntas nos ofrecen un marcador para identificar el nivel de <em>data literacy</em> de la organización. Según ese nivel, podremos diseñar palancas para mejorar o incrementar la sensibilidad de los datos en todas los estratos.</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-8.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="496" data-attachment-id="4204" data-permalink="https://n4gash.com/data-literacy-o-alfabetizacion-de-los-datos/image-8-2/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-8.png?fit=1339%2C648&amp;ssl=1" data-orig-size="1339,648" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-8" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-8.png?fit=1024%2C496&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/02/image-8-1024x496.png?resize=1024%2C496&#038;ssl=1" alt="Los datos se convierten en el nexo entre tecnología, procesos e individuos" class="wp-image-4204" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/02/image-8.png?resize=1024%2C496&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-8.png?resize=300%2C145&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-8.png?resize=768%2C372&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/02/image-8.png?w=1339&amp;ssl=1 1339w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Los datos se convierten en el nexo entre tecnología, procesos e individuos</figcaption></figure>



<h2 class="wp-block-heading">Desarrollar habilidades de data literacy</h2>



<p>Un buen nivel de data literacy en una organización se traduce una buena evangelización de los datos por todas las capas (desde dirección hasta los empleados del último nivel). Desarrollar las habilidades de comprensión, interpretación y análisis crítico de los datos en cada una de las personas allana el camino mejorando y facilitando la toma de decisiones en la dirección correcta (o al menos, basada en datos empíricos y no por intuición). Algunas ideas para mejorar la alfabetización de los datos:</p>



<ul class="wp-block-list">
<li>Educar a todos los individuos a comprender y diseñar sus propias <strong>visualizaciones de datos</strong>, cuadros de mando, etc. Con la irrupción desde hace unos años de herramientas self-service de visualización de datos que no precisan de conocimientos técnicos se ha democratizado el acceso (P<em>ower BI, Qlik Sense, MicroStrategy Visual Insight</em>)</li>



<li>Organizar <strong>talleres </strong>impartidos por los expertos en data (IT o dominios) al resto de áreas.</li>



<li>Mejorar y desarrollar <strong>documentación </strong>que orbita a entender los datos que utiliza o expone cada área de la compañía</li>



<li>Mantener, distribuir y apostar por un <strong>gobierno de datos</strong> que favorezca el descubrimiento de metadatos para todos los individuos (linaje de datos, explotación, definiciones, seguridad, etc). Esta herramienta debe estar accesible para toda la compañía, de forma que cualquiera sea capaz de resolver cualquier duda sobre los datos que necesita.</li>



<li>Incentivar y facilitar las <strong>conversaciones o debates</strong> en torno a los datos para encaminar la toma de decisiones basadas en éstos y no en la intuición. Para ello es imprescindible </li>
</ul>



<p>El equipo de Qlik ha desarrollado la plataforma <em><a href="https://thedataliteracyproject.org/" target="_blank" rel="noreferrer noopener">Data Literacy Project</a> </em>para ayudar a organizaciones e individuos a entender y desarrollar las habilidades necesarias para extender la cultura del dato. Incluso ofrecen un plan de formación y certificación. A modo de pequeño juego, podemos hacer un pequeño examen o encuesta para comprobar nuestro nivel de <em>data literacy.</em></p>
<p>La entrada <a href="https://n4gash.com/data-literacy-o-alfabetizacion-de-los-datos/">Data literacy o alfabetización de los datos</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4202</post-id>	</item>
		<item>
		<title>Domain Driven Design (DDD) en arquitectura de datos</title>
		<link>https://n4gash.com/domain-driven-design-ddd-en-arquitectura-de-datos/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=domain-driven-design-ddd-en-arquitectura-de-datos</link>
		
		<dc:creator><![CDATA[Marcos Torregrosa]]></dc:creator>
		<pubDate>Thu, 02 Feb 2023 13:27:48 +0000</pubDate>
				<category><![CDATA[Data Architecture]]></category>
		<category><![CDATA[Data Engineering]]></category>
		<category><![CDATA[DDD]]></category>
		<category><![CDATA[Domain Driven Design]]></category>
		<guid isPermaLink="false">https://www.n4gash.com/?p=4175</guid>

					<description><![CDATA[<p>Dentro de la transformación de la arquitectura de datos de un modelo centralizado (Data Warehouse o Data Lake) a uno descentralizado (Data Mesh) podemos encontramos con un cambio de enfoque de desarrollo software, desplazándonos de un punto de vista data-driven (enfocado en los datos) a uno domain-driven (centrado en el conocimiento del dominio). Data-Driven Design En Data-Driven Design (DDD) se utiliza los datos para tomar decisiones y guiar el diseño y desarrollo de un sistema. Los datos se ven como el principal insumo para la construcción de ese sistema. Se utilizan para la definición de funcionalidades y características, es decir,…</p>
<p>La entrada <a href="https://n4gash.com/domain-driven-design-ddd-en-arquitectura-de-datos/">Domain Driven Design (DDD) en arquitectura de datos</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Dentro de la transformación de la arquitectura de datos de un modelo centralizado (<em>Data Warehouse o Data Lake</em>) a uno descentralizado (<em>Data Mesh</em>) podemos encontramos con un cambio de enfoque de desarrollo software, desplazándonos de un punto de vista <strong>data-driven</strong> (enfocado en los datos) a uno <strong>domain-driven</strong> (centrado en el conocimiento del dominio).</p>



<h2 class="wp-block-heading">Data-Driven Design </h2>



<p>En <strong>Data-Driven Design (DDD)</strong> se utiliza los datos para tomar decisiones y guiar el diseño y desarrollo de un sistema. Los datos se ven como el principal insumo para la construcción de ese sistema. Se utilizan para la definición de funcionalidades y características, es decir, para la toma de decisiones técnicas y arquitectónicas. El objetivo es construir sistemas que sean altamente escalables, flexibles y adaptables, y que sean capaces de responder a los cambios en los requisitos y las necesidades del negocio apoyándose en una fuerte capacidad de recopilación, almacenamiento, procesamiento y análisis de datos.</p>



<h2 class="wp-block-heading">Domain-Driven Design</h2>



<p>En <strong>Domain-driven Design (DDD)</strong> busca resolver un problema de negocio (dominio) convirtiendo el software en un reflejo del sistema en el mundo real. La toma de decisiones se basa en el conocimiento y comprensión del dominio. Éste se puede modularizar en subdominios que respondan a necesidades más específicas e independientes, mejorando la adaptabilidad, escalabilidad y resiliencia del sistema. El objetivo es crear un entorno de colaboración entre los expertos de negocio (saben el qué) y el equipo técnico (saben el cómo) para un desarrollo que sea coherente y pueda adaptarse a los cambios constantes en los requisitos.</p>



<h2 class="wp-block-heading">Conceptos de Domain-Driven Design (DDD)</h2>



<p>Los siguientes términos definen los elementos clave que componen un enfoque DDD:</p>



<ul class="wp-block-list">
<li><strong>Core Domain y Subdomains </strong>se refieren al ámbito del problema o necesidad del mundo real que se busca resolver. Los dominios pueden dividirse en subdominios para centrarse en dar soluciones más acotadas modularizando el dominio en porciones más pequeñas independientes.</li>



<li><strong>Domain model </strong>representación abstracta del problema del mundo real en entidades, relaciones y reglas de negocio.</li>



<li><strong>Bounded Context </strong>sería una frontera clara y explícita alrededor de una parte del dominio que puede ser comprendida y utilizada de manera autónoma. Esta frontera ayuda a evitar ambigüedades, malentendidos y acoplamientos. Permite que los equipos de desarrollo se enfoquen en una parte específica del dominio.</li>



<li><strong>Context Mapping </strong>es un proceso en el que los equipos de desarrollo trabajan con los expertos del negocio para mapear los conceptos y procesos de negocios y establecer las fronteras de los diferentes <em>Bounded Contexts</em>. Un mismo concepto no tiene por qué tener el mismo significado en dos dominios, por lo que el contexto identifica el significado de ese concepto dentro de un dominio. Este proceso ayuda a identificar las relaciones entre los diferentes <em>Bounded Contexts.</em> </li>



<li><strong>Ubiquitous Language</strong> es un lenguaje común y coherente utilizado por todas las partes interesadas, incluyendo los expertos del negocio y los desarrolladores, para describir los conceptos y procesos de negocios. Este lenguaje ayuda a reducir malentendidos y a asegurarse de que todas las partes comprendan de manera consistente.</li>
</ul>



<h3 class="has-medium-font-size wp-block-heading" style="font-style:normal;font-weight:100">Definición de un modelo</h3>



<p>Uno de los elementos clave de DDD es el <strong>modelo de dominio</strong> formado por las entidades, relaciones y reglas de negocio de un problema o necesidad del mundo real. Para la definición del modelo es importante la interacción entre los expertos de negocio y el equipo técnico siguiendo la regla de <em>ubiquitous language</em> comentaba en el punto anterior. Algunos de los elementos clave del modelo de dominio incluyen:</p>



<ol class="wp-block-list">
<li><strong>Entities</strong>: Las entidades son objetos únicos y distintos que representan conceptos significativos en el dominio, como clientes, productos o pedidos. Cada entidad tiene atributos y relaciones con otras entidades.</li>



<li><strong>Values</strong>: Los valores son objetos que representan conceptos sin identidad en el dominio, como una dirección o una fecha. Los valores son inmutables y se usan para representar información que no cambia.</li>



<li><strong>Services</strong>: Los servicios son componentes que representan procesos o tareas que no son parte de una entidad o un valor en particular, pero que son importantes en el dominio. Los servicios se utilizan para implementar lógica que es demasiado compleja o no pertenece a una entidad o un valor en particular.</li>



<li><strong>Aggregates</strong>: Los agregados son colecciones de entidades y valores que representan una unidad coherente en el dominio. Cada agregado tiene una entidad de raíz que es la entidad principal.</li>
</ol>



<h2 class="wp-block-heading">Caso de uso</h2>



<p>Veámoslo con un ejemplo, el diseño de un sistema de gestión de viajes para una aerolínea.</p>



<p>El equipo de desarrollo comenzaría identificando los distintos <strong>dominios </strong>o áreas de negocio dentro del sistema, como <em>reservas, facturación, asignación de asientos</em>, etc. Cada uno de estos dominios tendrá sus propias reglas y requisitos específicos. A continuación, se utilizaría el <em>Ubiquitous Language</em> para asegurarse de que todas las partes involucradas (expertos de negocio y equipo técnico) comprenden claramente los términos y conceptos específicos de cada dominio o subdominio. Por ejemplo, en el dominio de <em>reservas</em>, se podrían definir <strong>entidades </strong>como <em>«pasajero», «reserva», «vuelo»,</em> etc. Una vez que se han definido las <em>entidades</em>, se puede comenzar a modelar el comportamiento y las <strong>interacciones </strong>entre ellas. Por ejemplo, se puede especificar cómo se realiza una reserva, cómo se asigna un asiento, cómo se factura un vuelo, etc. A partir de aquí, se podría comenzar a implementar el sistema, utilizando patrones de diseño y técnicas específicas de DDD para asegurarse de que el sistema se ajuste claramente a los requisitos y reglas de cada dominio.</p>



<h2 class="wp-block-heading">Data Mesh y Domain-Driven Design (DDD)</h2>



<p><a href="https://www.n4gash.com/2023/arquitectura-data-mesh-gestion-de-datos-descentralizada/">Data Mesh</a> es un enfoque para la gestión de datos que se basa en el <strong>Domain-Driven Design (DDD)</strong>. La idea detrás de <em>Data Mesh</em> es que los datos se traten como un producto de software independiente, con sus propios equipos de desarrollo, su propia arquitectura y su propio ciclo de vida.</p>



<p>Un ejemplo para ilustrar esto podría ser una inmobiliaria. En este caso, siguiendo el enfoque DDD se identificarían los distintos <strong>dominios </strong>de negocio, como <em>propiedades, compradores, vendedores</em>, etc. Cada uno de estos dominios tendrá sus propios datos específicos, como descripciones de propiedades, información de contacto de compradores y vendedores, etc. En lugar de gestionar todos los datos en una arquitectura monolítica como un <em>data warehouse </em>o <em>data lake</em>, cada <em>Dominio</em> se encarga de crear y mantener sus propios conjuntos de datos conocidos como <em>Data as a Product</em>. Cada dominio estaría formado por los expertos de negocio y personal técnico. De esta manera, los equipos pueden centrarse en el desarrollo de los datos para su dominio específico, y el sistema en su conjunto se vuelve más escalable y fácil de mantener.</p>



<p>En resumen, en este ejemplo, Data Mesh utiliza DDD para dividir los datos en pequeñas piezas autónomas, permitiendo que los equipos se centren en el desarrollo de los datos para sus respectivos dominios de negocio.</p>
<p>La entrada <a href="https://n4gash.com/domain-driven-design-ddd-en-arquitectura-de-datos/">Domain Driven Design (DDD) en arquitectura de datos</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4175</post-id>	</item>
		<item>
		<title>Data Swamp: ¿Qué es y cómo evitarlo?</title>
		<link>https://n4gash.com/data-swamp-que-es-y-como-evitarlo/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=data-swamp-que-es-y-como-evitarlo</link>
					<comments>https://n4gash.com/data-swamp-que-es-y-como-evitarlo/#comments</comments>
		
		<dc:creator><![CDATA[Marcos Torregrosa]]></dc:creator>
		<pubDate>Tue, 31 Jan 2023 05:00:00 +0000</pubDate>
				<category><![CDATA[Cloud Data]]></category>
		<category><![CDATA[Data Architecture]]></category>
		<category><![CDATA[Data Engineering]]></category>
		<category><![CDATA[Data Lake]]></category>
		<category><![CDATA[Data Swamp]]></category>
		<guid isPermaLink="false">https://www.n4gash.com/?p=4118</guid>

					<description><![CDATA[<p>En la era digital, las organizaciones recopilan y almacenan una cantidad masiva de datos de diferentes fuentes. Sin embargo, el acúmulo de información sin clasificar y sin gestionar adecuadamente puede convertirse en un problema y dificultar la recuperación y el análisis de datos importantes. Este problema se conoce como Data Swamp o pantano/ciénaga de datos. ¿Qué es un Data Swamp? Un Data Swamp es un sistema de almacenamiento de datos ineficiente y sin estructura que se encuentra sobrecargado con grandes cantidades de información sin clasificar y sin gestionar adecuadamente. La falta de un plan o procesos efectivos de gestión de…</p>
<p>La entrada <a href="https://n4gash.com/data-swamp-que-es-y-como-evitarlo/">Data Swamp: ¿Qué es y cómo evitarlo?</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>En la era digital, las organizaciones recopilan y almacenan una cantidad masiva de datos de diferentes fuentes. Sin embargo, el acúmulo de información sin clasificar y sin gestionar adecuadamente puede convertirse en un problema y dificultar la recuperación y el análisis de datos importantes. Este problema se conoce como <strong>Data Swamp</strong> o <em>pantano/ciénaga de datos.</em></p>



<h2 class="wp-block-heading">¿Qué es un Data Swamp? </h2>



<p>Un <strong>Data Swamp es un sistema de almacenamiento de datos ineficiente y sin estructura</strong> que se encuentra sobrecargado con grandes cantidades de información sin clasificar y sin gestionar adecuadamente. La falta de un plan o procesos efectivos de gestión de datos puede resultar en un acúmulo de información sin valor que es difícil de recuperar y analizar.</p>



<figure class="wp-block-image alignfull size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/01/image-44.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="422" data-attachment-id="4124" data-permalink="https://n4gash.com/data-swamp-que-es-y-como-evitarlo/image-44/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/01/image-44.png?fit=2107%2C868&amp;ssl=1" data-orig-size="2107,868" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-44" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/01/image-44.png?fit=1024%2C422&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/01/image-44-1024x422.png?resize=1024%2C422&#038;ssl=1" alt="Data Lake vs Data Swamp" class="wp-image-4124" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/01/image-44.png?resize=1024%2C422&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-44.png?resize=300%2C124&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-44.png?resize=768%2C316&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-44.png?resize=1536%2C633&amp;ssl=1 1536w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-44.png?resize=2048%2C844&amp;ssl=1 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Data Lake vs Data Swamp</figcaption></figure>



<h2 class="wp-block-heading">Data Lake vs Data Swamp</h2>



<figure class="wp-block-table is-style-stripes, tab-head is-style-stripes"><table class="has-white-background-color has-background has-fixed-layout" style="border-style:none;border-width:0px"><thead><tr><th>Data Lake</th><th>Data Swamp</th></tr></thead><tbody><tr><td>Los datos están identificados con sus propiedades, relaciones y significado. Se gestiona activamente la <strong>metadata </strong>en base a reglas y controles.</td><td>No hay gestión de la metadata.</td></tr><tr><td>La <strong>ingesta de datos</strong> está engrasada y monitorizada.</td><td>Problemas en la ingesta de datos, lo que provoca pérdida de información y rastreo de los datos.</td></tr><tr><td>Los datos están <strong>organizados </strong>y jerarquizados para facilitar su acceso.</td><td>No hay organización, surgen silos de datos y se obstaculiza la exploración.</td></tr><tr><td>Garantiza la <strong>disponibilidad</strong>, en cualquier momento se pueden consumir los datos ya que están listos para su explotación.</td><td>Consumir los datos requiere tiempo de identificación y construir los mecanismos para disponobilizarlos.</td></tr><tr><td>Facilita la <strong>escalabilidad </strong>del sistema al gozar de controles y organización de los datos.</td><td>Se dificulta la escalabilidad al tener una estructura fragmentada y rígida. Favorece el surgimiento de silos y datos aislados.</td></tr><tr><td>Permite establecer <strong>controles de calidad</strong> que garantizan el valor de los mismos para su consumo.</td><td>No hay controles de calidad, los datos están incompletos o son opacos (invisibles o de difícil acceso).</td></tr></tbody></table><figcaption class="wp-element-caption">Comparativa Data Lake vs Data Swamp</figcaption></figure>



<h2 class="wp-block-heading">Cómo surgen los Data Swamps</h2>



<p>Los <em>Data Swamps</em> surgen cuando una organización recopila y almacena grandes cantidades de datos sin un plan o procesos efectivos de gestión y clasificación. La falta de estructura y organización puede hacer que los datos se acumulen de manera descontrolada y se conviertan en un problema, ya que no sólo no responden a su cometido: dar respuestas, si no que además, dificulta su explotación y comprensión.</p>



<figure class="wp-block-pullquote has-palette-color-1-color has-text-color" style="border-style:none;border-width:0px"><blockquote><p>Un data swamp surge cuando se abusa de un data lake </p></blockquote></figure>



<p>Las organizaciones a menudo se enfocan en recopilar y almacenar la mayor cantidad de datos posible sin preocuparse por su calidad y valor (¡<em>hay que historificar todo! ¡debemos ingestar cualquier dato que se genere</em>!). Esto puede resultar en que se guarden de datos duplicados, incompletos o sin valor que contribuyen al crecimiento del <em>Data Swamp</em> y que además de que nunca vayan a ser utilizados, dificultan encontrar y trabajar con los datos que realmente sí son valiosos.</p>



<h2 class="wp-block-heading">Cómo evitar los Data Swamps </h2>



<p>Para evitar el surgimiento de un <em>Data Swamp</em>, es importante <strong>implementar una estructura y procesos efectivos de gestión de datos</strong>. Algunos de los pasos que puede tomar una organización para evitarlos incluyen:</p>



<ol class="wp-block-list">
<li><strong>Implementar una estrategia de gestión de datos:</strong> Definir una estrategia clara para la recopilación, almacenamiento y análisis de datos, así como los objetivos y metas a alcanzar ¿es necesario guardar todos los datos de un origen? ¿debemos historificar toda la información? Priorizar qué datos son los realmente valiosos y por tanto, deben almacenarse. Es cierto que la respuesta a esta pregunta es el <em>ahora</em> y no sabemos si en el futuro necesitaremos datos que a día de hoy no se explotan. El ejercicio por tanto sería analizar y priorizar los datos que estamos seguros necesitamos o podremos necesitar en un futuro cercano.</li>



<li><strong>Catálogo de metadatos:</strong> los metadatos es información sobre los propios datos. Es un mecanismo para clasificar e identificar la información, clave para mantener un Data Lake. Si no sabemos qué tenemos, qué significa, de dónde viene o para que se usa, lo que hemos construido es un <em>data swamp.</em></li>



<li><strong>Fomentar una cultura de datos: </strong>Asegurarse de que los empleados comprendan la importancia de los datos y cómo pueden ser utilizados para mejorar la toma de decisiones. En nuestra organización se cuida la cultura del dato mediante un programa denominado <em>Data Fluency</em> cuyo objetivo es evangelizar alrededor del concepto de qué son los datos desde el punto de vista de IT.</li>



<li><strong>Adoptar tecnologías efectivas de gestión de datos</strong>: Invertir en tecnologías que permitan una gestión eficiente de los datos, incluyendo herramientas de integración, limpieza y análisis de datos. Aunque esto entraría más en procesos ETL o ELT posteriores, en realidad deberían implementarse controles en la ingesta de datos en el <em>data lake</em>.</li>



<li><strong>Definir políticas y procedimientos claros: </strong>Establecer políticas y procedimientos claros para la gestión de datos, incluyendo la seguridad, privacidad y cumplimiento de regulaciones. Especialmente en compliance y controles RGPD con la conservación de datos.</li>



<li><strong>Evaluar regularmente la eficacia de la gestión de datos</strong>: Realizar periódicamente auditorías y evaluaciones para determinar si se están cumpliendo los objetivos y si se requiere mejorar la estrategia de gestión de datos.</li>



<li><strong>Involucrar a todos los departamentos</strong>: Asegurarse de que todos los departamentos de la organización estén involucrados en la gestión de datos y que comprendan su importancia. En línea con el punto nº2.</li>
</ol>
<p>La entrada <a href="https://n4gash.com/data-swamp-que-es-y-como-evitarlo/">Data Swamp: ¿Qué es y cómo evitarlo?</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://n4gash.com/data-swamp-que-es-y-como-evitarlo/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4118</post-id>	</item>
		<item>
		<title>¿Qué es una arquitectura Data Fabric?</title>
		<link>https://n4gash.com/que-es-una-arquitectura-data-fabric/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=que-es-una-arquitectura-data-fabric</link>
					<comments>https://n4gash.com/que-es-una-arquitectura-data-fabric/#comments</comments>
		
		<dc:creator><![CDATA[Marcos Torregrosa]]></dc:creator>
		<pubDate>Mon, 30 Jan 2023 09:14:58 +0000</pubDate>
				<category><![CDATA[Data Architecture]]></category>
		<category><![CDATA[Data Engineering]]></category>
		<category><![CDATA[Data Fabric]]></category>
		<category><![CDATA[Data virtualization]]></category>
		<guid isPermaLink="false">https://www.n4gash.com/?p=4078</guid>

					<description><![CDATA[<p>Data Fabric es una arquitectura de datos que proporciona una plataforma unificada &#8211; o capa de abstracción &#8211; flexible para acceder, procesar y gestionar datos en diferentes formatos, estructuras y localizaciones. Permite conectar diferentes fuentes de datos y ofrecer una vista única de los mismos, al tiempo que mantiene la seguridad, la privacidad y la calidad de los datos. Con Data Fabric se abona el camino para la construcción de una vista holística de la organización que entrega a los usuarios (analistas, data scientists y de negocio) la capacidad de descubrir todos los datos y sus relaciones. En comparación con…</p>
<p>La entrada <a href="https://n4gash.com/que-es-una-arquitectura-data-fabric/">¿Qué es una arquitectura Data Fabric?</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p><strong>Data Fabric</strong> es una arquitectura de datos que proporciona una plataforma unificada &#8211; o capa de abstracción &#8211; flexible para acceder, procesar y gestionar datos en diferentes formatos, estructuras y localizaciones. Permite conectar diferentes fuentes de datos y ofrecer una vista única de los mismos, al tiempo que mantiene la seguridad, la privacidad y la calidad de los datos. Con <em>Data Fabric </em>se abona el camino para la construcción de una vista holística  de la organización que entrega a los usuarios (analistas, data scientists y de negocio) la capacidad de descubrir todos los datos y sus relaciones.</p>



<p>En comparación con los <strong>Data Warehouse</strong>, los Data Fabrics son más flexibles y escalables. Los Data Warehouse están diseñados para almacenar grandes cantidades de datos estructurados y proporcionar informes y análisis a partir de ellos. Sin embargo, esto puede limitar la capacidad de un Data Warehouse para adaptarse a nuevos tipos de datos o fuentes, y puede requerir una reconfiguración costosa y técnica para hacerlo.</p>



<p>Los <strong>Data Lake</strong> son soluciones de almacenamiento de datos no estructurados, diseñados para recopilar grandes cantidades de datos <em>raw</em> sin una estructura predefinida y a un coste moderado. Mientras que los Data Lakes pueden ser útiles para almacenar grandes cantidades de datos, pueden ser difíciles de acceder y analizar, y pueden requerir una gran cantidad de tiempo y recursos para asegurarse de que los datos son precisos y coherentes (riesgo de convertirse en <em>data swamps</em>).</p>



<figure class="wp-block-pullquote has-palette-color-2-color has-text-color" style="border-style:none;border-width:0px"><blockquote><p><strong><em>Data Fabric</em> es una pieza complementaria a cualquier arquitectura de datos implantada, sea centralizada o descentralizada</strong></p></blockquote></figure>



<p>Los <a href="https://www.n4gash.com/2023/arquitectura-data-mesh-gestion-de-datos-descentralizada/"><strong>Data Mesh</strong> </a>es una arquitectura o filosofía para la gestión de datos que busca desarrollar una estructura descentralizada y de autogestión, donde cada equipo o departamento es responsable de sus propios datos y puede compartirlos de forma segura con otros equipos. Los datos se tratan como <em>Data as a product </em>(<em>DaaP</em>). A diferencia de los <em>Data Fabrics</em>, los <em>Data Mesh </em>no tienen un punto central de control o gestión, y en su lugar, se basan en la colaboración y el intercambio de datos entre dominios en un sistema federado de propiedad de los datos (DaaPs). Los <em>data fabric</em> intentan resolver los mismos problemas (organizaciones con multitud de orígenes de datos en distintas ubicaciones y tipologías) que el <em>Data Mesh</em> pero desde un punto de vista técnico y no organizativo.</p>



<p>En resumen, el <em>Data Fabric </em>es una arquitectura de datos que combina las fortalezas de los<em> Data Warehouse </em>y<em> Data Lakes</em>, con el mismo objetivo que los <em>Data Mesh</em> pero desde la tecnología y de forma centralizada, que ofrece una solución completa y flexible para la gestión de datos. Supone una vista unificada de los datos, permite conectar diferentes fuentes de datos y ofrece una gestión eficiente y segura de los mismos. </p>



<p>Desde mi punto de vista, <em><strong>Data Fabric</strong></em> se trata de una arquitectura complementaria, no excluyente. Tanto si la arquitectura de datos es centralizada (data lake, data lakehouse) o descentralizada (data mesh), puede ser una <strong>pieza acompañante</strong> que haga de facilitador para el gobierno y disponibilidad en la ingesta desde los distintos orígenes, así como en la capa de consumo.</p>



<figure class="wp-block-image alignfull size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/01/image-37.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="742" data-attachment-id="4086" data-permalink="https://n4gash.com/que-es-una-arquitectura-data-fabric/image-37/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/01/image-37.png?fit=2307%2C1671&amp;ssl=1" data-orig-size="2307,1671" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-37" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/01/image-37.png?fit=1024%2C742&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/01/image-37-1024x742.png?resize=1024%2C742&#038;ssl=1" alt="Diagrama arquitectura Data Fabric" class="wp-image-4086" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/01/image-37.png?resize=1024%2C742&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-37.png?resize=300%2C217&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-37.png?resize=768%2C556&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-37.png?resize=1536%2C1113&amp;ssl=1 1536w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-37.png?resize=2048%2C1483&amp;ssl=1 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Diagrama arquitectura Data Fabric</figcaption></figure>



<h2 class="wp-block-heading">Ventajas y desventajas de Data Fabric</h2>



<figure class="wp-block-table alignleft is-style-stripes"><table class="has-light-green-cyan-background-color has-background"><thead><tr><th>Ventajas</th><th>D<strong>esventa</strong>jas</th></tr></thead><tbody><tr><td><strong>Accesibilidad</strong>: Acceso más fácil y ágil a los datos, lo que significa que los usuarios pueden encontrar los datos que necesitan rápidamente y usarlos de forma más eficiente.<br /><strong>Integración</strong>: La integración de los datos mejora gracias al <em>Data Fabric</em>, lo que significa que los datos pueden ser compartidos y utilizados de forma más efectiva en toda la organización.<br /><strong>Flexibilidad</strong>: Permite una gestión más flexible de los datos, lo que significa que los usuarios pueden adaptarse rápidamente a los cambios en las necesidades de negocios y a las nuevas fuentes de datos.</td><td><strong>Complejidad</strong>: La implementación de un <em>Data Fabric</em> puede ser un proceso complejo que requiere de un conocimiento profundo de la tecnología de datos y de los procesos empresariales.<br /><strong>Costo</strong>: La implementación puede ser costosa, especialmente si se requiere de la compra de software.<br /><strong>Seguridad</strong>: Aspecto crítico que debe ser considerado (tanto en reposo como en tránsito).<br /><strong>Rendimiento</strong>: Puede ser un problema si el sistema no está bien diseñado u optimizado desde un principio.<br /><strong>Escalabilidad</strong>: Desafío si el sistema no está diseñado para manejar una gran cantidad de datos o una alta tasa de crecimiento de los mismos.</td></tr></tbody></table><figcaption class="wp-element-caption">Ventajas y desventajas Data Fabric</figcaption></figure>



<h2 class="wp-block-heading">Componentes de un Data Fabric</h2>



<ul class="wp-block-list">
<li><strong>Virtualización de datos</strong>: permite el acceso a los datos desde diferentes fuentes a través de una única capa de abstracción.</li>



<li><strong>Catalogación y metadatos</strong>: proporciona información sobre los datos y su ubicación para facilitar su acceso y gestión.</li>



<li><strong>Seguridad y privacidad</strong>: proporciona medidas para garantizar la seguridad y privacidad de los datos.</li>



<li><strong>Integración de datos</strong>: permite la integración de diferentes fuentes de datos para un acceso más fácil y una gestión más eficiente.</li>



<li><strong>Procesamiento de datos:</strong> permite la ejecución de tareas de procesamiento de datos, incluyendo la limpieza, transformación y análisis de datos.</li>



<li><strong>Colaboración</strong>: permite la colaboración entre los usuarios y equipos para una gestión más eficiente de los datos.</li>



<li><strong>Monitorización y análisis</strong>: proporciona información sobre el uso y rendimiento de los datos para una mejor toma de decisiones y optimización del sistema.</li>
</ul>



<h2 class="wp-block-heading">Virtualización de datos</h2>



<p>La <strong>virtualización de datos es una tecnología que permite crear una capa abstracta sobre los datos reales para ocultar su complejidad y mejorar su accesibilidad</strong>. Esa nueva capa se encarga de agrupar los datos de distintos orígenes y complejidad, exponiéndolos al usuario de una forma homogénea e integrada. La virtualización de datos permite a los usuarios acceder a los datos de una manera más sencilla y rápida, sin tener que preocuparse por la dificultad subyacente de la infraestructura de datos. Facilitaría el cruce de información entre distintos orígenes (<em>SoR</em>) almacenados en software diferentes. Por ejemplo, seríamos capaces de cruzar datos de una tabla de Oracle con otra de MongoDB ubicadas en distintas máquinas sin necesidad de construir ETLs o replicar datos en capas intermedias.</p>



<p>La <em>virtualización de datos</em> es un componente clave del <em>Data Fabric</em>, ya que permite crear una vista unificada de los datos que se encuentran en diferentes formatos, estructuras y localizaciones. Permite a los usuarios acceder a los datos de una manera sencilla, al tiempo que mantiene la seguridad, la privacidad y la calidad de los mismos.</p>



<p>Además, permite modificar los datos virtuales sin afectar a los datos reales, lo que significa que los datos pueden ser integrados, migrados y gestionados de forma más eficiente. La gestión de los datos se vuelve más flexible y escalable, lo que significa que los usuarios pueden adaptarse rápidamente a los cambios en las necesidades de negocios y a las nuevas fuentes de datos.</p>



<p>Una de las desventajas de la virtualización de datos es el impacto en los sistemas fuente, ya que cuando se realiza una consulta en realidad se está atacando directamente el origen. Para mitigar problemas de rendimiento, se pueden cachear las consultas en una capa intermedia (una BD) de forma que cuando se consulte una vista virtualizada en primer lugar lo haga sobre la caché. El ciclo de vida de esta caché se puede configurar y gestionar desde el software de virtualización. Otra forma de mejorar el rendimiento es mapeando datos entre los orígenes, si consultamos datos de dos fuentes distintas el sistema copiaría los datos menos voluminosos en el otro origen para realizar la consulta sobre la misma ubicación.</p>



<figure class="wp-block-image aligncenter size-large"><a href="https://i0.wp.com/www.n4gash.com/wp-content/2023/01/image-38.png?ssl=1"><img data-recalc-dims="1" loading="lazy" decoding="async" width="1024" height="552" data-attachment-id="4088" data-permalink="https://n4gash.com/que-es-una-arquitectura-data-fabric/image-38/" data-orig-file="https://i0.wp.com/n4gash.com/wp-content/2023/01/image-38.png?fit=2436%2C1314&amp;ssl=1" data-orig-size="2436,1314" data-comments-opened="0" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-38" data-image-description="" data-image-caption="" data-large-file="https://i0.wp.com/n4gash.com/wp-content/2023/01/image-38.png?fit=1024%2C552&amp;ssl=1" src="https://i0.wp.com/www.n4gash.com/wp-content/2023/01/image-38-1024x552.png?resize=1024%2C552&#038;ssl=1" alt="Virtualización de datos" class="wp-image-4088" srcset="https://i0.wp.com/n4gash.com/wp-content/2023/01/image-38.png?resize=1024%2C552&amp;ssl=1 1024w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-38.png?resize=300%2C162&amp;ssl=1 300w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-38.png?resize=768%2C414&amp;ssl=1 768w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-38.png?resize=1536%2C829&amp;ssl=1 1536w, https://i0.wp.com/n4gash.com/wp-content/2023/01/image-38.png?resize=2048%2C1105&amp;ssl=1 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><figcaption class="wp-element-caption">Virtualización de datos</figcaption></figure>



<p>La <em>virtualización de datos</em> está construida por un catálogo de <strong>vistas </strong>que se alimentan de los distintos orígenes de datos cuya comunicación se configura a través de <strong>conectores</strong>. Por tanto, podríamos decir que para construir una capa de virtualización de datos necesitaríamos en primer lugar crear los conectores a los distintos orígenes (Oracle, Teradata, Azure PaaS SQL Server, Google BigQuery, PostgreSQL, mongoDB, Profile, SQL Server&#8230;) y después generar las vistas que extraen información de estos orígenes. El cemento que une todo el sistema es un<strong> catálogo de metadata</strong> que se encarga de almacenar la ubicación, conexión y tipología de cada dato.</p>



<h3 class="has-medium-font-size wp-block-heading" style="font-style:normal;font-weight:100">Conectores</h3>



<p>Como en cualquier herramienta de integración de datos, los <strong>conectores </strong>se encargan de abrir la conexión del origen de datos con nuestro sistema. El propio software de virtualización de datos (<a href="https://www.denodo.com/es" target="_blank" rel="noreferrer noopener">Denodo</a>, <a href="https://www.ibm.com/products/cloud-pak-for-data" target="_blank" rel="noreferrer noopener">CP4D</a>) dispone de conectores ya <em>prefabricados</em> que solo debemos configurar para abrir la comunicación.</p>



<h2 class="has-medium-font-size wp-block-heading" style="font-style:normal;font-weight:100">Vistas</h2>



<p>Una <strong>vista </strong>es una representación de datos de un origen específico, el concepto sería similar a una <a href="https://www.n4gash.com/2011/diferencias-vista-oracle-vista-materializada-view/">vista tradicional en una BBDD</a>. Podemos encontrar <em><strong>base views </strong></em>que sería una vista generada directamente de una tabla o query del mismo origen de datos, o <em><strong>derived views</strong></em>, que serían la combinación de varias <em>base views</em> donde ya podrían cruzarse datos de distintos orígenes. Al utilizar los conectores, la vista permite el acceso a los datos sin tener que conocer su ubicación o formato real.</p>



<h3 class="has-medium-font-size wp-block-heading" style="font-style:normal;font-weight:100">Catálogo de metadatos</h3>



<p>El <strong>catálogo de metadatos</strong> sería donde se almacena toda la información de los datos: ubicación, formato, estructura y relación con otros datos. Puede utilizarse para ayudar a la toma de decisiones sobre la gestión, incluyendo la seguridad, la calidad y la integridad de los datos. También puede ser utilizado para optimizar el rendimiento del sistema y para mejorar la eficiencia.</p>
<p>La entrada <a href="https://n4gash.com/que-es-una-arquitectura-data-fabric/">¿Qué es una arquitectura Data Fabric?</a> se publicó primero en <a href="https://n4gash.com">Marcos Torregrosa</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://n4gash.com/que-es-una-arquitectura-data-fabric/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4078</post-id>	</item>
	</channel>
</rss>