Count unique column values in Power BI and replace them with their name and count total - powerbi

In Power BI, how does one count the unique values in a column and replace those values with the value name and count total? Doing things this way seems like the only way to get the totals inside of a visual's legend.
Starting data example:
Type
Example1
Example1
Example2
Example3
Example2
Example5
Example3
Example4
Example1
Example5
Example1
Example1
Example2
Example3
Example2
Example5
Example3
Example4
Example1
Example5
Example1
Desired result:
Type
Example1 (7)
Example1 (7)
Example2 (4)
Example3 (4)
Example2 (4)
Example5 (4)
Example3 (4)
Example4 (2)
Example1 (7)
Example5 (4)
Example1 (7)
Example1 (7)
Example2 (4)
Example3 (4)
Example2 (4)
Example5 (4)
Example3 (4)
Example4 (2)
Example1 (7)
Example5 (4)
Example1 (7)

You could use Powerquery for it like that.
In the example I took an input table from Excel which means you have to adjust Source accordingly
let
Source = Excel.CurrentWorkbook(){[Name="Tabelle1"]}[Content],
#"Hinzugefügter Index" = Table.AddIndexColumn(Source, "Index", 1, 1, Int64.Type),
groupedbyType = Table.Group(#"Hinzugefügter Index", {"Type"}, {{"CountIt", each _, type table [Type=text, Index=number]}}),
addCol = Table.AddColumn(groupedbyType, "Count", each List.Count([CountIt][Type])),
#"Erweiterte CountIt" = Table.ExpandTableColumn(addCol, "CountIt", {"Index"}, {"CountIt.Index"}),
#"Hinzugefügte benutzerdefinierte Spalte" = Table.AddColumn(#"Erweiterte CountIt", "Result", each [Type] & " (" & Text.From([Count]) & ")"),
#"Sortierte Zeilen" = Table.Sort(#"Hinzugefügte benutzerdefinierte Spalte",{{"CountIt.Index", Order.Ascending}}),
#"Entfernte Spalten" = Table.RemoveColumns(#"Sortierte Zeilen",{"Type", "CountIt.Index", "Count"})
in
#"Entfernte Spalten"
First step in Power Query looks like
Last step looks like
What I am doing is that
I add an index to restore the sorting later
I group by Type but keep the rows
I count the number of rows per group
I extend the result
I combine Type and the result of the count as requested
I restore the sorting
I remove all other columns
Update: Below same steps with translated steps
let
Source = Excel.CurrentWorkbook(){[Name="Tabelle1"]}[Content],
addedIndex = Table.AddIndexColumn(Source, "Index", 1, 1, Int64.Type),
groupedbyType = Table.Group(addedIndex, {"Type"}, {{"CountIt", each _, type table [Type=text, Index=number]}}),
addCol = Table.AddColumn(groupedbyType, "Count", each Table.RowCount([CountIt])),
extendCountIt = Table.ExpandTableColumn(addCol, "CountIt", {"Index"}, {"CountIt.Index"}),
addedColResult = Table.AddColumn(extendCountIt, "Result", each [Type] & " (" & Text.From([Count]) & ")"),
sortAgain = Table.Sort(addedColResult,{{"CountIt.Index", Order.Ascending}}),
removedCol = Table.RemoveColumns(sortAgain,{"Type", "CountIt.Index", "Count"})
in
removedCol
Update for Power BI: For Power BI the M-Code could look like that where an Excel file is used as data source.
let
Source = Excel.Workbook(File.Contents("Excelfilename.xlsx"), null, true),
Table1 = Source{[Item="Tabelle1",Kind="Table"]}[Data],
addedIndex = Table.AddIndexColumn(Table1, "Index", 1, 1, Int64.Type),
groupedbyType = Table.Group(addedIndex, {"Type"}, {{"CountIt", each _, type table [Type=text, Index=number]}}),
addCol = Table.AddColumn(groupedbyType, "Count", each Table.RowCount([CountIt])),
extendCountIt = Table.ExpandTableColumn(addCol, "CountIt", {"Index"}, {"CountIt.Index"}),
addedColResult = Table.AddColumn(extendCountIt, "Result", each [Type] & " (" & Text.From([Count]) & ")"),
sortAgain = Table.Sort(addedColResult,{{"CountIt.Index", Order.Ascending}}),
removedCol = Table.RemoveColumns(sortAgain,{"Type", "CountIt.Index", "Count"})
in
removedCol

Alternate method
let Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Added Custom" = Table.AddColumn(Source,"count",(i)=>Table.RowCount(Table.SelectRows(Source, each [Type]=i[Type])), type number),
#"Added Custom1" = Table.AddColumn(#"Added Custom", "Custom", each [Type] & " (" & Text.From([count]) & ")"),
#"Removed Other Columns" = Table.SelectColumns(#"Added Custom1",{"Custom"})
in #"Removed Other Columns"

Related

Compare 2 consecutive rows of a column in PBI

I would like to compare 2 consecutive rows of the same column ("Niveau num") of a table in PBI and display the result of this comparison in another column ("Niveau arbre"). This can be done very easily in Excel but Power BI is online and it is more complicated to set it up.
I added an index column to have access to the row number and I wrote the following formula in Power Query:
= Table.AddColumn(#"Colonnes permutées", "Niveau arbre", each if Estimations[Niveau num]{[Index]} > Estimations[Niveau num]{[Index]+1} then 1 else 0, type number)
screenshot
I then get the error:
Expression.Error : A cyclic reference was detected during the evaluation.
Here is all the code:
let
Source = Access.Database(File.Contents("C:\Users\T0275244\Desktop\BDD Access PBI\Off\BDD-Off.accdb"), [CreateNavigationProperties=true]),
_Estimations = Source{[Schema="",Item="Estimations"]}[Data],
#"Personnalisée ajoutée" = Table.AddColumn(_Estimations, "Niveau num", each List.Sum(List.Transform(Text.ToList([WBS]), each if _ = "." then 0 else 1))),
#"Lignes filtrées" = Table.SelectRows(#"Personnalisée ajoutée", each [WBS] <> null and [WBS] <> ""),
#"Type modifié" = Table.TransformColumnTypes(#"Lignes filtrées",{{"Niveau num", Int64.Type}}),
#"Index ajouté" = Table.AddIndexColumn(#"Type modifié", "Index", 1, 1, Int64.Type),
#"Colonnes permutées" = Table.ReorderColumns(#"Index ajouté",{"Devis", "WBS", "Désignation", "Qté", "Respde Lot niveau2", "Centre de taux", "Activity", "Activities", "ECPS/Transverse", "SW Comp", "MO ING", "MO TECH", "Total MO (H)", "Total MO (K€)", "Autres MO", "Total MO et Autre MO", "Total tarifs (PRV)", "Divers CPP", "Total MO et Div Tarifs", "Durée ID Ingé", "Nb de Voyages", "Divers transports", "Total Frais Déplacement", "INTRAGROUP PURCHASES", "EXTERNAL PURCHASES", "Total DEVIS (non inflaté)", "Commentaires", "Options", "Devis(Devis)", "Index", "Niveau num"}),
#"Personnalisée ajoutée1" = Table.AddColumn(#"Colonnes permutées", "Niveau arbre", each if Estimations[Niveau num]{[Index]} > Estimations[Niveau num]{[Index]+1} then 1 else 0, type number)
in
#"Personnalisée ajoutée1"
Would you have an idea to solve this?
Thanks in advance for your help
change
#"Personnalisée ajoutée1" = Table.AddColumn(#"Colonnes permutées", "Niveau arbre", each if Estimations[Niveau num]{[Index]} > Estimations[Niveau num]{[Index]+1} then 1 else 0, type number)
to
#"Personnalisée ajoutée1" = Table.AddColumn(#"Colonnes permutées", "Niveau arbre", each if [Niveau num]{[Index]} > [Niveau num]{[Index]+1} then 1 else 0, type number)
you cannot refer to Estimations which is (a) the wrong name, since you used _Estimations, and (b) wrong since that step would not have the index added yet anyway
EDIT -- from your updated image, your column is not numerical so you can't use >. Change the column format to numerical before the last Table.AddColumn()
FINAL EDIT
let Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Column1", Int64.Type}}),
#"Added Index" = Table.AddIndexColumn(#"Changed Type", "Index", 0, 1, Int64.Type),
#"Added Custom" = Table.AddColumn(#"Added Index", "Custom", each try if #"Added Index"{[Index]}[Column1] > #"Added Index"{[Index]+1}[Column1] then 1 else 0 otherwise 0)
in #"Added Custom"

Power Query: recursive function to append elements in a table

I am trying to use a recursive function to append values from a list to a table, however the code below only show me the first and second results:
let
Source = {"second", "third", "forth", "fith", "seventh", "eighth"},
Count = List.Count(Source),
Table = Table.FromRecords({[sequence = "first"]}, type table[sequence = text]),
appendTbl = (x as list, n as number, tbl as table) =>
let
appTable = Table.InsertRows(Table, n, {[sequence = Source{n}]}),
Check = if n = (Count-1) then #appendTbl(x, n+1, appTable) else appTable
in
Check,
Result = appendTbl(Source, 0, Table)
in
Result
Can anyone please give me a help? Thanks !
Its kind of hard to tell if you are using the number to designate the spot in the table you want to insert, or the number of times you want to duplicate the array before inserting it into the table
That said, you can combine tables with Table.Combine() after converting the list to a table with Table.FromList(). If you need to append it multiple times then just use List.Repeat on the list. If you need to use the Count variable in your function, you have to send it there appendTbl = (x as list, n as number, tbl as table, count as number) =>
some sample codes that probably don't do exactly what you want
let Source = {"second", "third", "forth", "fith", "seventh", "eighth"},
AppendCount=2, //# times to append the list onto the table
#"Converted to Table" = Table.FromList(List.Repeat(Source,AppendCount), Splitter.SplitByNothing(), null),
#"Renamed Columns" = Table.RenameColumns(#"Converted to Table",{{"Column1", "sequence"}}),
Table = Table.FromRecords({[sequence = "first"]}, type table[sequence = text]),
combined= Table.Combine({Table, #"Renamed Columns"})
in combined
or
let Source = {"second", "third", "forth", "fith", "seventh", "eighth"},
Table = Table.FromRecords({[sequence = "first"]}, type table[sequence = text]),
appendTbl = (x as list, n as number, tbl as table) => // append list x to table tbl, n times on column sequence
let #"Converted to Table" = Table.FromList(List.Repeat(x,n), Splitter.SplitByNothing(), null),
#"Renamed Columns" = Table.RenameColumns(#"Converted to Table",{{"Column1", "sequence"}}),
combined= Table.Combine({tbl, #"Renamed Columns"})
in combined,
Result = appendTbl(Source, 2, Table) // append Source to Table, 2 times
in Result

How to count row combinations based on common value

I'm trying to find the frequency of combinations that occur per an ID value.
Example given here: https://i.stack.imgur.com/ZG9gJ.png
The problem is that the number of rows that could make up a combination is variable, meaning a combination could consist of just 1 value or 2, 3, 4, etc.
I'm currently trying to do this within Power BI, but perhaps another tool would be more appropriate.
You can do this with Power Query (from Power BI => Transform)
Basic algorithm
Group by ID
for each subGroup
Concatenate a sorted list of Cats
Count the number of Cats per ID for subsequent sorting
Then Group by COMBI
Aggregate with Count function
M Code
let
//change next line to however you are getting your table
Source = Excel.CurrentWorkbook(){[Name="idCat"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"ID", Int64.Type}, {"Cat1", type text}}),
//group by ID and create COMBI
// and also length of each cat string for subsequen intelligent sorting
#"Grouped Rows" = Table.Group(#"Changed Type", {"ID"}, {
{"COMBI", each Text.Combine(List.Sort([Cat1])),type text},
{"lenCat", each Text.Length(Text.Combine(List.Sort([Cat1]))),Int64.Type}
}),
maxLen = List.Max(#"Grouped Rows"[lenCat]),
#"Delete length column" = Table.RemoveColumns(#"Grouped Rows","lenCat"),
//Group by Cats for counting
#"Grouped Cats" = Table.Group(#"Delete length column",{"COMBI"},{
{"COUNT", each List.Count([COMBI]), Int64.Type}
}),
#"Pad COMBI for Sorting" = Table.TransformColumns(#"Grouped Cats",{"COMBI", each Text.PadStart(_,maxLen), type text}),
#"Sorted Rows" = Table.Sort(#"Pad COMBI for Sorting",{{"COMBI", Order.Ascending}}),
#"Trim Leading Spaces" = Table.TransformColumns(#"Sorted Rows",{"COMBI", each Text.Trim(_), type text})
in
#"Trim Leading Spaces"

Power Query: Table.Group with a dynamic list of columns specifying column type

I have
ttOKLostTypes=Table.Group(#"Pivoted Column", {"Index"}, List.Transform(columnList2, each {_, (grp) => List.Max(Table.Column(grp, _)) })),
However this resets column types. How can I specify column types in the above transformation as here:
#"Grouped Rows" = Table.Group(#"Pivoted Column", {"Index"}, {{"InvoiceDate", each List.Max([InvoiceDate]),type nullable date},....
I know I can find out column types by using
schema=Table.Schema(#"Pivoted Column"),
but I cannot figure out how can I build a proper List with column types to be used in the Table.Group()
You can build a dynamic list of all the aggregations to include the data type, using List.Transform, by just adding the data type to your transformation.
Assuming the data types are all the same:
For example, if your grouping column is "Column1", then
maxCols = List.RemoveItems(Table.ColumnNames(#"Changed Type"),{"Column1"}),
colAggregations =
List.Transform(
maxCols,
(c)=> {c, each List.Max(Table.Column(_,c)),Int64.Type}
),
group = Table.Group(#"Changed Type","Column1", colAggregations)
EDIT
To include the types of the original columns, dynamically, is more difficult. Table.Schema will return the column types as text so they have to be transformed into a Type.
One way to do this is with a custom function.
Custom Function
name it: fnTextToType
I only included a few types. The Field name is a name returned by Table.Schema for a particular type, and the field value is the type. It is hopefully obvious how to extend this function to account for other types
(txt as text) =>
let
typeRecord =
Record.Field(
[Number.Type = Number.Type,
Int64.Type = Int64.Type,
DateTime.Type = DateTime.Type],
txt
)
in
typeRecord
Then you can use it in code like this:
#"Changed Type" = Table.TransformColumnTypes(rem,{{"Column1", Int64.Type}, {"Column2", type number}, {"Column3", Int64.Type}}),
//get list of column types in column order
//note these are returned as text strings and not as "types"
colTypes = Table.Schema(#"Changed Type")[TypeName],
//create list of columns upon which to execute the aggregation (List.Max in this case)
maxCols = List.RemoveItems(Table.ColumnNames(#"Changed Type"),{"Column1"}),
//create list of aggregations
colAggregations =
List.Transform(maxCols,(c)=> {c, each List.Max(Table.Column(_,c)),
fnTextToType(colTypes{List.PositionOf(Table.ColumnNames(#"Changed Type"),c)})}),
//now group them
group = Table.Group(#"Changed Type","Column1", colAggregations)
in
group
You can see how the types were maintained in the screenshots below.
Changed Type
group
Thanks #Ron Rosenfeld. Your answer which works suggested me to find another way using Expression.Evaluate. Evaluate without #shared does not work. See https://blog.crossjoin.co.uk/2015/02/06/expression-evaluate-in-power-querym/
columnList = Table.ColumnNames(#"Pivoted Column"),
columnList2 = List.RemoveItems(columnList,{"Index"}),
ColListWithTypes = List.Transform(columnList2,(colName)=> {colName,Table.SelectRows(schema,each [Name]=colName)[TypeName]{0}}),
ttTestWithTypes=Table.Group(#"Pivoted Column", {"Index"}, List.Transform(ColListWithTypes, each {_{0}, (grp) => List.Max(Table.Column(grp, _{0})),Expression.Evaluate(_{1},#shared)})),

PowerBI table: how to add number to column name

I have a table with the following column names:
A
B
C
D
E
F
G
I need to rename my columns so that from a certain column onwards they are numbered sequentially:
A
B
C
D (1)
E (2)
F (3)
G (4)
I know how to do it manually, but since I have 65 of such columns I was hoping to use something like TransformColumnNames to do it programmatically.
Many thanks!
Here's one way: It starts with a table named Table 1 as the source.
let
Source = Table1,
//Replace the "D" below with the name of your column that you want to start numbering at
#"Get Column Number to Start Adding Numbers At" = List.PositionOf(Table.ColumnNames(Source),"D"),
#"Setup Column Numbers" = List.Transform({1..List.Count(Table.ColumnNames(Source))}, each if _-#"Get Column Number to Start Adding Numbers At" > 0 then " (" & Text.From(_-#"Get Column Number to Start Adding Numbers At") & ")" else ""),
#"Create New Column Names" = List.Zip({Table.ColumnNames(Source), #"Setup Column Numbers"}),
#"Converted to Table" = Table.FromList(#"Create New Column Names", Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"Extracted Values" = Table.TransformColumns(#"Converted to Table", {"Column1", each Text.Combine(List.Transform(_, Text.From)), type text}),
Result = Table.RenameColumns(Source, List.Zip({Table.ColumnNames(Source),#"Extracted Values"[Column1]}))
in
Result
Maybe if you pivot the columns that need to have the number, then add an index and create a new concatenated column with number included. remove the other columns and unpivot again?