2

我对 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 中完成。

我知道如何获取每个键的总和,即(应用 + 映射(:p1 数据集)),但是如何创建一个函数来遍历数据集并将总计添加为数据集中的额外行?

谢谢

D

4

3 回答 3

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定义totals和。averagessumavg

(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"不是"Totals"or "Averages"

于 2012-06-07T17:09:35.460 回答
1

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

于 2012-06-06T13:50:39.947 回答
0

如果您只是询问生成包含相应键的总数或平均值的地图,则reduce可以很好地做到这一点:

user> (pprint (reduce #(assoc %2 :Total (+ (:Total %2) (:Total %1)))
                      {:Total 0} 条数据))
{:p1 0,
 :p3 7,
 :p2% 12.5,
 :p2 1,
 :p1% 0.0,
 :APP "f",
 :p3% 87.5,
 :总计 274}
零

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

(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改用。

于 2012-06-06T18:52:29.980 回答