【问题标题】:Passing a "list" to a function call with the 'each' keyword使用“每个”关键字将“列表”传递给函数调用
【发布时间】:2020-08-17 10:11:26
【问题描述】:

首先,我承认我不是 M 或 Power Query 专家,尽管我确实有一些使用 Power BI 的经验。

我正在尝试开发一个股票投资组合,该投资组合将跟踪自定义的股票列表及其价格历史记录和其他指标。由于我要解决的问题,我的部分代码基于以下博客:

https://datachant.com/2016/08/09/sentiment-analysis-power-bi-part-2/

我的项目中有以下函数 getPriceMetrics,它将采用一个参数 Sym 并返回单个股票代码的价格指标:

(Sym as any) => let
    Source = Json.Document(Web.Contents("
        https://finnhub.io/api/v1/stock/metric?metric=price&token=ABCXYZ&symbol=" & Sym))
in
    Source

参数Sym 基于包含股票代码列表的“查询”。

当我尝试用一​​个单独的符号调用它时,该函数本身运行良好。但我想针对股票代码列表运行该函数 - 因此上面的博客。

我认为博客中提供的解决方案可以很好地满足我的目的。唯一的问题是我无法使用 each 关键字正确调用函数,我认为:

Table.Group(#"Renamed Columns", {"Index"},
    {{"Data", each getPriceMetrics(_), type record}})

此时,我收到以下错误。

“getPriceMetrics”查询中出现错误。 Expression.Error:我们不能将运算符 & 应用于文本和表格类型。详情:
运算符 = &
左 = https://finnhub.io/api/v1/stock/metric?metric=price&token=ABCXYZ&symbol=
右 = [表格]

如果有人能提供有关在使用 each 关键字时如何为函数调用提供列表参数的任何见解,我将不胜感激。

编辑:这是我的原始方法,从技术上讲是可行的,但由于每秒 30 个 API 调用的限制,一些记录返回为 null。

let
Source = Excel.Workbook(File.Contents("C:\Users\Sikander\Google Drive\Docs\Finance\Investments\Questrade\FIRE_strategy.xlsx"), null, true),
Portfolio_Sheet = Source{[Item="Portfolio",Kind="Sheet"]}[Data],
#"Promoted Headers" = Table.PromoteHeaders(Portfolio_Sheet, [PromoteAllScalars=true]),
#"Changed Type" = Table.TransformColumnTypes(#"Promoted Headers",{{"Symbol", type text}, {"Company", type text}}),
#"Invoked Custom Function" = Table.AddColumn(#"Changed Type", "getPriceMetrics", each getPriceMetrics([Symbol])) 
in #"Invoked Custom Function"

为了解决这个问题,我遵循了博客中建议的方法,该方法允许人们“欺骗”这个 API 限制,可以这么说,通过索引和分组,如下所示:

let
Source = Excel.Workbook(File.Contents("C:\Users\Sikander\Google Drive\Docs\Finance\Investments\Questrade\FIRE_strategy.xlsx"), null, true),
Portfolio_Sheet = Source{[Item="Portfolio",Kind="Sheet"]}[Data],
#"Promoted Headers" = Table.PromoteHeaders(Portfolio_Sheet, [PromoteAllScalars=true]),
#"Changed Type" = Table.TransformColumnTypes(#"Promoted Headers",{{"Symbol", type text}, {"Company", type text}}),
#"Added Index" = Table.AddIndexColumn(#"Changed Type", "Index", 0, 1),
#"Integer-Divided Column" = Table.TransformColumns(#"Added Index", {{"Index", each Number.IntegerDivide(_, 10), Int64.Type}}),
#"Renamed Columns" = Table.RenameColumns(#"Integer-Divided Column",{{"Symbol", "Ticker"}}),
#"Grouped Rows" = Table.Group(#"Renamed Columns", {"Index"}, {{"Data", each getPriceMetrics(_), type record}})

在#“分组行”中

【问题讨论】:

    标签: powerbi powerquery m


    【解决方案1】:

    发生错误是因为您将表而不是固定符号传递给函数。该表是特定(单个)Index 值的 #"Renamed Columns" 的子表。根据您的表是什么,您可能能够从该表中选择一个元素来传递给函数。如果您将每个表的第一行 {0}[Index]_ 放在一起,就会出现这种情况。

    Table.Group(#"Renamed Columns", {"Index"},
        {{"Data", each getPriceMetrics(_{0}[Index]), type record}})
    

    但是,我认为完全没有理由使用Table.Group。如果您的“查询”是一个列表,那么您只需编写以下内容即可将函数应用于每个元素:

    List.Transform(Query, each getPriceMetrics(_))
    

    如果“查询”是一个包含[Sym] 列的表,您可以将上面的Query 替换为Query[Sym],因为表列是一个列表。

    或者,您可以使用简单的公式getPriceMetrics([Sym]) 在查询编辑器 GUI 中添加一个自定义列,它将使用以下代码创建一个步骤:

    Table.AddColumn(#"Previous Step Name Here", "Custom", each getPriceMetrics([Sym]))
    

    编辑:我最近的这个答案有一些在查询编辑器中使用与此处相关的函数的屏幕截图:

    Power Query: how to add one to a column when a specific values appear in an other column


    编辑 2: 正如下面的 cmets 所指出的,这个特定问题的解决方案与使语法完全正确无关,而是需要睡眠功能,正如我在这个 @ 中指出的那样987654322@.

    Table.AddColumn(#"Changed Type", "PriceMetrics",
        each Function.InvokeAfter(()=>getPriceMetrics([Symbol]),#duration(0,0,0,1)))`
    

    【讨论】:

    • 谢谢你,亚历克西斯。我编辑了我的问题,以明确我想要实现的目标。我还尝试了您的建议 - 每个 getPriceMetrics(_{0}[Ticker])。它确实部分起作用,因为它不会引发任何错误,但它只返回每个索引/组的第一个代码的指标。我想要的是一种遍历每个索引组中所有股票代码的方法,同时能够保持在 API 限制内。 :)
    • 为了进一步解释,我希望实现这样的目标,假设有 10 个组/索引:Table.Group(#"Renamed Columns", {"Index"}, {{"Data" , 每个 getPriceMetrics(_{0..9}[Ticker]), type record}})
    • 啊,我不知道你在试图欺骗 API。 {0} 表示第一行,所以它当然只返回第一行。把它拿出来,它会尝试做整个子列表。但是,我认为这不会有帮助,因为您的情况和 API 限制根本不同。在博客上,限制是您可以在一次通话中发送多少条消息,而您的 finnhub 限制是 30 次通话/秒。分组不会帮助呼叫/秒。您可能需要延迟:stackoverflow.com/questions/40411616/…
    • 再次感谢,我尝试像each getPriceMetrics(_{}[Ticker]) 一样删除{0},但我收到此错误:Expression.SyntaxError: Token Literal expected。我将看看速度延迟的方法。 :)
    • 我说错了。这里的不同之处在于您的函数被编写为采用单个值,而不是列表或表格(如博客中所示)。你可以重写它来获取一个列表,但它不会对你的 API 限制做任何事情,因为据我所知,API 不会批量获取代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-19
    • 2017-02-06
    • 1970-01-01
    • 1970-01-01
    • 2019-12-01
    • 2010-09-24
    相关资源
    最近更新 更多