<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Posts - A blog @ nonodename.com</title>
    <link>http://localhost:1313/post/</link>
    <description>Recent content in Posts on A blog @ nonodename.com</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Mon, 05 Jan 2026 09:35:22 -0500</lastBuildDate>
    
        <atom:link href="http://localhost:1313/post/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>The six ages of programming</title>
      <link>http://localhost:1313/post/6agesofprogramming/</link>
      <pubDate>Mon, 05 Jan 2026 09:35:22 -0500</pubDate>
      <author>Dan Bennett</author>
      <guid>http://localhost:1313/post/6agesofprogramming/</guid>
      <description>&lt;p&gt;One of the reasons I love the time between Christmas and New Year is that work is quiet and I can experiment. This year, I continued to work on my DuckDB RDF extension, adding support for parsing &lt;a href=&#34;https://github.com/nonodename/duck_rdf/tree/RDF/XML-Support&#34;&gt;RDF/XML.&lt;/a&gt; There&amp;rsquo;s really no need for RDF/XML support, hardly anyone uses it any more, largely because it&amp;rsquo;s such a bear to work with: overly complex with all sorts of largely irrelevant edge cases. (For example, a full XML literal in the object).&lt;/p&gt;
&lt;p&gt;But that wasn&amp;rsquo;t going to stop me! Once I had a basic parser integrated into DuckDB (more of that in a moment), my methodology was simple: create a set of unit tests using all the example documents from the &lt;a href=&#34;https://www.w3.org/TR/rdf-syntax-grammar/&#34;&gt;W3C spec:&lt;/a&gt; Each unit test would parse the xml and ntriple examples, pass them to DuckDB and ask it to do a union on the two. If the six columns returned were an exact match then the count in the union should be the same as the count in the nTriples. Side note, I used DuckDB parameterized queries for this which worked really well. Given a query like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;determine_delta&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;select&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;delta&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;select&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;subject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;predicate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;object_datatype&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;upper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;object_lang&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read_rdf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;testFile&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;.nt&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;union&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;select&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;subject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;predicate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;object_datatype&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;upper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;object_lang&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read_rdf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;testFile&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;.rdf&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;   
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;select&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;count&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read_rdf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;testFile&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;.nt&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then a single test is written &lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;execute&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;determine_delta&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;testFile&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;test/xmlrdf/example07&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;that passes if it returns 0 (e.g. the union of the two tables - one of the tables should be zero rows as they exactly match)&lt;/p&gt;
&lt;p&gt;Having created the tests, I had Gemini write the code using libxml2 to &lt;a href=&#34;https://en.wikipedia.org/wiki/Simple_API_for_XML&#34;&gt;SAX&lt;/a&gt; parse the XML documents. As each test failed I gave Gemini the example it failed on and asked it to improve the implementation. I find the mix of deterministic testing combined with probabilistic code generation to be a great match: keep iterating until the tests pass.&lt;/p&gt;
&lt;p&gt;While I worked on and off on this, I found myself reflecting on the fact that Gen AI enabled software development is really the next leap in developer productivity.  I started my engineering career in the PC era, pre Internet. You would look up how the SDK worked in a book, most libraries you used were purchased from vendors and were delivered via a mailed floppy disk. Support was largely figuring it out on your own, or if you were in an a team, summoning up the courage to ask a senior peer to help.&lt;/p&gt;
&lt;p&gt;By the mid Nineties, those with an Internet connection were asking each other questions in comp.sci.* newsgroups which became even more powerful when Google started indexing them.  There were also dedicated websites, for instance if you used Oracle you used Ask Tom (which I see &lt;a href=&#34;https://asktom.oracle.com&#34;&gt;still exists&lt;/a&gt;!)&lt;/p&gt;
&lt;p&gt;The point being that once you had an Internet connection and access to search your productivity radically changed. The next unlock was Open Source libraries: we went from purchasing (or writing) libraries to do what you needed to finding and integrating them. The innovations have continued, each time giving us enhanced productivity.  &lt;/p&gt;
&lt;p&gt;Taking a step back, it feels to me like we&amp;rsquo;re entering the sixth age of software development, with the ages characterized by ChatGPT and I as:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Name&lt;/th&gt;
          &lt;th&gt;Key Innovation&lt;/th&gt;
          &lt;th&gt;Productivity Unlock&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;1. Punch Cards 50s-60s&lt;/td&gt;
          &lt;td&gt;Batch processing&lt;/td&gt;
          &lt;td&gt;Compilers replace raw machine code. Glimpses of code portability.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;2. Interactive Terminals 60s-70s&lt;/td&gt;
          &lt;td&gt;Time-sharing&lt;/td&gt;
          &lt;td&gt;Instant feedback replaces batch cycles. Languages standardize, much more portability of code.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;3. PCs 80s- 90s&lt;/td&gt;
          &lt;td&gt;Local IDEs&lt;/td&gt;
          &lt;td&gt;Full local dev stack, configuration management tooling starts to appear&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;4. Internet Knowledge 2000-2010&lt;/td&gt;
          &lt;td&gt;Web, Stack Overflow&lt;/td&gt;
          &lt;td&gt;Access to everyone&amp;rsquo;s experiences , rise of sites like StackOverflow. Documentation available via search.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;5a. Open Source Reuse 2010-present&lt;/td&gt;
          &lt;td&gt;Package ecosystems&lt;/td&gt;
          &lt;td&gt;“Import instead of build”. Significant re-use of open source libraries.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;5b. Cloud &amp;amp; DevOps 2010-present&lt;/td&gt;
          &lt;td&gt;Automation + cloud infra&lt;/td&gt;
          &lt;td&gt;Continuous delivery &amp;amp; reproducibility: no waiting for equipment to arrive, find out immediately if code is unsafe for production.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;6. AI-Augmented now&lt;/td&gt;
          &lt;td&gt;LLMs, copilots&lt;/td&gt;
          &lt;td&gt;Code generation + reasoning&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
    </item><item>
      <title>Quantum Leap?</title>
      <link>http://localhost:1313/post/quantumleap/</link>
      <pubDate>Sun, 22 Dec 2024 14:35:22 -0500</pubDate>
      <author>Dan Bennett</author>
      <guid>http://localhost:1313/post/quantumleap/</guid>
      <description>&lt;p&gt;I recently hosted a fireside chat at the Excellence in Energy &lt;a href=&#34;https://commodityinsights.spglobal.com/excellence-in-energy.html&#34;&gt;conference&lt;/a&gt;. The topic was, flavor of the year, AI.&lt;/p&gt;
&lt;p&gt;One of the questions from the audience cited Google&amp;rsquo;s Willow chip and asked whether that has potential to impact AI. If you&amp;rsquo;ve not been following along, Google recently &lt;a href=&#34;https://www.forbes.com/sites/timbajarin/2024/12/13/why-googles-quantum-computer-chip-willow-is-a-game-changer/&#34;&gt;announced&lt;/a&gt; a new quantum chip that offers an advance in error management. Since errors are one of the larger roadblocks to scaling up quantum to practical workloads any advances here are welcome.&lt;/p&gt;
&lt;p&gt;This is a fascinating question. Here&amp;rsquo;s my take:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Quantum computing still has a long way to go from the lab to regular users, particularly in adapting it to workloads that &amp;lsquo;matter&amp;rsquo;. It&amp;rsquo;s a completely different programing paradigm and only suited to particular types of problems. We can&amp;rsquo;t just deploy a python script onto a quantum chip and see it run faster.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When quantum computing goes mainstream, it will likely be embedded in products that hide that complexity from most developers. This is not new: most developers interacting with OpenAI have no idea how they would build the LLM themselves. That&amp;rsquo;s the power of a good abstraction.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;As I&amp;rsquo;ve wrote &lt;a href=&#34;http://localhost:1313/post/energyForAI&#34;&gt;last month&lt;/a&gt;, evolution shows that AI is possible at far lower power draw than current techniques. We&amp;rsquo;re missing multiple technical breakthroughs to drive down power demand.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Quantum computing has already shown that it is capable of generating results that would take classical computers many years to replicate. Many years = power, therefore, in a sense, quantum computing shows us a far more power efficient way of computing.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So, it&amp;rsquo;s a reasonable bet that current complex AI approaches will be adapted to work on quantum platforms, driven by economic forces of lowered power consumption and cost.&lt;/p&gt;
&lt;p&gt;As a coda: back in 1989, Penrose published &amp;ldquo;&lt;a href=&#34;https://en.wikipedia.org/wiki/The_Emperor%27s_New_Mind&#34;&gt;The Emperor&amp;rsquo;s New Mind&lt;/a&gt;&amp;rdquo; which posited that human consciousness is non algorithmic, instead a result of quantum effects. It&amp;rsquo;s fascinating to me that if we do implement AI on quantum computing we&amp;rsquo;re not disproving that hypothesis.&lt;/p&gt;
</description>
    </item><item>
      <title>Energy for AI</title>
      <link>http://localhost:1313/post/energyforai/</link>
      <pubDate>Fri, 15 Nov 2024 19:35:22 -0500</pubDate>
      <author>Dan Bennett</author>
      <guid>http://localhost:1313/post/energyforai/</guid>
      <description>&lt;p&gt;Have you read Leopold Aschenbrenner&amp;rsquo;s &lt;a href=&#34;https://situational-awareness.ai/&#34;&gt;argument&lt;/a&gt; for a &amp;lsquo;Manhattan Project&amp;rsquo; to race to Artificial General Intelligence (AGI)? I admit to being swayed by some of the thinking. 
There&amp;rsquo;s certainly holes that can be picked in the arguments: here&amp;rsquo;s three that a good friend of mine called out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;much of the data that an AGI might need for training is locked up in organizations and, even if access is given, is poorly formatted. Further, a lot of data out there is garbage&lt;/li&gt;
&lt;li&gt;no mention is made of quantum computing. That could upend much of the thinking&lt;/li&gt;
&lt;li&gt;any AGI will be concentrated in a few data centers with very visible power supply. In a race between nation states that&amp;rsquo;s relatively easy infrastructure to destroy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Leopold identifies &lt;a href=&#34;https://situational-awareness.ai/racing-to-the-trillion-dollar-cluster/#Power&#34;&gt;energy&lt;/a&gt; as one of the key constraints to achieving AGI, and energy for AI was a key theme at the recent &lt;a href=&#34;https://www.thenationalnews.com/business/energy/2024/11/04/enact-majlis-abu-dhabi-hosts-global-energy-tech-ai-and-climate-leaders-on-eve-of-adipec/&#34;&gt;ENACT Majlis&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The Majlis agreed the answer to much greater energy demand for AI will be an &amp;lsquo;all of the above&amp;rsquo; with gas, nuclear and renewables all part of the mix. I&amp;rsquo;m no expert on the supply side but I find myself doubting some of this demand thinking:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;AI can be distributed: Apple Intelligence has been a bit of weak launch but the trend that our cell phones will run complex models and work can occur on the edge is only going to increase as models become more efficient and silicon more powerful.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;AGI is likely further away than optimists like Aschenbrenner think it is. We&amp;rsquo;ve seen this trend in technology time and time again and &lt;a href=&#34;https://en.wikipedia.org/wiki/AI_winter&#34;&gt;particularly&lt;/a&gt; in AI. Further, there is some evidence that GenAI is not penetrating into companies as quickly as vendors expect as The Economist &lt;a href=&#34;https://www.economist.com/business/2024/11/04/why-your-company-is-struggling-to-scale-up-generative-ai&#34;&gt;noted&lt;/a&gt; last week.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The biggest opportunity: we have lots of room for optimization. Physics shows us the hard bounds that we can convert power to motion or heat. Nature shows us that it takes 20W for NGI in the human brain. We are many orders of magnitude away from that right now with LLMs. If there is demand for AI then that creates massive market demand for efficient AI. Whether it&amp;rsquo;s binary neural nets, more efficient silicon, or knowledge graphs to ground models, there are many options to chase after.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are many drivers of growth of energy demand. The vast majority of these are distributed with numerous market participants and regulators: EVs, developing economy growth, transition to heat pumps, decarbonization of emissions intensive industries to name but a few.&lt;/p&gt;
&lt;p&gt;By contrast, AI is controlled and supplied by a few vendors who have massive incentive to keep their power consumption down and the deep pockets to drive that. &lt;/p&gt;
&lt;p&gt;Will energy demand continue to grow? Of course. Will AI be a material part of that growth? I&amp;rsquo;m not so sure.
 
 
 &lt;/p&gt;
</description>
    </item><item>
      <title>Semantics revisited</title>
      <link>http://localhost:1313/post/semanticsrevisited/</link>
      <pubDate>Sun, 20 Oct 2024 09:05:22 -0500</pubDate>
      <author>Dan Bennett</author>
      <guid>http://localhost:1313/post/semanticsrevisited/</guid>
      <description>&lt;p&gt;Last year I &lt;a href=&#34;http://localhost:1313/post/semanticLayer&#34;&gt;wrote&lt;/a&gt; about my dissatisfaction with the current state of semantic layers. The TL:DR being that if all you&amp;rsquo;re doing is tagging columns and tables with strings you&amp;rsquo;re not really helping with semantics.&lt;/p&gt;
&lt;p&gt;Last year saw an explosion in &lt;a href=&#34;https://en.wikipedia.org/wiki/Retrieval-augmented_generation&#34;&gt;RAG&lt;/a&gt; systems that, with a simple architecture and a decent LLM permit chat bots that do a good job of interacting with our textual content.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s only natural that the industry should turn to tabular data, since that&amp;rsquo;s where a lot of business information is locked up and SQL remains difficult for end users. The big database engine players are all working on this, for example Snowflake with &lt;a href=&#34;https://www.snowflake.com/en/blog/cortex-analyst-ai-self-service-analytics/&#34;&gt;Cortex Analyst&lt;/a&gt;, Databricks recently &lt;a href=&#34;https://www.databricks.com/blog/unlocking-financial-insights-nyse-ice&#34;&gt;showed&lt;/a&gt; a solution running over financial data and Microsoft have been investing in &lt;a href=&#34;https://learn.microsoft.com/en-us/power-bi/natural-language/q-and-a-copilot-enhancements&#34;&gt;synonyms&lt;/a&gt; to improve the Q&amp;amp;A feature of PowerBI.&lt;/p&gt;
&lt;p&gt;What each of these solutions require is additional metadata to drive the feature, be it synonyms in PBI, comments for Databricks or a &lt;a href=&#34;https://quickstarts.snowflake.com/guide/getting_started_with_cortex_analyst/index.html?_ga=2.150985662.905018482.1729433866-1872037584.1729433866#2&#34;&gt;YAML file&lt;/a&gt; for Snowflake.&lt;/p&gt;
&lt;p&gt;This shouldn&amp;rsquo;t be surprising. Generating SQL from just the table and column names is bound to fail for anything but the most trivial schema as there is far too much ambiguity in what the names &lt;em&gt;mean&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To recap: each vendor is building their own standards for semantics to power natural language query. That is a new layer of lock in. While we&amp;rsquo;re gradually solving the format wars of &lt;a href=&#34;https://blocksandfiles.com/2024/06/05/databricks-buys-tabular-to-win-the-iceberg-war/&#34;&gt;iceberg&lt;/a&gt; and &lt;a href=&#34;https://docs.snowflake.com/en/user-guide/tables-external-intro#delta-lake-support&#34;&gt;deltalake&lt;/a&gt;, we&amp;rsquo;re building a new set of imcompatibility over the top.&lt;/p&gt;
&lt;p&gt;Worse, each solution is based on &lt;a href=&#34;https://blog.google/products/search/introducing-knowledge-graph-things-not/&#34;&gt;strings not things&lt;/a&gt;: the same issues of interopability that I wrote about last year are there and we are not solving for non English speakers, except by adding another layer for error translating the end user&amp;rsquo;s query to English before we figure out the SQL.&lt;/p&gt;
&lt;p&gt;You know what&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;natively supports localization? RDF.&lt;/li&gt;
&lt;li&gt;works across domains and business boundaries? RDF.&lt;/li&gt;
&lt;li&gt;is almost thirty years old, tried and tested? RDF.&lt;/li&gt;
&lt;li&gt;can be &lt;a href=&#34;https://csvw.org&#34;&gt;simply applied&lt;/a&gt; to tabular data? RDF.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The solution is staring us right in the face but we engineers love to reinvent the wheel rather than research, learn and leverage something we don&amp;rsquo;t understand.&lt;/p&gt;
&lt;p&gt;There next couple of years are going to be annoying. Where&amp;rsquo;s the fast forward button on this?&lt;/p&gt;
</description>
    </item><item>
      <title>Single pain of glass?</title>
      <link>http://localhost:1313/post/itdiversity/</link>
      <pubDate>Sat, 20 Jul 2024 10:24:24 -0500</pubDate>
      <author>Dan Bennett</author>
      <guid>http://localhost:1313/post/itdiversity/</guid>
      <description>&lt;p&gt;An abiding desire in Cyber, IT and technology management is to establish a &amp;lsquo;single pane of glass&amp;rsquo;: a unified command and control plane that covers your technology estate.&lt;/p&gt;
&lt;p&gt;The rationale is simple and unarguable: you&amp;rsquo;re far less likely to miss a cyber attack, patching or other systems maintenance if you&amp;rsquo;re only looking in one place. Further, you&amp;rsquo;re likely to get better overall cost of ownership since you benefit from economies of scale and only having to have staff who know one system.&lt;/p&gt;
&lt;p&gt;The concept extends beyond IT. In systems architecture we strive to standardize, simplify and add scale, based on years of accumulated experience showing that complexity adds cost and reduces flexibility.&lt;/p&gt;
&lt;p&gt;Yet the &lt;a href=&#34;https://www.npr.org/2024/07/20/g-s1-12487/crowdstrike-microsoft-outage-update&#34;&gt;events&lt;/a&gt; of yesterday clearly demonstrate the risk: if everything can be managed from one system then that one system &lt;em&gt;can&lt;/em&gt; bring everything to its knees. Similarily if everything routes or depends on one system. Make no mistake, this will happen again. Human error is as sure in life as taxes.&lt;/p&gt;
&lt;p&gt;In human management most organizations embrace diversity, recognizing that a diverse organization brings disparate strengths and resiliance. Maybe it&amp;rsquo;s time to challenge our desire for homogeneity in IT and systems architecutre? Instead, a one plus one strategy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows &lt;em&gt;and&lt;/em&gt; Linux in the cloud (on two hyperscalers)&lt;/li&gt;
&lt;li&gt;Mac &lt;em&gt;and&lt;/em&gt; Windows as client devices&lt;/li&gt;
&lt;li&gt;iOS &lt;em&gt;and&lt;/em&gt; Android for phones&lt;/li&gt;
&lt;li&gt;Two patch and cyber security management systems, each deployed to half of the estate&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You get the idea. There&amp;rsquo;s plenty of nuance here to think about, in particular dependencies within single planes: if a Windows system is dependent on a Linux system and you loose all the Linux servers your Windows system is still out.&lt;/p&gt;
&lt;p&gt;It won&amp;rsquo;t be cheaper either: scale benefit is halved at negotiation time and staff must have a broader skill set.&lt;/p&gt;
&lt;p&gt;This will happen again. Maybe the added cost is worth it if your cleanup time is halved?&lt;/p&gt;
</description>
    </item><item>
      <title>Supporting eGauge in Apple Home</title>
      <link>http://localhost:1313/post/egauge/</link>
      <pubDate>Fri, 03 May 2024 08:49:24 -0500</pubDate>
      <author>Dan Bennett</author>
      <guid>http://localhost:1313/post/egauge/</guid>
      <description>&lt;p&gt;Last September we installed solar on our house. The installer added a LAN accessible realtime monitor from &lt;a href=&#34;https://www.egauge.net&#34;&gt;eGauge Systems&lt;/a&gt;. The device and it&amp;rsquo;s embedded software doesn&amp;rsquo;t have the most intuitive UX but it&amp;rsquo;s very hackable and includes a full, well documented, &lt;a href=&#34;https://webapi.redoc.ly&#34;&gt;REST API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Inspired by Ryan Seddon&amp;rsquo;s blog &lt;a href=&#34;https://lnkd.in/grcsBZ4N&#34;&gt;post&lt;/a&gt;, earlier this year I wrote a Homebridge Plugin that surfaces the data from the eGauge device into Apple Home.&lt;/p&gt;
&lt;p&gt;HomeKit doesn&amp;rsquo;t (yet) support energy monitoring devices natively so I implemented as a light bulb and light sensor.&lt;/p&gt;
&lt;p&gt;In the picture below, the solar is generating just over 5kW (expressed as lux) and providing 93% of the energy for the home.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;appleHome.jpg&#34; alt=&#34;Apple Home Screenshot&#34;&gt;&lt;/p&gt;
&lt;p&gt;Configuration is pretty simple. You&amp;rsquo;ll need a username &amp;amp; password to your eGauge. I recommend creating a separate read only credential so your write credentials aren&amp;rsquo;t stored in the clear in homebridge. To do that, click settings/access control:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;settings.png&#34; alt=&#34;Settings Screenshot&#34;&gt;&lt;/p&gt;
&lt;p&gt;Rest of configuration is done in homebridge, including the registers to read:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;homebridgeSettings.png&#34; alt=&#34;Homebridge Settings Screenshot&#34;&gt;&lt;/p&gt;
&lt;p&gt;Hopefully one day Apple will natively support energy sensors. When they do, I&amp;rsquo;ll update the package to support them. If you have an eGauge &amp;amp; &lt;a href=&#34;https://homebridge.io&#34;&gt;homebridge&lt;/a&gt;, you can download the plugin from &lt;a href=&#34;https://www.npmjs.com/package/homebridge-egauge&#34;&gt;NPM&lt;/a&gt; or install directly from your homebridge instance. Feel free to post any bug reports on &lt;a href=&#34;https://github.com/nonodename/homebridge-egauge&#34;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
</description>
    </item><item>
      <title>Facts, Reference &amp; Masterdata</title>
      <link>http://localhost:1313/post/factsrefandmaster/</link>
      <pubDate>Fri, 16 Feb 2024 15:49:24 -0500</pubDate>
      <author>Dan Bennett</author>
      <guid>http://localhost:1313/post/factsrefandmaster/</guid>
      <description>&lt;p&gt;&lt;em&gt;I connected with an ex-colleague this weekend who not only was gracious enough to share that he found the blog useful but also used some of the articles to explain details to others. With that in mind, here&amp;rsquo;s something I wrote last year that might have wider applicability.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A common confusion in the data space is the difference between facts, reference and master data. To some degree, this is our own fault since the terms are often used interchangeably. For this post I thought I would try and demonstrate the difference, with domain specific examples that, hopefully, help to distinguish the three. To close, I’ll try and give a simple field guide for spotting them in the wild.&lt;/p&gt;
&lt;p&gt;With that out of the way, let’s first tackle reference data. This is data that typically describes a lookup, valuable to defining some characteristic of another piece of data. Some common examples might be lists of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Countries/Regions/Counties&lt;/li&gt;
&lt;li&gt;Commodities&lt;/li&gt;
&lt;li&gt;Units of measurement&lt;/li&gt;
&lt;li&gt;Status fields (a project might be planned, approved, operational, cancelled, abandoned&amp;hellip;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the data item is shown as a drop-down list, or radio buttons it’s likely reference data. Reference data is typically a slowly changing dimension. New countries do occur from time to time, but that is infrequent. We might add a new value to a status field, but rarely would we remove one or add 100 new. Reference data can be a flat list or hierarchical (a taxonomy) or perhaps, something more complex, an &lt;a href=&#34;https://en.wikipedia.org/wiki/Ontology_(information_science)&#34;&gt;ontology&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The fundamental value of reference data is to permit, via standardization, comparison across domains of information. If we have two data sets, LNG liquefaction terminals and gas power stations, using the same reference data for location, we can easily compare by country or region. If they don’t, we place burden on the consumer to make sense of it.&lt;/p&gt;
&lt;p&gt;What distinguishes master data from reference data? Fundamentally, it’s the mutability of the data: Master data inherently changes over time. The list of LNG liquefaction terminals, along with their status, is constantly changing as new facilities are brought online and old retired.&lt;/p&gt;
&lt;p&gt;Like reference data, the value of master data is that if we all use the same set we permit easier modelling and comparison. We also remove potential for duplication both of data and effort. For this reason, customer and product lists are classically thought of as master data: If we want a single view of the customer it’s important that every piece of data we have on them ties to the same record for the customer.&lt;/p&gt;
&lt;p&gt;That mutability over time means that master data is typically more voluminous than reference data. This, in turn, makes clear &lt;a href=&#34;http://localhost:1313/post/identities/&#34;&gt;identifiers&lt;/a&gt; important for uniquely referencing a particular entity on the master data list.&lt;/p&gt;
&lt;p&gt;Everything that is not reference or master data is facts. Facts typically change over time and are often numerical. The maximum output of a power station is a fact: it changes over time as the owner invests in and upgrades it. The real time output is also a fact, albeit one that changes much more rapidly. If it’s a time series, it’s highly likely facts. Invoices, pay slips, utility bils, all are facts referencing master and reference data.&lt;/p&gt;
&lt;p&gt;Where this can get confusing is one person’s facts can be another’s master data. For example, in a CRM system you might have the current revenue of your customer. If you are in the business of tracking company revenues, revenue is a fact, something you track, manage, and sell. For someone else, it’s just a piece of our master data displayed to help contextualize the account.&lt;/p&gt;
&lt;p&gt;That point hints at how to distinguish between the three in the wild as well as the inherent overlap. If:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;someone sells the data, it’s facts or maybe master data (think Cusip or DUNs number), but very rarely reference data&lt;/li&gt;
&lt;li&gt;you can get it for free, it’s reference data (for example, &lt;a href=&#34;https://www.qudt.org&#34;&gt;QUDT&lt;/a&gt;) or perhaps master data (&lt;a href=&#34;https://permid.org&#34;&gt;PermID&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;it&amp;rsquo;s part of your business as usual, it’s either facts or possibly master data.&lt;/li&gt;
&lt;li&gt;you prepare it once and then forget about it, it’s probably reference data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another rule of thumb: the more it changes, the more time we collectively spend on it, the more likely it is facts.&lt;/p&gt;
&lt;p&gt;Hopefully that helps!&lt;/p&gt;
</description>
    </item><item>
      <title>Predictions for 2024</title>
      <link>http://localhost:1313/post/24predictions/</link>
      <pubDate>Sat, 13 Jan 2024 08:59:24 -0500</pubDate>
      <author>Dan Bennett</author>
      <guid>http://localhost:1313/post/24predictions/</guid>
      <description>&lt;p&gt;Happy New Year to you all and welcome to the year of peak? generative AI. If you thought that was 2023, think again.
Inspired by Dave Kellog, who every year posts his &lt;a href=&#34;https://kellblog.com/2024/01/02/kellblog-predictions-for-2024&#34;&gt;predictions&lt;/a&gt; (I encourage you to read and follow) I thought I would have a go. In no particular order:&lt;/p&gt;
&lt;h2 id=&#34;arvrmetaverse-will-continue-to-underwhelm-for-now&#34;&gt;AR/VR/Metaverse will continue to underwhelm, For now&lt;/h2&gt;
&lt;p&gt;This year will see the release of Apple Vision Pro. At a high price, this tool will not move much volume, but it will wow those who try it. The challenge is content: who and why will we invest in 3D content for a device with a very limited user base?&lt;/p&gt;
&lt;p&gt;The Apple strategy is to drive content creation through their existing devices, with 3D video recording enabled on the latest iPhone 15 pros, AR embedded in much of their &lt;a href=&#34;https://www.apple.com/augmented-reality/&#34;&gt;existing phones and tablets&lt;/a&gt; and free software for creating models.  &lt;/p&gt;
&lt;p&gt;Yet, we haven&amp;rsquo;t seen the killer app yet. Sure, there&amp;rsquo;s some gaming on Oculus, and some niche industry applications but until the hardware is cheaper and lighter I don&amp;rsquo;t think this will move the needle.&lt;/p&gt;
&lt;h2 id=&#34;microsoft-will-release-a-data-marketplace&#34;&gt;Microsoft will release a Data Marketplace&lt;/h2&gt;
&lt;p&gt;Microsoft have had many bites at the analytics space over the years with confusing and overlapping services like SSIS, Data Factory, SSAS, Synapse, PowerBI, Purview, Azure Data Catalog, Excel. Highly confusing compared to Snowflake or Databricks and you ran a real risk of choosing the wrong technology and being on an evolutionary dead end.&lt;/p&gt;
&lt;p&gt;The release of &lt;a href=&#34;https://www.microsoft.com/en-us/microsoft-fabric&#34;&gt;Fabric&lt;/a&gt; as a unified marketing message and (in theory) set of compatible services shows they are not ignoring the competition. What&amp;rsquo;s missing, so far, is a data marketplace that works for companies data providers: they want their data to be easy to consume and don&amp;rsquo;t want to be charged for customer compute over it.  Maybe this year Microsoft will make that happen.&lt;/p&gt;
&lt;h2 id=&#34;the-market-will-figure-out-what-genai-is-good-for&#34;&gt;The market will figure out what Gen/AI is good for&lt;/h2&gt;
&lt;p&gt;Back in &amp;lsquo;09 when the iPhone SDK came out, everyone went mad for mobile apps. If you had a website, you had to have a mobile app and the board wanted to know when it would be released. Over time, most of those apps died out, to be replaced by apps that could only exist because of the mobile form factor and connectivity. Think Lyft and Venmo.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re at the same stage with Gen/AI. We know it&amp;rsquo;s there, we&amp;rsquo;re exploring it, adding it to our existing products but no killer app yet. Later this year, someone will really figure it out and we&amp;rsquo;ll look back and wonder how we existed without it.&lt;/p&gt;
&lt;h2 id=&#34;twitterx-will-flame-out&#34;&gt;Twitter/X will flame out&lt;/h2&gt;
&lt;p&gt;Elon has done lots of smart things but buying Twitter doesn&amp;rsquo;t seem like one of them. The strategy is to make it the &amp;rsquo;everything platform&amp;rsquo; but that requires audience, and audience is quickly jumping ship measured by &lt;a href=&#34;https://daringfireball.net/2023/12/app_store_rankings_as_a_proxy_for_social_network_momentum&#34;&gt;App Store downloads&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Reversing the wrong end of network effect is really hard and I don&amp;rsquo;t see Elon wanting to put the time or the money into it when he&amp;rsquo;s about to launch Starship and faces increasing EV competition from Chinese manufacturers. At what point do you just pull the plug?&lt;/p&gt;
&lt;h2 id=&#34;one-of-the-genai-copyright-suits-will-succeed&#34;&gt;One of the Gen/AI Copyright Suits will succeed&lt;/h2&gt;
&lt;p&gt;Tension between making text available for free for SEO and desire for it not to be included in LLMs continues to grow. Both &lt;a href=&#34;https://blog.google/technology/ai/an-update-on-web-publisher-controls/&#34;&gt;Google&lt;/a&gt; and &lt;a href=&#34;https://platform.openai.com/docs/gptbot&#34;&gt;OpenAI&lt;/a&gt; published instructions to block training (while retaining SEO), implicitly agreeing that publishers see a difference between the two use cases. &lt;/p&gt;
&lt;p&gt;Content owners &amp;amp; publishers went to court, most recently the &lt;a href=&#34;https://www.nytimes.com/2023/12/27/business/media/new-york-times-open-ai-microsoft-lawsuit.html&#34;&gt;New York Times&lt;/a&gt;. The core question before the courts will be: &amp;ldquo;is LLM training fair use?&amp;rdquo;. My guess is at least one of the courts will say no. That creates licensing opportunities for companies that own lots of copyright text and, I suspect, we&amp;rsquo;ll end up with some sort of monetization model just like we have for traffic coming from SEO.&lt;/p&gt;
&lt;h2 id=&#34;office-copilot-will-be--30usermonth-by-the-end-of-the-year&#34;&gt;Office CoPilot will be &amp;lt; $30/user/month by the end of the year&lt;/h2&gt;
&lt;p&gt;Microsoft 365 E5 licensing (the one that all big enterprises buy) is $57/user/month. To that Microsoft wants enterprises to add $30/user/month for &lt;a href=&#34;https://www.microsoft.com/en-us/microsoft-365/enterprise/copilot-for-microsoft-365&#34;&gt;CoPilot&lt;/a&gt; - it&amp;rsquo;s GenAI feature set for Office. The argument is that staff productivity will be so much greater that it&amp;rsquo;s worth it. But both customers and Microsoft are going to struggle to quantify that and a &amp;gt;50% increase in licensing is a tough pill to swallow.&lt;/p&gt;
&lt;p&gt;My guess, enterprises will trial, leave it on for a few users but off for most as the cost can&amp;rsquo;t be justified. Small margin insensitive companies (think hedge funds, asset managers) will go all in but that&amp;rsquo;s not where the volume is. Microsoft will respond late this year or early next by bumping E5 to $77/month and including CoPilot.&lt;/p&gt;
&lt;h2 id=&#34;swing-for-the-fences-apple-buys-lucid&#34;&gt;Swing for the Fences: Apple buys Lucid&lt;/h2&gt;
&lt;p&gt;Apple has been playing with cars for some time and yet, CarPlay aside, hasn&amp;rsquo;t brought anything to market. Lucid has some fine technology, a sedan that few people are buying, an SUV that&amp;rsquo;s not coming until 2025 and aggressive competition from Chinese EV manufacturers at price points Lucid cannot meet. Their stock price is way down. They sell direct to customers through their own facilities with their own financing. Further, EV technology, especially battery and sensor, is rapidly changing meaning the half life of EVs is short compared to ICE cars, yet some of this can be fixed over the air. All, just like iPhones. &lt;/p&gt;
&lt;p&gt;If there&amp;rsquo;s a company that knows how to direct sell premium product to the masses, it&amp;rsquo;s Apple. &lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s my predictions. What do you predict?
 &lt;/p&gt;
</description>
    </item><item>
      <title>Filling in the blanks with Power Query</title>
      <link>http://localhost:1313/post/xfinity2/</link>
      <pubDate>Sat, 04 Nov 2023 10:59:24 -0500</pubDate>
      <author>Dan Bennett</author>
      <guid>http://localhost:1313/post/xfinity2/</guid>
      <description>&lt;p&gt;A couple of weeks ago I &lt;a href=&#34;http://localhost:1313/post/xfinity1/&#34;&gt;posted&lt;/a&gt; on using PowerQuery to visualize my many Xfinity outages. The completist in me couldn&amp;rsquo;t leave the data along with missing days (essentially the days when my monitoring didn&amp;rsquo;t log an outage).&lt;/p&gt;
&lt;p&gt;This post explains how to add them back in. It&amp;rsquo;s really pretty simple, using some more advanced Power Query functions. The essential algorithm is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a list of all the days in the dataset&lt;/li&gt;
&lt;li&gt;Subtract from that list days when there was an outage&lt;/li&gt;
&lt;li&gt;Add that resulting list to the list of outages, filling in the gaps in the data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To do that, we need to add a new column to the first dataset, the day of the outage, essentially truncating the start time:
&lt;img src=&#34;1.png&#34; alt=&#34;Add column&#34;&gt;
For simplicity I&amp;rsquo;m going to ignore outages that span days.&lt;/p&gt;
&lt;p&gt;One Power Query can reference another. So the easiest way forward is to create a new query that implements the algorithm above.  Here&amp;rsquo;s the full code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-pq&#34; data-lang=&#34;pq&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;let&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;Earliest&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;List.Min&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;temp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OutageDay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;Latest&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;List.Max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;temp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OutageDay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;Difference&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Duration.TotalDays&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Latest&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Earliest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;InterveningDays&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;List.Dates&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Earliest&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Difference&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;#duration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;ZeroInterveningDays&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;List.Difference&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;InterveningDays&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;temp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OutageDay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;AsTable&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Table.FromList&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ZeroInterveningDays&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Splitter.SplitByNothing&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;OutageDay&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;WithZero&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Table.AddColumn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AsTable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;DownDuration&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;each&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Int32.Type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;SetType&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Table.TransformColumnTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WithZero&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{{&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;OutageDay&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}})&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;AddStartTime&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Table.TransformColumnTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Table.AddColumn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SetType&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;StartTime&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;each&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DateTime.From&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OutageDay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]))&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{{&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;StartTime&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;datetime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}})&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;AddEndTime&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Table.AddColumn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AddStartTime&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;EndTime&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;each&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;StartTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;Joined&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Table.Combine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;temp&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AddEndTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;Joined&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The first two lines calculate the earliest and latest outage days in the dataset &amp;rsquo;temp&amp;rsquo;. That way, when the underlying text file changes so too will our dataset.&lt;/p&gt;
&lt;p&gt;Next we generate a list of days between the earliest and latest using &lt;a href=&#34;https://learn.microsoft.com/en-us/powerquery-m/list-dates&#34;&gt;List.Dates&lt;/a&gt;. That function generates a list of dates and requires a start date, a number of days to list and the gap between each date (#duration(1,0,0,0) essentially means 1 day).&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://learn.microsoft.com/en-us/powerquery-m/list-difference&#34;&gt;List.Difference&lt;/a&gt; is like a set minus operation. Given one list (all the days), take away another list (outage days) and return the days that there was not an outage.&lt;/p&gt;
&lt;p&gt;From then on, it&amp;rsquo;s just a case of making the list look like our existing table:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://learn.microsoft.com/en-us/powerquery-m/table-fromlist&#34;&gt;Table.FromList&lt;/a&gt; creates a table&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://learn.microsoft.com/en-us/powerquery-m/table-addcolumn&#34;&gt;Table.AddColumn&lt;/a&gt; adds the outage duration of 0 for each day&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://learn.microsoft.com/en-us/powerquery-m/table-transformcolumntypes&#34;&gt;Table.TransformColumnTypes&lt;/a&gt; converts the outage day to a date type&lt;/li&gt;
&lt;li&gt;Table.AddColumn is used, twice, to add start and end times of the outage, which in this case we set to midnight.&lt;/li&gt;
&lt;li&gt;Finally we add this new table with the existing outage table using &lt;a href=&#34;https://learn.microsoft.com/en-us/powerquery-m/table-combine&#34;&gt;Table.Combine&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Switch Excel chart to the new table and we finally get a chart with 0 day outages:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;2.png&#34; alt=&#34;Chart&#34;&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve not tried Power Query, I really encourage you to give it a try. While it has its eccentricities it really is a very powerful tool for manipulating data.&lt;/p&gt;
</description>
    </item><item>
      <title>Visualizing Cable Outages using Power Query</title>
      <link>http://localhost:1313/post/xfinity1/</link>
      <pubDate>Sun, 15 Oct 2023 10:59:24 -0500</pubDate>
      <author>Dan Bennett</author>
      <guid>http://localhost:1313/post/xfinity1/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve long been a fan of &lt;a href=&#34;https://powerquery.microsoft.com/en-us/&#34;&gt;PowerQuery&lt;/a&gt; (PQ), even more so since Microsoft finally made it available on Mac. If you&amp;rsquo;re unfamiliar, here&amp;rsquo;s a worked example of how to use it for a, sadly, every day problem.&lt;/p&gt;
&lt;p&gt;Since moving house this summer we&amp;rsquo;ve gone from a rock solid fiber Internet connection to a less than glorious Xfinity cable connection that suffers an outage or two every day (as my team can attest to with frozen Teams calls). It&amp;rsquo;s infuriating.&lt;/p&gt;
&lt;p&gt;Getting customer service from any cable company is always a challenge (&amp;lsquo;have you rebooted your router?&amp;rsquo;) so I resolved to collect some outage data that will perhaps nudge Xfinity to take me seriously.&lt;/p&gt;
&lt;p&gt;The first thing to do is collect good quality data. I installed the &lt;a href=&#34;https://github.com/TristanBrotherton/netcheck&#34;&gt;netcheck&lt;/a&gt; package on my hardwired Raspberry PI  and started to collect data. The software logs outages like this: &lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csh&#34; data-lang=&#34;csh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-----------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;LINK DOWN:                                       Wed 20 Sep 2023 10:29:17 CDT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;LINK RECONNECTED:                                Wed 20 Sep 2023 10:30:51 CDT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;TOTAL DOWNTIME:                                  1 minutes and 34 seconds.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;RECONNECTED LINK SPEED:                         
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-----------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                                 Ping: 22.03 ms
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                                 Download: 26.02 Mbit/s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                                 Upload: 39.22 Mbit/s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;-----------------------------------------------------------------------------&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The key data we need is those LINK DOWN and RECONNECTED lines. Those are easily extracted with a Grep statement:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csh&#34; data-lang=&#34;csh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grep -P &lt;span class=&#34;s2&#34;&gt;&amp;#34;LINK (D|R)&amp;#34;&lt;/span&gt; connection.log &amp;gt; temp.log&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That gives us a log file like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csh&#34; data-lang=&#34;csh&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;LINK DOWN:                                       Tue 19 Sep 2023 07:39:40 CDT
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;LINK RECONNECTED:                                Tue 19 Sep 2023 07:42:26 CDT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;My goal is to get to a three column file, outage start, outage end, duration (in seconds), so I can chart the data, perhaps using a Pivot Chart:
&lt;img src=&#34;1.png&#34; alt=&#34;Pivot Chart&#34;&gt;&lt;/p&gt;
&lt;p&gt;To manipulate the file using PowerQuery, first open Excel and select the Get Data (Power Query) option under the Data Tab (my screenshots are Mac but they are almost identical for PC).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;2.png&#34; alt=&#34;Get data&#34;&gt;&lt;/p&gt;
&lt;p&gt;Select the Text/CSV option, choose the file and you&amp;rsquo;re presented with a preview of the data:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;3.png&#34; alt=&#34;Preview&#34;&gt;&lt;/p&gt;
&lt;p&gt;Click the transform button to launch the PowerQuery Editor. What I love about this environment is that if you prefer to edit your data visually, you can, yet the editing steps are stored as code you can directly edit if you wish.&lt;/p&gt;
&lt;p&gt;A bit like Excel, each line of code (in this case, loading a document) is shown in the function bar with the resulting data shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;4.png&#34; alt=&#34;Transform&#34;&gt;&lt;/p&gt;
&lt;p&gt;Notice all the columns are imported as text (the &amp;lsquo;ABC&amp;rsquo; icon in the column header. The first thing to do convert the time to a timestamp. Click that column, click Transform and change the type to time:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;5.png&#34; alt=&#34;Change data type&#34;&gt;&lt;/p&gt;
&lt;p&gt;Some of the columns we don&amp;rsquo;t need: the timezone, day and &amp;lsquo;LINK&amp;rsquo; columns. Click each column header and select remove column. As you transform the data, you&amp;rsquo;ll see each step being recorded on the right hand side:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;6.png&#34; alt=&#34;Applied Steps&#34;&gt;&lt;/p&gt;
&lt;p&gt;This is one of the things I love about PQ. Click on each step and you can see the exact changes you&amp;rsquo;ve made to your data.&lt;/p&gt;
&lt;p&gt;The next thing to do is to recombine those three date columns into a new column with the date type. Click &amp;lsquo;Custom Column&amp;rsquo; on the &amp;lsquo;Add Column&amp;rsquo; tab. A quick search of the PQ website shows me the function to convert a string to a date:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;7.png&#34; alt=&#34;Custom column&#34;&gt;&lt;/p&gt;
&lt;p&gt;Next, I combine the date and time columns with a new custom column called StartTime. The formula is as simple as
 &lt;code&gt;[Timestamp] &amp;amp; [Column7]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I can now remove the columns I don&amp;rsquo;t need. Unlike Excel, PQ actually copies the data when you create a new column, so removing superfluous columns won&amp;rsquo;t &amp;lsquo;break&amp;rsquo; your formula. It&amp;rsquo;s always good practice to remove unneeded columns to reduce storage and keep things tidy, leaving me with two columns.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;8.png&#34; alt=&#34;Two columns left&#34;&gt;&lt;/p&gt;
&lt;p&gt;To chart this data, I need to combine each pair of rows so I have a start and end time. There&amp;rsquo;s multiple ways of doing this. An easy way is to use Fill Up to copy data from a subsequent row. To do that, we need a new column that will become the end time. I use a conditional column here to only copy the end time if it truly is:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;9.png&#34; alt=&#34;Add conditional column&#34;&gt;&lt;/p&gt;
&lt;p&gt;This results in a half filled End Time column:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;10.png&#34; alt=&#34;but with nulls&#34;&gt;&lt;/p&gt;
&lt;p&gt;Next, I use a &amp;lsquo;Fill Up&amp;rsquo; (under the Transform tab, Fill button) command to replace the &amp;rsquo;null&amp;rsquo; values with the value from the row below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;11.png&#34; alt=&#34;replaced with fill up&#34;&gt;&lt;/p&gt;
&lt;p&gt;Only two steps remain. We need to remove the &amp;lsquo;RECONNECTED&amp;rsquo; rows as we don&amp;rsquo;t need them any more and then calculate the duration of the outage.&lt;/p&gt;
&lt;p&gt;There are multiple ways to achieve the first goal, an easy way is &amp;lsquo;Remove Alternate Rows&amp;rsquo; under the &amp;lsquo;Remove Rows&amp;rsquo; button&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;12.png&#34; alt=&#34;Remove alternates&#34;&gt;&lt;/p&gt;
&lt;p&gt;Finally we add a custom column for duration with the following formula to count seconds:
Duration.TotalSeconds(Duration.From([EndTime]-[StartTime]))&lt;/p&gt;
&lt;p&gt;After removing the Status column we end up with three columns. Click &amp;lsquo;Close and Load&amp;rsquo; to insert them into Excel:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;13.png&#34; alt=&#34;Data loaded to Excel&#34;&gt;&lt;/p&gt;
&lt;p&gt;One thing is missing.. we don&amp;rsquo;t have rows for days when there were no outages. That&amp;rsquo;s a post for another day&amp;hellip; and with the number of outages there&amp;rsquo;s not many needed anyway!&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s all the steps. While it might seem a lot, each is a very simple transformation that we can easily understand and change or delete if we make a mistake:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;14.png&#34; alt=&#34;Full steps&#34;&gt;&lt;/p&gt;
&lt;p&gt;Hopefully this has inspired you to give PQ a try. yourself. Let me know how you get on.&lt;/p&gt;
</description>
    </item>
  </channel>
</rss>