I have two lists. For e.g.: list A is [1 2 3 2 2 1] and list B is [1.2 2.2 1 1 1 1]. I want to have the unique numbers of list A on the x-axis and sum of the corresponding entries in list B. For eg: For the above example, I want to plot {(1,2.2),(2,4.2),(3,1)} as a histogram ( not a scatter plot).
My requirement involves two steps.
First to sum values in list B for each unique value in list A
Plotting these sums against the corresponding values in list A as a histogram.
Can you please help me.
Edit:
Here is my attempt, based on the little I could understand from reading other answers on SO:
(def A [1 2 3 2 1])
(def B [1.2 2.3 2 1 1])
(for [x (distinct A)] (map first
(filter #(= (second %) x)
(map-indexed vector A))))
;; This gives the indices for each unique element in A
;; In this case, it gives ((0 4) (1 3) (2))
I am unable to figure out how to find how to get corresponding sum from list B. I tried the following but it does not work.
(apply nth B (map first
(filter #(= (second %) 1)
(map-indexed vector A))) )
;; In this case, it gives on the first element i.e. 1.2
As you can see, I am new to Clojure and functional programming languages. Can you please point me towards some examples which have solved similar problems?
Thanks in advance.
Edit:
Final solution for the first task:
(for [x (distinct A)] (reduce + 0.0 (map #(nth B %) (map first
(filter #(= (second %) x)
(map-indexed vector A))) ) ) )
;; This gives me the correct output (2.2 3.3 2.0)
P.S: I did not understand this concept of using (map #(nth B%)... I just stumbled onto it from other examples.
For the first task, I guess this way is a bit simpler:
(def A [1 2 3 2 2 1])
(def B [1.2 2.2 1 1 1 1])
(def C
(reduce (partial merge-with +)
(map hash-map A B))) ; Vector of key-values [{1 1.2} {2 2.2} ...]
; {1 2.2, 2 4.2, 3 1}
For the second task, there are many chart library options out there. I picked up clj-xchart as an example:
(require '[com.hypirion.clj-xchart :as c])
(let [x-values (keys C)
min-x (apply min x-values)
max-x (apply max x-values)]
(c/view
(c/category-chart
{"C" C}
{:title "Example"
:legend {:visible? false}
:x-axis {:order (range min-x max-x)}
:theme :ggplot2})))
And the final plot:
Related
I'm a newbie to Clojure and need some help with my code.
Here's the situation:
My first table (products) looks like : (product_name price)
([1 (candies 6.5)]
[2 (sweets 1.75)]
[3 (jam 2.99)]
[4 (gum 1.25)])
My first table (products) looks like : (customer_name product_name quantity)
([1 (Sara candies 3)]
[2 (Joe jam 3)]
[3 (Sara gum 1)])
What I'm trying to do is if i type for example Sara, I'll get the sum of the sales made by Sara, which means: (3 * $6.5 + 1 * $1.25) = $20.75 (in this case)
I'm fine for the input part (I get the name of the customer as an input from the terminal)
However, my code:
(defn sales_prices_with_cond [name_cus] (map
(fn [y z]
(if (= (str name_cus) (str (nth (first z) 1)))
list (* (Double. (nth (second y) 1)) (Integer/parseInt (nth (second z) 2))))
)
products
sales))
(println (reduce + sales_prices_with_cond "Sara"))
Gives me the sum of ALL the sales*quantities. It's like the condition is skipped or maybe not well written ...
I also tried with (some) and got the same result...
Please Help :') .
Your tables don't look like valid Clojure code which makes it difficult to help you - are they strings? I would transform the first one into a map of the form:
(def prices {"candies" 6.5}) ;etc
Your second table would be something like:
(def sales [["sarah" "candies" 3]]) ;etc
And then your main function:
(defn sales-by-customer [name-cus]
(->> sales
(filter #(= (first %) name-cus))
(map #(* (last %) (prices (second %)))
(reduce +)))
I've tried this for so many nights that I've finally given up on myself. Seems like an extremely simple problem, but I guess I'm just not fully understanding Clojure as well as I should be (I partially attribute that to my almost sole experience with imperative languages). The problem is from hackerrank.com
Here is the problem:
Problem Statement
Given a list repeat each element of the list n times. The input and output
portions will be handled automatically by the grader.
Input Format
First line has integer S where S is the number of times you need to repeat
elements. After this there are X lines, each containing an integer. These are the
X elements of the array.
Output Format
Repeat each element of the original list S times. So you have to return
list/vector/array of S*X integers. The relative positions of the values should be
same as the original list provided as input.
Constraints
0<=X<=10
1<=S<=100
So, given:
2
1
2
3
Output:
1
1
2
2
3
3
I've tried:
(fn list-replicate [num list]
(println (reduce
(fn [element seq] (dotimes [n num] (conj seq element)))
[]
list))
)
But that just gives me an exception. I've tried so many other solutions, and this probably isn't one of my better ones, but it was the quickest one I could come up with to post something here.
(defn list-replicate [num list]
(mapcat (partial repeat num) list))
(doseq [x (list-replicate 2 [1 2 3])]
(println x))
;; output:
1
1
2
2
3
3
The previous answer is short and it works, but it is very "compressed" and is not easy for new people to learn. I would do it in a simpler and more obvious way.
First, look at the repeat function:
user=> (doc repeat)
-------------------------
clojure.core/repeat
([x] [n x])
Returns a lazy (infinite!, or length n if supplied) sequence of xs.
user=> (repeat 3 5)
(5 5 5)
So we see how to easily repeat something N times.
What if we run (repeat n ...) on each element of the list?
(def N 2)
(def xvals [1 2 3] )
(for [curr-x xvals]
(repeat N curr-x))
;=> ((1 1) (2 2) (3 3))
So we are getting close, but we have a list-of-lists for output. How to fix? The simplest way is to just use the flatten function:
(flatten
(for [curr-x xvals]
(repeat N curr-x)))
;=> (1 1 2 2 3 3)
Note that both repeat and for are lazy functions, which I prefer to avoid unless I really need them. Also, I usually prefer to store my linear collections in a concrete vector, instead of a generic "seq" type. For these reasons, I include an extra step of forcing the results into a single (eagar) vector for the final product:
(defn list-replicate [num-rep orig-list]
(into []
(flatten
(for [curr-elem xvals]
(repeat N curr-elem)))))
(list-replicate N xvals)
;=> [1 1 2 2 3 3]
I would suggest building onto Alan's solution and instead of flatten use concat as this will preserve the structure of the data in case you have input sth like this [[1 2] [3 4]].
((fn [coll] (apply concat (for [x coll] (repeat 2 x)))) [[1 2] [3 4]])
output: => ([1 2] [1 2] [3 4] [3 4])
unlike with flatten, which does the following
((fn [coll] (flatten (for [x coll] (repeat 2 x)))) [[1 2] [3 4]])
output: => (1 2 1 2 3 4 3 4)
as for simple lists e.g. '(1 2 3), it works the same:
((fn [coll] (apply concat (for [x coll] (repeat 2 x)))) '(1 2 3))
output => (1 1 2 2 3 3)
(reduce #(count (map println (repeat %1 %2))) num list)
I am working on a function that will split a sequence of dates (or anything else) into a number of sequences contained within a vector based on a given number x.
(date1 date2 date3 date4 date5 date6 date7)
So given the list of dates above and passing in the variable 2 it will produce the vector below.
[(date1 date2) (date3 date4) (date5 date6) (date7)]
The code I have so far is below but all it returns is a vector containing nil.
(defn date-splitter [date-count dates x]
(loop [i date-count, current-split dates, split-dates (vector)]
(if (<= i x)
(conj split-dates (get current-split 1))
(let [s (split-at x current-split)]
(recur (int (- i x)) (get s 1) (conj split-dates (get s 0)))))))
I have also had a look at the split-with function, thinking that I could use it to split the sequence when the modulus of the index divided by x is zero, but I haven't had any luck with this.
Any help would be greatly appreciated.
David.
Take a look at split's cousin partition
=> (partition-all 2 '(1 2 3 4 5 6 7))
((1 2) (3 4) (5 6) (7))
If you want to do index based operations, you can use map-indexed and keep-indexed.
=> (map-indexed (fn [idx itm] [(Math/floor (/ idx 2)) itm]) [1 2 3 4 5 6 7])
([0.0 1] [0.0 2] [1.0 3] [1.0 4] [2.0 5] [2.0 6] [3.0 7])
In FP, non-index based operations are usually the better option though.
Index based solutions can be considered code smell.
There's several ways of doing partition the FP way without index. Ankur's is a great example of how to solve this if partition-all wouldn't have been in core clojure.
(defn date-splitter [n v]
(if (not (seq v))
[]
(lazy-cat [(take n v)] (date-splitter n (drop n v)))))
user=> (date-splitter 3 [1,2,3,4])
((1 2 3) (4))
I have a collection which I'd like to split by an arbitrary percentage. The actual problem I'm trying to solve is to split a dataset into a training and cross-validation set.
The destination of each element should be chosen at random, but each source element should appear only once in the result and the size of the partitions is fixed. If the source collection has duplicates, the duplicates could appear in different output partitions or the same.
I have this implementation:
(defn split-shuffled
"Returns a 2 element vector partitioned by the percentage
specified by p. Elements are selected at random. Each
element of the source collection will appear only once in
the result."
[c p]
(let [m (count c)
idxs (into #{} (take (* m p) (shuffle (range m))))
afn (fn [i x] (if (idxs i) x))
bfn (fn [i x] (if-not (idxs i) x))]
[(keep-indexed afn c) (keep-indexed bfn c)]))
repl> (split-shuffled (range 10) 0.2)
[(4 6) (0 1 2 3 5 7 8 9)]
repl> (split-shuffled (range 10) 0.4)
[(1 4 6 7) [0 2 3 5 8 9)]
But I'm not happy that keep-indexed is called twice.
How can this be improved?
EDIT: I originally wanted to keep the order in the partitions, but I dropped that requirement without re-thinking, so #mikera's solution is correct!
Why do you need the indexes at all?
Just shuffle the collection directly:
(defn split-shuffled
[c p]
(let [c (shuffle c)
m (count c)
t (* m p)]
[(take t c) (drop t c)]))
My question is how can I capture the index of the vector row where a match occurred? In the code below, what am I doing wrong?
I have a vector of vectors
(def v1 [[44 2 3 4 5][1 6 7 5 10][11 12 13 14 15]])
a column index, and a comparison value
(def cmp-val1 11)
(def col-idx 0)
I want to return the row index where a comparison returned true. With col-idx = 0 and cmp-val1 = 11, I should see (first row-num) return 2, and it is returning 1.
(defn ret-match-row
"Return the index of the row, in which the cmp-val is found.
It is okay to increment 0."
[in-seq cmp-val col-idx]
(let [rn 0]
(let [row-num
(for [seq-row in-seq
:let [local-row-num (inc rn)]
:when (= cmp-val (nth seq-row col-idx nil))]
local-row-num)]
(first row-num))))
From lein repl:
bene-csv.core=> (ret-match-row v1 cmp-val1 col-idx)
1
=> (defn ret-match-row
[coll cmp idx]
(keep-indexed (fn [i v] (if (= cmp (get v idx)) i)) coll))
=> (ret-match-row v1 11 0)
(2)
A flexible answer comes from separating this into three distinct problems and composing them.
creating the data you seek
finding the data you want
presenting the way it should look.
first we number the rows by adding row numbers to them
(map vector v1 (range))
then filter out the rows not containing the number you want:
(filter (fn [[data index]] (some #{11} data)) (map vector v1 (range)))
> ([[11 12 13 14 15] 2])
here i used the trick that sets are functions that test their input for inclusion in the set which allows this to test for multiple values:
(filter (fn [[data index]] (some #{11 44} data)) (map vector v1 (range)))
> ([[44 2 3 4 5] 0] [[11 12 13 14 15] 2])
then since you only want to know where it matched and not what matched we filter that out:
(map second (filter (fn [[data index]] (some #{11 44} data)) (map vector v1 (range))))
> (0 2)
to wrap this into a nice function we write out the steps:
(defn with-row-numbers [col] (map vector col (range)))
(defn find-my-rows [rows goals]
(filter (fn [[data index]] (some (set goals) data)) rows))
(defn present-rows [rows] (map second rows))
and then compose them:
(defn ret-match-row [data rows]
(-> data
(with-row-numbers)
(find-my-rows rows)
(present-rows)))
(ret-match-row v1 [11])
(2)
sorry i couldn't help making it work with multiple values, its a habit.
(ret-match-row v1 [11 15 44])
> (0 2)
There may be other ways to do what your'e asking, but you can use a loop/recur to achieve the iteration you're after:
(defn ret-match-row [rows val col-idx]
(loop [[row & rows] rows
pos 0]
(cond
(not row)
nil
(= val (nth row col-idx))
pos
:not-found
(recur rows (inc pos)))))
(ret-match-row [[44 2 3 4 5]
[1 6 7 8 10]
[11 12 13 14 15]]
11
0)
;; => 2
You're also running into Clojure's immutability - the (inc rn) is not actually modifying rn. The loop / recur solution uses inc as well, but it passes the result of inc to the next iteration of the loop.
Clojure's for (list comprehension) form also will loop over all of the values in the sequence, resulting in a new sequence -- which is most likely not what you want. Even if you made the for loop do what you want, it will find all the matches, not just the first. The loop / recur example stops at the first match.
My take, using
clojure.contrib.seq find-first, indexed:
(defn ret-match-row [rows val col-idx]
(first
(find-first #(= val (nth (second %) col-idx))
(indexed rows))))