在clojure中汇总值并将总计添加到数据集

时间:2012-06-06 13:46:42

标签: clojure

我是clojure的新手,拥有我从xml文档解析并在excel文件中显示的以下数据集:

({:Total 28, :p3percent 89.28571428571429, :p2percent 0.0, :p1percent 10.71428571428571, :APP "A", :p1 3, :p2 0, :p3 25}
 {:Total 40, :p3percent 92.5, :p2percent 0.0, :p1percent 7.5, :APP "b", :p1 3, :p2 0, :p3 37} 
 {:Total 64, :p3percent 93.75, :p2percent 0.0, :p1percent 6.25, :APP "c", :p1 4, :p2 0, :p3 60} 
 {:Total 128, :p3percent 83.59375, :p2percent 12.5, :p1percent 3.90625, :APP "d", :p1 5, :p2 16, :p3 107}
 {:Total 6, :p3percent 83.33333333333333, :p2percent 16.66666666666667, :p1percent 0.0, :APP "e", :p1 0, :p2 1, :p3 5}
 {:Total 8, :p3percent 87.5, :p2percent 12.5, :p1percent 0.0, :APP "f", :p1 0, :p2 1, :p3 7})

我想总结/平均每个键的值并在数据集中创建一个新条目,使用APP键“Total”,然后显示最后一行中的所有求和/平均值。这可以很容易地在excel中完成,但我显然想先在clojure中完成。

我知道如何获得每个键的总和即(apply + map(:p1数据集)),但是如何创建一个函数来迭代数据集并将总数作为数据集中的额外行添加?

由于

d

3 个答案:

答案 0 :(得分:1)

尝试merge-with功能。检查链接中的第二个示例 我想这就是你想要的。此功能将帮助您创建最后一个“总计”行。唯一的问题是你有一个不是数字的字段。因此,您可以将特殊函数传递给忽略字符串的merge-with

答案 1 :(得分:1)

  

我想总结/平均每个键的值并在数据集中创建一个新条目,使用APP键“Total”,然后显示最后一行中的所有求和/平均值。

我相信以前的答案误解了这个问题。如果您想在数据集中创建一个新条目,其中包含某些键的总计和其他键的平均值,那么我的答案可能有所帮助。


首先根据数字集合定义sumavg。您以后可以随时改进这些功能的实现,所以现在就保持简单。

(defn sum [coll] (reduce + coll))
;;(sum [1 2 3])
;;=> 6
(defn avg [coll] (/ (sum coll) (count coll)))
;;(avg [1 2 3])
;;=> 2

为避免重复自己,请定义一个用于减少数据集的函数。

(defn dataset-keys [d] (reduce #(into %1 (keys %2)) #{} d))

(defn reduce-dataset
  [f val dataset]
  (reduce (fn [m k] (assoc m k (f (map k dataset))))
          val
          (dataset-keys dataset)))

reduce-dataset期望val成为地图,dataset成为地图集合,例如数据集。

使用reduce-dataset根据totalsaverages来定义sumavg

(defn totals [dataset] (reduce-dataset sum {:APP "Totals"} dataset))
(defn averages [dataset] (reduce-dataset avg {:APP "Averages"} dataset))

由于您想要数据集中某些键的总数以及其他键的平均值,因此您需要一种方法来选择数据集中的这些键。

(defn select-cols [dataset ks] (map #(select-keys % ks) dataset))

现在,您拥有了在数据集中有选择地计算总计和平均值所需的一切。

(totals (select-cols your-dataset [:Total :p1 :p2 :p3]))
;;{:Total 274, :p2 18, :p3 241, :p1 15, :APP "Totals"}

(averages (select-cols your-dataset [:p1percent :p2percent :p3percent]))
;; {:p3percent 88.32713293650794, :p1percent 4.728422619047618, :p2percent 6.9444444444444455, :APP "Averages"}

您可以使用conj。

将结果与原始数据集合并
(conj dataset
      (totals (select-cols dataset [:Total :p1 :p2 :p3]))
      (averages (select-cols dataset [:p1percent :p2percent :p3percent])))

这会向数据集添加两行,一行用于总计,一行用于平均值。要添加单行,您可以在结合之前合并结果。

(conj dataset
      (merge (totals (select-cols dataset [:Total :p1 :p2 :p3]))
             (averages (select-cols dataset [:p1percent :p2percent :p3percent]))
             {:APP "Total/Avg"}))

如果密钥冲突,merge将始终使用它看到的最后一个值,因此在上面的示例中,:APP的值将为"Total/Avg"而不是{{1} }或"Totals"

答案 2 :(得分:0)

如果您只是想要制作一张包含相应键reduce的总数或平均值的地图,可以很好地做到这一点:

user> (pprint (reduce #(assoc %2 :Total (+ (:Total %2) (:Total %1))) 
                      {:Total 0} data))
{:p1 0,
 :p3 7,
 :p2percent 12.5,
 :p2 1,
 :p1percent 0.0,
 :APP "f",
 :p3percent 87.5,
 :Total 274}
nil

这可以包含在一个保留原始密钥的函数中,这个函数很重要,这样你就可以得到一个密钥的总和,然后取其平均值:

(defn sum-key [key new-key map] 
  (reduce #(assoc %2 new-key (+ (%1 new-key) (%2 key))) {new-key 0} data))
#'user/sum-key
user> (sum-key :Total :Total-sum data)
{:p1 0, :p3 7, :p2percent 12.5, :p2 1, :p1percent 0.0, :APP "f", :p3percent 87.5, :Total-sum 274, :Total 8}

然后可以链接此功能以总结您想要的键

user> (->> data 
        (sum-key :Total :Total-sum) 
        (sum-key :p1 :p1-sum) 
        (sum-key :p2 :p2-sum))
{:p1 0, :p2-sum 18, 
 :p3 7, :p2percent 12.5, 
 :p2 1, :p1percent 0.0, 
 :APP "f", :p3percent 87.5, 
 :Total 8}

如果您想要运行总计,请改用reductions