2016-08-03 51 views
3

我正在进行个人市场分析项目。我已经得到了所有代表在目前市场上的转折点的数据结构,看起来像这样:Clojure中的复杂数据操作

[{:high 1.121455, :time "2016-08-03T05:15:00.000000Z"} 
{:low 1.12109, :time "2016-08-03T05:15:00.000000Z"} 
{:high 1.12173, :time "2016-08-03T04:30:00.000000Z"} 
{:high 1.121925, :time "2016-08-03T00:00:00.000000Z"} 
{:high 1.12215, :time "2016-08-02T23:00:00.000000Z"} 
{:high 1.12273, :time "2016-08-02T21:15:00.000000Z"} 
{:high 1.12338, :time "2016-08-02T18:15:00.000000Z"} 
{:low 1.119215, :time "2016-08-02T12:30:00.000000Z"} 
{:low 1.118755, :time "2016-08-02T12:00:00.000000Z"} 
{:low 1.117575, :time "2016-08-02T06:00:00.000000Z"} 
{:low 1.117135, :time "2016-08-02T04:30:00.000000Z"} 
{:low 1.11624, :time "2016-08-02T02:00:00.000000Z"} 
{:low 1.115895, :time "2016-08-01T21:30:00.000000Z"} 
{:low 1.11552, :time "2016-08-01T11:45:00.000000Z"} 
{:low 1.11049, :time "2016-07-29T12:15:00.000000Z"} 
{:low 1.108825, :time "2016-07-29T08:30:00.000000Z"} 
{:low 1.10839, :time "2016-07-29T08:00:00.000000Z"} 
{:low 1.10744, :time "2016-07-29T05:45:00.000000Z"} 
{:low 1.10716, :time "2016-07-28T19:30:00.000000Z"} 
{:low 1.10705, :time "2016-07-28T18:45:00.000000Z"} 
{:low 1.106875, :time "2016-07-28T18:00:00.000000Z"} 
{:low 1.10641, :time "2016-07-28T05:45:00.000000Z"} 
{:low 1.10591, :time "2016-07-28T01:45:00.000000Z"} 
{:low 1.10579, :time "2016-07-27T23:15:00.000000Z"} 
{:low 1.105275, :time "2016-07-27T22:00:00.000000Z"} 
{:low 1.096135, :time "2016-07-27T18:00:00.000000Z"}] 

概念,我想匹配:high /:low双,制定的价格范围(高至低)和中点(高点的平均值很低),但我不希望生成每个可能的配对。

我想要做的是从第一个项目集合{:high 1.121455, :time "2016-08-03T05:15:00.000000Z"}在开始走“下”通过收集的剩余部分,创建与每个:low项对,直到我打接下来的:high项目。一旦我击中了下一个:high项目,我对任何其他对都不感兴趣。在这种情况下,只会创建一对,即:high和第一个:low - 我在那里停止,因为下一个(第三个)项目是:high。 1.生成的记录应该像{:price-range 0.000365, :midpoint 1.121272, :extremes [{:high 1.121455, :time "2016-08-03T05:15:00.000000Z"}{:low 1.12109, :time "2016-08-03T05:15:00.000000Z"}]}

接下来,我会移动到第二个项目集合{:low 1.12109, :time "2016-08-03T05:15:00.000000Z"}在走“下”通过收集的剩余部分,创建与每个:high项对,直到我打的未来:low item。在这种情况下,我得到5个新记录,即:low和接下来的5个:high项目都是连续的;这些第一条记录看起来像

{:price-range 0.000064, :midpoint 1.12131, :extremes [{:low 1.12109, :time "2016-08-03T05:15:00.000000Z"}{:high 1.12173, :time "2016-08-03T04:30:00.000000Z"}]} 

的这5个记录第二会是什么样

{:price-range 0.000835, :midpoint 1.1215075, :extremes [{:low 1.12109, :time "2016-08-03T05:15:00.000000Z"}{:high 1.121925, :time "2016-08-03T00:00:00.000000Z"}]} 

等。

之后,我得到一个:low,所以我停在那里。

然后,我会转到第3项{:high 1.12173, :time "2016-08-03T04:30:00.000000Z"},然后“向下”,直到每个:low创建一对,直到我点击下一个:high。在这种情况下,我生成了0对,因为:high后面紧接着是另一个:high。同样在未来3:高的项目,这些都是由另一个:high

下一页紧接着我到了第7项{:high 1.12338, :time "2016-08-02T18:15:00.000000Z"}这应该产生与每个以下20个:low项目的一对。

我产生的结果将是创建的所有对的列表:

[{:price-range 0.000365, :midpoint 1.121272, :extremes [{:high 1.121455, :time "2016-08-03T05:15:00.000000Z"}{:low 1.12109, :time "2016-08-03T05:15:00.000000Z"}]} 
{:price-range 0.000064, :midpoint 1.12131, :extremes [{:low 1.12109, :time "2016-08-03T05:15:00.000000Z"}{:high 1.12173, :time "2016-08-03T04:30:00.000000Z"}]} 
... 

如果我实现这个使用类似Python的,我可能会使用一对夫妇的嵌套循环,使用break退出当我停止看到:high s与我的:low配对时,反之亦然,并在我遍历2个循环时将所有生成的记录累积到数组中。我只是不能找出一个很好的方法来使用Clojure攻击它...

任何想法?

+0

一般建议:将问题分解为小函数,并查看'reduce' /'reduced' +的使用示例。 –

+1

这是另一种常见面试问题的形式:“直方图可以存多少水”我想我会用这个方法让人们更加抽象地思考它 –

+0

继续并使用您的嵌套循环和中断。在Clojure中,您通常会使用'loop'和'recur'代替 – skrat

回答

5

首先你可以改写这个方式如下:

  1. ,你必须找到所有的边界点,其中:high之后:low,反之亦然
  2. 你需要的项目之前绑定,并且用它和绑定后的每个项目做些事情,但是直到下一次切换绑定为止。

为简单起见让我们使用下面的数据模型:

(def data0 [{:a 1} {:b 2} {:b 3} {:b 4} {:a 5} {:a 6} {:a 7}]) 

第一部分可以通过使用partition-by功能,即每对处理项目的功能改变它的值时将输入集合来实现:

user> (def step1 (partition-by (comp boolean :a) data0)) 
#'user/step1 
user> step1 
(({:a 1}) ({:b 2} {:b 3} {:b 4}) ({:a 5} {:a 6} {:a 7})) 

现在你需要把这些组中的每两个组合起来并操作它们。 ({:a 1})({:b 2} {:b 3} {:b 4})]] [({:b 2} {:b 3} {:b 4})({:5} {:一个6} {:7})]

这由partition功能实现:

user> (def step2 (partition 2 1 step1)) 
#'user/step2 
user> step2 
((({:a 1}) ({:b 2} {:b 3} {:b 4})) 
(({:b 2} {:b 3} {:b 4}) ({:a 5} {:a 6} {:a 7}))) 

你必须为每对组做一些事情。你可以用地图做:

user> (def step3 (map (fn [[lbounds rbounds]] 
        (map #(vector (last lbounds) %) 
         rbounds)) 
        step2)) 
#'user/step3 
user> step3 
(([{:a 1} {:b 2}] [{:a 1} {:b 3}] [{:a 1} {:b 4}]) 
([{:b 4} {:a 5}] [{:b 4} {:a 6}] [{:b 4} {:a 7}])) 

但因为你需要拼接列表,而不是分组的一个,你会想用mapcat,而不是map

user> (def step3 (mapcat (fn [[lbounds rbounds]] 
          (map #(vector (last lbounds) %) 
           rbounds)) 
         step2)) 
#'user/step3 
user> step3 
([{:a 1} {:b 2}] 
[{:a 1} {:b 3}] 
[{:a 1} {:b 4}] 
[{:b 4} {:a 5}] 
[{:b 4} {:a 6}] 
[{:b 4} {:a 7}]) 

这就是我们想要的结果(它几乎是,因为我们只是生成矢量,而不是地图)。

现在你可以用丝扣宏美化它:

(->> data0 
    (partition-by (comp boolean :a)) 
    (partition 2 1) 
    (mapcat (fn [[lbounds rbounds]] 
       (map #(vector (last lbounds) %) 
        rbounds)))) 

,让你完全一样的结果。

应用到你的数据会看起来几乎相同(与另一结果产生FN)

user> (defn hi-or-lo [item] 
     (item :high (item :low))) 
#'user/hi-or-lo 
user> 
(->> data 
    (partition-by (comp boolean :high)) 
    (partition 2 1) 
    (mapcat (fn [[lbounds rbounds]] 
       (let [left-bound (last lbounds) 
        left-val (hi-or-lo left-bound)] 
       (map #(let [right-val (hi-or-lo %) 
          diff (Math/abs (- right-val left-val))] 
         {:extremes [left-bound %] 
          :price-range diff 
          :midpoint (+ (min right-val left-val) 
             (/ diff 2))}) 
         rbounds)))) 
    (clojure.pprint/pprint)) 

它打印如下:

({:extremes 
    [{:high 1.121455, :time "2016-08-03T05:15:00.000000Z"} 
    {:low 1.12109, :time "2016-08-03T05:15:00.000000Z"}], 
    :price-range 3.6500000000017074E-4, 
    :midpoint 1.1212725} 
{:extremes 
    [{:low 1.12109, :time "2016-08-03T05:15:00.000000Z"} 
    {:high 1.12173, :time "2016-08-03T04:30:00.000000Z"}], 
    :price-range 6.399999999999739E-4, 
    :midpoint 1.12141} 
{:extremes 
    [{:low 1.12109, :time "2016-08-03T05:15:00.000000Z"} 
    {:high 1.121925, :time "2016-08-03T00:00:00.000000Z"}], 
    :price-range 8.350000000001412E-4, 
    :midpoint 1.1215074999999999} 
{:extremes 
    [{:low 1.12109, :time "2016-08-03T05:15:00.000000Z"} 
    {:high 1.12215, :time "2016-08-02T23:00:00.000000Z"}], 
    :price-range 0.001060000000000061, 
    :midpoint 1.12162} 
{:extremes 
    [{:low 1.12109, :time "2016-08-03T05:15:00.000000Z"} 
    {:high 1.12273, :time "2016-08-02T21:15:00.000000Z"}], 
    :price-range 0.0016400000000000858, 
    :midpoint 1.12191} 
{:extremes 
    [{:low 1.12109, :time "2016-08-03T05:15:00.000000Z"} 
    {:high 1.12338, :time "2016-08-02T18:15:00.000000Z"}], 
    :price-range 0.0022900000000001253, 
    :midpoint 1.1222349999999999} 
{:extremes 
    [{:high 1.12338, :time "2016-08-02T18:15:00.000000Z"} 
    {:low 1.119215, :time "2016-08-02T12:30:00.000000Z"}], 
    :price-range 0.004164999999999974, 
    :midpoint 1.1212975} 
{:extremes 
    [{:high 1.12338, :time "2016-08-02T18:15:00.000000Z"} 
    {:low 1.118755, :time "2016-08-02T12:00:00.000000Z"}], 
    :price-range 0.004625000000000101, 
    :midpoint 1.1210675} 
... 

作为回答关于“复杂的数据处理问题“,我会建议你查看clojure核心中所有集合的操作函数,然后尝试将任何任务分解到这些应用程序。当你需要超越他们的东西时,没有那么多的情况。

+0

梦幻般的答案 - 感谢您的教训!我可以按照你的想法来思考如何将问题分解为一系列较小的问题,并且我非常确定,在理解Clojure数据操作的基础上,我已经提升了至少2级。现在,我要完成你的建议了 - 再次通过clojure.core中的一组集合操作函数来工作,并尝试吸收它们如何清晰地勾画出它们的连接方式 – monch1962