<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Soda on Home</title>
    <link>https://gontcharov.eu/tags/soda/</link>
    <description>Recent content in Soda on Home</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Sat, 29 Mar 2025 11:13:27 +0100</lastBuildDate><atom:link href="https://gontcharov.eu/tags/soda/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>🎥 Testing Data Quality with Soda Core in Databricks</title>
      <link>https://gontcharov.eu/posts/youtube/databricks-soda-core/</link>
      <pubDate>Sat, 29 Mar 2025 11:13:27 +0100</pubDate>
      
      <guid>https://gontcharov.eu/posts/youtube/databricks-soda-core/</guid>
      <description>&lt;p&gt;In this video I demonstrate how to perform data quality checks on a Delta table in Databricks using &lt;a href=&#34;https://docs.soda.io/soda-core/overview-main.html&#34;&gt;Soda Core&lt;/a&gt;.&lt;/p&gt;
&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/cyasTwPdZEs?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;
    &lt;/div&gt;

&lt;br&gt;
&lt;p&gt;Soda Core is the open-source Python package developed by Soda. It can be compared to Great Expectations, but is much simpler in my opinion. I enjoy using Soda in my professional projects and will continue exploring this framework.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>In this video I demonstrate how to perform data quality checks on a Delta table in Databricks using <a href="https://docs.soda.io/soda-core/overview-main.html">Soda Core</a>.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/cyasTwPdZEs?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<br>
<p>Soda Core is the open-source Python package developed by Soda. It can be compared to Great Expectations, but is much simpler in my opinion. I enjoy using Soda in my professional projects and will continue exploring this framework.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Exploring Soda Data Quality Testing on Databricks</title>
      <link>https://gontcharov.eu/posts/blog/exploring-soda-data-quality-framework/</link>
      <pubDate>Fri, 14 Feb 2025 10:08:53 +0100</pubDate>
      
      <guid>https://gontcharov.eu/posts/blog/exploring-soda-data-quality-framework/</guid>
      <description>&lt;h1 id=&#34;use-case&#34;&gt;Use Case&lt;/h1&gt;
&lt;p&gt;For my current engagement I&amp;rsquo;m tasked with developing an automated data quality framework for a large industrial enterprise in the renewable energy sector. The client has over a hundred independent SCADA systems from various vendors gathering energy production data. All this data has to flow in one central repository to be analyzed with Databricks. The client is obligated to ensure high data quality for contractual reporting to external parties. Failure to deliver incurs high financial penalties.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h1 id="use-case">Use Case</h1>
<p>For my current engagement I&rsquo;m tasked with developing an automated data quality framework for a large industrial enterprise in the renewable energy sector. The client has over a hundred independent SCADA systems from various vendors gathering energy production data. All this data has to flow in one central repository to be analyzed with Databricks. The client is obligated to ensure high data quality for contractual reporting to external parties. Failure to deliver incurs high financial penalties.</p>
<h1 id="soda">Soda</h1>
<p>Soda built their product offering Soda cloud on top of the open-source Python package Soda Core<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. In a nutshell, their solution allows you to:</p>
<ol>
<li>Test your data pipelines</li>
<li>Monitor data quality</li>
<li>Create reports</li>
</ol>
<p><img loading="lazy" src="/posts/blog/exploring-soda-data-quality-framework/soda_architecture.png" type="" alt=""  />
<em>Image source: <a href="https://www.soda.io/integrations/databricks">Exploring the Soda Data Quality Platform</a></em></p>
<h1 id="simple-workflow">Simple Workflow</h1>
<p>To test the framework, I defined a simple workflow that performs a number of data quality checks on two large datasets.</p>
<h2 id="installation">Installation</h2>
<p>First I install the framework on my Databricks notebook:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">pip install -i https://pypi.cloud.soda.io soda-spark-df
</span></span></code></pre></div><p>Next, I import and instantiate the <code>Scan</code> class, create a data definition name, and set the data source to be tested to a Spark dataframe:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">soda.scan</span> <span class="kn">import</span> <span class="n">Scan</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Create a scan object</span>
</span></span><span class="line"><span class="cl"><span class="n">scan</span> <span class="o">=</span> <span class="n">Scan</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Set a scan definition</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Use a scan definition to configure which data to scan,</span>
</span></span><span class="line"><span class="cl"><span class="c1"># and when and how to execute the scan.</span>
</span></span><span class="line"><span class="cl"><span class="n">scan</span><span class="o">.</span><span class="n">set_scan_definition_name</span><span class="p">(</span><span class="s2">&#34;Data Completeness&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">scan</span><span class="o">.</span><span class="n">set_data_source_name</span><span class="p">(</span><span class="s2">&#34;spark_df&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Attach a Spark session</span>
</span></span><span class="line"><span class="cl"><span class="n">scan</span><span class="o">.</span><span class="n">add_spark_session</span><span class="p">(</span><span class="n">spark</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="configuration">Configuration</h2>
<p>I like the simplicity of Soda&rsquo;s YAML configuration that is stored in my Databricks workspace.</p>
<p>The connection to Scoda Cloud&rsquo;s dashboard and datasources is configured in a <em>sonda_conf.yml</em> file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-YAML" data-lang="YAML"><span class="line"><span class="cl"><span class="nt">soda_cloud</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="l">cloud.soda.io</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">api_key_id</span><span class="p">:</span><span class="w"> </span><span class="l">2bcda34c-xxxx-xxxx-xxxx-xxxxxxxxxxxx</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">api_key_secret</span><span class="p">:</span><span class="w"> </span><span class="l">zuNLl1k55YM_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span><span class="w">
</span></span></span></code></pre></div><p>The actual data quality checks are defined in a <em>checks.yml</em> file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-YAML" data-lang="YAML"><span class="line"><span class="cl"><span class="nt">checks for table_one</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">missing_count(site_id) = 0</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Ensure there are no null values in the site ID column</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">duplicate_count(site_id) = 0</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Ensure there are no duplicate site ID&#39;s</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">checks for table_two</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">missing_count(device_id) = 0</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Ensure there are no null values in the site ID column</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">num_devices &gt; 50</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">num_devices query</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">        SELECT COUNT(DISTINCT device_id) FROM table_two</span><span class="w">
</span></span></span></code></pre></div><p><em>Note how I&rsquo;m defining a custom data quality check with SQL code.</em></p>
<p>The configuration is then added to the <code>scan</code> instance:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">scan</span><span class="o">.</span><span class="n">add_sodacl_yaml_file</span><span class="p">(</span><span class="s2">&#34;/Workspace/Users/Denis/Soda/soda_settings/checks.yml&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">scan</span><span class="o">.</span><span class="n">add_configuration_yaml_file</span><span class="p">(</span><span class="s2">&#34;/Workspace/Users/Denis/Soda/soda_settings/soda_conf.yml&#34;</span><span class="p">)</span>
</span></span></code></pre></div><h2 id="result">Result</h2>
<p>Executing the checks and viewing the logs is straightforward:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">scan</span><span class="o">.</span><span class="n">execute</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">scan</span><span class="o">.</span><span class="n">get_logs_text</span><span class="p">())</span>
</span></span></code></pre></div><p>This is an extract of the output:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">INFO   <span class="p">|</span> Scan summary:
</span></span><span class="line"><span class="cl">INFO   <span class="p">|</span> 2/4 checks PASSED:
</span></span><span class="line"><span class="cl">INFO   <span class="p">|</span>     table_one in spark_df
</span></span><span class="line"><span class="cl">INFO   <span class="p">|</span>       Ensure there are no null values in the site_id column <span class="o">[</span>PASSED<span class="o">]</span>
</span></span><span class="line"><span class="cl">INFO   <span class="p">|</span>     table_one in spark_df
</span></span><span class="line"><span class="cl">INFO   <span class="p">|</span>       Ensure there are no null values in the site_id column <span class="o">[</span>PASSED<span class="o">]</span>
</span></span><span class="line"><span class="cl">INFO   <span class="p">|</span> 2/4 checks FAILED:
</span></span><span class="line"><span class="cl">INFO   <span class="p">|</span>     table_two in spark_df
</span></span><span class="line"><span class="cl">INFO   <span class="p">|</span>       Ensure there are no duplicate site_id<span class="err">&#39;</span>s <span class="o">[</span>FAILED<span class="o">]</span>
</span></span><span class="line"><span class="cl">INFO   <span class="p">|</span>         check_value: <span class="m">1</span>
</span></span><span class="line"><span class="cl">INFO   <span class="p">|</span>     table_two
</span></span><span class="line"><span class="cl">INFO   <span class="p">|</span>       num_devices &gt; <span class="m">50</span> <span class="o">[</span>FAILED<span class="o">]</span>
</span></span><span class="line"><span class="cl">INFO   <span class="p">|</span>         check_value: 41.0
</span></span><span class="line"><span class="cl">INFO   <span class="p">|</span> Oops! <span class="m">2</span> failures. <span class="m">0</span> warnings. <span class="m">0</span> errors. <span class="m">2</span> pass.
</span></span><span class="line"><span class="cl">INFO   <span class="p">|</span> Sending results to Soda Cloud
</span></span></code></pre></div><p>We see that only two of the four checks pass successfully:</p>
<ul>
<li>The <code>site_id</code> values are not unique for every row.</li>
<li>There are less than 50 distinct <code>device_id</code> values.</li>
</ul>
<h1 id="reporting">Reporting</h1>
<p>What I like about Soda Cloud is the out-of-the-box visualization of my test results. At a glance, I can instantly see how my data quality varies from day-to-day, troubleshoot issues, view data, and even send alarms.</p>
<p><img loading="lazy" src="/posts/blog/exploring-soda-data-quality-framework/soda_dashboard.png" type="" alt=""  />
<em>Screenshot of my Soda Dashboard</em></p>
<h1 id="verdict">Verdict</h1>
<p>Soda looks promising, but it&rsquo;s too early to tell whether it fits my current use cases.</p>
<p>What I like:</p>
<ul>
<li>Minimalistic design</li>
<li>Intuitive configuration</li>
<li>Convenient dashboard</li>
</ul>
<p>What I <em>don&rsquo;t</em> like:</p>
<ul>
<li>Data is sent off-premises</li>
<li>Financial cost<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></li>
<li>Not very mature product yet<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></li>
</ul>
<p>Overall, I&rsquo;m excited to continue exploring Soda as a potential alternative to <a href="https://greatexpectations.io/">Great Expectations</a>!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Soda Core <a href="https://docs.soda.io/soda-core/overview-main.html">documentation</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Even though the cost is low, it still has to go through a lengthy procurement process.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>I spotted some errors and typos in the documentation.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
