Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Find everything you need to get certified on Fabric—skills challenges, live sessions, exam prep, role guidance, and more. Get started

Reply
dpwhitey1
Frequent Visitor

How to pivot Multiple Columns

Hello

 

Is there an easy way to use Power Query to pivot data with a Unique ID spread over multiple rows, into a spreadsheet with one row per ID?

eg I have 22 milestones below, each milestone has 7 values columns.

How do I pivot the milestone column 7 times to create 154 columns in each unique ID row?

 

dpwhitey1_0-1678454867349.png

 

eg I need it to display like this, as this data needs to be merged with an existing legacy excel report

 

dpwhitey1_1-1678455766500.png

 

Do I need to duplicate the milestone column 7 times, then pivot each milestone duplicate against each value column?

If so is there an easy way to rename each new column to "Milestone 0 Active", "Milestone 1 Active" etc without having to manually rename 154 columns?

 

dpwhitey1_2-1678456119452.png

 

 

Thanks very much

1 ACCEPTED SOLUTION

Assuming all of the ID's have all of the Milestone's listed in proper order, You can:

  • Group by ID
  • For each sub-table
    • Select the ID and Milestone columns and UNPIVOT other columns
    • Merge the Milestone and Attribute column => the new column headers
    • Remove the original columns and more the new merged column to Column2
    • Transpose the resultant table
      • Remove the top row and make the next row the new headers
  • Re-Expand the grouped table

In the code below, change line 2 to reflect your actual data source

let
    Source = Excel.CurrentWorkbook(){[Name="Table7"]}[Content],
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"ID", Int64.Type}, {"Milestone", type text}, {"Actual Completion Date", type date}, {"Actual Start Date", type date}, {"Active", type text}, {"Activity Status", type text}}),
    #"Grouped Rows" = Table.Group(#"Changed Type", {"ID"}, {
        {"Transpose", (t)=>
            let 
                #"Unpivoted Other Columns" = Table.UnpivotOtherColumns(t, {"ID", "Milestone"}, "Attribute", "Value"),
                #"Inserted Merged Column" = 
                    Table.AddColumn(
                        #"Unpivoted Other Columns", "Merged", each Text.Combine({[Milestone], [Attribute]}, " - "), type text),
                #"Removed Columns" = Table.RemoveColumns(#"Inserted Merged Column",{"Milestone", "Attribute"}),
                #"Reordered Columns" = Table.ReorderColumns(#"Removed Columns",{"ID", "Merged", "Value"}),
                #"Transposed Table" = Table.Transpose(#"Reordered Columns"),
                #"Removed Top Rows" = Table.Skip(#"Transposed Table",1),
                #"Promoted Headers" = Table.PromoteHeaders(#"Removed Top Rows", [PromoteAllScalars=true])
            in #"Promoted Headers"}}),

    #"Column Names" = Table.ColumnNames(#"Grouped Rows"[Transpose]{0}),
    #"Data Types" = List.Transform(#"Column Names", each if Text.EndsWith(_, "Date") then {_, type date} else {_, type text}), 
    #"Expanded Transpose" = Table.ExpandTableColumn(#"Grouped Rows", "Transpose", #"Column Names"),
    #"Set Data Types" = Table.TransformColumnTypes(#"Expanded Transpose", #"Data Types")
in
    #"Set Data Types"

ronrsnfld_0-1678654944284.png

 

 

 

View solution in original post

6 REPLIES 6
ronrsnfld
Super User
Super User

Please provide your data sample as text which can be copy/pasted. Much simpler than trying to work from a screenshot.

Here is some sample data

 

IDMilestone Actual Completion DateActual Start DateActiveActivity Status
105610934Milestone 02023-02-222023-02-22NoCompleted
105610934Milestone 1  NoNot Started
105610934Milestone 1.12023-02-26 NoCompleted
105610934Milestone 1.2 2023-02-26YesIn Progress
105610934Milestone 1.3  NoNot Started
105610934Milestone 1.4 2023-02-26YesIn Progress
105610934Milestone 2  NoNot Started
105610934Milestone 2.1  NoNot Started
105610934Milestone 2.2  NoNot Started
105610934Milestone 2.3  NoNot Started
105610934Milestone 2.4  NoNot Started
105610934Milestone 2.5  NoNot Started
105610934Milestone 2.6  NoNot Started
105610934Milestone 3  NoNot Started
105610934Milestone 3.1  NoNot Started
105610934Milestone 3.2  NoNot Started
105610934Milestone 3.3  NoNot Started
105610934Milestone 3.4  NoNot Started
105610934Milestone 3.5  NoNot Started
105610934Milestone 3.6  NoNot Started
105610934Milestone 4  NoNot Started
105610934Milestone 4.1  NoNot Started
105728888Milestone 0 2023-02-23YesIn Progress
105728888Milestone 1  NoNot Started
105728888Milestone 1.1  NoNot Started
105728888Milestone 1.2  NoNot Started
105728888Milestone 1.3  NoNot Started
105728888Milestone 1.4  NoNot Started
105728888Milestone 2  NoNot Started
105728888Milestone 2.1  NoNot Started
105728888Milestone 2.2  NoNot Started
105728888Milestone 2.3  NoNot Started
105728888Milestone 2.4  NoNot Started
105728888Milestone 2.5  NoNot Started
105728888Milestone 2.6  NoNot Started
105728888Milestone 3  NoNot Started
105728888Milestone 3.1  NoNot Started
105728888Milestone 3.2  NoNot Started
105728888Milestone 3.3  NoNot Started
105728888Milestone 3.4  NoNot Started
105728888Milestone 3.5  NoNot Started
105728888Milestone 3.6  NoNot Started
105728888Milestone 4  NoNot Started
105728888Milestone 4.1  NoNot Started
105744750Milestone 02023-02-232023-02-23NoCompleted
105744750Milestone 1  NoNot Started
105744750Milestone 1.12023-02-23 NoCompleted
105744750Milestone 1.22023-02-232023-02-23YesIn Progress
105744750Milestone 1.3  NoNot Started
105744750Milestone 1.42023-02-232023-02-23YesIn Progress
105744750Milestone 2  NoNot Started
105744750Milestone 2.1 2023-02-23YesIn Progress
105744750Milestone 2.2  NoNot Started
105744750Milestone 2.3  NoNot Started
105744750Milestone 2.4  NoNot Started
105744750Milestone 2.5  NoNot Started
105744750Milestone 2.6  NoNot Started
105744750Milestone 3  NoNot Started
105744750Milestone 3.1  NoNot Started
105744750Milestone 3.2  NoNot Started
105744750Milestone 3.3  NoNot Started
105744750Milestone 3.4  NoNot Started
105744750Milestone 3.5  NoNot Started
105744750Milestone 3.6  NoNot Started
105744750Milestone 4  NoNot Started
105744750Milestone 4.1  NoNot Started
105744917Milestone 02023-02-232023-02-23NoCompleted
105744917Milestone 1  NoNot Started
105744917Milestone 1.12023-02-23 NoCompleted
105744917Milestone 1.22023-02-232023-02-23NoCompleted
105744917Milestone 1.3  NoNot Started
105744917Milestone 1.42023-02-232023-02-23NoCompleted
105744917Milestone 2  NoNot Started
105744917Milestone 2.1 2023-02-23YesIn Progress
105744917Milestone 2.2  NoNot Started
105744917Milestone 2.3  NoNot Started
105744917Milestone 2.4  NoNot Started
105744917Milestone 2.5  NoNot Started
105744917Milestone 2.6  NoNot Started
105744917Milestone 3  NoNot Started
105744917Milestone 3.1  NoNot Started
105744917Milestone 3.2  NoNot Started
105744917Milestone 3.3  NoNot Started
105744917Milestone 3.4  NoNot Started
105744917Milestone 3.5  NoNot Started
105744917Milestone 3.6  NoNot Started
105744917Milestone 4  NoNot Started
105744917Milestone 4.1  NoNot Started

Assuming all of the ID's have all of the Milestone's listed in proper order, You can:

  • Group by ID
  • For each sub-table
    • Select the ID and Milestone columns and UNPIVOT other columns
    • Merge the Milestone and Attribute column => the new column headers
    • Remove the original columns and more the new merged column to Column2
    • Transpose the resultant table
      • Remove the top row and make the next row the new headers
  • Re-Expand the grouped table

In the code below, change line 2 to reflect your actual data source

let
    Source = Excel.CurrentWorkbook(){[Name="Table7"]}[Content],
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"ID", Int64.Type}, {"Milestone", type text}, {"Actual Completion Date", type date}, {"Actual Start Date", type date}, {"Active", type text}, {"Activity Status", type text}}),
    #"Grouped Rows" = Table.Group(#"Changed Type", {"ID"}, {
        {"Transpose", (t)=>
            let 
                #"Unpivoted Other Columns" = Table.UnpivotOtherColumns(t, {"ID", "Milestone"}, "Attribute", "Value"),
                #"Inserted Merged Column" = 
                    Table.AddColumn(
                        #"Unpivoted Other Columns", "Merged", each Text.Combine({[Milestone], [Attribute]}, " - "), type text),
                #"Removed Columns" = Table.RemoveColumns(#"Inserted Merged Column",{"Milestone", "Attribute"}),
                #"Reordered Columns" = Table.ReorderColumns(#"Removed Columns",{"ID", "Merged", "Value"}),
                #"Transposed Table" = Table.Transpose(#"Reordered Columns"),
                #"Removed Top Rows" = Table.Skip(#"Transposed Table",1),
                #"Promoted Headers" = Table.PromoteHeaders(#"Removed Top Rows", [PromoteAllScalars=true])
            in #"Promoted Headers"}}),

    #"Column Names" = Table.ColumnNames(#"Grouped Rows"[Transpose]{0}),
    #"Data Types" = List.Transform(#"Column Names", each if Text.EndsWith(_, "Date") then {_, type date} else {_, type text}), 
    #"Expanded Transpose" = Table.ExpandTableColumn(#"Grouped Rows", "Transpose", #"Column Names"),
    #"Set Data Types" = Table.TransformColumnTypes(#"Expanded Transpose", #"Data Types")
in
    #"Set Data Types"

ronrsnfld_0-1678654944284.png

 

 

 

Thank you for sharing this! For my use for a Word mail merge, I added steps to dynamically use the largest Table to expand the Grouped Rows. This enables using this even if the Pivot value isn't present for each group. Basically, I

  1. Added a Row count aggregate column to the "Grouped Rows" action, alongside the Transpose column you created
  2. Added an Index column
  3. Calculated the Maximum count to determine which table had the MOST rows (that become columns later)
  4. Filtered the Table to show only Rows with a [Count] equal to the max
  5. Removed everything except for the first Row
  6. Drilled down to the Index value of this record
  7. Used THIS INDEX as the key for getting the Column Names
  8. (kept everything after the same)

These steps could certainly be consolidated and shortened to quickly return the Index of the table with ALL columns present.

Here's the code:

 ...

#"Grouped Rows" = Table.Group(#"Merged Columns", {"ID"}, {
{"Transpose", (t)=>
let
#"Unpivoted Other Columns" = Table.UnpivotOtherColumns(t, {"ID", "Milestone"}, "Attribute", "Value"),
#"Inserted Merged Column" =
Table.AddColumn(
#"Unpivoted Other Columns", "Merged", each Text.Combine({[Milestone], [Attribute]}, ""), type text),
#"Removed Columns" = Table.RemoveColumns(#"Inserted Merged Column",{"Milestone", "Attribute"}),
#"Reordered Columns" = Table.ReorderColumns(#"Removed Columns",{"ID", "Merged", "Value"}),
#"Transposed Table" = Table.Transpose(#"Reordered Columns"),
#"Removed Top Rows" = Table.Skip(#"Transposed Table",1),
#"Promoted Headers" = Table.PromoteHeaders(#"Removed Top Rows", [PromoteAllScalars=true])
in #"Promoted Headers"},
{"Count", each Table.RowCount(_), Int64.Type}}),
#"Added Index" = Table.AddIndexColumn(#"Grouped Rows", "Index", 0, 1, Int64.Type),
#"Calculated Maximum" = List.Max(#"Added Index"[Count]),
#"Filtered Rows" = Table.SelectRows(#"Added Index", each ([Count] = #"Calculated Maximum")),
#"Kept First Rows" = Table.FirstN(#"Filtered Rows",1),
IndexOfMax = #"Kept First Rows"{[Count=#"Calculated Maximum"]}[Index],
#"Column Names" = Table.ColumnNames(#"Grouped Rows"[Transpose]{IndexOfMax})

...

Your solution is amazing, it works wonderful and is easy to follow.  Thank you for sharing.

Thanks very much that worked!

Helpful resources

Announcements
Sept PBI Carousel

Power BI Monthly Update - September 2024

Check out the September 2024 Power BI update to learn about new features.

September Hackathon Carousel

Microsoft Fabric & AI Learning Hackathon

Learn from experts, get hands-on experience, and win awesome prizes.

Sept NL Carousel

Fabric Community Update - September 2024

Find out what's new and trending in the Fabric Community.

Top Solution Authors