将 CSV 文件缓慢解析为向量图

问题描述 投票:0回答:1

我正在尝试读取 CSV 文件并将其解析为向量图。因此,映射的键是 CSV 中的列名称,映射的值是包含 CSV 中的值列的向量。

我使用内置的

clojure.data.csv
来读取文件,即使 CSV 文件(在here找到)只有 32 MB,我的代码运行速度确实很慢。

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

(defn csv->df [file-path]
  (with-open [reader (io/reader file-path)]
    (let [in-file (csv/read-csv reader)
          names (first in-file)
          data (rest in-file)]
      (zipmap (map keyword names) (apply mapv vector data)))))

(csv->df "data/flights.csv")

我怀疑我正在做一些与惰性序列相关的愚蠢的事情,因为作为一个 Clojure 新手,我仍然在掌握它们,但我无法确定问题的根本原因。

是否可以重组此功能,使其运行速度不至于缓慢?

csv clojure
1个回答
0
投票

你并没有从懒惰中受益,因为转置矩阵(这就是

apply map vector
正在做的事情)不能是懒惰的。但你并没有做任何特别错误的事情。在我的机器上进行的测试中,9 秒是在此文件上调用
read-csv
并迭代结果所需的时间。你的函数大约需要两倍的时间。因此,您的处理速度并不快,但如果您以某种方式设法在零时间内完成所有后处理,那么您所希望的最好结果是整个过程加速 50%。

我认为如果您使用 clojure.data.csv,这些成本是不可避免的。将内容打包到整洁的 Clojure 数据结构中非常方便,但它并不是免费的。我尝试使用 FastCSV 进行比较:读取并丢弃整个文件的速度大约快 20 倍,并且从文件生成向量=向量的速度也同样快。转置仍然很慢,但整个过程只需 5 秒,而不是您的函数的 17 秒。这是我写的;不灵活且笨重,但它产生相同的结果并且足够简单:

(defmacro row-fn [[arg] & body]
  (let [x (gensym 'arg)]
    `(reify java.util.function.Consumer
       (accept [this ~x]
         (let [~(with-meta arg {:tag `CsvRow}) ~x]
           ~@body)))))

(defn fastcsv->df [file-path]
  (let [headers (promise)
        csv (-> (CsvReader/builder)
                (.build (io/reader file-path))
                (.spliterator))
        rows (atom (transient []))]
    (.tryAdvance csv (row-fn [row]
                       (deliver headers (map keyword (.getFields row)))))
    (.forEachRemaining csv (row-fn [row]
                             (swap! rows conj! (.getFields row))))
    (zipmap @headers (apply map vector (persistent! @rows)))))
© www.soinside.com 2019 - 2024. All rights reserved.