簡體   English   中英

Clojure:鏈接分組:使用剩余鍵上的選擇鍵鍵入

[英]Clojure: Chaining group-by :key with select-keys on remaining keys

我正在嘗試用clojure地圖理解一個簡單的(如在其他語言中)工作流程。

它基本上歸結為:如何鏈接這些操作?

  1. group-by :鍵入地圖矢量

  2. select-keys剩余的地圖沒有以前的關鍵

  3. 再次group-by (0..n次)和選擇鍵

  4. count唯一的密鑰實例。

另請參閱我之前的問題: 地圖中的聚合和計數

例:

給出一張地圖矢量

(def DATA [{:a "X", :b "M", :c "K", :d 10}
           {:a "Y", :b "M", :c "K", :d 20}
           {:a "Y", :b "M", :c "F", :d 30}
           {:a "Y", :b "P", :c "G", :d 40}])

進行group-by

(defn get-tree-level-1 [] (group-by :a DATA))

生成按該特定鍵的值分組的映射。

{ X [{:a X, :b M, :c K, :d 10}],
  Y [{:a Y, :b M, :c K, :d 20}
     {:a Y, :b M, :c F, :d 30}
     {:a Y, :b P, :c G, :d 40}]}  

到現在為止還挺好。 但是,如果我想從數據中構建一個樹狀結構 ,這意味着選擇剩余的鍵並忽略一些,請選擇:b:c並忽略:d ,這將在下一級產生:

(def DATA2   [{ :X [{:b "M", :c "K"}],
                :Y [{:b "M", :c "K"}
                    {:b "M", :c "F"}
                    {:b "P", :c "G"}]}])

最后,計算剩余鍵的所有實例(例如,計算Y -root下的:b鍵的所有唯一值):

(def DATA3   [{ :X [{:M  1}],
                :Y [{:M  2}
                    {:P  1}])

我嘗試在group-by后執行select-keys ,但在第一步后結果為空:

(defn get-proc-sums []
  (into {}
    (map
      (fn [ [k vs] ]
        [k (select-keys vs [:b :c])])
      (group-by :a DATA))))

重復應用分組是錯誤的工具:它不能很好地組合自身。 相反,查看輸入映射並將其中的每一個轉換為對您有用的格式(使用formap ),然后減少它以構建樹結構。 這是一個簡單的實現:

(defn hierarchy [keyseq xs]
  (reduce (fn [m [ks x]]
            (update-in m ks conj x))
          {}
          (for [x xs]
            [(map x keyseq) (apply dissoc x keyseq)])))

user> (hierarchy [:a :b :c] '[{:a "X", :b "M", :c "K", :d 10}
                              {:a "Y", :b "M", :c "K", :d 20}
                              {:a "Y", :b "M", :c "F", :d 30}
                              {:a "Y", :b "P", :c "G", :d 40}])
{"Y" {"P" {"G" ({:d 40})},
      "M" {"F" ({:d 30}),
           "K" ({:d 20})}},
 "X" {"M" {"K" ({:d 10})}}}

這為您提供了所需的分層格式,其中包含僅包含“剩余”鍵的所有地圖的列表。 通過這個,你可以計算它們,區分它們,刪除:d鍵,或者你想要的任何其他東西,或者通過編寫另一個處理這個地圖的函數,或者通過調整reduce函數中的內容或者上面的for comprehension。

錯誤是您正在嘗試從值集合中選擇鍵,而您應該為coll中的每個項目執行此操作,例如使用map

(defn get-proc-sums []
  (into {}
        (map
         (fn [ [k vs] ]
           [k (map #(select-keys % [:b :c]) vs)])
         (group-by :a DATA))))

user> (get-proc-sums)
{"X" ({:b "M", :c "K"}), 
 "Y" ({:b "M", :c "K"} {:b "M", :c "F"} {:b "P", :c "G"})}

你正在做的是:

user> (group-by :a DATA)
{"X" [{:a "X", :b "M", :c "K", :d 10}], 
 "Y" [{:a "Y", :b "M", :c "K", :d 20} 
      {:a "Y", :b "M", :c "F", :d 30} 
      {:a "Y", :b "P", :c "G", :d 40}]}

然后你正在處理每個鍵值對(讓我們采用“Y”對):

user> (let [[k vals] ["Y" ((group-by :a DATA) "Y")]]
         [k vals])
["Y" [{:a "Y", :b "M", :c "K", :d 20} 
      {:a "Y", :b "M", :c "F", :d 30} 
      {:a "Y", :b "P", :c "G", :d 40}]]

所以你為地圖矢量做了select-keys

user> (select-keys [{:a "Y", :b "M", :c "K", :d 20} 
                    {:a "Y", :b "M", :c "F", :d 30} 
                    {:a "Y", :b "P", :c "G", :d 40}]
                   [:a :b])
{}

這是合乎邏輯的,因為你在向量中沒有這些鍵。

user> (map #(select-keys % [:a :b]) [{:a "Y", :b "M", :c "K", :d 20} 
                                     {:a "Y", :b "M", :c "F", :d 30} 
                                     {:a "Y", :b "P", :c "G", :d 40}])
({:a "Y", :b "M"} {:a "Y", :b "M"} {:a "Y", :b "P"})

更新:為了完成整個任務,我建議如下:

(defn process-data [data]
  (->> data
       (group-by :a)
       (map (fn [[k vals]] [k (frequencies (map :b vals))]))
       (into {})))

user> (process-data DATA)
{"X" {"M" 1}, "Y" {"M" 2, "P" 1}}

在這里,我將只討論您的問題的工作流程方面,以及思考功能設計的一種方式。 我只提出了許多方法,但我認為這種方式是充分慣用的。 如果你正在尋找一個實現, amalloy提供了一個很好的實現。

你提出的問題是一個完美的遞歸用例。 您希望構建一個嵌套結構,其中每個嵌套級別(除了最后一個)只是在前一個分組結果上遵循相同的分組過程。 最后一層嵌套改為執行計數。 而且你事先並不知道將會有多少級別的嵌套。

你丟棄了:c:d ,所以你不妨在開始時這樣做 - 這在邏輯上是一個獨特的處理步驟。

讓我們假設你已經編寫了你的​​函數(稱之為foo - 我將其寫作作為讀者的練習)。 它可以根據對自身的遞歸調用來構造嵌套結構。

我們來看看你的示例數據集:

(def DATA [{:a "X", :b "M", :c "K", :d 10}
           {:a "Y", :b "M", :c "K", :d 20}
           {:a "Y", :b "M", :c "F", :d 30}
           {:a "Y", :b "P", :c "G", :d 40}])

讓我們忽略:d ,所以我們的預處理集看起來像:

(def filtered-data [{:a "X", :b "M", :c "K"}
                    {:a "Y", :b "M", :c "K"}
                    {:a "Y", :b "M", :c "F"}
                    {:a "Y", :b "P", :c "G"}])

這是一個示例“查詢”:

(foo filtered-data
     [:a :b :c])

我們希望它吐出一個看起來有點像這樣的嵌套結構:

[{ :X (foo [{:b "M", :c "K"}]
           [:b :c]),
   :Y (foo [{:b "M", :c "K"}
            {:b "M", :c "F"}
            {:b "P", :c "G"}]
           [:b :c]}])

這反過來相當於:

[{ :X [{:M (foo [{:c "K"}]
                [:c])}],
   :Y [{:M (foo [{:c "K"}
                 {:c "F"}]
                [:c]),
        :P (foo [{:c "G"}]
                [:c])}]
]}

這些foo可以很容易地識別遞歸的結束並切換到計數行為:

[{ :X [{:M [{:K 1}]}],
   :Y [{:M [{:F 1}
            {:K 1}],
        :P [{:G 1}]
      }]
]}

就個人而言,如果我正在構建這樣一個結構,我會針對一個沒有“多余”嵌套的對象,比如這個特里

{"X" {"M" {"K" 1}},
 "Y" {"M" {"F" 1, "K" 1},
      "P" {"G" 1}}

但我不知道你的用例以及這些是否真的是多余的。 如果您可能希望使用此數據生成多個統計信息,那么請查看合金化如何構建一個可以從中獲取計數的壓縮結構 ,或其他任何內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM