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

Powerquery: Expand all columns of that have records in them

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

// 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"}
)

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

// 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

背景

只要我的数据 table 具有不同的布局,我就需要重写 询问。在幻想世界中,您可以展开所有包含 Records 的列 他们使用特定的密钥。 理想情况下,您将拥有以下库 函数:

// 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. 您能否获得具有未在 table 中指定的特定类型的列的列表,也就是推断它?
  2. 你能使该记录扩展通用吗?

编辑:添加了带有两个空单元格的第 0 行。

(首先,感谢清晰的解释和示例数据及建议!)

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

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

如果您在特定情况下无法解决此问题,是否想更新您的问题? :)

2) 只要​​ table 的单元格值是记录,就很有可能使记录扩展成为通用的,只是有点复杂。这会找到所有行都是 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 构造类型非常混乱。

抱歉来晚了一点,但我刚刚遇到了类似的挑战。我尝试使用 Chris Webb 的 ExpandAll 函数:

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

... 但这只适用于 Table 类型的列,不适用于记录类型的列,但我已经设法将其破解到那个目的。我将 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

我尝试在一个函数中同时扩展表和记录,但我一直遇到类型错误。

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

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

编辑:

出于担心有一天伟大的片段(Chris Webb 所著,@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

You can then use this function on the XML file as follows:

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  

(Source and downloadable example: Chris Webb's Bi Blog, 2014-05-21)