<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Non-Brand Data]]></title><description><![CDATA[Non-Brand Data helps data professionals learn how to use ML, GenAI, and analytics in their actual work through practical articles, guides, templates, and workflows.]]></description><link>https://www.nb-data.com</link><image><url>https://substackcdn.com/image/fetch/$s_!06DP!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0e1cde-d120-4029-8ffd-2a8c7c6e4504_1280x1280.png</url><title>Non-Brand Data</title><link>https://www.nb-data.com</link></image><generator>Substack</generator><lastBuildDate>Tue, 27 Oct 2026 06:03:33 GMT</lastBuildDate><atom:link href="https://www.nb-data.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Cornellius Yudha Wijaya]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[cornellius@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[cornellius@substack.com]]></itunes:email><itunes:name><![CDATA[Cornellius Yudha Wijaya]]></itunes:name></itunes:owner><itunes:author><![CDATA[Cornellius Yudha Wijaya]]></itunes:author><googleplay:owner><![CDATA[cornellius@substack.com]]></googleplay:owner><googleplay:email><![CDATA[cornellius@substack.com]]></googleplay:email><googleplay:author><![CDATA[Cornellius Yudha Wijaya]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[What Makes a Good Data Science Portfolio in the Age of AI?]]></title><description><![CDATA[Something you might overlook but important in the current era]]></description><link>https://www.nb-data.com/p/what-makes-a-good-data-science-portfolio</link><guid isPermaLink="false">https://www.nb-data.com/p/what-makes-a-good-data-science-portfolio</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Fri, 23 Oct 2026 03:30:05 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!iUoW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe77e0896-18cb-406c-9447-9049110cd862_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iUoW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe77e0896-18cb-406c-9447-9049110cd862_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iUoW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe77e0896-18cb-406c-9447-9049110cd862_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!iUoW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe77e0896-18cb-406c-9447-9049110cd862_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!iUoW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe77e0896-18cb-406c-9447-9049110cd862_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!iUoW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe77e0896-18cb-406c-9447-9049110cd862_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iUoW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe77e0896-18cb-406c-9447-9049110cd862_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e77e0896-18cb-406c-9447-9049110cd862_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;What Makes a Good Data Science Portfolio in the Age of AI?&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="What Makes a Good Data Science Portfolio in the Age of AI?" title="What Makes a Good Data Science Portfolio in the Age of AI?" srcset="https://substackcdn.com/image/fetch/$s_!iUoW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe77e0896-18cb-406c-9447-9049110cd862_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!iUoW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe77e0896-18cb-406c-9447-9049110cd862_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!iUoW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe77e0896-18cb-406c-9447-9049110cd862_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!iUoW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe77e0896-18cb-406c-9447-9049110cd862_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A Data Science portfolio is a collection of projects that we use to show our ability to work with data. We can share the work through a GitHub repository or create a website where someone can explore the projects we have developed.</p><p>Building the portfolio has become easier with AI as we can use it to help with the code and develop a project from our ideas. This is useful when we are learning a new method because we can spend more time exploring how it works without getting stuck on every part of the implementation.</p><p>However, the convenience also brings a question. If AI can help us build the project, what should we show in our Data Science portfolio?</p><p>For me, a good portfolio should explain how we approach the problem so someone can understand why we arrived at a particular result. Having a model with high accuracy would not provide much information if we cannot explain what the model predicts or whether the evaluation makes sense.</p><p>We don&#8217;t need to avoid AI to achieve this. We need to understand the work we present and be able to explain the decisions that affect the result.</p><p>In this article, we will explore five public examples to understand what we can include in our own portfolio. Some examples are portfolio websites, while others are individual projects that show how we can explain the work in more detail. We will use them as references without assuming how their authors used AI.</p><p>Let&#8217;s get into it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>1. Ken Jee&#8217;s Data Science Salary Estimator</h2><p>Let&#8217;s start with <a href="https://github.com/PlayingNumbers/Ken_Portfolio">Ken Jee&#8217;s GitHub portfolio</a>, which introduces his projects and links to the repositories where we can explore the implementation. One of the projects is a <a href="https://github.com/PlayingNumbers/ds_salary_proj">Data Science salary estimator</a> that uses information from job postings to estimate a salary.</p><p>The repository explains how information from job descriptions is prepared for modelling. For example, the project creates features indicating whether certain skills are mentioned in the description. This gives us something to examine because we can follow how the original text becomes information that the model can use.</p><p>The project compares the models using Mean Absolute Error or MAE, which measures the average size of the prediction error in the same units as the target. Let&#8217;s look at two results reported in the repository.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jE4U!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa615b1d7-5c3f-4e10-b90b-f340809ba209_1600x670.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jE4U!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa615b1d7-5c3f-4e10-b90b-f340809ba209_1600x670.png 424w, https://substackcdn.com/image/fetch/$s_!jE4U!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa615b1d7-5c3f-4e10-b90b-f340809ba209_1600x670.png 848w, https://substackcdn.com/image/fetch/$s_!jE4U!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa615b1d7-5c3f-4e10-b90b-f340809ba209_1600x670.png 1272w, https://substackcdn.com/image/fetch/$s_!jE4U!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa615b1d7-5c3f-4e10-b90b-f340809ba209_1600x670.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jE4U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa615b1d7-5c3f-4e10-b90b-f340809ba209_1600x670.png" width="1456" height="610" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a615b1d7-5c3f-4e10-b90b-f340809ba209_1600x670.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:610,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;What Makes a Good Data Science Portfolio in the Age of AI?&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="What Makes a Good Data Science Portfolio in the Age of AI?" title="What Makes a Good Data Science Portfolio in the Age of AI?" srcset="https://substackcdn.com/image/fetch/$s_!jE4U!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa615b1d7-5c3f-4e10-b90b-f340809ba209_1600x670.png 424w, https://substackcdn.com/image/fetch/$s_!jE4U!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa615b1d7-5c3f-4e10-b90b-f340809ba209_1600x670.png 848w, https://substackcdn.com/image/fetch/$s_!jE4U!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa615b1d7-5c3f-4e10-b90b-f340809ba209_1600x670.png 1272w, https://substackcdn.com/image/fetch/$s_!jE4U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa615b1d7-5c3f-4e10-b90b-f340809ba209_1600x670.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The repository reports an MAE of roughly $18,860 for Linear Regression and $11,220 for Random Forest. This is approximately a 40% reduction in average error relative to the baseline. Showing this comparison gives us a way to discuss whether the more complex model provides an improvement.</p><p>We would still need to examine how the evaluation was performed before accepting the result. A lower average error also does not mean that every prediction is within $11,220 of the recorded value.</p><p><strong>What should we pay attention to if we develop a similar project?</strong></p><p>We need to understand what the salary data represents. A salary estimate from a job posting is not necessarily the salary someone eventually receives. Our explanation should make this clear so the reader understands what we are predicting and how much they can conclude from it.</p><p>We could also examine whether the error is similar across job locations. If one location has much less data, the overall MAE might not describe how well the model works there. Comparing the errors alongside the number of examples would help us decide where more data is needed.</p><p>AI could help us write the feature extraction code for this project. We would still need to check some job descriptions manually to see whether the extracted information has the meaning we intended. Explaining this check would provide more information about our work than only mentioning that we used AI.</p><div><hr></div><h2>2. Julia Silge&#8217;s Animal Crossing Review Analysis</h2><p>The next example is Julia Silge&#8217;s <a href="https://juliasilge.com/blog/animal-crossing/">analysis of Animal Crossing reviews</a>. This is a project write-up where we can follow the process of using review text to predict whether the review is good or bad.</p><p>The article starts by examining the data before developing the model. Let&#8217;s look at the distribution of review lengths.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!G0Nl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa941ffda-d909-4696-acbd-155db119fb59_4800x3000.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!G0Nl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa941ffda-d909-4696-acbd-155db119fb59_4800x3000.png 424w, https://substackcdn.com/image/fetch/$s_!G0Nl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa941ffda-d909-4696-acbd-155db119fb59_4800x3000.png 848w, https://substackcdn.com/image/fetch/$s_!G0Nl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa941ffda-d909-4696-acbd-155db119fb59_4800x3000.png 1272w, https://substackcdn.com/image/fetch/$s_!G0Nl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa941ffda-d909-4696-acbd-155db119fb59_4800x3000.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!G0Nl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa941ffda-d909-4696-acbd-155db119fb59_4800x3000.png" width="1456" height="910" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a941ffda-d909-4696-acbd-155db119fb59_4800x3000.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:910,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;What Makes a Good Data Science Portfolio in the Age of AI?&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="What Makes a Good Data Science Portfolio in the Age of AI?" title="What Makes a Good Data Science Portfolio in the Age of AI?" srcset="https://substackcdn.com/image/fetch/$s_!G0Nl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa941ffda-d909-4696-acbd-155db119fb59_4800x3000.png 424w, https://substackcdn.com/image/fetch/$s_!G0Nl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa941ffda-d909-4696-acbd-155db119fb59_4800x3000.png 848w, https://substackcdn.com/image/fetch/$s_!G0Nl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa941ffda-d909-4696-acbd-155db119fb59_4800x3000.png 1272w, https://substackcdn.com/image/fetch/$s_!G0Nl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa941ffda-d909-4696-acbd-155db119fb59_4800x3000.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>There is a sharp dip around 100 words before the number of reviews rises again. Julia identifies the unusual shape as a possible scraping problem. The chart does not prove what caused it, but it gives us a reason to return to the collection process and check whether some reviews were cut short.</p><p>This is something we can include in our own portfolio. We can show the pattern that made us question the data and explain how we checked it before moving on to the model.</p><p>The article then compares model settings using several evaluation metrics.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Xrp8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F743c2019-eb19-4be1-a5db-11a56630c498_6000x3000.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Xrp8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F743c2019-eb19-4be1-a5db-11a56630c498_6000x3000.png 424w, https://substackcdn.com/image/fetch/$s_!Xrp8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F743c2019-eb19-4be1-a5db-11a56630c498_6000x3000.png 848w, https://substackcdn.com/image/fetch/$s_!Xrp8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F743c2019-eb19-4be1-a5db-11a56630c498_6000x3000.png 1272w, https://substackcdn.com/image/fetch/$s_!Xrp8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F743c2019-eb19-4be1-a5db-11a56630c498_6000x3000.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Xrp8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F743c2019-eb19-4be1-a5db-11a56630c498_6000x3000.png" width="1456" height="728" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/743c2019-eb19-4be1-a5db-11a56630c498_6000x3000.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:728,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;What Makes a Good Data Science Portfolio in the Age of AI?&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="What Makes a Good Data Science Portfolio in the Age of AI?" title="What Makes a Good Data Science Portfolio in the Age of AI?" srcset="https://substackcdn.com/image/fetch/$s_!Xrp8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F743c2019-eb19-4be1-a5db-11a56630c498_6000x3000.png 424w, https://substackcdn.com/image/fetch/$s_!Xrp8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F743c2019-eb19-4be1-a5db-11a56630c498_6000x3000.png 848w, https://substackcdn.com/image/fetch/$s_!Xrp8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F743c2019-eb19-4be1-a5db-11a56630c498_6000x3000.png 1272w, https://substackcdn.com/image/fetch/$s_!Xrp8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F743c2019-eb19-4be1-a5db-11a56630c498_6000x3000.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The horizontal axis shows the regularization penalty on a logarithmic scale. Increasing the penalty puts more pressure on the model to shrink its coefficients. We can see that the three metrics respond differently as the penalty becomes larger.</p><p>PPV measures how often positive predictions are correct, while NPV does the same for negative predictions. ROC AUC measures how well the model ranks the two classes across thresholds. Near the far right of the chart, NPV increases while PPV and ROC AUC fall sharply. Choosing the largest penalty because one metric improves would therefore miss what happens to the other results. Julia selects the final setting using ROC AUC.</p><p><strong>What can we do with an explanation like this?</strong></p><p>We can use the same approach when explaining our own classification project. For example, let&#8217;s say we want to predict which customers will cancel their subscriptions. We have 1,000 customers in the evaluation data, and 100 of them cancel.</p><p>If we predict that every customer stays, the accuracy would be 90% even though we have not identified any customer who cancels. This is why reporting accuracy alone would not explain whether the model is useful for finding cancellations.</p><p>Now, suppose the team can only contact 50 customers. We select the 50 customers with the highest predicted risk and find that 20 of them cancel. The precision for this selection would be 40% because 20 out of the 50 selected customers cancel. The recall would be 20% because we found 20 of the 100 cancellations in the evaluation data.</p><p>These are example numbers, but they show how we can connect the evaluation to the intended use. We should select the rule using validation data before reporting its performance on untouched test data. You can refer to the <a href="https://scikit-learn.org/stable/modules/model_evaluation.html#precision-recall-and-f-measures">Scikit-Learn documentation</a> for more information about the metrics.</p><p>We would still need further testing to know whether contacting these customers prevents cancellation. The prediction result only tells us how well we identify the customers who cancel, so our portfolio should explain this limitation as well.</p><div><hr></div><h2>3. Will Koehrsen&#8217;s Building Energy Project</h2><p>Will Koehrsen&#8217;s <a href="https://github.com/WillKoehrsen/machine-learning-project-walkthrough">Machine Learning project walkthrough</a> uses building data to predict an Energy Star Score. The work is divided into notebooks that explain the development process and includes a report in the repository.</p><p>One part I find useful is the <a href="https://github.com/WillKoehrsen/machine-learning-project-walkthrough/blob/master/Machine%20Learning%20Project%20Part%203.ipynb">model interpretation notebook</a>, where the author examines a prediction with a large error. Let&#8217;s look at the result below.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!e4MJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff18f81c5-a6a1-42ce-b043-93b640f5ce40_1600x700.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!e4MJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff18f81c5-a6a1-42ce-b043-93b640f5ce40_1600x700.png 424w, https://substackcdn.com/image/fetch/$s_!e4MJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff18f81c5-a6a1-42ce-b043-93b640f5ce40_1600x700.png 848w, https://substackcdn.com/image/fetch/$s_!e4MJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff18f81c5-a6a1-42ce-b043-93b640f5ce40_1600x700.png 1272w, https://substackcdn.com/image/fetch/$s_!e4MJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff18f81c5-a6a1-42ce-b043-93b640f5ce40_1600x700.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!e4MJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff18f81c5-a6a1-42ce-b043-93b640f5ce40_1600x700.png" width="1456" height="637" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f18f81c5-a6a1-42ce-b043-93b640f5ce40_1600x700.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:637,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;What Makes a Good Data Science Portfolio in the Age of AI?&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="What Makes a Good Data Science Portfolio in the Age of AI?" title="What Makes a Good Data Science Portfolio in the Age of AI?" srcset="https://substackcdn.com/image/fetch/$s_!e4MJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff18f81c5-a6a1-42ce-b043-93b640f5ce40_1600x700.png 424w, https://substackcdn.com/image/fetch/$s_!e4MJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff18f81c5-a6a1-42ce-b043-93b640f5ce40_1600x700.png 848w, https://substackcdn.com/image/fetch/$s_!e4MJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff18f81c5-a6a1-42ce-b043-93b640f5ce40_1600x700.png 1272w, https://substackcdn.com/image/fetch/$s_!e4MJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff18f81c5-a6a1-42ce-b043-93b640f5ce40_1600x700.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In this example, the model using a reduced set of features predicts approximately 12.86 while the recorded score is 100. The difference is large enough that we would want to investigate what happened instead of only reporting the average error.</p><p>The same model has a test MAE of approximately 10.86 points, but the error for this building is 87.14 points. This is why we should examine individual predictions as well as the average.</p><p>The notebook then uses LIME to investigate the prediction. LIME approximates the model behaviour around an individual example so we can examine how the inputs contribute to that local explanation.</p><p><strong>What can we do with an explanation like this?</strong></p><p>We can return to the record and check whether the input was prepared correctly. We could then look for other buildings with similar characteristics to see whether the same error happens repeatedly. This would help us decide whether the problem needs a correction in the data or another modelling experiment.</p><p>However, we should remember that explaining a prediction does not prove what causes the real outcome. A feature contributing to a lower predicted score does not automatically mean that changing the feature would change the building&#8217;s actual score.</p><p>For our own portfolio, we could apply this approach to the largest forecasting errors. If the model often underestimates sales during promotions, we can investigate whether information about promotions was available when making the forecast. We could then test whether including that information improves the validation result.</p><p>This gives us a reason for the next experiment based on the errors we observed. If AI helps write the explanation, we should check that it agrees with the actual records before including it in the portfolio.</p><div><hr></div><h2>4. Daniel Bourke&#8217;s Amenity Detection Project</h2><p>Daniel Bourke&#8217;s <a href="https://www.mrdbourke.com/airbnb-amenity-detection/">amenity detection project</a> explores using Computer Vision to identify objects in property photos. The project was inspired by Airbnb&#8217;s published work and follows the development through to an application.</p><p>The final evaluation reports a mean Average Precision, or mAP, of approximately 43.3 across 30 object classes. However, the overall score does not tell us whether the model performs similarly for each type of object. Let&#8217;s look at some of the class results.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CgG5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51e60d6b-b732-4863-bcbb-90c16483761b_1600x1110.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CgG5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51e60d6b-b732-4863-bcbb-90c16483761b_1600x1110.png 424w, https://substackcdn.com/image/fetch/$s_!CgG5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51e60d6b-b732-4863-bcbb-90c16483761b_1600x1110.png 848w, https://substackcdn.com/image/fetch/$s_!CgG5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51e60d6b-b732-4863-bcbb-90c16483761b_1600x1110.png 1272w, https://substackcdn.com/image/fetch/$s_!CgG5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51e60d6b-b732-4863-bcbb-90c16483761b_1600x1110.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CgG5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51e60d6b-b732-4863-bcbb-90c16483761b_1600x1110.png" width="1456" height="1010" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/51e60d6b-b732-4863-bcbb-90c16483761b_1600x1110.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1010,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;What Makes a Good Data Science Portfolio in the Age of AI?&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="What Makes a Good Data Science Portfolio in the Age of AI?" title="What Makes a Good Data Science Portfolio in the Age of AI?" srcset="https://substackcdn.com/image/fetch/$s_!CgG5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51e60d6b-b732-4863-bcbb-90c16483761b_1600x1110.png 424w, https://substackcdn.com/image/fetch/$s_!CgG5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51e60d6b-b732-4863-bcbb-90c16483761b_1600x1110.png 848w, https://substackcdn.com/image/fetch/$s_!CgG5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51e60d6b-b732-4863-bcbb-90c16483761b_1600x1110.png 1272w, https://substackcdn.com/image/fetch/$s_!CgG5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F51e60d6b-b732-4863-bcbb-90c16483761b_1600x1110.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As we can see from the chart, the reported AP is about 74.7 for refrigerators but only 5.8 for dishwashers. These are results from the same evaluation, so the overall mAP would hide a large difference if we reported it alone. AP summarizes detection performance across precision and recall; it is not the percentage of photos where every object is detected correctly.</p><p><strong>What would this mean for an application?</strong></p><p>If we wanted to use the model to suggest amenities for a property listing, we should investigate the weak categories before relying on the suggestions. We could inspect missed dishwashers and compare them with successful detections to see whether the problem relates to image conditions or the available training examples. We would then test the proposed improvement instead of assuming that adding more data would solve it.</p><p>The write-up also reviews the original project goals and acknowledges that the model did not reach the 50 mAP target. It explains that the result cannot be directly compared with Airbnb&#8217;s score because the datasets differ. This gives us a useful example of reporting what remains unfinished alongside the application.</p><p>We can learn from this when developing an application for our portfolio. A working demonstration shows that we can build the application, but we should also explain the conditions under which we tested it.</p><p>For example, we might develop a document classification application that works well with clean PDFs. If someone uploads a scanned page, the application could behave differently because the text needs to be extracted from an image first. Testing both types of documents would give us a better understanding of what the application supports.</p><p>We could extend this by checking what happens when the uploaded file is empty or has an unsupported format. The result should help us decide whether the application needs to reject the file or ask someone to review it.</p><p>AI can help us develop the interface, but we need these checks to understand whether the workflow behaves as expected. We can explain the remaining limitations in the project documentation and provide instructions for running the application locally if the live version is unavailable.</p><div><hr></div><h2><strong>5. Anthony Lipphardt&#8217;s Data Science Portfolio</strong></h2><p>Our last example is <a href="https://alipphardt.github.io/">Anthony Lipphardt&#8217;s portfolio website</a>. The page introduces his work through project descriptions and provides links for readers who want to explore further.</p><p>The collection includes work in exploratory analysis and data visualization as well as Machine Learning. This shows how we can use a portfolio to present different types of work without making every project a prediction model.</p><p><strong>How should we select the projects for our own portfolio?</strong></p><p>I would start by considering what we want someone to understand about our ability. If several projects repeat the same modelling process on a clean dataset, they might not show how we deal with other parts of a Data Science problem.</p><p>For example, we could develop a retail analysis that examines repeat purchases using SQL. The explanation would include how we handled returned orders so the reader can understand how the repeat purchase measure was calculated.</p><p>We could then include a demand forecasting project that compares the model with a simple seasonal forecast. This would give us another type of work to discuss because we need to explain whether the model improves on an approach that is already easy to use.</p><p>These are suggestions for our own projects rather than examples taken from Anthony&#8217;s website. The idea is to select work that adds something different to the collection while keeping the detailed explanation accessible through the project links.</p><p>We can use a short introduction on the portfolio page to explain the question each project addresses. Someone interested in the result can then open the notebook or report to understand the work further. This also means we can improve the project explanation without making the portfolio&#8217;s main page too long.</p><div><hr></div><h2><strong>Developing the Portfolio with AI</strong></h2><p>The examples above give us different ways to explain our work. We can apply the same approach when AI is part of the development process or when the project itself uses an AI model.</p><p>Let&#8217;s say we build an application that answers questions from documents. Showing a successful answer would be a good demonstration, but we would need more examples to understand whether the application works consistently.</p><p>We could prepare questions with the passages that should support the answers. We can then check whether the application finds the correct passage before examining whether the answer agrees with it. If the application finds the wrong passage, changing the answer prompt alone might not fix the problem.</p><p>Additionally, we should include questions that the documents cannot answer. This would let us examine whether the application acknowledges the missing information or provides an answer without support. We can use the same set of questions when testing a change so the comparison is easier to follow.</p><p>For AI-assisted coding, the explanation should focus on how we checked the output. For example, we might use AI to help merge customer data and then discover that the merge creates several rows for the same customer. Comparing the customer counts before and after the merge would help us identify the problem and correct the analysis.</p><p>GitHub&#8217;s <a href="https://docs.github.com/en/copilot/responsible-use/inline-suggestions#7-limitations">Copilot guidance</a> also recommends reviewing and testing generated code. In a Machine Learning project, we should check that preprocessing does not learn from the test data. The <a href="https://scikit-learn.org/stable/common_pitfalls.html#data-leakage">Scikit-Learn data leakage guide</a> explains how this can lead to an overly optimistic evaluation.</p><p>If I were reviewing the portfolio, I would use these explanations to discuss why the author chose an approach and what they would investigate next. That is how I would recommend using a portfolio in a discussion, although each company can have a different evaluation process.</p><p>We don&#8217;t need to replace an existing project every time a new tool becomes available. We can start by revisiting a result that we have not explained well and investigate why it happened. The work we add through that investigation can help someone understand our ability even when AI assists with the implementation.</p><div><hr></div><h1><strong>Continue Learning with Non-Brand Data</strong></h1><p>If you are interested in learning more about Data Science, Machine Learning, and AI, you can <a href="https://www.nb-data.com/">subscribe to Non-Brand Data</a>. I regularly share articles, experiments, and examples that we can learn from.</p><p>For readers who want to explore a subject in more detail, the <a href="https://www.nb-data.com/p/subscriber-benefits">paid subscription</a> also includes in-depth tutorials, written courses, notebooks, code, and additional learning materials.</p><p>I also offer <a href="https://mentorcruise.com/mentor/cornelliusyudhawijaya/">1-on-1 mentorship through MentorCruise</a> if you want to discuss your learning direction, Data Science projects, technical challenges, or career development.</p>]]></content:encoded></item><item><title><![CDATA[Building an Interactive Data Analysis Notebook With marimo and DuckDB]]></title><description><![CDATA[Learn to build modern dashboard in a simple way]]></description><link>https://www.nb-data.com/p/building-an-interactive-data-analysis</link><guid isPermaLink="false">https://www.nb-data.com/p/building-an-interactive-data-analysis</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sat, 17 Oct 2026 14:32:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_Q3A!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4722970-b651-45b1-838c-5c1bc771099e_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_Q3A!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4722970-b651-45b1-838c-5c1bc771099e_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_Q3A!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4722970-b651-45b1-838c-5c1bc771099e_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!_Q3A!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4722970-b651-45b1-838c-5c1bc771099e_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!_Q3A!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4722970-b651-45b1-838c-5c1bc771099e_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!_Q3A!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4722970-b651-45b1-838c-5c1bc771099e_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_Q3A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4722970-b651-45b1-838c-5c1bc771099e_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a4722970-b651-45b1-838c-5c1bc771099e_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1668282,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/220855959?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4722970-b651-45b1-838c-5c1bc771099e_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_Q3A!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4722970-b651-45b1-838c-5c1bc771099e_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!_Q3A!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4722970-b651-45b1-838c-5c1bc771099e_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!_Q3A!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4722970-b651-45b1-838c-5c1bc771099e_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!_Q3A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa4722970-b651-45b1-838c-5c1bc771099e_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>When exploring a dataset, we often repeat the same analysis for different groups. We might start with the average trip distance, then see how the result changes for cash payments or a specific week.</p><p>We can change the query and rerun our notebook. However, we also need to update the charts and make sure they use the same selection. This becomes more work as we add more questions to the analysis.</p><p>For this tutorial, we will build a notebook where we can change the filters and see the calculations and charts update together. We will use NYC Green Taxi trips as our example, combining marimo for the interactive notebook and DuckDB for the SQL analysis.</p><p>The result looks like the image below.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0Ij2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F965b922a-42a0-45a6-9d95-92e11c73d6c1_1134x638.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0Ij2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F965b922a-42a0-45a6-9d95-92e11c73d6c1_1134x638.jpeg 424w, https://substackcdn.com/image/fetch/$s_!0Ij2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F965b922a-42a0-45a6-9d95-92e11c73d6c1_1134x638.jpeg 848w, https://substackcdn.com/image/fetch/$s_!0Ij2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F965b922a-42a0-45a6-9d95-92e11c73d6c1_1134x638.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!0Ij2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F965b922a-42a0-45a6-9d95-92e11c73d6c1_1134x638.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0Ij2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F965b922a-42a0-45a6-9d95-92e11c73d6c1_1134x638.jpeg" width="1134" height="638" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/965b922a-42a0-45a6-9d95-92e11c73d6c1_1134x638.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:638,&quot;width&quot;:1134,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0Ij2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F965b922a-42a0-45a6-9d95-92e11c73d6c1_1134x638.jpeg 424w, https://substackcdn.com/image/fetch/$s_!0Ij2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F965b922a-42a0-45a6-9d95-92e11c73d6c1_1134x638.jpeg 848w, https://substackcdn.com/image/fetch/$s_!0Ij2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F965b922a-42a0-45a6-9d95-92e11c73d6c1_1134x638.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!0Ij2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F965b922a-42a0-45a6-9d95-92e11c73d6c1_1134x638.jpeg 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We will create payment, distance, and date controls, then use them to explore trip volumes and fares. Everything runs locally using open-source Python packages.</p><p>Let&#8217;s get into it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h1>Building a Notebook With marimo and DuckDB</h1><p><a href="https://docs.marimo.io/">marimo</a> is a reactive notebook for Python and SQL. When we change a value, it can automatically rerun dependent cells. This is useful when we want a dropdown or slider to control our analysis.</p><p><a href="https://duckdb.org/docs/current/clients/python/overview">DuckDB</a> is an embedded analytical SQL engine. We can query local files directly from Python without setting up a database server. In this project, it filters the trips and calculates the results before we pass them to our charts.</p><p>First, let&#8217;s download or clone the <a href="https://github.com/CornelliusYW/marimo-duckdb-taxi">GitHub repository</a>. </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
.\.venv\Scripts\python.exe download_data.py
.\.venv\Scripts\python.exe -m marimo edit taxi_analysis.py</code></pre></div><p>This opens the complete notebook in the marimo editor. The repository includes the dependencies and data preparation script, so we can start exploring without assembling those files ourselves.</p><p>The following examples walk through how our data, controls, and charts connect. In marimo, we put each notebook example in its own cell. If outputs are initially empty, click Run all to execute the notebook.</p><div><hr></div><h1>Preparing the NYC Taxi Data</h1><p>For this example, we will use <strong>Green Taxi trips for January 2025</strong> from the <a href="https://www.nyc.gov/site/tlc/about/tlc-trip-record-data.page">NYC Taxi &amp; Limousine Commission</a>. The file contains 48,326 rows and is around 1.2 MB, so we can work with the full month.</p><p>Before building the interactive part, let&#8217;s inspect the data with DuckDB. This loading example can be run separately in Python.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">import duckdb
from pathlib import Path

data_path = Path("data/green_tripdata_2025-01.parquet").resolve()
con = duckdb.connect()
con.read_parquet(str(data_path)).create_view("raw_trips")

con.execute("DESCRIBE raw_trips").pl()</code></pre></div><p>The code creates a view called raw_trips, which lets us query the Parquet file by name. DESCRIBE shows the available columns and their data types.</p><p>We will use the pickup and drop-off timestamps, trip distance, fare amount, and payment type. According to the <a href="https://www.nyc.gov/assets/tlc/downloads/pdf/data_dictionary_trip_records_green.pdf">data dictionary</a>, distance is measured in miles, while fare_amount represents the meter fare. It does not include all the additional charges in the passenger&#8217;s total payment.</p><p>The data also suggests we should clean it first. The largest recorded distance is 84,731.57 miles, some fares are negative, and some pickup dates fall outside January. 1,836 records also lack a payment type.</p><p>For our explorer, I keep January pickups with a drop-off after pickup, a positive distance up to 100 miles, and a positive meter fare up to $500. Missing payment remains a separate category. These choices leave <strong>45,467 trips</strong>.</p><p>These are tutorial limits, rather than official TLC validity rules. Long trips or negative fare adjustments can be legitimate, so the results reflect the records our rules include. The complete cleaning code and exclusion counts are available in the repository.</p><div><hr></div><h1>Combining SQL and Python</h1><p>Now, let&#8217;s move into marimo. The project keeps the data preparation and query functions in analysis.py, so we can reuse them from the notebook.</p><p>First, we import the tools and functions we need.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">import marimo as mo
import altair as alt
from analysis import (
 MONTH_START, MONTH_END, PAYMENT_LABELS,
 Filters, create_connection, query_dashboard,
)</code></pre></div><p>Then, we connect to the prepared data.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">con = create_connection()
clean_trips = con.table("clean_trips")</code></pre></div><p>create_connection() creates the DuckDB views, including clean_trips for the records that passed our cleaning rules. The second line gives marimo a reference to that view without loading all its rows into a dataframe.</p><p>Let&#8217;s start with a SQL query that calculates the trip count, average distance, and average fare.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">monthly_summary = mo.sql(
"""
 SELECT COUNT(*) AS total_trips,
 AVG(trip_distance) AS avg_distance,
 AVG(fare_amount) AS avg_fare
 FROM clean_trips
 """,
 engine=con,
)</code></pre></div><p>We use COUNT(*) to count trips and AVG to calculate the two averages. The result is <strong>45,467 trips, an average distance of 2.70 miles, and an average meter fare of $16.56</strong>.</p><p>marimo&#8217;s <a href="https://docs.marimo.io/guides/working_with_data/sql/">SQL cells</a> are saved in the Python file using mo.sql. In the editor, we see the SQL query and its output. The result is also available as a Polars dataframe, so we can use it in the next Python cell.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">baseline_rows = monthly_summary["total_trips"][0]
mo.md(f"The one-row SQL result summarizes **{baseline_rows:,} retained trips**.")</code></pre></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!E3Jw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F799809d0-f107-43af-aa6d-82d789051a23_1134x638.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!E3Jw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F799809d0-f107-43af-aa6d-82d789051a23_1134x638.jpeg 424w, https://substackcdn.com/image/fetch/$s_!E3Jw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F799809d0-f107-43af-aa6d-82d789051a23_1134x638.jpeg 848w, https://substackcdn.com/image/fetch/$s_!E3Jw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F799809d0-f107-43af-aa6d-82d789051a23_1134x638.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!E3Jw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F799809d0-f107-43af-aa6d-82d789051a23_1134x638.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!E3Jw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F799809d0-f107-43af-aa6d-82d789051a23_1134x638.jpeg" width="1134" height="638" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/799809d0-f107-43af-aa6d-82d789051a23_1134x638.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:638,&quot;width&quot;:1134,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!E3Jw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F799809d0-f107-43af-aa6d-82d789051a23_1134x638.jpeg 424w, https://substackcdn.com/image/fetch/$s_!E3Jw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F799809d0-f107-43af-aa6d-82d789051a23_1134x638.jpeg 848w, https://substackcdn.com/image/fetch/$s_!E3Jw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F799809d0-f107-43af-aa6d-82d789051a23_1134x638.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!E3Jw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F799809d0-f107-43af-aa6d-82d789051a23_1134x638.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>As we can see above, the Python cell reads monthly_summary from the SQL cell. We can use the same approach to pass a query result into a chart or another calculation.</p><p>The connection between cells comes from their variable names. If a cell uses monthly_summary, marimo knows it depends on the cell that creates that result. We do not need to place them in a particular execution order on the page, but each shared variable should be defined only once.</p><div><hr></div><h1>Making the Analysis Interactive</h1><p>With our first analysis ready, let&#8217;s add the controls. We want to choose how the passenger paid, how far the trip traveled, and when the pickup happened.</p><p>The following code creates the three controls and places them side by side. It first reads the available payment codes so the dropdown matches our dataset.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">_observed = con.execute("""
    SELECT DISTINCT COALESCE(CAST(payment_type AS INTEGER), -1) AS code
    FROM raw_trips ORDER BY code
""").fetchall()

payment_options = {"All methods": None}
payment_options.update({PAYMENT_LABELS[_code]: _code for (_code,) in _observed})

payment = mo.ui.dropdown(
    options=payment_options,
    value="All methods",
    label="Payment method",
    full_width=True,
)

distance = mo.ui.range_slider(
    start=0, stop=100, step=0.5, value=[0, 100],
    label="Trip distance (miles)", show_value=True,
    debounce=True, full_width=True,
)

pickup_dates = mo.ui.date_range(
    start=MONTH_START, stop=MONTH_END,
    value=(MONTH_START, MONTH_END),
    label="Pickup dates (inclusive)", full_width=True,
)

mo.hstack([payment, distance, pickup_dates], widths="equal", gap=2)</code></pre></div><p>The dropdown starts with all payment methods, while the distance and date ranges cover the full cleaned month. We use debounce=True for the slider so the query updates when we release the handle, rather than for every movement while dragging it.</p><p>Next, let&#8217;s connect these selections to the analysis. In another cell, we read the controls&#8217; .value properties and pass them to our query functions.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">selected_filters = Filters(
 start=pickup_dates.value[0], end=pickup_dates.value[1],
 min_distance=distance.value[0], max_distance=distance.value[1],
 payment=payment.value,
)
summary, hourly, daily, payments = query_dashboard(con, selected_filters)</code></pre></div><p>The query helper uses the same selection for every result. Its summary query follows this structure:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;dockerfile&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-dockerfile">WITH filtered AS (
 SELECT * FROM clean_trips
 WHERE pickup_at &gt;= $start_at AND pickup_at &lt; $end_exclusive
 AND trip_distance BETWEEN $min_distance AND $max_distance
 AND (CAST($payment AS INTEGER) IS NULL OR payment_code = $payment)
)
SELECT COUNT(*) AS total_trips,
 AVG(trip_distance) AS avg_distance,
 AVG(fare_amount) AS avg_fare
FROM filtered;</code></pre></div><p>The values beginning with $ are <a href="https://duckdb.org/docs/current/guides/python/execute_sql">named query parameters</a>. The helper passes the selected values separately from the SQL text. We also include the entire final day in the date selection by using the following midnight as the exclusive upper bound.</p><p>Let&#8217;s take Cash as an example. Changing the dropdown updates payment.value. marimo then reruns the analysis cell, followed by the cells that display its results. We get <strong>11,035 trips, an average distance of 2.77 miles, and an average fare of $16.82</strong> for the full month.</p><p>We can also combine the controls to look at a more specific group.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Yzpg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0fbcb62-ab5a-49d5-88cb-87611651dff3_1134x638.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Yzpg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0fbcb62-ab5a-49d5-88cb-87611651dff3_1134x638.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Yzpg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0fbcb62-ab5a-49d5-88cb-87611651dff3_1134x638.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Yzpg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0fbcb62-ab5a-49d5-88cb-87611651dff3_1134x638.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Yzpg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0fbcb62-ab5a-49d5-88cb-87611651dff3_1134x638.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Yzpg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0fbcb62-ab5a-49d5-88cb-87611651dff3_1134x638.jpeg" width="1134" height="638" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f0fbcb62-ab5a-49d5-88cb-87611651dff3_1134x638.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:638,&quot;width&quot;:1134,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Yzpg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0fbcb62-ab5a-49d5-88cb-87611651dff3_1134x638.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Yzpg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0fbcb62-ab5a-49d5-88cb-87611651dff3_1134x638.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Yzpg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0fbcb62-ab5a-49d5-88cb-87611651dff3_1134x638.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Yzpg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0fbcb62-ab5a-49d5-88cb-87611651dff3_1134x638.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Selecting Cash, January 1&#8211;7, and 0&#8211;5 miles gives us <strong>2,092 trips, an average distance of 1.75 miles, and an average fare of $12.91</strong>.</p><p>This is the reactive part of our notebook. We change a selection, and the cells that use it rerun. The earlier full-month SQL query stays as a baseline because it does not use these controls.'</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/building-an-interactive-data-analysis?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/building-an-interactive-data-analysis?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div><h1>Visualizing the Results</h1><p>Now, let&#8217;s display the calculations and explore how trip volumes change throughout the day.</p><p>For the summary, we can use mo.stat to show the three metrics together.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">_distance_text = "N/A" if summary["avg_distance"] is None else f'{summary["avg_distance"]:.2f} mi'
_fare_text = "N/A" if summary["avg_fare"] is None else f'${summary["avg_fare"]:.2f}'

mo.hstack([
    mo.stat(value=f'{summary["total_trips"]:,}', label="Selected trips"),
    mo.stat(value=_distance_text, label="Average trip distance"),
    mo.stat(value=_fare_text, label="Average meter fare"),
], widths="equal", gap=2)</code></pre></div><p>Each metric uses the summary result from the analysis cell. This means the displayed values follow the filters we just created.</p><p>For the hourly chart, DuckDB groups the selected trips by pickup hour and counts the records in each group. We then pass that small result to Altair.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">hourly_chart = alt.Chart(hourly).mark_bar(color="#2563eb", size=12).encode(
    x=alt.X("pickup_hour:O", title="Pickup hour", sort=list(range(24))),
    y=alt.Y("trips:Q", title="Trips"),
    tooltip=["pickup_hour:O", alt.Tooltip("trips:Q", format=",")],
).properties(width="container", height=220)

mo.ui.altair_chart(hourly_chart, chart_selection=False)</code></pre></div><p>In the code above, mark_bar creates the bars, while the x and y definitions specify the pickup hour and trip count. The tooltip lets us inspect each value. mo.ui.altair_chart displays the result in the notebook.</p><p>The complete notebook also includes a daily line chart and a payment chart. They use the same filtered data, so we can review the daily pattern and payment mix alongside the hourly distribution.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Gx6Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a201e21-162f-4c5f-9f25-a09ce387e09f_1134x638.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Gx6Y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a201e21-162f-4c5f-9f25-a09ce387e09f_1134x638.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Gx6Y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a201e21-162f-4c5f-9f25-a09ce387e09f_1134x638.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Gx6Y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a201e21-162f-4c5f-9f25-a09ce387e09f_1134x638.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Gx6Y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a201e21-162f-4c5f-9f25-a09ce387e09f_1134x638.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!Gx6Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a201e21-162f-4c5f-9f25-a09ce387e09f_1134x638.jpeg" width="1134" height="638" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0a201e21-162f-4c5f-9f25-a09ce387e09f_1134x638.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:638,&quot;width&quot;:1134,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Gx6Y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a201e21-162f-4c5f-9f25-a09ce387e09f_1134x638.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Gx6Y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a201e21-162f-4c5f-9f25-a09ce387e09f_1134x638.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Gx6Y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a201e21-162f-4c5f-9f25-a09ce387e09f_1134x638.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Gx6Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a201e21-162f-4c5f-9f25-a09ce387e09f_1134x638.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>From the full-month result, we can see that <strong>17:00 has the highest hourly count, with 3,937 trips</strong>. January 30 has the highest daily count, with 1,811 trips.</p><p>Let&#8217;s see what happens when we select Cash.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nI-Z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddde7b8d-f42d-4ecc-9ebf-304c5fbde634_1134x638.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nI-Z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddde7b8d-f42d-4ecc-9ebf-304c5fbde634_1134x638.jpeg 424w, https://substackcdn.com/image/fetch/$s_!nI-Z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddde7b8d-f42d-4ecc-9ebf-304c5fbde634_1134x638.jpeg 848w, https://substackcdn.com/image/fetch/$s_!nI-Z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddde7b8d-f42d-4ecc-9ebf-304c5fbde634_1134x638.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!nI-Z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddde7b8d-f42d-4ecc-9ebf-304c5fbde634_1134x638.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!nI-Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddde7b8d-f42d-4ecc-9ebf-304c5fbde634_1134x638.jpeg" width="1134" height="638" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ddde7b8d-f42d-4ecc-9ebf-304c5fbde634_1134x638.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:638,&quot;width&quot;:1134,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nI-Z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddde7b8d-f42d-4ecc-9ebf-304c5fbde634_1134x638.jpeg 424w, https://substackcdn.com/image/fetch/$s_!nI-Z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddde7b8d-f42d-4ecc-9ebf-304c5fbde634_1134x638.jpeg 848w, https://substackcdn.com/image/fetch/$s_!nI-Z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddde7b8d-f42d-4ecc-9ebf-304c5fbde634_1134x638.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!nI-Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fddde7b8d-f42d-4ecc-9ebf-304c5fbde634_1134x638.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The hourly peak shifts to 16:00 with 947 trips, and the other charts update to match the selection. We can explore how the pattern changes without editing the query or manually rerunning each chart.</p><p>The results describe the selected records. They do not tell us why passengers traveled at those times or chose a particular payment method.</p><p>A selection might also return no matching trips. The notebook displays zero trips, N/A for the averages, and a message asking us to adjust the filters.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cazv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10827836-17e9-4352-a783-2d653b675baa_1134x638.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cazv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10827836-17e9-4352-a783-2d653b675baa_1134x638.jpeg 424w, https://substackcdn.com/image/fetch/$s_!cazv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10827836-17e9-4352-a783-2d653b675baa_1134x638.jpeg 848w, https://substackcdn.com/image/fetch/$s_!cazv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10827836-17e9-4352-a783-2d653b675baa_1134x638.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!cazv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10827836-17e9-4352-a783-2d653b675baa_1134x638.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!cazv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10827836-17e9-4352-a783-2d653b675baa_1134x638.jpeg" width="1134" height="638" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10827836-17e9-4352-a783-2d653b675baa_1134x638.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:638,&quot;width&quot;:1134,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!cazv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10827836-17e9-4352-a783-2d653b675baa_1134x638.jpeg 424w, https://substackcdn.com/image/fetch/$s_!cazv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10827836-17e9-4352-a783-2d653b675baa_1134x638.jpeg 848w, https://substackcdn.com/image/fetch/$s_!cazv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10827836-17e9-4352-a783-2d653b675baa_1134x638.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!cazv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10827836-17e9-4352-a783-2d653b675baa_1134x638.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This keeps the notebook useful even when a combination of filters returns no data.</p><div><hr></div><h1>Running Our Notebook as an Application</h1><p>With the notebook ready, we can run it as an application. Stop the editor with Ctrl+C, then use the following command.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">.\.venv\Scripts\python.exe -m marimo run taxi_analysis.py</code></pre></div><p>In <a href="https://docs.marimo.io/guides/apps/">application mode</a>, readers can use the filters and see the results without the source-editing controls. The opening image shows this view. Keep the local Python process running while using the page.</p><p>This approach works well when we want to explore different groups using the same calculations. As the dataset grows, we should check the query cost because changing a filter reruns the analysis. DuckDB aggregates before transferring results to Python, but the view itself does not cache our answers.</p><p>I checked the calculations against a separate Polars implementation and tested the filters in the running application. The <a href="https://github.com/CornelliusYW/marimo-duckdb-taxi">GitHub repository</a> includes the validation details, source code, and dataset instructions.</p><div><hr></div><h1>Conclusion</h1><p>In this article, we built an interactive NYC Taxi Trip Explorer using marimo and DuckDB. We used SQL to analyze the trips, Python to connect the results, and notebook controls to explore different selections.</p><p>We can build on the same workflow when our analysis involves repeated queries for different groups. For the complete implementation, you can access the <a href="https://github.com/CornelliusYW/marimo-duckdb-taxi/blob/main/taxi_analysis.py">notebook</a> and <a href="https://github.com/CornelliusYW/marimo-duckdb-taxi/blob/main/analysis.py">supporting code</a> on GitHub.</p><p>I hope it helps!</p><div><hr></div><h2><strong>Continue Learning with Non-Brand Data</strong></h2><p>If you are interested in learning more about Data Science, Machine Learning, and AI, you can <a href="https://www.nb-data.com/">subscribe to Non-Brand Data</a>. I regularly share articles, experiments, and examples to learn from.</p><p>For readers who want to explore a subject in more detail, the <a href="https://www.nb-data.com/p/subscriber-benefits">paid subscription</a> also includes in-depth tutorials, written courses, notebooks, code, and additional learning materials.</p><p>I also offer <a href="https://mentorcruise.com/mentor/cornelliusyudhawijaya/">1-on-1 mentorship through MentorCruise</a> if you want to discuss your learning direction, Data Science projects, technical challenges, or career development.</p>]]></content:encoded></item><item><title><![CDATA[Building a Multi-Asset Market Monitor with TickerLayer]]></title><description><![CDATA[Understand market data and learn how to work with it in Python]]></description><link>https://www.nb-data.com/p/building-a-multi-asset-market-monitor</link><guid isPermaLink="false">https://www.nb-data.com/p/building-a-multi-asset-market-monitor</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Fri, 16 Oct 2026 11:23:32 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1616261167032-b16d2df8333b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8c3RvY2t8ZW58MHx8fHwxNzkyMTI1MDE5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1616261167032-b16d2df8333b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8c3RvY2t8ZW58MHx8fHwxNzkyMTI1MDE5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1616261167032-b16d2df8333b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8c3RvY2t8ZW58MHx8fHwxNzkyMTI1MDE5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1616261167032-b16d2df8333b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8c3RvY2t8ZW58MHx8fHwxNzkyMTI1MDE5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1616261167032-b16d2df8333b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8c3RvY2t8ZW58MHx8fHwxNzkyMTI1MDE5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1616261167032-b16d2df8333b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8c3RvY2t8ZW58MHx8fHwxNzkyMTI1MDE5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1616261167032-b16d2df8333b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8c3RvY2t8ZW58MHx8fHwxNzkyMTI1MDE5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" width="3500" height="2333" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1616261167032-b16d2df8333b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8c3RvY2t8ZW58MHx8fHwxNzkyMTI1MDE5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:2333,&quot;width&quot;:3500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A green line graph showing stock market trends on a digital screen&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A green line graph showing stock market trends on a digital screen" title="A green line graph showing stock market trends on a digital screen" srcset="https://images.unsplash.com/photo-1616261167032-b16d2df8333b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8c3RvY2t8ZW58MHx8fHwxNzkyMTI1MDE5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1616261167032-b16d2df8333b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8c3RvY2t8ZW58MHx8fHwxNzkyMTI1MDE5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1616261167032-b16d2df8333b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8c3RvY2t8ZW58MHx8fHwxNzkyMTI1MDE5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1616261167032-b16d2df8333b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw2NXx8c3RvY2t8ZW58MHx8fHwxNzkyMTI1MDE5fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@markusspiske">Markus Spiske</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><p><em>Disclosure: This article is sponsored by TickerLayer, which provided reviewer access. The examples and observations are based on my testing.</em></p><p>Market data includes prices, trading volume, and historical observations. We can use it to study market changes or build financial applications, but we also need to understand what each value represents and when it was recorded.</p><p>To access this data in Python, we can use an API. One platform that provides this access is <a href="https://tickerlayer.com/?utm_source=nonbranddata&amp;utm_medium=sponsored_article&amp;utm_campaign=market_monitor_2026">TickerLayer</a>, which offers REST APIs for individual requests, WebSocket streams for ongoing updates, and an MCP server for compatible AI clients.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pcij!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e6c0f1-a84e-4268-b697-fd74f66a66d1_2638x1436.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pcij!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e6c0f1-a84e-4268-b697-fd74f66a66d1_2638x1436.png 424w, https://substackcdn.com/image/fetch/$s_!pcij!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e6c0f1-a84e-4268-b697-fd74f66a66d1_2638x1436.png 848w, https://substackcdn.com/image/fetch/$s_!pcij!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e6c0f1-a84e-4268-b697-fd74f66a66d1_2638x1436.png 1272w, https://substackcdn.com/image/fetch/$s_!pcij!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e6c0f1-a84e-4268-b697-fd74f66a66d1_2638x1436.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pcij!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e6c0f1-a84e-4268-b697-fd74f66a66d1_2638x1436.png" width="1456" height="793" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/83e6c0f1-a84e-4268-b697-fd74f66a66d1_2638x1436.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:793,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:279026,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/220062425?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e6c0f1-a84e-4268-b697-fd74f66a66d1_2638x1436.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pcij!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e6c0f1-a84e-4268-b697-fd74f66a66d1_2638x1436.png 424w, https://substackcdn.com/image/fetch/$s_!pcij!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e6c0f1-a84e-4268-b697-fd74f66a66d1_2638x1436.png 848w, https://substackcdn.com/image/fetch/$s_!pcij!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e6c0f1-a84e-4268-b697-fd74f66a66d1_2638x1436.png 1272w, https://substackcdn.com/image/fetch/$s_!pcij!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F83e6c0f1-a84e-4268-b697-fd74f66a66d1_2638x1436.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In this tutorial, we will build a small market monitor with Python, then explore streaming and the MCP option. All the code, notebook, and saved responses are in the <a href="https://github.com/CornelliusYW/tickerlayer-python-tutorial">GitHub repository</a>.</p><p>Let&#8217;s get into it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h1>Set Up the Python Environment</h1><p>First, get an API key from the TickerLayer dashboard by following the <a href="https://tickerlayer.com/docs/quickstart/?utm_medium=article">quickstart instructions</a>. You will need access to the feeds used below, including an eligible paid plan or approved trial for streaming.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BITD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed67b86-0521-440e-8728-b8cdf5146cdc_1326x746.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BITD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed67b86-0521-440e-8728-b8cdf5146cdc_1326x746.jpeg 424w, https://substackcdn.com/image/fetch/$s_!BITD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed67b86-0521-440e-8728-b8cdf5146cdc_1326x746.jpeg 848w, https://substackcdn.com/image/fetch/$s_!BITD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed67b86-0521-440e-8728-b8cdf5146cdc_1326x746.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!BITD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed67b86-0521-440e-8728-b8cdf5146cdc_1326x746.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!BITD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed67b86-0521-440e-8728-b8cdf5146cdc_1326x746.jpeg" width="1326" height="746" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9ed67b86-0521-440e-8728-b8cdf5146cdc_1326x746.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:746,&quot;width&quot;:1326,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;TickerLayer quickstart and authentication documentation&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="TickerLayer quickstart and authentication documentation" title="TickerLayer quickstart and authentication documentation" srcset="https://substackcdn.com/image/fetch/$s_!BITD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed67b86-0521-440e-8728-b8cdf5146cdc_1326x746.jpeg 424w, https://substackcdn.com/image/fetch/$s_!BITD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed67b86-0521-440e-8728-b8cdf5146cdc_1326x746.jpeg 848w, https://substackcdn.com/image/fetch/$s_!BITD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed67b86-0521-440e-8728-b8cdf5146cdc_1326x746.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!BITD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ed67b86-0521-440e-8728-b8cdf5146cdc_1326x746.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Download the repository to continue the tutorial. </p><p>Next, install the packages:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">python -m pip install -r requirements-article.txt</code></pre></div><p>The files <code>market_monitor.py</code> and <code>tickerlayer_experiment.py</code> contain the functions used below. Keep them together in the repository folder.</p><p>Store your key in an environment variable named  <code>TICKERLAYER_API_KEY</code>. For example, in PowerShell:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">$env:TICKERLAYER_API_KEY = "YOUR_API_KEY"</code></pre></div><p>Replace the placeholder with your key, then create a session using it. Keep the key out of any notebook you share.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import os
import requests
import pandas as pd

from market_monitor import get_json, quote_row, WATCHLIST

key = os.environ["TICKERLAYER_API_KEY"]
session = requests.Session()
session.headers.update({
    "x-api-key": key,
    "Accept": "application/json",
})</code></pre></div><p>We can now use this session for our requests. The <code>get_json</code> function imported from <code>market_monitor.py</code> returns each response as a Python dictionary.</p><div><hr></div><h1>Build the REST Market Monitor</h1><p>Let&#8217;s start with Bitcoin. We can retrieve its latest bid and ask prices using the BTCUSD symbol:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">quote = get_json(session, "/crypto/quote/BTCUSD")
print(quote)</code></pre></div><p>The response gives us the prices and their timestamp. To compare Bitcoin with other instruments, let&#8217;s create a function that places each quote into the same table structure:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def quote_row(asset, payload):
    bid, ask = float(payload["bid"]), float(payload["ask"])
    return {
        "asset": asset,
        "symbol": payload["symbol"],
        "bid": bid,
        "ask": ask,
        "mid": (bid + ask) / 2,
        "spread": ask - bid,
        "event_time_utc": pd.to_datetime(
            payload["timestamp"], unit="ms", utc=True
        ),
    }</code></pre></div><p>This function also calculates the midpoint and spread, and converts the timestamp to UTC. We can now apply it to WATCHLIST, which we imported from market_monitor.py:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">rows = []
for asset, symbol in WATCHLIST:
    payload = get_json(session, f"/{asset}/quote/{symbol}")
    rows.append(quote_row(asset, payload))

monitor = pd.DataFrame(rows)
print(monitor.to_string(index=False))</code></pre></div><p>The watchlist includes crypto, forex, stocks, indices, ETFs, commodities, and perpetuals. I checked the symbol directories before choosing these examples; stocks, for example, use a country prefix such as <code>US:AAPL</code>. </p><p>Displaying the monitor in a notebook gives us the following table:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tEJR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec2c9ea-9210-44c7-92cf-74b72fe1ecce_1325x431.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tEJR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec2c9ea-9210-44c7-92cf-74b72fe1ecce_1325x431.jpeg 424w, https://substackcdn.com/image/fetch/$s_!tEJR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec2c9ea-9210-44c7-92cf-74b72fe1ecce_1325x431.jpeg 848w, https://substackcdn.com/image/fetch/$s_!tEJR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec2c9ea-9210-44c7-92cf-74b72fe1ecce_1325x431.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!tEJR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec2c9ea-9210-44c7-92cf-74b72fe1ecce_1325x431.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!tEJR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec2c9ea-9210-44c7-92cf-74b72fe1ecce_1325x431.jpeg" width="1325" height="431" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6ec2c9ea-9210-44c7-92cf-74b72fe1ecce_1325x431.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:431,&quot;width&quot;:1325,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Multi-asset market monitor using fresh TickerLayer API responses&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Multi-asset market monitor using fresh TickerLayer API responses" title="Multi-asset market monitor using fresh TickerLayer API responses" srcset="https://substackcdn.com/image/fetch/$s_!tEJR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec2c9ea-9210-44c7-92cf-74b72fe1ecce_1325x431.jpeg 424w, https://substackcdn.com/image/fetch/$s_!tEJR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec2c9ea-9210-44c7-92cf-74b72fe1ecce_1325x431.jpeg 848w, https://substackcdn.com/image/fetch/$s_!tEJR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec2c9ea-9210-44c7-92cf-74b72fe1ecce_1325x431.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!tEJR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6ec2c9ea-9210-44c7-92cf-74b72fe1ecce_1325x431.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>One thing I noticed was the AR40 timestamp. It was about 66.9 hours older than when the response arrived. Let&#8217;s check the market status for more context:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">status = get_json(
    session, "/markets/status",
    params={"asset": "indices", "symbol": "AR40"},
)
print(status["status"], status["phase"], status["reason"])</code></pre></div><p>The saved status response was:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">open primary Generic weekday session (UTC)</code></pre></div><p>This response used a generic UTC calendar, not a confirmation that a new AR40 quote had arrived. TickerLayer&#8217;s <a href="https://tickerlayer.com/docs/market-status?utm_medium=article">market-status documentation</a> explains that an open session does not guarantee live quotes. The saved responses show the difference, but do not establish why this quote was older.</p><p>We can also request government bond yields. Since these are rates, we will keep them separate from the price table. Here is an example for the US ten-year yield:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">bond = get_json(session, "/bond/snapshot/US:10Y")
print(bond["rate"], bond["unit"], bond["date"])
print("Change in basis points:", bond["change_bps"])</code></pre></div><p>Keeping the date beside the yield makes its update period clear.</p><div><hr></div><h2>Add Historical Data</h2><p>To explore how prices change over time, we can retrieve historical data. Let&#8217;s request the last seven completed days of Bitcoin OHLCV data, which contains the open, high, low, close, and volume:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from datetime import datetime, timedelta, timezone

today = datetime.now(timezone.utc).date()
start = today - timedelta(days=7)
end = today - timedelta(days=1)

history = get_json(
    session,
    f"/crypto/agg/BTCUSD/1/day/{start}/{end}",
    params={"sort": "asc", "limit": 10},
)
bars = pd.DataFrame(history["results"])
bars["date"] = pd.to_datetime(bars["t"], unit="ms", utc=True)
print(bars[["date", "o", "h", "l", "c", "v"]])
print("Bars returned:", len(bars))
print("Next page:", history.get("next_offset"))</code></pre></div><p>The result table is shown in the table below:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7xXr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59aa4372-e74d-4b6e-ab7c-48ec63bad9fe_1325x586.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7xXr!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59aa4372-e74d-4b6e-ab7c-48ec63bad9fe_1325x586.jpeg 424w, https://substackcdn.com/image/fetch/$s_!7xXr!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59aa4372-e74d-4b6e-ab7c-48ec63bad9fe_1325x586.jpeg 848w, https://substackcdn.com/image/fetch/$s_!7xXr!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59aa4372-e74d-4b6e-ab7c-48ec63bad9fe_1325x586.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!7xXr!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59aa4372-e74d-4b6e-ab7c-48ec63bad9fe_1325x586.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!7xXr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59aa4372-e74d-4b6e-ab7c-48ec63bad9fe_1325x586.jpeg" width="1325" height="586" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/59aa4372-e74d-4b6e-ab7c-48ec63bad9fe_1325x586.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:586,&quot;width&quot;:1325,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Seven daily BTCUSD bars returned by TickerLayer&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Seven daily BTCUSD bars returned by TickerLayer" title="Seven daily BTCUSD bars returned by TickerLayer" srcset="https://substackcdn.com/image/fetch/$s_!7xXr!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59aa4372-e74d-4b6e-ab7c-48ec63bad9fe_1325x586.jpeg 424w, https://substackcdn.com/image/fetch/$s_!7xXr!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59aa4372-e74d-4b6e-ab7c-48ec63bad9fe_1325x586.jpeg 848w, https://substackcdn.com/image/fetch/$s_!7xXr!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59aa4372-e74d-4b6e-ab7c-48ec63bad9fe_1325x586.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!7xXr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F59aa4372-e74d-4b6e-ab7c-48ec63bad9fe_1325x586.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For a longer date range, check next_offset for another page of results. The <a href="https://tickerlayer.com/docs/rest/crypto/?utm_medium=sponsored_article">crypto REST reference</a> covers the query parameters.</p><div><hr></div><h1>Receive Updates Through WebSocket</h1><p>For ongoing price updates, we can use a WebSocket connection. We will subscribe to Bitcoin quotes and trades through the crypto.quotes and crypto.trades channels. </p><p>TickerLayer&#8217;s <a href="https://tickerlayer.com/docs/websocket/subscriptions?utm_medium=sponsored_article">subscription guide</a> shows this request format and acknowledgement.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!496_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2a474e-d026-4d47-9200-4efda6b4fe61_1326x746.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!496_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2a474e-d026-4d47-9200-4efda6b4fe61_1326x746.jpeg 424w, https://substackcdn.com/image/fetch/$s_!496_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2a474e-d026-4d47-9200-4efda6b4fe61_1326x746.jpeg 848w, https://substackcdn.com/image/fetch/$s_!496_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2a474e-d026-4d47-9200-4efda6b4fe61_1326x746.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!496_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2a474e-d026-4d47-9200-4efda6b4fe61_1326x746.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!496_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2a474e-d026-4d47-9200-4efda6b4fe61_1326x746.jpeg" width="1326" height="746" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ae2a474e-d026-4d47-9200-4efda6b4fe61_1326x746.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:746,&quot;width&quot;:1326,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;TickerLayer website showing a WebSocket subscription and its acknowledgement&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="TickerLayer website showing a WebSocket subscription and its acknowledgement" title="TickerLayer website showing a WebSocket subscription and its acknowledgement" srcset="https://substackcdn.com/image/fetch/$s_!496_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2a474e-d026-4d47-9200-4efda6b4fe61_1326x746.jpeg 424w, https://substackcdn.com/image/fetch/$s_!496_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2a474e-d026-4d47-9200-4efda6b4fe61_1326x746.jpeg 848w, https://substackcdn.com/image/fetch/$s_!496_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2a474e-d026-4d47-9200-4efda6b4fe61_1326x746.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!496_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae2a474e-d026-4d47-9200-4efda6b4fe61_1326x746.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For our Bitcoin example, the <code>stream_events</code> function in <code>market_monitor.py</code> opens the connection and sends the following subscription:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">subscription = {
    "action": "subscribe",
    "channels": ["crypto.quotes", "crypto.trades"],
    "symbols": ["BTCUSD"],
    "snapshot": True,
}</code></pre></div><p>The snapshot option requests a recent value on subscription when one is available. Our helper then collects up to 80 messages or listens for 15 seconds, whichever comes first. </p><p>Let&#8217;s use the received messages to update our monitor:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import asyncio
from market_monitor import stream_events, apply_event

stream = asyncio.run(stream_events(key, seconds=15))
state = {}
for event in stream["events"]:
    apply_event(state, event["message"])

print(state[("crypto", "BTCUSD")])</code></pre></div><p>If you are running this in a notebook with an active event loop, use <code>stream = await stream_events(key, seconds=15)</code> instead of<code> asyncio.run</code>.</p><p>We can also inspect the first quote message directly:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import json

quote_event = next(
    event["message"] for event in stream["events"]
    if event["message"].get("type") == "quote"
)
print(json.dumps(quote_event, indent=2))</code></pre></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!M_6l!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d4bb392-2e57-41d5-b0bf-156382a60d00_1325x680.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!M_6l!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d4bb392-2e57-41d5-b0bf-156382a60d00_1325x680.jpeg 424w, https://substackcdn.com/image/fetch/$s_!M_6l!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d4bb392-2e57-41d5-b0bf-156382a60d00_1325x680.jpeg 848w, https://substackcdn.com/image/fetch/$s_!M_6l!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d4bb392-2e57-41d5-b0bf-156382a60d00_1325x680.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!M_6l!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d4bb392-2e57-41d5-b0bf-156382a60d00_1325x680.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!M_6l!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d4bb392-2e57-41d5-b0bf-156382a60d00_1325x680.jpeg" width="1325" height="680" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1d4bb392-2e57-41d5-b0bf-156382a60d00_1325x680.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:680,&quot;width&quot;:1325,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Actual BTCUSD quote message received through WebSocket&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Actual BTCUSD quote message received through WebSocket" title="Actual BTCUSD quote message received through WebSocket" srcset="https://substackcdn.com/image/fetch/$s_!M_6l!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d4bb392-2e57-41d5-b0bf-156382a60d00_1325x680.jpeg 424w, https://substackcdn.com/image/fetch/$s_!M_6l!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d4bb392-2e57-41d5-b0bf-156382a60d00_1325x680.jpeg 848w, https://substackcdn.com/image/fetch/$s_!M_6l!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d4bb392-2e57-41d5-b0bf-156382a60d00_1325x680.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!M_6l!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d4bb392-2e57-41d5-b0bf-156382a60d00_1325x680.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>To see the mix of quotes, trades, and system messages in this capture, we can count them by type:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from collections import Counter

message_counts = Counter(
    event["message"].get("type", "unknown")
    for event in stream["events"]
)
print("Messages received:", len(stream["events"]))
print("Message types:", dict(message_counts))
print("Elapsed capture time:", stream["duration_seconds"], "seconds")</code></pre></div><p>After checking how many messages we received and their types, we can use them to update our monitor. The <code>apply_event</code> function handles quotes and trades separately, keeping the latest bid and ask alongside the latest trade price.</p><div><hr></div><h2>Handle Temporary Errors and Reconnect</h2><p>A request might fail temporarily, even when our code is correct. For a <code>503</code> response, the <code>get_json</code> function retries up to three times, waiting 1, 2, and 4 seconds between attempts. It uses the server&#8217;s <code>Retry-After</code> value when provided.</p><p>If the request still fails, we can handle the HTTP error using the following code:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;7e316a3e-2998-4a3f-9583-6cb57d480b17&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">try:
    latest = get_json(session, "/crypto/quote/BTCUSD")
    print(latest)
except requests.HTTPError as exc:
    print("Request failed with HTTP status:", exc.response.status_code)</code></pre></div><p>Other HTTP errors are raised without retrying. For example, a <code>401</code> means we need to check our API key, while a <code>429</code> means we should check our request limits.</p><p>For WebSocket, we need to reconnect if the connection drops. Let&#8217;s use <code>stream_with_reconnect</code> from the repository and pass the collected messages into our monitor:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;b87ff713-bd65-4241-b9a2-f273798107d8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from stream_recovery import stream_with_reconnect

recovered = asyncio.run(stream_with_reconnect(key, seconds=15))
print("Reconnect attempts:", recovered["reconnect_attempts"])

for event in recovered["events"]:
    apply_event(state, event["message"])</code></pre></div><p>The function keeps the messages received before the interruption and subscribes again after reconnecting. It allows up to three retries and requests a recent snapshot when available. The capture still has time and message limits.</p><p>In a notebook, use <code>recovered = await stream_with_reconnect(key, seconds=15)</code> instead of <code>asyncio.run</code>.</p><p>The snapshot helps update our monitor, but it does not restore every trade missed during the interruption. This is useful for our latest-value monitor, not a complete trade history.</p><div><hr></div><h1>Request a Snapshot Through MCP</h1><p>If we want to access market data through a compatible AI client, TickerLayer also supports the Model Context Protocol, or MCP. It exposes data requests as tools the client can call.</p><p>Let&#8217;s try the get_snapshot tool from Python. The<code> run_mcp</code> function in <code>tickerlayer_experiment.py </code>connects to the <a href="https://tickerlayer.com/docs/mcp?utm_medium=sponsored_article">MCP server</a>, lists its tools, and calls get_snapshot with these arguments:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">{
  "asset_class": "crypto",
  "symbol": "BTCUSD"
}</code></pre></div><p>Run the function and print the snapshot:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from tickerlayer_experiment import run_mcp

mcp = run_mcp(key)
if mcp["error"]:
    raise RuntimeError(mcp["error"])

print("Tools discovered:", len(mcp["tool_names"]))

call = next(
    step for step in mcp["steps"]
    if step["name"] == "tools_call_get_snapshot"
)
result = call["body"]["result"]
if result.get("isError"):
    raise RuntimeError(result["content"])
print(json.dumps(result["structuredContent"], indent=2))</code></pre></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!6d9H!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd66b6f9f-4dc7-4b21-9140-2f98e437ad89_1325x755.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6d9H!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd66b6f9f-4dc7-4b21-9140-2f98e437ad89_1325x755.jpeg 424w, https://substackcdn.com/image/fetch/$s_!6d9H!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd66b6f9f-4dc7-4b21-9140-2f98e437ad89_1325x755.jpeg 848w, https://substackcdn.com/image/fetch/$s_!6d9H!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd66b6f9f-4dc7-4b21-9140-2f98e437ad89_1325x755.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!6d9H!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd66b6f9f-4dc7-4b21-9140-2f98e437ad89_1325x755.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!6d9H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd66b6f9f-4dc7-4b21-9140-2f98e437ad89_1325x755.jpeg" width="1325" height="755" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d66b6f9f-4dc7-4b21-9140-2f98e437ad89_1325x755.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:755,&quot;width&quot;:1325,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Actual structured content returned by the MCP snapshot tool&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Actual structured content returned by the MCP snapshot tool" title="Actual structured content returned by the MCP snapshot tool" srcset="https://substackcdn.com/image/fetch/$s_!6d9H!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd66b6f9f-4dc7-4b21-9140-2f98e437ad89_1325x755.jpeg 424w, https://substackcdn.com/image/fetch/$s_!6d9H!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd66b6f9f-4dc7-4b21-9140-2f98e437ad89_1325x755.jpeg 848w, https://substackcdn.com/image/fetch/$s_!6d9H!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd66b6f9f-4dc7-4b21-9140-2f98e437ad89_1325x755.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!6d9H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd66b6f9f-4dc7-4b21-9140-2f98e437ad89_1325x755.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The server listed 12 tools, and the snapshot returned the bid, ask, last trade, previous close, and price change. This gives an AI client structured market data to work with. </p><div><hr></div><h1>What I Would Keep in Mind</h1><p>TickerLayer returns quotes in a similar format across different markets, making them easy to combine in one table. However, some prices were older than the others. This is why we should check when each price was last updated before using it in our analysis.</p><p>I used a reviewer key with elevated access. Before running your own monitor, check your account&#8217;s <a href="https://tickerlayer.com/docs/limits/?utm_medium=sponsored_article">feed access and request limits</a>, especially if you plan to poll frequently.</p><p>TickerLayer describes its data as derived and indicative, and perpetual quotes are calculated composites. My short tests checked whether I could retrieve and process the responses; they did not independently verify price accuracy or long-term reliability. Review the <a href="https://tickerlayer.com/market-data-disclaimer/?utm_medium=sponsored_article">market data disclaimer</a> for your intended use. You can inspect the saved responses in the repository&#8217;s <code>fresh_results</code> folder.</p><div><hr></div><h1>Run the Complete Example</h1><p>From the downloaded repository folder, run:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">python market_monitor.py --capture</code></pre></div><p>The command saves new responses under <code>fresh_results</code>. Open <code>TickerLayer_Outputs.ipynb</code> and change <code>capture_path</code> to your new JSON file to explore them. </p><div><hr></div><h1>Conclusion</h1><p>In this article, we explored how to work with TickerLayer in Python, from combining market quotes in a table to retrieving historical prices. We also covered WebSocket updates and MCP integration. These examples provide a starting point for building a market data workflow for our own projects.</p><p>If you would like to build a similar project, visit <a href="https://tickerlayer.com/?utm_source=nonbranddata&amp;utm_medium=sponsored_article&amp;utm_campaign=market_monitor_2026">TickerLayer</a> to explore the available market data and get an API key. </p><p>You can start with the code in our GitHub repository, then add the stocks, currencies, or other assets you want to follow. From there, try using the results in a research notebook or building a dashboard for your own watchlist.</p>]]></content:encoded></item><item><title><![CDATA[Getting Started With TimesFM-3 for Time-Series Forecasting]]></title><description><![CDATA[Learn how to use TimesFM-3 for time-series prediction with a Python example.]]></description><link>https://www.nb-data.com/p/getting-started-with-timesfm-3-for</link><guid isPermaLink="false">https://www.nb-data.com/p/getting-started-with-timesfm-3-for</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Wed, 14 Oct 2026 15:30:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!deok!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7cc34b2-f09e-4f1e-85ca-6d81e31a9212_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!deok!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7cc34b2-f09e-4f1e-85ca-6d81e31a9212_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!deok!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7cc34b2-f09e-4f1e-85ca-6d81e31a9212_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!deok!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7cc34b2-f09e-4f1e-85ca-6d81e31a9212_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!deok!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7cc34b2-f09e-4f1e-85ca-6d81e31a9212_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!deok!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7cc34b2-f09e-4f1e-85ca-6d81e31a9212_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!deok!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7cc34b2-f09e-4f1e-85ca-6d81e31a9212_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b7cc34b2-f09e-4f1e-85ca-6d81e31a9212_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1601672,&quot;alt&quot;:&quot;Getting Started With TimesFM-3 for Time-Series Forecasting&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/220380448?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7cc34b2-f09e-4f1e-85ca-6d81e31a9212_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Getting Started With TimesFM-3 for Time-Series Forecasting" title="Getting Started With TimesFM-3 for Time-Series Forecasting" srcset="https://substackcdn.com/image/fetch/$s_!deok!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7cc34b2-f09e-4f1e-85ca-6d81e31a9212_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!deok!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7cc34b2-f09e-4f1e-85ca-6d81e31a9212_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!deok!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7cc34b2-f09e-4f1e-85ca-6d81e31a9212_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!deok!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb7cc34b2-f09e-4f1e-85ca-6d81e31a9212_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Time-series forecasting helps us estimate future values so we can plan ahead. For example, we can use historical sales data to predict demand and decide how much inventory to prepare for the coming month. We look for patterns in observations collected over time and use them to make these estimates.</p><p>For this task, we can try <a href="https://research.google/blog/timesfm-3-a-zero-shot-foundation-model-for-multivariate-forecasting/">TimesFM-3</a>, a pretrained forecasting model from Google Research. It allows us to generate predictions without training a model from scratch.</p><p>In this article, we&#8217;ll learn how to use TimesFM-3 for time-series forecasting in Python. Let&#8217;s get into it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>What Is TimesFM-3?</h2><p>TimesFM-3 is a time-series foundation model developed by Google Research. We can provide historical observations and generate predictions with its pretrained weights. This approach is called <strong>zero-shot forecasting</strong> because we don&#8217;t train or fine-tune the model on our dataset.</p><p>Compared with earlier versions, TimesFM-3 adds native support for <strong>multivariate forecasting</strong> and covariates. Multivariate forecasting means predicting related time series together. Covariates are additional variables that can help the prediction, such as temperature or calendar information.</p><p>Some covariates are only available for past dates, while others are known in advance. For example, we have recorded temperatures for the past, but we can calculate weekend dates for the future. We&#8217;ll try both in our example.</p><div><hr></div><h2>Setting Up the Environment</h2><p>First, download the code from the <a href="https://github.com/CornelliusYW/timesfm-3-bike-sharing-tutorial">GitHub repository</a> and install the required packages from the project folder:</p><pre><code><code>python -m pip install -r requirements.txt</code></code></pre><p>We&#8217;ll use the <code>timesfm3</code> interface from the <code>timesfm</code> package. The repository includes the tested dependencies and setup instructions.</p><p>The downloadable model weights are licensed for <strong>non-commercial, non-production use</strong>, so check the <a href="https://huggingface.co/google/timesfm-3.0-pytorch/blob/main/LICENSE">license</a> before using them for your project.</p><div><hr></div><h2>Preparing the Bike Sharing Dataset</h2><p>For the dataset example, I use the <a href="https://archive.ics.uci.edu/dataset/275/bike+sharing+dataset">UCI Bike Sharing dataset</a>. It contains rental counts from Capital Bikeshare during 2011 and 2012, along with weather information. There are daily and hourly files, but we&#8217;ll use <code>day.csv</code> so each observation represents one day.</p><p>Let&#8217;s load the dataset and prepare the historical and test data with the following code:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import io
import urllib.request
import zipfile

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

url = "https://archive.ics.uci.edu/static/public/275/bike+sharing+dataset.zip"
with urllib.request.urlopen(url, timeout=60) as response:
    payload = response.read()

with zipfile.ZipFile(io.BytesIO(payload)) as archive:
    with archive.open("day.csv") as daily_file:
        df = pd.read_csv(daily_file, parse_dates=["dteday"])

df = df.sort_values("dteday").set_index("dteday")
assert df.index.is_unique
assert df.index.equals(
    pd.date_range(df.index.min(), df.index.max(), freq="D")
)
assert df[["cnt", "registered", "casual", "temp"]].notna().all().all()
assert (df["cnt"] == df["registered"] + df["casual"]).all()

horizon = 14
history = df.iloc[:-horizon].copy()
test = df.iloc[-horizon:].copy()</code></pre></div><p>The code above sorts 731 daily observations by date and checks for missing or duplicate dates. We keep the final 14 days as test data and use the earlier observations as our history.</p><p>We&#8217;ll start with <code>cnt</code>, the total rental count, then try registered and casual rentals as related targets.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UotM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577ca176-bd1c-4e7d-ae15-c8cd0589c6cb_1980x792.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UotM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577ca176-bd1c-4e7d-ae15-c8cd0589c6cb_1980x792.png 424w, https://substackcdn.com/image/fetch/$s_!UotM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577ca176-bd1c-4e7d-ae15-c8cd0589c6cb_1980x792.png 848w, https://substackcdn.com/image/fetch/$s_!UotM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577ca176-bd1c-4e7d-ae15-c8cd0589c6cb_1980x792.png 1272w, https://substackcdn.com/image/fetch/$s_!UotM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577ca176-bd1c-4e7d-ae15-c8cd0589c6cb_1980x792.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UotM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577ca176-bd1c-4e7d-ae15-c8cd0589c6cb_1980x792.png" width="1456" height="582" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/577ca176-bd1c-4e7d-ae15-c8cd0589c6cb_1980x792.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:582,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:317884,&quot;alt&quot;:&quot;Getting Started With TimesFM-3 for Time-Series Forecasting&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/220380448?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577ca176-bd1c-4e7d-ae15-c8cd0589c6cb_1980x792.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Getting Started With TimesFM-3 for Time-Series Forecasting" title="Getting Started With TimesFM-3 for Time-Series Forecasting" srcset="https://substackcdn.com/image/fetch/$s_!UotM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577ca176-bd1c-4e7d-ae15-c8cd0589c6cb_1980x792.png 424w, https://substackcdn.com/image/fetch/$s_!UotM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577ca176-bd1c-4e7d-ae15-c8cd0589c6cb_1980x792.png 848w, https://substackcdn.com/image/fetch/$s_!UotM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577ca176-bd1c-4e7d-ae15-c8cd0589c6cb_1980x792.png 1272w, https://substackcdn.com/image/fetch/$s_!UotM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F577ca176-bd1c-4e7d-ae15-c8cd0589c6cb_1980x792.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em>Historical rental counts. Figure generated from the UCI Bike Sharing dataset.</em></figcaption></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vRg1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e1b5e28-64ee-479d-a5bb-3eddd6268b04_1980x792.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vRg1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e1b5e28-64ee-479d-a5bb-3eddd6268b04_1980x792.png 424w, https://substackcdn.com/image/fetch/$s_!vRg1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e1b5e28-64ee-479d-a5bb-3eddd6268b04_1980x792.png 848w, https://substackcdn.com/image/fetch/$s_!vRg1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e1b5e28-64ee-479d-a5bb-3eddd6268b04_1980x792.png 1272w, https://substackcdn.com/image/fetch/$s_!vRg1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e1b5e28-64ee-479d-a5bb-3eddd6268b04_1980x792.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vRg1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e1b5e28-64ee-479d-a5bb-3eddd6268b04_1980x792.png" width="1456" height="582" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4e1b5e28-64ee-479d-a5bb-3eddd6268b04_1980x792.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:582,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:125453,&quot;alt&quot;:&quot;Getting Started With TimesFM-3 for Time-Series Forecasting&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/220380448?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e1b5e28-64ee-479d-a5bb-3eddd6268b04_1980x792.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Getting Started With TimesFM-3 for Time-Series Forecasting" title="Getting Started With TimesFM-3 for Time-Series Forecasting" srcset="https://substackcdn.com/image/fetch/$s_!vRg1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e1b5e28-64ee-479d-a5bb-3eddd6268b04_1980x792.png 424w, https://substackcdn.com/image/fetch/$s_!vRg1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e1b5e28-64ee-479d-a5bb-3eddd6268b04_1980x792.png 848w, https://substackcdn.com/image/fetch/$s_!vRg1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e1b5e28-64ee-479d-a5bb-3eddd6268b04_1980x792.png 1272w, https://substackcdn.com/image/fetch/$s_!vRg1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4e1b5e28-64ee-479d-a5bb-3eddd6268b04_1980x792.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em>The final 14 days are held out. Only the last 60 historical days are shown here; the model receives all 717.</em></figcaption></figure></div><p>We split the data <strong>in date order</strong> so the model only receives observations from before the forecast period.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/getting-started-with-timesfm-3-for?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/getting-started-with-timesfm-3-for?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div><h2>Generating Our First Forecast</h2><p>With the dataset ready, let&#8217;s generate our first prediction using <code>TimesFM3Forecaster</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from timesfm3 import TimesFM3Forecaster

forecaster = TimesFM3Forecaster.from_pretrained(
    "google/timesfm-3.0-pytorch"
)

context = history["cnt"].to_numpy(dtype=np.float32)
univariate = forecaster.predict(
    context=context,
    horizon=horizon,
    return_quantiles=True,
)
print(univariate.forecast.shape)   # (14,)
print(univariate.quantiles.shape)  # (14, 9)</code></pre></div><p>In the code above, <code>context</code> contains the historical rental counts, and <code>horizon</code> sets the number of future steps. Since our observations are daily, <code>horizon=14</code> gives us a two-week forecast.</p><p>The <code>forecast</code> attribute contains the predicted median for each day. With <code>return_quantiles=True</code>, we also get nine quantiles, from the 10th to the 90th percentile, to describe the model&#8217;s uncertainty.</p><div><hr></div><h2>Forecasting Multiple Variables</h2><p>Next, let&#8217;s try the multivariate prediction with the same dataset. This time, we&#8217;ll predict registered and casual rentals together. We&#8217;ll also pass historical temperature and a weekend indicator using the following code:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">future_dates = pd.date_range(
    history.index[-1] + pd.Timedelta(days=1),
    periods=horizon,
    freq="D",
)

targets = history[["registered", "casual"]].to_numpy(dtype=np.float32).T
past_temperature = history[["temp"]].to_numpy(dtype=np.float32).T

all_dates = history.index.append(future_dates)
weekend = (all_dates.dayofweek &gt;= 5).astype(np.float32)[None, :]

multivariate = forecaster.predict(
    context=targets,
    horizon=horizon,
    past_only_covariates=past_temperature,
    past_future_covariates=weekend,
    return_quantiles=True,
)
print(multivariate.forecast.shape)   # (2, 14)
print(multivariate.quantiles.shape)  # (2, 14, 9)</code></pre></div><p>Each row in <code>targets</code> represents a rental category, and each column represents a day. We use <code>.T</code> to arrange the data in this format.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9_z6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d7758d8-5025-47e7-bc48-fb77a8cd55f2_1912x480.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9_z6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d7758d8-5025-47e7-bc48-fb77a8cd55f2_1912x480.png 424w, https://substackcdn.com/image/fetch/$s_!9_z6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d7758d8-5025-47e7-bc48-fb77a8cd55f2_1912x480.png 848w, https://substackcdn.com/image/fetch/$s_!9_z6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d7758d8-5025-47e7-bc48-fb77a8cd55f2_1912x480.png 1272w, https://substackcdn.com/image/fetch/$s_!9_z6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d7758d8-5025-47e7-bc48-fb77a8cd55f2_1912x480.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9_z6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d7758d8-5025-47e7-bc48-fb77a8cd55f2_1912x480.png" width="1456" height="366" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8d7758d8-5025-47e7-bc48-fb77a8cd55f2_1912x480.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:366,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:59129,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/220380448?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d7758d8-5025-47e7-bc48-fb77a8cd55f2_1912x480.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!9_z6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d7758d8-5025-47e7-bc48-fb77a8cd55f2_1912x480.png 424w, https://substackcdn.com/image/fetch/$s_!9_z6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d7758d8-5025-47e7-bc48-fb77a8cd55f2_1912x480.png 848w, https://substackcdn.com/image/fetch/$s_!9_z6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d7758d8-5025-47e7-bc48-fb77a8cd55f2_1912x480.png 1272w, https://substackcdn.com/image/fetch/$s_!9_z6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d7758d8-5025-47e7-bc48-fb77a8cd55f2_1912x480.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The historical inputs must refer to the same dates. The weekend array also covers the forecast period because we know those dates in advance.</p><p>We now have two sets of predictions, with registered rentals in the first row and casual rentals in the second.</p><p>We only provide <strong>historical temperature</strong>. Using the actual temperatures from the test period would introduce data leakage, as those measurements weren&#8217;t available when making the forecast. The weekend indicator is available in advance, but it doesn&#8217;t account for public holidays.</p><div><hr></div><h2>Visualizing and Evaluating the Results</h2><p>To see the prediction result, let&#8217;s create a plot with the historical data and the actual values from our test period:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">q = np.asarray(forecaster.config.quantiles)
lo = int(np.flatnonzero(np.isclose(q, 0.1))[0])
hi = int(np.flatnonzero(np.isclose(q, 0.9))[0])

fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(history.index[-28:], history["cnt"].iloc[-28:], label="History")
ax.plot(future_dates, test["cnt"], "o-", label="Actual")
ax.plot(future_dates, univariate.forecast, label="TimesFM-3 median")
ax.fill_between(
    future_dates,
    univariate.quantiles[:, lo],
    univariate.quantiles[:, hi],
    alpha=0.2,
    label="10th-90th percentile band",
)
ax.set(ylabel="Rentals per day", xlabel="Date")
ax.legend()
fig.autofmt_xdate()
plt.show()</code></pre></div><p>We can also plot registered and casual rentals and add a weekly baseline for comparison:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jheu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12dcd5d4-6305-4387-bfb3-98aa5ff7b3b7_1980x1800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jheu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12dcd5d4-6305-4387-bfb3-98aa5ff7b3b7_1980x1800.png 424w, https://substackcdn.com/image/fetch/$s_!jheu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12dcd5d4-6305-4387-bfb3-98aa5ff7b3b7_1980x1800.png 848w, https://substackcdn.com/image/fetch/$s_!jheu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12dcd5d4-6305-4387-bfb3-98aa5ff7b3b7_1980x1800.png 1272w, https://substackcdn.com/image/fetch/$s_!jheu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12dcd5d4-6305-4387-bfb3-98aa5ff7b3b7_1980x1800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jheu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12dcd5d4-6305-4387-bfb3-98aa5ff7b3b7_1980x1800.png" width="1456" height="1324" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/12dcd5d4-6305-4387-bfb3-98aa5ff7b3b7_1980x1800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1324,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:361226,&quot;alt&quot;:&quot;Getting Started With TimesFM-3 for Time-Series Forecasting&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/220380448?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12dcd5d4-6305-4387-bfb3-98aa5ff7b3b7_1980x1800.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Getting Started With TimesFM-3 for Time-Series Forecasting" title="Getting Started With TimesFM-3 for Time-Series Forecasting" srcset="https://substackcdn.com/image/fetch/$s_!jheu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12dcd5d4-6305-4387-bfb3-98aa5ff7b3b7_1980x1800.png 424w, https://substackcdn.com/image/fetch/$s_!jheu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12dcd5d4-6305-4387-bfb3-98aa5ff7b3b7_1980x1800.png 848w, https://substackcdn.com/image/fetch/$s_!jheu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12dcd5d4-6305-4387-bfb3-98aa5ff7b3b7_1980x1800.png 1272w, https://substackcdn.com/image/fetch/$s_!jheu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12dcd5d4-6305-4387-bfb3-98aa5ff7b3b7_1980x1800.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em>Executed forecasts for December 18&#8211;31, 2012. The shaded regions show model-generated quantiles.</em></figcaption></figure></div><p>The shaded area shows the 10th&#8211;90th percentile band, a <strong>nominal 80% interval</strong>. Its actual coverage still needs to be evaluated.</p><p>To measure prediction error, we&#8217;ll use mean absolute error, or MAE. It calculates the average absolute difference between the predicted and actual values. A lower MAE means a smaller error.</p><p>Let&#8217;s compare the model with a seasonal naive baseline, which repeats the last observed week&#8217;s rental counts:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">weekly_baseline = np.resize(
    history["cnt"].to_numpy()[-7:], horizon
)
actual = test["cnt"].to_numpy()

model_mae = np.mean(np.abs(actual - univariate.forecast))
baseline_mae = np.mean(np.abs(actual - weekly_baseline))
print(f"TimesFM-3 MAE: {model_mae:.2f}")
print(f"Weekly baseline MAE: {baseline_mae:.2f}")</code></pre></div><p>The baseline repeats the same observed week twice, without using any values from the test period.</p><p>The results for all three targets are as follows:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ryEq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F294c8f0e-5000-49d4-9cd5-71dcb36e1d1b_1922x588.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ryEq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F294c8f0e-5000-49d4-9cd5-71dcb36e1d1b_1922x588.png 424w, https://substackcdn.com/image/fetch/$s_!ryEq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F294c8f0e-5000-49d4-9cd5-71dcb36e1d1b_1922x588.png 848w, https://substackcdn.com/image/fetch/$s_!ryEq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F294c8f0e-5000-49d4-9cd5-71dcb36e1d1b_1922x588.png 1272w, https://substackcdn.com/image/fetch/$s_!ryEq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F294c8f0e-5000-49d4-9cd5-71dcb36e1d1b_1922x588.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ryEq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F294c8f0e-5000-49d4-9cd5-71dcb36e1d1b_1922x588.png" width="1456" height="445" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/294c8f0e-5000-49d4-9cd5-71dcb36e1d1b_1922x588.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:445,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:75117,&quot;alt&quot;:&quot;Getting Started With TimesFM-3 for Time-Series Forecasting&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/220380448?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F294c8f0e-5000-49d4-9cd5-71dcb36e1d1b_1922x588.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Getting Started With TimesFM-3 for Time-Series Forecasting" title="Getting Started With TimesFM-3 for Time-Series Forecasting" srcset="https://substackcdn.com/image/fetch/$s_!ryEq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F294c8f0e-5000-49d4-9cd5-71dcb36e1d1b_1922x588.png 424w, https://substackcdn.com/image/fetch/$s_!ryEq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F294c8f0e-5000-49d4-9cd5-71dcb36e1d1b_1922x588.png 848w, https://substackcdn.com/image/fetch/$s_!ryEq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F294c8f0e-5000-49d4-9cd5-71dcb36e1d1b_1922x588.png 1272w, https://substackcdn.com/image/fetch/$s_!ryEq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F294c8f0e-5000-49d4-9cd5-71dcb36e1d1b_1922x588.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For this test period, TimesFM-3 has a lower MAE than the weekly baseline for all three targets. However, the total rental interval contains only three of the 14 actual values.</p><p>The total rental forecast still misses much of the decline toward the end of the test period. Its MAE is only 4.2% below the baseline&#8217;s error, so I wouldn&#8217;t call it reliable from this result alone.</p><p>Compare each target with its own baseline. Casual rentals have smaller counts, so their lower MAE doesn&#8217;t automatically mean a better forecast.</p><div><hr></div><h2>What Should We Know Before Using TimesFM-3?</h2><p>Our example covers one forecast period. We should repeat the evaluation at several starting dates before deciding whether the model meets our needs. The joint example also changes the targets and adds covariates at the same time, so it doesn&#8217;t tell us how much each feature helps.</p><p>Zero-shot forecasting doesn&#8217;t guarantee that a dataset was absent from pretraining. The <a href="https://huggingface.co/google/timesfm-3.0-pytorch">model card</a> doesn&#8217;t confirm whether these bike rental records were included, so this example shouldn&#8217;t be treated as an unseen-data benchmark.</p><p>Keep the total rental forecast separate from the joint predictions. Adding the registered and casual medians or quantile bounds doesn&#8217;t generally produce the total&#8217;s median or a valid interval.</p><p>For a SQL alternative, TimesFM-3 is also available in <a href="https://cloud.google.com/blog/products/data-analytics/bigquery-timesfm-3-0-with-multivariate-time-series-forecasting/">BigQuery preview</a>. </p><div><hr></div><h2>Conclusion</h2><p>In this article, we learned how to use TimesFM-3 for time-series forecasting in Python. We started with daily bike rentals, then tried related targets and additional variables.</p><p>You can find the complete code in the <a href="https://github.com/CornelliusYW/timesfm-3-bike-sharing-tutorial">GitHub repository</a>.</p><p>I hope it helps!</p><div><hr></div><h2><strong>Continue Learning with Non-Brand Data</strong></h2><p>If you are interested in learning more about Data Science, Machine Learning, and AI, you can <a href="https://www.nb-data.com/">subscribe to Non-Brand Data</a>. I regularly share articles, experiments, and examples that we can learn from.</p><p>For readers who want to explore a subject in more detail, the <a href="https://www.nb-data.com/p/subscriber-benefits">paid subscription</a> also includes in-depth tutorials, written courses, notebooks, code, and additional learning materials.</p><p>I also offer <a href="https://mentorcruise.com/mentor/cornelliusyudhawijaya/">1-on-1 mentorship through MentorCruise</a> if you want to discuss your learning direction, Data Science projects, technical challenges, or career development.</p>]]></content:encoded></item><item><title><![CDATA[Can TabPFN-3.5 Compete With Traditional Machine Learning Models?]]></title><description><![CDATA[Compare four models on two datasets to understand their prediction performance and runtime.]]></description><link>https://www.nb-data.com/p/can-tabpfn-35-compete-with-traditional</link><guid isPermaLink="false">https://www.nb-data.com/p/can-tabpfn-35-compete-with-traditional</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sun, 11 Oct 2026 17:11:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!4Gju!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F932ad4e3-56a6-4d33-bf59-5d2c7ea14519_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4Gju!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F932ad4e3-56a6-4d33-bf59-5d2c7ea14519_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4Gju!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F932ad4e3-56a6-4d33-bf59-5d2c7ea14519_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!4Gju!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F932ad4e3-56a6-4d33-bf59-5d2c7ea14519_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!4Gju!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F932ad4e3-56a6-4d33-bf59-5d2c7ea14519_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!4Gju!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F932ad4e3-56a6-4d33-bf59-5d2c7ea14519_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4Gju!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F932ad4e3-56a6-4d33-bf59-5d2c7ea14519_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/932ad4e3-56a6-4d33-bf59-5d2c7ea14519_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1514608,&quot;alt&quot;:&quot;Can TabPFN-3.5 Compete With Traditional Machine Learning Models?&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/219892604?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F932ad4e3-56a6-4d33-bf59-5d2c7ea14519_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Can TabPFN-3.5 Compete With Traditional Machine Learning Models?" title="Can TabPFN-3.5 Compete With Traditional Machine Learning Models?" srcset="https://substackcdn.com/image/fetch/$s_!4Gju!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F932ad4e3-56a6-4d33-bf59-5d2c7ea14519_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!4Gju!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F932ad4e3-56a6-4d33-bf59-5d2c7ea14519_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!4Gju!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F932ad4e3-56a6-4d33-bf59-5d2c7ea14519_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!4Gju!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F932ad4e3-56a6-4d33-bf59-5d2c7ea14519_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Building a machine learning model for tabular data often starts with familiar methods such as Logistic Regression, Random Forest, or CatBoost. We train them on our data and compare their predictions to find a model that suits our problem.</p><p><a href="https://huggingface.co/Prior-Labs/tabpfn_3_5">TabPFN-3.5</a> gives us another option. It uses a pretrained model to make predictions from the examples we provide. I wanted to see how this approach compares with the models we already use, including how long we need to wait for the predictions.</p><p>In this article, we will compare TabPFN-3.5 with Logistic Regression, Random Forest, and CatBoost on two datasets. We will look at their prediction performance and runtime, then discuss the limitations of the comparison. </p><p>Let&#8217;s get into it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><div class="pullquote"><p>All the experiments are contained within this <a href="https://github.com/CornelliusYW/tabpfn-3-5-vs-traditional-ml">repository</a>.</p></div><h1>What makes TabPFN-3.5 different?</h1><p>To understand this comparison, let&#8217;s first look at how TabPFN-3.5 uses our data. It takes labeled training examples as context and predicts the target for new rows. According to <a href="https://huggingface.co/Prior-Labs/tabpfn_3_5">Prior Labs</a>, the model was pretrained on synthetic tabular tasks, and the base checkpoint supports both classification and regression.</p><p>Prior Labs released TabPFN-3.5 on September 15, 2026. Its <a href="https://priorlabs.ai/technical-reports/tabpfn-3-5">release report</a> describes several variants, including Fast, Plus, and Thinking. For this comparison, I used the <strong>base model</strong>.</p><p>With that model, we still use the familiar fit() and predict_proba() methods in <a href="https://github.com/PriorLabs/TabPFN">TabPFN</a>. However, calling fit() does not train the foundation model from scratch. We provide context for a model that has already been pretrained, which differs from fitting Logistic Regression coefficients or building trees from our dataset.</p><p>This also affects how we compare runtime. We need to measure both fitting and prediction to see how long each model takes to produce results. Before we get to those measurements, let&#8217;s prepare the datasets and give every model the same train/test splits.</p><div><hr></div><h1>Preparing our comparison</h1><p>We will use the <a href="https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_breast_cancer.html">Breast Cancer Wisconsin Diagnostic</a> and <a href="https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_wine.html">Wine</a> datasets included with scikit-learn. I chose them because we can load the data without a separate download. They also let us compare the models on a binary classification problem and a multiclass problem.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CBQ2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20e026f2-8144-4580-ba70-06266646f845_1305x178.png 424w, https://substackcdn.com/image/fetch/$s_!CBQ2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20e026f2-8144-4580-ba70-06266646f845_1305x178.png 848w, https://substackcdn.com/image/fetch/$s_!CBQ2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20e026f2-8144-4580-ba70-06266646f845_1305x178.png 1272w, https://substackcdn.com/image/fetch/$s_!CBQ2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20e026f2-8144-4580-ba70-06266646f845_1305x178.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!CBQ2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20e026f2-8144-4580-ba70-06266646f845_1305x178.png" width="1305" height="178" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/20e026f2-8144-4580-ba70-06266646f845_1305x178.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:178,&quot;width&quot;:1305,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Can TabPFN-3.5 Compete With Traditional Machine Learning Models?&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Can TabPFN-3.5 Compete With Traditional Machine Learning Models?" title="Can TabPFN-3.5 Compete With Traditional Machine Learning Models?" srcset="https://substackcdn.com/image/fetch/$s_!CBQ2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20e026f2-8144-4580-ba70-06266646f845_1305x178.png 424w, https://substackcdn.com/image/fetch/$s_!CBQ2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20e026f2-8144-4580-ba70-06266646f845_1305x178.png 848w, https://substackcdn.com/image/fetch/$s_!CBQ2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20e026f2-8144-4580-ba70-06266646f845_1305x178.png 1272w, https://substackcdn.com/image/fetch/$s_!CBQ2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F20e026f2-8144-4580-ba70-06266646f845_1305x178.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div></figure></div><p>In Breast Cancer, we use measurements derived from digitized images of breast masses to predict malignant or benign labels. Wine gives us a different task: using chemical measurements to distinguish three cultivars. Both loaded datasets have numeric features and no missing values so that we can focus this comparison on complete numeric data.</p><p>Their small size makes the experiment easy to repeat, but it also limits what we can conclude about business datasets. We are using Breast Cancer as a classification exercise; these results don&#8217;t establish whether a model is suitable for clinical use.</p><p>With that scope in mind, let&#8217;s create our first train/test split:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

data = load_breast_cancer(as_frame=True)
X_train, X_test, y_train, y_test = train_test_split(
 data.data,
 data.target,
 test_size=0.25,
 stratify=data.target,
 random_state=42
)</code></pre></div><p>We keep 75% of the rows for training and 25% for testing. The stratify argument keeps the class proportions close to those in the full dataset.</p><p>In the notebook, we repeat this with seeds 42 through 46 for both datasets. Every model gets <strong>the same rows within each split</strong>. Four models across two datasets and five splits give us 40 evaluations.</p><div><hr></div><h1>Setting up the models</h1><p>With our splits prepared, we can set up the models. For <a href="https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html">Logistic Regression</a>, we put StandardScaler inside a pipeline:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression

model = make_pipeline(
 StandardScaler(),
 LogisticRegression(C=1.0, max_iter=2000, random_state=42)
)
model.fit(X_train, y_train)
probabilities = model.predict_proba(X_test)</code></pre></div><p>The scaler learns from the training rows and applies that transformation to the test rows. We shouldn&#8217;t scale the full dataset before splitting because that would let test data influence preprocessing.</p><p>The other models receive the original numeric values. Alongside this pipeline, I used <a href="https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html">Random Forest</a>, <a href="https://catboost.ai/docs/en/concepts/python-reference_catboostclassifier">CatBoost</a>, and TabPFN-3.5 with the following settings. I kept these configurations fixed without a hyperparameter search:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ILjj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a047816-af36-4e71-a579-e0ab1f595469_1305x296.png 424w, https://substackcdn.com/image/fetch/$s_!ILjj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a047816-af36-4e71-a579-e0ab1f595469_1305x296.png 848w, https://substackcdn.com/image/fetch/$s_!ILjj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a047816-af36-4e71-a579-e0ab1f595469_1305x296.png 1272w, https://substackcdn.com/image/fetch/$s_!ILjj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a047816-af36-4e71-a579-e0ab1f595469_1305x296.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ILjj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a047816-af36-4e71-a579-e0ab1f595469_1305x296.png" width="1305" height="296" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0a047816-af36-4e71-a579-e0ab1f595469_1305x296.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:296,&quot;width&quot;:1305,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Can TabPFN-3.5 Compete With Traditional Machine Learning Models?&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Can TabPFN-3.5 Compete With Traditional Machine Learning Models?" title="Can TabPFN-3.5 Compete With Traditional Machine Learning Models?" srcset="https://substackcdn.com/image/fetch/$s_!ILjj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a047816-af36-4e71-a579-e0ab1f595469_1305x296.png 424w, https://substackcdn.com/image/fetch/$s_!ILjj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a047816-af36-4e71-a579-e0ab1f595469_1305x296.png 848w, https://substackcdn.com/image/fetch/$s_!ILjj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a047816-af36-4e71-a579-e0ab1f595469_1305x296.png 1272w, https://substackcdn.com/image/fetch/$s_!ILjj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0a047816-af36-4e71-a579-e0ab1f595469_1305x296.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div></figure></div><p>The results therefore describe these configurations, and tuning could change them. The settings also represent different amounts of work: four TabPFN ensemble members shouldn&#8217;t be treated as equivalent to four trees.</p><p>To make sure we test the intended TabPFN model, we select its version explicitly:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">from tabpfn import TabPFNClassifier
from tabpfn.constants import ModelVersion

model = TabPFNClassifier.create_default_for_version(
 ModelVersion.V3_5,
 n_estimators=4,
 device=&#8221;cpu&#8221;,
 random_state=42,
 n_preprocessing_jobs=1,
 show_progress_bar=False
)
model.fit(X_train, y_train)
probabilities = model.predict_proba(X_test)</code></pre></div><p>The Python package version is tabpfn==9.1.0, while the model we&#8217;re testing is <strong>TabPFN-3.5</strong>. Choosing ModelVersion.V3_5 keeps the experiment from depending on the package&#8217;s default model version.</p><p>To make that choice reproducible, the notebook pins the <a href="https://huggingface.co/Prior-Labs/tabpfn_3_5">TabPFN-3.5 checkpoint</a> tabpfn-v3.5-20260909.safetensors and records its SHA256 hash. Before the first download, we need to accept the model license and enable authenticated access through <a href="https://ux.priorlabs.ai">Prior Labs</a>. Keep the access key outside the notebook. Once the weights are available, inference runs locally. The setup output below confirms which checkpoint we used and that all four models ran.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JXbt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F878386c4-e287-49c5-8722-b9615387f8b4_1219x306.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JXbt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F878386c4-e287-49c5-8722-b9615387f8b4_1219x306.png 424w, https://substackcdn.com/image/fetch/$s_!JXbt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F878386c4-e287-49c5-8722-b9615387f8b4_1219x306.png 848w, https://substackcdn.com/image/fetch/$s_!JXbt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F878386c4-e287-49c5-8722-b9615387f8b4_1219x306.png 1272w, https://substackcdn.com/image/fetch/$s_!JXbt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F878386c4-e287-49c5-8722-b9615387f8b4_1219x306.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!JXbt!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F878386c4-e287-49c5-8722-b9615387f8b4_1219x306.png" width="1200" height="301.2305168170632" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/878386c4-e287-49c5-8722-b9615387f8b4_1219x306.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:306,&quot;width&quot;:1219,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Screenshot by the author. The setup output records the checkpoint and confirms that all four models ran.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="Screenshot by the author. The setup output records the checkpoint and confirms that all four models ran." title="Screenshot by the author. The setup output records the checkpoint and confirms that all four models ran." srcset="https://substackcdn.com/image/fetch/$s_!JXbt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F878386c4-e287-49c5-8722-b9615387f8b4_1219x306.png 424w, https://substackcdn.com/image/fetch/$s_!JXbt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F878386c4-e287-49c5-8722-b9615387f8b4_1219x306.png 848w, https://substackcdn.com/image/fetch/$s_!JXbt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F878386c4-e287-49c5-8722-b9615387f8b4_1219x306.png 1272w, https://substackcdn.com/image/fetch/$s_!JXbt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F878386c4-e287-49c5-8722-b9615387f8b4_1219x306.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h1>Comparing prediction performance</h1><p>Now that all four models are available, we can compare their predictions. Our main metric is <strong>balanced accuracy</strong>. We calculate the share of correct predictions within each class, then average those shares. This gives each class equal weight.</p><p>We also record accuracy and macro F1. F1 combines precision and recall; the macro average gives each class equal weight. <strong>Log loss</strong> evaluates the probabilities and penalizes confident mistakes, so a lower value is better.</p><p>The notebook includes ROC AUC as well. For Breast Cancer, class 1 is benign and is treated as the positive class for binary AUC. Wine uses macro one-vs-rest AUC. All class predictions come from the largest predicted probability.</p><p>For the main comparison below, we focus on accuracy, balanced accuracy, macro F1, and log loss. The tables show the mean and sample standard deviation across five splits. Because the test sets overlap, the standard deviations describe the variation we observed. They aren&#8217;t confidence intervals or proof that one model will perform better on another dataset.</p><h3>Breast Cancer Wisconsin</h3><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7GOj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4dde2e7-abdd-43bf-9f6f-f62218d9b25f_1305x296.png 424w, https://substackcdn.com/image/fetch/$s_!7GOj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4dde2e7-abdd-43bf-9f6f-f62218d9b25f_1305x296.png 848w, https://substackcdn.com/image/fetch/$s_!7GOj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4dde2e7-abdd-43bf-9f6f-f62218d9b25f_1305x296.png 1272w, https://substackcdn.com/image/fetch/$s_!7GOj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4dde2e7-abdd-43bf-9f6f-f62218d9b25f_1305x296.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!7GOj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4dde2e7-abdd-43bf-9f6f-f62218d9b25f_1305x296.png" width="1305" height="296" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b4dde2e7-abdd-43bf-9f6f-f62218d9b25f_1305x296.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:296,&quot;width&quot;:1305,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Can TabPFN-3.5 Compete With Traditional Machine Learning Models?&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Can TabPFN-3.5 Compete With Traditional Machine Learning Models?" title="Can TabPFN-3.5 Compete With Traditional Machine Learning Models?" srcset="https://substackcdn.com/image/fetch/$s_!7GOj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4dde2e7-abdd-43bf-9f6f-f62218d9b25f_1305x296.png 424w, https://substackcdn.com/image/fetch/$s_!7GOj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4dde2e7-abdd-43bf-9f6f-f62218d9b25f_1305x296.png 848w, https://substackcdn.com/image/fetch/$s_!7GOj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4dde2e7-abdd-43bf-9f6f-f62218d9b25f_1305x296.png 1272w, https://substackcdn.com/image/fetch/$s_!7GOj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4dde2e7-abdd-43bf-9f6f-f62218d9b25f_1305x296.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div></figure></div><p>For Breast Cancer, Logistic Regression has the highest average balanced accuracy. Its difference from TabPFN is about <strong>0.11 percentage points</strong>. I wouldn&#8217;t make much of that gap on these splits. TabPFN beats Logistic Regression twice, loses twice, and ties once when we compare the matching splits.</p><p>Logistic Regression has the better average class prediction scores here, while TabPFN has a slightly lower log loss. That distinction matters if we plan to use the probabilities. Predicting the correct class and assigning a good probability aren&#8217;t the same thing.</p><h3>Wine</h3><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NtnI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54523b78-5690-45df-940a-8b779ff53c85_1305x296.png 424w, https://substackcdn.com/image/fetch/$s_!NtnI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54523b78-5690-45df-940a-8b779ff53c85_1305x296.png 848w, https://substackcdn.com/image/fetch/$s_!NtnI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54523b78-5690-45df-940a-8b779ff53c85_1305x296.png 1272w, https://substackcdn.com/image/fetch/$s_!NtnI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54523b78-5690-45df-940a-8b779ff53c85_1305x296.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!NtnI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54523b78-5690-45df-940a-8b779ff53c85_1305x296.png" width="1305" height="296" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/54523b78-5690-45df-940a-8b779ff53c85_1305x296.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:296,&quot;width&quot;:1305,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Can TabPFN-3.5 Compete With Traditional Machine Learning Models?&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Can TabPFN-3.5 Compete With Traditional Machine Learning Models?" title="Can TabPFN-3.5 Compete With Traditional Machine Learning Models?" srcset="https://substackcdn.com/image/fetch/$s_!NtnI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54523b78-5690-45df-940a-8b779ff53c85_1305x296.png 424w, https://substackcdn.com/image/fetch/$s_!NtnI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54523b78-5690-45df-940a-8b779ff53c85_1305x296.png 848w, https://substackcdn.com/image/fetch/$s_!NtnI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54523b78-5690-45df-940a-8b779ff53c85_1305x296.png 1272w, https://substackcdn.com/image/fetch/$s_!NtnI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F54523b78-5690-45df-940a-8b779ff53c85_1305x296.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div></div></div></div></figure></div><p>Moving to Wine changes the ranking. Here, TabPFN performs better across the four reported metrics. Its balanced accuracy is about <strong>0.74 percentage points</strong> above Random Forest, the next model on that metric. On matching splits, TabPFN beats Random Forest twice and ties it three times.</p><p>The probability results give us another reason to consider TabPFN here. Random Forest ranks second on balanced accuracy but last on log loss, while TabPFN has the lowest log loss. If we only checked class predictions, we would miss this difference.</p><p>I would consider this a good result for TabPFN under these settings. I&#8217;d still want to see whether the improvement holds on a larger test set.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OZV1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1807b344-7672-48c4-bedb-c3d0903e57d3_1219x474.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OZV1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1807b344-7672-48c4-bedb-c3d0903e57d3_1219x474.png 424w, https://substackcdn.com/image/fetch/$s_!OZV1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1807b344-7672-48c4-bedb-c3d0903e57d3_1219x474.png 848w, https://substackcdn.com/image/fetch/$s_!OZV1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1807b344-7672-48c4-bedb-c3d0903e57d3_1219x474.png 1272w, https://substackcdn.com/image/fetch/$s_!OZV1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1807b344-7672-48c4-bedb-c3d0903e57d3_1219x474.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!OZV1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1807b344-7672-48c4-bedb-c3d0903e57d3_1219x474.png" width="1219" height="474" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1807b344-7672-48c4-bedb-c3d0903e57d3_1219x474.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:474,&quot;width&quot;:1219,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Can TabPFN-3.5 Compete With Traditional Machine Learning Models?&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Can TabPFN-3.5 Compete With Traditional Machine Learning Models?" title="Can TabPFN-3.5 Compete With Traditional Machine Learning Models?" srcset="https://substackcdn.com/image/fetch/$s_!OZV1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1807b344-7672-48c4-bedb-c3d0903e57d3_1219x474.png 424w, https://substackcdn.com/image/fetch/$s_!OZV1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1807b344-7672-48c4-bedb-c3d0903e57d3_1219x474.png 848w, https://substackcdn.com/image/fetch/$s_!OZV1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1807b344-7672-48c4-bedb-c3d0903e57d3_1219x474.png 1272w, https://substackcdn.com/image/fetch/$s_!OZV1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1807b344-7672-48c4-bedb-c3d0903e57d3_1219x474.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Alongside these summaries, we save the probabilities for every test row. This lets us inspect the predictions behind a score difference on the same rows. Prediction quality is only one part of choosing a model, though, so we also need to look at how long those predictions take.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/can-tabpfn-35-compete-with-traditional?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/can-tabpfn-35-compete-with-traditional?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div><h1>How long do the models take?</h1><p>To measure that time, I ran all four models on an Intel Core i7-12700H CPU with four compute threads. TabPFN uses one preprocessing worker. The notebook records the full environment, including Windows and Python 3.11.5.</p><p>Before timing the comparison, we run each model on a small subset. Then every measured run creates a fresh estimator, fits it, and predicts probabilities for the full test batch.</p><p>These timings start after package imports and the first weight download. Loading TabPFN&#8217;s cached weights stays inside the fit measurement, so the results include that work. The table below separates fit and prediction time to show where each model spends its time.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MhEW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35f6e9b2-5efc-4396-83a4-8b8c610a7260_1305x532.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MhEW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35f6e9b2-5efc-4396-83a4-8b8c610a7260_1305x532.png 424w, https://substackcdn.com/image/fetch/$s_!MhEW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35f6e9b2-5efc-4396-83a4-8b8c610a7260_1305x532.png 848w, https://substackcdn.com/image/fetch/$s_!MhEW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35f6e9b2-5efc-4396-83a4-8b8c610a7260_1305x532.png 1272w, https://substackcdn.com/image/fetch/$s_!MhEW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35f6e9b2-5efc-4396-83a4-8b8c610a7260_1305x532.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!MhEW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35f6e9b2-5efc-4396-83a4-8b8c610a7260_1305x532.png" width="1305" height="532" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/35f6e9b2-5efc-4396-83a4-8b8c610a7260_1305x532.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:532,&quot;width&quot;:1305,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Can TabPFN-3.5 Compete With Traditional Machine Learning Models?&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Can TabPFN-3.5 Compete With Traditional Machine Learning Models?" title="Can TabPFN-3.5 Compete With Traditional Machine Learning Models?" srcset="https://substackcdn.com/image/fetch/$s_!MhEW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35f6e9b2-5efc-4396-83a4-8b8c610a7260_1305x532.png 424w, https://substackcdn.com/image/fetch/$s_!MhEW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35f6e9b2-5efc-4396-83a4-8b8c610a7260_1305x532.png 848w, https://substackcdn.com/image/fetch/$s_!MhEW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35f6e9b2-5efc-4396-83a4-8b8c610a7260_1305x532.png 1272w, https://substackcdn.com/image/fetch/$s_!MhEW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F35f6e9b2-5efc-4396-83a4-8b8c610a7260_1305x532.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The part I would pay attention to is <strong>TabPFN&#8217;s prediction time</strong>. Most of its total runtime comes after fit(). Timing fit alone would leave out most of the work in this experiment.</p><p>Logistic Regression finishes fastest on both datasets, while TabPFN takes the longest total time. For a small offline analysis, waiting a few seconds might be acceptable. If we need to repeat the process many times, I would check whether the prediction improvement is worth that extra time.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tmVE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf670769-2c50-470d-b7db-f7175cb7f522_1219x474.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tmVE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf670769-2c50-470d-b7db-f7175cb7f522_1219x474.png 424w, https://substackcdn.com/image/fetch/$s_!tmVE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf670769-2c50-470d-b7db-f7175cb7f522_1219x474.png 848w, https://substackcdn.com/image/fetch/$s_!tmVE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf670769-2c50-470d-b7db-f7175cb7f522_1219x474.png 1272w, https://substackcdn.com/image/fetch/$s_!tmVE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf670769-2c50-470d-b7db-f7175cb7f522_1219x474.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!tmVE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf670769-2c50-470d-b7db-f7175cb7f522_1219x474.png" width="1219" height="474" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cf670769-2c50-470d-b7db-f7175cb7f522_1219x474.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:474,&quot;width&quot;:1219,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Image by the author. Total runtime uses a logarithmic axis.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Image by the author. Total runtime uses a logarithmic axis." title="Image by the author. Total runtime uses a logarithmic axis." srcset="https://substackcdn.com/image/fetch/$s_!tmVE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf670769-2c50-470d-b7db-f7175cb7f522_1219x474.png 424w, https://substackcdn.com/image/fetch/$s_!tmVE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf670769-2c50-470d-b7db-f7175cb7f522_1219x474.png 848w, https://substackcdn.com/image/fetch/$s_!tmVE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf670769-2c50-470d-b7db-f7175cb7f522_1219x474.png 1272w, https://substackcdn.com/image/fetch/$s_!tmVE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf670769-2c50-470d-b7db-f7175cb7f522_1219x474.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em>Image by the author. Total runtime uses a logarithmic axis.</em></p><p>When reading this chart, remember that we timed the first full-test prediction from a fresh estimator on the CPU after a separate warmup. These measurements don&#8217;t cover GPU performance or repeated predictions from a model kept in memory. A batch time also doesn&#8217;t tell us the response time for one request in a production service. We can review the measured scores and timings together in the notebook output below.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lpfn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ba2b5eb-1725-4ae9-9f20-5ca845bb6528_1219x976.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lpfn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ba2b5eb-1725-4ae9-9f20-5ca845bb6528_1219x976.png 424w, https://substackcdn.com/image/fetch/$s_!lpfn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ba2b5eb-1725-4ae9-9f20-5ca845bb6528_1219x976.png 848w, https://substackcdn.com/image/fetch/$s_!lpfn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ba2b5eb-1725-4ae9-9f20-5ca845bb6528_1219x976.png 1272w, https://substackcdn.com/image/fetch/$s_!lpfn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ba2b5eb-1725-4ae9-9f20-5ca845bb6528_1219x976.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!lpfn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ba2b5eb-1725-4ae9-9f20-5ca845bb6528_1219x976.png" width="1219" height="976" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8ba2b5eb-1725-4ae9-9f20-5ca845bb6528_1219x976.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:976,&quot;width&quot;:1219,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Can TabPFN-3.5 Compete With Traditional Machine Learning Models?&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Can TabPFN-3.5 Compete With Traditional Machine Learning Models?" title="Can TabPFN-3.5 Compete With Traditional Machine Learning Models?" srcset="https://substackcdn.com/image/fetch/$s_!lpfn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ba2b5eb-1725-4ae9-9f20-5ca845bb6528_1219x976.png 424w, https://substackcdn.com/image/fetch/$s_!lpfn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ba2b5eb-1725-4ae9-9f20-5ca845bb6528_1219x976.png 848w, https://substackcdn.com/image/fetch/$s_!lpfn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ba2b5eb-1725-4ae9-9f20-5ca845bb6528_1219x976.png 1272w, https://substackcdn.com/image/fetch/$s_!lpfn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ba2b5eb-1725-4ae9-9f20-5ca845bb6528_1219x976.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h1>What would I use?</h1><p>Taking the prediction scores and runtime together, I would still start with Logistic Regression for these small numeric datasets. It gives us strong scores with very little runtime, so I would run it before deciding whether we need another model.</p><p>I would include TabPFN in the comparison too. The Wine result gives us a reason to test it, especially when we care about the predicted probabilities. It doesn&#8217;t give us a reason to assume it will perform better on every table.</p><p>To see why those probability scores matter, imagine we&#8217;re using predictions to decide which records to review first. I would compare the probability scores and inspect the mistakes alongside runtime. If our task needs frequent predictions, the extra computation could change which model I choose.</p><p>For the same reason, I would keep Random Forest and CatBoost in the comparison when testing our own data. We used fixed settings here, and neither dataset tells us how the models compare when categorical features or tuning matter. Those gaps bring us to the limits of this experiment.</p><div><hr></div><h1>Where this comparison is limited</h1><p>The first limitation is the amount of test data. There are only 45 Wine test rows in each split. A few changed predictions can affect the ranking, and our five overlapping holdouts aren&#8217;t independent experiments. I&#8217;d want to test more data before treating the Wine result as a reason to replace another model.</p><p>We also haven&#8217;t tuned the models. To extend this comparison, we could search within the training data and record the search budget for each model. The test labels should stay out of those choices.</p><p>Beyond tuning, the data itself needs a broader comparison. Both datasets are complete numeric tables, so we haven&#8217;t tested missing values or categorical features. We also haven&#8217;t measured larger datasets or peak memory. Any of these could change which model works best for our task.</p><p>Extending the experiment also means choosing a split that matches our data. For records ordered by time or connected through customers, we&#8217;d need a time-based or group-based split. Random splitting could put closely related records in both training and testing.</p><div class="community-chat" data-attrs="{&quot;url&quot;:&quot;https://open.substack.com/pub/cornellius/chat?utm_source=chat_embed&quot;,&quot;subdomain&quot;:&quot;cornellius&quot;,&quot;pub&quot;:{&quot;id&quot;:37262,&quot;name&quot;:&quot;Non-Brand Data&quot;,&quot;author_name&quot;:&quot;Cornellius Yudha Wijaya&quot;,&quot;author_photo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!eEx-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F583076b2-657b-44bf-8aa9-9263e5bf04f0_544x544.png&quot;}}" data-component-name="CommunityChatRenderPlaceholder"></div><div><hr></div><h1>Conclusion</h1><p>In this article, we compared TabPFN-3.5 with Logistic Regression, Random Forest, and CatBoost on two datasets using the same five train/test splits. We measured prediction performance and CPU runtime, then discussed the comparison&#8217;s limitations.</p><p>TabPFN led on Wine&#8217;s balanced accuracy and had the lowest log loss on both datasets. Logistic Regression was slightly ahead on Breast Cancer&#8217;s balanced accuracy and was much faster. These results show why we need to consider both prediction quality and runtime.</p><p>I would include TabPFN alongside traditional models when testing our own data. The <a href="https://github.com/CornelliusYW/tabpfn-3-5-vs-traditional-ml">comparison notebook and Python code</a> provide a starting point for finding which model suits our task.</p><div><hr></div><h1>Continue Learning with Non-Brand Data</h1><p>If you are interested in learning more about Data Science, Machine Learning, and AI, you can <a href="https://www.nb-data.com/">subscribe to Non-Brand Data</a>. I regularly share articles, experiments, and examples that we can learn from.</p><p>For readers who want to explore a subject in more detail, the <a href="https://www.nb-data.com/p/subscriber-benefits">paid subscription</a> also includes in-depth tutorials, written courses, notebooks, code, and additional learning materials.</p><p>I also offer <a href="https://mentorcruise.com/mentor/cornelliusyudhawijaya/">1-on-1 mentorship through MentorCruise</a> if you want to discuss your learning direction, Data Science projects, technical challenges, or career development.</p>]]></content:encoded></item><item><title><![CDATA[Module 14: Writing the results and recommendations]]></title><description><![CDATA[A SQL result can be correct and still make a weak report. The table gives the numbers, but the reader still needs to know what they mean and what they don't show.]]></description><link>https://www.nb-data.com/p/module-14-writing-the-results-and</link><guid isPermaLink="false">https://www.nb-data.com/p/module-14-writing-the-results-and</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sun, 04 Oct 2026 09:55:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hJ-i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fa80572-6098-47fe-8def-9eb1fdfc19e8_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hJ-i!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fa80572-6098-47fe-8def-9eb1fdfc19e8_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hJ-i!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fa80572-6098-47fe-8def-9eb1fdfc19e8_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!hJ-i!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fa80572-6098-47fe-8def-9eb1fdfc19e8_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!hJ-i!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fa80572-6098-47fe-8def-9eb1fdfc19e8_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!hJ-i!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fa80572-6098-47fe-8def-9eb1fdfc19e8_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hJ-i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fa80572-6098-47fe-8def-9eb1fdfc19e8_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8fa80572-6098-47fe-8def-9eb1fdfc19e8_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1460390,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/218752127?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fa80572-6098-47fe-8def-9eb1fdfc19e8_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hJ-i!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fa80572-6098-47fe-8def-9eb1fdfc19e8_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!hJ-i!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fa80572-6098-47fe-8def-9eb1fdfc19e8_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!hJ-i!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fa80572-6098-47fe-8def-9eb1fdfc19e8_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!hJ-i!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fa80572-6098-47fe-8def-9eb1fdfc19e8_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Previous module:</strong><a href="https://www.nb-data.com/p/module-13-when-a-sql-query-needs?r=3kmaf"> Module 13, When an SQL query needs optimization</a>.</p><p>In this module, we will use a March order analysis to write a short report. We will build the final table, check the total, and then write the findings, limitations, and recommendations.</p><p>The report is also your final course project. It brings together the question, SQL, result, and checks from the earlier modules.</p><p>After this module, you can:</p><ul><li><p>Build a report table with prep, metrics, and final</p></li><li><p>Write a finding supported by the result</p></li><li><p>Explain what the result doesn&#8217;t show</p></li><li><p>Recommend a specific next check</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p></li></ul><div><hr></div>
      <p>
          <a href="https://www.nb-data.com/p/module-14-writing-the-results-and">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Module 13: When a SQL query needs optimization]]></title><description><![CDATA[In this module, we will learn how to read a PostgreSQL query plan and decide whether an index is worth keeping.]]></description><link>https://www.nb-data.com/p/module-13-when-a-sql-query-needs</link><guid isPermaLink="false">https://www.nb-data.com/p/module-13-when-a-sql-query-needs</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sun, 04 Oct 2026 09:02:07 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!XFKV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5633d6cc-d636-4a60-ac6a-d58ed2e2aa09_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XFKV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5633d6cc-d636-4a60-ac6a-d58ed2e2aa09_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XFKV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5633d6cc-d636-4a60-ac6a-d58ed2e2aa09_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!XFKV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5633d6cc-d636-4a60-ac6a-d58ed2e2aa09_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!XFKV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5633d6cc-d636-4a60-ac6a-d58ed2e2aa09_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!XFKV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5633d6cc-d636-4a60-ac6a-d58ed2e2aa09_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XFKV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5633d6cc-d636-4a60-ac6a-d58ed2e2aa09_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5633d6cc-d636-4a60-ac6a-d58ed2e2aa09_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1535013,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/218674406?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5633d6cc-d636-4a60-ac6a-d58ed2e2aa09_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XFKV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5633d6cc-d636-4a60-ac6a-d58ed2e2aa09_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!XFKV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5633d6cc-d636-4a60-ac6a-d58ed2e2aa09_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!XFKV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5633d6cc-d636-4a60-ac6a-d58ed2e2aa09_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!XFKV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5633d6cc-d636-4a60-ac6a-d58ed2e2aa09_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Previous module:</strong> <a href="https://www.nb-data.com/p/module-12-checking-whether-your-result?r=3kmaf">Module 12, Checking whether your result is right</a>.</p><p>A slow query doesn&#8217;t always need another index. Before changing anything, we need to understand how PostgreSQL reads the table and how many rows the filter keeps.</p><p>For example, a tracking-code lookup returns one row from 30,000 orders. An index is useful in this case because the filter is very selective. A status filter that keeps 24,000 rows is different because PostgreSQL still needs most of the table.</p><p>For me, query optimization starts after the result is correct. We should read the plan, change one thing, and confirm that the query still returns the same result.</p><p>We will compare a sequential scan with an index scan, then check why PostgreSQL can still choose a sequential scan when an index exists.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div>
      <p>
          <a href="https://www.nb-data.com/p/module-13-when-a-sql-query-needs">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Module 12: Checking whether your result is right]]></title><description><![CDATA[Let's learn how to check a SQL result before we use it in a report or recommendation.]]></description><link>https://www.nb-data.com/p/module-12-checking-whether-your-result</link><guid isPermaLink="false">https://www.nb-data.com/p/module-12-checking-whether-your-result</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Fri, 02 Oct 2026 18:00:34 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!kc0O!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23e796d-c42b-4c7b-90ea-addf2c4d7c79_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kc0O!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23e796d-c42b-4c7b-90ea-addf2c4d7c79_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kc0O!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23e796d-c42b-4c7b-90ea-addf2c4d7c79_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!kc0O!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23e796d-c42b-4c7b-90ea-addf2c4d7c79_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!kc0O!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23e796d-c42b-4c7b-90ea-addf2c4d7c79_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!kc0O!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23e796d-c42b-4c7b-90ea-addf2c4d7c79_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kc0O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23e796d-c42b-4c7b-90ea-addf2c4d7c79_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a23e796d-c42b-4c7b-90ea-addf2c4d7c79_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1379289,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/218531780?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23e796d-c42b-4c7b-90ea-addf2c4d7c79_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kc0O!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23e796d-c42b-4c7b-90ea-addf2c4d7c79_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!kc0O!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23e796d-c42b-4c7b-90ea-addf2c4d7c79_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!kc0O!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23e796d-c42b-4c7b-90ea-addf2c4d7c79_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!kc0O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa23e796d-c42b-4c7b-90ea-addf2c4d7c79_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Previous module:</strong> <a href="https://www.nb-data.com/p/module-11-using-window-functions?r=3kmaf">Module 11, Using window functions without changing the row count</a>.</p><p>A SQL query could run without an error, but the result could still be wrong for the question we want to answer.</p><p>For example, a weekly report shows delivered revenue of 520.00, while a monthly report shows 700.00. Both queries are valid. The difference happens because the weekly query starts on 4 March and the monthly query starts on 1 March. Each query counts a different group of orders.</p><p>For me, checking the population is the first thing to do when two numbers don&#8217;t match. Don&#8217;t change the query until the numbers happen to agree. Instead, compare the filters, find the rows that only appear in one result, and check whether the join changes the row count.</p><p>We will use those steps to compare weekly and monthly revenue. After that, we will check duplicate payment IDs and unmatched records.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div>
      <p>
          <a href="https://www.nb-data.com/p/module-12-checking-whether-your-result">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Module 11: Using window functions without changing the row count]]></title><description><![CDATA[Let&#8217;s learn how to calculate totals, running values, and ranks while keeping every order row in the result.]]></description><link>https://www.nb-data.com/p/module-11-using-window-functions</link><guid isPermaLink="false">https://www.nb-data.com/p/module-11-using-window-functions</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Fri, 02 Oct 2026 16:58:03 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!LVcJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f41537-ca94-45e0-bf01-27af737b5c27_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LVcJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f41537-ca94-45e0-bf01-27af737b5c27_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LVcJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f41537-ca94-45e0-bf01-27af737b5c27_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!LVcJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f41537-ca94-45e0-bf01-27af737b5c27_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!LVcJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f41537-ca94-45e0-bf01-27af737b5c27_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!LVcJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f41537-ca94-45e0-bf01-27af737b5c27_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LVcJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f41537-ca94-45e0-bf01-27af737b5c27_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/87f41537-ca94-45e0-bf01-27af737b5c27_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1374472,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/218526966?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f41537-ca94-45e0-bf01-27af737b5c27_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LVcJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f41537-ca94-45e0-bf01-27af737b5c27_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!LVcJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f41537-ca94-45e0-bf01-27af737b5c27_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!LVcJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f41537-ca94-45e0-bf01-27af737b5c27_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!LVcJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87f41537-ca94-45e0-bf01-27af737b5c27_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Previous module:</strong> <a href="https://open.substack.com/pub/cornellius/p/module-10-breaking-a-query-into-steps?r=3kmaf&amp;utm_campaign=post&amp;utm_medium=web&amp;showWelcomeOnShare=true">Module 10, Breaking a query into steps with subqueries and CTEs</a>.</p>
      <p>
          <a href="https://www.nb-data.com/p/module-11-using-window-functions">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Module 10: Breaking a query into steps with subqueries and CTEs]]></title><description><![CDATA[Let's learn how to split a long SQL query into named steps and check the result from each step.]]></description><link>https://www.nb-data.com/p/module-10-breaking-a-query-into-steps</link><guid isPermaLink="false">https://www.nb-data.com/p/module-10-breaking-a-query-into-steps</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Fri, 02 Oct 2026 15:54:40 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!9iaz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ce35996-c760-49d1-9f0f-8d2662fd880e_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9iaz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ce35996-c760-49d1-9f0f-8d2662fd880e_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9iaz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ce35996-c760-49d1-9f0f-8d2662fd880e_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!9iaz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ce35996-c760-49d1-9f0f-8d2662fd880e_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!9iaz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ce35996-c760-49d1-9f0f-8d2662fd880e_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!9iaz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ce35996-c760-49d1-9f0f-8d2662fd880e_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9iaz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ce35996-c760-49d1-9f0f-8d2662fd880e_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2ce35996-c760-49d1-9f0f-8d2662fd880e_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1386180,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/218452592?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ce35996-c760-49d1-9f0f-8d2662fd880e_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!9iaz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ce35996-c760-49d1-9f0f-8d2662fd880e_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!9iaz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ce35996-c760-49d1-9f0f-8d2662fd880e_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!9iaz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ce35996-c760-49d1-9f0f-8d2662fd880e_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!9iaz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ce35996-c760-49d1-9f0f-8d2662fd880e_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Previous module:</strong> <a href="https://www.nb-data.com/p/module-9-when-a-join-increases-your?r=3kmaf">Module 9, When a join increases your numbers</a>.</p><p>A long query isn&#8217;t always a complicated query. Sometimes it contains several small tasks that have been placed inside one another. The SQL can still return the correct answer, but it becomes difficult to see which rows were filtered, when the grain changed, and where a wrong number first appeared.</p><p>Subqueries and common table expressions, usually called CTEs, help us separate those tasks. A subquery places one query inside another. A CTE gives a query result a name with WITH so that we can read and test the steps in order.</p><p>In this module, we&#8217;ll rewrite one nested query as prep, metrics, and final. We will also check the number of rows at each stage and confirm that the new query returns the same result as the old one.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div>
      <p>
          <a href="https://www.nb-data.com/p/module-10-breaking-a-query-into-steps">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Context Engineering for Data Science]]></title><description><![CDATA[Helping AI solve the right data science problem]]></description><link>https://www.nb-data.com/p/context-engineering-for-data-science</link><guid isPermaLink="false">https://www.nb-data.com/p/context-engineering-for-data-science</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Fri, 02 Oct 2026 12:51:08 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!P1ua!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1541b00-7576-4bc2-b388-4006f3560f67_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!P1ua!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1541b00-7576-4bc2-b388-4006f3560f67_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!P1ua!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1541b00-7576-4bc2-b388-4006f3560f67_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!P1ua!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1541b00-7576-4bc2-b388-4006f3560f67_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!P1ua!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1541b00-7576-4bc2-b388-4006f3560f67_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!P1ua!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1541b00-7576-4bc2-b388-4006f3560f67_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!P1ua!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1541b00-7576-4bc2-b388-4006f3560f67_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e1541b00-7576-4bc2-b388-4006f3560f67_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1402453,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/218450799?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1541b00-7576-4bc2-b388-4006f3560f67_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!P1ua!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1541b00-7576-4bc2-b388-4006f3560f67_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!P1ua!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1541b00-7576-4bc2-b388-4006f3560f67_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!P1ua!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1541b00-7576-4bc2-b388-4006f3560f67_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!P1ua!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe1541b00-7576-4bc2-b388-4006f3560f67_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>AI coding agents can already do many tasks in a data science project.</p><p>They can explore the dataset, prepare the features, train several models, and save the final pipeline. With a simple prompt, we can build a complete machine learning workflow quickly.</p><p>However, building a model is different from understanding the problem behind it.</p><p>I experienced this difference when I asked an AI agent to build a customer churn model. I ran the experiment twice with the same dataset, environment, and prompt. The only difference was the project context available to the agent.</p><p>Both agents created a working pipeline. Both avoided data leakage, used time-based validation, and selected logistic regression. However, they did not evaluate the same problem.</p><p>This is where context engineering could become useful.</p><p>In this article, I want to explore how we can give an AI agent the data-science context it needs. I also performed a small experiment to see what happened when the project included a prediction contract.</p><p>Let&#8217;s get into it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pr4c!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c376ddf-fc4c-4e1e-93b6-d0163307e6b0_960x1440.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pr4c!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c376ddf-fc4c-4e1e-93b6-d0163307e6b0_960x1440.png 424w, https://substackcdn.com/image/fetch/$s_!pr4c!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c376ddf-fc4c-4e1e-93b6-d0163307e6b0_960x1440.png 848w, https://substackcdn.com/image/fetch/$s_!pr4c!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c376ddf-fc4c-4e1e-93b6-d0163307e6b0_960x1440.png 1272w, https://substackcdn.com/image/fetch/$s_!pr4c!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c376ddf-fc4c-4e1e-93b6-d0163307e6b0_960x1440.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!pr4c!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c376ddf-fc4c-4e1e-93b6-d0163307e6b0_960x1440.png" width="960" height="1440" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5c376ddf-fc4c-4e1e-93b6-d0163307e6b0_960x1440.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1440,&quot;width&quot;:960,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Title: Context Engineering for Data Science infographic - Description: Context Engineering for Data Science infographic&quot;,&quot;title&quot;:&quot;Title: Context Engineering for Data Science infographic - Description: Context Engineering for Data Science infographic&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Title: Context Engineering for Data Science infographic - Description: Context Engineering for Data Science infographic" title="Title: Context Engineering for Data Science infographic - Description: Context Engineering for Data Science infographic" srcset="https://substackcdn.com/image/fetch/$s_!pr4c!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c376ddf-fc4c-4e1e-93b6-d0163307e6b0_960x1440.png 424w, https://substackcdn.com/image/fetch/$s_!pr4c!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c376ddf-fc4c-4e1e-93b6-d0163307e6b0_960x1440.png 848w, https://substackcdn.com/image/fetch/$s_!pr4c!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c376ddf-fc4c-4e1e-93b6-d0163307e6b0_960x1440.png 1272w, https://substackcdn.com/image/fetch/$s_!pr4c!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c376ddf-fc4c-4e1e-93b6-d0163307e6b0_960x1440.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h1>AI Agents in the Data Science Workflow</h1><p>AI agents have become more capable of working independently inside a project. They are no longer limited to suggesting a single Python function. The agent could inspect files, write code, run the training process, fix errors, and save the model artifacts.</p><p>Recent research also evaluates this capability in a data-science setting. <a href="https://aclanthology.org/2026.findings-acl.181/">DataSciBench</a> evaluates language models using realistic data-science tasks rather than only isolated code generation.</p><p>Another benchmark,&nbsp;<a href="https://arxiv.org/abs/2603.19005">AgentDS,</a> compares AI-only and human-AI workflows across domain-specific data-science challenges. The agents produced executable pipelines and ran iterative experiments. However, they often returned to generic modeling strategies when the task required domain knowledge.</p><p>A similar situation appeared in <a href="https://arxiv.org/abs/2606.12736">SciAgentArena</a>. The agent became more useful when the analysis workflow and evaluation criteria were clearly specified. The performance was less reliable when the research question was open-ended.</p><p>For me, the results show that the coding capability is only one part of the workflow.</p><p>The AI agent might know how to build a churn model, but it does not automatically understand what churn means for our project.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/context-engineering-for-data-science?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/context-engineering-for-data-science?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div><h1>The Data Science Problem Is More Than the Dataset</h1><p>Let&#8217;s use customer churn prediction as an example.</p><p>The dataset might contain a snapshot_date, customer information, and a target named churned_next_30d. From these columns, the AI agent could identify the prediction target, encode categorical variables, and notice that the target was imbalanced.</p><p>However, several important questions were still unanswered.</p><p>When would the prediction happen? Would the customer be scored at the start or the end of the month? Was cancellation_date already available during the prediction? Should the model use random validation or future data? How many customers could the retention team contact?</p><p>Another question: Was the model predicting churn risk, or estimating the effect of sending a retention offer?</p><p>These questions would change how we prepare and evaluate the model. However, we could not answer them reliably from the column names.</p><p>To provide the missing information, I created a prediction contract for the project.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext"># Customer churn prediction contract

At the end of every calendar month, the retention team scores customers
with an active subscription.

The target indicates whether the customer cancels during the 30 days
following snapshot_date.

Only information known by the end of snapshot_date may be used.
Cancellation dates, the account status after 30 days, and retention-offer
outcomes are not available at prediction time.

Use records before July 2025 for development. Records from July through
December 2025 are the untouched final evaluation period.

Average precision is the primary metric. The retention team can contact
only 10% of customers, so also report recall among the 10% with the
highest predicted risk.

The evaluation measures risk ranking. It does not estimate the causal
effect of contacting a customer.</code></pre></div><p>The document did not teach the agent how to create a Scikit-Learn pipeline. The agent already knew how.</p><p>Instead, the prediction contract explained decisions the agent should not make on its own.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/context-engineering-for-data-science?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/context-engineering-for-data-science?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div><h1>Connecting the Project Context with the AI Agent</h1><p>Repository-level instructions are one way to connect the project knowledge with an AI agent.</p><p>VS Code supports several instruction files depending on the selected agent, including .github/copilot-instructions.md, AGENTS.md, CLAUDE.md, and scoped instruction files. The VS Code documentation describes this as a&nbsp;<a href="https://code.visualstudio.com/docs/agents/guides/context-engineering-guide">context-engineering workflow</a>, where project knowledge stays available across&nbsp;agent interactions.</p><p>For the experiment, I used a short AGENTS.md file.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext"># Project instructions

Before changing data preparation, feature engineering, training, or
evaluation, read docs/prediction-contract.md.

Treat the prediction moment, target window, unavailable information,
validation design, and evaluation metrics in that document as binding
project constraints.

Do not silently replace them with generic machine-learning defaults.

The final implementation must be runnable from the command line. Save the
fitted pipeline and a JSON metrics file, then run the finished command.</code></pre></div><p>The AGENTS.md file was intentionally short. Its purpose was to tell the agent where the important context was and how to use it.</p><p>The detailed information stayed inside docs/prediction-contract.md. OpenAI also described a similar pattern in its <a href="https://openai.com/index/harness-engineering/">harness-engineering article</a>, where a short AGENTS.md file points the agent toward deeper project knowledge.</p><p>For a data-science project, the deeper knowledge could include the prediction definition, data availability, validation design, metric reasoning, and operational constraints.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mQov!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898dbc0-41f3-4c07-ad0e-ec1660218ca7_1210x712.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mQov!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898dbc0-41f3-4c07-ad0e-ec1660218ca7_1210x712.png 424w, https://substackcdn.com/image/fetch/$s_!mQov!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898dbc0-41f3-4c07-ad0e-ec1660218ca7_1210x712.png 848w, https://substackcdn.com/image/fetch/$s_!mQov!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898dbc0-41f3-4c07-ad0e-ec1660218ca7_1210x712.png 1272w, https://substackcdn.com/image/fetch/$s_!mQov!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898dbc0-41f3-4c07-ad0e-ec1660218ca7_1210x712.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!mQov!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898dbc0-41f3-4c07-ad0e-ec1660218ca7_1210x712.png" width="1210" height="712" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7898dbc0-41f3-4c07-ad0e-ec1660218ca7_1210x712.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:712,&quot;width&quot;:1210,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Title: The data-science project context - Description: The data-science project context&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Title: The data-science project context - Description: The data-science project context" title="Title: The data-science project context - Description: The data-science project context" srcset="https://substackcdn.com/image/fetch/$s_!mQov!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898dbc0-41f3-4c07-ad0e-ec1660218ca7_1210x712.png 424w, https://substackcdn.com/image/fetch/$s_!mQov!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898dbc0-41f3-4c07-ad0e-ec1660218ca7_1210x712.png 848w, https://substackcdn.com/image/fetch/$s_!mQov!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898dbc0-41f3-4c07-ad0e-ec1660218ca7_1210x712.png 1272w, https://substackcdn.com/image/fetch/$s_!mQov!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7898dbc0-41f3-4c07-ad0e-ec1660218ca7_1210x712.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The flow was simple. The AI agent read AGENTS.md, while AGENTS.md directed it to the prediction contract. The contract then provided the data-science decisions used during model development.</p><div><hr></div><h1>Experiment Setup</h1><p>For the experiment, I generated a synthetic customer-month dataset with 9,000 rows. The data covered January 2024 to December 2025, and the overall churn rate was around 2.38%.</p><p>The dataset also contained several risky columns:</p><ul><li><p>cancellation_date</p></li><li><p>account_status_30d</p></li><li><p>retention_offer_accepted</p></li></ul><p>The first two columns perfectly revealed the target because they were created after the prediction moment. Meanwhile, retention_offer_accepted represented an action that happened after customer scoring.</p><p>I then prepared two separate project repositories. Both had the same dataset, package versions, README file, and prompt. However, only the second repository contained AGENTS.md and docs/prediction-contract.md.</p><p>Both experiments used OpenAI Codex CLI 0.147.0 with the gpt-5.6-sol model and medium reasoning effort. Each condition was run once in a fresh session.</p><p>The prompt was the following:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">Build a complete, reproducible customer churn model using the data in this
repository. Train and evaluate the model, save the fitted pipeline and a
machine-readable metrics file, and document the important modeling decisions.
Work independently and run the finished pipeline before you stop.</code></pre></div><p>The experiment was not designed as a benchmark for Codex. One run for each condition would not be enough to make a general performance claim. Instead, I wanted to observe which decisions came from the dataset and which came from the project context.</p><div><hr></div><h2>Reproducing the Experiment</h2><p>To make the experiment accessible, I published the complete project in a public <a href="https://github.com/CornelliusYW/context-engineering-data-science">GitHub repository</a>.</p><p>The repository contains the 9,000-row synthetic dataset, the data-generation script, the exact prompt used in both runs, both training pipelines, the AGENTS.md file, the prediction contract, the saved metrics, and the figures used in this article. The data is synthetic and contains no real customer information.</p><p>The README explains how to install the required packages and rerun each condition. This lets readers inspect each agent's decisions instead of relying only on the metric values shown here.</p><p>The final evaluation periods selected by both runs are shown below.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!W-9a!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23e0ca85-5903-48f4-b95f-cab2a52ef434_1210x681.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!W-9a!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23e0ca85-5903-48f4-b95f-cab2a52ef434_1210x681.png 424w, https://substackcdn.com/image/fetch/$s_!W-9a!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23e0ca85-5903-48f4-b95f-cab2a52ef434_1210x681.png 848w, https://substackcdn.com/image/fetch/$s_!W-9a!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23e0ca85-5903-48f4-b95f-cab2a52ef434_1210x681.png 1272w, https://substackcdn.com/image/fetch/$s_!W-9a!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23e0ca85-5903-48f4-b95f-cab2a52ef434_1210x681.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!W-9a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23e0ca85-5903-48f4-b95f-cab2a52ef434_1210x681.png" width="1210" height="681" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/23e0ca85-5903-48f4-b95f-cab2a52ef434_1210x681.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:681,&quot;width&quot;:1210,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Title: Final evaluation period comparison - Description: Final evaluation period comparison&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Title: Final evaluation period comparison - Description: Final evaluation period comparison" title="Title: Final evaluation period comparison - Description: Final evaluation period comparison" srcset="https://substackcdn.com/image/fetch/$s_!W-9a!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23e0ca85-5903-48f4-b95f-cab2a52ef434_1210x681.png 424w, https://substackcdn.com/image/fetch/$s_!W-9a!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23e0ca85-5903-48f4-b95f-cab2a52ef434_1210x681.png 848w, https://substackcdn.com/image/fetch/$s_!W-9a!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23e0ca85-5903-48f4-b95f-cab2a52ef434_1210x681.png 1272w, https://substackcdn.com/image/fetch/$s_!W-9a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F23e0ca85-5903-48f4-b95f-cab2a52ef434_1210x681.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Even though both agents received the same prompt and data, they reserved different months for the final evaluation. This difference also means that the average-precision scores could not be directly compared.</p><div><hr></div><h1>Experiment Result Without Project Context</h1><p>The result without project context was better than I expected.</p><p>The agent inspected the suspicious columns and their relationship with the target. It found that cancellation_date and account_status_30d perfectly revealed churn. The agent correctly identified them as outcome leakage and excluded them from the features.</p><p>It also excluded retention_offer_accepted and customer_id.</p><p>The agent noticed that the data covered 24 monthly periods. Therefore, it avoided random validation and created expanding-window validation folds. It selected average precision as the main metric, compared several models, and saved a reproducible pipeline.</p><p>These were reasonable data-science decisions.</p><p>However, the agent still had to decide on information the project did not provide.</p><p>It selected September to December 2025 as the untouched test period. The agent reported outreach performance at 5%, 10%, and 20% capacity. It selected the model using the highest mean validation average precision and created another decision threshold based on the F1 score.</p><p>None of the decisions were necessarily wrong. However, none came from the project owner either.</p><p>The final evaluation included 1,500 rows and 43 churners. The model achieved 0.0836 average precision and captured 12 churners within the monthly top 10% of the risk scores.</p><p>The agent produced good code, but it also created part of the prediction problem.</p><div><hr></div><h1>Experiment Result With Project Context</h1><p>The second agent received the prediction contract through the project instructions.</p><p>It reserved July to December 2025 as the final evaluation period, resulting in 2,250 rows and 67 churners. The agent used only pre-July data for model development and maintained time order during validation.</p><p>It compared a class-weighted logistic regression baseline with a gradient-boosting model. The prediction contract also provided the selection rule: the challenger could replace the baseline only when it improved average precision without reducing recall at the 10% outreach capacity.</p><p>Gradient boosting did not pass the requirement. Therefore, the agent kept the logistic-regression baseline.</p><p>The final model achieved an average precision of 0.0748. Within the 10% outreach capacity, the model captured 18 of 67 churners, which resulted in 0.2687 recall.</p><p>The agent also saved the prediction moment, target window, excluded columns, validation period, baseline comparison, and operational metric inside the JSON metrics file. The limitation that the model did not estimate the causal effect of a retention offer was also recorded.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AYXi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c0b8f9-cdb9-4ad0-a0af-be77a0d77a53_1210x699.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AYXi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c0b8f9-cdb9-4ad0-a0af-be77a0d77a53_1210x699.png 424w, https://substackcdn.com/image/fetch/$s_!AYXi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c0b8f9-cdb9-4ad0-a0af-be77a0d77a53_1210x699.png 848w, https://substackcdn.com/image/fetch/$s_!AYXi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c0b8f9-cdb9-4ad0-a0af-be77a0d77a53_1210x699.png 1272w, https://substackcdn.com/image/fetch/$s_!AYXi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c0b8f9-cdb9-4ad0-a0af-be77a0d77a53_1210x699.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!AYXi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c0b8f9-cdb9-4ad0-a0af-be77a0d77a53_1210x699.png" width="1210" height="699" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/73c0b8f9-cdb9-4ad0-a0af-be77a0d77a53_1210x699.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:699,&quot;width&quot;:1210,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Title: Metrics output comparison - Description: Metrics output comparison&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Title: Metrics output comparison - Description: Metrics output comparison" title="Title: Metrics output comparison - Description: Metrics output comparison" srcset="https://substackcdn.com/image/fetch/$s_!AYXi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c0b8f9-cdb9-4ad0-a0af-be77a0d77a53_1210x699.png 424w, https://substackcdn.com/image/fetch/$s_!AYXi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c0b8f9-cdb9-4ad0-a0af-be77a0d77a53_1210x699.png 848w, https://substackcdn.com/image/fetch/$s_!AYXi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c0b8f9-cdb9-4ad0-a0af-be77a0d77a53_1210x699.png 1272w, https://substackcdn.com/image/fetch/$s_!AYXi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73c0b8f9-cdb9-4ad0-a0af-be77a0d77a53_1210x699.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The image shows selected information from the actual metrics.json files. The context run did not only use the required decisions during training. It also kept them as machine-readable evidence for future work.</p><p>The overall decision comparison is shown below.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UabY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F233910e2-41f4-4f56-8f71-c21b9dad08fa_1210x793.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UabY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F233910e2-41f4-4f56-8f71-c21b9dad08fa_1210x793.png 424w, https://substackcdn.com/image/fetch/$s_!UabY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F233910e2-41f4-4f56-8f71-c21b9dad08fa_1210x793.png 848w, https://substackcdn.com/image/fetch/$s_!UabY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F233910e2-41f4-4f56-8f71-c21b9dad08fa_1210x793.png 1272w, https://substackcdn.com/image/fetch/$s_!UabY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F233910e2-41f4-4f56-8f71-c21b9dad08fa_1210x793.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!UabY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F233910e2-41f4-4f56-8f71-c21b9dad08fa_1210x793.png" width="1210" height="793" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/233910e2-41f4-4f56-8f71-c21b9dad08fa_1210x793.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:793,&quot;width&quot;:1210,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Title: Data-science decision comparison - Description: Data-science decision comparison&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Title: Data-science decision comparison - Description: Data-science decision comparison" title="Title: Data-science decision comparison - Description: Data-science decision comparison" srcset="https://substackcdn.com/image/fetch/$s_!UabY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F233910e2-41f4-4f56-8f71-c21b9dad08fa_1210x793.png 424w, https://substackcdn.com/image/fetch/$s_!UabY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F233910e2-41f4-4f56-8f71-c21b9dad08fa_1210x793.png 848w, https://substackcdn.com/image/fetch/$s_!UabY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F233910e2-41f4-4f56-8f71-c21b9dad08fa_1210x793.png 1272w, https://substackcdn.com/image/fetch/$s_!UabY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F233910e2-41f4-4f56-8f71-c21b9dad08fa_1210x793.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The project context did not suddenly make the AI agent better at Python or machine learning. Both runs were already technically capable.</p><p>The difference was that the second agent solved the prediction problem defined by the project rather than selecting its own version of the problem.</p><div><hr></div><h1>What Did the Project Context Change</h1><p>The prediction contract did not contain any model code. It didn't explain logistic regression, tune the hyperparameters, or provide the feature-transformation pipeline.</p><p>What it changed was the number of decisions the agent needed to make.</p><p>This difference matters because many data-science problems aren't caused by Python errors. The training script might run successfully, the metric might be calculated correctly, and the model file might be saved.</p><p>However, the result could still be misleading when the target was defined at the wrong time, future information entered the features, or the evaluation did not represent how the model would be used.</p><p>For the churn project, one of the most important sentences was the following:</p><blockquote><p><em>At the end of every calendar month, the retention team scores customers with an active subscription.</em></p></blockquote><p>The sentence defined the prediction moment. From this information, the agent could determine what data was available, how to form the target, and why time-based evaluation was required.</p><p>Another important sentence was:</p><blockquote><p><em>The retention team can contact only 10% of customers.</em></p></blockquote><p>This information turned the task from a generic classification problem into a ranking problem connected with an operational decision.</p><p>The value of context engineering was not providing more information in general. It provided the specific information the AI agent could not reliably infer.</p><div><hr></div><h1>What Should Remain as Human Work</h1><p>Project context could preserve human decisions, but it could not make all the decisions for us.</p><p>The data scientist still needed to decide whether churn was the right target, whether the 30-day window matched the business action, and whether the historical customers represented future conditions.</p><p>We also needed to decide whether a risk model was the correct method. A model that ranks customers by churn risk does not automatically tell us which customers would respond to a retention offer.</p><p>After we defined the decisions, the AI agent could help translate them into the data pipeline, validation process, metrics, and model artifacts.</p><div><hr></div><h1>Conclusion</h1><p>AI coding agents already understand a lot about Python, Scikit-Learn, and common machine learning workflows. Repeating the same coding explanation in every prompt might not be the most valuable use of project instructions.</p><p>More valuable context was the information the code could not explain: when the prediction happened, what information was available, how to simulate the future, which metric represented the operational decision, and what the model result could not prove.</p><p>In my experiment, the agent without project context still produced a good pipeline. However, it still had to make several important decisions on its own.</p><p>The agent with project context solved the version of the prediction problem that the project had already defined.</p><p>For me, this is the role of context engineering in data science.</p><p>It does not teach the AI agent how to code the model. It teaches the agent which modeling problem we are trying to solve.</p><p>I hope it has helped!</p><div><hr></div><h3>Need More Personal Guidance?</h3><p>If you are working on your Data Science or AI career and want more personalized guidance, I also offer mentorship through MentorCruise.</p><p>We can discuss your career direction, skills, portfolio, technical challenges, or what you should focus on next.</p><p>&#8594; <a href="https://mentorcruise.com/mentor/cornelliusyudhawijaya/">Check out my mentorship on MentorCruise</a></p>]]></content:encoded></item><item><title><![CDATA[Module 9: When a join increases your numbers]]></title><description><![CDATA[Let's learn why a one-to-many join can increase a total and how to keep the number correct.]]></description><link>https://www.nb-data.com/p/module-9-when-a-join-increases-your</link><guid isPermaLink="false">https://www.nb-data.com/p/module-9-when-a-join-increases-your</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Thu, 01 Oct 2026 16:37:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!WGPj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa931361-4c0b-4673-9dc4-66002a8406d9_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WGPj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa931361-4c0b-4673-9dc4-66002a8406d9_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WGPj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa931361-4c0b-4673-9dc4-66002a8406d9_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!WGPj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa931361-4c0b-4673-9dc4-66002a8406d9_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!WGPj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa931361-4c0b-4673-9dc4-66002a8406d9_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!WGPj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa931361-4c0b-4673-9dc4-66002a8406d9_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WGPj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa931361-4c0b-4673-9dc4-66002a8406d9_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aa931361-4c0b-4673-9dc4-66002a8406d9_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1370646,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/218366663?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa931361-4c0b-4673-9dc4-66002a8406d9_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!WGPj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa931361-4c0b-4673-9dc4-66002a8406d9_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!WGPj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa931361-4c0b-4673-9dc4-66002a8406d9_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!WGPj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa931361-4c0b-4673-9dc4-66002a8406d9_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!WGPj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa931361-4c0b-4673-9dc4-66002a8406d9_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Previous module:</strong> <a href="https://www.nb-data.com/p/module-8-choosing-between-inner-join?r=3kmaf">Module 8, Choosing between INNER JOIN and LEFT JOIN</a>.</p><p>An order total can be correct in the orders table but become wrong after a join. This happens when one order matches several item rows. SQL repeats the order value on every matched row, and SUM counts every copy.</p><p>In this module, we&#8217;ll learn how to check the row count after a join. We will also aggregate each child table before joining when the report needs one row per order.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div>
      <p>
          <a href="https://www.nb-data.com/p/module-9-when-a-join-increases-your">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Module 8: Choosing between INNER JOIN and LEFT JOIN]]></title><description><![CDATA[Let's learn how `INNER JOIN`, `LEFT JOIN`, and filter placement change the rows in our result.]]></description><link>https://www.nb-data.com/p/module-8-choosing-between-inner-join</link><guid isPermaLink="false">https://www.nb-data.com/p/module-8-choosing-between-inner-join</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Wed, 30 Sep 2026 17:06:02 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xLcR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2760451c-bc9a-4b9e-b5b0-d41e4fccd9ba_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xLcR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2760451c-bc9a-4b9e-b5b0-d41e4fccd9ba_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xLcR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2760451c-bc9a-4b9e-b5b0-d41e4fccd9ba_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!xLcR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2760451c-bc9a-4b9e-b5b0-d41e4fccd9ba_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!xLcR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2760451c-bc9a-4b9e-b5b0-d41e4fccd9ba_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!xLcR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2760451c-bc9a-4b9e-b5b0-d41e4fccd9ba_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xLcR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2760451c-bc9a-4b9e-b5b0-d41e4fccd9ba_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2760451c-bc9a-4b9e-b5b0-d41e4fccd9ba_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1505006,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/218207190?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2760451c-bc9a-4b9e-b5b0-d41e4fccd9ba_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xLcR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2760451c-bc9a-4b9e-b5b0-d41e4fccd9ba_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!xLcR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2760451c-bc9a-4b9e-b5b0-d41e4fccd9ba_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!xLcR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2760451c-bc9a-4b9e-b5b0-d41e4fccd9ba_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!xLcR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2760451c-bc9a-4b9e-b5b0-d41e4fccd9ba_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Previous module:</strong> <a href="https://www.nb-data.com/p/module-7-how-to-define-a-metric-clearly?r=3kmaf">Module 7, How to define a metric clearly.</a></p><p>An INNER JOIN and a LEFT JOIN can use the same columns but return a different number of customers. The difference comes from what happens when a customer doesn&#8217;t have a matching order.</p><p>In this module, we&#8217;ll compare both joins with a customer coverage example. After that, we&#8217;ll see why a WHERE condition can still remove customers from a left join.</p><p>The first thing we need to understand is which table defines the population. If the report should include every customer, the result still needs eight customers after the join. If the report is only about customers with an order, unmatched customers don&#8217;t belong in the result.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div>
      <p>
          <a href="https://www.nb-data.com/p/module-8-choosing-between-inner-join">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Module 7: How to define a metric clearly]]></title><description><![CDATA[Let's learn how to define a conversion rate so another person can calculate the same number.]]></description><link>https://www.nb-data.com/p/module-7-how-to-define-a-metric-clearly</link><guid isPermaLink="false">https://www.nb-data.com/p/module-7-how-to-define-a-metric-clearly</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Tue, 29 Sep 2026 15:23:27 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!8Kk_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a22945-e560-44db-ab60-1f4853fdcdbe_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8Kk_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a22945-e560-44db-ab60-1f4853fdcdbe_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8Kk_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a22945-e560-44db-ab60-1f4853fdcdbe_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!8Kk_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a22945-e560-44db-ab60-1f4853fdcdbe_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!8Kk_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a22945-e560-44db-ab60-1f4853fdcdbe_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!8Kk_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a22945-e560-44db-ab60-1f4853fdcdbe_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8Kk_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a22945-e560-44db-ab60-1f4853fdcdbe_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/39a22945-e560-44db-ab60-1f4853fdcdbe_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1408794,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/218016093?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a22945-e560-44db-ab60-1f4853fdcdbe_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8Kk_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a22945-e560-44db-ab60-1f4853fdcdbe_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!8Kk_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a22945-e560-44db-ab60-1f4853fdcdbe_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!8Kk_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a22945-e560-44db-ab60-1f4853fdcdbe_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!8Kk_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39a22945-e560-44db-ab60-1f4853fdcdbe_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p><strong>Previous module:</strong> <a href="https://open.substack.com/pub/cornellius/p/module-6-where-and-having-filtering?r=3kmaf&amp;utm_campaign=post-expanded-share&amp;utm_medium=web">Module 6, WHERE and HAVING: filtering before and after GROUP BY</a>.</p><p>Two people can calculate a conversion rate and still get different numbers. One person might count orders while the other counts customers. They might also use different dates to decide which rows belong in January.</p><p>The SQL can be correct in both cases because the problem starts with the metric definition. We still need to choose the numerator, denominator, counting unit, and time period.</p><p>In this module, we&#8217;ll use a January purchase cohort to compare two definitions. First, we&#8217;ll check what happens when the numerator and denominator use different date columns. After that, we&#8217;ll compare an order completion rate with a customer completion rate.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div>
      <p>
          <a href="https://www.nb-data.com/p/module-7-how-to-define-a-metric-clearly">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[What Is Jev AI and How Can We Use It for Data Science?]]></title><description><![CDATA[Explore its applications, advantages, and limitations through a Python walkthrough.]]></description><link>https://www.nb-data.com/p/what-is-jev-ai-and-how-can-we-use</link><guid isPermaLink="false">https://www.nb-data.com/p/what-is-jev-ai-and-how-can-we-use</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sun, 27 Sep 2026 09:49:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!PBXo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3be0b175-e63c-4e9b-ac1d-47b761b05ba9_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PBXo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3be0b175-e63c-4e9b-ac1d-47b761b05ba9_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PBXo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3be0b175-e63c-4e9b-ac1d-47b761b05ba9_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!PBXo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3be0b175-e63c-4e9b-ac1d-47b761b05ba9_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!PBXo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3be0b175-e63c-4e9b-ac1d-47b761b05ba9_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!PBXo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3be0b175-e63c-4e9b-ac1d-47b761b05ba9_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PBXo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3be0b175-e63c-4e9b-ac1d-47b761b05ba9_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3be0b175-e63c-4e9b-ac1d-47b761b05ba9_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1289491,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/217640597?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3be0b175-e63c-4e9b-ac1d-47b761b05ba9_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PBXo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3be0b175-e63c-4e9b-ac1d-47b761b05ba9_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!PBXo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3be0b175-e63c-4e9b-ac1d-47b761b05ba9_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!PBXo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3be0b175-e63c-4e9b-ac1d-47b761b05ba9_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!PBXo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3be0b175-e63c-4e9b-ac1d-47b761b05ba9_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Take a look at these two customer messages:</p><blockquote><p>&#8220;How can I request a refund?&#8221;</p></blockquote><blockquote><p>&#8220;My refund still has not arrived.&#8221;</p></blockquote><p>Both messages mention a refund, but the customers need different kinds of help. If we only search for the word &#8220;refund,&#8221; we would put them in the same group.</p><p>This is a familiar problem when working with text data. We have the information, but we still need to organize it before we can analyze it or use it for machine learning.</p><p>Jev is one model we could use for this task. Developed by TypeSafe, Jev answers questions about text by returning a category, score, or probability that we can use in our code.</p><p>Many&nbsp;<a href="https://www.langchain.com/blog/building-a-harness-with-jev">articles</a> already introduce Jev and how it can help our work. However, what makes it interesting for data science? For me, it is the ability to turn a question about our data into a column we can inspect and evaluate.</p><p>In this article, we will explore what Jev does, compare it with other approaches, and walk through a Python example. We will also analyze published Jev predictions to see what happens when we only keep answers with high confidence.</p><p>Before we continue, a note about the experiment: I did not have Jev API access for this article. The API code is a documentation-based example that I have not run. The results and screenshots come from an analysis I ran on publicly available predictions so that you can reproduce that part without an API key.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h1>What is Jev</h1><p>Let&#8217;s return to the refund messages. We could give Jev a message and ask it to choose between &#8220;requesting a refund&#8221; and &#8220;checking a refund.&#8221;</p><p>There are two main parts to this request:</p><ul><li><p>The <strong>state</strong>, which is the content we want to analyze.</p></li><li><p>The <strong>questions</strong>, which describe what we want to know about that content.</p></li></ul><p><a href="https://docs.typesafe.ai/introduction">TypeSafe</a> calls Jev a <em>System One</em> model. The idea is to make specific decisions that our program can use, rather than produce a long written answer. </p><p>It is also worth remembering that Jev is a hosted model. Installing its <a href="https://docs.typesafe.ai/sdk/python/api/clients/sync">Python client documentation</a> lets us call the service; it does not install the model on our computer. </p><p>Jev provides three question types. Let&#8217;s look at what each one means.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!h0WC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe841beb8-5920-4db6-bbb4-beb6bcc95406_1324x451.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!h0WC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe841beb8-5920-4db6-bbb4-beb6bcc95406_1324x451.png 424w, https://substackcdn.com/image/fetch/$s_!h0WC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe841beb8-5920-4db6-bbb4-beb6bcc95406_1324x451.png 848w, https://substackcdn.com/image/fetch/$s_!h0WC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe841beb8-5920-4db6-bbb4-beb6bcc95406_1324x451.png 1272w, https://substackcdn.com/image/fetch/$s_!h0WC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe841beb8-5920-4db6-bbb4-beb6bcc95406_1324x451.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!h0WC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe841beb8-5920-4db6-bbb4-beb6bcc95406_1324x451.png" width="1324" height="451" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e841beb8-5920-4db6-bbb4-beb6bcc95406_1324x451.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:451,&quot;width&quot;:1324,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Question type, What it does, Example; Choice, Selects one of the categories we provide, Is the feedback about billing, account access, or something else?; Score, Rates the text using descriptions arranged from low to high, Is this document irrelevant, partly relevant, or directly relevant?; Noul, Returns a probability from 0 to 1 for a yes-or-no question, Does the customer explicitly ask to cancel?&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Question type, What it does, Example; Choice, Selects one of the categories we provide, Is the feedback about billing, account access, or something else?; Score, Rates the text using descriptions arranged from low to high, Is this document irrelevant, partly relevant, or directly relevant?; Noul, Returns a probability from 0 to 1 for a yes-or-no question, Does the customer explicitly ask to cancel?" title="Question type, What it does, Example; Choice, Selects one of the categories we provide, Is the feedback about billing, account access, or something else?; Score, Rates the text using descriptions arranged from low to high, Is this document irrelevant, partly relevant, or directly relevant?; Noul, Returns a probability from 0 to 1 for a yes-or-no question, Does the customer explicitly ask to cancel?" srcset="https://substackcdn.com/image/fetch/$s_!h0WC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe841beb8-5920-4db6-bbb4-beb6bcc95406_1324x451.png 424w, https://substackcdn.com/image/fetch/$s_!h0WC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe841beb8-5920-4db6-bbb4-beb6bcc95406_1324x451.png 848w, https://substackcdn.com/image/fetch/$s_!h0WC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe841beb8-5920-4db6-bbb4-beb6bcc95406_1324x451.png 1272w, https://substackcdn.com/image/fetch/$s_!h0WC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe841beb8-5920-4db6-bbb4-beb6bcc95406_1324x451.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Choice and Score also return a probability distribution and a confidence value. Noul returns the probability for the statement, without a separate confidence value. See the documentation for <a href="https://docs.typesafe.ai/primitives/choice">Choice</a>, <a href="https://docs.typesafe.ai/primitives/score">Score</a>, and <a href="https://docs.typesafe.ai/primitives/noul">Noul</a>.</p><p>For our refund example, Choice would be the starting point because we want to select a category. If we wanted to rate a document's relevance, we could use Score instead.</p><p>We can include several questions in one request. However, each question is answered independently using the same state. If one result should affect what happens next, we need to write that logic ourselves. </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/what-is-jev-ai-and-how-can-we-use?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/what-is-jev-ai-and-how-can-we-use?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div><h1>Why would we use Jev for data science?</h1><p>Suppose we have a dataset containing account information and customer messages. The account age and number of purchases are already available as columns. The messages might contain useful information too, but we cannot summarize their meaning with a simple numeric calculation.</p><p>We could ask whether each message mentions a cancellation, an unresolved issue, or a competitor. The answers could become additional features to test in our model.</p><p>This is the part I find useful. We can explain what a feature is supposed to represent and inspect the original message when its value looks wrong. That does not make the model&#8217;s internal reasoning transparent, but it gives us a clear definition for the column.</p><h3>Why Jev is interesting for a data science workflow</h3><p>Another interesting part of Jev is how we can use the result directly in our data workflow.</p><p>Normally, text data is difficult to use as a feature because we need to transform the meaning into something our analysis or machine learning model can understand.</p><p>With Jev, we can ask a specific question about the text and receive a category, score, or probability as the answer.</p><p>For example, suppose we have customer messages and want to know whether the customer is likely to cancel.</p><p>We could ask Jev:</p><pre><code><code>Does the customer want to cancel?</code></code></pre><p>The returned probability could then become another column in our dataset:</p><pre><code><code>cancellation_probability</code></code></pre><p>From there, we can use the value like any other feature. We could analyze it with pandas, apply a threshold, or include it in a machine learning model.</p><p>This is also where Jev fits well with ordinary Python code.</p><p>We do not need Jev to handle the entire workflow. We can use Jev for the part that requires understanding the text, while Python handles calculations and other deterministic logic.</p><p>For example, our workflow could look like this:</p><pre><code><code>Customer message
      &#8595;
Jev
      &#8595;
Category or probability
      &#8595;
DataFrame
      &#8595;
Python logic or ML model</code></code></pre><p>TypeSafe also uses this idea in its workflow evaluations. A larger task can be divided into several smaller Choice, Score, or Noul questions, while the surrounding application controls what happens next.</p><p>For me, this is probably one of the more useful parts of Jev for data science. We can convert information hidden inside text into values that are easier to analyze and test.</p><p>I would start with three applications.</p><div><hr></div><h3>1. Labeling text data</h3><p>Imagine that we want to group survey responses into pricing, usability, reliability, and other feedback. We could describe these categories in a Choice question and ask Jev to suggest a label for each response.</p><p>The advantage is that we can begin with category descriptions rather than first training our own classifier.</p><p>However, these are still predicted labels. We should not treat them as verified answers just because they have been added to our DataFrame. We need to inspect a sample and measure the mistakes.</p><p>The experiment later in this article will focus on this issue.</p><h4>2. Creating features for machine learning</h4><p>For a customer churn model, a message asking to cancel might provide information that is missing from account activity alone. We could use a Noul question to create a cancellation-related feature, then compare our model with and without that feature.</p><p><a href="https://docs.typesafe.ai/concepts/use-case-map">TypeSafe</a> includes combining text-derived probabilities with structured data among its suggested uses. This gives us an approach to try, not a guarantee that the new features will improve our model.</p><p>As a reminder, the message must be available when we make the prediction. A message written after the customer has already canceled would give our model information it should not have.</p><h4>3. Selecting documents to review</h4><p>We could also use <a href="https://docs.typesafe.ai/primitives/score">Score</a> to help organize a collection of documents. For example, we might describe what makes a paper relevant to a research question, then use the returned scores to decide which papers to inspect first. </p><p>This could be useful when we have many documents and a clear idea of what we are looking for. I would still check some of the low-scoring documents, because an incorrect score could hide something important.</p><p>These feature and document-review examples are possible applications. They were not tested in the banking-intent experiment below.</p><div><hr></div><h1>How does Jev compare with other approaches?</h1><p>We already have several ways to classify text. Does that mean we need Jev as well?</p><p>Not necessarily. The choice depends on our data, the task, and how much control we need. Let&#8217;s compare the main options.</p><h3>Rules and keyword matching</h3><p>For exact patterns, I would start with ordinary code. Checking whether an ID follows a required format does not need a language model.</p><p>The difficulty appears when the meaning depends on the wording. In our refund example, finding the keyword is easy, but separating the two requests needs more information. We can keep adding rules, but they become harder to maintain as we encounter new expressions.</p><p>Jev lets us describe the distinction in words. Whether it handles those expressions correctly still needs to be tested.</p><h3>Traditional text classifiers</h3><p>Another option is to train a model using labeled examples. For instance, we could represent the text with TF-IDF features, which use weighted word occurrences, and train a classifier on those features. Scikit-learn has a <a href="https://scikit-learn.org/stable/auto_examples/text/plot_document_classification_20newsgroups.html">text-classification example</a> showing this approach.</p><p>If we already have representative labels and stable categories, I would include this as a baseline. A model we run locally also avoids sending every message to an external API.</p><p><a href="https://docs.typesafe.ai/models">Jev</a> is different because we can change the category descriptions in the request without training new model weights. The limitation is that TypeSafe does not offer customer-specific fine-tuning for Jev. Instead, we adapt it through instructions and context. </p><p>Also, not needing training labels does not mean we can skip labeled data altogether. We still need trusted answers to check the predictions.</p><h3>General-purpose LLMs</h3><p>We could also ask a general-purpose LLM to classify our messages.</p><p>It would be misleading to say that Jev is the only option for structured answers. Models such as Claude support responses constrained by a JSON schema.</p><p>What I like about Jev&#8217;s approach is that the selected answer and the <a href="https://docs.typesafe.ai/confidence">probabilities</a> are already part of the response. We can use them to compare alternatives or decide which rows to review. We do not need to ask for a written statement about how confident the model feels. </p><p>This is not a capability unique to Jev, either. Traditional classifiers can provide probability estimates, and scikit-learn includes methods to check and improve their calibration.</p><p>If the task also requires writing an explanation or summary, I would consider a general-purpose LLM. If it only needs a defined label or score, Jev is another option worth evaluating.</p><p>Here is a short comparison.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gVLh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464aa56b-c743-4aa0-b25a-bca6fa5154db_1324x572.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gVLh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464aa56b-c743-4aa0-b25a-bca6fa5154db_1324x572.png 424w, https://substackcdn.com/image/fetch/$s_!gVLh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464aa56b-c743-4aa0-b25a-bca6fa5154db_1324x572.png 848w, https://substackcdn.com/image/fetch/$s_!gVLh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464aa56b-c743-4aa0-b25a-bca6fa5154db_1324x572.png 1272w, https://substackcdn.com/image/fetch/$s_!gVLh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464aa56b-c743-4aa0-b25a-bca6fa5154db_1324x572.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!gVLh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464aa56b-c743-4aa0-b25a-bca6fa5154db_1324x572.png" width="1324" height="572" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/464aa56b-c743-4aa0-b25a-bca6fa5154db_1324x572.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:572,&quot;width&quot;:1324,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Approach, Main advantage, Main disadvantage; Rules and keywords, Clear logic; can run locally without a model API, Requires us to handle different expressions and exceptions; Supervised classifier, Learns from our labeled data; can run locally, Needs representative training examples and maintenance; General-purpose LLM, Can combine classification with writing and other tasks, Cost and accuracy need testing for the selected model; Jev, Can start from question definitions and returns decision probabilities, Depends on a hosted service and has a narrower range of tasks&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Approach, Main advantage, Main disadvantage; Rules and keywords, Clear logic; can run locally without a model API, Requires us to handle different expressions and exceptions; Supervised classifier, Learns from our labeled data; can run locally, Needs representative training examples and maintenance; General-purpose LLM, Can combine classification with writing and other tasks, Cost and accuracy need testing for the selected model; Jev, Can start from question definitions and returns decision probabilities, Depends on a hosted service and has a narrower range of tasks" title="Approach, Main advantage, Main disadvantage; Rules and keywords, Clear logic; can run locally without a model API, Requires us to handle different expressions and exceptions; Supervised classifier, Learns from our labeled data; can run locally, Needs representative training examples and maintenance; General-purpose LLM, Can combine classification with writing and other tasks, Cost and accuracy need testing for the selected model; Jev, Can start from question definitions and returns decision probabilities, Depends on a hosted service and has a narrower range of tasks" srcset="https://substackcdn.com/image/fetch/$s_!gVLh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464aa56b-c743-4aa0-b25a-bca6fa5154db_1324x572.png 424w, https://substackcdn.com/image/fetch/$s_!gVLh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464aa56b-c743-4aa0-b25a-bca6fa5154db_1324x572.png 848w, https://substackcdn.com/image/fetch/$s_!gVLh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464aa56b-c743-4aa0-b25a-bca6fa5154db_1324x572.png 1272w, https://substackcdn.com/image/fetch/$s_!gVLh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F464aa56b-c743-4aa0-b25a-bca6fa5154db_1324x572.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This table compares how we would use the tools. Our experiment does not rank their performance.</p><div><hr></div><h1>What are Jev&#8217;s limitations?</h1><p>I'd consider a few things before adding Jev to a project.</p><p>First, it is not a general-purpose writing model. <a href="https://docs.typesafe.ai/model-jaggedness/jev-1.13">TypeSafe also documents weaknesses</a> with counting, exact arithmetic, date comparisons, and questions that require several reasoning steps. I would leave these calculations to Python. </p><p>Second, the input still matters. Unrelated information can reduce accuracy, and adversarial instructions inside the text can influence the answer. We should test those cases rather than assume a structured response is safe or correct. </p><p>Third, the current model accepts text, not raw images or audio. TypeSafe reports its strongest language performance in English. We also need to consider API availability, rate limits, and whether we can send our data to the service.</p><p>Finally, a high confidence value can still accompany a wrong answer. We will see two examples of this in the experiment.</p><p>For me, Jev is interesting for repeated text-labeling and scoring tasks. I would not use it to replace pandas calculations or assume it will outperform a classifier trained for our particular problem.</p><div><hr></div><h1>Using Jev with Python</h1><p>Let&#8217;s look at how we could add a topic and an urgency value to a small feedback dataset.</p><p>To run this example, we need a TypeSafe account, an API key, and Python 3.10 or newer. First, install the packages:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">pip install typesafe-sdk pandas</code></pre></div><p>The client reads the key from the TYPESAFE_API_KEY environment variable. Set it privately in your environment, and do not include it in a notebook you plan to share. </p><p>The following example uses three sample messages. I have not run this API call, so there is no claimed model output for these inputs.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">import pandas as pd
from typesafe_sdk import Choice, Noul, TypeSafeClient
 
feedback = pd.DataFrame({
    "message": [
        "My refund still has not arrived.",
        "I cannot sign in after resetting my password.",
        "Please restore access today; my team cannot work.",
    ]
})
 
questions = {
    "topic": Choice(
        instructions="Select the main topic of this feedback.",
        criteria={
            "billing": "Charges, refunds, invoices, or payments.",
            "access": "Signing in, passwords, or account access.",
            "other": "A topic outside billing and account access.",
        },
    ),
    "urgency": Noul(
        instructions="Does the message explicitly express time urgency?"
    ),
}
 
records = []
with TypeSafeClient(model="jev-1.13.0") as client:
    for message in feedback["message"]:
        response = client.system_one(state=message, questions=questions)
        topic = response.answers["topic"]
        records.append({
            "jev_topic": topic.choice,
            "jev_topic_confidence": topic.confidence,
            "jev_urgency_probability": response.answers["urgency"].noul,
            "jev_model": response.model,
        })
 
enriched = feedback.join(pd.DataFrame(records, index=feedback.index))
print(enriched.to_string(index=False))</code></pre></div><p>In the code above, feedback contains the messages we want to analyze. We define a Choice question for the topic and a Noul question for urgency, then send each message to Jev.</p><p>After receiving an answer, we collect the values and join them back to the original DataFrame. Keeping the message beside its predictions makes it easier to inspect the result. The request uses the documented <a href="https://docs.typesafe.ai/sdk/python/api/clients/sync">Python client interface</a>.</p><p>We provide descriptions for the topic categories. These descriptions help separate the options. The other category also gives the model a place to put feedback that doesn't fit billing or account access. </p><p>That price makes it worth considering for repeated labeling, but it doesn't cover the full cost of the work. We still need to account for retries and review. This example does not measure API cost or demonstrate savings over another model.</p><p>For a larger dataset, I would first test a small sample, add error handling, and save progress between batches. The loop above is intended to explain the basic usage.</p><div><hr></div><h1>Experiment: Checking Jevs predictions with pandas</h1><p>Now that we understand the basic usage, let&#8217;s look at some recorded results.</p><p><a href="https://www.ayautomate.com/blog/jev-vs-llm-benchmark">AY Automate</a> published Jev predictions for a banking-intent classification task. For this analysis, I selected the 77-category task, which contains 231 predictions: three examples per category. The publisher reports that the original model calls ran on September 19, 2026. </p><p>I have a specific question: can we use confidence to select more accurate labels, and how many rows would still need review?</p><p>This is an analysis of the published answers, not a new run of Jev.</p><h3>Load the data and check the predictions</h3><p>Download the <a href="https://www.ayautomate.com/benchmarks/jev-vs-llm/results.jsonl">published results</a> and save the file as recorded_results.jsonl. We can then load it with pandas:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">import pandas as pd
 
raw = pd.read_json("recorded_results.jsonl", lines=True)
df = raw.loc[
    raw["system"].eq("typesafe/jev-1.13")
    &amp; raw["task"].eq("intent77")
].copy()
df["correct"] = df["ok"].eq(True) &amp; df["pred"].eq(df["gold"])
 
print(df[["gold", "pred", "conf"]].head(3).to_string(index=False))
print(f"Accuracy: {df['correct'].mean():.1%}")
</code></pre></div><p>There are three columns we need to understand:</p><ul><li><p>gold contains the expected label from the dataset.</p></li><li><p>pred contains Jev&#8217;s prediction.</p></li><li><p>conf contains the recorded confidence value.</p></li></ul><p>We compare the predicted and expected labels to create the correct column. If the request failed, we count it as incorrect.</p><p>Here is the output:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">                 gold                  pred  conf
Refund not showing up Refund not showing up  1.00
Refund not showing up        request refund  0.52
Refund not showing up        request refund  0.79
Accuracy: 78.8%</code></pre></div><p>In the first three rows, the expected category is the same, but the predictions are not. Two rows were classified as request refund instead of Refund, which is not showing up.</p><p>Across the full subset, 182 of the 231 predictions were correct, giving us 78.8% accuracy.</p><p>For me, this result is a reason to inspect the labels before using them. Let&#8217;s see whether confidence helps us select a more reliable subset.</p><h3>Select a confidence threshold</h3><p>A threshold is simply the minimum confidence we require before keeping a prediction.</p><p>We should not choose that minimum and evaluate it on the same rows. Otherwise, we could keep adjusting it until the result looks good for the data we have already seen.</p><p>For this example, I used 77 rows to choose the threshold and kept the remaining 154 rows for evaluation. The split follows the source IDs: those ending in -0 go into the selection subset, and the others go into the evaluation subset.</p><p>This gives us one selection example and two evaluation examples per category. It is a small split of public data, not a separate production dataset.</p><p>We can perform the selection with the following code:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">cal = df.loc[df["id"].str.endswith("-0")].copy()
test = df.loc[~df["id"].str.endswith("-0")].copy()
 
def evaluate(data, threshold):
    accepted = data.loc[data["ok"].eq(True) &amp; data["conf"].ge(threshold)]
    return {
        "threshold": threshold,
        "accepted": len(accepted),
        "correct": int(accepted["correct"].sum()),
        "accuracy": accepted["correct"].mean(),
        "coverage": len(accepted) / len(data),
    }
 
thresholds = [0, .5, .7, .8, .9, .95, .99]
sweep = pd.DataFrame([evaluate(cal, t) for t in thresholds])
eligible = sweep.loc[(sweep["accuracy"] &gt;= .95) &amp; (sweep["accepted"] &gt;= 10)]
if eligible.empty:
    raise ValueError("No threshold met the rule; keep manual review.")
chosen = eligible.sort_values(
    ["accepted", "threshold"], ascending=[False, True]
).iloc[0]
threshold = float(chosen["threshold"])
print(sweep.round(4).to_string(index=False))</code></pre></div><p>The code tries seven thresholds. From the thresholds that reach at least 95% accuracy and keep at least 10 selection rows, it chooses the one that keeps the most rows.</p><p>These requirements are settings for our example. They are not universal rules for using Jev.</p><p>The selected threshold was <strong>0.99</strong>. On the selected subset, it kept 32 predictions, of which 31 were correct.</p><p>Next, let&#8217;s apply that same threshold to the 154 evaluation rows:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">result = evaluate(test, threshold)
print(result)</code></pre></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mJYZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623560c4-e62c-4b4d-854b-d911be77cf90_1326x746.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mJYZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623560c4-e62c-4b4d-854b-d911be77cf90_1326x746.jpeg 424w, https://substackcdn.com/image/fetch/$s_!mJYZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623560c4-e62c-4b4d-854b-d911be77cf90_1326x746.jpeg 848w, https://substackcdn.com/image/fetch/$s_!mJYZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623560c4-e62c-4b4d-854b-d911be77cf90_1326x746.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!mJYZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623560c4-e62c-4b4d-854b-d911be77cf90_1326x746.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!mJYZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623560c4-e62c-4b4d-854b-d911be77cf90_1326x746.jpeg" width="1326" height="746" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/623560c4-e62c-4b4d-854b-d911be77cf90_1326x746.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:746,&quot;width&quot;:1326,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Held-out replay results: 67 predictions accepted, 65 correct, and 87 deferred&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Held-out replay results: 67 predictions accepted, 65 correct, and 87 deferred" title="Held-out replay results: 67 predictions accepted, 65 correct, and 87 deferred" srcset="https://substackcdn.com/image/fetch/$s_!mJYZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623560c4-e62c-4b4d-854b-d911be77cf90_1326x746.jpeg 424w, https://substackcdn.com/image/fetch/$s_!mJYZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623560c4-e62c-4b4d-854b-d911be77cf90_1326x746.jpeg 848w, https://substackcdn.com/image/fetch/$s_!mJYZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623560c4-e62c-4b4d-854b-d911be77cf90_1326x746.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!mJYZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F623560c4-e62c-4b4d-854b-d911be77cf90_1326x746.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em>Screenshot of the local analysis report. The pandas analysis reproduced these counts from published predictions; this is not a screenshot of a live Jev session.</em></figcaption></figure></div><p>As the result shows, the filter kept 67 predictions, and 65 were correct. This gives us <strong>97.0% accuracy among the accepted predictions</strong>.</p><p>Without the filter, Jev was correct on 124 of the 154 evaluation rows, or 80.5%.</p><p>The filtered result looks better, but we need to check how much data remains. Only 43.5% of the evaluation rows passed the filter. This percentage is called <strong>coverage</strong>. The other 87 rows, or 56.5%, still need review.</p><p>In other words, we have not made Jev 97% accurate on the whole dataset. We have selected a smaller group of answers that were more accurate in this experiment.</p><p>This helps plan the labeling process because we can see both the quality of the selected predictions and the remaining work.</p><h3>Inspect the incorrect answers</h3><p>Two mistakes remain among the accepted predictions. Let&#8217;s take a closer look.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">test["review_required"] = ~(
    test["ok"].eq(True) &amp; test["conf"].ge(threshold)
)
test["candidate_label"] = test["pred"].where(~test["review_required"])
 
errors = test.loc[
    ~test["review_required"] &amp; ~test["correct"],
    ["gold", "pred", "conf"],
]
print(errors.to_string(index=False))
 
test[["id", "pred", "conf", "candidate_label", "review_required"]].to_csv(
    "label_review_queue.csv", index=False
)</code></pre></div><p>The code marks rows that need review and creates a candidate_label column for the predictions that passed the filter. It also saves the result to label_review_queue.csv.</p><p>I use the name &#8220;candidate label&#8221; deliberately. Passing the filter does not turn a prediction into a verified answer.</p><p>The two accepted mistakes are shown below.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KSh0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3348f818-c75a-4594-9cfb-9dd0d914e8b0_1326x746.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KSh0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3348f818-c75a-4594-9cfb-9dd0d914e8b0_1326x746.jpeg 424w, https://substackcdn.com/image/fetch/$s_!KSh0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3348f818-c75a-4594-9cfb-9dd0d914e8b0_1326x746.jpeg 848w, https://substackcdn.com/image/fetch/$s_!KSh0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3348f818-c75a-4594-9cfb-9dd0d914e8b0_1326x746.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!KSh0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3348f818-c75a-4594-9cfb-9dd0d914e8b0_1326x746.jpeg 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!KSh0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3348f818-c75a-4594-9cfb-9dd0d914e8b0_1326x746.jpeg" width="1326" height="746" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3348f818-c75a-4594-9cfb-9dd0d914e8b0_1326x746.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:746,&quot;width&quot;:1326,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Two incorrect Jev predictions that passed the confidence threshold&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Two incorrect Jev predictions that passed the confidence threshold" title="Two incorrect Jev predictions that passed the confidence threshold" srcset="https://substackcdn.com/image/fetch/$s_!KSh0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3348f818-c75a-4594-9cfb-9dd0d914e8b0_1326x746.jpeg 424w, https://substackcdn.com/image/fetch/$s_!KSh0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3348f818-c75a-4594-9cfb-9dd0d914e8b0_1326x746.jpeg 848w, https://substackcdn.com/image/fetch/$s_!KSh0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3348f818-c75a-4594-9cfb-9dd0d914e8b0_1326x746.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!KSh0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3348f818-c75a-4594-9cfb-9dd0d914e8b0_1326x746.jpeg 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em>Screenshot of the local error report. Both examples came from the evaluation subset and passed the 0.99 confidence threshold.</em></figcaption></figure></div><p>The first prediction was failed transfer, while the expected label was beneficiary not allowed. The second was transfer timing, while the expected label was transfer not received by recipient.</p><p>Notice that the second answer had a confidence of 1.0. It was still incorrect.</p><p>TypeSafe explains that confidence summarizes how concentrated the probabilities are around an answer. It should not be read as a guarantee that a particular prediction is correct.</p><p>The source file contains the labels and predictions, but not the original messages. That means we can identify these disagreements, although we cannot explain exactly why the model made them from this file alone.</p><div><hr></div><h1>What did we learn from the experiment?</h1><p>What I find useful is that we can separate the predictions into two groups: those we might use after further checks and those that need review.</p><p>However, I would not take the 0.99 threshold and apply it directly to another dataset. Different categories, languages, and message styles could produce different results.</p><p>For my own dataset, I would start with a small human-labeled sample. I would use part of it to improve the category descriptions and choose a threshold, then evaluate the final setup on data I had kept separate. I would also compare it with a simple classifier rather than assume that Jev is the best option.</p><p>One more point to remember for EDA: If we only count categories among the accepted rows, we may get a misleading picture of the full dataset. The accepted rows could contain easier examples or a different mix of topics. We should keep the rejected rows visible in our analysis.</p><p>For feature engineering, we would need another experiment. We would compare the prediction model with and without the Jev-derived features, using the same evaluation data. Better labels in this example do not prove better churn predictions.</p><p>The size of this experiment is also a limitation. Only 67 evaluation predictions passed the filter. Their approximate 95% Wilson interval is 89.8% to 99.2%, which shows how uncertain the accuracy estimate remains with a small sample. That interval does not account for every dependency in the data.</p><p>I did not independently verify the published responses against provider logs or measure new inference speed, API costs, or human-review time. The companion files include the executable analysis and source-file checksum so that readers can check the calculation.</p><div><hr></div><h1>Conclusion</h1><p>Jev is a model for answering defined questions about text. We can use its categories, scores, and probabilities as values in our data science projects.</p><p>For me, its main attraction is being able to try a text-labeling task or a new feature without first training a separate classifier. Its limitations include reliance on an external API, a narrower range of tasks, and answers that still need validation.</p><p>In the experiment, a confidence filter selected 67 of 154 evaluation predictions, 65 of which were correct. The remaining 87 rows still needed review. This shows a possible way to organize labeling work, but it does not establish that Jev is better than every alternative.</p><p>I would begin with one text column and one clearly defined task. Check the predictions, compare them with a simple baseline, and decide whether the result is useful before processing the rest of the dataset.</p>]]></content:encoded></item><item><title><![CDATA[Module 6: WHERE and HAVING: filtering before and after GROUP BY]]></title><description><![CDATA[Let's learn when a condition belongs in `WHERE` and when it belongs in `HAVING`.]]></description><link>https://www.nb-data.com/p/module-6-where-and-having-filtering</link><guid isPermaLink="false">https://www.nb-data.com/p/module-6-where-and-having-filtering</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sat, 26 Sep 2026 12:03:41 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!OJ66!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12968b05-ebf7-480f-81e3-748d4376ed59_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OJ66!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12968b05-ebf7-480f-81e3-748d4376ed59_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OJ66!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12968b05-ebf7-480f-81e3-748d4376ed59_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!OJ66!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12968b05-ebf7-480f-81e3-748d4376ed59_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!OJ66!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12968b05-ebf7-480f-81e3-748d4376ed59_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!OJ66!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12968b05-ebf7-480f-81e3-748d4376ed59_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OJ66!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12968b05-ebf7-480f-81e3-748d4376ed59_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/12968b05-ebf7-480f-81e3-748d4376ed59_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1328535,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/217517915?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12968b05-ebf7-480f-81e3-748d4376ed59_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!OJ66!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12968b05-ebf7-480f-81e3-748d4376ed59_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!OJ66!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12968b05-ebf7-480f-81e3-748d4376ed59_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!OJ66!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12968b05-ebf7-480f-81e3-748d4376ed59_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!OJ66!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12968b05-ebf7-480f-81e3-748d4376ed59_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>WHERE filters rows before SQL creates the groups. HAVING filters the groups after SQL calculates COUNT, SUM, or another aggregate.</p><p>Both clauses can remove data, but moving a condition between them can change the question we answer. A status filter in WHERE might remove orders that should still be included in a customer&#8217;s total.</p><p>In this module, we&#8217;ll compare two similar requests. The first needs customers with at least two total orders and one delivered order. The second needs customers with at least two delivered orders.</p><p>Upgrade to continue with the remaining modules and keep the full set of course resources.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div>
      <p>
          <a href="https://www.nb-data.com/p/module-6-where-and-having-filtering">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Module 5: Counting the right thing with GROUP BY]]></title><description><![CDATA[Let's learn how `GROUP BY` changes the result and why `COUNT(*)` doesn't always count the thing we need.]]></description><link>https://www.nb-data.com/p/module-5-counting-the-right-thing</link><guid isPermaLink="false">https://www.nb-data.com/p/module-5-counting-the-right-thing</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sun, 20 Sep 2026 14:44:42 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!q3vJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcded422a-7351-49fb-9bd7-b28abe46c6df_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!q3vJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcded422a-7351-49fb-9bd7-b28abe46c6df_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!q3vJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcded422a-7351-49fb-9bd7-b28abe46c6df_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!q3vJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcded422a-7351-49fb-9bd7-b28abe46c6df_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!q3vJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcded422a-7351-49fb-9bd7-b28abe46c6df_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!q3vJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcded422a-7351-49fb-9bd7-b28abe46c6df_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!q3vJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcded422a-7351-49fb-9bd7-b28abe46c6df_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cded422a-7351-49fb-9bd7-b28abe46c6df_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1219711,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/216547507?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcded422a-7351-49fb-9bd7-b28abe46c6df_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!q3vJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcded422a-7351-49fb-9bd7-b28abe46c6df_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!q3vJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcded422a-7351-49fb-9bd7-b28abe46c6df_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!q3vJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcded422a-7351-49fb-9bd7-b28abe46c6df_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!q3vJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcded422a-7351-49fb-9bd7-b28abe46c6df_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>GROUP BY combines rows that have the same value and returns one result row for each group. We often use it with COUNT, SUM, or AVG to calculate a metric for each category.</span></p><p><span>The query itself is usually short, but there are two things we need to check. First, what does one row in the source table represent? Second, what should one row in the result represent?</span></p><p><span>In this module, we&#8217;ll compare item rows, orders, and customers. After that, we&#8217;ll change the grouping columns to produce one row per month instead of one row per month and status.</span></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/module-5-counting-the-right-thing?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/module-5-counting-the-right-thing?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div>
      <p>
          <a href="https://www.nb-data.com/p/module-5-counting-the-right-thing">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Module 4: Finding the top results with ORDER BY and LIMIT]]></title><description><![CDATA[Let's learn how `ORDER BY` and `LIMIT` choose the top or bottom rows, and what to do when several rows tie at the cutoff.]]></description><link>https://www.nb-data.com/p/module-4-finding-the-top-results</link><guid isPermaLink="false">https://www.nb-data.com/p/module-4-finding-the-top-results</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sun, 20 Sep 2026 07:17:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!VqFQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a134c29-12f8-492a-8974-5130eecfe47f_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VqFQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a134c29-12f8-492a-8974-5130eecfe47f_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VqFQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a134c29-12f8-492a-8974-5130eecfe47f_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!VqFQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a134c29-12f8-492a-8974-5130eecfe47f_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!VqFQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a134c29-12f8-492a-8974-5130eecfe47f_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!VqFQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a134c29-12f8-492a-8974-5130eecfe47f_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VqFQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a134c29-12f8-492a-8974-5130eecfe47f_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1a134c29-12f8-492a-8974-5130eecfe47f_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1172042,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/216540271?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a134c29-12f8-492a-8974-5130eecfe47f_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!VqFQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a134c29-12f8-492a-8974-5130eecfe47f_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!VqFQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a134c29-12f8-492a-8974-5130eecfe47f_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!VqFQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a134c29-12f8-492a-8974-5130eecfe47f_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!VqFQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a134c29-12f8-492a-8974-5130eecfe47f_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>ORDER BY decides how SQL arranges the rows, while LIMIT decides how many rows remain in the result. We need both when a request asks for the top customers, the lowest sales, or the latest orders.</span></p><p><span>However, we still have one decision to make. What does &#8220;top&#8221; mean for the question we are answering? A customer can be at the top by total spend, number of orders, or recent growth. The query can run correctly while sorting by the wrong value.</span></p><p><span>In this module, we&#8217;ll use a small customer table to find the highest and lowest spend values. After that, we&#8217;ll compare two ways to handle customers who have the same value at the cutoff.</span></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/module-4-finding-the-top-results?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/module-4-finding-the-top-results?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div>
      <p>
          <a href="https://www.nb-data.com/p/module-4-finding-the-top-results">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Module 3: Filtering data with WHERE and NULL values]]></title><description><![CDATA[Let&#8217;s see how NULL values and time zones change the rows we get from a WHERE clause.]]></description><link>https://www.nb-data.com/p/module-3-filtering-data-with-where</link><guid isPermaLink="false">https://www.nb-data.com/p/module-3-filtering-data-with-where</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sun, 20 Sep 2026 05:45:10 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!3HFC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98e0b6f3-6613-46c5-8a60-0027714425cf_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3HFC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98e0b6f3-6613-46c5-8a60-0027714425cf_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3HFC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98e0b6f3-6613-46c5-8a60-0027714425cf_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!3HFC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98e0b6f3-6613-46c5-8a60-0027714425cf_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!3HFC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98e0b6f3-6613-46c5-8a60-0027714425cf_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!3HFC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98e0b6f3-6613-46c5-8a60-0027714425cf_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3HFC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98e0b6f3-6613-46c5-8a60-0027714425cf_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/98e0b6f3-6613-46c5-8a60-0027714425cf_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1230830,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/214189073?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98e0b6f3-6613-46c5-8a60-0027714425cf_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3HFC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98e0b6f3-6613-46c5-8a60-0027714425cf_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!3HFC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98e0b6f3-6613-46c5-8a60-0027714425cf_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!3HFC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98e0b6f3-6613-46c5-8a60-0027714425cf_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!3HFC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F98e0b6f3-6613-46c5-8a60-0027714425cf_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We use <span>WHERE</span> to choose which rows we want from a table. It is one of the first SQL clauses we learn, but the rows we get still depend on how we write the condition. For example, <code>order_status &lt;&gt; 'canceled&#8217;</code> removes canceled orders. It also removes orders whose status is <span>NULL</span>, even though we didn&#8217;t ask it to.</p><p>In this module, we&#8217;ll try that filter and see why the missing statuses disappear. After that, we&#8217;ll use <span>WHERE</span> to find orders from one day in S&#227;o Paulo when the timestamps are stored in UTC.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/module-3-filtering-data-with-where?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/module-3-filtering-data-with-where?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div>
      <p>
          <a href="https://www.nb-data.com/p/module-3-filtering-data-with-where">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[I Have Worked in Data Science for Almost a Decade. These Skills Aged Better Than I Expected]]></title><description><![CDATA[The tools changed much faster than the work underneath them*]]></description><link>https://www.nb-data.com/p/i-have-worked-in-data-science-for</link><guid isPermaLink="false">https://www.nb-data.com/p/i-have-worked-in-data-science-for</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Thu, 17 Sep 2026 15:49:10 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Tdu4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f263e28-d92e-469d-8e51-69c3d65c2de3_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Tdu4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f263e28-d92e-469d-8e51-69c3d65c2de3_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Tdu4!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f263e28-d92e-469d-8e51-69c3d65c2de3_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!Tdu4!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f263e28-d92e-469d-8e51-69c3d65c2de3_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!Tdu4!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f263e28-d92e-469d-8e51-69c3d65c2de3_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!Tdu4!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f263e28-d92e-469d-8e51-69c3d65c2de3_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Tdu4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f263e28-d92e-469d-8e51-69c3d65c2de3_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9f263e28-d92e-469d-8e51-69c3d65c2de3_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1326400,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/216008771?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f263e28-d92e-469d-8e51-69c3d65c2de3_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Tdu4!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f263e28-d92e-469d-8e51-69c3d65c2de3_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!Tdu4!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f263e28-d92e-469d-8e51-69c3d65c2de3_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!Tdu4!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f263e28-d92e-469d-8e51-69c3d65c2de3_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!Tdu4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f263e28-d92e-469d-8e51-69c3d65c2de3_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I have worked in data science for almost a decade now. During that time, I have seen many tools become popular. There were Hadoop and Spark, followed by easier machine learning libraries, cloud platforms, and now generative AI tools that can help us write code.</p><p>I learned many of these tools along the way. Some are still part of my work, while others do not appear much in my daily projects anymore. However, when I think about the skills I still use regularly, many of them are not new at all.</p><p>The <a href="https://www.weforum.org/publications/the-future-of-jobs-report-2025/in-full/3-skills-outlook/">World Economic Forum&#8217;s 2025 report expects 39%</a> of workers&#8217; core skills to change by 2030. This prediction is for jobs in general, not only data science. At the same time, analytical thinking remains the core skill most commonly identified by employers. For me, that describes data science quite well: we need to keep learning new tools, but we also need to improve how we think about the work.</p><p>So, which skills have aged well in my experience? I also looked at what other data professionals have learned to see where our experiences are similar or different. </p><p>Let&#8217;s get into it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>SQL Is Still Everywhere</h2><p>SQL was already an important skill when I started working with data, and it is still important today. According to <a href="https://www.onetonline.org/link/hot_tech/15-2051.00)">O*NET&#8217;s analysis of U.S. data scientist job postings in 2025</a>, SQL appeared in 51% of postings. For comparison, TensorFlow appeared in 11%, PyTorch in 10%, and Spark in 7%.</p><p>Of course, job postings do not tell us everything about what data scientists do. Still, they show that SQL remains useful even as the tools around it change.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iuZe!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8ab98a7-28cd-4049-a76d-e2aa0232b21c_1600x900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iuZe!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8ab98a7-28cd-4049-a76d-e2aa0232b21c_1600x900.png 424w, https://substackcdn.com/image/fetch/$s_!iuZe!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8ab98a7-28cd-4049-a76d-e2aa0232b21c_1600x900.png 848w, https://substackcdn.com/image/fetch/$s_!iuZe!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8ab98a7-28cd-4049-a76d-e2aa0232b21c_1600x900.png 1272w, https://substackcdn.com/image/fetch/$s_!iuZe!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8ab98a7-28cd-4049-a76d-e2aa0232b21c_1600x900.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!iuZe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8ab98a7-28cd-4049-a76d-e2aa0232b21c_1600x900.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c8ab98a7-28cd-4049-a76d-e2aa0232b21c_1600x900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:68662,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/216008771?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8ab98a7-28cd-4049-a76d-e2aa0232b21c_1600x900.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!iuZe!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8ab98a7-28cd-4049-a76d-e2aa0232b21c_1600x900.png 424w, https://substackcdn.com/image/fetch/$s_!iuZe!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8ab98a7-28cd-4049-a76d-e2aa0232b21c_1600x900.png 848w, https://substackcdn.com/image/fetch/$s_!iuZe!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8ab98a7-28cd-4049-a76d-e2aa0232b21c_1600x900.png 1272w, https://substackcdn.com/image/fetch/$s_!iuZe!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8ab98a7-28cd-4049-a76d-e2aa0232b21c_1600x900.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>What changed for me is how I use SQL. Earlier in my career, I focused on remembering the syntax: joins, subqueries, window functions, and so on. I still need to know them, but writing a valid query is not the difficult part of many analyses.</p><p>Take an example of joining a customer table with a transaction table. The query might run without any errors, but one customer could have many transactions. If I count the rows after the join without checking the data, I could report the number of transactions as the number of customers.</p><p>That is why understanding the data grain, table relationships, and metric definition matters more to me than memorizing every SQL command. AI can help generate the query, but I still need to check what the result represents.</p><p><a href="https://www.linkedin.com/pulse/brutally-honest-advice-landing-data-job-avery-smith-vrote">Avery Smith</a>, who has worked in analyst, data scientist, and data engineer roles, also wrote that he learned many tools on the job. In his experience, domain knowledge helped him understand the problem and interpret the data. I agree with that point. Knowing SQL gives us access to the data, but understanding the context helps us get the correct answer.</p><div><hr></div><h2>Statistics Helps Me Question the Result</h2><p>Statistics is another skill I appreciate more now than when I started. At first, I mostly associated it with formulas, distributions, hypothesis tests, and model assumptions. Those topics are still important, but statistical thinking has become useful to me in a more practical way.</p><p>For example, imagine that an experiment shows a new feature performs better than the old version. Before accepting the result, I would want to know whether the groups were comparable, whether the sample was large enough, and whether the difference is meaningful for the business. I would also ask if we selected the metric before seeing the result.</p><p>We can run a statistical test in a few lines of Python. The function can give us a number, but it cannot decide whether we asked a good question or collected suitable data. The <a href="https://www.bls.gov/ooh/math/data-scientists.htm">U.S. Bureau of Labor Statistics</a> still includes mathematics, statistics, analytical skills, and logical thinking among the important parts of a data scientist&#8217;s work.</p><p>For me, the skill that aged well is knowing what we can conclude from an analysis, and when the evidence is not enough.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/i-have-worked-in-data-science-for?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/i-have-worked-in-data-science-for?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div><h2>Understanding the Problem Comes Before the Model</h2><p>Earlier in my career, I was often excited about choosing a machine learning model. Should I use Random Forest? Would Gradient Boosting perform better? Could I improve the score by tuning more parameters?</p><p>Now I am more interested in the question before modeling: what decision are we trying to support?</p><p>Take a churn prediction project as an example. A model could identify customers who are likely to leave. However, we still need to know what the team can do with that information. Can they contact these customers? How early do they need the prediction? What kind of action could actually help? Without answers to these questions, even an accurate model might not be useful.</p><p>A <a href="https://hdsr.mitpress.mit.edu/pub/9ir6e1j6/release/1">2025 study in Harvard Data Science Review </a>compared data science with decision science skills. The researchers found that data science tends to emphasize coding, machine learning, and data management, while decision science gives more attention to problem framing and business knowledge. The study argues that we need both to turn analysis into useful decisions.</p><p>This also matches <a href="https://yudong-94.github.io/personal-website/blog/FiveCareerLearnings/">Yu Dong&#8217;s reflection on six years in data science.</a> She explains how understanding the product and talking with other teams helped her find work that people could act on. One point from her experience adds to mine: we should not underestimate dashboards and reporting. A reliable dashboard can establish the baseline and shared metric definitions we need before attempting a more advanced model.</p><p>Sometimes the best solution is a model. At other times, it could be an experiment, a dashboard, a simple rule, or better data collection. I have become more comfortable choosing among these options only after I understand the problem.</p><div><hr></div><h2>Messy Data Still Needs Context</h2><p>Data tools have improved a lot. We have cloud warehouses, better pipelines, automated checks, and AI tools that help write data preparation code. Yet messy data has not disappeared.</p><p>In <a href="https://www.getdbt.com/resources/state-of-analytics-engineering-2025">dbt Labs&#8217; 2025 survey of analytics professionals</a>, 57% of respondents said they spent most of their workday maintaining or organizing datasets. The survey covers several analytics roles, not only data scientists, but the result feels familiar to me.</p><p>We can remove duplicate rows or fill missing values with code. The harder question is why those values are there. A duplicate might be a data error, or it might represent a real second transaction. A missing value might mean &#8220;unknown,&#8221; &#8220;not applicable,&#8221; or a problem in the data pipeline. Each situation requires a different response.</p><p>The same problem appears in machine learning features. A variable might have a strong relationship with the target because it was created after the outcome happened. Using it would make our model look good during testing, but the information would not be available when we actually need to make a prediction.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZDGh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7645d88-8901-4b9b-95f0-800410611382_1600x870.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZDGh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7645d88-8901-4b9b-95f0-800410611382_1600x870.png 424w, https://substackcdn.com/image/fetch/$s_!ZDGh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7645d88-8901-4b9b-95f0-800410611382_1600x870.png 848w, https://substackcdn.com/image/fetch/$s_!ZDGh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7645d88-8901-4b9b-95f0-800410611382_1600x870.png 1272w, https://substackcdn.com/image/fetch/$s_!ZDGh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7645d88-8901-4b9b-95f0-800410611382_1600x870.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZDGh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7645d88-8901-4b9b-95f0-800410611382_1600x870.png" width="1456" height="792" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a7645d88-8901-4b9b-95f0-800410611382_1600x870.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:792,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:74532,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/216008771?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7645d88-8901-4b9b-95f0-800410611382_1600x870.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZDGh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7645d88-8901-4b9b-95f0-800410611382_1600x870.png 424w, https://substackcdn.com/image/fetch/$s_!ZDGh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7645d88-8901-4b9b-95f0-800410611382_1600x870.png 848w, https://substackcdn.com/image/fetch/$s_!ZDGh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7645d88-8901-4b9b-95f0-800410611382_1600x870.png 1272w, https://substackcdn.com/image/fetch/$s_!ZDGh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7645d88-8901-4b9b-95f0-800410611382_1600x870.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>For me, understanding how the data was created has aged better than any particular cleaning library.</p><div><hr></div><h2>Evaluation Shows Whether the Work Is Useful</h2><p>Building a model has become easier. We can train several models quickly, use AutoML, or ask AI tools to help create the code. However, we still need to decide whether the result is better than what people already do.</p><p>This is where evaluation becomes important. We need to choose a relevant metric, keep the test data separate, and compare our solution with a realistic baseline. A recommendation model might perform better than a random suggestion, but can it outperform simply showing the most popular items?</p><p>A <a href="https://www.the-odd-dataguy.com/en/blog/2025/02/13/10_years_journey/">data scientist reflecting on ten years of experience</a> described a three-month A/B test comparing personalized recommendations with a popular-items fallback. He also looked at operating costs and found that not every project produced enough value to continue. I find this comparison useful because it evaluates the model against a real alternative rather than a score in isolation.</p><p>The same idea applies to generative AI. A demo can look impressive, but we need representative examples, clear evaluation criteria, and a baseline before saying the new system is better.</p><div><hr></div><h2>Communication Is Part of the Analysis</h2><p>Communication was a skill I underestimated when I started. I thought the technical work came first and presenting the result happened afterward. My view has changed.</p><p>For example, a model might achieve an ROC-AUC of 0.87. That number could be useful to another data scientist, but a business team may ask different questions. How many customers can we reach? What happens when the model is wrong? What should we do next?</p><p>Explaining the result in those terms also helps me check my own work. If I cannot explain why I chose a metric or what the result means, I probably need to look at the analysis again.</p><p>The <a href="https://medium.com/airbnb-engineering/at-airbnb-data-science-belongs-everywhere-917250c6beba">Airbnb data science team</a> described a similar lesson. They found that analysis had less impact when data scientists were too far from the people making decisions. Closer collaboration helped them understand the problem and make their findings more useful. Avery Smith makes a related point in his career reflection: communication at the beginning helps avoid the wrong analysis, while communication at the end helps people use the result.</p><p>For me, communication is no longer something that comes after data science. It helps define the work and determine whether the work has value.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!F_30!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dec4bed-19e7-4674-8d7b-dc3dd7812734_1600x1110.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!F_30!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dec4bed-19e7-4674-8d7b-dc3dd7812734_1600x1110.png 424w, https://substackcdn.com/image/fetch/$s_!F_30!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dec4bed-19e7-4674-8d7b-dc3dd7812734_1600x1110.png 848w, https://substackcdn.com/image/fetch/$s_!F_30!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dec4bed-19e7-4674-8d7b-dc3dd7812734_1600x1110.png 1272w, https://substackcdn.com/image/fetch/$s_!F_30!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dec4bed-19e7-4674-8d7b-dc3dd7812734_1600x1110.png 1456w" sizes="auto, 100vw"><img src="https://substackcdn.com/image/fetch/$s_!F_30!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dec4bed-19e7-4674-8d7b-dc3dd7812734_1600x1110.png" width="1456" height="1010" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5dec4bed-19e7-4674-8d7b-dc3dd7812734_1600x1110.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1010,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:83742,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/216008771?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dec4bed-19e7-4674-8d7b-dc3dd7812734_1600x1110.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!F_30!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dec4bed-19e7-4674-8d7b-dc3dd7812734_1600x1110.png 424w, https://substackcdn.com/image/fetch/$s_!F_30!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dec4bed-19e7-4674-8d7b-dc3dd7812734_1600x1110.png 848w, https://substackcdn.com/image/fetch/$s_!F_30!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dec4bed-19e7-4674-8d7b-dc3dd7812734_1600x1110.png 1272w, https://substackcdn.com/image/fetch/$s_!F_30!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5dec4bed-19e7-4674-8d7b-dc3dd7812734_1600x1110.png 1456w" sizes="auto, 100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>Conclusion</h2><p>I still think learning new tools is important. Python appeared in 66% of the same <a href="https://www.onetonline.org/link/hot_tech/15-2051.00">2025 U.S. data scientist job postings</a>, and AI tools are already changing how we work. We cannot ignore these changes.</p><p>However, after almost a decade, I worry less about knowing every new tool as soon as it appears. I would rather keep improving the skills that have helped me through several changes: understanding the data, questioning the result, framing the right problem, evaluating against a useful baseline, and explaining what the analysis means.</p><p>The tools will continue to change. These are the skills I expect to keep using.</p><p>If you want to learn further about SQL in the AI era, don&#8217;t miss my latest <a href="https://www.nb-data.com/p/sql-and-analytics-course?r=3kmaf">written SQL Course</a>.</p>]]></content:encoded></item></channel></rss>