Search code examples
clojure

Plotting two lists in Clojure


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.

  1. First to sum values in list B for each unique value in list A
  2. 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.


Solution

  • 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:

    Chart example