<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Tableau Prep on stdin</title><link>https://stdin.org/tags/tableau-prep/</link><description>Recent content in Tableau Prep on stdin</description><generator>Hugo -- 0.161.1</generator><language>en</language><copyright>Isaac Kunen</copyright><lastBuildDate>Wed, 09 May 2018 00:00:00 +0000</lastBuildDate><atom:link href="https://stdin.org/tags/tableau-prep/index.xml" rel="self" type="application/rss+xml"/><item><title>Tableau Prep: The Power of Composability</title><link>https://stdin.org/tableau-prep-the-power-of-composability/</link><pubDate>Wed, 09 May 2018 00:00:00 +0000</pubDate><author>Isaac</author><guid>https://stdin.org/tableau-prep-the-power-of-composability/</guid><description>&amp;lt;no value&amp;gt;</description><content type="text/html" mode="escaped"><![CDATA[<p>When we built <a href="https://www.tableau.com/products/prep">Tableau Prep</a>, we put a premium on ensuring  <em>composability</em> of operations: you can take the operations Prep supports and string them together in any combination you need. There are  <em>no</em> restrictions based on where the data came from, or what operations came before.</p>
<p>This means that you never need to think about whether a particular operation is supported in your particular situation: if Prep supports it ever, Prep supports it always. Moreover, this gives you a lot of  <em>power</em> to do what you need to with your data.</p>
<p><img src="youhavethepower.jpg" alt="youhavethepower"></p>
<p>In the rest of this post, we&rsquo;ll walk through a Superstore example that highlights this power.</p>
<h1 id="the-problem">The Problem<a href="#the-problem" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h1>
<p>Let&rsquo;s start with the sample Superstore data from Tableau Desktop. This data set is a list of order details: each row represents one item from an order, with multiple line items accruing to each order.</p>
<p>Given these data, let&rsquo;s try to fulfill what seems like a simple request:</p>
<p>Get the order details for customers with fewer than the median number of orders.</p>
<p>This seems relatively straightforward&hellip; or is it? In cases like this, I often find it helpful to think backwards to come up with a solution:</p>
<p><strong>Step 4</strong>
If we had a list of customers with fewer than the median number of orders, we could cull the order details down to just those from customers on the list. But we don&rsquo;t have a list of these sub-median customers.</p>
<p><strong>Step 3</strong>
If we knew the median number of orders, we could prune the list of customers down to those with fewer than the median. But we don&rsquo;t have the median number of orders.</p>
<p><strong>Step 2</strong>
If we knew the count of orders for each customer, we could aggregate it to find the median number of orders over all customers. But we don&rsquo;t have the number of orders for each customer.</p>
<p><strong>Step 1</strong>
If we had the list of orders for each customer, we could aggregate to get the count for each customer.  _And we do have the order list!<br>
_</p>
<p>Now we have a plan: we&rsquo;ll start with the order details we have, and climb the ladder outlined above to get to the solution.</p>
<h1 id="the-solution">The Solution<a href="#the-solution" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h1>
<p>We start by loading the Superstore data:<img src="step0.png" alt="Step0"></p>
<p>As we&rsquo;ve already observed, these are order details. Each order has a distinct Order ID, but may have more than one line.</p>
<h2 id="step-1"><strong>Step 1</strong><a href="#step-1" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h2>
<p>Following our plan, the first thing we need to is get the count of orders for each customer. To do this we introduce an aggregate: we group by customer and count the <em>distinct</em> number of Order IDs:<img src="step1-1.png" alt="Step1-1"></p>
<p>The distinct makes it so repeated Order IDs — which come from having more than one order detail line per order — are only counted once.</p>
<p>So we don&rsquo;t confuse ourselves later, we&rsquo;ll rename Order ID to Number of Orders:<img src="step1-2-anno.png" alt="Step1-2-anno"></p>
<h2 id="step-2"><strong>Step 2</strong><a href="#step-2" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h2>
<p>Now that we have the list orders for each customer, we can aggregate  <em>again</em> to find the median number of orders per customer:<br>
<img src="step2.png" alt="Step2"></p>
<p>This aggregate is a little funny: There&rsquo;s no grouping field, so we don&rsquo;t partition the table at all. The result is an odd little table with one row and one column, but this record represents the median over all customers we were looking for.</p>
<p>We&rsquo;ll rename this once again:<br>
<img src="step2-2-anno.png" alt="Step2-2-anno"></p>
<h2 id="step-3"><strong>Step 3</strong><a href="#step-3" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h2>
<p>With the median number of orders in hand, we can join it with our list of customers and order counts to filter down that list. I.e., we&rsquo;ll join it with the result of our first aggregate:</p>
<p><img src="step31.png" alt="Step3"></p>
<p>Note the join clause here: we&rsquo;re doing an inner join, but matching when the median is greater than the customer&rsquo;s order count. We also have an error: the types don&rsquo;t match because the result of the median is a floating-point number, not an integer.</p>
<p>If we correct the type, we get our list of customers with fewer than the median number of orders:<img src="step3-21.png" alt="Step3-2"></p>
<h2 id="step-4"><strong>Step 4</strong><a href="#step-4" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h2>
<p>Now that we have our customer list, we&rsquo;re ready to cull the line items. We&rsquo;ll again use a join as a filter, but this time we&rsquo;re joining our latest table with the original input:<br>
<img src="step41.png" alt="Step4"></p>
<p>You can see that there are a bunch of records dropping out from the right: those were the customers with more than the median number of orders. What remain are the line items we care about:<img src="step51.png" alt="Step5.PNG"></p>
<h1 id="wrapping-up">Wrapping Up<a href="#wrapping-up" class="anchor" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
      stroke-linecap="round" stroke-linejoin="round" class="feather">
      <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path>
      <line x1="8" y1="12" x2="16" y2="12"></line>
   </svg></a></h1>
<p>At this point, you might want to clean up a few of the columns we created along the way, but our data are ready to output to Tableau or anywhere else you want to take them.</p>
<p>This may seem a little complex — and it&rsquo;s clearly stretching our flow layout algorithm — but it makes a perfectly fine flow. There was no operator that solved our problem out of the box, but composability made it possible to mix-and-match the operations present to build a computational machines for our task.</p>
<p>We certainly aren&rsquo;t done adding operations to Prep, but there&rsquo;s a rich set already present. And with a little composition, you can make them do some pretty cool tricks.</p>
]]></content></item><item><title>Tableau Prep: The Flow</title><link>https://stdin.org/tableau-prep-the-flow/</link><pubDate>Mon, 07 May 2018 00:00:00 +0000</pubDate><author>Isaac</author><guid>https://stdin.org/tableau-prep-the-flow/</guid><description>&amp;lt;no value&amp;gt;</description><content type="text/html" mode="escaped"><![CDATA[<p>I&rsquo;ve been a bit quiet lately, but Tableau Prep out the door and it&rsquo;s time to make a little noise.</p>
<p>Clark recently wrote an <a href="https://www.tableau.com/about/blog/2018/4/ux-notebook-designing-tableau-preps-coordinated-workspace-85846">excellent post</a> on the basic UX architecture of Prep. Here I&rsquo;d like to cover a key concept underlying Prep that may be a bit foreign to people coming from Tableau: the <em>flow</em>.</p>
<p><img src="1flow.png" alt="1flow"></p>
<p>This isn&rsquo;t the most glamorous part of Prep, but it is one of the most fundamental concepts in the tool, so it seems worth spending some quality time on.</p>
<p>Strap on your life jacket and read on for more.</p>
<p><strong>Data In; Data Out</strong></p>
<p>To understand flows, we start with <em>steps</em> , which are the conceptual unit of work in Tableau Prep. Every time you take an action on your data in Prep, you&rsquo;re adding a step. For example, if we take the world consumer price index data included with the product and add a filter, we find that a new step is added to the flow:</p>
<p><img src="2step.png" alt="2step"></p>
<p>Each item in the flow pane represents a step, and each step works in the same way: data come in from the left, are modified by the step, and leave to the right:</p>
<p><img src="3inandout-annotated.png" alt="3inandout-annotated"></p>
<p>Some steps — <em>cleaning steps</em> — may have multiple sub-steps, or <em>changes.</em> These are just like steps in the flow, but are smaller increments of work. They flow top to bottom:</p>
<p><img src="5cleaning-annotated.png" alt="5cleaning-annotated"></p>
<p>We group these together to help conceptually simplify the flow, but each change acts just like any other step: rows come in, they&rsquo;re modified, and they go out.</p>
<p>Some steps — such as joins — have multiple inputs, but they work the same way: two sets of data come in from the left, they&rsquo;re put together, and the result leaves to the right:</p>
<p><img src="4join-annotated.png" alt="4join-annotated"></p>
<p>And where do they go? On to the next step! Some steps may even have multiple outputs, with the data going to multiple targets:</p>
<p><img src="6twooutannotated.png" alt="6twooutannotated"></p>
<p>Step-by-step we build up a flow: an  <em>ordered</em> sequence of steps that does what we want.</p>
<p><img src="1flow.png" alt="1flow"></p>
<p><strong>Clarity and Control</strong></p>
<p>That ordering is a key aspect of flows. If you&rsquo;re coming from Tableau, you may be aware that it performs operations in a particular order, but the system doesn&rsquo;t advertise this, and generally you don&rsquo;t need to think about it.</p>
<p>But order sometimes matters, and we designed Prep with those times in mind. The CPI data contain both a food index and a general index. Let&rsquo;s say that we&rsquo;ve pivoted the data, and now want to compare each country&rsquo;s CPI to the global average for each year — except we only care about the food index.</p>
<p>To do this, we&rsquo;ll first filter to keep only the food index:</p>
<p><img src="filter-annotated.png" alt="filter-annotated"></p>
<p>And <em>then</em> we&rsquo;ll aggregate by year:</p>
<p><img src="agg-annotated.png" alt="agg-annotated"></p>
<p>Order matters: if we did the aggregate first, we would have folded in the general CPI as well.</p>
<p>This kind of ordering is explicit in Prep. You don&rsquo;t have to guess, and you don&rsquo;t need to coax the system into doing what you want: you just build your flow in the order fits your problem.</p>
<p>And with Prep, you can always go back and see your data at any point along the flow. Just click back and look. This way you can see and control what the flow is doing to your data every step along the way.</p>
<p><strong>Prep is a Competent Cook</strong></p>
<p>We can add another metaphor: think of a flow as a recipe, and let&rsquo;s take a moment to bake some cookies.</p>
<p><img src="julia-spoon.jpg" alt="julia-spoon"></p>
<p>We&rsquo;ve already mixed the wet ingredients — the eggs, the vanilla, the butter — when we get to this part of the recipe:</p>
<ol start="6">
<li>&hellip;</li>
<li>Measure 1.5 cups flour</li>
<li>Add 1/4 teaspoon salt</li>
<li>Add 1/2 teaspoon baking powder</li>
<li>Mix thoroughly</li>
<li>Add dry ingredients to wet ingredients</li>
<li>…</li>
</ol>
<p>A competent cook would mix these dry ingredients before adding them to the wet, but they would take the liberty of combining them in any convenient order: they know it&rsquo;s irrelevant.</p>
<p>Tableau Prep is a competent cook. It can figure out many cases where the order won&rsquo;t matter, and can rearrange them to make your flow run more efficiently. But it will only do this when the reordering won&rsquo;t affect the results that <em>you</em> intended.</p>
<p>So while the flow give a <em>conceptual</em> order to the operations and their execution order, they may not be run that way at all. The result is that you can ignore order when it doesn’t matter, but rely on it when it does.</p>
<p><strong>More than Just Flows</strong></p>
<p>The notion of a flow is not unique to Tableau Prep, and it isn&rsquo;t Prep&rsquo;s most distinguishing feature. The way that Prep uses samples to give you immediate feedback, the way we use analytics to help you see what needs to be done, and the direct manipulation all more directly contribute to what makes Prep special.</p>
<p>But understanding flows is central to understanding how to make Prep do exactly what you want, and it can be a bit of a leap for folks coming from Tableau Desktop. I hope this helps make that leap a little easier.</p>
<p>Happy hacking!</p>
]]></content></item></channel></rss>