MICROSOFT POWER BI • DATA PREPARATION WITH POWER QUERY

Basic Power Query Transformations — Filter rows, remove columns, split columns, and replace values

Master the four foundational data-shaping operations that transform raw datasets into analysis-ready tables inside Power BI.

Historical Context & Motivation

Before the rise of self-service business intelligence platforms, data preparation was almost exclusively the domain of database administrators and ETL engineers who wrote complex SQL scripts, stored procedures, and SSIS packages to cleanse, reshape, and load enterprise data into warehouses. Analysts who needed a quick column split or a filtered subset of rows had to submit change requests or resort to fragile spreadsheet macros. This bottleneck—where the people closest to the business questions were furthest from the data-shaping tools—motivated Microsoft to develop Power Query, a graphical yet code-backed transformation engine that places ETL capabilities directly in the hands of analysts and data scientists. Understanding the lineage of this tool helps contextualize why its design favors composable, step-by-step transformations rather than monolithic scripts.

2010
Power Pivot Launches
Microsoft ships Power Pivot as an Excel add-in, introducing the xVelocity in-memory engine and the DAX formula language. While it solved the modeling and calculation layer, it exposed a glaring gap: there was no user-friendly way to clean and reshape data before it reached the model.
2013
Power Query Preview for Excel
Microsoft releases "Data Explorer" (soon renamed Power Query) as a free Excel add-in. It introduces the M language (formally, Power Query Formula Language) and a ribbon-driven GUI for filtering rows, removing columns, and performing dozens of other transformations—all recorded as reproducible query steps.
2015
Power BI Desktop Ships
Power BI Desktop bundles Power Query as its built-in "Get Data & Transform" experience, making it the default entry point for every dataset. The editor gains a dedicated UI pane called the Power Query Editor, cementing filter, remove, split, and replace as the four most-used ribbon operations.
2018–Present
Dataflows & Power Query Online
Microsoft extends Power Query into the cloud via Power BI Dataflows and integrates the engine into Azure Data Factory, Microsoft Fabric, and Power Apps. The same M expressions and GUI transformations now operate at enterprise scale, validating the composable-step paradigm from 2013.

The central question these developments address is straightforward yet consequential: how can an analyst reproducibly clean messy, real-world data without writing raw SQL or procedural code, while still maintaining an auditable, editable transformation log? The four operations we examine in this lesson—filtering rows, removing columns, splitting columns, and replacing values—form the irreducible core of that workflow.

Core Principles & Definitions

Power Query's transformation model rests on a few design principles borrowed from functional programming: every transformation is a pure function that takes a table (or a scalar) as input and returns a new table (or scalar) as output, with no side effects. The editor records each GUI action as an Applied Step—an M-language expression appended to an ordered list. Because each step references the output of the previous step by a let … in binding, the entire pipeline is deterministic and replayable. This functional, step-chain architecture makes Power Query transformations composable, auditable, and safe to reorder or delete without the cascading breakage typical of imperative ETL scripts.

1

Filter Rows

A row-level predicate that retains only records satisfying a Boolean condition—analogous to SQL's WHERE clause. Power Query generates Table.SelectRows(source, each [Column] = value) in M.
2

Remove Columns

A projection that eliminates unneeded fields, reducing table width and memory footprint. Equivalent to omitting columns from a SQL SELECT list. M function: Table.RemoveColumns(prev, {"Col"}).
3

Split Column

Decomposes a single column into two or more columns by delimiter, character count, or pattern. Normalizes denormalized text fields. M function: Table.SplitColumn(prev, "Col", Splitter.SplitTextByDelimiter(",")).
4

Replace Values

A cell-level substitution that replaces every occurrence of a search value with a new value within a specified column. Useful for standardizing labels, fixing typos, and handling nulls. M function: Table.ReplaceValue(prev, "old", "new", Replacer.ReplaceText, {"Col"}).
KEY TAKEAWAY
Think of a Power Query pipeline as a Git commit history for your data: each applied step is a discrete, reversible commit that transforms the table's state. You can inspect, reorder, or delete any step, and the downstream steps automatically recompute—just as rebasing a branch replays commits on a new base. This composability is what separates Power Query from ad-hoc spreadsheet edits where every change is destructive and untracked.

Visual Explanation — The Transformation Pipeline

The diagram traces a raw table through four sequential Power Query steps. Each colored box represents one applied step that mutates the table state: Filter Rows reduces cardinality from 8 to 5, Remove Columns drops two fields, Split Column decomposes City-State into City and State, and Replace Values eliminates nulls. The bottom bar shows the corresponding entries in Power Query's Applied Steps pane.

Notice that the pipeline is strictly linear: each step's output becomes the next step's input, and the Applied Steps pane on the right side of the Power Query Editor mirrors this chain exactly. If you click on any intermediate step, the data preview refreshes to show the table's state at that point—effectively a time-travel debugger for your data pipeline. This deterministic, inspectable design is what makes Power Query transformations so valuable for reproducible data engineering, and it maps directly onto the functional-programming concept of function composition, where f(g(h(x))) is evaluated inside-out with each function producing an immutable intermediate result.

How It Works — The M Language Under the Hood

Every GUI click in the Power Query Editor generates an M expression (Power Query Formula Language). M is a functional, dynamically typed, case-sensitive language with lazy evaluation semantics—expressions are only evaluated when their results are needed. Understanding the generated M is essential for debugging, parameterizing queries, and performing transformations that go beyond the GUI's capabilities. Below we dissect the M expression that each of our four transformations produces.

Filter Rows — Table.SelectRows

M EXPRESSION — FILTER ROWS
= Table.SelectRows(PreviousStep, each [Status] = "Active")
The each keyword is syntactic sugar for an anonymous function (_) => _[Status] = "Active". The underscore _ represents the current row record. The function returns true or false, and only rows evaluating to true are retained.

Remove Columns — Table.RemoveColumns

M EXPRESSION — REMOVE COLUMNS
= Table.RemoveColumns(FilteredRows, {"Notes", "Status"})
The second argument is a list (denoted by curly braces in M) of column names to drop. The complementary function Table.SelectColumns retains only the listed columns instead—useful when you need a whitelist rather than a blacklist approach.

Split Column — Table.SplitColumn

M EXPRESSION — SPLIT COLUMN
= Table.SplitColumn(RemovedCols, "City-State", Splitter.SplitTextByDelimiter("-", QuoteStyle.None), {"City", "State"})
The Splitter.SplitTextByDelimiter factory returns a function that tokenizes each cell's text at the specified delimiter. The final list argument names the resulting columns. Alternative splitters include Splitter.SplitTextByEachDelimiter, Splitter.SplitTextByPositions, and Splitter.SplitTextByCharacterTransition.

Replace Values — Table.ReplaceValue

M EXPRESSION — REPLACE VALUES
= Table.ReplaceValue(SplitCol, null, "Unknown", Replacer.ReplaceValue, {"Name"})
The fourth argument specifies the replacement strategy. Replacer.ReplaceValue performs an exact match (suitable for null, numeric, or exact-text replacements), while Replacer.ReplaceText performs a substring match (useful for fixing partial text like replacing "NY" with "New York" inside a longer string).
💡 The let…in Pattern
Power Query wraps all steps inside a let block. Each step is a named binding: let Source = Csv.Document(...), Filtered = Table.SelectRows(Source, ...), Removed = Table.RemoveColumns(Filtered, ...) in Removed. The in clause specifies which binding is the query's final output. If you have experience with Haskell's let…in or F#'s pipelines, M's semantics will feel familiar.

Detailed Breakdown — Transformation Options & Variants

Each of the four core transformations has multiple variants exposed through the Power Query Editor's ribbon and context menus. Choosing the right variant depends on data types, data quality, and downstream modeling requirements. The diagram below classifies these variants in a decision-tree format, and the table that follows provides a quick-reference comparison.

A decision tree showing the main variants of each transformation. The legend box in the lower-right summarizes when to reach for each operation, and the performance note at the bottom explains why step ordering matters—a direct parallel to query optimization in relational databases.
Comparison of the four transformations with their M functions, SQL equivalents, and structural effects.
TransformationM FunctionSQL AnalogyAffects
Filter RowsTable.SelectRowsWHERERow count (cardinality)
Remove ColumnsTable.RemoveColumnsOmitting from SELECTColumn count (degree)
Split ColumnTable.SplitColumnSUBSTRING / CHARINDEXColumn count (increases degree)
Replace ValuesTable.ReplaceValueCASE WHEN / REPLACECell values (no structural change)

Worked Example — Cleaning a Sales Dataset

Suppose you receive a CSV export from a legacy CRM system containing 12 columns and 50,000 rows of sales transactions. The dataset has several quality issues: inactive customer records are mixed with active ones, there are columns like "InternalAuditCode" and "LegacyID" that are irrelevant to your analysis, the "Product-Region" column contains concatenated values like "Widget-EMEA", and the "PaymentMethod" column uses inconsistent labels ("CC" instead of "Credit Card"). We will walk through all four transformations to produce a clean, analysis-ready table.

End-to-End Data Cleaning Pipeline
1
Step 1 — Filter Rows to Active CustomersIn the Power Query Editor, click the dropdown arrow on the CustomerStatus column header. Uncheck "Inactive" and "Suspended" from the value checklist, leaving only "Active" selected. Alternatively, use Home → Remove Rows → Remove Blank Rows to first strip rows with null keys. The generated M is: = Table.SelectRows(Source, each [CustomerStatus] = "Active"). This reduces the dataset from 50,000 rows to approximately 38,000 rows, depending on data.
50,000 → ~38,000 rows (inactive/suspended records removed)
2
Step 2 — Remove Irrelevant ColumnsHold Ctrl and click on the column headers for "InternalAuditCode", "LegacyID", "CustomerStatus" (no longer needed since all remaining rows are Active), and "ModifiedTimestamp". Right-click and select Remove Columns. The M expression is: = Table.RemoveColumns(FilteredRows, {"InternalAuditCode", "LegacyID", "CustomerStatus", "ModifiedTimestamp"}). This trims the table from 12 columns to 8, reducing memory consumption and downstream model complexity.
12 → 8 columns (4 irrelevant fields dropped)
3
Step 3 — Split the Product-Region ColumnSelect the "Product-Region" column. Navigate to Transform → Split Column → By Delimiter. In the dialog, set the delimiter to the custom value "-" and choose "At the left-most delimiter" (since product names themselves might contain hyphens, we want only the first split). Name the new columns "Product" and "Region". The generated M is: = Table.SplitColumn(RemovedColumns, "Product-Region", Splitter.SplitTextByEachDelimiter({"-"}, QuoteStyle.None, false), {"Product", "Region"}). After splitting, set the data types of both new columns to Text.
8 → 9 columns ("Product-Region" becomes "Product" and "Region")
4
Step 4 — Replace Inconsistent Payment LabelsSelect the "PaymentMethod" column. Go to Transform → Replace Values. Enter "CC" in the "Value to Find" box and "Credit Card" in "Replace With". Click OK. Repeat for "BT" → "Bank Transfer" and "PP" → "PayPal". Each replacement generates a separate Applied Step. The first M expression is: = Table.ReplaceValue(SplitColumn, "CC", "Credit Card", Replacer.ReplaceValue, {"PaymentMethod"}). For efficiency, you could consolidate these into a single custom M step using List.Accumulate, but the GUI approach is perfectly acceptable for three replacements.
All abbreviated payment codes standardized to full descriptive labels
5
Step 5 — Verify and Close & ApplyReview the Applied Steps pane to confirm all four transformations appear in the correct order. Click on each step to inspect intermediate results. Verify data types are correct (especially after the split). Finally, click Close & Apply in the Home ribbon to load the cleaned table into the Power BI data model. The entire pipeline will replay automatically on every future data refresh.
Clean table: 9 columns × ~38,000 rows, fully reproducible on refresh

Strengths, Limitations & GUI vs. M Trade-offs

Power Query's GUI-first approach to these four transformations offers significant productivity advantages, but it also introduces constraints that become apparent as transformations grow in complexity. The table below compares the GUI workflow against hand-written M code across several dimensions that matter in production data pipelines.

GUI vs. hand-written M comparison for the four basic transformations
DimensionGUI ApproachHand-Written M
Learning CurveMinimal — point-and-click interface with immediate data previewModerate — requires understanding M syntax, types, and lazy evaluation
ExpressivenessLimited to pre-built ribbon options; complex predicates require switching to Advanced EditorFull M language available: custom functions, recursion, error handling, parameterization
AuditabilityGood — Applied Steps pane provides step-by-step inspectionExcellent — code is version-controllable and can be reviewed in PRs
Batch ReplacementsOne replace at a time — generates N steps for N replacementsCan use List.Accumulate or a lookup table for bulk replacements in a single step
Schema ResilienceFragile if source columns are renamed; errors break downstream stepsCan use try…otherwise and dynamic column detection for robust pipelines
Query FoldingSupported for standard operations on foldable sources (SQL Server, OData)Same folding rules apply; custom M functions may break the fold
KEY TAKEAWAY
The GUI is your REPL; the M code is your source of truth. Use the ribbon to prototype transformations interactively—watch the data preview change in real time—then open the Advanced Editor to refine, parameterize, or consolidate the generated M. This mirrors the workflow many CS practitioners follow when prototyping in a Jupyter notebook and then refactoring into production Python modules. The key limitation to watch for is query folding: if your data source is a relational database, Power Query can translate M steps into native SQL and push computation to the server. Certain custom M expressions break this fold, forcing Power Query to pull all data locally before filtering—a critical performance concern at scale.

Connection to Advanced Power Query & Data Engineering

The four transformations covered in this lesson are the building blocks for a much richer set of Power Query capabilities. As your data pipelines mature—handling multiple sources, slowly changing dimensions, or incremental refresh patterns—you will encounter advanced M constructs that extend these basics. The table below maps each basic operation to its advanced counterpart, giving you a roadmap for continued learning.

Basic to advanced transformation roadmap
Basic OperationAdvanced ExtensionUse Case
Filter RowsParameterized filters (Table.SelectRows with dynamic values from query parameters or other tables)Incremental refresh: filter to only the last N days of data, where N is a parameter
Remove ColumnsDynamic column selection (Table.SelectColumns with programmatically generated column lists via Table.ColumnNames)Handling schemas that change over time (e.g., new columns added monthly to a SharePoint list)
Split ColumnCustom parsing with Text.BetweenDelimiters, regex-like Splitter.SplitTextByCharacterTransition, and JSON/XML record expansionParsing semi-structured API responses, log files, or embedded JSON columns
Replace ValuesConditional columns (Table.AddColumn with if…then…else), lookup-table-driven replacements via Table.NestedJoinMapping codes to descriptive labels maintained in a separate reference table—a standard data warehousing pattern

Beyond Power BI Desktop, Power Query now runs in Microsoft Fabric Dataflows Gen2 and Azure Data Factory Wrangling Data Flows, where the same M transformations execute at cloud scale against data lake storage. If you continue into data engineering or MLOps, you will find that the conceptual patterns—predicate-based row filtering, projection, string decomposition, and value normalization—reappear in every ETL framework, from Apache Spark's DataFrame API to dbt's SQL transformations. Mastering them here in Power Query gives you a transferable mental model for data preparation regardless of the toolchain.

Practice Problems

PROBLEM 1CONCEPTUAL
Explain why Power Query records each transformation as a separate "Applied Step" rather than modifying the data in place. What software engineering principle does this design embody, and what practical advantage does it give an analyst debugging a data issue?
PROBLEM 2BASIC
You have a table with columns: OrderID, CustomerName, OrderDate, Amount, InternalNotes, and ArchiveFlag. Write the M expression to (a) filter to rows where ArchiveFlag = false, and (b) remove the InternalNotes and ArchiveFlag columns.
PROBLEM 3INTERMEDIATE
A column named "FullAddress" contains values like "123 Main St|Springfield|IL|62704" (pipe-delimited). You need to split this into four columns: StreetAddress, City, State, and ZipCode. Write the M expression and explain which Splitter function you would use and why you would choose "At each occurrence of the delimiter" rather than "At the left-most delimiter."
PROBLEM 4APPLIED
You are building a Power BI report over a SQL Server database with 10 million rows. Your pipeline filters to the current year, removes 8 of 20 columns, splits a concatenated column, and replaces null values. Your colleague notices the report refresh takes 15 minutes. Using your knowledge of query folding, diagnose what might be causing the slowdown and propose an optimized step ordering.
PROBLEM 5CRITICAL THINKING
Consider a scenario where a source CSV file's schema is unstable: new columns are occasionally added by the data vendor, and existing columns are sometimes renamed. Using Remove Columns (blacklist) would break when a listed column disappears; using Select Columns (whitelist) would silently ignore new columns that might be valuable. Design an M-based strategy that (a) always retains a set of known required columns, (b) flags but does not break when those columns are missing, and (c) passes through any new, unknown columns. Sketch the M logic and justify your approach.

Summary

Power Query's four foundational transformations provide a complete toolkit for reshaping raw data into analysis-ready tables. Filter Rows uses Table.SelectRows to apply row-level predicates that reduce cardinality, analogous to a SQL WHERE clause. Remove Columns employs Table.RemoveColumns to project away unneeded fields, minimizing memory footprint and model complexity. Split Column leverages Table.SplitColumn with various Splitter functions to decompose concatenated text into properly normalized fields. Replace Values applies Table.ReplaceValue for cell-level substitutions that standardize labels, fix typos, and handle nulls.

All four transformations are recorded as Applied Steps in the M language, forming a deterministic, inspectable, and replayable pipeline modeled on functional composition. Step ordering matters for performance: filter and remove early to minimize data volumes before expensive operations like splits and replacements, especially when query folding can push computation to a source database. These four operations form the irreducible core of data preparation in Power BI and transfer directly to advanced M patterns, cloud-scale Dataflows, and the broader discipline of data engineering.

Varsity Tutors • Microsoft Power BI • Basic Power Query Transformations — Filter rows, remove columns, split columns, and replace values