<?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>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>
