Power Query¶
Exercise power-query-annex.pdf · PDF
Quick test, no peeking. Last lesson you ran one script and games_clean appeared. What did that script do to price? And to owners?
If you had to think, good. Now the real question: how could you find out? You open the .sql file and read it. Every decision is written there, in order. That file is the recipe of your cleaning.
Here is the catch. Most people who clean data at work never write SQL. They click. So today you clean the same file by clicking, in Power BI, and you ask one question the whole time: when you click, where does the recipe go?
What you will learn
- What Power Query is, and how to open its editor from Power BI Desktop
- How to read an Applied Steps list and say what each step did to the table
- How to redo the cleaning of this unit by clicking: replace values, change types, add a conditional column
- Which SQL you already know matches each click
- The trap: why the text
9.99is not the same number on every computer (the locale) - When to choose Power Query and when to choose SQL
A kitchen that writes the recipe¶
Power Query is the part of Power BI that prepares data before you use it: it connects to a source, and then cleans and reshapes the table step by step.
You met Power BI in Getting data with Power BI. There you pressed Load and took the data as it came. Power Query is the room you skipped: the one between the source and the report.
Think of two kitchens.
In the SQL kitchen you write the whole recipe first, then the kitchen cooks it. That was games_clean.sql.
In the Power Query kitchen you just cook. But a notebook on the wall writes down every move you make, one line per move. When you finish, the recipe is there, and you never wrote it.
Same dish. And in both kitchens the raw ingredient, the CSV file, is never touched.
flowchart LR
CSV["<b>video_games.csv</b><br>the raw ingredient:<br>never modified"]
subgraph PQ ["Power Query (the kitchen that writes the recipe)"]
direction LR
S1["Source"] --> S2["Promoted Headers"] --> S3["Changed Type"] --> S4["your steps..."]
end
CSV --> S1
S4 --> M["<b>Table in Power BI</b><br>the dish: clean,<br>ready for charts"]
Power BI Desktop only runs on Windows
Power BI Desktop is a Windows program. If you do not have a Windows computer, you can still follow this page: every Power Query step below has a SQL twin that you can run in SQL Lab, and the numbers must match.
The Power Query Editor¶
Open Power BI Desktop and go Home → Get data → Text/CSV. Choose video_games.csv (download it from the Resources page). In the preview window press Transform Data, not Load. A second window opens.
The Power Query Editor is the window where you see a preview of your table and apply transformations to it.
| Part | Where | What it is |
|---|---|---|
| Ribbon | Top | The tabs with every transformation: Home, Transform, Add Column, View |
| Queries pane | Left | One query per table you brought in. Today there is one: video_games |
| Data preview | Centre | A preview of the table after the selected step. Not the whole file |
| Query settings | Right | The name of the query and the Applied Steps list |
Applied Steps is the list of every transformation done to the table, in the order it was done. It is the notebook on the kitchen wall.
Look at it now. It already has three lines and you have not done anything yet:
| Step | What Power Query did, by itself |
|---|---|
| Source | Opened the file |
| Promoted Headers | Took the first row and used it as the column names |
| Changed Type | Guessed a type for every column by looking at the values |
That third line deserves suspicion. In Types and formats the rule was: when you convert, you declare the format, you never let the tool guess. Power Query does guess, and it guesses from the first 200 rows of the file. Look at the small icon to the left of each column name: it shows the type that was chosen. You will correct two of those guesses in a moment.
Look before you clean¶
In Data quality you audited the table with COUNT queries before fixing anything. Power Query has the same habit built in.
On the View tab, tick Column quality and Column distribution. Under each column name a small bar appears, with the share of values that are Valid, Error and Empty.
Now look at metascore. In SQL you found that 23,838 of the 26,688 rows have no Metascore: 89%. Does Power Query agree?
Before you trust what you read, look at the bottom-left corner of the window. It says Column profiling based on top 1000 rows. Click that message and switch to Column profiling based on entire data set.
Careful: the first reading was a sample
By default Power Query profiles only the first 1,000 rows. Your file has 26,688. A percentage computed from 4% of the table can be far from the truth, especially when the file is sorted. Always switch to the entire data set before you write a number down.
There is a second surprise, and it is the most important idea of this page. In this CSV, "empty" is not empty. It is the two letters NA:
47,Half-Life 2: Deathmatch,"Nov 1, 2004",4.99,"5,000,000 .. 10,000,000",Valve,Valve,0,0,NA
When you uploaded the file to Superset, the upload turned every NA into a real NULL, and you never saw the letters. Power Query does not assume that. To Power Query, NA is a piece of text like any other until you say otherwise. Telling it so is your first step.
The same cleaning, twice¶
Six steps. For each one you get the clicks in Power Query, the SQL twin on the table games, and one number that proves both did the same thing. After every click, look at Applied Steps: a new line has appeared.
Step 1 — NA means empty. Select the column price. Transform → Replace Values: Value to find NA, Replace with null.
There is nothing to write in SQL: the upload already did it. Count the result instead.
SELECT COUNT(*) - COUNT(price) AS empty_prices FROM games;
Answer: 3,095 prices are empty.
Step 2 — price is a number, written the American way. Right-click the header of price → Change Type → Using Locale... → Data type: Decimal Number, Locale: English (United States).
In SQL, price is already a number. But ask yourself who decided that 9.99 uses a dot for the decimals. The next section is about that question.
Step 3 — is_free, then fill. Two moves, in this order.
First, Add Column → Conditional Column: new column name is_free; if price equals null then true, else false.
Then, Replace Values on price: null → 0.
SELECT COUNT(*) AS free_games
FROM (SELECT COALESCE(price, 0) AS price, (price IS NULL) AS is_free FROM games) AS t
WHERE is_free;
Answer: 3,095 rows are flagged as free. The same number as step 1, as it must be.
Step 4 — owners becomes a number. The column holds text such as 10,000,000 .. 20,000,000. Select it, then:
- Transform → Extract → Text Before Delimiter, delimiter
.. - Replace Values:
,→ nothing - Replace Values again, with Advanced options → Replace using special characters → Non-breaking Space → nothing
- Change the type to Whole Number
The third bullet is the invisible space you fought in Units and duplicates: it looks like a space and is not one.
SELECT MAX(REGEXP_REPLACE(SPLIT_PART(owners, '..', 1), '[^0-9]', '', 'g')::bigint) AS max_owners_min
FROM games;
Answer: 100,000,000. That is Dota 2.
Step 5 — release_date becomes a date. Right-click the header → Change Type → Using Locale... → Date, English (United States). Then look at Column quality: is there any Error?
In SQL this was the CASE with two TO_DATE calls. Count how many rows fit each format:
SELECT COUNT(*) FILTER (WHERE release_date ~ '^[A-Z][a-z]{2} [0-9]{1,2}, [0-9]{4}$') AS full_dates,
COUNT(*) FILTER (WHERE release_date ~ '^[A-Z][a-z]{2} [0-9]{4}$') AS month_only
FROM games;
Answer: 26,646 full dates such as Nov 16, 2004, and 41 with only a month, such as Jun 2009. That leaves exactly one row that fits neither: 8 Oct, 2014. Find out what Power Query does with those 42 odd rows. Whatever it does, Column quality will tell you.
Step 6 — a band and a filter. Add Column → Conditional Column: name price_band, with one branch per band (Free, Under 5, 5 to 15, 15 to 30, 30 or more). Then click the filter arrow on the header of game and untick NA.
In SQL: the CASE WHEN from Categorisation and WHERE game IS NOT NULL.
SELECT COUNT(*) FROM games WHERE game IS NOT NULL;
Answer: 26,685 rows. The three games without a name are gone.
To finish, press Home → Close & Apply. Until now you were working on a preview. Now the steps run on the whole file and the table lands in Power BI.
Your Applied Steps list now reads like this (repeated steps get a number):
| Applied step | What it did | SQL twin |
|---|---|---|
| Source, Promoted Headers, Changed Type | Opened the file, named the columns, guessed the types | The CSV upload |
| Replaced Value | NA → null in price |
Done by the upload |
| Changed Type with Locale | price as a decimal number, read the American way |
The column type |
| Added Conditional Column | is_free |
(price IS NULL) AS is_free |
| Replaced Value1 | null → 0 in price |
COALESCE(price, 0) |
| Extracted Text Before Delimiter | The lower end of owners |
SPLIT_PART(owners, '..', 1) |
| Replaced Value2, Replaced Value3, Changed Type1 | Commas and odd spaces out, then a whole number | REGEXP_REPLACE(..., '[^0-9]', '', 'g')::bigint |
| Changed Type with Locale1 | release_date as a date |
TO_DATE(...) |
| Added Conditional Column1 | price_band |
CASE WHEN ... END |
| Filtered Rows | Rows whose game is NA removed |
WHERE game IS NOT NULL |
Step names are the defaults
These are the names Power Query gives a step when you do not rename it. Yours may differ slightly depending on the version and on the order in which you clicked. What matters is that you can read the list and say what each line did.
The order is the logic¶
Go back to step 3 and swap its two moves. First replace null with 0, then add is_free.
What happens? Every game is "not free". When the conditional column looks for null, there is none left: you already overwrote the evidence.
| Order | price when is_free is computed |
is_free |
|---|---|---|
| Flag first, then fill | Still null for free games |
true for 3,095 rows |
| Fill first, then flag | Already 0, never null |
false for every row |
In a recorded recipe, the order of the lines is the logic. SQL has the same rule in a different shape: in games_clean.sql, is_free reads the original price, not the filled one.
The same reasoning explains step 1. If you skip it, no price is ever null, so is_free is also false everywhere.
A number is not a number: the locale¶
A locale is the set of regional conventions a computer uses to read and write values: which character marks the decimals, in which order a date is written, how the months are called.
Here is a sentence that sounds obviously true: "9.99 is 9.99 on every computer." It is false when the number arrives as text, which is what a CSV is.
| The text in the file | Read with English (United States) | Read with Spanish (Spain) |
|---|---|---|
9.99 |
9.99: the dot is the decimal mark | Not 9.99: the dot is the thousands separator and the comma is the decimal mark |
Nov 16, 2004 |
16 November 2004 | May fail: Spanish dates are written day, month, year |
Power Query Desktop reads text using the regional format of your Windows. On a PC set to Spanish, the text 9.99 can turn into 999 or into an error. Nothing warns you. The column simply fills with wrong numbers.
Using Locale tells the kitchen which country the ingredient comes from. This file was written in the United States, so dates look like Nov 16, 2004 and decimals use a dot. That is why steps 2 and 5 use Change Type → Using Locale... and not a plain Change Type.
You have seen this bug before, from the other side
In Types and formats you repaired numbers written the Spanish way, 1.234,50, so that a database expecting a point could read them. This is the same bug, mirrored: an American number read by a Spanish PC. The rule is one: a number written as text has a nationality, and you must know it.
Rewind, and read the code¶
Back in the editor (Home → Transform data), click an early line in Applied Steps. The preview travels back to that moment: the table as it was after that step. Click the last line and you are back to now. The cross next to a step deletes it. That is your undo.
Now open View → Advanced Editor. A window full of code appears.
M is the language in which Power Query writes down your steps. Every click you made added one line of M.
The kitchen was writing the recipe all along. Clicking did not replace the code. It wrote the code for you.
Careful: no code does not mean no mistakes
The mistakes do not disappear. They move: to the order of the steps and to the locale. Both are invisible unless you read Applied Steps. Reading that list is to Power Query what reading the script is to SQL.
Power Query or SQL?¶
| Power Query | SQL (SQL Lab) | |
|---|---|---|
| How you work | Click on a column, see the result at once | Write, run, read the result |
| Where the recipe is | Applied Steps, with M code behind it | The .sql script |
| The original | Untouched. The steps replay on every Refresh | Untouched. games stays, games_clean is rebuilt |
| Where it runs | On your PC, inside the .pbix file |
On the database server |
| Empty values | NA is text until you say otherwise |
Already NULL after the upload |
| What you need | Windows and Power BI Desktop | A browser |
Imagine your boss sends a new CSV every Monday, with the same columns. Which tool do you choose, and what do you do each Monday?
Either one. In Power Query you press Refresh and every step runs again on the new file. In SQL you run the script again. What matters is not clicks versus code. It is that the recipe exists and can be replayed.
The only wrong answer is the third kitchen: opening the CSV in a spreadsheet and fixing cells by hand. It leaves no recipe at all. Next Monday you start from zero, and nobody can check what you did.
Careful: Power Query did not change your CSV
Never. It reads the file and replays the steps on a copy in memory. Open video_games.csv in a text editor after all your work: every NA is still there.
Key takeaways¶
- Power Query is the data-preparation part of Power BI. You work in the Power Query Editor, opened with Transform Data.
- Applied Steps records every transformation, in order. It is the recipe, and behind it there is code in a language called M.
- Every click has a SQL twin: Replace Values ↔
COALESCE, Conditional Column ↔CASE WHEN, Text Before Delimiter ↔SPLIT_PART, Filtered Rows ↔WHERE. - The order of the steps is the logic: flag the empty prices before you fill them, or the flag is
falsefor every row. - A number or a date written as text has a locale. Use Change Type → Using Locale... to say where the file comes from.
- Profiling reads only the first 1,000 rows unless you switch to the entire data set, and
NAis text until you replace it withnull. - Clicks or code is not the question. The question is whether the recipe exists and can be replayed.
Quick check¶
1. What is the Applied Steps list?
- The list of tables loaded into Power BI
- The record of every transformation done to the table, in order
- The list of errors found in each column
- A copy of the original CSV file
Applied Steps is the notebook on the kitchen wall: one line per transformation, in the order you made them. You can click a line to see the table at that moment, or delete a line to undo it.
2. A classmate's list reads: Source, Promoted Headers, Changed Type, Replaced Value, Added Conditional Column. Which steps did Power Query add by itself?
- None: every step comes from a click
- Only Source
- Source, Promoted Headers and Changed Type
- Replaced Value and Added Conditional Column
Those three appear as soon as the CSV is opened: the file is read, the first row becomes the column names, and a type is guessed for each column. Everything after them was done by the person.
3. That same classmate's is_free column is false for every game. Pick the two likely causes.
- The empty prices were replaced with 0 before the conditional column looked for
null - The text
NAwas never replaced withnull, so no price was evernull - Power Query cannot create true/false columns
- The CSV file was modified by an earlier step
In both cases the conditional column finds no null to flag. Either the evidence was overwritten too early, or the letters NA were still plain text. The order of the steps is the logic. And the CSV is never modified.
4. Why does the page use Change Type → Using Locale... for price, and not a plain Change Type?
- Because a plain Change Type deletes the rows it cannot convert
- Because prices must always be stored as text
- Because the file was written with US conventions, and a PC set to another region may read the dot differently
- Because Using Locale makes the conversion faster
A number written as text follows the conventions of the place it was written. On a PC set to Spanish, the dot in 9.99 is a thousands separator. Using Locale tells Power Query to read the column as English (United States), whatever the PC is set to.
5. Right after opening the file, Column quality shows a percentage of empty values for metascore. Why should you not write that number down yet?
- Because by default it is computed from the first 1,000 rows only, and
NAstill counts as text - Because Column quality only works on number columns
- Because Power Query rounds every percentage to the nearest ten
- Because the file has not been saved yet
Profiling starts on the top 1,000 rows of a 26,688-row file: click the message in the bottom-left corner to switch to the entire data set. And until you replace it, NA is text, not an empty value.
6. Which SQL does the same job as Transform → Extract → Text Before Delimiter with the delimiter ..?
-
COALESCE(owners, '..') -
SPLIT_PART(owners, '..', 1) -
WHERE owners IS NOT NULL -
TO_DATE(owners, '..')
Both keep the text before the first ..: from 10,000,000 .. 20,000,000 you get the lower end of the range. COALESCE fills empty values, WHERE filters rows and TO_DATE converts text to a date.
7. A shop sends you a new sales CSV every Monday with the same columns. Which way of working is the one to avoid?
- Power Query: press Refresh so the recorded steps run on the new file
- SQL: run the same script again on the new table
- Opening the CSV in a spreadsheet and fixing the cells by hand each week
- None of them: all three are equally safe
Fixing cells by hand leaves no recipe: nobody can see what was changed, and next Monday you start again from zero. Power Query and SQL are both fine because the steps are written down and can be replayed.
The table is clean, in two kitchens. Nobody has asked it anything yet. Next: Exploratory statistics, where games_clean finally answers a simple question with a surprising answer: how much does a game cost?