Powerquery:展开其中包含记录的所有列

时间:2016-03-03 09:07:27

标签: powerquery

在Microsoft Excel 2013中使用Power Query,我创建了一个如下所示的表:

// To insert this in Power Query, append a '=' before the 'Table.FromRows'
Table.FromRows(
  {
    {"0", "Tom", "null", "null"},
    {"1", "Bob", [ name="Berlin" , street="BarStreet" ], [ name="Mary", age=25 ]},
    {"2", "Jim", [ name="Hamburg", street="FooStreet" ], [ name="Marta", age=30 ]}
  },
  {"ID", "Name", "Address", "Wife"}
)

现在,我想使用Address属性展开列Wifename 在两个记录上。手动,我会这样做:

// To insert this in Power Query, append a '=' before the 'Table.FromRows'
let
  t = Table.FromRows(
    {
      {"0", "Tom", "null", "null"},
      {"1", "Bob", [ name="Berlin" , street="BarStreet" ], [ name="Mary", age=25 ]},
      {"2", "Jim", [ name="Hamburg", street="FooStreet" ], [ name="Marta", age=30 ]}
    },
    {"ID", "Name", "Address", "Wife"}
  ),
  expAddress = Table.ExpandRecordColumn(t, "Address", {"name"}, {"Address → name"}),
  expWife = Table.ExpandRecordColumn(expAddress, "Wife", {"name"}, {"Wife → name"})
in
  expWife

背景

每当我有不同布局的数据表时,我需要重写 查询。在幻想世界中,您可以扩展所有包含记录的列 他们使用特定的密钥。 理想情况下,您将拥有以下库 功能:

// Returns a list with the names of the columns that match the secified type.
// Will also try to infer the type of a column if the table is untyped.
Table.ColumnsOfTypeInfer(
  table as table,
  listOfTypes as list
) as list

// Expands a column of records into columns with each of the values.
Table.ExpandRecordColumnByKey(
  table as table,
  columns as list,
  key as text,
) as table

然后,我可以打电话

// To insert this in Power Query, append a '=' before the 'Table.FromRows'
let
  t = Table.FromRows(
    {
      {"0", "Tom", "null", "null"},
      {"1", "Bob", [ name="Berlin" , street="BarStreet" ], [ name="Mary", age=25 ]},
      {"2", "Jim", [ name="Hamburg", street="FooStreet" ], [ name="Marta", age=30 ]}
    },
    {"ID", "Name", "Address", "Wife"}
  ),
  recordColumns = Table.ColumnsOfTypeInfer(t, {type record}),
  expAll = Table.ExpandRecordColumnByKey(t, recordColumns, "name")
in
  expAll

问题

  1. 你能得到一个列表,列中列出了一个未在表格中指定的特定类型,又推断它吗?
  2. 你能将该记录扩展为通用吗?
  3. 编辑:添加了包含两个空单元格的第0行。

2 个答案:

答案 0 :(得分:2)

(首先,感谢明确的解释以及样本数据和建议!)

1)M代码无法进行类型推断。这个限制几乎可以被认为是一个"功能",因为如果源数据以导致推断类型不同的方式发生变化,它几乎肯定会破坏您的查询。

加载无类型数据后,应该快速使用Detect Data Type按钮为此生成M.或者,如果您正在从JSON读取数据,那么它应该已经足够输入了。

如果您有一个特定的情况,这不起作用想要更新您的问题? :)

2)只要表的单元格值是记录,它就非常可能并且只有一点点令人费解以使记录扩展成为通用的。这会找到所有行都为null或记录的列,并展开name列。

以下是您可以添加到库中的一些简单实现:

let
  t = Table.FromRows(
    {
      {"0", "Tom", null, null},
      {"1", "Bob", [ name="Berlin" , street="BarStreet" ], [ name="Mary", age=25 ]},
      {"2", "Jim", [ name="Hamburg", street="FooStreet" ], [ name="Marta", age=30 ]}
    },
    {"ID", "Name", "Address", "Wife"}
  ),

  Table.ColumnsOfAllRowType = (table as table, typ as type) as list => let
    ColumnNames = Table.ColumnNames(table),
    ColumnsOfType = List.Select(ColumnNames, (name) => 
      List.AllTrue(List.Transform(Table.Column(table, name), (cell) => Type.Is(Value.Type(cell), typ))))
  in
    ColumnsOfType,

  Table.ExpandRecordColumnByKey = (table as table, columns as list, key as text) as table  => 
    List.Accumulate(columns, table, (state, columnToExpand) => 
      Table.ExpandRecordColumn(state, columnToExpand, {key}, { columnToExpand & " → " & key })),

  recordColumns = Table.ColumnsOfAllRowType(t, type nullable record),
  expAll = Table.ExpandRecordColumnByKey(t, recordColumns, "name")
in
  expAll

如果只能在M中实现新的库函数,我们就不太可能将它添加到我们的标准库中,但是如果您觉得它丢失了,请随时在https://ideas.powerbi.com/forums/265200-power-bi/

你可能有一个很好的论据来添加Table.ReplaceTypeFromFirstRow(table as table) as table这样的东西,因为用M构造类型非常混乱。

答案 1 :(得分:0)

很抱歉有点迟到,但我遇到了类似的挑战。我尝试使用Chris Webb的ExpandAll函数:

http://blog.crossjoin.co.uk/2014/05/21/expanding-all-columns-in-a-table-in-power-query/

...但是这只适用于Table类型的列,而不是Record-type列,但我已经设法将其破解为此目的。我将Chris的功能复制为“ExpandAllRecords”并进行了3次编辑:

  1. each if _ is table then Table.ColumnNames(_)替换为each if _ is record then Record.FieldNames(_)
  2. Table.ExpandTableColumn替换为Table.ExpandRecordColumn
  3. ExpandAll替换为ExpandAllRecords
  4. 我尝试在一个函数中扩展表和记录,但我一直遇到类型错误。

    无论如何,有了这个,最后的查询就是:

    let
        t = Table.FromRows(
            {
                {"1", "Tom", null, [ name="Jane", age=35 ]},
                {"2", "Bob", [ name="Berlin" , street="BarStreet" ], [ name="Mary", age=25 ]},
                {"3", "Jim", [ name="Hamburg", street="FooStreet" ], [ name="Marta", age=30 ]}
            },
            {"ID", "Name", "Address", "Wife"}
        ),
        Output = ExpandAllRecords(t)
    in
        Output
    

    编辑:

    出于担心,有一天这个伟大的片段(由ChrisMob,由@MikeHoney提到)有一天会消失),我将在这里反映整个代码:

    let
         //Define function taking two parameters - a table and an optional column number 
         Source = (TableToExpand as table, optional ColumnNumber as number) =>
         let
          //If the column number is missing, make it 0
          ActualColumnNumber = if (ColumnNumber=null) then 0 else ColumnNumber,
    
          //Find the column name relating to the column number
          ColumnName = Table.ColumnNames(TableToExpand){ActualColumnNumber},
    
          //Get a list containing all of the values in the column
          ColumnContents = Table.Column(TableToExpand, ColumnName),
    
          //Iterate over each value in the column and then
          //If the value is of type table get a list of all of the columns in the table
          //Then get a distinct list of all of these column names
          ColumnsToExpand = List.Distinct(List.Combine(List.Transform(ColumnContents, 
                             each if _ is table then Table.ColumnNames(_) else {}))),
    
          //Append the original column name to the front of each of these column names
          NewColumnNames = List.Transform(ColumnsToExpand, each ColumnName & "." & _),
    
          //Is there anything to expand in this column?
          CanExpandCurrentColumn = List.Count(ColumnsToExpand)>0,
    
          //If this column can be expanded, then expand it
          ExpandedTable = if CanExpandCurrentColumn 
                              then 
                              Table.ExpandTableColumn(TableToExpand, ColumnName, 
                                     ColumnsToExpand, NewColumnNames) 
                              else 
                              TableToExpand,
          //If the column has been expanded then keep the column number the same, otherwise add one to it
          NextColumnNumber = if CanExpandCurrentColumn then ActualColumnNumber else ActualColumnNumber+1,
    
          //If the column number is now greater than the number of columns in the table
          //Then return the table as it is
          //Else call the ExpandAll function recursively with the expanded table
          OutputTable = if NextColumnNumber>(Table.ColumnCount(ExpandedTable)-1) 
                             then 
                             ExpandedTable 
                             else 
                             ExpandAll(ExpandedTable, NextColumnNumber)
         in
         OutputTable
    in
         Source
    
         

    然后,您可以在XML文件上使用此函数,如下所示:

    let
         //Load XML file
         Source = Xml.Tables(File.Contents("C:\Users\Chris\Documents\PQ XML Expand All Demo.xml")),
         ChangedType = Table.TransformColumnTypes(Source,{{"companyname", type text}}),
    
         //Call the ExpandAll function to expand all columns
         Output = ExpandAll(ChangedType)
    in
         Output  
    
         

    来源和可下载示例: Chris Webb's Bi Blog, 2014-05-21