<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>watsy0007</title>
<link>https://watsy0007.com/blog.html</link>
<atom:link href="https://watsy0007.com/blog.xml" rel="self" type="application/rss+xml"/>
<description></description>
<generator>quarto-1.5.56</generator>
<lastBuildDate>Mon, 26 Aug 2024 00:00:00 GMT</lastBuildDate>
<item>
  <title>I Build My Own Terminal Configuration</title>
  <link>https://watsy0007.com/blog/i_build_my_own_terminal_configuration/</link>
  <description><![CDATA[ 





<p>最近回忆起自己作为开发人员的职业生涯，我使用过无数工具，包括各种集成开发环境（IDE）、数据库管理软件和各种系统工具。尝试过 10 多把键盘。随着年龄的增长，逐渐意识到时间越来越重要，以前的我在这些琐碎事情上浪费太多时间。</p>
<p>我想是时候专注于长期目标, 尽可能的做得更少, 更多思考。这个想法促使我创建自己的终端配置。考虑到我已经使用了 10 年的 MacOS，作为后端工程师也有 8 年，而且日常我经常使用终端。我知道终端可以有效的提高我的生产力.</p>
<p>最后，我想减少对付费工具的依赖。付费软件会经常更改其用户界面，引入新功能，废弃一些功能。我理解公司需要“创新”和赚钱，但这是以用户体验为代价,当我依赖的工具发生重大变化或被停用，我的一切基于工具建立的知识都作废了, 这个过程我经历了好几次!!! 以后要避免这种风险。</p>
<p>我的目标是使用<strong>简单</strong>、<strong>有效的工具</strong>， <strong>帮助我完成工作</strong> ，而不会增加不必要的复杂性。</p>
<p>基于以上考虑，我的目标很明确：使用可以提高我的编码效率的工具。</p>
<p>以下是我的标准：</p>
<ol type="1">
<li>开源：假设创建者放弃了项目，我还可以使用甚至可能维护自己的版本。</li>
<li>可定制：可以能够禁用我不需要的功能。</li>
<li>熟悉：我熟悉喜欢的工具应该继续使用，减少迁移的压力。</li>
<li>键盘为中心：尽可能多地使用键盘以提高效率。</li>
</ol>
<p>按照这些标准，我选择了 NeoVim 替换其他 IDE，当在 Kitty、WezTerm、iTerm2 和系统默认终端之间选择终端时，我选择了 WezTerm。因为可以使用 Lua 同时配置 NeoVim 和 WezTerm。</p>
<p>下面，让我介绍一下我的终端配置步骤。我的工具包括 WezTerm、NeoVim、Sketchybar、yabai 和 skhd。</p>
<section id="wezterm" class="level2">
<h2 class="anchored" data-anchor-id="wezterm">WezTerm</h2>
<p>我的 WezTerm 配置主要是从这篇优秀的文章 <a href="https://alexplescan.com/posts/2024/08/10/wezterm/">Okay, I really like WezTerm</a> 中获取灵感。通过这篇文章我了解了 WezTerm 的基本功能，如何使用 Lua 配置这些功能。因此我的大部分 WezTerm 配置都是参考这篇文章配置的。</p>
</section>
<section id="neovim" class="level2">
<h2 class="anchored" data-anchor-id="neovim">NeoVim</h2>
<p>当我开始配置 NeoVim 时，我最初考虑了几种开箱即用的解决方案，如 DoomVim、LunarVim、AstroVim 等。之后觉得这些都太复杂了, 因此打算从头开始构建自己的配置，我花了一下午阅读官方 <a href="https://neovim.io/doc/user/index.html">NeoVim 文档</a> 后, 意识到 NeoVim 的复杂性使得从零开始在这个阶段变得不切实际。</p>
<p>于是改变下策略，使用第三方包来实现我的目标，尽可能保持尽可能简洁。在这个过程中非常好奇 DHH 是如何快速实现切换到 NeoVim 并且配置了一套 IDE 的，通过分析 <a href="https://github.com/basecamp/omakub">omakub</a> 代码，觉得 DHH 简直太聪明了, 他直接使用 LayVim 提供的 <a href="https://github.com/LazyVim/starter">starter</a> 模板，LazyVim 我已经非常熟悉了。 因此，我的 NeoVim 配置几乎完全参考 omakub 的设置。直接使用 starter.</p>
</section>
<section id="sketchybar" class="level2">
<h2 class="anchored" data-anchor-id="sketchybar">Sketchybar</h2>
<p>sketchybar 是 MacOS 菜单栏的高度可定制替代品。在用 Sketchybar 之前，我使用 <a href="https://www.macbartender.com/">bartender</a> 和 <a href="https://bjango.com/mac/istatmenus/">iStat Menus</a> 来管理和简化我的状态栏。Sketchybar 提供了一个受欢迎的开源替代方案，与我的目标一致。这是我的 <a href="https://github.com/watsy0007/sketchybar">sketchybar 配置链接</a>。</p>
</section>
<section id="yabai-skhd" class="level2">
<h2 class="anchored" data-anchor-id="yabai-skhd">Yabai + skhd</h2>
<p>最后一块拼图是我最初认为最不重要但现在经常使用的工具组合。遵循 MacOS 功能用户社区的常见做法，我选择了 yabai 用于窗口管理，skhd 用于自定义键盘快捷键。</p>
<p>Yabai 允许高级窗口平铺和管理，而 skhd 让我可以为各种应用程序控件创建自定义键盘快捷键。这是我的 <a href="https://gist.github.com/watsy0007/b6cb19b2fdd38655c2545d3a4a36957b">yabai 配置链接</a>，这是我的 <a href="https://gist.github.com/watsy0007/e08bfebc74acefe518fc2af504edef07">skhd 配置链接</a>。 即使我已经习惯于触控板手势，还是能感觉到 yabai 和 skhd 的组合显著提高了我的工作效率。</p>
</section>
<section id="结论" class="level1">
<h1>结论</h1>
<p>在这篇文章中，我解释了构建自定义终端配置的动机，并概述了我选择的工具。虽然我没有深入探讨每个配置的细节，但我分享了我的设置文件链接，以供大家参考。</p>
<p>现在这个配置我相当满意，我认为达到了我的预定目标。随着我继续使用和完善，我对后续进一步的优化相当期待。如果大家对我的配置有任何问题或建议，请告诉我。</p>


</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">二次使用</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/deed.zh">CC BY-SA 4.0</a></div></div></section></div> ]]></description>
  <category>productivity</category>
  <guid>https://watsy0007.com/blog/i_build_my_own_terminal_configuration/</guid>
  <pubDate>Mon, 26 Aug 2024 00:00:00 GMT</pubDate>
  <media:content url="https://watsy0007.com/blog/i_build_my_own_terminal_configuration/i-build-my-own-terminal-configuration-thumpnail.webp" medium="image" type="image/webp"/>
</item>
<item>
  <title>Analyzing Blockchain Data with DuckDB: Data Preparation</title>
  <link>https://watsy0007.com/blog/analyzing_blockchain_data_with_duckdb_1/</link>
  <description><![CDATA[ 





<p>最近使用 <code>DuckDB</code> 替换 <code>pandas</code> + <code>python</code> 做数据处理的部分越来越多, 用起来非常顺手.</p>
<p>有朋友想了解下我日常是如何使用 DuckDB 的, 考虑写成系列文章, 本篇是第一篇, 主要分享如何使用 <code>DuckDB</code> 做前期数据处理.</p>
<section id="案例" class="level1">
<h1>案例</h1>
<p>用日常案例举例, 希望分析 <code>ETH</code> 某个地址交易信息.</p>
</section>
<section id="演示" class="level1">
<h1>演示</h1>
<p>这个过程在以往, 首先通过 <code>requests</code> 获取数据, 接着用 <code>pandas</code> 预览数据和清洗数据, 最后才会使用 <code>DuckDB</code> 进行分析.</p>
<p>在准备数据阶段有 3 个必要步骤</p>
<ol type="1">
<li>三方接口的翻页处理</li>
<li>提取接口返回数据中的有效字段</li>
<li>清洗数据,空值和类型等处理</li>
</ol>
<p>下面展示使用 <code>blockscout</code> API<sup>1</sup>, 通过 <code>DuckDB UDF</code><sup>2</sup> 和 <code>DuckDB Macro</code><sup>3</sup> 简化这个过程.</p>
<p>导入相关依赖</p>
<div id="7ef32ee6" class="cell" data-execution_count="1">
<details class="code-fold">
<summary>代码</summary>
<div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> duckdb</span>
<span id="cb1-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> requests</span>
<span id="cb1-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> duckdb.typing <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> VARCHAR, INTEGER, DuckDBPyType</span>
<span id="cb1-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> json</span></code></pre></div>
</details>
</div>
<section id="方案1" class="level2">
<h2 class="anchored" data-anchor-id="方案1">方案1</h2>
<p>通过 <code>blockscout</code> API<sup>4</sup> 获取 <code>ETH</code> 地址的交易信息, 代码如下:</p>
<div id="d55a89e8" class="cell" data-execution_count="2">
<details class="code-fold">
<summary>代码</summary>
<div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> blockscout_api(module: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>, action: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>, address: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>, start_block: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, end_block: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, page: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, offset: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>]:</span>
<span id="cb2-2">    url_prefix <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f'https://eth.blockscout.com/api?module=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>module<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">&amp;action=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>action<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">'</span></span>
<span id="cb2-3">    </span>
<span id="cb2-4">    result <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb2-5">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>:</span>
<span id="cb2-6">        url <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>url_prefix<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">&amp;address=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>address<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">&amp;startblock=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>start_block<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">&amp;endblock=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>end_block<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">&amp;page=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>page<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">&amp;offset=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>offset<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">&amp;sort=asc'</span></span>
<span id="cb2-7">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f'query page </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>page<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">'</span>)</span>
<span id="cb2-8">        data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> requests.get(url).json()</span>
<span id="cb2-9">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'message'</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'OK'</span>:</span>
<span id="cb2-10">            items <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'result'</span>]</span>
<span id="cb2-11">            result.extend(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">map</span>(json.dumps,items))</span>
<span id="cb2-12">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb2-13">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">break</span></span>
<span id="cb2-14">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(items) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> offset:</span>
<span id="cb2-15">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">break</span></span>
<span id="cb2-16">        page <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb2-17">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> result</span></code></pre></div>
</details>
</div>
<p>注册 <code>DuckDB</code> 的自定义函数</p>
<div id="928250b7" class="cell" data-execution_count="3">
<div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1">conn <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> duckdb.<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">connect</span>()</span>
<span id="cb3-2">conn <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> conn.create_function(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'blockscout_api'</span>, blockscout_api)</span></code></pre></div>
</div>
<p>定义 <code>DuckDB</code> 的宏, 这里为了演示, 限制 page 和 offset, 实际使用时会根据实际情况调整. 注意下面的输出 <code>query page 1</code> 和 <code>query page 2</code></p>
<div id="1c1cce87" class="cell" data-execution_count="4">
<div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1">conn.execute(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb4-2"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">CREATE OR REPLACE MACRO blockscout_trxs(address, start_block, end_block) as table </span></span>
<span id="cb4-3"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    select blockscout_api('account', 'txlist', address, start_block, end_block, 1, 2) as data</span></span>
<span id="cb4-4"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span>)</span></code></pre></div>
</div>
<p>查询 <code>ETH</code> 地址的交易信息</p>
<div id="63a3ff16" class="cell" data-execution_count="5">
<div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1">conn.execute(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb5-2"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">with raw_transactions as (</span></span>
<span id="cb5-3"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    select unnest(data) as trx from blockscout_trxs('0x603602E9A2ac7f1E26717C2b2193Fd68f5fafFf6', 20485198, 20490674)</span></span>
<span id="cb5-4"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">), decode_transactions as (</span></span>
<span id="cb5-5"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">select </span></span>
<span id="cb5-6"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    trx-&gt;'$.blockHash' as block_hash,</span></span>
<span id="cb5-7"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    (trx-&gt;'$.blockNumber')::integer as block_number,</span></span>
<span id="cb5-8"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    (trx-&gt;'$.timeStamp')::integer as timestamp,</span></span>
<span id="cb5-9"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    to_timestamp(timestamp) as datetime,</span></span>
<span id="cb5-10"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    trx-&gt;'$.hash' as hash,</span></span>
<span id="cb5-11"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    (trx-&gt;'$.transactionIndex')::integer as transaction_index,</span></span>
<span id="cb5-12"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    trx-&gt;'$.from' as 'from',</span></span>
<span id="cb5-13"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    trx-&gt;'$.to' as 'to',</span></span>
<span id="cb5-14"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    trx-&gt;'$.value' as value,</span></span>
<span id="cb5-15"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    trx-&gt;'$.contractAddress' as contract_address,</span></span>
<span id="cb5-16"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    (trx-&gt;'$.gas')::integer as gas,</span></span>
<span id="cb5-17"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    (trx-&gt;'$.gasPrice')::bigint as gas_price,</span></span>
<span id="cb5-18"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    (trx-&gt;'$.gasUsed')::integer as gas_used,</span></span>
<span id="cb5-19"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    trx-&gt;'$.isError' as is_error,</span></span>
<span id="cb5-20"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    trx-&gt;'$.txreceipt_status' as txreceipt_status,</span></span>
<span id="cb5-21"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    trx-&gt;'input' as 'input'</span></span>
<span id="cb5-22"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">from raw_transactions</span></span>
<span id="cb5-23"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb5-24"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">select </span></span>
<span id="cb5-25"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  block_number,</span></span>
<span id="cb5-26"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  datetime,</span></span>
<span id="cb5-27"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  hash,</span></span>
<span id="cb5-28"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  'from',</span></span>
<span id="cb5-29"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  'to',</span></span>
<span id="cb5-30"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  value,</span></span>
<span id="cb5-31"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">from decode_transactions</span></span>
<span id="cb5-32"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span>).df()</span></code></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>query page 1
query page 2</code></pre>
</div>
<div class="cell-output cell-output-display" data-execution_count="5">
<div>


<table class="dataframe caption-top table table-sm table-striped small" data-quarto-postprocess="true" data-border="1">
<thead>
<tr class="header">
<th data-quarto-table-cell-role="th"></th>
<th data-quarto-table-cell-role="th">block_number</th>
<th data-quarto-table-cell-role="th">datetime</th>
<th data-quarto-table-cell-role="th">hash</th>
<th data-quarto-table-cell-role="th">'from'</th>
<th data-quarto-table-cell-role="th">'to'</th>
<th data-quarto-table-cell-role="th">value</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td data-quarto-table-cell-role="th">0</td>
<td>20485198</td>
<td>2024-08-08 16:55:23+00:00</td>
<td>"0x16e9d0643ce6bf9bc59d5e6c756a196af2941cefc46...</td>
<td>from</td>
<td>to</td>
<td>"500000000000000000"</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">1</td>
<td>20488106</td>
<td>2024-08-09 02:38:47+00:00</td>
<td>"0x3f29ab5ba5779df75aee038cb9d529ab7d7e94ff727...</td>
<td>from</td>
<td>to</td>
<td>"500000000000000000"</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">2</td>
<td>20490674</td>
<td>2024-08-09 11:14:23+00:00</td>
<td>"0xcba85af304112c712c978968ff19fb150cdfd18e1f4...</td>
<td>from</td>
<td>to</td>
<td>"200000000000000000"</td>
</tr>
</tbody>
</table>

</div>
</div>
</div>
</section>
<section id="方案2" class="level2">
<h2 class="anchored" data-anchor-id="方案2">方案2</h2>
<p>实际使用时候, 三方 API 字段可能会调整, 返回的数据会存在空值情况, 因此必须要<strong>严格字段约束</strong>, 这里也演示下.</p>
<p>申明需要的字段与类型</p>
<div id="92ab5aa7" class="cell" data-execution_count="6">
<div class="sourceCode cell-code" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1">fields <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {</span>
<span id="cb7-2">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'blockHash'</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>,</span>
<span id="cb7-3">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'blockNumber'</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>,</span>
<span id="cb7-4">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'timeStamp'</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>,</span>
<span id="cb7-5">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'hash'</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>,</span>
<span id="cb7-6">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'transactionIndex'</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>,</span>
<span id="cb7-7">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'from'</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>,</span>
<span id="cb7-8">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'to'</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>,</span>
<span id="cb7-9">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'value'</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>,</span>
<span id="cb7-10">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'contractAddress'</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>,</span>
<span id="cb7-11">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'gas'</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>,</span>
<span id="cb7-12">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'gasPrice'</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>,</span>
<span id="cb7-13">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'gasUsed'</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>,</span>
<span id="cb7-14">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'isError'</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>,</span>
<span id="cb7-15">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'txreceipt_status'</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>,</span>
<span id="cb7-16">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'input'</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>,</span>
<span id="cb7-17">}</span></code></pre></div>
</div>
<p>请求 <code>blockscout</code> API<sup>5</sup> 并提取有效字段</p>
<div id="34a4c931" class="cell" data-execution_count="7">
<details class="code-fold">
<summary>代码</summary>
<div class="sourceCode cell-code" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb8-1">field_keys <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> fields.keys()</span>
<span id="cb8-2"></span>
<span id="cb8-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> blockscout_api_with_fields(module: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>, action: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>, address: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>, start_block: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, end_block: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, page: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, offset: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>):</span>
<span id="cb8-4">    url_prefix <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f'https://eth.blockscout.com/api?module=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>module<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">&amp;action=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>action<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">'</span></span>
<span id="cb8-5">    result <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb8-6">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>:</span>
<span id="cb8-7">        url <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>url_prefix<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">&amp;address=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>address<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">&amp;startblock=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>start_block<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">&amp;endblock=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>end_block<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">&amp;page=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>page<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">&amp;offset=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>offset<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">&amp;sort=asc'</span></span>
<span id="cb8-8">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f'query page </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>page<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> -&gt; </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>url<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">'</span>)</span>
<span id="cb8-9">        resp <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> requests.get(url).json()</span>
<span id="cb8-10">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> resp[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'message'</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'OK'</span>:</span>
<span id="cb8-11">            items <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> resp[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'result'</span>]</span>
<span id="cb8-12">            result.extend([{f: i[f] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> f <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> field_keys} <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> items])</span>
<span id="cb8-13">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(items) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> offset:</span>
<span id="cb8-14">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">break</span></span>
<span id="cb8-15">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb8-16">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">break</span></span>
<span id="cb8-17">        page <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb8-18">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> result</span></code></pre></div>
</details>
</div>
<p>注册 <code>DuckDB</code> 的自定义函数, 注意 <code>page</code> 和 <code>offset</code> 调整, 只获取 1 页面数据, 不做翻页演示.</p>
<div id="57aa9690" class="cell" data-execution_count="8">
<details class="code-fold">
<summary>代码</summary>
<div class="sourceCode cell-code" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb9-1">conn <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> duckdb.<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">connect</span>()</span>
<span id="cb9-2">conn <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> conn.create_function(blockscout_api_with_fields.<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">__name__</span>, blockscout_api_with_fields, [VARCHAR, VARCHAR, VARCHAR, INTEGER, INTEGER, INTEGER, INTEGER], DuckDBPyType(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>[fields]))</span>
<span id="cb9-3">conn.execute(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb9-4"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">CREATE OR REPLACE MACRO blockscout_trxs_with_fields(address, start_block, end_block) as table </span></span>
<span id="cb9-5"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    select blockscout_api_with_fields('account', 'txlist', address, start_block, end_block, 1, 5) as data</span></span>
<span id="cb9-6"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span>)</span></code></pre></div>
</details>
</div>
<p>查询 <code>ETH</code> 地址的交易信息</p>
<div id="8b90d921" class="cell" data-execution_count="9">
<div class="sourceCode cell-code" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb10-1">conn.execute(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb10-2"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">with raw_transactions as (</span></span>
<span id="cb10-3"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    select unnest(data) as trx from blockscout_trxs_with_fields('0x603602E9A2ac7f1E26717C2b2193Fd68f5fafFf6', 20485198, 20490674)</span></span>
<span id="cb10-4"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">), flatten_transactions as (</span></span>
<span id="cb10-5"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  select unnest(trx) from raw_transactions</span></span>
<span id="cb10-6"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb10-7"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">select </span></span>
<span id="cb10-8"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  blockNumber as block_number,</span></span>
<span id="cb10-9"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  to_timestamp(timeStamp) as datetime,</span></span>
<span id="cb10-10"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  hash,</span></span>
<span id="cb10-11"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  'from',</span></span>
<span id="cb10-12"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  'to',</span></span>
<span id="cb10-13"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  value</span></span>
<span id="cb10-14"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">from flatten_transactions</span></span>
<span id="cb10-15"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span>).df()</span></code></pre></div>
<div class="cell-output cell-output-stdout">
<pre><code>query page 1 -&gt; https://eth.blockscout.com/api?module=account&amp;action=txlist&amp;address=0x603602E9A2ac7f1E26717C2b2193Fd68f5fafFf6&amp;startblock=20485198&amp;endblock=20490674&amp;page=1&amp;offset=5&amp;sort=asc</code></pre>
</div>
<div class="cell-output cell-output-display" data-execution_count="9">
<div>


<table class="dataframe caption-top table table-sm table-striped small" data-quarto-postprocess="true" data-border="1">
<thead>
<tr class="header">
<th data-quarto-table-cell-role="th"></th>
<th data-quarto-table-cell-role="th">block_number</th>
<th data-quarto-table-cell-role="th">datetime</th>
<th data-quarto-table-cell-role="th">hash</th>
<th data-quarto-table-cell-role="th">'from'</th>
<th data-quarto-table-cell-role="th">'to'</th>
<th data-quarto-table-cell-role="th">value</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td data-quarto-table-cell-role="th">0</td>
<td>20485198</td>
<td>2024-08-08 16:55:23+00:00</td>
<td>0x16e9d0643ce6bf9bc59d5e6c756a196af2941cefc467...</td>
<td>from</td>
<td>to</td>
<td>500000000000000000</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">1</td>
<td>20488106</td>
<td>2024-08-09 02:38:47+00:00</td>
<td>0x3f29ab5ba5779df75aee038cb9d529ab7d7e94ff7277...</td>
<td>from</td>
<td>to</td>
<td>500000000000000000</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">2</td>
<td>20490674</td>
<td>2024-08-09 11:14:23+00:00</td>
<td>0xcba85af304112c712c978968ff19fb150cdfd18e1f48...</td>
<td>from</td>
<td>to</td>
<td>200000000000000000</td>
</tr>
</tbody>
</table>

</div>
</div>
</div>
</section>
</section>
<section id="总结" class="level1">
<h1>总结</h1>
<p>通过 <code>DuckDB</code> 的 <code>UDF</code><sup>6</sup> 和 <code>Macro</code><sup>7</sup> 可以简化数据处理的过程, 使得数据处理更加高效, 代码更加简洁. 日常使用, 分析阶段推荐大家使用方案 1 快速验证, 发布生产环境推荐大家使用方案2, 严格字段约束, 避免数据处理过程中出现问题.</p>
<p>另外, 也可以通过 <code>with recursive</code><sup>8</sup> 实现翻页查询, 但是这个过程比较复杂, <code>SQL</code> 写的更多, 不推荐使用, 如果大家感兴趣后续有时间会写篇文章分享.</p>
</section>
<section id="references" class="level1">
<h1>references</h1>


</section>


<div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">脚注</h2>

<ol>
<li id="fn1"><p><a href="https://docs.blockscout.com/for-users/api/rpc-endpoints/account#get-transactions-by-address">Get Transactions By Address - Blockscout</a>↩︎</p></li>
<li id="fn2"><p><a href="https://duckdb.org/docs/api/python/function">Python Runction API - DuckDB</a>↩︎</p></li>
<li id="fn3"><p><a href="https://duckdb.org/docs/sql/statements/create_macro">CREATE MACRO Statement - DuckDB</a>↩︎</p></li>
<li id="fn4"><p><a href="https://docs.blockscout.com/for-users/api/rpc-endpoints/account#get-transactions-by-address">Get Transactions By Address - Blockscout</a>↩︎</p></li>
<li id="fn5"><p><a href="https://docs.blockscout.com/for-users/api/rpc-endpoints/account#get-transactions-by-address">Get Transactions By Address - Blockscout</a>↩︎</p></li>
<li id="fn6"><p><a href="https://duckdb.org/docs/api/python/function">Python Runction API - DuckDB</a>↩︎</p></li>
<li id="fn7"><p><a href="https://duckdb.org/docs/sql/statements/create_macro">CREATE MACRO Statement - DuckDB</a>↩︎</p></li>
<li id="fn8"><p><a href="https://duckdb.org/docs/sql/query_syntax/with#recursive-ctes">With Clause - DuckDB</a>↩︎</p></li>
</ol>
</section><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">二次使用</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/deed.zh">CC BY-SA 4.0</a></div></div></section></div> ]]></description>
  <category>duckdb</category>
  <category>blockchain</category>
  <category>analysis</category>
  <guid>https://watsy0007.com/blog/analyzing_blockchain_data_with_duckdb_1/</guid>
  <pubDate>Sat, 10 Aug 2024 00:00:00 GMT</pubDate>
  <media:content url="https://watsy0007.com/blog/analyzing_blockchain_data_with_duckdb_1/analyse-blockchain-data-thumbnail.webp" medium="image" type="image/webp"/>
</item>
<item>
  <title>find missing dates with DuckDB</title>
  <link>https://watsy0007.com/blog/find_missing_dates_with_duckdb/</link>
  <description><![CDATA[ 





<section id="需求背景" class="level1">
<h1>需求背景</h1>
<p>近期业务反馈存在数据缺失的情况, 需要定位出缺失的日期, 以便补充历史数据.</p>
<p>经过调研决定使用 Gantt 图展示所有任务的每日执行.</p>
</section>
<section id="duckdb-登场" class="level1">
<h1>DuckDB 登场</h1>
<p>考虑到任务数据已经存在 <code>Postgres</code> 中, 而且需要可视化, <code>DuckDB</code> 有 <code>Postgres</code> 的插件可以直接获取数据 因此选择使用 <code>DuckDB</code> 配合 <code>Jupyter Notebook</code> 可以快速验证逻辑.</p>
</section>
<section id="实际操作" class="level1">
<h1>实际操作</h1>
<p><code>DuckDB</code> 操作 <code>Postgres</code> 的部分可以参考 <a href="https://duckdb.org/docs/extensions/postgres">PostgreSQL Extension</a></p>
<p>省去掉不想关的代码, 代码逻辑分成几个步骤</p>
<section id="获取数据" class="level2">
<h2 class="anchored" data-anchor-id="获取数据">1. 获取数据</h2>
<p>这里使用 mock 数据</p>
<div id="83bb9932" class="cell" data-execution_count="2">
<details class="code-fold">
<summary>代码</summary>
<div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> duckdb</span>
<span id="cb1-2"></span>
<span id="cb1-3">conn <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> duckdb.<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">connect</span>()</span>
<span id="cb1-4"></span>
<span id="cb1-5">mock_data_sql <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb1-6"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">SELECT * FROM (</span></span>
<span id="cb1-7"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    VALUES </span></span>
<span id="cb1-8"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (1, DATE '2024-07-01'),</span></span>
<span id="cb1-9"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (1, DATE '2024-07-02'),</span></span>
<span id="cb1-10"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (1, DATE '2024-07-03'),</span></span>
<span id="cb1-11"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (1, DATE '2024-07-05'),</span></span>
<span id="cb1-12"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (1, DATE '2024-07-06'),</span></span>
<span id="cb1-13"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (2, DATE '2024-07-01'),</span></span>
<span id="cb1-14"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (2, DATE '2024-07-02'),</span></span>
<span id="cb1-15"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (2, DATE '2024-07-03'),</span></span>
<span id="cb1-16"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-01'),</span></span>
<span id="cb1-17"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-02'),</span></span>
<span id="cb1-18"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-03'),</span></span>
<span id="cb1-19"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-04'),</span></span>
<span id="cb1-20"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-05'),</span></span>
<span id="cb1-21"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-06'),</span></span>
<span id="cb1-22"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-07'),</span></span>
<span id="cb1-23"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-08'),</span></span>
<span id="cb1-24"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-09'),</span></span>
<span id="cb1-25"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (4, DATE '2024-07-05'),</span></span>
<span id="cb1-26"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (4, DATE '2024-07-06'),</span></span>
<span id="cb1-27"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (4, DATE '2024-07-07'),</span></span>
<span id="cb1-28"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (4, DATE '2024-07-08'),</span></span>
<span id="cb1-29"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (4, DATE '2024-07-09'),</span></span>
<span id="cb1-30"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  ) AS t(source_id, end_date)</span></span>
<span id="cb1-31"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb1-32">df <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> conn.execute(mock_data_sql).df()</span>
<span id="cb1-33">df</span></code></pre></div>
</details>
<div class="cell-output cell-output-display" data-execution_count="2">
<div>


<table class="dataframe caption-top table table-sm table-striped small" data-quarto-postprocess="true" data-border="1">
<thead>
<tr class="header">
<th data-quarto-table-cell-role="th"></th>
<th data-quarto-table-cell-role="th">source_id</th>
<th data-quarto-table-cell-role="th">end_date</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td data-quarto-table-cell-role="th">0</td>
<td>1</td>
<td>2024-07-01</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">1</td>
<td>1</td>
<td>2024-07-02</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">2</td>
<td>1</td>
<td>2024-07-03</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">3</td>
<td>1</td>
<td>2024-07-05</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">4</td>
<td>1</td>
<td>2024-07-06</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">5</td>
<td>2</td>
<td>2024-07-01</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">6</td>
<td>2</td>
<td>2024-07-02</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">7</td>
<td>2</td>
<td>2024-07-03</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">8</td>
<td>3</td>
<td>2024-07-01</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">9</td>
<td>3</td>
<td>2024-07-02</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">10</td>
<td>3</td>
<td>2024-07-03</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">11</td>
<td>3</td>
<td>2024-07-04</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">12</td>
<td>3</td>
<td>2024-07-05</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">13</td>
<td>3</td>
<td>2024-07-06</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">14</td>
<td>3</td>
<td>2024-07-07</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">15</td>
<td>3</td>
<td>2024-07-08</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">16</td>
<td>3</td>
<td>2024-07-09</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">17</td>
<td>4</td>
<td>2024-07-05</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">18</td>
<td>4</td>
<td>2024-07-06</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">19</td>
<td>4</td>
<td>2024-07-07</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">20</td>
<td>4</td>
<td>2024-07-08</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">21</td>
<td>4</td>
<td>2024-07-09</td>
</tr>
</tbody>
</table>

</div>
</div>
</div>
</section>
<section id="对时间进行分组" class="level2">
<h2 class="anchored" data-anchor-id="对时间进行分组">2. 对时间进行分组</h2>
<p>寻找缺失的日期, 主要难点在于按照时间连续性进行分组, 连续的时间放在同一个分组中, 这样如果一个 <code>source_id</code> 有多个时间段, 那么表示存在时间空缺.</p>
<p>使用窗口函数, 对当前行的时间进行处理, 根据时间差, 获得当前行的分组时间. 代码如下:</p>
<div id="7b5a461b" class="cell" data-execution_count="3">
<div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1">group_date_sql <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb2-2"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"> SELECT </span></span>
<span id="cb2-3"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    source_id,</span></span>
<span id="cb2-4"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    end_date,</span></span>
<span id="cb2-5"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    end_date - INTERVAL (ROW_NUMBER() OVER (PARTITION BY source_id ORDER BY end_date) - 1) DAY AS group_date</span></span>
<span id="cb2-6"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  FROM df</span></span>
<span id="cb2-7"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  order by source_id, end_date</span></span>
<span id="cb2-8"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb2-9">grouped_date_df <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> conn.execute(group_date_sql).df()</span>
<span id="cb2-10">grouped_date_df</span></code></pre></div>
<div class="cell-output cell-output-display" data-execution_count="3">
<div>


<table class="dataframe caption-top table table-sm table-striped small" data-quarto-postprocess="true" data-border="1">
<thead>
<tr class="header">
<th data-quarto-table-cell-role="th"></th>
<th data-quarto-table-cell-role="th">source_id</th>
<th data-quarto-table-cell-role="th">end_date</th>
<th data-quarto-table-cell-role="th">group_date</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td data-quarto-table-cell-role="th">0</td>
<td>1</td>
<td>2024-07-01</td>
<td>2024-07-01</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">1</td>
<td>1</td>
<td>2024-07-02</td>
<td>2024-07-01</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">2</td>
<td>1</td>
<td>2024-07-03</td>
<td>2024-07-01</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">3</td>
<td>1</td>
<td>2024-07-05</td>
<td>2024-07-02</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">4</td>
<td>1</td>
<td>2024-07-06</td>
<td>2024-07-02</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">5</td>
<td>2</td>
<td>2024-07-01</td>
<td>2024-07-01</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">6</td>
<td>2</td>
<td>2024-07-02</td>
<td>2024-07-01</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">7</td>
<td>2</td>
<td>2024-07-03</td>
<td>2024-07-01</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">8</td>
<td>3</td>
<td>2024-07-01</td>
<td>2024-07-01</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">9</td>
<td>3</td>
<td>2024-07-02</td>
<td>2024-07-01</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">10</td>
<td>3</td>
<td>2024-07-03</td>
<td>2024-07-01</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">11</td>
<td>3</td>
<td>2024-07-04</td>
<td>2024-07-01</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">12</td>
<td>3</td>
<td>2024-07-05</td>
<td>2024-07-01</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">13</td>
<td>3</td>
<td>2024-07-06</td>
<td>2024-07-01</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">14</td>
<td>3</td>
<td>2024-07-07</td>
<td>2024-07-01</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">15</td>
<td>3</td>
<td>2024-07-08</td>
<td>2024-07-01</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">16</td>
<td>3</td>
<td>2024-07-09</td>
<td>2024-07-01</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">17</td>
<td>4</td>
<td>2024-07-05</td>
<td>2024-07-05</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">18</td>
<td>4</td>
<td>2024-07-06</td>
<td>2024-07-05</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">19</td>
<td>4</td>
<td>2024-07-07</td>
<td>2024-07-05</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">20</td>
<td>4</td>
<td>2024-07-08</td>
<td>2024-07-05</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">21</td>
<td>4</td>
<td>2024-07-09</td>
<td>2024-07-05</td>
</tr>
</tbody>
</table>

</div>
</div>
</div>
</section>
<section id="根据-source_id-和分组时间分组" class="level2">
<h2 class="anchored" data-anchor-id="根据-source_id-和分组时间分组">3. 根据 source_id 和分组时间分组</h2>
<div id="34c4632d" class="cell" data-execution_count="4">
<div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1">group_sql <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb3-2"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">SELECT </span></span>
<span id="cb3-3"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    source_id,</span></span>
<span id="cb3-4"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    MIN(end_date) AS start_date,</span></span>
<span id="cb3-5"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    MAX(end_date) AS end_date,</span></span>
<span id="cb3-6"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  FROM grouped_date_df</span></span>
<span id="cb3-7"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  GROUP BY source_id, group_date</span></span>
<span id="cb3-8"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  ORDER BY source_id, group_date</span></span>
<span id="cb3-9"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb3-10">grouped_df <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> conn.execute(group_sql).df()</span>
<span id="cb3-11">grouped_df</span></code></pre></div>
<div class="cell-output cell-output-display" data-execution_count="4">
<div>


<table class="dataframe caption-top table table-sm table-striped small" data-quarto-postprocess="true" data-border="1">
<thead>
<tr class="header">
<th data-quarto-table-cell-role="th"></th>
<th data-quarto-table-cell-role="th">source_id</th>
<th data-quarto-table-cell-role="th">start_date</th>
<th data-quarto-table-cell-role="th">end_date</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td data-quarto-table-cell-role="th">0</td>
<td>1</td>
<td>2024-07-01</td>
<td>2024-07-03</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">1</td>
<td>1</td>
<td>2024-07-05</td>
<td>2024-07-06</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">2</td>
<td>2</td>
<td>2024-07-01</td>
<td>2024-07-03</td>
</tr>
<tr class="even">
<td data-quarto-table-cell-role="th">3</td>
<td>3</td>
<td>2024-07-01</td>
<td>2024-07-09</td>
</tr>
<tr class="odd">
<td data-quarto-table-cell-role="th">4</td>
<td>4</td>
<td>2024-07-05</td>
<td>2024-07-09</td>
</tr>
</tbody>
</table>

</div>
</div>
</div>
</section>
<section id="可视化" class="level2">
<h2 class="anchored" data-anchor-id="可视化">4. 可视化</h2>
<div id="884715b7" class="cell" data-execution_count="5">
<details class="code-fold">
<summary>代码</summary>
<div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> plotly.express <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> px</span>
<span id="cb4-2">fig <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> px.timeline(grouped_df, x_start<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'start_date'</span>, x_end<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'end_date'</span>, y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'source_id'</span>)</span>
<span id="cb4-3">fig.update_yaxes(autorange<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"reversed"</span>)</span>
<span id="cb4-4">fig.show()</span></code></pre></div>
</details>
<div class="cell-output cell-output-display">
<div>                            <div id="c63ae8c4-f88d-4844-86d2-1c3250cf558d" class="plotly-graph-div" style="height:525px; width:100%;"></div>            <script type="text/javascript">                require(["plotly"], function(Plotly) {                    window.PLOTLYENV=window.PLOTLYENV || {};                                    if (document.getElementById("c63ae8c4-f88d-4844-86d2-1c3250cf558d")) {                    Plotly.newPlot(                        "c63ae8c4-f88d-4844-86d2-1c3250cf558d",                        [{"alignmentgroup":"True","base":["2024-07-01T00:00:00","2024-07-05T00:00:00","2024-07-01T00:00:00","2024-07-01T00:00:00","2024-07-05T00:00:00"],"hovertemplate":"start_date=%{base}\u003cbr\u003eend_date=%{x}\u003cbr\u003esource_id=%{y}\u003cextra\u003e\u003c\u002fextra\u003e","legendgroup":"","marker":{"color":"#636efa","pattern":{"shape":""}},"name":"","offsetgroup":"","orientation":"h","showlegend":false,"textposition":"auto","x":[172800000.0,86400000.0,172800000.0,691200000.0,345600000.0],"xaxis":"x","y":[1,1,2,3,4],"yaxis":"y","type":"bar"}],                        {"template":{"data":{"histogram2dcontour":[{"type":"histogram2dcontour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"choropleth":[{"type":"choropleth","colorbar":{"outlinewidth":0,"ticks":""}}],"histogram2d":[{"type":"histogram2d","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmap":[{"type":"heatmap","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmapgl":[{"type":"heatmapgl","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"contourcarpet":[{"type":"contourcarpet","colorbar":{"outlinewidth":0,"ticks":""}}],"contour":[{"type":"contour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"surface":[{"type":"surface","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"mesh3d":[{"type":"mesh3d","colorbar":{"outlinewidth":0,"ticks":""}}],"scatter":[{"fillpattern":{"fillmode":"overlay","size":10,"solidity":0.2},"type":"scatter"}],"parcoords":[{"type":"parcoords","line":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolargl":[{"type":"scatterpolargl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"bar":[{"error_x":{"color":"#2a3f5f"},"error_y":{"color":"#2a3f5f"},"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"scattergeo":[{"type":"scattergeo","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolar":[{"type":"scatterpolar","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"scattergl":[{"type":"scattergl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatter3d":[{"type":"scatter3d","line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattermapbox":[{"type":"scattermapbox","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterternary":[{"type":"scatterternary","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattercarpet":[{"type":"scattercarpet","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"carpet":[{"aaxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"baxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"type":"carpet"}],"table":[{"cells":{"fill":{"color":"#EBF0F8"},"line":{"color":"white"}},"header":{"fill":{"color":"#C8D4E3"},"line":{"color":"white"}},"type":"table"}],"barpolar":[{"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"pie":[{"automargin":true,"type":"pie"}]},"layout":{"autotypenumbers":"strict","colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#2a3f5f"},"hovermode":"closest","hoverlabel":{"align":"left"},"paper_bgcolor":"white","plot_bgcolor":"#E5ECF6","polar":{"bgcolor":"#E5ECF6","angularaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"radialaxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"ternary":{"bgcolor":"#E5ECF6","aaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"baxis":{"gridcolor":"white","linecolor":"white","ticks":""},"caxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]]},"xaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"yaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"scene":{"xaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"yaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"zaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2}},"shapedefaults":{"line":{"color":"#2a3f5f"}},"annotationdefaults":{"arrowcolor":"#2a3f5f","arrowhead":0,"arrowwidth":1},"geo":{"bgcolor":"white","landcolor":"#E5ECF6","subunitcolor":"white","showland":true,"showlakes":true,"lakecolor":"white"},"title":{"x":0.05},"mapbox":{"style":"light"},"margin":{"b":0,"l":0,"r":0,"t":30}}},"xaxis":{"anchor":"y","domain":[0.0,1.0],"type":"date"},"yaxis":{"anchor":"x","domain":[0.0,1.0],"title":{"text":"source_id"},"autorange":"reversed"},"legend":{"tracegroupgap":0},"barmode":"overlay"},                        {"responsive": true}                    ).then(function(){
                            
var gd = document.getElementById('c63ae8c4-f88d-4844-86d2-1c3250cf558d');
var x = new MutationObserver(function (mutations, observer) {{
        var display = window.getComputedStyle(gd).display;
        if (!display || display === 'none') {{
            console.log([gd, 'removed!']);
            Plotly.purge(gd);
            observer.disconnect();
        }}
}});

// Listen for the removal of the full notebook cells
var notebookContainer = gd.closest('#notebook-container');
if (notebookContainer) {{
    x.observe(notebookContainer, {childList: true});
}}

// Listen for the clearing of the current output cell
var outputEl = gd.closest('.output');
if (outputEl) {{
    x.observe(outputEl, {childList: true});
}}

                        })                };                });            </script>        </div>
</div>
</div>
</section>
<section id="完整代码" class="level2">
<h2 class="anchored" data-anchor-id="完整代码">5. 完整代码</h2>
<div id="1b9c8ca4" class="cell" data-execution_count="6">
<div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1">sql <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb5-2"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">with raw_data as (</span></span>
<span id="cb5-3"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  SELECT * FROM (</span></span>
<span id="cb5-4"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    VALUES </span></span>
<span id="cb5-5"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (1, DATE '2024-07-01'),</span></span>
<span id="cb5-6"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (1, DATE '2024-07-02'),</span></span>
<span id="cb5-7"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (1, DATE '2024-07-03'),</span></span>
<span id="cb5-8"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (1, DATE '2024-07-05'),</span></span>
<span id="cb5-9"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (1, DATE '2024-07-06'),</span></span>
<span id="cb5-10"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (2, DATE '2024-07-01'),</span></span>
<span id="cb5-11"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (2, DATE '2024-07-02'),</span></span>
<span id="cb5-12"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (2, DATE '2024-07-03'),</span></span>
<span id="cb5-13"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-01'),</span></span>
<span id="cb5-14"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-02'),</span></span>
<span id="cb5-15"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-03'),</span></span>
<span id="cb5-16"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-04'),</span></span>
<span id="cb5-17"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-05'),</span></span>
<span id="cb5-18"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-06'),</span></span>
<span id="cb5-19"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-07'),</span></span>
<span id="cb5-20"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-08'),</span></span>
<span id="cb5-21"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (3, DATE '2024-07-09'),</span></span>
<span id="cb5-22"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (4, DATE '2024-07-05'),</span></span>
<span id="cb5-23"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (4, DATE '2024-07-06'),</span></span>
<span id="cb5-24"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (4, DATE '2024-07-07'),</span></span>
<span id="cb5-25"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (4, DATE '2024-07-08'),</span></span>
<span id="cb5-26"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">      (4, DATE '2024-07-09'),</span></span>
<span id="cb5-27"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  ) AS t(source_id, end_date)</span></span>
<span id="cb5-28"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">), group_date as (</span></span>
<span id="cb5-29"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  SELECT </span></span>
<span id="cb5-30"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    source_id,</span></span>
<span id="cb5-31"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    end_date,</span></span>
<span id="cb5-32"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    end_date - INTERVAL (ROW_NUMBER() OVER (PARTITION BY source_id ORDER BY end_date) - 1) DAY AS group_date</span></span>
<span id="cb5-33"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  FROM raw_data</span></span>
<span id="cb5-34"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  order by source_id, end_date</span></span>
<span id="cb5-35"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">), final as (</span></span>
<span id="cb5-36"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  SELECT </span></span>
<span id="cb5-37"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    source_id,</span></span>
<span id="cb5-38"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    MIN(end_date) AS start_date,</span></span>
<span id="cb5-39"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    MAX(end_date) AS end_date,</span></span>
<span id="cb5-40"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  FROM grouped_date_df</span></span>
<span id="cb5-41"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  GROUP BY source_id, group_date</span></span>
<span id="cb5-42"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  ORDER BY source_id, group_date</span></span>
<span id="cb5-43"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb5-44"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">from final</span></span>
<span id="cb5-45"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb5-46">date_df <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> conn.execute(sql).df()</span>
<span id="cb5-47">gap_fig <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> px.timeline(date_df, x_start<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'start_date'</span>, x_end<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'end_date'</span>, y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'source_id'</span>)</span>
<span id="cb5-48">gap_fig.update_yaxes(autorange<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"reversed"</span>)</span>
<span id="cb5-49">gap_fig.show()</span></code></pre></div>
</div>
</section>
</section>
<section id="总结" class="level1">
<h1>总结</h1>
<p>本文主要介绍如何使用 DuckDB 查找缺失的日期, 通过窗口函数和分组函数, 可以快速定位出缺失的日期.</p>
<p>PS: 最近工作中使用 DuckDB 的频率越来越高, 究其原因是 DuckDB 轻量, 速度快, 配合 Jupyter Notebook 效率出奇的高.</p>


</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">二次使用</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/deed.zh">CC BY-SA 4.0</a></div></div></section></div> ]]></description>
  <category>code</category>
  <category>duckdb</category>
  <guid>https://watsy0007.com/blog/find_missing_dates_with_duckdb/</guid>
  <pubDate>Fri, 26 Jul 2024 00:00:00 GMT</pubDate>
  <media:content url="https://watsy0007.com/blog/find_missing_dates_with_duckdb/missing-dates-thumbnail.webp" medium="image" type="image/webp"/>
</item>
<item>
  <title>DuckDB Example</title>
  <link>https://watsy0007.com/blog/duckdb-example/</link>
  <description><![CDATA[ 





<p>基于 <code>quarto</code> 测试 <code>DuckDB</code> 功能。</p>
<p>以下所有代码可以复制到 jupyter notebook 中执行。</p>
<section id="安装依赖" class="level1">
<h1>安装依赖</h1>
<div id="3417ebb3" class="cell" data-execution_count="1">
<div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>pip install duckdb jupysql <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--</span>quiet</span></code></pre></div>
</div>
</section>
<section id="基础配置" class="level1">
<h1>基础配置</h1>
<div id="1101b857" class="cell" data-execution_count="2">
<div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> duckdb</span>
<span id="cb2-2"></span>
<span id="cb2-3">conn <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> duckdb.<span class="ex" style="color: null;
background-color: null;
font-style: inherit;">connect</span>()</span></code></pre></div>
</div>
<p>jupysql 配置</p>
<div id="82d0b1a7" class="cell" data-execution_count="3">
<div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span>load_ext sql</span>
<span id="cb3-2"></span>
<span id="cb3-3"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span>config SqlMagic.autopandas <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span></span>
<span id="cb3-4"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span>config SqlMagic.feedback <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb3-5"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span>config SqlMagic.displaycon <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span></span>
<span id="cb3-6"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span>config SqlMagic.displaylimit <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span></span>
<span id="cb3-7"></span>
<span id="cb3-8"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span>sql conn <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--</span>alias duckdb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>native</span></code></pre></div>
</div>
</section>
<section id="演示数据" class="level1">
<h1>演示数据</h1>
<p>参考 <a href="https://duckdb.org/docs/guides/python/jupyter">Jupyter Notebooks</a></p>
<div id="4837fcbd" class="cell" data-execution_count="4">
<div class="sourceCode cell-code" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%%</span>sql <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--</span>save short_trips <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--</span>no<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>execute</span>
<span id="cb4-2">SELECT <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span></span>
<span id="cb4-3">FROM <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2021-01.parquet'</span></span>
<span id="cb4-4">WHERE trip_distance <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">6.3</span></span></code></pre></div>
<div class="cell-output cell-output-display">
<span style="None">Skipping execution...</span>
</div>
</div>
<div id="a7960c12" class="cell" data-execution_count="5">
<div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%</span>sqlplot histogram <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--</span>table short_trips <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--</span>column trip_distance <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--</span>bins <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">with</span> short_trips</span></code></pre></div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><img src="https://watsy0007.com/blog/duckdb-example/index_files/figure-html/cell-6-output-1.png" width="618" height="449" class="figure-img"></p>
</figure>
</div>
</div>
</div>


</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">二次使用</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/deed.zh">CC BY-SA 4.0</a></div></div></section></div> ]]></description>
  <category>code</category>
  <category>duckdb</category>
  <guid>https://watsy0007.com/blog/duckdb-example/</guid>
  <pubDate>Sat, 17 Feb 2024 00:00:00 GMT</pubDate>
  <media:content url="https://watsy0007.com/blog/duckdb-example/duckdb-sql-analysis-thumbnail.webp" medium="image" type="image/webp"/>
</item>
</channel>
</rss>
