1

次のデータのレイアウトでデータベースから返されるマップのコレクションがあります

[{: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}]
4

2 に答える 2

4

以下は、問題が説明されているように、これを具体的に解決できる1つの方法です-のように、一般性はありません:

(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"}]}

これが group-by の仕組みです。このグループ化されたデータにマップされる関数はdate、各シーケンスの最初の要素と、各シーケンスdata-pointsの 2 番目の要素にバインドされます。(apply assoc {:date date} ...)データ ポイントの「再構築」を行います。ここで、での値は、での値:regionであったもののキーワードになります:status。「再構築」は、各データポイントの呼び出しである関数を使用してmapcatダウンすることによって発生します。data-pointsjuxt(comp keyword :region):status

このjuxt-ed 関数が何をしているかをうまく解明するために、それを書き出すと同等のものがどのようになるかを示しました。

(defn above-juxted-fn
  [data-point]
  [(keyword (:region data-point))
   (:status data-point)])

実際には、これは完全な解決策ではありません。すべての可能な地域をキーワードとして含めたいようで、その地域と日付のデータがない場合は値をゼロにするためです。ただし、上記のソリューションが返すものを(get region-key-here)使用すると、任意のデータポイントに対して行うたびに、代わりに を実行できる(get region-key-here 0)ため、ルックアップで特定の地域キーが見つからなかった場合のデフォルトはゼロになります。

もう 1 つ: 大量のデータを処理している場合、これが特に高速な方法であるとは思えません (他の人がここでより正確で詳細なアドバイスを持っているかもしれません)。シンプルさとミニマルさを目指して書きました。

また、上で述べたように、一般性はありません。一般性については、3 つの引数を取る関数に成形することでそれを変更できます: 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 すると、新しい戻りデータに -ed さdata-pointsれるキー/値ペアの新しいシーケンスを返すことです。assoc最良の部分は、この機能を使用すると、データを柔軟に再構築できることです。たとえば、次のように でグループ化し: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"})
于 2012-07-12T16:31:49.357 に答える
1

これは、欠落している領域にゼロが必要な場合を処理しません。

(->> (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}]
于 2012-07-12T07:38:50.617 に答える