Adds window-by-time
This commit is contained in:
parent
35a5527b7e
commit
24b2397d76
4 changed files with 72 additions and 3 deletions
|
|
@ -4,7 +4,7 @@ More transducers and reducing functions for Clojure!
|
||||||
|
|
||||||
[](https://travis-ci.org/cgrand/xforms)
|
[](https://travis-ci.org/cgrand/xforms)
|
||||||
|
|
||||||
Transducers: `reduce`, `into`, `by-key`, `partition`, `pad`, `for` and `window`.
|
Transducers: `reduce`, `into`, `by-key`, `partition`, `pad`, `for`, `window` and `window-by-time`.
|
||||||
|
|
||||||
Reducing functions: `str`, `str!`, `avg`, `count`, `juxt`, `juxt-map` and `first`.
|
Reducing functions: `str`, `str!`, `avg`, `count`, `juxt`, `juxt-map` and `first`.
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ Transducing context: `transjuxt` (for performing several transductions in a sing
|
||||||
Add this dependency to your project:
|
Add this dependency to your project:
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
[net.cgrand/xforms "0.1.1-SNAPSHOT"]
|
[net.cgrand/xforms "0.2.0"]
|
||||||
```
|
```
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
(defproject net.cgrand/xforms "0.1.2-SNAPSHOT"
|
(defproject net.cgrand/xforms "0.2.0"
|
||||||
:description "Extra transducers for Clojure"
|
:description "Extra transducers for Clojure"
|
||||||
#_#_:url "http://example.com/FIXME"
|
#_#_:url "http://example.com/FIXME"
|
||||||
:license {:name "Eclipse Public License"
|
:license {:name "Eclipse Public License"
|
||||||
|
|
|
||||||
|
|
@ -280,6 +280,59 @@
|
||||||
(vreset! vi (let [i (inc i)] (if (= n i) 0 i)))
|
(vreset! vi (let [i (inc i)] (if (= n i) 0 i)))
|
||||||
(rf acc (f (vreset! vwacc (f (invf wacc x') x))))))))))))
|
(rf acc (f (vreset! vwacc (f (invf wacc x') x))))))))))))
|
||||||
|
|
||||||
|
(defn window-by-time
|
||||||
|
"Returns a transducer which computes a windowed accumulator over chronologically sorted items.
|
||||||
|
|
||||||
|
timef is a function from one item to its scaled timestamp (as a double). The window length is always 1.0
|
||||||
|
so timef must normalize timestamps. For example if timestamps are in seconds (and under the :ts key),
|
||||||
|
to get a 1-hour window you have to use (fn [x] (/ (:ts x) 3600.0)) as timef.
|
||||||
|
|
||||||
|
n is the integral number of steps by which the window slides. With a 1-hour window, 4 means that the window slides every 15 minutes.
|
||||||
|
|
||||||
|
f and invf work like in #'window."
|
||||||
|
[timef n f invf]
|
||||||
|
(let [timef (fn [x] (long (Math/floor (* n (timef x)))))]
|
||||||
|
(fn [rf]
|
||||||
|
(let [dq (java.util.ArrayDeque.)
|
||||||
|
vwacc (volatile! (f))
|
||||||
|
flush!
|
||||||
|
(fn [acc ^long from-ts ^long to-ts]
|
||||||
|
(loop [ts from-ts acc acc wacc @vwacc]
|
||||||
|
(let [x (.peekFirst dq)]
|
||||||
|
(cond
|
||||||
|
(= ts (timef x))
|
||||||
|
(do
|
||||||
|
(.pollFirst dq)
|
||||||
|
(recur ts acc (invf wacc x)))
|
||||||
|
(= ts to-ts)
|
||||||
|
(do
|
||||||
|
(vreset! vwacc wacc)
|
||||||
|
acc)
|
||||||
|
:else
|
||||||
|
(let [acc (rf acc (f wacc))]
|
||||||
|
(if (reduced? acc)
|
||||||
|
(do
|
||||||
|
(vreset! vwacc wacc)
|
||||||
|
acc)
|
||||||
|
(recur (inc ts) acc wacc)))))))]
|
||||||
|
(fn
|
||||||
|
([] (rf))
|
||||||
|
([acc]
|
||||||
|
(let [acc (if-not (.isEmpty dq)
|
||||||
|
(unreduced (rf acc (f @vwacc)))
|
||||||
|
acc)]
|
||||||
|
(rf acc)))
|
||||||
|
([acc x]
|
||||||
|
(let [limit (- (timef x) n)
|
||||||
|
prev-limit (if-some [prev-x (.peekLast dq)]
|
||||||
|
(- (timef prev-x) n)
|
||||||
|
limit)
|
||||||
|
_ (.addLast dq x) ; so dq is never empty for flush!
|
||||||
|
acc (flush! acc prev-limit limit)]
|
||||||
|
(when-not (reduced? acc)
|
||||||
|
(vswap! vwacc f x))
|
||||||
|
acc)))))))
|
||||||
|
|
||||||
(defn count ([] 0) ([n] n) ([n _] (inc n)))
|
(defn count ([] 0) ([n] n) ([n _] (inc n)))
|
||||||
|
|
||||||
(defn juxt
|
(defn juxt
|
||||||
|
|
|
||||||
|
|
@ -71,3 +71,19 @@
|
||||||
4 (range 16)))
|
4 (range 16)))
|
||||||
(is (trial (x/pad 8 (repeat :pad))
|
(is (trial (x/pad 8 (repeat :pad))
|
||||||
4 (range 16)))))
|
4 (range 16)))))
|
||||||
|
|
||||||
|
(deftest window-by-time
|
||||||
|
(is (= (into
|
||||||
|
[]
|
||||||
|
(x/window-by-time :ts 4
|
||||||
|
(fn
|
||||||
|
([] clojure.lang.PersistentQueue/EMPTY)
|
||||||
|
([q] (vec q))
|
||||||
|
([q x] (conj q x)))
|
||||||
|
(fn [q _] (pop q)))
|
||||||
|
(map (fn [x] {:ts x}) (concat (range 0 2 0.5) (range 3 5 0.25))))
|
||||||
|
[[{:ts 0}] [{:ts 0}] [{:ts 0} {:ts 0.5}] [{:ts 0} {:ts 0.5}] [{:ts 0.5} {:ts 1.0}] [{:ts 0.5} {:ts 1.0}]
|
||||||
|
[{:ts 1.0} {:ts 1.5}] [{:ts 1.0} {:ts 1.5}] [{:ts 1.5}] [{:ts 1.5}] [] [] [{:ts 3}] [{:ts 3} {:ts 3.25}]
|
||||||
|
[{:ts 3} {:ts 3.25} {:ts 3.5}] [{:ts 3} {:ts 3.25} {:ts 3.5} {:ts 3.75}] [{:ts 3.25} {:ts 3.5} {:ts 3.75} {:ts 4.0}]
|
||||||
|
[{:ts 3.5} {:ts 3.75} {:ts 4.0} {:ts 4.25}] [{:ts 3.75} {:ts 4.0} {:ts 4.25} {:ts 4.5}]
|
||||||
|
[{:ts 4.0} {:ts 4.25} {:ts 4.5} {:ts 4.75}]])))
|
||||||
Loading…
Reference in a new issue