<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jack Kisutsa</title>
    <description>The latest articles on DEV Community by Jack Kisutsa (@jkisutsa).</description>
    <link>https://dev.to/jkisutsa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4087193%2F8eccedfb-a5e1-49c5-9bf7-12a3bdc811c4.png</url>
      <title>DEV Community: Jack Kisutsa</title>
      <link>https://dev.to/jkisutsa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jkisutsa"/>
    <language>en</language>
    <item>
      <title>From Dirty Logistics Data to a Management-Ready Power BI Solution</title>
      <dc:creator>Jack Kisutsa</dc:creator>
      <pubDate>Sat, 26 Sep 2026 13:01:13 +0000</pubDate>
      <link>https://dev.to/jkisutsa/from-dirty-logistics-data-to-a-management-ready-power-bi-solution-2ik0</link>
      <guid>https://dev.to/jkisutsa/from-dirty-logistics-data-to-a-management-ready-power-bi-solution-2ik0</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Building a dashboard is only the visible part of a business intelligence project. Before a chart can support a decision, the underlying data must be understood, cleaned, standardised, modelled and validated.&lt;/p&gt;

&lt;p&gt;That was the central lesson from my JCars Logistics Power BI project.&lt;/p&gt;

&lt;p&gt;The assignment was to develop a complete business intelligence solution for a vehicle sales and logistics company operating through branches in Kenya. The source file contained sales, customers, vehicles, representatives, payment information, delivery activity, logistics cost, returns, ratings and recorded revenue.&lt;/p&gt;

&lt;p&gt;It also contained many of the problems analysts meet in real operational data: duplicate identifiers, mixed date formats, invalid calendar dates, inconsistent categories, text inside numeric columns, multiple currencies and financial values that did not always agree.&lt;/p&gt;

&lt;p&gt;This article explains how I moved from that raw CSV to a tested Power BI model and an interactive management report.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the business problem
&lt;/h2&gt;

&lt;p&gt;JCars Logistics needed more than a collection of visuals. Management needed to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How much active revenue and profit the business generated.&lt;/li&gt;
&lt;li&gt;Which branches and vehicle types contributed the most value.&lt;/li&gt;
&lt;li&gt;Whether deliveries met a reasonable service level.&lt;/li&gt;
&lt;li&gt;Where logistics cost and loss-making orders required attention.&lt;/li&gt;
&lt;li&gt;Which vehicles experienced high returns.&lt;/li&gt;
&lt;li&gt;Whether the source revenue and date fields were reliable enough for decision-making.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I therefore organised the project around a simple analytical flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Preserve and profile the source.&lt;/li&gt;
&lt;li&gt;Clean and validate the data in Power Query.&lt;/li&gt;
&lt;li&gt;Build a dimensional model.&lt;/li&gt;
&lt;li&gt;Create explicit DAX measures.&lt;/li&gt;
&lt;li&gt;Design an executive page and supporting analytical pages.&lt;/li&gt;
&lt;li&gt;Test the entire report.&lt;/li&gt;
&lt;li&gt;Convert the analysis into findings and recommendations.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The raw dataset
&lt;/h2&gt;

&lt;p&gt;The CSV contained 276 rows and 32 columns covering the 2025-2026 period.&lt;/p&gt;

&lt;p&gt;Each row represented an order record or order line associated with a customer, vehicle configuration, branch, sales representative and delivery outcome. Because &lt;code&gt;Units Sold&lt;/code&gt; could exceed one, a row did not necessarily represent one individual vehicle.&lt;/p&gt;

&lt;p&gt;The first important modelling decision was not to trust &lt;code&gt;Order ID&lt;/code&gt; as a technical key. The column contained blanks, placeholders and repeated identifiers. I added &lt;code&gt;Source Row ID&lt;/code&gt; from 1 to 276 to create a stable audit key while preserving the original identifier for business reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Profiling the data before cleaning
&lt;/h2&gt;

&lt;p&gt;I reviewed every column before making transformations. This prevented me from applying a data type simply because the column name suggested one.&lt;/p&gt;

&lt;p&gt;The audit identified 21 significant issue categories, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Order and delivery dates stored as text, Excel serial numbers and invalid calendar dates.&lt;/li&gt;
&lt;li&gt;Delivery dates earlier than order dates.&lt;/li&gt;
&lt;li&gt;Many spellings and formats for the same customer type, branch and region.&lt;/li&gt;
&lt;li&gt;Misspelled sales-representative and vehicle names.&lt;/li&gt;
&lt;li&gt;Text, zero, negative and blank values in &lt;code&gt;Units Sold&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Prices and costs stored in KES, USD, EUR and ZAR.&lt;/li&gt;
&lt;li&gt;Question-mark prefixes in some financial values.&lt;/li&gt;
&lt;li&gt;Discounts represented as percentages, decimals, whole numbers and text.&lt;/li&gt;
&lt;li&gt;Ratings written as numbers, fractions and descriptive text.&lt;/li&gt;
&lt;li&gt;Return indicators stored as Boolean values, yes/no labels, letters and blanks.&lt;/li&gt;
&lt;li&gt;Recorded revenue that was missing, negative, zero or inconsistent with the other order fields.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The audit changed how I approached the project. Rather than trying to force every source value into a clean category, I decided to preserve uncertainty through nulls, &lt;code&gt;Unknown&lt;/code&gt; categories and explicit quality statuses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structuring the Power Query workflow
&lt;/h2&gt;

&lt;p&gt;I preserved the original query as &lt;code&gt;Raw JCars Data&lt;/code&gt; and disabled its load. I then created &lt;code&gt;Stg JCars Data&lt;/code&gt; as a reference query for the transformations.&lt;/p&gt;

&lt;p&gt;This structure served two purposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The source remained unchanged and available for comparison.&lt;/li&gt;
&lt;li&gt;Every downstream table could be created from one prepared layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I cleaned the data column by column. The main stages were:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add &lt;code&gt;Source Row ID&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Trim and clean text.&lt;/li&gt;
&lt;li&gt;Standardise null markers.&lt;/li&gt;
&lt;li&gt;Clean order, customer and location fields.&lt;/li&gt;
&lt;li&gt;Map branches, representatives and lead sources.&lt;/li&gt;
&lt;li&gt;Standardise vehicle fields.&lt;/li&gt;
&lt;li&gt;Parse quantities and discount rates.&lt;/li&gt;
&lt;li&gt;Parse and convert monetary values.&lt;/li&gt;
&lt;li&gt;Standardise operational status fields.&lt;/li&gt;
&lt;li&gt;Parse ratings, review counts and return indicators.&lt;/li&gt;
&lt;li&gt;Clean the date fields.&lt;/li&gt;
&lt;li&gt;Create calculated and validation columns.&lt;/li&gt;
&lt;li&gt;Create the fact and dimension tables.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cleaning one business area at a time made it easier to verify the effect of each step before moving to the next column.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solving the date problem
&lt;/h2&gt;

&lt;p&gt;The date fields required the most judgement.&lt;/p&gt;

&lt;p&gt;The source included examples such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;09-Sept-25&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Excel serial values such as &lt;code&gt;45914&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;31/02/2026&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;April 31 2026&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Ambiguous slash dates such as &lt;code&gt;01/12/2026&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A direct type conversion would either produce errors or interpret some values incorrectly. I used controlled parsing for Excel serials and recognised text formats then documented special decisions at row level.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;09-Sept-25&lt;/code&gt; was parsed as 9 September 2025.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;April 31 2026&lt;/code&gt; was corrected to 30 April only when another valid date supported that correction.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;31/02/2026&lt;/code&gt; was corrected to 28 February 2026 where a valid related date supported the assumption.&lt;/li&gt;
&lt;li&gt;Impossible dates without supporting evidence remained null.&lt;/li&gt;
&lt;li&gt;Valid dates with delivery before order were retained and flagged instead of silently changed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I created &lt;code&gt;Date Validation Status&lt;/code&gt; so the report could distinguish:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Valid date sequences.&lt;/li&gt;
&lt;li&gt;Missing or invalid order dates.&lt;/li&gt;
&lt;li&gt;Missing or invalid delivery dates.&lt;/li&gt;
&lt;li&gt;Delivery before order date.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The final model identified 48 orders with date-quality issues. These records remained part of the dataset but invalid chronology did not enter the average delivery-duration calculation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Converting currencies to KES
&lt;/h2&gt;

&lt;p&gt;The financial columns included KES, USD, EUR, ZAR, unlabeled amounts and question-mark prefixes. Aggregating those values directly would have produced meaningless totals.&lt;/p&gt;

&lt;p&gt;I separated currency identification from numeric parsing, then applied fixed project rates:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Currency&lt;/th&gt;
&lt;th&gt;KES per unit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;KES or unlabeled&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USD&lt;/td&gt;
&lt;td&gt;130.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EUR&lt;/td&gt;
&lt;td&gt;150.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ZAR&lt;/td&gt;
&lt;td&gt;7.50&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The assignment specifically required unlabeled monetary values to be treated as KES.&lt;/p&gt;

&lt;p&gt;The question-mark prefix required an additional assumption. Based on the scale and pattern of the affected amounts, I treated it as USD and documented the decision rather than allowing the values to become null without explanation.&lt;/p&gt;

&lt;p&gt;The rates were fixed rounded assumptions so the analysis would be reproducible across the dataset. They were not intended to represent the exact exchange rate on every transaction date.&lt;/p&gt;

&lt;p&gt;I referred to the &lt;a href="https://www.centralbank.go.ke/rates/forex-exchange-rates/" rel="noopener noreferrer"&gt;Central Bank of Kenya Foreign Exchange Rates&lt;/a&gt; page. CBK publishes indicative daily rates based on average market buying and selling rates. The model therefore creates a common analytical reporting currency but it should not replace transaction-date accounting conversion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Calculating and validating revenue
&lt;/h2&gt;

&lt;p&gt;The dataset already contained &lt;code&gt;Revenue Recorded&lt;/code&gt; but I did not assume it was correct. I calculated revenue independently in Power Query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Calculated Revenue KES =
Units Sold × Unit Selling Price KES × (1 − Discount Rate)
+ Delivery Fee KES
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then compared calculated and recorded revenue and created three validation outcomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Match&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Mismatch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cannot Validate&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result was one of the most important findings in the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;116 records matched.&lt;/li&gt;
&lt;li&gt;123 records mismatched.&lt;/li&gt;
&lt;li&gt;37 records could not be validated.&lt;/li&gt;
&lt;li&gt;The match rate among validateable records was 48.54%.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of hiding the discrepancy, I made it visible on the Customer and Data Quality page. A dashboard can communicate both business performance and the reliability of the data used to calculate it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining transaction status
&lt;/h2&gt;

&lt;p&gt;The source contained payment, delivery and return information that could produce different interpretations of whether an order should count as active revenue.&lt;/p&gt;

&lt;p&gt;I created &lt;code&gt;Final Transaction Status&lt;/code&gt; with four mutually exclusive categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Active Sale: 153 orders.&lt;/li&gt;
&lt;li&gt;Returned: 73 orders.&lt;/li&gt;
&lt;li&gt;Cancelled: 42 orders.&lt;/li&gt;
&lt;li&gt;Refunded: 8 orders.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Active commercial KPIs use only &lt;code&gt;Active Sale&lt;/code&gt; records. This prevents returned, cancelled and refunded transactions from inflating active revenue and profit.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Returned Orders&lt;/code&gt; measure is deliberately different from the final transaction-status count. It uses the cleaned returned flag, resulting in 88 returned orders among 256 records with a known return status. That distinction is documented because the two fields answer different questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the data model
&lt;/h2&gt;

&lt;p&gt;The final Power BI model uses a compact star schema:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;FactSalesOrders&lt;/code&gt;: 276 rows.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DimDate&lt;/code&gt;: 730 dates covering 2025 and 2026.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DimBranch&lt;/code&gt;: 8 standard branches.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DimSalesRep&lt;/code&gt;: 10 representatives.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DimVehicle&lt;/code&gt;: 35 vehicle combinations.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_Measures&lt;/code&gt;: explicit DAX measures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model uses many-to-one relationships and single-direction filtering from dimensions to the fact table.&lt;/p&gt;

&lt;p&gt;The active date relationship connects &lt;code&gt;FactSalesOrders[Order Date]&lt;/code&gt; to &lt;code&gt;DimDate[Date]&lt;/code&gt;. A second inactive relationship connects &lt;code&gt;Delivery Date&lt;/code&gt; to the same dimension. Delivery-date measures activate that relationship using &lt;code&gt;USERELATIONSHIP&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This allowed one date table to support both sales analysis and delivery analysis without creating ambiguous filter paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the DAX layer
&lt;/h2&gt;

&lt;p&gt;I used explicit measures so the business logic remained visible and reusable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Active Orders =
CALCULATE(
    [Total Orders],
    FactSalesOrders[Final Transaction Status] = "Active Sale"
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Active Sales Revenue KES =
CALCULATE(
    SUM(FactSalesOrders[Calculated Revenue KES]),
    FactSalesOrders[Final Transaction Status] = "Active Sale"
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;On-Time Delivery Rate =
DIVIDE(
    [On-Time Delivered Orders],
    [Delivered Orders with Valid Duration]
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Return Rate =
DIVIDE(
    [Returned Orders],
    [Orders with Known Return Status]
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Orders by Delivery Date =
CALCULATE(
    [Total Orders],
    USERELATIONSHIP(
        FactSalesOrders[Delivery Date],
        DimDate[Date]
    )
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other measures covered gross order revenue, active cost, gross profit, gross margin, average order value, logistics cost, discounts, cancellations, refunds, loss-making orders, revenue year to date and data-quality counts.&lt;/p&gt;

&lt;p&gt;One practical DAX lesson was that &lt;code&gt;CALCULATE&lt;/code&gt; changes filter context. It does not replace multiplication, subtraction or addition. Row-level financial calculations remained in Power Query, while measures used &lt;code&gt;CALCULATE&lt;/code&gt;, &lt;code&gt;SUMX&lt;/code&gt;, &lt;code&gt;DIVIDE&lt;/code&gt; and time-intelligence functions to respond dynamically to report filters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the report
&lt;/h2&gt;

&lt;p&gt;The final report contains five visible pages and two hidden support pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Executive Overview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foct7nz7idbyhogm1efsg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foct7nz7idbyhogm1efsg.png" alt=" " width="799" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first page provides the management summary. It shows active revenue, gross profit, gross margin, active orders, on-time delivery and average customer rating. Supporting visuals compare branch revenue, monthly revenue and margin, transaction status and vehicle profitability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sales and Profitability
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6m2zoe95e1kjjfhwkw03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6m2zoe95e1kjjfhwkw03.png" alt=" " width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This page focuses on revenue, cost, profit, margin, loss-making orders, sales representatives, discounts, customer types and vehicle economics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operations and Logistics
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ydrwfjl9ztgz9g3f8j6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ydrwfjl9ztgz9g3f8j6.png" alt=" " width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The logistics page analyses delivered orders, average delivery days, the on-time rate, delivery status, branch delivery efficiency and logistics cost by vehicle type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customer and Data Quality
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foaqzzmgt3tswbimvrubt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foaqzzmgt3tswbimvrubt.png" alt=" " width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This page combines customer ratings and returns with revenue-validation and date-quality results. I included data quality in the business report because management should understand the confidence limits around the KPIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Insights and Recommendations
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fda0azqfvnzfbuisv62ky.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fda0azqfvnzfbuisv62ky.png" alt=" " width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final visible page connects the analysis to decisions through business questions, findings, insights and actions.&lt;/p&gt;

&lt;p&gt;The hidden support pages are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Branch Details&lt;/code&gt;, which receives drill-through context from branch visuals.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Vehicle Tooltip&lt;/code&gt;, which displays vehicle revenue, profit, margin and return rate on hover.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Year, Region, Branch and Vehicle Type slicers are synchronised across the main analytical pages. A page navigator provides consistent movement through the report.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the analysis found
&lt;/h2&gt;

&lt;p&gt;With all slicers cleared, the final baseline results were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;276 total orders.&lt;/li&gt;
&lt;li&gt;153 active sales orders.&lt;/li&gt;
&lt;li&gt;Approximately KES 905.0M active sales revenue.&lt;/li&gt;
&lt;li&gt;Approximately KES 804.7M active sales cost.&lt;/li&gt;
&lt;li&gt;Approximately KES 99.2M active gross profit.&lt;/li&gt;
&lt;li&gt;11.65% active gross margin.&lt;/li&gt;
&lt;li&gt;11 loss-making active orders.&lt;/li&gt;
&lt;li&gt;113 on-time orders among 123 delivered orders with valid duration.&lt;/li&gt;
&lt;li&gt;91.87% on-time delivery rate.&lt;/li&gt;
&lt;li&gt;88 returned orders among 256 known return statuses.&lt;/li&gt;
&lt;li&gt;34.38% return rate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The branch analysis showed that Thika Yard led active revenue at approximately KES 171M. Kakamega Yard followed at KES 149M and Athi River Yard at KES 144M. Nairobi HQ recorded approximately KES 54M.&lt;/p&gt;

&lt;p&gt;SUVs dominated the vehicle portfolio, producing approximately KES 510M in active revenue and KES 60M in gross profit. That makes SUVs the most important commercial category but it also creates product-concentration risk.&lt;/p&gt;

&lt;p&gt;The overall delivery result was strong, but branch performance was uneven. Thika and Kisumu achieved 100% on-time performance. Nairobi HQ recorded approximately 75% and an average delivery duration near 45 days.&lt;/p&gt;

&lt;p&gt;Pickup and Hatchback had the highest return rates at approximately 42.86% and 42.42%. The dataset cannot prove why those returns occurred, so the result should trigger investigation by model, supplier, complaint and delivery handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommendations
&lt;/h2&gt;

&lt;p&gt;I converted the findings into five management recommendations.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Protect profitability
&lt;/h3&gt;

&lt;p&gt;Review the 11 loss-making active orders and introduce minimum-margin controls and approval thresholds for unusually high discounts.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Improve delivery performance
&lt;/h3&gt;

&lt;p&gt;Conduct operational reviews at Nairobi HQ, Mombasa Port Yard and Athi River Yard. The review should separate routing issues, carrier performance, workload and data-entry delays.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Reduce returns
&lt;/h3&gt;

&lt;p&gt;Analyse Pickup and Hatchback returns by model, customer complaint, supplier and delivery handling before changing procurement or inventory decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Strengthen data controls
&lt;/h3&gt;

&lt;p&gt;Make order date, delivery date, currency and revenue fields mandatory. Add validation rules before records enter reporting systems so impossible dates and malformed financial values are rejected early.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Manage concentration risk
&lt;/h3&gt;

&lt;p&gt;Continue monitoring profitable SUV growth while expanding profitable Sedan, Pickup and Crossover opportunities where the evidence supports them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the completed report
&lt;/h2&gt;

&lt;p&gt;Before finalising the project, I performed a complete quality-assurance review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refreshed the model and confirmed that no Power Query errors appeared.&lt;/li&gt;
&lt;li&gt;Reconciled baseline measures with all slicers cleared.&lt;/li&gt;
&lt;li&gt;Tested the Year, Region, Branch and Vehicle Type slicers.&lt;/li&gt;
&lt;li&gt;Tested page navigation.&lt;/li&gt;
&lt;li&gt;Tested the Branch Details drill-through page.&lt;/li&gt;
&lt;li&gt;Tested the Vehicle Tooltip on multiple vehicle categories.&lt;/li&gt;
&lt;li&gt;Tested cross-filtering between visuals.&lt;/li&gt;
&lt;li&gt;Reviewed visual, page and report filters.&lt;/li&gt;
&lt;li&gt;Checked for truncated titles, unintended blanks, overlapping visuals and inconsistent formats.&lt;/li&gt;
&lt;li&gt;Confirmed that support pages remained hidden.&lt;/li&gt;
&lt;li&gt;Exported and reviewed the final report PDF.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All final checks passed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons from the project
&lt;/h2&gt;

&lt;p&gt;The project reinforced several practical lessons.&lt;/p&gt;

&lt;p&gt;First, data cleaning is not only technical. Correcting an impossible date requires business reasoning, supporting evidence and documentation. When the evidence is insufficient, retaining null is often better than inventing certainty.&lt;/p&gt;

&lt;p&gt;Second, calculated and recorded financial values should be reconciled. A mismatch is not an inconvenience to remove; it can be one of the most important findings in the analysis.&lt;/p&gt;

&lt;p&gt;Third, Power Query and DAX solve different problems. Power Query prepared row-level fields and classifications. DAX created filter-responsive measures for the semantic model.&lt;/p&gt;

&lt;p&gt;Fourth, interactivity should help answer a question. The branch drill-through and vehicle tooltip were useful because they allowed deeper investigation without overcrowding the main pages.&lt;/p&gt;

&lt;p&gt;Finally, the report became more useful when data quality was treated as part of business performance. Management needs to know both what the numbers say and how reliable the underlying records are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The final JCars Logistics solution is more than a dashboard. It is a documented analytical system that preserves the source, exposes assumptions, validates financial values, separates active from non-active transactions and connects operational evidence to management action.&lt;/p&gt;

&lt;p&gt;The project demonstrates the complete Power BI workflow: profiling, Power Query transformation, dimensional modelling, DAX, visual design, interactivity, validation and business communication.&lt;/p&gt;

&lt;p&gt;The strongest outcome was not simply producing a set of KPIs. It was building a report in which those KPIs could be traced back to documented rules, cleaning decisions and validation results.&lt;/p&gt;




</description>
      <category>powebi</category>
      <category>dataanalytics</category>
      <category>dax</category>
      <category>powerquery</category>
    </item>
    <item>
      <title>Power BI Data Modelling, Relationships and Joins</title>
      <dc:creator>Jack Kisutsa</dc:creator>
      <pubDate>Sun, 13 Sep 2026 15:40:38 +0000</pubDate>
      <link>https://dev.to/jkisutsa/power-bi-data-modelling-relationships-and-joins-4idj</link>
      <guid>https://dev.to/jkisutsa/power-bi-data-modelling-relationships-and-joins-4idj</guid>
      <description>&lt;h2&gt;
  
  
  Power BI Data Modelling, Relationships and Joins: A Practical Guide
&lt;/h2&gt;

&lt;p&gt;When I began learning Power BI, I naturally focused on dashboards. However, an attractive dashboard can still be slow or misleading when the data behind it is poorly organised. The data model is the foundation: it determines how tables communicate, how filters move and how DAX measures behave.&lt;/p&gt;

&lt;p&gt;This article uses a retail-sales example to explain modelling schemas, fact and dimension tables, relationships, filter direction and Power Query joins. It also addresses a common beginner question: if both joins and relationships connect tables, when should each be used?&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Data modelling in Power BI
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Data modelling&lt;/strong&gt; is the process of organising data into tables and defining logical connections between them. Sales transactions may be stored separately from customers, products, dates and locations. A good model allows a user to select &lt;em&gt;Electronics&lt;/em&gt;, &lt;em&gt;Nairobi&lt;/em&gt; or &lt;em&gt;2026&lt;/em&gt; and receive the correct result.&lt;/p&gt;

&lt;p&gt;A well-designed model improves accuracy, makes DAX easier to write, reduces duplicated data, improves VertiPaq compression and supports future growth. It also makes the solution easier for another analyst to understand and maintain.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Flat table, star schema and snowflake schema
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fucsocw9xq0ixvz9s47mk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fucsocw9xq0ixvz9s47mk.png" alt=" " width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  A. Flat table
&lt;/h4&gt;

&lt;p&gt;A &lt;strong&gt;flat table&lt;/strong&gt; stores facts and descriptions together. One Sales table might contain OrderID, Date, CustomerName, ProductName, Category, City, Quantity and SalesAmount.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; It is quick to build, familiar to Excel users and suitable for a small prototype. No relationships need to be managed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt; Product and customer descriptions repeat for every transaction, increasing table width and storage. Repetition creates inconsistent values and difficult maintenance. Large flat tables are less reusable and can make complex DAX harder to organise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best use:&lt;/strong&gt; Small, one-purpose datasets with limited growth.&lt;/p&gt;

&lt;h4&gt;
  
  
  B. Star schema
&lt;/h4&gt;

&lt;p&gt;A &lt;strong&gt;star schema&lt;/strong&gt; places a fact table at the centre and connects it directly to dimensions such as Customer, Product, Date and Location.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; It provides clear filter paths, simpler DAX, less duplication and strong performance. Dimensions can also be reused by several reports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt; It requires deliberate preparation and a clearly defined fact-table grain. Dimensions may contain controlled repetition, such as Category repeated for products in that category.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best use:&lt;/strong&gt; Most production Power BI models, especially when data volume and reporting needs will grow.&lt;/p&gt;

&lt;h4&gt;
  
  
  C. Snowflake schema
&lt;/h4&gt;

&lt;p&gt;A &lt;strong&gt;snowflake schema&lt;/strong&gt; normalises dimensions. Instead of storing Category and Subcategory in DimProduct, it may use DimProduct → DimSubcategory → DimCategory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; It reduces repetition within dimensions and can mirror a normalised source database or independently managed hierarchy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt; Extra tables and relationships increase model complexity. Filters cross more paths, fields are harder to find and DAX becomes less intuitive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best use:&lt;/strong&gt; Large or independently governed hierarchies. For ordinary reporting, small snowflaked dimensions are often flattened in Power Query.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Flat table&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Star schema&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Snowflake schema&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Structure&lt;/td&gt;
&lt;td&gt;One wide table&lt;/td&gt;
&lt;td&gt;Central fact with dimensions&lt;/td&gt;
&lt;td&gt;Dimensions split further&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redundancy&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Low and controlled&lt;/td&gt;
&lt;td&gt;Lowest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance at scale&lt;/td&gt;
&lt;td&gt;Often weak&lt;/td&gt;
&lt;td&gt;Usually strongest&lt;/td&gt;
&lt;td&gt;More traversal required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complexity&lt;/td&gt;
&lt;td&gt;Simple initially&lt;/td&gt;
&lt;td&gt;Clear and balanced&lt;/td&gt;
&lt;td&gt;Highest&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Fact tables, dimension tables and grain
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1tertgbaod6v76140l37.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1tertgbaod6v76140l37.png" alt=" " width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;fact table&lt;/strong&gt; records measurable business events—what happened and how much. FactSales may contain DateKey, CustomerID, ProductID, LocationID, OrderID, Quantity, Cost and SalesAmount. It is usually tall and narrow because transactions grow continuously.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;dimension table&lt;/strong&gt; provides context used for filtering, grouping and labelling. DimProduct may contain ProductName, Brand and Category; DimCustomer may contain CustomerName and Segment; DimDate may contain Month, Quarter and Year. Dimensions answer &lt;strong&gt;who, what, when and where&lt;/strong&gt;, while facts answer &lt;strong&gt;how many and how much&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;grain&lt;/strong&gt; states exactly what one fact row represents. If FactSales has a grain of &lt;strong&gt;one product line per order&lt;/strong&gt;, an order containing three products creates three rows. Grain must remain consistent. Combining order totals with order-line records can double-count revenue.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Table&lt;/th&gt;
&lt;th&gt;Typical contents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FactSales&lt;/td&gt;
&lt;td&gt;Foreign keys, Quantity, Discount, Cost, SalesAmount&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FactOrders&lt;/td&gt;
&lt;td&gt;One row per order and order-level measures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FactTransactions&lt;/td&gt;
&lt;td&gt;Debit, Credit, Amount and account keys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DimCustomer&lt;/td&gt;
&lt;td&gt;Customer name, segment, city&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DimProduct&lt;/td&gt;
&lt;td&gt;Product name, brand, category&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DimDate&lt;/td&gt;
&lt;td&gt;Date, month, quarter, year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DimLocation&lt;/td&gt;
&lt;td&gt;Branch, city, region, country&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For example, &lt;code&gt;Total Sales = SUM(FactSales[SalesAmount])&lt;/code&gt; can be analysed by any related dimension without copying its descriptive columns into FactSales.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Relationships in Power BI
&lt;/h3&gt;

&lt;p&gt;A relationship is a logical connection between columns in two loaded tables. It allows filters and calculations to work across tables without physically combining them.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;primary key&lt;/strong&gt; uniquely identifies a dimension row. DimCustomer[CustomerID] should contain C001 once. &lt;br&gt;
A &lt;strong&gt;foreign key&lt;/strong&gt; is the matching fact column; FactSales[CustomerID] may contain C001 many times because that customer can purchase repeatedly. Related columns need compatible data types.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Referential integrity&lt;/strong&gt; means each foreign key has a matching dimension key. If FactSales contains C999 but DimCustomer does not, Power BI may group the transaction under a blank member. Anti joins can help detect such orphan records.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8x8sqxyghzq7dxwc81tq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8x8sqxyghzq7dxwc81tq.png" alt=" " width="799" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One-to-many (1:*)&lt;/strong&gt;&lt;br&gt;
One unique dimension value matches many fact rows. One product appears once in DimProduct but can appear in thousands of sales. This is the standard star-schema relationship.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One-to-one (1:1)&lt;/strong&gt;&lt;br&gt;
Each key occurs once in both tables—for example, one Employee row matched to one EmployeeSecurity row. It can separate sensitive fields, although merging the tables may be simpler when both describe the same entity at the same grain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Many-to-many (:)&lt;/strong&gt;&lt;br&gt;
Keys repeat on both sides. Many students take many courses. A direct many-to-many relationship can make totals difficult to interpret, so a bridge table containing unique StudentID–CourseID pairs is often safer. It connects to Students and Courses through two one-to-many relationships.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An &lt;strong&gt;active relationship&lt;/strong&gt; appears as a solid line and operates automatically. An &lt;strong&gt;inactive relationship&lt;/strong&gt; is dotted and must be activated in a measure. If FactSales has OrderDateKey and ShipDateKey linked to DimDate, Order Date may be active while Ship Date is inactive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;Sales&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;Ship&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="n"&gt;CALCULATE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Total&lt;/span&gt; &lt;span class="n"&gt;Sales&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;USERELATIONSHIP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FactSales&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ShipDateKey&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;DimDate&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DateKey&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Filter direction
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo6hmo9hkfut91k733qyc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo6hmo9hkfut91k733qyc.png" alt=" " width="800" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;single-direction filtering&lt;/strong&gt;, a filter normally travels from the one-side dimension to the many-side fact. Selecting &lt;strong&gt;Electronics&lt;/strong&gt; in DimProduct identifies the relevant ProductIDs, filters FactSales and recalculates Total Sales. This behaviour is predictable and is the recommended default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bidirectional filtering&lt;/strong&gt; allows filters to travel both ways. It can help in carefully designed bridge-table scenarios, but it should not be enabled simply to make a visual work. Multiple routes between tables can create ambiguous filter paths, unexpected totals, more query work and difficult debugging.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Joins in Power Query
&lt;/h3&gt;

&lt;p&gt;A join matches rows from two tables using a common column. In Power Query, select &lt;strong&gt;Home → Merge Queries&lt;/strong&gt;, choose both tables and matching columns, choose a join kind then expand the required columns.&lt;/p&gt;

&lt;h4&gt;
  
  
  Customers (left)
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;CustomerID&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Name&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;C1&lt;/td&gt;
&lt;td&gt;Amina&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C2&lt;/td&gt;
&lt;td&gt;Brian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C3&lt;/td&gt;
&lt;td&gt;Carol&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Orders (right)
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;OrderID&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;CustomerID&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Amount&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;O10&lt;/td&gt;
&lt;td&gt;C1&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O11&lt;/td&gt;
&lt;td&gt;C1&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O12&lt;/td&gt;
&lt;td&gt;C2&lt;/td&gt;
&lt;td&gt;700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O13&lt;/td&gt;
&lt;td&gt;C4&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;C3 has no order, while O13 refers to a missing customer. This reveals how each join treats unmatched records.&lt;/p&gt;

&lt;h4&gt;
  
  
  Left outer join
&lt;/h4&gt;

&lt;p&gt;Keeps every customer and matching orders. Amina appears twice; Carol remains with null order values.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;CustomerID&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Name&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;OrderID&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Amount&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;C1&lt;/td&gt;
&lt;td&gt;Amina&lt;/td&gt;
&lt;td&gt;O10&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C1&lt;/td&gt;
&lt;td&gt;Amina&lt;/td&gt;
&lt;td&gt;O11&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C2&lt;/td&gt;
&lt;td&gt;Brian&lt;/td&gt;
&lt;td&gt;O12&lt;/td&gt;
&lt;td&gt;700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C3&lt;/td&gt;
&lt;td&gt;Carol&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use it when Customers is the master list and no customer should disappear.&lt;/p&gt;

&lt;h4&gt;
  
  
  Right outer join
&lt;/h4&gt;

&lt;p&gt;Keeps every order and matching customers. O13 remains with a null customer name.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;CustomerID&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Name&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;OrderID&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Amount&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;C1&lt;/td&gt;
&lt;td&gt;Amina&lt;/td&gt;
&lt;td&gt;O10&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C1&lt;/td&gt;
&lt;td&gt;Amina&lt;/td&gt;
&lt;td&gt;O11&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C2&lt;/td&gt;
&lt;td&gt;Brian&lt;/td&gt;
&lt;td&gt;O12&lt;/td&gt;
&lt;td&gt;700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C4&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;O13&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use it when every transaction must remain. Reversing the tables and using Left Outer gives the same logic.&lt;/p&gt;

&lt;h4&gt;
  
  
  Full outer join
&lt;/h4&gt;

&lt;p&gt;Keeps all matches and unmatched rows from both tables. The output contains the three valid order matches, &lt;em&gt;C3&lt;/em&gt; with null order fields and &lt;em&gt;O13/C4&lt;/em&gt; with a null name. It is useful for reconciliation, although it may create many nulls.&lt;/p&gt;

&lt;h4&gt;
  
  
  Inner join
&lt;/h4&gt;

&lt;p&gt;Keeps only keys found in both tables: &lt;em&gt;C1–O10&lt;/em&gt;, &lt;em&gt;C1–O11&lt;/em&gt; and &lt;em&gt;C2–O12&lt;/em&gt;. &lt;em&gt;C3&lt;/em&gt; and &lt;em&gt;O13&lt;/em&gt; are removed. Use it when analysis requires valid matches only, but check row counts because unmatched data is silently excluded.&lt;/p&gt;

&lt;h4&gt;
  
  
  Left anti join
&lt;/h4&gt;

&lt;p&gt;Keeps only left-table rows without a right-table match. The result is &lt;strong&gt;C3–Carol&lt;/strong&gt;. It can identify customers who have never purchased, missing submissions or unused master records.&lt;/p&gt;

&lt;h4&gt;
  
  
  Right anti join
&lt;/h4&gt;

&lt;p&gt;Keeps only right-table rows without a left-table match. The result is &lt;strong&gt;O13–C4–200&lt;/strong&gt;. It is valuable for identifying orphan transactions or broken foreign keys.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhfm4zyo1cdj1jsy9tgv1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhfm4zyo1cdj1jsy9tgv1.png" alt=" " width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Power Query merge versus model relationship
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Question&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Power Query merge&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Model relationship&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Stage&lt;/td&gt;
&lt;td&gt;Data preparation before loading&lt;/td&gt;
&lt;td&gt;Model view after loading&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effect&lt;/td&gt;
&lt;td&gt;Physically adds matching data to a query&lt;/td&gt;
&lt;td&gt;Logically connects separate tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Purpose&lt;/td&gt;
&lt;td&gt;Enrichment, consolidation, reconciliation&lt;/td&gt;
&lt;td&gt;Filtering and analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structure&lt;/td&gt;
&lt;td&gt;Can create a wider table&lt;/td&gt;
&lt;td&gt;Preserves facts and dimensions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Suppose FactSales has one million rows and DimProduct contains ProductName and Category. Merging repeats those descriptions across many transactions. A relationship stores each product once and uses ProductID to filter FactSales. This is generally cleaner and more efficient.&lt;/p&gt;

&lt;p&gt;A merge is appropriate when creating a dimension from several sources, adding a small lookup required before loading, consolidating datasets or using anti joins for quality checks. Excessive merging can recreate a wide flat table and weaken the model.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Recommended model
&lt;/h3&gt;

&lt;p&gt;For a typical BI project, I would use a star schema. I would define the grain first, create dimensions with unique keys and keep transactional measures in fact tables. Relationships would normally be one-to-many with single-direction filtering from dimensions to facts.&lt;/p&gt;

&lt;p&gt;I would use inactive relationships for alternative dates, bridge tables for genuine many-to-many scenarios and bidirectional filtering only when the business requirement and path are clear. Power Query merges would prepare data rather than flatten the entire model.&lt;/p&gt;

&lt;p&gt;This design improves compression, query performance, DAX simplicity, readability, scalability and maintenance. Most importantly, every table has a clear responsibility. Dashboards are what users see but a trustworthy model is what makes their answers correct.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>data</category>
      <category>database</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building an Interactive Excel Dashboard for E-commerce Product Analysis: A Case Study of Jumia Products.</title>
      <dc:creator>Jack Kisutsa</dc:creator>
      <pubDate>Sun, 06 Sep 2026 00:10:57 +0000</pubDate>
      <link>https://dev.to/jkisutsa/building-an-interactive-excel-dashboard-for-e-commerce-product-analysis-a-case-study-of-jumia-56ck</link>
      <guid>https://dev.to/jkisutsa/building-an-interactive-excel-dashboard-for-e-commerce-product-analysis-a-case-study-of-jumia-56ck</guid>
      <description>&lt;h2&gt;
  
  
  1. Project Introduction and Objective
&lt;/h2&gt;

&lt;p&gt;In this project, I used Microsoft Excel and Power Query to clean and analyze a Jumia product dataset and then built an interactive dashboard to summarize pricing, discounts, ratings and customer engagement.&lt;/p&gt;

&lt;p&gt;The main objective was to turn a small raw e-commerce dataset into useful business information. I wanted the final dashboard to answer practical questions such as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Do products with higher discounts receive more customer engagement?&lt;/li&gt;
&lt;li&gt;Do higher priced products have better ratings?&lt;/li&gt;
&lt;li&gt;Is there a relationship between product rating and number of
reviews?&lt;/li&gt;
&lt;li&gt;Which products have the highest review engagement?&lt;/li&gt;
&lt;li&gt;Which products may require further investigation because they have high discounts but low ratings?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The project also gave me practical experience in data cleaning, excel formulas, PivotTables, PivotCharts, slicers, correlation analysis and dashboard design.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Dataset and Business Questions
&lt;/h2&gt;

&lt;p&gt;The original dataset contained 115 rows and 6 columns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Product&lt;/li&gt;
&lt;li&gt;Current price&lt;/li&gt;
&lt;li&gt;Old price&lt;/li&gt;
&lt;li&gt;Discount&lt;/li&gt;
&lt;li&gt;Review&lt;/li&gt;
&lt;li&gt;Rating&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The dataset was small but it contained several realistic data quality problems. This made it useful for me to practice the complete analytics process rather than going directly to visualization.&lt;/p&gt;

&lt;p&gt;I structured the workbook into the following sheets:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Raw_Data&lt;/code&gt;&lt;br&gt;
&lt;code&gt;Cleaned_Data&lt;/code&gt;&lt;br&gt;
&lt;code&gt;Analysis&lt;/code&gt;&lt;br&gt;
&lt;code&gt;Pivot_Tables&lt;/code&gt;&lt;br&gt;
&lt;code&gt;Dashboard&lt;/code&gt;&lt;br&gt;
&lt;code&gt;Data_Dictionary&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As we have always been taught in class,I kept the &lt;code&gt;Raw_Data&lt;/code&gt; sheet unchanged so that I always have a copy of the original source data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1mvq33gdq18tdvj1do6a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1mvq33gdq18tdvj1do6a.png" alt=" " width="799" height="326"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Initial Data-Quality Audit
&lt;/h2&gt;

&lt;p&gt;Before cleaning the data, I profiled the dataset in Power Query&lt;br&gt;
using Column Quality, Column Distribution and Column Profile.&lt;/p&gt;

&lt;p&gt;The audit identified several issues:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Data-quality check&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Original rows&lt;/td&gt;
&lt;td&gt;115&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Original columns&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blank Review values&lt;/td&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blank Rating values&lt;/td&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Populated Review values stored as negative numbers&lt;/td&gt;
&lt;td&gt;57&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Current Price ranges&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Old Price ranges&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exact duplicate rows removed&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Discount values outside 0 to 100%&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rating values outside 0 to 5 after cleaning&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One unusual issue was the &lt;strong&gt;Review&lt;/strong&gt; field. All populated review counts were negative, even though a review count cannot logically be negative.&lt;br&gt;
I therefore treated the negative sign as a data collection or scraping issue rather than a genuine business value.&lt;/p&gt;

&lt;p&gt;The Rating field also required cleaning because values were stored as text such as:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;4.5 out of 5&lt;/code&gt;&lt;br&gt;
rather than as numeric ratings.&lt;/p&gt;

&lt;p&gt;This audit was important because it prevented incorrect calculations later in the project.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Cleaning and Preparation Decisions
&lt;/h2&gt;

&lt;p&gt;I performed the main cleaning steps in Power Query.&lt;/p&gt;
&lt;h3&gt;
  
  
  Product names
&lt;/h3&gt;

&lt;p&gt;I applied Trim and Clean to remove unnecessary spaces and non-printable characters. Repeated product names were not automatically deleted because two rows with the same product name are not necessarily duplicates.&lt;/p&gt;
&lt;h3&gt;
  
  
  Current and old prices
&lt;/h3&gt;

&lt;p&gt;The price fields contained KSh and commas so I removed the currency text and converted the fields to numeric values.&lt;/p&gt;

&lt;p&gt;I notieced that two records contained price ranges rather than single prices:&lt;/p&gt;

&lt;p&gt;Current Price: &lt;code&gt;KSh 1,620 - KSh 1,980&lt;/code&gt;&lt;br&gt;
Old Price: &lt;code&gt;KSh 2,200 - KSh 3,200&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I used the midpoint of each range:&lt;br&gt;
Current Price midpoint = &lt;strong&gt;KSh 1,800&lt;/strong&gt;&lt;br&gt;
Old Price midpoint = &lt;strong&gt;KSh 2,700&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This decision was documented in the Data Dictionary.&lt;/p&gt;
&lt;h3&gt;
  
  
  Discount
&lt;/h3&gt;

&lt;p&gt;The Discount field was already interpreted correctly as a percentage.&lt;br&gt;
The cleaned values ranged from &lt;strong&gt;1% to 64%&lt;/strong&gt;, so I did not divide the field by 100 or remove its percentage data type.&lt;/p&gt;
&lt;h3&gt;
  
  
  Reviews
&lt;/h3&gt;

&lt;p&gt;The populated Review values were negative. I used Absolute Value in Power Query to convert them to valid positive counts.&lt;/p&gt;

&lt;p&gt;The missing reviews remained blank. I deliberately did not convert missing reviews to zero because a blank means that the review information is unavailable, while zero would mean that the product definitely had no reviews.&lt;/p&gt;
&lt;h3&gt;
  
  
  Ratings
&lt;/h3&gt;

&lt;p&gt;I renamed &lt;code&gt;Ratingd&lt;/code&gt; to &lt;code&gt;Rating&lt;/code&gt;, removed the text &lt;code&gt;out of 5&lt;/code&gt; and&lt;br&gt;
converted the result to a decimal number.&lt;/p&gt;

&lt;p&gt;The final valid ratings ranged from &lt;strong&gt;2.0&lt;/strong&gt; to &lt;strong&gt;5.0&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Duplicate records
&lt;/h3&gt;

&lt;p&gt;I checked duplicates using the analytical fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product&lt;/li&gt;
&lt;li&gt;Current Price&lt;/li&gt;
&lt;li&gt;Old Price&lt;/li&gt;
&lt;li&gt;Discount&lt;/li&gt;
&lt;li&gt;Review&lt;/li&gt;
&lt;li&gt;Rating&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Three redundant duplicate rows were removed, reducing the dataset from &lt;strong&gt;115 to 112&lt;/strong&gt; products/records.&lt;/p&gt;
&lt;h3&gt;
  
  
  After cleaning:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;112 records remained.&lt;/li&gt;
&lt;li&gt;57 had complete Rating and Review information.&lt;/li&gt;
&lt;li&gt;55 were missing both Rating and Review.&lt;/li&gt;
&lt;li&gt;There were 0 Power Query errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fksohiikjuoq7b1ay6e2u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fksohiikjuoq7b1ay6e2u.png" alt=" " width="800" height="297"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Excel Formulas and Enrichment Fields
&lt;/h2&gt;

&lt;p&gt;After cleaning the source fields in Power Query, I used Excel formulas to create analytical categories and flags. The cleaned Excel table was named &lt;code&gt;tblProducts&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Discount Amount
&lt;/h3&gt;

&lt;p&gt;I calculated the difference between Old Price and Current Price:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=[@[Old Price]]-[@[Current Price]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rating Category
&lt;/h3&gt;

&lt;p&gt;I grouped ratings into three categories while keeping missing values separate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=IF([@Rating]="","Missing",IF([@Rating]&amp;lt;3,"Poor",IF([@Rating]&amp;lt;=4.5,"Average","Excellent")))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The categories were:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Poor: below 3&lt;/li&gt;
&lt;li&gt;Average: 3 to 4.5&lt;/li&gt;
&lt;li&gt;Excellent: above 4.5&lt;/li&gt;
&lt;li&gt;Missing: no rating available&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Discount Category
&lt;/h3&gt;

&lt;p&gt;I grouped discounts into Low, Medium, and High:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=IF([@Discount]="","Missing",IF([@Discount]&amp;lt;20%,"Low Discount",IF([@Discount]&amp;lt;=40%,"Medium Discount","High Discount")))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The thresholds were:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Low Discount: below 20%&lt;/li&gt;
&lt;li&gt;Medium Discount: 20%--40%&lt;/li&gt;
&lt;li&gt;High Discount: above 40%&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Price Category
&lt;/h3&gt;

&lt;p&gt;I calculated the first and third quartiles of Current Price:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=QUARTILE.INC(tblProducts[Current Price],1)
Result: **KSh 493**
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=QUARTILE.INC(tblProducts[Current Price],3)
Result: **KSh 1,669.50**
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then categorized each product:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=IF([@[Current Price]]&amp;lt;=Price_Q1,"Low Price",IF([@[Current Price]]&amp;lt;=Price_Q3,"Medium Price","High Price"))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Engagement Flag
&lt;/h3&gt;

&lt;p&gt;I used the 75th percentile of Review count as the threshold for high engagement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=QUARTILE.INC(tblProducts[Review],3)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result was &lt;strong&gt;14 reviews&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The engagement formula was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=IF([@Review]="","Missing",IF([@Review]&amp;gt;=Review_P75,"High Engagement","Low Engagement"))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Therefore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High Engagement: 14 reviews or more&lt;/li&gt;
&lt;li&gt;Low Engagement: fewer than 14 reviews&lt;/li&gt;
&lt;li&gt;Missing: no Review value&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance Flags
&lt;/h3&gt;

&lt;p&gt;I created three Yes/No flags:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;High Rating + High Engagement&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=IF(AND([@[Rating Category]]="Excellent",[@[Engagement Flag]]="High Engagement"),"Yes","No")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;High Discount + Low Rating&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=IF(AND([@[Discount Category]]="High Discount",[@[Rating Category]]="Poor"),"Yes","No")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;High Discount + High Rating&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=IF(AND([@[Discount Category]]="High Discount",[@[Rating Category]]="Excellent"),"Yes","No")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These fields helped turn individual rows into business segments that could be summarized using PivotTables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn15kg96oi9v6lstlbein.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn15kg96oi9v6lstlbein.png" alt=" " width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. PivotTable and Analysis Workflow
&lt;/h2&gt;

&lt;p&gt;I used both formulas and PivotTables for analysis.&lt;br&gt;
The overall cleaned dataset had:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;112 products&lt;/li&gt;
&lt;li&gt;Average Current Price: KSh 1,186.89&lt;/li&gt;
&lt;li&gt;Average Discount: 37%&lt;/li&gt;
&lt;li&gt;Average Rating: 3.89&lt;/li&gt;
&lt;li&gt;Average Reviews: 13&lt;/li&gt;
&lt;li&gt;Discount analysis&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Discount Analysis
&lt;/h3&gt;

&lt;p&gt;The Discount Category PivotTable produced:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Discount Category&lt;/th&gt;
&lt;th&gt;Products&lt;/th&gt;
&lt;th&gt;Average Discount&lt;/th&gt;
&lt;th&gt;Average Reviews&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Low Discount&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;8%&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Medium Discount&lt;/td&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;td&gt;31%&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High Discount&lt;/td&gt;
&lt;td&gt;62&lt;/td&gt;
&lt;td&gt;48%&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Medium-discount products recorded the highest average review count.&lt;/p&gt;
&lt;h3&gt;
  
  
  Price analysis
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Price Category&lt;/th&gt;
&lt;th&gt;Products&lt;/th&gt;
&lt;th&gt;Avg Current Price&lt;/th&gt;
&lt;th&gt;Avg Discount&lt;/th&gt;
&lt;th&gt;Avg Reviews&lt;/th&gt;
&lt;th&gt;Avg Rating&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Low Price&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;KSh 284.75&lt;/td&gt;
&lt;td&gt;48%&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;3.64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Medium Price&lt;/td&gt;
&lt;td&gt;56&lt;/td&gt;
&lt;td&gt;KSh 1,073.84&lt;/td&gt;
&lt;td&gt;36%&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;3.88&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High Price&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;KSh 2,315.14&lt;/td&gt;
&lt;td&gt;27%&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;4.08&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Low-priced products had the highest average review engagement while high-priced products had the highest average rating.&lt;/p&gt;
&lt;h3&gt;
  
  
  Rating and engagement
&lt;/h3&gt;

&lt;p&gt;Among the 57 products with complete Review and Rating data:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rating Category&lt;/th&gt;
&lt;th&gt;High Engagement&lt;/th&gt;
&lt;th&gt;Low Engagement&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Poor&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;16&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;41&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;57&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I also calculated the Pearson correlation between Rating and Review:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=CORREL(tblProducts[Rating],tblProducts[Review])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result was approximately 0.06, showing almost no linear relationship between rating and review count.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance flags
&lt;/h3&gt;

&lt;p&gt;The analysis identified:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6&lt;/strong&gt; High Rating + High Engagement products&lt;br&gt;
&lt;strong&gt;10&lt;/strong&gt; High Discount + Low Rating products&lt;br&gt;
&lt;strong&gt;8&lt;/strong&gt; High Discount + High Rating products&lt;/p&gt;

&lt;p&gt;Because these are independent flags, the counts should not simply be added together and interpreted as unique products.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi7pztmxgjtko0q7941hv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi7pztmxgjtko0q7941hv.png" alt=" " width="799" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Dashboard Design and Slicer Connections
&lt;/h2&gt;

&lt;p&gt;After completing the analysis, I created a one-page dashboard designed to provide a quick summary of product performance.&lt;/p&gt;

&lt;p&gt;The dashboard included five KPI cards:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Total Products: &lt;strong&gt;112&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Average Price: &lt;strong&gt;KSh 1,186.89&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Average Discount: &lt;strong&gt;37%&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Average Rating: &lt;strong&gt;3.89&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Total Customer Reviews: &lt;strong&gt;723&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I created nine charts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Top 10 Products by Ratings&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Top 10 Products by Review Count&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Top 10 Products by Discount Percentage&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discount vs Reviews&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rating vs Reviews&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Price vs Rating&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rating mix&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Discount mix&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Engagement by Discount&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first two charts summarize category-level patterns. The scatter plotshows the weak relationship between ratings and reviews while the Top 10 chart identifies products with the strongest observed review&lt;br&gt;
engagement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rating vs Review scatter plot
&lt;/h3&gt;

&lt;p&gt;The scatter plot used Rating on the X-axis and Review Count on the Y-axis. I added a linear trendline and displayed the R-squared value.&lt;/p&gt;

&lt;p&gt;The trendline is almost flat and the chart shows &lt;strong&gt;R² ≈ 0.0033&lt;/strong&gt;, which supports the correlation result of approximately 0.06.&lt;/p&gt;

&lt;h3&gt;
  
  
  Top 10 products by review count
&lt;/h3&gt;

&lt;p&gt;A horizontal bar chart was used because product names were long and easier to read in this format.&lt;/p&gt;

&lt;p&gt;Review count was treated as an engagement indicator, not as evidence of sales.&lt;/p&gt;

&lt;h3&gt;
  
  
  Slicers
&lt;/h3&gt;

&lt;p&gt;I added three slicers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rating Category&lt;/li&gt;
&lt;li&gt;Price Category&lt;/li&gt;
&lt;li&gt;Discount Category&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The slicers were connected to compatible PivotTables through &lt;strong&gt;Report Connections/PivotTable Connections&lt;/strong&gt;. This allows a user to select a category and dynamically explore different product segments.&lt;/p&gt;

&lt;p&gt;The scatter plot was created as a normal Excel XY chart rather than a PivotChart, so it remains an overall view and is not controlled directly by the PivotTable slicers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final dashboard
&lt;/h3&gt;

&lt;p&gt;The dashboard was arranged so that the viewer sees the KPIs first, followed by the charts, slicers and business insights.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fca1qmmufka475kbg078t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fca1qmmufka475kbg078t.png" alt=" " width="643" height="729"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Key Findings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Finding 1: Medium discounts recorded the strongest average review engagement
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Evidence:&lt;/strong&gt; Medium-discount products averaged &lt;strong&gt;15 reviews&lt;/strong&gt;, compared with &lt;strong&gt;11&lt;/strong&gt; for high-discount products and &lt;strong&gt;10&lt;/strong&gt; for low-discount products.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meaning:&lt;/strong&gt; The deepest discounts were not associated with the highest review engagement in this dataset.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action:&lt;/strong&gt; Sellers could test medium and high discount bands and&lt;br&gt;
compare engagement before assuming that deeper discounting produces better product response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caveat:&lt;/strong&gt; Review count is an engagement proxy. The dataset does not include sales or listing age, so this relationship should not be interpreted as causal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding 2: Average rating increased across price categories
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Evidence:&lt;/strong&gt; Average rating increased from &lt;strong&gt;3.64&lt;/strong&gt; for low-priced products, to &lt;strong&gt;3.88&lt;/strong&gt; for medium-priced products and &lt;strong&gt;4.08&lt;/strong&gt; for high-priced products.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meaning:&lt;/strong&gt; Higher-priced products in this dataset tended to have stronger average customer ratings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action:&lt;/strong&gt; Sellers could investigate the product quality, features,brands or listing characteristics associated with highly rated high-priced products.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caveat:&lt;/strong&gt; This is an association. The analysis does not prove that increasing a product's price would improve its rating.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding 3: Rating and review engagement had almost no linear relationship
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Evidence:&lt;/strong&gt; The correlation between Rating and Review was&lt;br&gt;
approximately &lt;strong&gt;r = 0.06&lt;/strong&gt;, with &lt;strong&gt;R² ≈ 0.0033&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meaning:&lt;/strong&gt; A highly rated product does not necessarily receive a high number of reviews.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action:&lt;/strong&gt; Sellers should consider both rating quality and review engagement when evaluating product performance rather than relying on rating alone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caveat:&lt;/strong&gt; Only 57 products had complete Rating and Review&lt;br&gt;
information and review count is not a direct measure of sales.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding 4: Performance flags identified both opportunities and risks
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Evidence:&lt;/strong&gt; The analysis found &lt;strong&gt;6 high-rating/high-engagement products&lt;/strong&gt;, &lt;strong&gt;10 high-discount/low-rating products&lt;/strong&gt; and &lt;strong&gt;8 high-discount/high-rating products&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meaning:&lt;/strong&gt; Some products appear to be strong engagement candidates, while others may require investigation because heavy discounting is occurring alongside weak ratings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action:&lt;/strong&gt; Sellers could investigate the 6 high-rating/high-engagement products for promotional opportunities and review quality or listing issues among the 10 high-discount/low-rating products before increasing promotional support.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caveat:&lt;/strong&gt; These flags are screening tools. The dataset does not&lt;br&gt;
contain revenue, units sold, margin, advertising spend, or profitability data, so the flagged products cannot automatically be classified as commercial winners or failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Business Recommendations
&lt;/h2&gt;

&lt;p&gt;Based on the completed analysis, I would recommend the following&lt;br&gt;
actions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test discount bands rather than automatically increasing discounts&lt;/strong&gt;. Medium-discount products had higher average review engagement than high-discount products.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Investigate highly rated, higher-priced products&lt;/strong&gt;. Understanding what these products have in common may provide useful lessons for product positioning and listing quality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use rating and engagement together&lt;/strong&gt;. The weak correlation shows that a high rating alone does not guarantee strong review activity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review high-discount/low-rating products&lt;/strong&gt;. These products may require investigation into quality, expectations, listing content, or promotional strategy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Study high-rating/high-engagement products&lt;/strong&gt;. These products can be investigated further as possible candidates for targeted visibility or promotional tests.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These recommendations are deliberately limited to what the dataset supports.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Limitations and Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Limitations&lt;br&gt;
The dataset had substantial missing feedback data. Out of 112 cleaned records, only &lt;strong&gt;57&lt;/strong&gt; had both Rating and Review information. This reduces the amount of data available for analyses involving customer feedback.&lt;/p&gt;

&lt;p&gt;The project is also observational. Patterns such as higher ratings among high-priced products or stronger engagement among medium-discount products show &lt;strong&gt;association, not causation&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons learned
&lt;/h2&gt;

&lt;p&gt;This project reinforced several lessons for me.&lt;/p&gt;

&lt;p&gt;First, &lt;strong&gt;data cleaning is part of analysis, not a separate optional step&lt;/strong&gt;. If I had used the original negative Review values or text-based ratings directly, the final analysis would have been  misleading.&lt;/p&gt;

&lt;p&gt;Second, I learned the importance of documenting assumptions. Converting price ranges to midpoints, using 14 reviews as the high-engagement threshold and defining category boundaries all affect the results and therefore need to be transparent.&lt;/p&gt;

&lt;p&gt;Third, you can use PivotTables to summarize data and also create an interactive dashboard. I also found out that PivotCharts rely on the PivotTables they get their data from. This means you can create separate PivotTables from the same data source for your charts. That way, a chart on your dashboard won't accidentally update a detailed analytical PivotTable.&lt;/p&gt;

&lt;p&gt;Fourth, a dashboard should not contain every calculation. The Analysis and Pivot_Tables sheets can hold detailed work while the Dashboard should focus on the KPIs, charts, filters and insights that help a user understand the data quickly.&lt;/p&gt;

&lt;p&gt;Finally, I learned to be careful with business language. A correlation does not prove causation, reviews do not equal sales and a flagged product is a starting point for investigation rather than a final business conclusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This project took a Jumia product dataset through the complete Excel analytics workflow: &lt;strong&gt;audit, cleaning, enrichment, analysis,visualization and business interpretation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Starting with 115 raw records, I used Power Query to resolve price formatting, rating text, negative review values, missing information and duplicate records. The final cleaned dataset contained 112 records. I then used Excel formulas to create price, discount, rating, engagement and performance categories before summarizing the results through PivotTables and charts.&lt;/p&gt;

&lt;p&gt;The final interactive dashboard provides a concise view of product pricing, discounts, ratings and customer engagement while still recognizing the limitations of the available data.&lt;/p&gt;

&lt;p&gt;For me, the main lesson was that building a useful dashboard is not only about creating attractive charts. The quality of the final dashboard depends on the decisions made before visualization: &lt;strong&gt;how the data is cleaned, how metrics are defined, how assumptions are documented and how carefully the findings are interpreted&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>data</category>
      <category>productivity</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Getting Started With Excel For Data Analytics: From Basics To Data Cleaning</title>
      <dc:creator>Jack Kisutsa</dc:creator>
      <pubDate>Sun, 30 Aug 2026 15:30:38 +0000</pubDate>
      <link>https://dev.to/jkisutsa/getting-started-with-excel-for-data-analytics-from-basics-to-data-cleaning-161</link>
      <guid>https://dev.to/jkisutsa/getting-started-with-excel-for-data-analytics-from-basics-to-data-cleaning-161</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Excel is a pretty straightforward tool for anyone starting out with data. You can input, organize, calculate and look at information without needing to write any code. However, just having data in Excel doesn't automatically make it usable. Real-world datasets often have gaps, repeated information, spelling mistakes, and other small issues that need fixing before you can really use the data.&lt;/p&gt;

&lt;p&gt;For this task, I worked with a file named &lt;strong&gt;HR_Dataset_Dirty.xlsx.&lt;/strong&gt; It had 876 employee records with details like Employee ID, Name, Department, Salary, Hire Date, Age, Gender and Performance Score. This article explains the basic Excel features I learned and how I used them to clean up this data, making it ready for analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Basics
&lt;/h2&gt;

&lt;p&gt;A file in Excel is called a &lt;strong&gt;workbook&lt;/strong&gt; and each sheet within it is a &lt;strong&gt;worksheet&lt;/strong&gt;. A worksheet is made up of rows, numbered down the side, and columns, labeled across the top. The point where a row and column intersect is a &lt;strong&gt;cell&lt;/strong&gt;; for instance, cell A2 might contain an employee's ID number.&lt;/p&gt;

&lt;p&gt;Usually, the very first row of a dataset lists the column titles, explaining what kind of information is in each column below. In my dataset, some columns contained numbers (like Salary and Age), some had dates (like Hire Date), and others had text categories (like Department and Gender). Excel handles these types differently. You can perform calculations like sums or averages on numbers, but if a number is accidentally saved as text, those calculations won't work correctly.&lt;/p&gt;

&lt;p&gt;Before I made any changes, my first step was to look over the raw data so I could get a sense of what I was dealing with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkw1sepa08a0enkf65jwk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkw1sepa08a0enkf65jwk.png" alt=" " width="799" height="267"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A section of the raw dataset, with blank cells highlighted in red and duplicate Employee IDs highlighted in yellow.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Spotting the Problems
&lt;/h2&gt;

&lt;p&gt;Looking through the raw data, I noticed a few problems right away:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some cells were &lt;strong&gt;blank&lt;/strong&gt; — missing an age, a salary or a gender.&lt;/li&gt;
&lt;li&gt;Some Employee IDs appeared &lt;strong&gt;more than once&lt;/strong&gt; meaning the same employee had been entered twice.&lt;/li&gt;
&lt;li&gt;The same category was &lt;strong&gt;spelled differently&lt;/strong&gt; in different rows — for example, "HR," "Hr," and "Human Resources" all meant the same department, but Excel would treat them as three separate values.&lt;/li&gt;
&lt;li&gt;Some &lt;strong&gt;dates didn't make sense&lt;/strong&gt;, like a hire date of &lt;code&gt;2020/13/05&lt;/code&gt; — there's no 13th month.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used Excel's &lt;strong&gt;Conditional Formatting&lt;/strong&gt; (Home tab → Conditional Formatting) to highlight these problems directly on the sheet, so I could see exactly which cells needed attention before touching anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Missing Values
&lt;/h2&gt;

&lt;p&gt;Instead of guessing what a missing value should be, I used Excel's &lt;strong&gt;Filter&lt;/strong&gt; feature to check each column for blanks. For example, filtering the Salary column showed me exactly which employees had no salary recorded.&lt;/p&gt;

&lt;p&gt;We were told in class that it's important not to just replace every blank with a zero — a missing salary doesn't mean the employee earns nothing, it just means the information wasn't captured. I left genuinely missing values as blank rather than guessing a number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Removing Duplicates
&lt;/h2&gt;

&lt;p&gt;I looked for employee IDs that showed up more than once because each ID should be unique. Some, like &lt;code&gt;10407&lt;/code&gt; and &lt;code&gt;10870&lt;/code&gt; appeared twice and had the same details.&lt;/p&gt;

&lt;p&gt;Once I made sure these were actual duplicates and not two different employees with similar IDs, I used Excel's &lt;strong&gt;Remove Duplicates&lt;/strong&gt; feature from the Data tab to get rid of them. This process removed 23 duplicate rows from the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleaning Text
&lt;/h2&gt;

&lt;p&gt;When i cheched the data some text entries had too many spaces or the capitalization was all over the place; for instance, some names were just in lowercase. Excel has a couple of easy functions for fixing this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TRIM()&lt;/code&gt; removes extra spaces from text.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PROPER()&lt;/code&gt; changes text to proper case, so "grace karanja" becomes "Grace Karanja."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used these on the Name and Location columns to get everything looking the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Standardizing Categories
&lt;/h2&gt;

&lt;p&gt;I also noticed that the Department column had the same problem. There were several spellings for the same department like "Markting" and "Marketing," or "HR" and "Human Resources." Since these functions don't fix spelling on their own, I mapped each incorrect spelling to its correct version, so that every version of a department name became one standard value.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvrpuf7bh33tqbriqydrc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvrpuf7bh33tqbriqydrc.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Cleaning formulas correcting names, departments, and other text fields, with anything still needing a manual check flagged in red.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing Dates and Numbers
&lt;/h2&gt;

&lt;p&gt;I also had to check the Hire Date field too. The goal was to make sure each entry was a valid date and to flag any that weren't, like the incorrect &lt;code&gt;2020/13/05&lt;/code&gt; instead of trying to guess what they should have been.&lt;/p&gt;

&lt;p&gt;The Age field had a similar problem. One entry was the word "thirty" instead of the number 30. Since Excel can't use text in calculations, I flagged entries like this so they could be reviewed, rather than letting them cause errors in later calculations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing the Clean Dataset
&lt;/h2&gt;

&lt;p&gt;Once every column had been checked and corrected, I copied the results into a new sheet as plain values, so the final table didn't depend on the raw data anymore. I also added a dropdown list to the Department column (Data tab → Data Validation) so that only the correct department names can be entered from now on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpmkzkjfu1mfclcibe6ai.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpmkzkjfu1mfclcibe6ai.png" alt=" " width="800" height="360"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The final cleaned dataset — 852 unique employees remain after removing duplicates, with a Department dropdown to keep future entries consistent.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Quick Summary
&lt;/h2&gt;

&lt;p&gt;With the data cleaned, I used &lt;code&gt;COUNTIF()&lt;/code&gt; and &lt;code&gt;AVERAGEIF()&lt;/code&gt; to summarise employees by department and built a simple chart from that summary to see the results at a glance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7qxlxx4jtqttq6xrhxrt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7qxlxx4jtqttq6xrhxrt.png" alt=" " width="800" height="242"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A quick summary of headcount and average salary by department, built from the cleaned data.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I learned from this exercise that before you even get to charts or dashboards, you first need to make sure the data itself is reliable. Things like missing information, duplicate entries, different spellings for the same thing or incorrect dates can all mess up your analysis if you don't find and fix them early on.&lt;/p&gt;

&lt;p&gt;Turning a messy raw dataset into a clean and consistent one really showed me how crucial this initial step is. It also showed me that some basic tools in Excel like Conditional Formatting, Filter, Remove Duplicates, TRIM, PROPER and Data Validation are all you really need to do it.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>My First GitHub Project: From a Local Folder to GitHub Using Git and SSH</title>
      <dc:creator>Jack Kisutsa</dc:creator>
      <pubDate>Sun, 23 Aug 2026 09:41:03 +0000</pubDate>
      <link>https://dev.to/jkisutsa/my-first-github-project-from-a-local-folder-to-github-using-git-and-ssh-27nh</link>
      <guid>https://dev.to/jkisutsa/my-first-github-project-from-a-local-folder-to-github-using-git-and-ssh-27nh</guid>
      <description>&lt;h2&gt;
  
  
  My First GitHub Project: From a Local Folder to GitHub Using Git and SSH
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This week I learned how to use &lt;strong&gt;Git, Git Bash, GitHub and setting up SSH Keys&lt;/strong&gt;. Before this, I knew about GitHub but I did not really understand how a project moves from a folder on my computer to GitHub.&lt;/p&gt;

&lt;p&gt;A simple way I now understand the relationship is:&lt;/p&gt;

&lt;p&gt;Git manages the history of my project while GitHub provides an online home for the project.&lt;/p&gt;

&lt;p&gt;In this article, I will explain the steps I followed to create a simple project locally and push it to GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating My Project
&lt;/h2&gt;

&lt;p&gt;I started by creating a folder for my project using Git Bash.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;Kenya-Hospital-Records-Analysis
&lt;span class="nb"&gt;cd &lt;/span&gt;Kenya-Hospital-Records-Analysis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the folder, I created a &lt;code&gt;README.md&lt;/code&gt; file and a folder called &lt;code&gt;data&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My project looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Kenya-Hospital-Records-Analysis/
├── README.md
└── data/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;README.md&lt;/code&gt; file is where I can explain what my project is about while the &lt;code&gt;data&lt;/code&gt; folder can be used to store datasets.&lt;/p&gt;

&lt;p&gt;In the data folder i uploaded an excel file called &lt;code&gt;Kenyan_Hospital_Health_Records&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Git
&lt;/h2&gt;

&lt;p&gt;The next step was to make Git start tracking my project. I did this using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helped me see which files Git was tracking and which files had not yet been added.&lt;/p&gt;

&lt;p&gt;To add my files, I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then saved the changes to Git using a commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial commit"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing I learned is that a commit is like saving a checkpoint of my project. The commit message helps explain what changes I made.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting Git to GitHub while generating SSH Keys
&lt;/h2&gt;

&lt;p&gt;To push my local project to GitHub, I needed a secure way for my computer to communicate with my GitHub account.&lt;/p&gt;

&lt;p&gt;I used SSH (Secure Shell).&lt;/p&gt;

&lt;p&gt;I first generated an SSH key on my computer and then added the public key to my GitHub account.&lt;/p&gt;

&lt;p&gt;An SSH key normally consists of two parts:&lt;/p&gt;

&lt;p&gt;Private key – stays securely on my computer.&lt;br&gt;
Public key – can be added to GitHub.&lt;/p&gt;

&lt;p&gt;One important lesson was that the private key should never be shared.&lt;/p&gt;

&lt;p&gt;After configuring my SSH key, I tested the connection using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-T&lt;/span&gt; git@github.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful authentication confirmed that my computer could communicate securely with GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting to GitHub Using SSH Keys
&lt;/h2&gt;

&lt;p&gt;I also learned how to connect Git on my computer to my GitHub account using an &lt;strong&gt;SSH key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After creating the SSH key, I added the public key to my GitHub account. I could then test whether the connection was working using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-T&lt;/span&gt; git@github.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was interesting because I learned that SSH allows my computer to communicate securely with GitHub without entering my GitHub password every time I push my work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pushing My Project to GitHub
&lt;/h2&gt;

&lt;p&gt;After creating a repository on GitHub, I connected my local project to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote add origin git@github.com:username/Kenya-Hospital-Records-Analysis.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then pushed my project to GitHub using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After refreshing my GitHub repository, I could see the files I had created on my computer now appearing on GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;The biggest thing I learned from this exercise is that &lt;strong&gt;Git and GitHub are different but work together&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Git helps me track changes to my project on my computer, while GitHub allows me to store and share the project online.&lt;/p&gt;

&lt;p&gt;I also now understand the basic Git workflow better:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create or change files
        ↓
git add
        ↓
git commit
        ↓
git push
        ↓
GitHub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first, the commands looked confusing but after using them practically, I started to understand what each command was doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This was my first time taking a project from a local folder on my computer and pushing it to GitHub using Git and SSH.&lt;/p&gt;

&lt;p&gt;I am still learning Git and GitHub but I can now create a project, track my files, make commits and push my work to GitHub.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>git</category>
      <category>github</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
