通过Clojure透视数据
我有一个从数据库返回的地图集合,数据布局如下通过Clojure透视数据,clojure,Clojure,我有一个从数据库返回的地图集合,数据布局如下 [{:date "2012-6-6" :region "US" :status 1} {:date "2012-6-10" :region "UK" :status 2} {:date "2012-6-10" :region "US" :status 1} {:date "2012-6-11" :region "UK" :status 3}] 我想对数据进行透视/转置,以便 [{:date "2012-6-6" :US 1 :UK 0} {
[{:date "2012-6-6" :region "US" :status 1}
{:date "2012-6-10" :region "UK" :status 2}
{:date "2012-6-10" :region "US" :status 1}
{:date "2012-6-11" :region "UK" :status 3}]
我想对数据进行透视/转置,以便
[{:date "2012-6-6" :US 1 :UK 0}
{:date "2012-6-10" :US 1 :UK 2}
{:date "2012-6-11" :US 0 :UK 3}]
这不能处理缺少区域需要零的情况
(->> (group-by :date DB-DATA)
(map (fn [[_ coll]]
(apply merge (map (fn [{:keys [status date region]}]
{:date date (keyword region) status})
coll))))
vec)
=> [{:date "2012-6-6", :US 1} {:US 1, :date "2012-6-10", :UK 2} {:date "2012-6-11", :UK 3}]
这不能处理缺少区域需要零的情况
(->> (group-by :date DB-DATA)
(map (fn [[_ coll]]
(apply merge (map (fn [{:keys [status date region]}]
{:date date (keyword region) status})
coll))))
vec)
=> [{:date "2012-6-6", :US 1} {:US 1, :date "2012-6-10", :UK 2} {:date "2012-6-11", :UK 3}]
下面是一种解决此问题的方法,如中所述,没有任何一般性:
(def data
[{:date "2012-6-6" :region "US" :status 1}
{:date "2012-6-10" :region "UK" :status 2}
{:date "2012-6-10" :region "US" :status 1}
{:date "2012-6-11" :region "UK" :status 3}]
=> (map (fn [[date data-points]]
(apply assoc {:date date}
(mapcat (juxt (comp keyword :region)
:status)
data-points)))
(group-by :date
data)))
=> ({:US 1, :date "2012-6-6"}
{:US 1, :UK 2, :date "2012-6-10"}
{:UK 3, :date "2012-6-11"})
它的工作原理是按日期对数据进行分组,然后映射一个函数(稍后我将讨论该函数)group by
通过对元素调用某个函数并对该函数返回值相等的元素进行分组,从而按顺序对元素进行分组。它生成一个哈希映射,其中每个唯一的返回值都是一个键,映射中的每个值都是返回该值的原始序列中的元素序列。例如,以下是按日期分组的数据:
{"2012-6-6" [{:status 1
:date "2012-6-6"
:region "US"}]
"2012-6-10" [{:status 2
:date "2012-6-10"
:region "UK"}
{:status 1
:date "2012-6-10"
:region "US"}]
"2012-6-11" [{:status 3
:date "2012-6-11"
:region "UK"}]}
这就是分组方式的工作原理。现在,映射到此分组数据的函数将date
绑定到每个序列的第一个元素,并将数据点
绑定到每个序列的第二个元素。我们将(将assoc{:date}…
应用于数据点的“重组”,其中:region
处的值现在是:status
处值的关键字。“重组”是通过使用调用每个数据点上的(comp关键字:region)
和:status
的函数来实现的
为了有希望地阐明这个juxt
-ed函数的作用,我给出了如果写出它的等价物的样子:
(defn above-juxted-fn
[data-point]
[(keyword (:region data-point))
(:status data-point)])
现在,实际上这不是一个完整的解决方案,因为您似乎希望将所有可能的区域都包含为关键字,如果没有该区域和日期的数据,则该值为零。但是,根据上述解决方案返回的结果,无论何时对任何数据点执行(此处获取区域键)
,您都可以执行(此处获取区域键0)
,因此,如果查找未找到给定区域键,则默认值为零
另一件事:如果你正在处理大量数据,我不相信这是一种特别快速的做事方式(也许其他人会在这里得到更准确和详细的建议)。我写它的目的是为了简单和简约
此外,正如我上面所说,它没有一般性。至于一般性,我们可以通过将其建模为一个函数来改变它,该函数包含三个参数:1)一系列映射2)一个分组键和3)一系列“new key at”“new val at”对,我称之为重构规范(例如,在上面,这将是[:region:status]
)。下面是:
(defn group-and-restructure
[data grouping-key & restructuring-specs]
(let [grouped-data (group-by grouping-key
data)
restructuring-fn (apply juxt
(mapcat (fn [[k1 k2]]
[(comp keyword k1)
k2])
restructuring-specs))]
(map (fn [[grouping-k-value
data-points]]
(apply assoc {grouping-key grouping-k-value}
(mapcat restructuring-fn
data-points)))
grouped-data)))
=> (group-and-restructure
[{:date "2012-6-6" :region "US" :status 1}
{:date "2012-6-10" :region "UK" :status 2}
{:date "2012-6-10" :region "US" :status 1}
{:date "2012-6-11" :region "UK" :status 3}]
:date
[:region :status])
=> ({:US 1, :date "2012-6-6"}
{:US 1, :UK 2, :date "2012-6-10"}
{:UK 3, :date "2012-6-11"})
这与上面的特定解决方案非常相似。主要区别在于,重组规范被转换成一个函数,当mapcat
-ted down数据点时,该函数将返回一个新的键/val对序列,以关联到新的返回数据中。最好的部分是,此功能允许您灵活地重新构造数据。例如,您可以按:region
分组,并按[:date:status]
重组,如下所示:
=> (group-and-restructure
[{:date "2012-6-6" :region "US" :status 1}
{:date "2012-6-10" :region "UK" :status 2}
{:date "2012-6-10" :region "US" :status 1}
{:date "2012-6-11" :region "UK" :status 3}]
:region
[:date :status])
=> ({:2012-6-10 1, :2012-6-6 1, :region "US"}
{:2012-6-11 3, :2012-6-10 2, :region "UK"})
下面是一种解决此问题的方法,如中所述,没有任何一般性:
(def data
[{:date "2012-6-6" :region "US" :status 1}
{:date "2012-6-10" :region "UK" :status 2}
{:date "2012-6-10" :region "US" :status 1}
{:date "2012-6-11" :region "UK" :status 3}]
=> (map (fn [[date data-points]]
(apply assoc {:date date}
(mapcat (juxt (comp keyword :region)
:status)
data-points)))
(group-by :date
data)))
=> ({:US 1, :date "2012-6-6"}
{:US 1, :UK 2, :date "2012-6-10"}
{:UK 3, :date "2012-6-11"})
它的工作原理是按日期对数据进行分组,然后映射一个函数(稍后我将讨论该函数)group by
通过对元素调用某个函数并对该函数返回值相等的元素进行分组,从而按顺序对元素进行分组。它生成一个哈希映射,其中每个唯一的返回值都是一个键,映射中的每个值都是返回该值的原始序列中的元素序列。例如,以下是按日期分组的数据:
{"2012-6-6" [{:status 1
:date "2012-6-6"
:region "US"}]
"2012-6-10" [{:status 2
:date "2012-6-10"
:region "UK"}
{:status 1
:date "2012-6-10"
:region "US"}]
"2012-6-11" [{:status 3
:date "2012-6-11"
:region "UK"}]}
这就是分组方式的工作原理。现在,映射到此分组数据的函数将date
绑定到每个序列的第一个元素,并将数据点
绑定到每个序列的第二个元素。我们将(将assoc{:date}…
应用于数据点的“重组”,其中:region
处的值现在是:status
处值的关键字。“重组”是通过使用调用每个数据点上的(comp关键字:region)
和:status
的函数来实现的
为了有希望地阐明这个juxt
-ed函数的作用,我给出了如果写出它的等价物的样子:
(defn above-juxted-fn
[data-point]
[(keyword (:region data-point))
(:status data-point)])
现在,实际上这不是一个完整的解决方案,因为您似乎希望将所有可能的区域都包含为关键字,如果没有该区域和日期的数据,则该值为零。但是,根据上述解决方案返回的结果,无论何时对任何数据点执行(此处获取区域键)
,您都可以执行(此处获取区域键0)
,因此,如果查找未找到给定区域键,则默认值为零
另一件事:如果你正在处理大量数据,我不相信这是一种特别快速的做事方式(也许其他人会在这里得到更准确和详细的建议)。我写它的目的是为了简单和简约
此外,正如我上面所说,它没有一般性。至于一般性,我们可以通过将其建模为一个函数来改变它,该函数包含三个参数:1)一系列映射2)一个分组键和3)一系列“new key at”“new val at”对,我称之为重构规范(例如,在上面,这将是[:region:status]
)。下面是:
(defn group-and-restructure
[data grouping-key & restructuring-specs]
(let [grouped-data (group-by grouping-key
data)
restructuring-fn (apply juxt
(mapcat (fn [[k1 k2]]
[(comp keyword k1)
k2])
restructuring-specs))]
(map (fn [[grouping-k-value
data-points]]
(apply assoc {grouping-key grouping-k-value}
(mapcat restructuring-fn
data-points)))
grouped-data)))
=> (group-and-restructure
[{:date "2012-6-6" :region "US" :status 1}
{:date "2012-6-10" :region "UK" :status 2}
{:date "2012-6-10" :region "US" :status 1}
{:date "2012-6-11" :region "UK" :status 3}]
:date
[:region :status])
=> ({:US 1, :date "2012-6-6"}
{:US 1, :UK 2, :date "2012-6-10"}
{:UK 3, :date "2012-6-11"})
这与上面的特定解决方案非常相似。主要区别在于,重组规范转换为一个函数,当mapcat
-t