Explore Power BI Core Visualizations: Part 3 – Pie, Donut, and Treemap


Pie, Donut, and Treemap charts can be helpful tools when we need to show how different parts contribute to a whole. Pie charts represent data as slices of a circle and show the relationship of parts to a whole. Donut charts are similar to pie charts. However, their center is blank, providing space to add a label or other icon. Treemap charts use nested rectangles to visualize each level of hierarchy in our data.

These charts can be helpful for specific visualizations, but it is important to recognize their limitations. As with all of our data visualization tasks, choosing the right type of visual is essential. To explore bar and column charts, visit Part #1 of this series, and to learn more about line and area charts, check out Part #2.

Chart your data journey! Transform data into insights with Power BI core visualizations. 

Chart your data journey! Transform data into insights with Power BI core visualizations. 


Customizing Pie Charts

Customizing our pie charts enhances their clarity, making it easier for our report viewers to interpret the data.

Adjusting Colors

The color choices used in our pie charts play a crucial role in their readability. Within the Slices property of our Pie chart, we can customize the colors used for each slice. When selecting colors, we should ensure they are visually distinct and that the colors used for our data categories are consistent across all visuals on the page and within the report.

We set the color of our slices in the Format pane, under the slices section. Here, we will find each data category included in the chart, and we can explicitly see the color.

Formatting Data Labels

The data labels on our pie charts help convey important insights to our viewers. Within the Data label properties of our pie chart we find several options to edit and format our data labels. We have options to set the position of our data labels (e.g. outside, prefer inside), what information to display such as category name, values, or percent of total, and under the Value grouping, we can format the text of the data labels.

Using a combination of these properties, we can make our charts more informative at a glance. For example, we can improve the above plot by displaying the category name and percent of total directly on our chart.

It is important to be cautious when using data labels. We want to provide essential information without overcrowding the chart with too much text. Adjusting the font size, background, font color, and label position can help us maintain a clean and organized look.

Using Legends Effectively

Our data labels display information directly on our charts, while legends offer a clean way to display what data category corresponds to which chart slice. Within the Legend properties we can set the position of the legend, set the title, and format the text and title of the legend.

We can update the Pie chart above to display the legend and then update the data label to display the total sales value and the percent of total value.

When using pie charts, it is important to limit the number of categories to ensure clarity. Pie charts are most effective when displaying data with no more than a few slices. If this cannot be accomplished, it’s best to consider other visuals to provide our insights.


Customizing Donut Charts

Donut charts provide an alternative to pie charts, with the added benefit of a central blank space. This space can be used for a variety of purposes or left blank. By customizing our donut charts, we can enhance their effectiveness and make them more informative and engaging.

Like pie charts, donut charts allow us to set the color of each chart slice, use data labels to provide additional information and position the legend to provide additional context.

Donut charts also offer additional properties we can customize to enhance these charts even further.

Modifying the Inner Radius

One of the main differences between our pie and donut charts is the open area in the center. Within the Slices property grouping, we will find a Spacing option. Here, we can adjust the inner radius, controlling how wide or narrow the chart’s ring appears.

Increasing this value creates a more pronounced donut shape, which can help draw viewers’ attention to the center of the chart. Decreasing this value makes the chart look more like a traditional pie chart.

Donut charts can be helpful when we need to compare proportions while also displaying an aggregate metric that we can place in the open center of the visual. However, like pie charts it is important to only use these charts when there is a small and manageable number of segments. When there are too many segments, donut and pie charts become cluttered and difficult to interpret.

When using these comparison charts, we should aim for simplicity and clarity.


Customizing Treemap Charts

Treemap charts are useful when visualizing hierarchical data. They allow us to display a large amount of information in a compact space. Each category in the visual is represented by a rectangle, the size of which reflects the category’s value.

Treemap charts offer various properties that we can customize to enhance their clarity, ensuring they effectively convey complex data structures and insights.

Adjusting the Colors

In our treemap visuals, colors distinguish between our different categories. Like in our other charts, we can set the color for each category, and it is important that this color selection is consistent across all visuals within our report. For example, we can create a treemap of our Total Sales by product category and product code and set the colors for each category to ensure visual consistency.

Setting Labels and Viewing Tooltips

Like many of our other Power BI visuals, Treemap visuals allow us to customize visual labels and tooltip properties to improve the readability of our visuals. Labels on our visuals provide context by displaying category names and values directly within each rectangle. By using the options available under the data labels and categories labels we can adjust the font size, color, display units, and the number of decimal places to improve the clarity of our visual.

We will use these properties to improve the treemap above by increasing the font size and weight of the category labels so they are easily identifiable. We will also turn on data labels to provide our viewers with precise information for the largest contributors.

Tooltips are another feature common across our Power BI visuals that adds an interactive element to our reports and can provide our viewers with additional information. When a user hovers over a rectangle, a tooltip appears, providing them with information about that category. Additionally, the information included within the tooltip can be customized to provide further context, such as percentages or other metrics.

In treemaps, some rectangles may be too small to display the data label, and tooltips become a valuable asset for delivering details without cluttering the visual.

Using Drill-Down Features

One strength of treemaps is their ability to represent hierarchical data. Power BI’s drill-down functionality lets us explore these hierarchies in detail. A user can use the drill-down feature to drill into the next level of the hierarchy, focusing on the subcategories or finer details of the data.

Drill-down features provide our viewers with another interactive element, allowing them to explore the dataset at multiple levels without overwhelming them with too much information upfront.

In our example treemap above, the first-level category is Product, and the second is Region. This allows our viewers to drill into a product category to view the distribution of product sales across sales regions.

Treemap charts can be a helpful tool for comparing proportions within hierarchical data. However, they are most effective when the differences in value between categories are significant. When all categories are similar in size, the visual can be difficult to interpret, so it is important to evaluate whether a treemap is appropriate for each use case.


Advanced Techniques and Customizations

Current Year Sales by Product Selection

The Current Year Sales donut chart helps visualize how a selected product contributes to the total current year sales.

To build this visual, we first, create two measures: Total Sales CY and Remaining CY Sales.

The Total Sales CY measure calculates the current year’s total sales, and the Remaining CY Sales calculates the difference between the total sales of all products and the selected product’s sales.

Total Sales CY = TOTALYTD([Total Sales], DateTable[Date])
Remaining CY Sales = 
VAR _allSales = CALCULATE([Total Sales CY], ALL(Sales))

RETURN
_allSales - [Total Sales CY]

We then add these two measures to our donut chart’s Values property and set the color of the Total Sales CY to a dark blue and Remaining CY Sales to a light gray.

We then turn off the legend, and update the data labels to show only the percent of total value.

Next, we create a new measure that will be used for the visual subtitle. The subtitle provides additional context by showing how close the product’s sales are to hitting a sales target, and how this year’s sales compare to last year’s sales for the same period of time.

Product Sales Subtitle = 
VAR _lyYTDSales = 
   TOTALYTD(
      [Total Sales],
      DATESBETWEEN(
         DateTable[Date], 
         DATE(MAX(DateTable[Year])-1, 1, 1), 
         DATE(YEAR(TODAY())-1, MONTH(TODAY()), DAY(TODAY()))
      )
   )
VAR _compare = 
   [Total Sales CY] - _lyYTDSales
VAR _percentDiff =
    _compare/((_lyYTDSales+[Total Sales CY])/2)

RETURN
FORMAT(
   [CY Sales Percent of Target], "0.0%") 
   & " of Sales Target | " 
   & IF(_compare<0, "▼", "▲") 
   & FORMAT(ABS(_percentDiff), "0.0%") & " compared to previous"

Lastly, we use the empty center of the donut chart to provide additional details to our viewers. Here, we display the total sales value for the selected product and add a dynamic label to clearly show what the sales value represents.

To do this we create another measure to dynamically update based on user selections. The Selected Product measure shows “All Products” when there is no product selection; otherwise, it displays the selected product name, or when there are multiple selections, it displays a list of all the selected products.

Selected Product = 
IF (
    ISFILTERED ( Products[Product] ),
    IF (
        HASONEVALUE ( Products[Product] ),
        VALUES ( Products[Product] ),
        CONCATENATEX ( 
            ALLSELECTED ( Products[Product] ), 
            Products[Product], 
            ", "
        ) & " Sales"
    ),
    "All Products"
)

Current Year Regional Sales

The Current Year Regional Sales donut chart compares sales across different sales regions.

We start building this chart with a series of measures. First, a set of measures calculating the current year sales for each region.

Asia Sales CY = CALCULATE([Total Sales CY], Regions[Region]="Asia")
Europe Sales CY = CALCULATE([Total Sales CY], Regions[Region]="Europe")
US Sales CY = CALCULATE([Total Sales CY], Regions[Region]="United States")

Then, we add another set of measures calculating the difference between the total sales and the region-specific sales.

Asia Sales CY Diff = [Total Sales CY] - [Asia Sales CY]
Europe Sales CY Diff = [Total Sales CY] - [Europe Sales CY]
US Sales CY Diff = [Total Sales CY] - [US Sales CY]

Similar to the visual above, we add the region-specific measures to a donut chart and format them to appear as concentric donut charts. This is done by turning off the border and background of the two inner donut charts and resizing them.

Next, we build a customized legend element that not only shows the total sales for each region but also the percentage that each region contributes to the overall sales. The percentage of overall sales is calculated using 3 new measures.

Asia CY % Sales = DIVIDE([Asia Sales CY], [Total Sales CY])
Europe CY % Sales = DIVIDE([Europe Sales CY], [Total Sales CY])
US CY % Sales = DIVIDE([US Sales CY], [Total Sales CY])

This visual is also dynamic based on the user interaction and selections made on the product slicer.

This visual provides our viewers with a quick comparison of regional sales. It clearly shows the sales distribution across our regions, allowing our viewers to identify which regions have the highest or lowest sales.


Wrapping Up

In this part of the series, we explored Pie, Donut, and Treemap charts and how to effectively use and customize these visuals in our Power BI reports. Pie and Donut charts can be helpful tools when displaying proportions but should be used selectively when comparing only a few categories. Treemap charts excel at displaying hierarchical data and provide us with a compact and insightful way to visualize this data.

While these visuals can prove to be just the right fit for a given requirement, they must only be used in the right scenarios, with an emphasis on simplicity and clarity.

In the next part of the series, we will explore Power BI’s Gauge, Card, and KPI visuals. These visuals help us display metrics and provide at-a-glance insights into key data points.


Thank you for reading! Stay curious, and until next time, happy learning.

And, remember, as Albert Einstein once said, “Anyone who has never made a mistake has never tried anything new.” So, don’t be afraid of making mistakes, practice makes perfect. Continuously experiment, explore, and challenge yourself with real-world scenarios.

If this sparked your curiosity, keep that spark alive and check back frequently. Better yet, be sure not to miss a post by subscribing! With each new post comes an opportunity to learn something new.

Power Query Functions: Building Flexible and Reusable Data Cleaning Routines


When extracting and transforming data in Power BI, we often repeat the same steps across different projects. This repetition raises the question: can these steps be generalized and reusable in future projects?

This post will explore a solution that breaks down these steps into flexible and reusable parameters and functions, eliminating the need to recreate these common steps across our projects. The solution leverages parameters, functions, and queries to standardize various parts of our data preparation process when CSV files in a SharePoint document library are the data source.

Don’t just read; get hands-on and view all the details in the template report available here:

This repository provides a Power BI template and sample CSV files for automating and standardizing CSV data transformation from SharePoint using Power Query functions.


Power Query Parameters

Parameters help keep our data preparation process as dynamic as possible. They provide a level of flexibility to our Power Query functions and allow us to modify their behavior without altering the underlying function definition. This flexibility enables this solution to adapt to different requirements and increases the reusability of our work.

In our solution, 7 parameters manage how data is loaded, cleaned, and transformed.

SharePoint Site Url (Text): specifies the SharePoint site URL where the CSV files are stored.

https://yourcompany.sharepoint.com/sites/yoursite

SharePoint Folder Path (Text): defines the folder path within the SharePoint site where the CSV files are located.

https://yourcompany.sharepoint.com/sites/yoursite/yourfolder/

textColumns (Text): a comma-separated list of column names that require text transformations.

Column Name 1, Column Name 2, Column Name 3

textFormatCase (Text): a text value that specifies how text columns should be formatted. Options include Proper, Lower, and Upper case.

columnDataTypes (Text): a semicolon-separated list of column names paired with their corresponding data type description, used to dynamically set the data types of each column.

Column Name 1,date;
Column Name 2,whole number;
Column Name 3,text;
Column Name 4,text;
Column Name 5,text;
Column Name 6,whole number;
Column Name 7,decimal number

errorHandlingOption (Text): sets how errors are handled within the dataset and provides the options of RemoveErrors, ReplaceErrors, or None.

viewErrorSummary (TRUE/FALSE): toggles between displaying a summary of rows that contain errors and returning the cleaned dataset.

Once we create our parameters and set their values, we can reference them throughout our functions and queries. This enables us to adjust the entire data transformation process by updating the parameter values.


Supporting Utility Functions

To enhance the reusability of our Power Query solution, we first break down complex data transformations into smaller, modular functions. Each utility function handles a specific task, improving its maintainability and allowing us to apply them individually or collectively across different datasets.

fxParameterToList

(parameterName as text) as list => 
   List.Transform(Text.Split(parameterName, ","), each Text.Trim(_))

Many of our parameters in this solution are comma-separated strings that we must convert to list objects before using them in a list-based transformation within Power Query. Since many of our parameters and functions require this, creating a function allows us to repeat this operation easily.

The function has a single input parameter (parameterName), a comma-separated string of values. It then splits this input by commas, trims surrounding spaces, and converts the result into a list. This is used when calling and passing parameter values to other utility functions such as fxTransformTextFormat and fxTransformTextTrim.

fxTransformTextTrim

The fxTransformTextTrim function trims any leading and trailing spaces from text values in the specified columns. Trimming can be essential when cleaning user-entered data or other text extracted from external sources.

(inputTable as table, columnsToTrim as list) as table =>
    Table.TransformColumns(inputTable, List.Transform(columnsToTrim, each {_, Text.Trim, type text}))

This function has two input parameters:

  • inputTable: a table containing the data to be trimmed
  • columnsToTrim: a list of text columns that require trimming

The fxTransformTextTrim function applies Text.Trim to each column specified in columnsToTrim, ensuring our text data is clean and consistent.

fxTransformTextFormat

The fxTransformTextFormat function standardizes the case formatting of our text data depending on the caseType input parameter. Depending on our requirements, the function can convert text data to Proper Case, Lower Case, or Upper Case.

(inputTable as table, columnsToTransform as list, caseType as text) as table =>
    let
        // Determine the transformation function based on caseType input
        CaseFunction = if caseType = "Proper" then Text.Proper
                       else if caseType = "Lower" then Text.Lower
                       else if caseType = "Upper" then Text.Upper
                       else error "Unsupported case type: Use 'Proper', 'Lower', or 'Upper'",

        // Apply the selected case transformation to each column in the list
        TransformTextCase = Table.TransformColumns(inputTable, List.Transform(columnsToTransform, each {_, CaseFunction, type text}))
    in
        TransformTextCase

This function has three input parameters:

  • inputTable: a table containing the data to be transformed
  • columnsToTransform: a list of columns that require text formatting
  • caseType: a text value specifying the desired text format (Proper, Lower, or Upper)

Using the caseType parameter, the function identifies the transformation type and then applies the transformation to the specified columns.

fxTransformRemoveDuplicates

The fxTransformRemoveDuplicates is a straightforward function that removes duplicated rows within our dataset.

(inputTable as table) as table =>
Table.Distinct(inputTable)

This function has a single parameter:

  • inputTable: a data table to remove duplicate rows from

The function uses Table.Distinct to identify and remove duplicated rows and returns a table with each row being unique.

fxTransformSetDataTypes

The fxTransformSetDataTypes function maps a textual description of the data type to the Power Query data type. The function allows dynamically setting column data types based on our columnDataTypes Power Query parameter.

(columnType as text) as type =>
    let
        lowerColumnType = Text.Lower(columnType),
        columnTypeResult = 
            if lowerColumnType = "decimal number" then type number
            else if lowerColumnType = "fixed decimal number" then type number
            else if lowerColumnType = "whole number" then Int64.Type
            else if lowerColumnType = "percentage" then type number
            else if lowerColumnType = "date/time" then type datetime
            else if lowerColumnType = "date" then type date
            else if lowerColumnType = "time" then type time
            else if lowerColumnType = "date/time/timezone" then type datetimezone
            else if lowerColumnType = "duration" then type duration
            else if lowerColumnType = "text" then type text
            else if lowerColumnType = "true/false" then type logical
            else if lowerColumnType = "binary" then type binary
            else type text // Default to text if the type is unrecognized
    in
        columnTypeResult

The function has a single input parameter:

  • columnType: a text value of the data type to set for a column

This function helps us leverage common textual descriptions to set Power Query data types across our dataset columns.

fxErrorHandling

The fxErrorHandling function allows us to manage how our dataset handles errors based on our requirements. We can use the function’s input parameter to remove rows containing errors, replace error values with null, or retain the errors within the dataset.

(inputTable as table, errorHandlingOption as text) as table =>
let
    ErrorRows = Table.SelectRowsWithErrors(inputTable),
    
    // Determine the action based on the error handling option provided
    ErrorHandlingResult = 
    if errorHandlingOption = "RemoveErrors" then
        // Remove rows with any errors
        Table.RemoveRowsWithErrors(inputTable)
    else if errorHandlingOption = "ReplaceErrors" then
        // Replace errors with a specified value (default is "Error" if replaceValue is not provided)
        let
            ReplaceErrorsStep = 
                let
                    // Get a list of column names
                    columnNames = Table.ColumnNames(inputTable),
                    // Create a list of replacement rules where each column is mapped to null in case of error
                    errorReplacementList = List.Transform(columnNames, each {_, null})
                in
                    Table.ReplaceErrorValues(inputTable, errorReplacementList)
        in
            ReplaceErrorsStep
    else
        // If the parameter is not set correctly, return the data as is
        inputTable,

    Output = if viewErrorSummary then ErrorRows else ErrorHandlingResult
in
    Output

The function has two input parameters:

  • inputTable: a data table to be evaluated for errors
  • errorHandlingOption: specifies how the errors within inputTable should be handled (RemoveErrors, ReplaceErrors, None)

The function also utilizes the viewErrorSummary Power Query parameter. This parameter can be set to TRUE/FALSE and determines whether to return an error summary or the cleaned dataset.

This function identifies rows with errors. Depending on the errorHandlingOption, it will remove the rows containing errors, replace the error values with null, or return inputTable without handling the errors.

If viewErrorSummary is set to TRUE, the function returns only the rows that contain errors, allowing us to inspect the errors further within our dataset.


Core Function – TranformCSV

The fxTransformCSV function is the start of our standardized data processing solution, designed to handle our CSV files in a modular and reusable way.

It takes the raw contents of our CSV files, applies a defined set of transformations, and then outputs a clean data table ready for our analysis. This function leverages the utility functions discussed in the previous section.

(FileContent as binary) =>
let
    //STEP 01: Importing CSV files
    ImportCSV = Csv.Document(FileContent,[Delimiter=",", Encoding=1252, QuoteStyle=QuoteStyle.None]),
    PromoteHeaders = Table.PromoteHeaders(ImportCSV, [PromoteAllScalars=true]),
    
    //STEP 02: Cleaning blank rows/columns
    //Clean blank rows
    RemoveBlankRows = Table.SelectRows(PromoteHeaders, each not List.IsEmpty(List.RemoveMatchingItems(Record.FieldValues(_), {"", null}))),
    //Clean blank columns
    RemoveBlankColumns = Table.SelectColumns(RemoveBlankRows, List.Select(Table.ColumnNames(RemoveBlankRows), each not List.IsEmpty(List.RemoveMatchingItems(Table.Column(RemoveBlankRows, _), {""," ", null})))),
    
    //STEP 03: Replace empty strings with  null
    ReplaceValue = Table.ReplaceValue(RemoveBlankColumns,"",null,Replacer.ReplaceValue,Table.ColumnNames(RemoveBlankColumns)),
    
    //STEP 04: Remove duplicates
    RemoveDuplicates = fxTransformRemoveDuplicates(ReplaceValue),

    //STEP 05: Text transformations
    // Convert the textColumns parameter into a list
    TextColumnsToTransform = fxParameterToList(textColumns),
    // Apply the TrimTextColumns function
    TrimTextColumns = fxTransfromTextTrim(RemoveDuplicates, TextColumnsToTransform),
    // Apply the TransformTextCase function for Proper Case (can also be "Lower" or "Upper")
    FormatTextColumns = fxTransformTextFormat(TrimTextColumns, TextColumnsToTransform, textFormatCase)

in
    FormatTextColumns

Breaking Down fxTransformCSV

STEP 01: Importing the CSV files

The function begins by reading the contents of the CSV files and then promotes the first row to headers.

//STEP 01: Importing CSV files
    ImportCSV = Csv.Document(FileContent,[Delimiter=",", Encoding=1252, QuoteStyle=QuoteStyle.None]),
    PromoteHeaders = Table.PromoteHeaders(ImportCSV, [PromoteAllScalars=true]),

STEP 02: Cleaning blank rows/columns

The next step, which begins our transformation steps, removes any empty rows or columns. By eliminating these blank rows or columns early in our process, we avoid potential issues downstream and reduce clutter in the dataset.

    //STEP 02: Cleaning blank rows/columns
    //Clean blank rows
    RemoveBlankRows = Table.SelectRows(PromoteHeaders, each not List.IsEmpty(List.RemoveMatchingItems(Record.FieldValues(_), {"", null}))),
    //Clean blank columns
    RemoveBlankColumns = Table.SelectColumns(RemoveBlankRows, List.Select(Table.ColumnNames(RemoveBlankRows), each not List.IsEmpty(List.RemoveMatchingItems(Table.Column(RemoveBlankRows, _), {""," ", null})))),

STEP 03: Replace empty strings with null

Empty strings can cause common issues in our analysis. This step replaces empty string values in the dataset with null, making the data more consistent, better understood, and easier to work with.

    //STEP 03: Replace empty strings with  null
    ReplaceValue = Table.ReplaceValue(RemoveBlankColumns,"",null,Replacer.ReplaceValue,Table.ColumnNames(RemoveBlankColumns)),

STEP 04: Remove duplicates

Using the fxTransformRemoveDuplicates utility function, this step removes duplicated rows from the dataset.

//STEP 04: Remove duplicates
    RemoveDuplicates = fxTransformRemoveDuplicates(ReplaceValue),

STEP 05: Text transformations

The text transformations start with trimming our text values to ensure there are no leading or trailing spaces, using the fxTransformTextTrim utility function.

Then, the text values get standardized using the fxTransformTextFormat utility function to make our data more readable and consistent.

//STEP 05: Text transformations
    // Convert the textColumns parameter into a list
    TextColumnsToTransform = fxParameterToList(textColumns),
    // Apply the TrimTextColumns function
    TrimTextColumns = fxTransfromTextTrim(RemoveDuplicates, TextColumnsToTransform),
    // Apply the TransformTextCase function for Proper Case (can also be "Lower" or "Upper")
    FormatTextColumns = fxTransformTextFormat(TrimTextColumns, TextColumnsToTransform, textFormatCase)

Core Function – LoadCSVData

The LoadCSVData function is the layer connecting the different elements of our Power Query solution. It loads multiple CSV files from our SharePoint source, applies the fxTransformCSV function to each file, and handles final data preparations.

This function is adaptable by leveraging Power Query parameters, allowing us to easily modify the data preparation process to meet our project’s requirements (e.g. switching to different SharePoint sites or folders).

() as table =>
let
    
    //STEP 01: Load files from SharePoint
    SharePointSite = SharePoint.Files(#"SharePoint Site Url", [ApiVersion = 15]),
    SharePointFolder = Table.SelectRows(SharePointSite, each ([Folder Path] = #"SharePoint Folder Path")),
    
    //STEP 02: Invoke the fxTransformCSV function for each file
    TransformCSVResult = Table.AddColumn(SharePointFolder, "TransformCSV", each fxTransformCSV([Content])),
    
    //STEP 03: Renaming and removing columns
    // Rename the "Name" column for clarity
    PrefixSouceName = Table.RenameColumns(TransformCSVResult,{{"Name", "source.Name"}}),
    // Remove other columns to focus on the transformed data
    RemoveColumns = Table.SelectColumns(PrefixSouceName,{"source.Name", "TransformCSV"}),
    
    //STEP 04: Expand the transformed CSV data
    ExpandTransformCSV = Table.ExpandTableColumn(RemoveColumns, "TransformCSV", Table.ColumnNames(TransformCSVResult{0}[TransformCSV])),

    //STEP 05: Set data types
    ColumnTypePairs = Text.Split(columnDataTypes, ";"),
    // Trim spaces and split each pair into a list of {column name, data type}
    ColumnTypeList = List.Transform(ColumnTypePairs, each 
        let
            PairParts = List.Transform(Text.Split(_, ","), Text.Trim),
            ColumnName = List.First(PairParts),
            ColumnType = fxTransformSetDataTypes(List.Last(PairParts))
        in
            {ColumnName, ColumnType}
    ),
    // Dynamically set the column data types
    SetDataTypes = Table.TransformColumnTypes(ExpandTransformCSV, ColumnTypeList),

    //STEP 06: Error handling
    ErrorHandlingOutput = fxErrorHandling(SetDataTypes, errorHandlingOption)
in
    ErrorHandlingOutput

Breaking down fxLoadCSVData

STEP 01: Load files from SharePoint

The function begins by connecting to the specified SharePoint site using the SharePoint Site Url parameter. Then, it filters the files to those located within the specified SharePoint Folder Path. Using both parameters allows us to target the specific CSV files required for our analysis.

//STEP 01: Load files from SharePoint
SharePointSite = SharePoint.Files(#"SharePoint Site Url", [ApiVersion = 15]),
SharePointFolder = Table.SelectRows(SharePointSite, each ([Folder Path] = #"SharePoint Folder Path"))

STEP 02: Invoke the fxTransformCSV function for each file

For each file in our SharePoint source, we add a new column and invoke our custom fxTransformCSV function. This step ensures each file is processed consistently.

//STEP 02: Invoke the fxTransformCSV function for each file
TransformCSVResult = Table.AddColumn(SharePointFolder, "TransformCSV", each fxTransformCSV([Content]))

STEP 03: Renaming and removing columns

To improve the clarity of our columns, we renamed the Name column to source.Name, indicating that it represents the original file name. This clearly indicates what this column is while providing information on the file the row of data is associated with.

The function then removes unnecessary columns, and we keep only source.Name and TransformCSV columns.

//STEP 03: Renaming and removing columns
// Rename the "Name" column for clarity
PrefixSouceName = Table.RenameColumns(TransformCSVResult,{{"Name", "source.Name"}}),
// Remove other columns to focus on the transformed data
RemoveColumns = Table.SelectColumns(PrefixSouceName,{"source.Name", "TransformCSV"})

STEP 04: Expand the transformed CSV data

We expand the TransformCSV column into individual files, which compiles all the transformed CSV data into a single table.

//STEP 04: Expand the transformed CSV data
ExpandTransformCSV = Table.ExpandTableColumn(RemoveColumns, "TransformCSV", Table.ColumnNames(TransformCSVResult{0}[TransformCSV]))

STEP 05: Set data types

In this step, the function dynamically sets the data types for each column based on the columnDataTypes Power Query parameter. The TransformSetDataTypes function maps each column to its specified data type.

//STEP 05: Set data types
ColumnTypePairs = Text.Split(columnDataTypes, ";"),
// Trim spaces and split each pair into a list of {column name, data type}
ColumnTypeList = List.Transform(ColumnTypePairs, each 
    let
        PairParts = List.Transform(Text.Split(_, ","), Text.Trim),
        ColumnName = List.First(PairParts),
        ColumnType = fxTransformSetDataTypes(List.Last(PairParts))
    in
       {ColumnName, ColumnType}
),
// Dynamically set the column data types
SetDataTypes = Table.TransformColumnTypes(ExpandTransformCSV, ColumnTypeList)

STEP 06: Error handling

The function concludes by handling errors in the dataset according to the errorHandlingOption parameter. If the viewErrorSummary is set to true, the function returns a summary of rows that contain errors, otherwise it returns the cleaned dataset.

//STEP 06: Error handling
ErrorHandlingOutput = fxErrorHandling(SetDataTypes, errorHandlingOption)

Applying and Extending the Power Query Solution

We have explored and started constructing a flexible approach to standardize and ensure our data preparation steps are applied consistently within and across our Power BI projects.

The final query loaded to our Power BI data model is rpt_SharePointCSVData. This query utilizes the fxLoadCSVData function to bring each step together and deliver a clean and consistent data set to our Power BI report.

let
    Source = fxLoadCSVData()
in
    Source

This simple query calls the fxLoadCSVData function, which loads and processes the CSV files according to previously defined transformations and parameters. The result is a clean, ready-to-use dataset.

This solution is designed to be adaptable and can be applied to different Power BI projects that require loading CSV files from a SharePoint source.

A Power BI template and sample data files are available at the link below to view an example of how this solution can be applied. This file can be used as a starting point for our Power BI projects, or you can copy and paste specific folders or elements of it into an existing project.

This repository provides a Power BI template and sample CSV files for automating and standardizing CSV data transformation from SharePoint using Power Query functions.

To successfully apply this solution:

Start with the Parameters: define the parameters relevant to the project.

Open the template file and, when prompted, populate each parameter with the required information.

For example, the parameters can be set using the sample files to the following.

SharePoint Site Url: 
https://yourcompany.sharepoint.com/sites/yoursite

SharePoint Folder Path:
https://yourcompany.sharepoint.com/sites/yoursite/yourfolder/

textFormatCase:
Proper

textColumns:
Event Name, Location, Organizer Name

columnDataTypes:
Event Date,date;Event ID,whole number;Event Name,text;Location,text;Organizer Name,text;Number of Attendees,whole number;Feedback Score,decimal number

errorHandlingOption:
ReplaceErrors

viewErrorSummary:
FALSE

Then, under the Load dropdown, select Edit to explore this solution in the Power Query Editor.

Once the data is loaded, you can modify the textFormatCase, error handling option, and viewErrorSummary parameters in the Power Query Editor to view the results when you select different options.

Customize the Utility Functions: if different projects have unique requirements, consider modifying or extending the utility functions.

Leverage the modular structure: This solution’s modular design makes adding or removing steps easy based on the project’s specific needs.

Extend with new functions: as our data processing needs evolve, we can extend this solution by adding new functions or adding more flexibility to our existing ones.


Wrapping Up

This post explores a solution that leverages Power Query parameters, functions, and queries to streamline our data preparation process. Combining custom functions with parameters provides us with a framework that can be adapted to meet the demands of different projects.

The design approach used in this solution aims to enhance the maintainability and reusability of each of its components. As we continue to work with and expand this solution, we will build a library of reusable functions to increase our efficiency in future projects.


Thank you for reading! Stay curious, and until next time, happy learning.

And, remember, as Albert Einstein once said, “Anyone who has never made a mistake has never tried anything new.” So, don’t be afraid of making mistakes, practice makes perfect. Continuously experiment, explore, and challenge yourself with real-world scenarios.

If this sparked your curiosity, keep that spark alive and check back frequently. Better yet, be sure not to miss a post by subscribing! With each new post comes an opportunity to learn something new.

Dive into DAX: Simplify Data Models with Relationship Functions


The Basics of DAX Relationship Functions

DAX Relationship Functions are an essential part of our data modeling toolkit. These functions allow us to navigate the relationships connecting our data model tables facilitating complex calculations and deriving the insights they provide.

Relationships in our data models matter because they help maintain the integrity and consistency of our data. They connect different tables, enabling us to create insightful and dynamic reports. When creating our Power BI reports understanding these relationships becomes crucial since they dictate how data filters and aggregations are applied throughout our reports.

DAX Relationship Functions allow us to control and manipulate these relationships to suite our specific needs. Using these functions, we can perform in-depth calculations that involve multiple tables. They can be particularly useful in scenarios where we need to bring data from different sources into a single coherent view. Understanding and utilizing these functions can significantly elevate our data analysis.

For details on these functions visit the DAX Function Reference documentation.

Learn more about: DAX Relationship Functions

For those eager to start experimenting there is a Power BI report pre-loaded with the same data used in this post ready for you! So don’t just read, follow along and get hands-on with DAX in Power BI. Get a copy of the sample report here:

This dynamic repository is the perfect place to enhance your learning journey.


RELATED: Fetching Related Values

The RELATED function in DAX is designed to fetch a related value from another table. Its syntax is straightforward.

RELATED(column_name)

Here, column_name is the column from the related table that we want to retrieve. This function can be particularly useful in calculated columns when we need to access data from a lookup table in our calculations.

The RELATED function requires that a relationship exists between the two tables. Then the function can navigate the existing many-to-one relationship and fetch the specified column from the related table. In addition to an existing relationship, the RELATED function requires row context.

Our Sales table has a ProductID which is used to establish a relationship in our data model to the Products table. Let’s bring in the Product field from the Products table into our Sales table.

Product Category = RELATED(Products[Product])

We can use this DAX formula to add a new calculated column to our Sales table showing the product category corresponding to each sales record. This can help make our sales data more informative and easier to analyze, as we can now see the product category directly in the sales table.

We can also use RELATED and the existing relationship between our Sales and Regions table to filter our Sales and create an explicit United States Sales. Let’s take a look at this measure.

United States Sales = 
SUMX(
    FILTER(
        Sales, 
        RELATED(Regions[Region]) = "United States"
    ), 
    Sales[Amount]
)

This formula is much more informative and clearer than filtering directly on the RegionID field contained within the Sales table. Using RELATED within our FILTER function like this makes our measure more readable and it can immediately be identified what this measure is calculating.

The RELATED function is a powerful tool for enhancing our data models by seamlessly integrating related information. This can help us create more detailed and comprehensive reports.


RELATEDTABLE: Navigating Related Tables

The RELATEDTABLE function in DAX allows us to navigate and retrieve a table related to the current row from another table. This function can be useful when we need to summarize or perform calculations on data from a related table based on the current context. Here is its syntax.

RELATEDTABLE(table_name)

Here, table_name is the name of an existing related table that we want to retrieve. The table_name parameter cannot be an expression.

Let’s consider a scenario where we want to calculate the total sales amount for each product using the RELATEDTABLE function. Here is how we can use it to create a new calculated column in our Products table.

Total Sales by Product = 
SUMX(
    RELATEDTABLE(Sales),
    Sales[Amount]
)

In the DAX expression, we sum the Amount column from the Sales table for each product. The RELATEDTABLE function fetches all the rows from the Sales table that are related to the current product row in the Products table, and SUMX sums the Amount column for these rows.

When we use RELATEDTABLE, we can navigate and perform calculations across related tables, enhancing our ability to analyze data in a more granular and insightful way.


USERELATIONSHIP: Activating Inactive Relationships

The USERELATIONSHIP function in DAX is designed to activate an inactive relationship between tables in a data model. This is useful when a table has multiple relationships with another table, and we need to switch between these relationships for different calculations. Here is its syntax.

USERELATIONSHIP(column1, column2)

Here, column1 is the name of an existing column and typically represents the many side of the relationship to be used. The column2 is the name of an existing column and typically represents the one side or lookup side of the relationship to be used.

The USERELATIONSHIP returns no value and can only be used in functions that take a filter as argument (e.g. CALCULATE, TOTALYTD). The function uses existing relationships in the data model and cannot be used when row level security is defined for the table in which the measure is included.

Let’s take a look at a scenario where we are interested in calculating the number of employees who have left the organization based on their end dates using the USERELATIONSHIP function.

The Employee table includes each employee’s StartDate and EndDate. Each of these columns are used to establish a relationship with the DateTable in the data model. The relationship with StartDate is set to active, while the relationship with EndDate is inactive.

We can use the following DAX formula to define our Employee Separations measure.

Employees Separations USERELATIONSHIP = 
CALCULATE(
    COUNT(Employee[EmployeeID]),
    USERELATIONSHIP(Employee[EndDate], DateTable[Date]),
    NOT(ISBLANK(Employee[EndDate]))
)

This measure calculates the number of employees who have left the organization based on their EndDate by activating the inactive relationship between Employee[EndDate] and DateTable[Date] and ensuring that it only counts employees who have an EndDate.

We can better understand the power of USERELATIONSHIP by comparing these results to the results of the same measure but this time without activating the inactive relationship.

Employee Separations No USERELATIONSHIP = 
CALCULATE(
    COUNT(Employee[EmployeeID]),
    NOT(ISBLANK(Employee[EndDate]))
)

In the No USERELATIONSHIP measure we try to calculate the number of employees who left the company based on EndDate. However, we can see that without activating the relationship the active relationship is used in the context of the calculation.

Of the 9 employees that have left the organization, we can see that for 2022 the No USERELATIONSHIP measure is counting the 8 employees that started in 2022 rather than the 3 that left in 2022.


CROSSFILTER: Controlling Cross-Filtering Behavior

The CROSSFILTER function in DAX helps us manage the direction of cross-filtering between two tables in our data model. With this function we specify whether the filtering direction is one-way, both ways, or none, providing control over how data flows between our tables. This becomes useful in complex models where bidirectional filtering can lead to unintended results. Here is its syntax.

CROSSFILTER(column1, column2, direction)

The parameters column1 and column2 are similar to the parameters of USERELATIONSHIP, where column1 is the name of an existing column and typically represents the many side of the relationship and column2 is a column name and typically represents the one side of the relationship.

The direction parameter specifies the cross-filter direction to be used and must be one of the following values.

  • None – no cross-filtering occurs along this relationship
  • Both – filters on either side filters the other side
  • OneWay – filters on the one side of a relationship filter the other side. This option cannot be used with a one-to-one relationship and is not recommended for many-to-many relationships.
  • OneWay_LeftFiltersRight – filters on the side of column1 filter the side of column2. This option cannot be used with a one-to-one or many-to-one relationship.
  • OneWay_RightFiltersLeft – filters on the side of column2 filter the side of column1. This option cannot be used with a one-to-one or many-to-one relationship.

The CROSSFILTER function returns no value and can only be used within functions that take a filter as an argument (e.g. CALCULATE, TOTALYTD). When we establish relationships in our data model we define the cross-filtering direction, when we use the CROSSFILTER function it overrides this setting.

Let’s consider the scenario where we want to analyze the distinct products sold and the total sales amount by month and year. We start by creating a Distinct Product Code Count measure.

Distinct Product Code Count = 
DISTINCTCOUNT(Products[Product Code])

If we add this measure to a table visual, we will notice an issue with the count. The count is returning the total product code count, and not the intended results of the count of distinct products sold that month.

We see this because the relationship is one-to-many (Product-to-Sales) with single direction relationship (i.e. Product filters Sales). This default set up does not allow for our Sales table to filter our Product tables leading to the unintended results.

Now, we could correct this by changing the cross-filtering direction property on the Product-Sales relationship. However, this would change how filters work for all data between these two tables, which may not be a desired or an acceptable outcome.

Another solution is to utilize the power of the CROSSFILTER function. We can use this function to change how the Product-Sales relationships works within a new measure.

Distinct Product Code Count Bidirectional = 
CALCULATE(
    [Distinct Product Code Count],
    CROSSFILTER(Sales[ProductID], Products[ProductID], Both)
)

We can add this new measure to our table and see we get the expected results. This measure gathers all the sales records in the current context (e.g. Jan 2022), then filters the Product table to only related products, and finally returns a distinct count of the products sold.

This measure and the Sales Amount can now be used to analyze our sales data with details on the number of different products sold each month.

By using CROSSFILTER, we maintain control over our data relationships, ensuring our reports reflect the precise insights we need without unintended data flows. This level of control is crucial for building robust and reliable Power BI models.


Wrapping Up

DAX relationship functions are powerful tools that significantly enhance our ability to manage and analyze data in Power BI. We have explored how these essential functions empower us to connect and manipulate data and relationships within our data model. By understanding and knowing when to leverage these functions we can create dynamic, accurate, and insightful reports. Here is a quick recap of the functions.

  • RELATED simplifies data retrieval by pulling in values from a related table, making our data more informative and easier to analyze
  • RELATEDTABLE enables us to navigate and summarize related tables, providing deeper insights into our data.
  • USERELATIONSHIP gives us the flexibility to activate inactive relationships, allowing us to create more complex and context-specific calculations.
  • CROSSFILTER allows us to control the direction of cross-filtering between tables, ensuring our data flows precisely as needed.

To further explore and learn the details of these functions visit the DAX Relationship Function reference documentation.

Learn more about: DAX Relationship Functions

By adding these functions into our DAX toolkit, we enhance our ability to create flexible and robust data models that ensure our reports are both visually appealing and deeply informative and reliable.

To explore other function groups that elevate our data analysis check out the Dive into DAX series, with each post comes the opportunity to enhance your data analytics and Power BI reports.

Explore the intricate landscape of DAX in Power BI, revealing the potential to enhance your data analytics with every post. 


Thank you for reading! Stay curious, and until next time, happy learning.

And, remember, as Albert Einstein once said, “Anyone who has never made a mistake has never tried anything new.” So, don’t be afraid of making mistakes, practice makes perfect. Continuously experiment and explore new DAX functions, and challenge yourself with real-world data scenarios.

If this sparked your curiosity, keep that spark alive and check back frequently. Better yet, be sure not to miss a post by subscribing! With each new post comes an opportunity to learn something new.

FP20 Analytics ZoomCharts Challenge 15: My Journey to the Top 5


Over the last two years I have ended each of my posts with two main messages, (1) stay curious and happy learning, and (2) continuously experiment, explore and challenge yourself. However, at times it can be hard to identify open ended opportunities to fulfill these.

One opportunity available is participating in various data challenges. I recently participated in and was a top 5 finalist in the FP20 Analytics ZoomCharts Challenge 15. This data challenge was a HR data analysis project with a provided dataset to explore and a chance to expand your report development skills.

What I enjoyed about the challenge is along with the dataset, it provided a series of questions to help guide the analysis and provide direction and a focus for the report.

Here is the resulting report submitted to the challenge and discussed in this post. View, interact, and get the PBIX file at the link below.

The report was built with the HR Analysis dataset and includes ZoomCharts custom Drill Down PRO visuals for Power BI. 


About the Challenge

The FP20 Analytics challenge was in collaboration with ZoomCharts and provided an opportunity to explore custom Power BI ZoomCharts drill down visuals.

The requirements included developing a Power BI report with a default canvas size, a maximum of 2 pages, and include at least two ZoomCharts Drill Down Visuals.

The goal of the challenge was to identify trends within the dataset and develop a report that provides viewers the answers to the following questions.

  1. How diverse is the workforce in terms of gender, ethnicity, and age?
  2. Is there a correlation between pay levels, departments, and job titles?
  3. How about the geographic distribution of the workforce?
  4. What is the employee retention rate trend yearly?
  5. What is the employee retention rate in terms of gender, ethnicity, and age?
  6. Which business unit had the highest and lowest employee retention rate?
  7. Which business unit and department paid the most and least bonuses annually?
  8. What is the annual historical bonus trend? Can we show new hires some statistics?
  9. How about the pay equity based on gender, ethnicity, and age?
  10. What is the employee turnover rate (e.g., monthly, quarterly, annually) since 2017?

There are countless ways to develop a Power BI report to address these requirements. You can see all the FP20 Analytics ZoomCharts Challenge 15 report submissions here.

This post will provide an overview and some insight into my approach and the resulting report.


Understanding the Data

With any analysis project, before diving into creating the report, I started by exploring and getting an understanding of the underlying data. The challenge provided a single table dataset, so I loaded the data into Power BI to use the Power Query editor’s column distribution, column profile, and column quality to help get an understanding of the data.

Using these tools, I was able to identify missing values, data errors, data types, and get a better sense of the distribution of the data. This initial familiarity will help inform the analysis and help identify what data could be used to answer the requirement questions, identify data gaps, and help ask the right questions to create an effective report.

The dataset contained 16 columns and provided the following data on each employee.

  • Employee/Organizational characteristics
    • Employee ID, full name, job title, department, business unit, hire date, exit date
  • Demographic information
    • Age, gender, ethnicity
  • Salary information
    • Annual salary and bonus percentage
  • Location information
    • City, country, latitude, longitude

The dataset was already clean. No columns contained any errors that had to be addressed, and the only column that had empty/null values was exit date, which is expected. One thing I noted at this stage is that the Employee ID column did not provide a unique identifier for an employee.

Additionally, I used a temporary page in the Power BI report containing basic charts to visualize distributions and experiment with different types of visuals to see which ones best represent the data and help reach the report objectives. Another driver of using this approach was start experimenting with and understanding the different ZoomCharts and their customizations.


Identifying Key Data Attributes

Once I had a basic understanding of the dataset, it is always tempting to jump right into data manipulation and visualization. However, I find it helpful at this stage to pause and review the questions the report should answer.

During this review I was able to further define these goals, with my new understanding of the data, which guided the selection of relevant data within the dataset.

I then broke the questions down into 3 main areas of focus and began to think about what data attributes within the dataset can be used, and possibility more importantly, think about what is missing or how I can enrich the dataset to create a more effective report.

Workforce Diversity (Question #1 and #3)

To analyze the workforce diversity, the dataset provided a set of demographic information fields that aligned with these questions.

Salary & Bonus Structure (Questions #2, #7, #8, #9)

The next set of questions I focused on revolved around the salary and bonus structure of the organization. I identified I could use the demographic fields along with the salary information to provide insights.

Employee Retention & Turnover (Questions #4, #5, #6, and #10)

The dataset did not directly include the retention and turnover rates and required enriching the dataset to calculate these values. To do this I used the hire date and exit date. Once calculated I am able to add an organization context to the analysis by using the business unit, department, and job title attributes.


Dataset Enrichment

After identifying key data attributes that can be used to answer the objectives of the report, it becomes clear that there are opportunities for enriching the dataset to aid in making a more effective visualization (e.g. age bins) and address data gaps or require calculations (e.g. employee retention and turnover).

For this report I achieved this through the use of both calculated columns and measures.

Creating Calculated Columns

Calculated columns are a great tool to add new data based on existing information in the dataset. For this report I created 7 calculated columns which were required because I wanted to use the calculated result in axes of report visuals or as a filter condition in a DAX query.

  • Age Bin: categorized the employee’s age based on their age decade (20s, 30s, 40s, 50s, or 60s). Here I used a calculated column rather than the built-in data group option to provide more flexibility and control over the bins.
  • Tenure (Years): while exploring salary across departments and demographic categories, I also wanted to include context for how long the employee has been with the organization as this might influence their salary and/or bonus.
  • Total Compensation: the dataset provided annual salary and bonus percent. The bonus percent was helpful when examining this attribute specifically, however when analyzing the organization pay structure across groups, I found the overall page (salary + bonus) to be more insight and provide the entire picture of the employee’s compensation.
  • Employee Status: the dataset included current and past employees. To ease analysis and provide the users the ability to filter on the employee’s status I included a calculated column to label the employee as active or inactive.
  • Abbreviation: the report required providing insight broken down by business unit, country, and department all of which could have long names and clutter the report. For each of these columns I included a calculated column providing a 3-letter abbreviation to be used in the report visuals.

Defining Measures

In addition to calculated columns, the report included various calculated measures. These dynamic calculations are versatile and aid the interactive nature of the Power BI report.

For this report I categorized my measure into the following main categories.

  • Explicit Summaries: these measures are not strictly required. However, I prefer the use of explicit aggregation measures over implicit auto-aggregation measures on the visuals due to the increased flexibility and reusability.
    • Average Total Compensation
    • Average Bonus ($)
    • Average Bonus (%)
    • Highest Average Bonus % (Dept) Summary
    • Lowest Average Bonus % (Dept) Summary
    • Maximum Bonus %
    • Minimum Bonus %
    • Average Annual Salary
    • Maximum Annual Salary
    • Median Annual Salary
    • Minimum Annual Salary
    • Active Employee Count
      • Total count
      • Each ethnicity count
      • Male/Female count
    • Inactive Employee Count
  • Report Labels: these measures were used to add additional context and information to the user when interacting with drill down visuals. On the drill down visuals when a user selects a data category or data point the visual drills down and shows the next level of the data hierarchy. What is lost, is what top level category was selected, so these labels are used to provide that information.
    • Selected Age Bin
    • Selected Business Unit Retention
    • Selected Business Unit Turnover
    • Selected Dept Retention
    • Selected Dept Turnover
  • Retention & Turnover: 4 of the report objectives revolved around employee retention and turnover rates. The dataset only provided employee hire dates and exits which are used to calculate these values.
    • Cumulative Total Employee Count (used in retention rate)
    • Employee Separations (used in retention rate)
    • Employee Retention
    • Brazil Retention Rate
    • China Retention Rate
    • United Sates Retention Rate
    • Employee Turnover Rate
    • Brazil Turnover Rate
    • China Turnover Rate
    • United States Turnover Rate

Report Development

After understanding the dataset, identifying key data attributes, and enriching the dataset I moved onto the report development.

Report Framework

From the requirements I knew the report would be 2 pages. The first focused on Workforce Diversity and Salary & Bonus Structure. The second focused on Employee Retention & Turnover.

I started the report with a two-page template that included all the functionality of an expandable navigational element. For details on how I created this, and where to find downloadable templates see my post below.

This navigation is a compact vertical navigation that can be expanded to provide the user page titles and was highlighted as a strong point of the report in the Learn from the Best HR Reports: ZoomCharts TOP Picks.

User-Centric Design: Next level reporting with interactive navigation for an enhanced user experience

Then I selected the icons used in the navigation and updated the report page titles on each page and within the expanded navigation.

Once the template was updated for the specifics of this report, I applied a custom theme to get the aesthetic just right. For more on creating custom themes and where to find downloadable themes, including the one used in this report (twilight-moorland-plum), see the following post.

Dive into the details of blending design and data with custom Power BI themes.

After updating the navigation template and implementing the report theme, I was set with a solid foundation to begin adding report visuals.

Demographic & Compensation Analysis

The first page of the report focused on two main objectives, the demographic distribution of the workforce and an in-depth analysis of the organizational compensation structure.

Demographic Distribution

The first objective was to provide the user insights into the diversity of the workforce in terms of gender, ethnicity, and age. This was a perfect fit for the Drill Down Combo PRO (filter) by ZoomCharts visual. The visual displays the percentage of the workforce broken down by gender and displayed by employee age. Each age bin then can be drilled into to reveal additional insights into the age bins ethnicity make up.

In addition to the core visual, I included a card visual displaying the Selected Age Bin measure to provide context to the data when viewing an age bins ethnicity make up.

Geographic Distribution

The other component of this analysis was objective #3 focused on the geographic distribution of the workforce. In my submitted report this comprised of two elements the first and primary visual is the Drill Down Map PRO (Filter) by ZoomCharts visual. The second is a Drill Down Combo Bar PRO (Filter) by ZoomCharts visual.

The Map visual shows the ethnicity of the workforce as a percentage of the total workforce for each geographic location.

This visual in the report had noted limitation. Mainly the initial view of the map did not show all the data available. The inclusion of the country break provided an effective means to filter to a specific country however, it crowded the visual. Additionally, the colors in the report for the ethnicity groups of Asian and Black used the same colors used throughout the report for Male and Female which can be a source of confusion. See the Feedback and Improvements sections to see the updates to more effectively visual this data.

Organization Compensation Structure – Compensation Equity

The first component of the compensation structure analysis was to examine the median total compensation (salary + bonus) by departments, business units and job title. The second was to provide insights into compensation equity among the demographic groups of age, ethnicity, and gender.

I used the Drill Down Combo PRO (Filter) visual to analyze the median total compensation for each organizational department broken down by gender. Each department can be drilled into to extract insights about the business unit and further drilled into each job title. I also included the average tenure in years of the employees within each category to better understand the compensation structure of the organization.

This report section contained another Drill Down Combo PRO (Filter) visual to provide insights on the median total compensation by ethnicity and gender. These two visuals when used in tandem and leveraging cross-filtering can provide a nearly complete picture of the compensation structure between departments and equity across demographic groups.

When the two Median Total Compensation visuals are used along with the demographic and geographic distributions visuals a full understanding and in-depth insights can be extracted. The user can interact and cross-filter all of these visuals to tailor the insights to meet their specific needs.

Organization Compensation Structure – Departmental & Historic Bonus Analysis

The second component of the compensation structure analysis was to provide an analysis of departmental bonuses and historical bonus trends.

To provide detailed insights into the bonus trends I utilized a set of box and whisker plots to display granular details and card visuals to provide high-level aggregations. I will note that box and whisker plots may not be suitable in every scenario. However, for an audience that is familiar and comfortable interpreting these plots they are a great tool and were well suited for this analysis.

Workforce Retention & Turnover

The second page of the report focused on the analysis of employee retention and turnover. For this report the retention rate was calculated as the percentage of employees that remained with the organization during a specific evaluation period (e.g. annually) and the turnover rate is the rate at which employees left the organization expressed as a percentage of the total number of employees.

For this analysis, I thought it was key to provide the user and quick and easy way to flip between these metrics depending on their specific requirement. I did this by implementing a button experience at the top of the report, so the user can easily find and reference what metric they are viewing.

Another key aspect to enhance the clarity of the report is the visuals remain the same regardless of the metric being viewed. This eases the transition between the different views of the data.

Across the top of the report page is a set of Drill Down Combo Bar PRO (Filter) visuals to analyze the selected metric by department and business unit on the left and age, gender, and ethnicity in the right-side grouping.

Each of these visuals also use the threshold visual property to display the average across all categories. This provides a clear visual indicator of how a specific category is performing compared to the overall average (e.g. retention for the R&D Business Unit is slightly worse (87%) than the organizational average of 92%).

All of these visuals can be used to cross-filter each other to get highly detailed and granular insights when required.

In addition to examining the retention and turnover rate among organizational and demographic groups there was an objective to provide insight to the temporal trends of these metrics. The Drill Down Timeline PRO (Filter) visual was perfect for this.

This visual provides a long-term view of the retention and turnover rate trend while providing the user an intuitive and interactive way to zoom into specific time periods of interest.

Additional Features

Outside to of the main objectives of the report outlined by the 10 specified questions there were additional insights, features, and functionalities built into the report to enhance usability and the user experience.

On the Demographic & Compensation Analysis page this included a summary statistics button to display a high-level overlay of demographic and salary summaries.

On both pages of the report there was also a clear slicer button to clear all selected slicer values. However, this clear slicer button did not reset the report to an initial state or reset data filters due to user interactions with the visuals. See the Feedback and Improvements section for details and the implemented fix.

Lastly, each page had a guided tutorial experience to inform new users about the report page and all the different features and functionalities the report and the visuals offered.

There are various other nuanced and detailed features of this reports and too much to all cover here. But please check out and interact with the report here:

The report was built with the HR Analysis dataset and includes ZoomCharts custom Drill Down PRO visuals for Power BI. 


Feedback and Improvements

My submitted report was discussed in the Learn from the Best HR Reports: ZoomCharts TOP Pick webinar which provided excellent feedback on areas to improve the report.

You can view the webinar discussion below.

The first improvement was to address the sizing of the map. Initially, when the report loads the map visual is too small to view all of the data it provides.

To address and correct this the Employee Count by country visual was removed. This visual provided helpful summary information and an effective way to filter the map by country, however, the benefits of displaying all the data on the map outweigh the benefits of this visual.

Also mentioned in the discussion of the map is the limitation in colors. Initially the ethnicity groups Black and Asian used the same colors used to visualized gender and a source of potential confusion.

To address this, I extended the color palette to include two additional colors to better distinguish these groups. These groupings are also visible on the Workforce Retention & Turnover page. These visuals were also updated to ensure consistency across the report.

The next area of feedback was around the clear slicer button. As shown in the webinar it only clears the selected slicer values and uses Power BI’s built-in Clear all slicers button.

The functionality of the clear filter button on both pages was updated to reset the report context to a base state with no filters applied to the page. The size of the button was also increased to make it easier to identify for the user.

Another point of feedback was regarding the navigation icon tooltips. I did not make an adjustment to the report to address this. As shown in the webinar if you hover over the active page icon there is not a visible tool tip. I left the report this way because the current page icon indicator on the navigational element and the report page has a title providing this information to the user.

However, on each page if you hover over an icon to a different page, there should be a tool tip that displays and addresses the main objective of this feedback. This functionality is correct on the Demographic & Compensation Analysis page but required correcting on the Workforce Retention & Turnover page to be consistent.

Lastly, there was feedback on the use of the Box and Whisker plot within the report. I agree the use of this visual is heavily dependent on the end user’s comfortability with interpreting these visuals and is not suitable in all cases. However, for this report I think they provide a helpful visualization of the bonus data and remained in the report.


Wrapping Up

Getting started with participating in these type of data challenges can be an intimidating task. With Power BI and report development there is always more to learn and areas to improve so there is not some static skill level or point to begin with these challenges. The best method to move forward is to just start and show yourself patience as you learn more and grow your skills.

For me, the main take aways from participating in this challenge and why I plan to participate in more moving forward are:

  1. When learning a new skill repetition and continued use is essential, and with report development the more reports you create the better and more experienced you will be. This challenge provided an excellent opportunity to use a unique dataset to create a report from scratch.
  2. Others are using the same data and creating the own report to share. Viewing these different reports to see how others solved the same data challenge can be extremely helpful in growing your skills and expanding the way you approached the challenge.
  3. Participating in the ZoomCharts Challenge provided tailored feedback on the report submission. Providing helpful insight on how others viewed my report and highlighting areas for improvement.
  4. Access to custom visuals. Through this challenge I was able to learn and work with ZoomCharts custom visuals. I really enjoyed learning these and adding that experience to my skillset. Find out more about these extremely useful visuals here.

Thank you for reading! Stay curious, and until next time, happy learning.

And, remember, as Albert Einstein once said, “Anyone who has never made a mistake has never tried anything new.” So, don’t be afraid of making mistakes, practice makes perfect. Continuously experiment, explore, and challenge yourself with real-world scenarios.

If this sparked your curiosity, keep that spark alive and check back frequently. Better yet, be sure not to miss a post by subscribing! With each new post comes an opportunity to learn something new.

Design Meets Data: Crafting Interactive Navigations in Power BI


An essential aspect of any multi-page report is an effective and intuitive way to navigate between the various report pages. A well-designed navigational element in our Power BI reports enhances our users’ experience and guides them to the data and visualizations they require.

This post outlines and explores how Power BI native tools and functionalities can be utilized to create a similar navigational experience that was created in the previous post using Figma and Power BI: Design Meets Data: A Guide to Building Interactive Power BI Report Navigation.

Looking for another approach to building report navigation that uses built-in Power BI tools? Visit Part 3 of this Power BI Navigation series.

Streamlined report navigation with built-in tools to achieve functional, maintainable, and engaging navigation in Power BI reports.

For those interested in implementing the navigation element presented in this post, there are 2-, 3-, 4-, and 5-page templates available for download, with more details at the end of the post.

Revisiting Interactive Navigation in Power BI

Welcome back to another Design Meets Data exploration focused on interactive report navigation in Power BI. In the first part, we dove into using Figma to design and develop a user-friendly report interface.

Now, it is time to shift our focus towards leveraging Power BI’s native arsenal of tools, primarily bookmarks, buttons, and tool tips, to achieve similar, if not enhanced, functionalities.

Why go native? Utilizing Power BI’s built-in tools streamlines support and maintenance and provides a reduction in external complexities and dependencies. Plus, staying within a single platform makes it easier to manage and update our reports.

This post will highlight the nuances of Power BI’s navigation capabilities. It will demonstrate how to replicate the interactive navigation from Design Meets Data: A Guide to Building Interactive Power BI Report Navigation using tools available directly within Power BI. These tools will help simplify our report while maintaining an engaging and interactive navigational element.

Let’s get started!


Setting the Stage with Power BI Navigation

Before diving into the details, let’s step back with a quick refresher on the Power BI tools that we can leverage for crafting our report navigation. Power BI is designed to support complex reporting requirements with ease, thanks to features like bookmarks, buttons, and tooltips that can be intricately configured to guide our users through our data seamlessly.

Bookmarks

Bookmarks in Power BI save various states of a report page, allowing users to switch views or data contexts with a single click. We can use bookmarks to allow our users to toggle between different data filters or visual representations without losing context or having to navigate multiple pages.

For our navigational element, bookmarks will be key to creating the collapsing and expanding functionality. To create a bookmark, we get the report page looking just right, then add a bookmark to save the report state in the bookmark pane.

The new bookmark can now act as a restore point, bringing the user back to this specific view whenever it is selected. To keep our bookmarks organized it is best to rename them with a description name, generally including the report page and an indication of what the bookmark is used for (e.g. Page1-NavExpanded).

Buttons

Buttons take interactivity to the next level. We can use buttons to trigger various events, such as bookmarks, and also serve as navigation aids within the report. Buttons within our Power BI reports can be styled and configured to react dynamically to user interactions.

To create a button, we simply add the button object from the Insert ribbon onto the report canvas. Power BI offers a variety of button styles, such as a blank button for custom designs, or predefined icons for common actions like reset, back, or informational buttons.

Each button can be styled to match our report’s theme, including colors, text, and much more. Another key property to configure is the button action. Using this, we can define whether the button should direct our users to a different report page, switch the report context to a different bookmark, or another one of the many options available.

Tooltips

Tooltips in Power BI can provide simple text hints, but when properly utilized, they can provide additional insights or contextual data relevant to specific visuals without cluttering the canvas. This provides detail when required while keeping our reports clean and simple.

Power BI allows us to customize tooltips to show detailed information, including additional visuals. This can turn each tooltip into a tool to provide context or additional layers of data related to a report visual when a user hovers over the element.

By effectively using tooltips we transform user interaction from just viewing to an engaging, exploratory experience. This boosts the usability of our reports and ensures that users can make informed decisions based on the data view provided.


The Navigation Framework

Now that we have explored some details of the elements used to create our navigation, let’s dive into building the navigational framework. We will craft a minimalistic navigation on the left-hand side of our report, with the functionality to expand when requested by user interaction. This approach to our navigation is focused on making the navigation pane both compact and informative, ensuring that it does not overpower the content of the report.

In the Design Meets Data: A Guide to Building Interactive Power BI Report Navigation blog post the navigational element was built using Figma. Although Figma is a powerful and approachable design tool, in this guide, we will explore creating a similar navigation pane using native Power BI tools and elements. We will use Power BI’s shapes, buttons, and bookmarks to construct the framework and functionality.

The Navigation Pane Base Elements

We will start by creating the navigation pane by adding the base elements. In this compact and expandable design, this includes the background of the navigation pane, which will contain the page navigation and menu icons.

Collapsed Navigation Pane

The base of the navigation consists of three main components that we add to our Power BI report to start building our interactive navigational element.

The collapsed navigation pane starts by adding the shape of the pane itself. The color is set to theme color 1, 50% darker of the Power BI theme. Using the theme color will help our navigation remain dynamic when changing Power BI themes.

The next base element is the menu icon, which expands and collapses our navigation pane. The button is configured to slightly darken when hovered over and darken further when pressed. Additionally, when the button is disabled, the icon color is set to the same color as the navigation pane and is used to contrast the current page indicator bar. This configuration is used for all buttons contained within the navigation pane (both the bookmark and page navigation buttons).

The last base element is the current page indicator. This is a lighter-colored (theme color 1, 60% lighter) rectangle tab that clearly indicates what page in the navigation pane is currently being viewed.

Here is the collapsed navigation pane containing the base elements.

Expanded Navigation Pane

The expanded navigation consists of the same base elements, with the addition of a close icon, and a click shield to prevent the user from interacting with the report visuals when the navigation is expanded.

The additional elements of the expanded menu provide the user with multiple methods to collapse the navigation pane. The close (X) button is added as a flyout from the base navigation pane background, so it is easily identifiable.

When the navigation pane is expanded, we want to prevent users from interacting with the report visuals. To achieve this, we use a partially transparent rectangle to serve as a click shield. If the user clicks anywhere on the report page outside of the navigation pane, the navigation pane will collapse returning the user to the collapsed report view.

Navigation Bookmarks

The last base element required for the interactive navigation is creating the required bookmarks to transition between the collapsed and expanded view. This is done by creating two bookmarks to store each of the required report page views, Page1-Default-NavCollapsed and Page1-NavExpanded.

We can now build on these base elements and bring our navigation to life with Power BI buttons and interactive features.


Navigation Interactive Features

The interactive features in the navigation pane consist of two types of buttons: (1) bookmark buttons and (2) page navigation buttons.

Expanding and Collapsing the Navigation Pane

The previous section added the base elements of the navigation pane which included a menu icon on both the collapsed and expanded navigation panes, and a close button and click shield on the expanded navigation screen.

Building the interactive elements of the navigation starts by assigning actions to each of these bookmark buttons, allowing the user to expand and collapse the navigation pane seamlessly.

The action property for each of these buttons is set to a bookmark type, with the appropriate bookmark selected. For example, for the menu icon button on the collapsed menu, the bookmark selected corresponds to the expanded navigation bookmark. This way, when a user selects this button on the collapsed navigation, it expands, revealing the additional information provided on the expanded navigation pane.

Page Navigation Buttons

The last element to add to the report navigation is the report page navigation buttons.

Each report page button is a blank button configured and formatted to meet the report’s requirements. For this report, each page button contains a circular numbered icon to indicate the report page it navigates to. When the navigation is expanded, an additional text element displays the report page title.

At the end of this post, there are details on obtaining templates that implement this report navigational element. The templates are fully customizable, so they will come with the numbered icons and default page titles, but these can simply be updated to match the aesthetic of any reporting needs.


Wrapping Up: Elevating Your Power BI Reports with Interactive Navigation

As Power BI continues to evolve, integrating more engaging and interactive elements into our reports will become crucial for creating dynamic and user-centric reports. The transition from static to interactive reports empowers our users to explore data in a more meaningful and memorable way. By leveraging bookmarks, buttons, and tooltips, we can transform our reports from a simple presentation of data into engaging, intuitive, and powerful analytical tools.

For those eager to implement the navigational element outlined in this post, there are 2-, 3-, 4-, and 5-page templates available for download. Each template has all the functionality built in, requiring only updating the button icons, if necessary, to better align with your reporting needs.

The template package is available here!

You will get individual template files for a 2-, 3-, 4-, and 5-page report provided in the PBIX, PBIT, and PBIP (12 total files) formats! 


Thank you for reading! Stay curious, and until next time, happy learning.

And, remember, as Albert Einstein once said, “Anyone who has never made a mistake has never tried anything new.” So, don’t be afraid of making mistakes, practice makes perfect. Continuously experiment, explore, and challenge yourself with real-world scenarios.

If this sparked your curiosity, keep that spark alive and check back frequently. Better yet, be sure not to miss a post by subscribing! With each new post comes an opportunity to learn something new.