Power BI Lesson 25 – Data Formatting | Dataplexa
Power Query · Lesson 25

Data Formatting in Power Query

Formatting in Power Query is not about how numbers look on screen — it is about shaping values into the correct structure before they reach DAX and visuals. Rounding a number here changes the value stored in the model. Formatting a date here determines whether time intelligence functions work. Padding a text code here determines whether RELATED() finds a match. Every formatting decision in Power Query is a data decision, not a display decision.

Number Formatting and Rounding

Power Query stores numbers with full precision by default. Rounding in Power Query physically changes the stored value — a SUM of rounded values will differ from a rounded SUM. Understand this distinction before choosing where to round.

M function What it does Example
Number.Round Rounds to N decimal places using standard rounding (0.5 rounds up) Number.Round(1234.567, 2) → 1234.57
Number.RoundDown Always rounds toward zero (floor). Never rounds up regardless of the decimal. Number.RoundDown(1234.999, 0) → 1234
Number.RoundUp Always rounds away from zero (ceiling). Any non-zero decimal triggers rounding up. Number.RoundUp(1234.001, 0) → 1235
Number.Round (banker's) Pass RoundingMode.ToEven as third argument. Rounds 0.5 to the nearest even number — reduces cumulative rounding bias in large datasets. Number.Round(2.5, 0, RoundingMode.ToEven) → 2
Number.IntegerDivide Integer division — returns the whole-number quotient, discarding the remainder. Equivalent to floor(a/b). Number.IntegerDivide(17, 5) → 3
Number.Mod Remainder after integer division. Used for bucketing — e.g. grouping order IDs into batches. Number.Mod(17, 5) → 2
// Rounding applied in a Transform Column step
// Transform tab → Rounding → Round (or use Custom Column for full control)

// Round Revenue to 2 decimal places
#"Rounded Revenue" = Table.TransformColumns(
    #"Changed Type",
    {{"Revenue", each Number.Round(_, 2), type number}}
),

// Round Margin % to 1 decimal place
#"Rounded Margin" = Table.TransformColumns(
    #"Rounded Revenue",
    {{"MarginPct", each Number.Round(_, 1), type number}}
),

// Defensive rounding — wrap in try to handle null rows safely
#"Safe Round" = Table.TransformColumns(
    #"Changed Type",
    {{"Revenue", each try Number.Round(_, 2) otherwise null, type number}}
)
Rounding — before and after
OrderID Revenue (raw) Revenue (rounded 2dp) MarginPct (rounded 1dp)
10011234.56781234.5732.4
1002850.1249850.1218.7
1003430.9999431.009.1
1004nullnullnull
Null rows pass through safely with the try wrapper — no errors generated · Values physically stored at 2dp in the model

Date and Time Formatting

Date handling is the most consequential formatting task in Power Query. A column with type Date connects to a Calendar table through a relationship, enables time intelligence DAX functions, and displays correctly on axis scales. A column left as text does none of these things — it is just a string that happens to look like a date.

M function What it returns Example input → output
Date.From Converts a text or datetime value to a Date type "2024-01-15" → #date(2024,1,15)
Date.FromText Converts text to Date with an explicit format string — use when the source format is ambiguous (e.g. "01/02/24" could be Jan 2 or Feb 1) Date.FromText("15/01/2024", [Format="dd/MM/yyyy"])
Date.Year / Month / Day Extracts the year, month number, or day number from a Date value Date.Year(#date(2024,1,15)) → 2024
Date.MonthName Returns the full month name as text ("January", "February" …). Pass a culture for localisation. Date.MonthName(#date(2024,3,1)) → "March"
Date.DayOfWeekName Returns the day of week name ("Monday", "Tuesday" …). Day numbering starts Sunday=0 by default. Date.DayOfWeekName(#date(2024,1,15)) → "Monday"
Date.ToText Converts a Date to a formatted text string. Use for display labels — the result is text, not a date type. Date.ToText(#date(2024,1,15), "MMM yyyy") → "Jan 2024"
Date.AddDays / AddMonths Adds a number of days or months to a date. Useful for calculating due dates, grace periods, or projected dates. Date.AddDays(#date(2024,1,15), 30) → #date(2024,2,14)
// Common date formatting patterns in Custom Column

// Pattern 1 — Safe date parse from ambiguous text
// "01/02/2024" — is this Jan 2 or Feb 1? Specify the format explicitly.
= try Date.FromText([OrderDate], [Format="dd/MM/yyyy"]) otherwise null

// Pattern 2 — YearMonth sort key (text, sorts correctly)
= Text.From(Date.Year([OrderDate])) & "-"
  & Text.PadStart(Text.From(Date.Month([OrderDate])), 2, "0")
// Result: "2024-01", "2024-02" ... "2024-12"

// Pattern 3 — Quarter label
= "Q" & Text.From(Date.QuarterOfYear([OrderDate]))
  & " " & Text.From(Date.Year([OrderDate]))
// Result: "Q1 2024", "Q2 2024"

// Pattern 4 — Fiscal year (April start)
// Fiscal year starts April 1 — months Jan-Mar belong to previous FY
= if Date.Month([OrderDate]) >= 4
  then "FY" & Text.From(Date.Year([OrderDate]))
  else "FY" & Text.From(Date.Year([OrderDate]) - 1)
// Jan 2024 → "FY2023", Apr 2024 → "FY2024"

// Pattern 5 — Days since order (age of order)
= Duration.Days(DateTime.LocalNow() - Date.From([OrderDate]))
Date columns derived from OrderDate = 2024-03-15
YearMonth sort key
"2024-03"
Quarter label
"Q1 2024"
Fiscal year (Apr start)
"FY2023"  — March is before April, so belongs to prior fiscal year
Month name
"March"
Days since order
varies per refresh — dynamic relative to today
YearMonth and Quarter are text labels for display — the OrderDate column (type Date) is what you use for the relationship to the Calendar table and for DAX time intelligence.

Text Padding and Formatting

Text padding ensures fixed-width codes and identifiers are consistent — critical when values are used as join keys between tables or exported to systems that expect a fixed-length field. The two padding functions work symmetrically: PadStart adds characters to the left, PadEnd adds them to the right.

// Text.PadStart(text, totalLength, padCharacter)
// Pads the LEFT side of the text until it reaches totalLength characters

// Pad a numeric ID to always be 6 digits
= Text.PadStart(Text.From([CustomerID]), 6, "0")
// 42     → "000042"
// 1234   → "001234"
// 100000 → "100000"  (already 6 digits — no padding added)

// Text.PadEnd(text, totalLength, padCharacter)
// Pads the RIGHT side of the text until it reaches totalLength characters

// Pad a department code to always be 8 characters
= Text.PadEnd([DeptCode], 8, "-")
// "HR"      → "HR------"
// "FINANCE" → "FINANCE-"
// "ENGINEER" → "ENGINEER"  (already 8 — no padding)

// Combined — pad month number for YearMonth sort key
= Text.From(Date.Year([OrderDate])) & "-"
  & Text.PadStart(Text.From(Date.Month([OrderDate])), 2, "0")
// Month 1  → "2024-01"
// Month 12 → "2024-12"

// Remove padding (strip leading zeros from a code)
= Text.TrimStart([ProductCode], "0")
// "000042" → "42"
// "001234" → "1234"
Padding — before and after
CustomerID (raw) PadStart 6 digits DeptCode (raw) PadEnd 8 chars
42000042HRHR------
1234001234FINANCEFINANCE-
100000100000ENGINEERENGINEER
Values at full length are unchanged — PadStart and PadEnd never truncate · Use padded IDs as join keys to ensure "42" in Orders matches "000042" in Customers

Power Query Formatting vs DAX Formatting vs Visual Formatting

Three layers of formatting exist in a Power BI report, and applying a format at the wrong layer produces the wrong outcome. Understanding where each layer operates prevents common mistakes — especially the assumption that formatting a column in Power Query changes how it looks in a visual.

Layer What it controls Changes the stored value? Example
Power Query The actual data value loaded into the model — type, precision, structure Yes — rounding here changes the number stored Number.Round([Revenue], 2) stores 1234.57 instead of 1234.5678
DAX / Model Column format strings in the model — how the value is displayed in all visuals by default, without changing the stored value No — the stored value is unchanged Setting format "#,##0.00" on Revenue column shows "$1,234.57" in all visuals but the model still stores 1234.5678
Visual Display format for a specific visual only — overrides the model format for that visual but changes nothing else No — display only Setting a card visual to show Revenue in thousands ("$1.2K") does not affect the table visual showing the full value
Practical rule: Format in Power Query only when you need to change the structure or type of the value — rounding to remove floating-point noise, parsing dates from text, padding codes for join key consistency. Format in the model (column properties) when you want a consistent display format across all visuals. Format in the visual only when one specific visual needs a different presentation from all others.

Common Formatting Patterns — Quick Reference

// Number cleanup
= Number.Round([Revenue], 2)                          // 2 decimal places
= Number.Abs([Variance])                              // remove negative sign
= Number.Sign([Profit])                               // -1, 0, or 1
= Number.Sqrt([Area])                                 // square root
= Number.Power([Base], 2)                             // raise to power

// Text cleanup and formatting
= Text.Upper([Status])                                // "SHIPPED"
= Text.Lower([Email])                                 // "alice@example.com"
= Text.Proper([CustomerName])                         // "Alice Brown"
= Text.Trim([City])                                   // removes edge spaces
= Text.Clean([Description])                           // removes control chars
= Text.Length([ProductCode])                          // count characters
= Text.Reverse([Code])                                // "ELEC" → "CELE"
= Text.Select([Phone], {"0".."9"})                    // keep only digits
= Text.Remove([Phone], {"-", " ", "(", ")"})          // remove specific chars

// Date extraction
= Date.Year([OrderDate])                              // 2024
= Date.Month([OrderDate])                             // 3
= Date.Day([OrderDate])                               // 15
= Date.QuarterOfYear([OrderDate])                     // 1
= Date.WeekOfYear([OrderDate])                        // 11
= Date.DayOfWeek([OrderDate])                         // 0=Sun … 6=Sat
= Date.IsLeapYear(Date.Year([OrderDate]))             // true / false

// Duration / age
= Duration.Days(DateTime.LocalNow() - [OrderDate])   // days since order
= Duration.TotalHours([ProcessingTime])               // hours as decimal

Teacher's Note: The single formatting mistake that causes the most downstream pain is leaving date columns as text. A text column that looks like "2024-01-15" will never connect to a Calendar table, will never work with DATEYTD or SAMEPERIODLASTYEAR, and will sort alphabetically rather than chronologically. Whenever you receive a date column, the very first thing you do is check its type icon in the column header — if it shows ABC it is text, and you fix it immediately with Changed Type or Date.FromText before doing anything else in the query.

Practice

Practice 1 of 3

You need to generate a YearMonth column like "2024-03" for sorting on a line chart axis. Single-digit months must be two digits. The M function that pads the left side of a text value with a character until it reaches a given length is Text.___.

Practice 2 of 3

A source file sends dates as "15/01/2024" — day first, then month. To parse this correctly without risk of day and month being swapped, you use Date.___ with an explicit format string specifying "dd/MM/yyyy".

Practice 3 of 3

Rounding Revenue to 2 decimal places in Power Query using Number.Round ___ the stored value in the model — it is not a display-only operation like setting a format string in column properties.

Lesson Quiz

Quiz 1 of 3

You round all Revenue values to 2 decimal places in Power Query. A finance colleague says the grand total is $0.02 off compared to summing the raw values in Excel. Why does this happen?

Quiz 2 of 3

A CustomerID column in the Orders table contains values like 42, 1234, and 100000 as numbers. The Customers table has the same IDs stored as zero-padded text: "000042", "001234", "100000". The relationship between the two tables fails to match any rows. What is the correct fix in Power Query?

Quiz 3 of 3

A date column in your report shows the correct values in the table visual but does not work with the SAMEPERIODLASTYEAR DAX time intelligence function, and the line chart axis shows dates in alphabetical rather than chronological order. What is the most likely cause?

Next up — Lesson 26 covers Custom Columns in depth, including the full M expression language for conditional logic, nested if statements, switch-style patterns, and building calculated columns that would be impossible to create through the point-and-click UI.