【问题标题】:Using Power Query with Excel, how do I replace a record filled will null values with a single null value?将 Power Query 与 Excel 一起使用,如何用单个空值替换填充的空值?
【发布时间】:2021-08-20 09:51:59
【问题描述】:

问题总结

我的 Power Query 表中有一个包含自定义链接数据类型的列。不需要创建填充所有空值的自定义链接数据类型。相反,如果自定义数据类型中包含的所有值都为 null,我希望列中的值为 null。

背景

我有一个包含 API 响应 JSON 文本的表。此 JSON 文本包含搜索结果列表(也是 JSON 格式),表示与请求中提供的搜索条件匹配的电影。可以有任意数量的搜索结果,包括零。使用 Power Query M,我使用内置解析器解析这些 JSON 文本,该解析器生成一个 list,每个搜索结果包含一个 record。然后我提取list 中的第一个record,将record 扩展为新列,并将这些新列合并为自定义数据类型。

示例

这是一个示例查询,仅模拟我的查询的问题区域。这个例子是完全包含的,可以用来准确地重现我的问题。

let
    // These two variables holds the API response JSON text obtained from calls to Web.Contents().
    // I've eliminated the actual calls in this example because that part of my query works fine.
    Search_Fast_and_Furious_Response =
        "{ ""total-results"":""2"", ""results"":[
            { ""title"":""Fast & Furious"", ""year"":""2009"" },
            { ""title"":""The Fast and the Furious"", ""year"":""2001"" } ] }",
    Search_mmmmm_Response =
        "{ ""total-results"":""0"", ""results"":[] }",
    
    // Create the table to hold the response text.
    Source = Table.FromRecords( { [#"API Response"=Search_Fast_and_Furious_Response],
        [#"API Response"=Search_mmmmm_Response] }),
    
    // Parse the JSON and put the output (a record) in a new column.
    #"Insert Parsed JSON" = Table.AddColumn(Source, "JSON", each Json.Document([API Response])),

    // Expand the record in the parsed JSON column. Each field in the record becomes a new column.
    #"Expand JSON" = Table.ExpandRecordColumn(#"Insert Parsed JSON", "JSON",
        {"total-results", "results"}, {"Result Count", "Results List"}),

    // Add a new column to hold the first search result in the responses results list.
    // This is also a record, like the parsed JSON two steps ago.
    #"Add Result #1 Column" = Table.AddColumn(#"Expand JSON", "Result #1", each
        try         _[Results List]{0}
        otherwise   null),                  // In case the list is empty

    // Expand the record in the Result #1 column.
    #"Expand Result #1" = Table.ExpandRecordColumn(#"Add Result #1 Column", "Result #1",
        {"title", "year"}, {"Title", "Year"}),

    // Combine the newly expanded columns into a single column.
    // Make the Display Name be the value in the Title field/column,
    // and make the Type Name be "Excel.DataType."
    // This is what creates the custom linked data type.
    #"Combine Result #1" = Table.CombineColumnsToRecord(#"Expand Result #1", "Result #1",
        {"Title", "Year"}, [ DisplayNameColumn = "Title", TypeName="Excel.DataType" ])
in
    #"Combine Result #1"

in 语句之前最后一行中的列表,ie Table.CombineColumnsToRecord 函数的第四个参数,允许将记录用作 Excel 新的自定义数据类型关联数据功能。我不确定,但我相信 Power Query/Excel 将它们存储为 records 以​​及其他元数据,例如 DisplayNameColumnTypeName(我确信后者是最重要的部分)。

问题与目标

这是示例查询创建的结果表。右下角的单元格被选中。它的内容显示在图像的底部。单元格本身包含一个值,特别是一条所有值都设置为 null 的记录。因为 Title 字段为 null,所以记录的显示文本为“null”。

下一张图片显示了我想要的输出。再次注意右下角的单元格。这一次,单元格是空的。它不再包含所有值为空的记录;现在它什么都不包含了,所以这个视图中显示的是null,斜体表示一个空值,而不是单词“null”。 (注意:我无法将第一张图像中的“null”单元格更改为文字 null 值,因此为了演示,我只是添加了一个新的 null 值列。)

不幸的是,由于我在try 后面的otherwise 子句,如果API 返回零搜索结果,“结果#1”列可能为空。如果此值在任何行中为 null,则 #"Expand Result #1" 创建的所有新列也将在该行中包含 null。最后,当所有空值在最后一步合并时,我留下了一个带有所有空值的record。相反,我希望实现的是在该单元格中有一个空值(null 类型)。

目前的努力

我尝试了Table.ReplaceValues 函数,将 null 作为新值传递,并将许多不同的值作为旧值(要替换的值)传递,例如具有所有空值的新记录。所有这些尝试要么在语法上不正确,要么导致了预期的和不需要的行为。我也尝试过使用 Power Query GUI 中的“替换值”选项,但结果相同。如果ReplaceValues 不喜欢空值,我还尝试在otherwise 子句中使用不同的值,例如文本类型的“N/A”,然后对该不同的值执行ReplaceValues。这产生了相同的结果。

结论

有什么方法可以用一个奇异的空值替换一条记录——它用空值填充并存储在包含记录的列中?在这种情况下,链接数据类型功能是一个高优先级,因此我更喜欢保留该功能的解决方案(当然欢迎所有解决方案)。

【问题讨论】:

  • 具有代表性的数据样本(以 text 形式发布)和预期结果示例将有助于为您提供帮助。阅读How do I Ask a Good QuestionHow to create a Minimal, Complete, and Verifiable example 的帮助主题可能会有所帮助。 编辑您的问题以提供更多有用的信息。
  • 谢谢@RonRosenfeld!我感谢您的帮助。在阅读了这些页面之后,我相信我已经提供了一个基本完整的“reprex”,尽管我将进一步充实它并以我的示例“从头开始”。我还将包括一个有代表性的数据样本。
  • 要检查Result#1中的值是否为*null*,可以检查Title列的值。例如:if Record.Field([#"Result #1"],"Title")=null then null else [#"Result #1"] 但这不会保留数据类型。这有多重要?没有它你能活吗?还是之后设置您的数据类型?
  • @RonRosenfeld 再次感谢!我将在第二天左右(目前正在休假)完全回答我自己的问题,但你的建议几乎就是我所做的:我稍后设置我的数据类型。我知道我可以检查 Title 的值为 null,但直到两天前我才想到我可以简单地 wait 来创建数据类型,直到 after我已经处理了空值。我将空行与非空行分离到一个单独的表中,对非空行执行数据类型创建,然后在最后将两个表再次放在一起。

标签: excel type-conversion powerquery record data-conversion


【解决方案1】:

我已经“解决”了我的问题。虽然从技术上讲不是我发布的问题的解决方案,但我已经使用一种解决方法达到了预期的结果。

我没有处理充满空字段的对象,而是确保该对象不会转换为一开始的自定义对象。我通过在提取结果列表列中的第一个列表项后移动所有具有空值的记录来实现这一点;这是在我展开提取的项目之前完成的。将空值放入一个新表(我称之为空表)后,我从第一个表(我称之为非空表)中删除这些空值。我对非空表执行常规操作,以仅为那些非空的行创建自定义链接数据类型。之后,我再次将这两个表合并在一起。

下面是包含我的代表性示例的解决方案的完整代码,其中新步骤“突出显示”了非缩进 cmets。

let
    // These two variables holds the API response JSON text obtained from calls to Web.Contents().
    // I've eliminated the actual calls in this example because that part of my query works fine.
    Search_Fast_and_Furious_Response =
        "{ ""total-results"":""2"", ""results"":[
            { ""title"":""Fast & Furious"", ""year"":""2009"" },
            { ""title"":""The Fast and the Furious"", ""year"":""2001"" } ] }",
    Search_mmmmm_Response =
        "{ ""total-results"":""0"", ""results"":[] }",
    
    // Create the table to hold the response text.
    Source = Table.FromRecords( { [#"API Response"=Search_Fast_and_Furious_Response],
        [#"API Response"=Search_mmmmm_Response] }),
    
    // Parse the JSON and put the output (a record) in a new column.
    #"Insert Parsed JSON" = Table.AddColumn(Source, "JSON", each Json.Document([API Response])),

    // Expand the record in the parsed JSON column. Each field in the record becomes a new column.
    #"Expand JSON" = Table.ExpandRecordColumn(#"Insert Parsed JSON", "JSON",
        {"total-results", "results"}, {"Result Count", "Results List"}),

    // Add a new column to hold the first search result in the responses results list.
    // This is also a record, like the parsed JSON two steps ago.
    #"Add Result #1 Column" = Table.AddColumn(#"Expand JSON", "Result #1", each
        try         _[Results List]{0}
        otherwise   null),                  // In case the list is empty

// New step
    // Filter down to only rows with null in the new column. Save this new table for later.
    #"Filter In Null" = Table.SelectRows(#"Add Result #1 Column", each _[#"Result #1"] = null),

// New step
    // Filter down to only rows with NOT null in the new column.
    #"Filter Out Null" = Table.SelectRows(#"Add Result #1 Column", each _[#"Result #1"] <> null),

    // Expand the record in the Result #1 column.
    #"Expand Result #1" = Table.ExpandRecordColumn(#"Filter Out Null", "Result #1",
        {"title", "year"}, {"Title", "Year"}),

    // Combine the newly expanded columns into a single column.
    // Make the Display Name be the value in the Title field/column,
    // and make the Type Name be "Excel.DataType."
    // This is what creates the custom linked data type.
    #"Combine Result #1" = Table.CombineColumnsToRecord(#"Expand Result #1", "Result #1",
        {"Title", "Year"}, [ DisplayNameColumn = "Title", TypeName="Excel.DataType" ]),

// New step
    // Convert the Null Table into a list of records.
    #"Convert Table" = Table.ToRecords(#"Filter In Null"),

// New step
    // Append the Null Table from earlier to the main table.
    #"Combine Tables" = Table.InsertRows(#"Combine Result #1", Table.RowCount(#"Combine Result #1"),
        #"Convert Table")
in
    #"Combine Tables"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-06-14
    • 2021-09-05
    • 1970-01-01
    • 2021-10-15
    • 1970-01-01
    • 2021-06-27
    • 2018-05-14
    相关资源
    最近更新 更多