2016-10-22 7 views
6

大きなjsonファイル(3GB)を解析し、このファイルの各行に対してハッシュマップを返したいとします。私の直感は、トランスデューサを使って行ごとにファイルを処理し、いくつかの選択されたフィールド(ファイルのバイトの> 5%)を持つベクトルを構築することでした。私はJVisualVMとプロセスを可視化すると、ヒープが終わっ成長Clojure Tranducersを使用して大きなファイルを解析する:OutOfMemoryエラー

(defn load-with! 
    "Load a file using a parser, a structure and a transducer." 
    [parser structure xform path] 
    (with-open [r (clojure.java.io/reader path)] 
    (into structure xform (parser r)))) 

(def xf (map #(get-in % ["experiments" "results"]))) 
(def parser (comp (partial map cheshire.core/parse-string) line-seq)) 

(load-with! parser (vector) xf "file.json") 

file.json

{"experiments": {"results": ...}} 
{"experiments": {"results": ...}} 
{"experiments": {"results": ...}} 

parser.clj:

ただし、次のコードは、のOutOfMemory例外をスロープロセスがクラッシュする前に25 GBを超えています。

この場合、トランスデューサは適切ですか?より良い選択肢がありますか?

私の要件の1つは、機能の最後に新しい構造を返すことです。したがって、私はdosqを使用してファイルをインプレースで処理することはできません。

さらに、ファイル形式に従ってパーサーとトランスデューサを変更する必要があります。

ありがとうございました!

+0

私はコードを完全に理解していません。パーサーの役割は何ですか?渡されたものの未使用のようです。また、 '(r)'という表現はおそらくあなたが望むものではなく、読者を関数と呼びます。 –

+2

トランスデューサがなぜ役立つのかわかりません。トランスデューサは、データに対して一連の操作を実行する場合に便利です。トランスデューサを使用すると、捨てられる中間データ構造を作成することを避けることができます。このコードは、 'get-in'をマップします。 'into'は非遅延型です。あなたは遅れてファイルを処理できますか? 'for'、' map'、または 'sequence'トランスデューサ関数を使って、怠惰なシーケンスのマップエントリを作成できますか?それらが正しく処理されていれば、すべてのファイルの内容をメモリに保存せずにそれぞれを処理することができます。 – Mars

+0

パーサー/トランスデューサの目的は、ファイルフォーマット(例えば、json、csv ...)およびファイル内のベンダフォーマットに従って容易に作業を適合させることである。 – fmind

答えて

1

あなたはかなり近いです。私はjson/parse-stringが何であるか分からないが、json/read-strからhereまで同じであれば、このコードはあなたがそこでやろうとしているものでなければならない。

あなたはこのような何かのために行っていたように見えます:

(require '[clojure.data.json :as json]) 
(require '[clojure.java.io :as java]) 

(defn load-with! 
    "Load a file using a parser, a structure and a transducer." 
    [parser structure xform path] 
    (with-open [r (java/reader path)] 
    (into structure (xform (parser r))))) 

(def xf (partial map #(get-in % ["experiments" "results"]))) 

(def parser (comp (partial map json/read-str) line-seq)) 


(load-with! parser [] xf "file.json") 

私はこれらのここにあなたの最低限の例にすべてのビジネスの詳細を切り出しから作られたばかりのミスだった推測しています。以下のコードを使用すると、上記のコードでOOMエラーが発生した大きなファイルを処理できました。

(require '[clojure.data.json :as json]) 
(require '[clojure.java.io :as java]) 

(def structure (atom [])) 

(defn do-it! [xform path] 
    (with-open [r (java/reader path)] 
    (doseq [line (line-seq r)] 
     (swap! structure conj (xform line))))) 

(defn xf [line] 
    (-> (json/read-str line) 
     (get-in ["experiments" "results"]))) 

(do-it! xf "file.json") 

(take 10 @structure) 
+0

あなたの提案をありがとうございます。この中で原子を使う必要があります。 – fmind

+0

あなたの提案をありがとう。グローバル変数が必要ですか?解決策と比較した違いは何ですか? – fmind

+0

十分なメモリがあれば、最初のビットが動作します。私は原子がdoseqと一緒に必要だと思う。私はこれを研究するために時間がなくなったので、私の答えはほんの少しの改善に過ぎませんでした。 –

関連する問題