How to solve Medication posession ratio (MPR) problem? - clojure

I am trying to solve medication possession ration problem.
I tried using intervals and then comparing them with observation period.
(ns clara-rules.mpr-new
(:require [clj-time.core :as t]
[clj-time.format :as f]))
(defn observe [interval]
(def start (map #(f/parse (f/formatter "dd MM yyyy") (% :start_)) interval))
(def end (map #(f/parse (f/formatter "dd MM yyyy") (% :end_)) interval))
)
(observe '({:start_ "20 01 2012" :end_ "20 02 2012"}
{:start_ "20 02 2012" :end_ "20 03 2012"}
{:start_ "20 04 2012" :end_ "20 05 2012"}
{:start_ "20 06 2012" :end_ "20 07 2012"}))
(defn calc[a b]
(def start_date (f/parse (f/formatter "dd MM yyyy") a)
)
(def end_date (f/parse (f/formatter "dd MM yyyy")b)
)
(def observation_period(t/in-days(t/interval start_date end_date)))
(println observation_period)
)
(calc "01 02 2012" "01 12 2012")
(defn mpr_ratio[]
(def overlp (map #(t/overlap (t/interval start_date end_date) (t/interval %1 %2))start end))
(def x (map #(t/in-days %)overlp))
(println x)
(def ratio (reduce +(map #(float(*(/ % observation_period)100))x)))
(println ratio)
)
(mpr_ratio)
I expect the calculated ratio of all the intervals and the observation period.

that's how the day intervals function could look like:
(defn process []
(let [from ["20 01 2012"
"20 03 2012"
"20 06 2012"
"20 08 2012"]
to ["20 02 2012"
"20 05 2012"
"20 07 2012"
"20 09 2012"]
get-date (partial f/parse (f/formatter "dd MM yyyy"))
days (map #(t/in-days (t/interval (get-date %1) (get-date %2)))
from to)]
days))
user> (process)
;;=> (31 61 30 31)
still, i advice you read some introduction to clojure

A few suggestions:
Use function with return value instead of relying on side effect (println and def inside a function) to obtain your result
Use def only for top level var, use let for any temporary var
inside a function
Create some single purpose functions (e.g. function to parse date; function to convert list of observations to dates), then use those functions to compose your solution
Use thread macro (e.g. -> ->>) to improve readability
Possible solution:
(def fmt
"default date formatter"
(f/formatter "dd MM yyyy"))
(def ->date
"utility function to convert string to date"
(partial f/parse fmt))
(->date "01 02 2012")
;; => #object[org.joda.time.DateTime 0x6e880ccd "2012-02-01T00:00:00.000Z"]
(defn ->observations
[intervals]
(->> intervals
(map (fn [{:keys [start_ end_]}]
{:start (->date start_)
:end (->date end_)}))))
(->observations '({:start_ "20 01 2012" :end_ "20 02 2012"}
{:start_ "20 02 2012" :end_ "20 03 2012"}))
;; => ({:start #object[org.joda.time.DateTime 0x4eb450bd "2012-01-20T00:00:00.000Z"], :end #object[org.joda.time.DateTime 0x558bd20f "2012-02-20T00:00:00.000Z"]} {:start #object[org.joda.time.DateTime 0x4009d145 "2012-02-20T00:00:00.000Z"], :end #object[org.joda.time.DateTime 0x42e32d6 "2012-03-20T00:00:00.000Z"]})
(defn mpr_ratio
[start_date end_date intervals]
(let [intrvrl (t/interval start_date end_date)
obsrv-prd (t/in-days intrvrl)]
(->> (map t/interval (map :start intervals) (map :end intervals))
(map (partial t/overlap intrvrl))
(map t/in-days)
(map #(-> %
(/ obsrv-prd)
(* 100.0)))
(reduce +))))
(mpr_ratio (->date "01 02 2012")
(->date "01 12 2012")
(->observations '({:start_ "20 01 2012" :end_ "20 02 2012"}
{:start_ "20 02 2012" :end_ "20 03 2012"}
{:start_ "20 04 2012" :end_ "20 05 2012"}
{:start_ "20 06 2012" :end_ "20 07 2012"})))
;; => 35.526315789473685
UPDATE - PDC utility function
(defn covered [state interval]
(if (some #(t/overlaps? interval %) state)
(->> state
(map #(if (t/overlaps? interval %)
(t/interval (t/min-date (t/start %) (t/start interval))
(t/max-date (t/end %) (t/end interval)))
%))
(into (empty state)))
(conj state interval)))
(covered #{} (t/interval (->date "01 02 2012") (->date "05 02 2012")))
;; => #{#object[org.joda.time.Interval 0x30addc0b "2012-02-01T00:00:00.000Z/2012-02-05T00:00:00.000Z"]}
(covered *1 (t/interval (->date "04 02 2012") (->date "07 02 2012")))
;; => #{#object[org.joda.time.Interval 0x7f8893c1 "2012-02-01T00:00:00.000Z/2012-02-07T00:00:00.000Z"]}
(covered *1 (t/interval (->date "02 03 2012") (->date "07 03 2012")))
;; => #{#object[org.joda.time.Interval 0x7f8893c1 "2012-02-01T00:00:00.000Z/2012-02-07T00:00:00.000Z"] #object[org.joda.time.Interval 0x67adc8d1 "2012-03-02T00:00:00.000Z/2012-03-07T00:00:00.000Z"]}
(reduce + (map (comp inc t/in-days) *1))
;; => 13
pdc function in full: (note that only one line needs to be added)
(defn pdc_ratio
[start_date end_date intervals]
(let [intrvrl (t/interval start_date end_date)
obsrv-prd (t/in-days intrvrl)]
(->> (map t/interval (map :start intervals) (map :end intervals))
(map (partial t/overlap intrvrl))
;; get covered days only
(reduce covered #{})
(map t/in-days)
(map #(-> %
(/ obsrv-prd)
(* 100.0)))
(reduce +))))

Related

Looking for a way to partition date on a monthly basis in Clojure

Want to split a date range into monthly chunks.
example Input - [10/20/2019 - 12/20/2019]
example output - { [10/20/2019 10/31/2019] [11/01/2019 11/30/2019] [12/012019 12/20/2019] }
Thank you
You can use java-time:
(refer-clojure :exclude [range iterate format max min])
(use 'java-time)
(->> (iterate plus (local-date 2019 10 20) (days 1))
(take-while #(before? % (local-date 2019 12 21)))
(partition-by month)
(map (fn [dates] [(first dates) (last dates)])))
Output:
([#object[java.time.LocalDate 0x26c16faf "2019-10-20"]
#object[java.time.LocalDate 0x4113c834 "2019-10-31"]]
[#object[java.time.LocalDate 0x7d0a5212 "2019-11-01"]
#object[java.time.LocalDate 0x249fe02f "2019-11-30"]]
[#object[java.time.LocalDate 0x7345f070 "2019-12-01"]
#object[java.time.LocalDate 0x26d66577 "2019-12-20"]])
here is a simple draft of what you can do (with java interop, no external libs):
first of all let's make an iteration by month, starting with the specified one:
(defn by-month [[mm yyyy]]
(iterate #(.plusMonths % 1)
(java.time.YearMonth/of yyyy mm)))
user> (take 4 (by-month [10 2019]))
;;=> (#object[java.time.YearMonth 0x62fc8302 "2019-10"]
;; #object[java.time.YearMonth 0x1a7bc211 "2019-11"]
;; #object[java.time.YearMonth 0x6a466e83 "2019-12"]
;; #object[java.time.YearMonth 0x652ac30f "2020-01"])
then get start and end date for each YearMonth:
(defn start-end [^java.time.YearMonth ym]
[(.atDay ym 1) (.atEndOfMonth ym)])
;;=> ([#object[java.time.LocalDate 0xe880abb "2019-10-01"]
;; #object[java.time.LocalDate 0x54aadf50 "2019-10-31"]]
;; [#object[java.time.LocalDate 0x14c1b42d "2019-11-01"]
;; #object[java.time.LocalDate 0x32d0a22c "2019-11-30"]])
now, wrap it up in a function of ranges by your input dates:
(defn day-range [[mm1 dd1 yyyy1] [mm2 dd2 yyyy2]]
(let [start (java.time.LocalDate/of yyyy1 mm1 dd1)
end (java.time.LocalDate/of yyyy2 mm2 dd2)
internal (->> [mm1 yyyy1]
by-month
(mapcat start-end)
(drop 1)
(take-while #(neg? (compare % end))))]
(partition 2 `(~start ~#internal ~end))))
user> (day-range [10 20 2019] [12 20 2019])
;;=> ((#object[java.time.LocalDate 0x6a8f92f2 "2019-10-20"]
;; #object[java.time.LocalDate 0x10135df3 "2019-10-31"])
;; (#object[java.time.LocalDate 0x576bcff7 "2019-11-01"]
;; #object[java.time.LocalDate 0x7b5ed908 "2019-11-30"])
;; (#object[java.time.LocalDate 0x6b2117a9 "2019-12-01"]
;; #object[java.time.LocalDate 0x57bf0864 "2019-12-20"]))
now you can postprocess each start-end pair as you need:
(map (fn [[^java.time.LocalDate start ^java.time.LocalDate end]]
(let [fmt (java.time.format.DateTimeFormatter/ofPattern "MM/dd/yyyy")]
[(.format start fmt) (.format end fmt)]))
(day-range [10 20 2019] [12 20 2019]))
;;=> (["10/20/2019" "10/31/2019"]
;; ["11/01/2019" "11/30/2019"]
;; ["12/01/2019" "12/20/2019"])
another way is to iterate by day, and then partition by [month year], collecting first-last afterwards:
(defn ranges [[mm1 dd1 yyyy1] [mm2 dd2 yyyy2]]
(let [start (java.time.LocalDate/of yyyy1 mm1 dd1)
end (java.time.LocalDate/of yyyy2 mm2 dd2)]
(->> start
(iterate (fn [^java.time.LocalDate curr] (.plusDays curr 1)))
(take-while (fn [^java.time.LocalDate dt] (not (pos? (compare dt end)))))
(partition-by (fn [^java.time.LocalDate dt] [(.getMonthValue dt) (.getYear dt)]))
(map (juxt first last)))))
user> (ranges [10 20 2019] [12 20 2019])
;;=> ([#object[java.time.LocalDate 0x383f6a9e "2019-10-20"]
;; #object[java.time.LocalDate 0x2ca14c39 "2019-10-31"]]
;; [#object[java.time.LocalDate 0x74d1974 "2019-11-01"]
;; #object[java.time.LocalDate 0x5f6c16cc "2019-11-30"]]
;; [#object[java.time.LocalDate 0x74f63a42 "2019-12-01"]
;; #object[java.time.LocalDate 0x4b90c388 "2019-12-20"]])
which produces some unneeded intermediate vals, but also gives you a way to split ranges however you want.
Straight Java interop is the way to go here:
(let [start-ld (LocalDate/parse "2019-10-20")
start-bom (.with start-ld (TemporalAdjusters/firstDayOfMonth))
start-eom (.with start-ld (TemporalAdjusters/lastDayOfMonth))]
start-bom => #object[java.time.LocalDate 0x1a69aaa8 "2019-10-01"]
start-eom => #object[java.time.LocalDate 0x329970b5 "2019-10-31"]
You can increment the month like so:
next-bom (.plusMonths start-bom 1)
to get
next-bom => #object[java.time.LocalDate 0x21ced418 "2019-11-01"]
and then you can write a loop.

Clojure - StackOverflowError while iterating over lazy collection

I am currently implementing solution for one of Project Euler problems, namely Sieve of Eratosthenes (https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes), in Clojure. Here's my code:
(defn cross-first-element [coll]
(filter #(not (zero? (rem % (first coll)))) coll))
(println
(last
(map first
(take-while
(fn [[primes sieve]] (not (empty? sieve)))
(iterate
(fn [[primes sieve]] [(conj primes (first sieve)) (cross-first-element sieve)])
[[] (range 2 2000001)])))))
The basic idea is to have two collections - primes already retrieved from the sieve, and the remaining sieve itself. We start with empty primes, and until the sieve is empty, we pick its first element and append it to primes, and then we cross out the multiples of it from the sieve. When it's exhausted, we know we have all prime numbers from below two millions in the primes.
Unfortunately, as good as it works for small upper bound of sieve (say 1000), it causes java.lang.StackOverflowError with a long stacktrace with repeating sequence of:
...
clojure.lang.RT.seq (RT.java:531)
clojure.core$seq__5387.invokeStatic (core.clj:137)
clojure.core$filter$fn__5878.invoke (core.clj:2809)
clojure.lang.LazySeq.sval (LazySeq.java:42)
clojure.lang.LazySeq.seq (LazySeq.java:51)
...
Where is the conceptual error in my solution? How to fix it?
the reason for this is the following: since the filter function in your cross-first-element is lazy, it doesn't actually filter your collection on every iterate step, rather it 'stacks' filter function calls. This leads to the situation that when you are going to actually need the resulting element, the whole load of test functions would be executed, roughly like this:
(#(not (zero? (rem % (first coll1))))
(#(not (zero? (rem % (first coll2))))
(#(not (zero? (rem % (first coll3))))
;; and 2000000 more calls
leading to stack overflow.
the simplest solution in your case is to make filtering eager. You can do it by simply using filterv instead of filter, or wrap it into (doall (filter ...
But still your solution is really slow. I would rather use loop and native arrays for that.
You have (re-)discovered that having nested lazy sequences can sometimes be problematic. Here is one example of what can go wrong (it is non-intuitive).
If you don't mind using a library, the problem is much simpler with a single lazy wrapper around an imperative loop. That is what lazy-gen and yield give you (a la "generators" in Python):
(ns tst.demo.core
(:use demo.core tupelo.test)
(:require [tupelo.core :as t]))
(defn unprime? [primes-so-far candidate]
(t/has-some? #(zero? (rem candidate %)) primes-so-far))
(defn primes-generator []
(let [primes-so-far (atom [2])]
(t/lazy-gen
(t/yield 2)
(doseq [candidate (drop 3 (range))] ; 3..inf
(when-not (unprime? #primes-so-far candidate)
(t/yield candidate)
(swap! primes-so-far conj candidate))))))
(def primes (primes-generator))
(dotest
(is= (take 33 primes)
[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 ])
; first prime over 10,000
(is= 10007 (first (drop-while #(< % 10000) primes)))
; the 10,000'th prime (https://primes.utm.edu/lists/small/10000.txt)
(is= 104729 (nth primes 9999)) ; about 12 sec to compute
)
We could also use loop/recur to control the loop, but it's easier to read with an atom to hold the state.
Unless you really, really need a lazy & infinite solution, the imperative solution is so much simpler:
(defn primes-upto [limit]
(let [primes-so-far (atom [2])]
(doseq [candidate (t/thru 3 limit)]
(when-not (unprime? #primes-so-far candidate)
(swap! primes-so-far conj candidate)))
#primes-so-far))
(dotest
(is= (primes-upto 100)
[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97]) )

Using Clojure's filter to find entry with minimum value

I developed a function using Clojure that returns the row with the minimum value determined. Here is the function that does that:
(#(reduce (fn [r1 r2] (let [v1 (read-string (apply str (clojure.string/split (get r1 %2) (re-pattern " "))))
v2 (read-string (apply str (clojure.string/split (get r2 %2) (re-pattern " "))))]
(if (< v1 v2) r1 r2))) %1) [[1 "2007 05 18"] [2 "2004 06 15"] [3 "2004 06 10"]] 1)
Returns:
[3 "2004 06 10"]
Question is I want to implement the above function as predicate for a filter function that will return the same result as the above function. Please how do I do this? Thanks
Given the reasonable assumption that what you have are dates, and you want the row with the lower date.
This is an approach using sort-by.
(sort-by (fn [[idx date]]
(let [[_ y m d] (re-find #"\d{4} \d{2} \d{2}" date)]
y))
[[1 "2007 05 18"] [2 "2004 06 15"] [3 "2004 06 10"]])
Or you can parse the input string as a date and avoid the regex all together with the added benefit of properly sorting by year, month and day.
(sort-by (fn [[idx date]]
(.parse (java.text.SimpleDateFormat. "yyyy MM dd") date))
[[1 "2007 05 18"] [2 "2004 06 15"] [3 "2004 06 10"]])
=> ([3 "2004 06 10"] [2 "2004 06 15"] [1 "2007 05 18"])
Then just take first or last of the result for the lowest or highest valued row.
Using filter makes no sense, since filter doesn't accumulate state, which you need in order to compare rows among each other. (That's what you're doing manually with your reduce function).
Filtering the sequence with a predicate would give you all rows matching the predicate, but would say nothing about the relationship between rows themselves.
That's why sort-by is the idiomatic way to sort with a custom function in this situation.
Even better, if you assume the lexicographic sort of your stringified date matches with the order you want, you can just do:
(sort-by second [[1 "2007 05 18"] [2 "2004 06 15"] [3 "2004 06 10"]])
=> ([3 "2004 06 10"] [2 "2004 06 15"] [1 "2007 05 18"])

Combining Two clojure.walk/postwalk Calls Into One

I have the following data:
28 (def example {"1ce9b863-5681-4660-85e7-fbd0cc184aed"
29 {"58825b50-23bc-4204-8f8d-c9a9d3ac8beb" {},
30 "4b1763f9-8380-4507-9a8f-5c86878e49a9" {},
31 "160f34ac-68b9-4c8e-930b-1ab6df895df4" {}},
32 "6378daf6-3b7f-4cf4-8156-a50cf5f7b6ef"
33 {"669fe949-057f-43c0-af7b-ff39594a183d" {},
34 "73d2a203-e3c1-4d2f-aaf8-a9f2e870792b" {},
35 "8c9c57a0-d20d-4474-9afb-c9d17df83a91" {},
36 "94bf72cb-01cd-4430-b669-b2e954b5639b"
37 {"ba96a425-a3f0-4ce5-8c19-6ea9add14013" {},
38 "1ceff8fe-a0a8-46ad-81a8-d13fb837aaf6" {}}}})
39
40 (def titles (list {:id "58825b50-23bc-4204-8f8d-c9a9d3ac8beb", :title "Low"}
41 {:id "4b1763f9-8380-4507-9a8f-5c86878e49a9", :title "Medium"}
42 {:id "160f34ac-68b9-4c8e-930b-1ab6df895df4", :title "High"}
43 {:id "1ce9b863-5681-4660-85e7-fbd0cc184aed", :title "Priority"}
44 {:id "1ceff8fe-a0a8-46ad-81a8-d13fb837aaf6", :title "Drafting"}
45 {:id "ba96a425-a3f0-4ce5-8c19-6ea9add14013", :title "Brainstorm"}
46 {:id "94bf72cb-01cd-4430-b669-b2e954b5639b", :title "Planning"}
47 {:id "8c9c57a0-d20d-4474-9afb-c9d17df83a91", :title "Testing"}
48 {:id "73d2a203-e3c1-4d2f-aaf8-a9f2e870792b", :title "Implementation"}
49 {:id "669fe949-057f-43c0-af7b-ff39594a183d", :title "Completed"}
50 {:id "6378daf6-3b7f-4cf4-8156-a50cf5f7b6ef", :title "Status"}))
I am attempting to turn this data into a nested list structure using sablono/hiccup style syntax. Currently I have the following working solution:
52 (defn id->title [x]
53 (let [tuples (map (fn [x] [(:id x) (:title x)]) titles)]
54 (first (for [[k t] tuples :when (= k x)] t))))
57 (->> example
58 (clojure.walk/postwalk
59 (fn [x] (if-not (string? x)
60 (vec x) x)))
61 (clojure.walk/postwalk
62 (fn [x] (if (vector? x)
63 (if (vector? (first x))
64 (vec (cons :ul x))
65 (vec (cons :li x)))
66 (id->title x)))))
This results in:
[:ul
[:li "Priority"
[:ul
[:li "Low" [:li]]
[:li "Medium" [:li]]
[:li "High" [:li]]]]
[:li "Status"
[:ul
[:li "Completed" [:li]]
[:li "Implementation" [:li]]
[:li "Testing" [:li]]
[:li "Planning"
[:ul
[:li "Brainstorm" [:li]]
[:li "Drafting" [:li]]]]]]]
How can I simplify this to use one walk? I am also considering replacing titles with a map for an efficient lookup (I am pulling all of this data from a Neo4j).
Given the data you provided, you could try something like the following:
(defn id->title [x]
(->> titles (filter #(= (:id %) x)) first :title))
(defn format-data [structure]
(clojure.walk/postwalk
(fn [x]
(if (map? x)
(into [:ul] (map (fn [[k v]]
(if (= v [:ul])
[:li (id->title k)]
[:li (id->title k) v]))
(seq x)))
x))
structure))
During the postwalk, this will turn each map into a vector representing an unordered list (even the empty maps) and each key-value pair into a vector representing a list item. The (if (= v [:ul]) ...) ensures that the empty unordered lists are removed from the final structure.
Running (pprint (format-data example)) gives the following results:
[:ul
[:li "Priority" [:ul [:li "Low"] [:li "Medium"] [:li "High"]]]
[:li
"Status"
[:ul
[:li "Completed"]
[:li "Implementation"]
[:li "Testing"]
[:li "Planning" [:ul [:li "Brainstorm"] [:li "Drafting"]]]]]]

Clojure - how to make reference to an element in a list

I want to know how to make reference to the smallest elements inside a series of lists that i have partitioned like so:
data(map(keyword :counter)querieddata)
sortedlist(sort > tosort)
part(into [] (partition-all (/ (count data) 10) sortedlist))
zi(zipmap [:a :b :c :d :e :f :g :h :i] part)
which gives me results like:
[:a(40 40 36 33) :b(33 30 27 25) :c(25 19 18 5)]
I want to make reference to the smallest number in each list, as for example in the lists above, I would have the number 33 returned for a, 25 for b, and so on.
I would have assumed I could have made reference to the keyword and then used apply max but I am getting an error with this code:
a(map(keyword :a)zi)
minimum(apply min a)
Any help is appreciated greatly!
Your code doesn't look at all like valid Clojure and presuming your input is actually a map and not a vector then this would work:
(into {}
(map (fn [[k v]]
[k (apply max v)])
{:a '(40 40 36 33)
:b '(33 30 27 25)
:c '(25 19 18 5)}))
; => {:a 40, :c 25, :b 33}
Apply min to each element in part and then zipmap the result like you did for zi:
smallest-numbers (map (partial apply min) part)
smallest-numbers-zipped (zipmap [:a :b :c :d :e :f :g :h :i] smallest-numbers)
Also, there's no need to coerce part to a vector, unless it's specifically needed to be a vector elsewhere. The code you've given us will work just as well if part is defined as (partition-all (/ (count data) 10) sortedlist).
Starting at your last result value [:a(40 40 36 33) :b(33 30 27 25) :c(25 19 18 5)] you can use the next function:
(def your-result '[:a(40 40 36 33) :b(33 30 27 25) :c(25 19 18 5)])
(->> (map vec (partition 2 your-result))
(map #(vector (first %) (apply min (second %))))
(into {})
)
To obtain this output
{:a 33 :b 25 :c 5}
And then you can use your keywords as:
(:a (->> (map vec (partition 2 your-result))
(map #(vector (first %) (apply min (second %))))
(into {})
))
=> 33