【问题标题】:How can I build a collection type with multidimensional wildcard indexing to return multiple matches?如何构建具有多维通配符索引的集合类型以返回多个匹配项?
【发布时间】:2017-05-23 09:14:16
【问题描述】:

如果问题标题令人困惑,我深表歉意;随意提出替代方案。

我正在编写一些数据分析代码,并且我有一组数据要用于构建映射,可用于查找某些键以找到某些值。但我希望能够对部分多维键使用通配符,以便返回多个值。我还想要快速查找和高效的内存使用。

我的钥匙是 5 维的。如果我不想在查找中使用通配符,那么解决方案就是使用 5 维元组作为 Map 的键。有时我只想使用完全指定的键返回单个值,但有时我想返回部分指定键的结果聚合。我存储的值是浮点数的 3 维元组。

如果我不想要高效的内存使用,那么我会创建一个 5 维数组并创建 5 个索引查找(每个部分对应一个键),然后用适当的访问器方法包装整个 this。要获取其中一个关键部分是通配符的所有值,只需使用“0..”访问数组,然后根据需要聚合结果。在这种情况下,大部分数组将是空的。这似乎不是一个好主意,因为我事先不知道每个维度会有多大,因此叉积可能超出可能。 我现在采用的解决方案只是为我感兴趣的每种查找设置单独的映射。例如,如果我的完整键是 k1*k2*k3*k4*k5,并且我的部分代码想要访问与 k1**k3*k4* 匹配的任何内容的聚合数据,然后我将创建一个由 k1*k3*k4 索引的地图,该地图存储它关心的聚合浮点数。组合的数量相当大(我认为是 32 * 3 = 96),所以如果我希望能够使用我所采用的方法以各种可能的方式访问这些数据,我将需要 96 个不同的映射和访问器方法。

当我写完这篇文章时,我意识到必须有一种方法可以做到这一点,因为数据库可以很好地处理这个问题。他们是怎么做的,我可以在内存中的 F# 中做同样的事情吗?

【问题讨论】:

  • 您是否考虑过使用像kd-tree 这样的结构?
  • @TheInnerLight 直到现在才听说过。听起来当然是对的。希望我能找到一个包,而不是自己尝试实现它。
  • 如果你没有找到任何东西,我会试着给你一个如何实现它的纲要,但我可能需要一两天的时间才能做到这一点。感兴趣的每个维度的key是什么类型,即k1的类型是什么?
  • TradeDate (DateTime) * SecurityID (string) * PortID (string) * TradeType (6 case union) * IDType (5 case union)

标签: dictionary collections f# binary-search-tree wildcard


【解决方案1】:

这是一个使用嵌套字典的解决方案。我没有做任何基准测试,但至少它不需要“全表搜索”,只要所有键都不是通配符。请注意,我在这里用整数替换了联合类型。

open System
open System.Collections.Generic

type MultidimensionalIndex<'T>() =

    let data = Dictionary<DateTime, Dictionary<string, Dictionary<string, Dictionary<int, Dictionary<int, 'T>>>>>()

    let getOrAddEmpty key (d : Dictionary<_,_>) =
        match d.TryGetValue key with
        | true, innerDict -> innerDict
        | false, _ ->
            let innerDict = Dictionary<_,_>()
            d.[key] <- innerDict
            innerDict

    let selectValues keyOpt (dicts : Dictionary<_,_> array) =
        dicts
        |> Array.collect (fun d ->
            match keyOpt with
            | Some key ->
                match d.TryGetValue key with
                | true, v -> [| v |]
                | false, _ -> [||]
            | None ->
                Seq.toArray(d.Values))

    member __.Add (k1, k2, k3, k4, k5) value =
        let innermostDict =
            data
            |> getOrAddEmpty k1
            |> getOrAddEmpty k2
            |> getOrAddEmpty k3
            |> getOrAddEmpty k4
        innermostDict.[k5] <- value

    // None represents a wildcard
    member __.Query (k1Opt, k2Opt, k3Opt, k4Opt, k5Opt) =
        [| data |]
        |> selectValues k1Opt
        |> selectValues k2Opt
        |> selectValues k3Opt
        |> selectValues k4Opt
        |> selectValues k5Opt

【讨论】:

  • 这似乎是个好主意。现在我想知道为什么我自己没有想到它!让我做一些测试,如果一切顺利,我会接受这个作为答案。
猜你喜欢
  • 2019-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-31
  • 1970-01-01
  • 2017-04-21
相关资源
最近更新 更多