Clojure使用clojure.data.csv解析CSV为向量映射速度过慢求助
优化Clojure CSV解析性能的方案
原代码的问题分析
- 惰性序列的低效处理:
csv/read-csv返回惰性序列,rest in-file同样是惰性的,执行apply mapv vector data时会强制将所有行一次性加载到内存,对于大文件来说会产生大量内存开销,拖慢处理速度。 - 未完成数值转换:原代码直接保留CSV中的字符串值,没有转换为你需要的数值类型,后续使用时还会额外消耗性能。
- 转置操作效率低下:
apply mapv vector的转置方式需要先把所有行读入内存再进行列重组,对于行数较多的文件来说是低效的。
重构后的代码
(require '[clojure.data.csv :as csv] '[clojure.java.io :as io]) (defn parse-number [s] (when (seq s) (try (Double/parseDouble s) (catch NumberFormatException _ nil)))) (defn csv->df [file-path] (with-open [reader (io/reader file-path :encoding "UTF-8")] (let [csv-data (csv/read-csv reader) headers (map keyword (first csv-data)) initial-df (zipmap headers (repeat []))] (reduce (fn [df row] (let [values (map parse-number row)] (merge-with into df (zipmap headers (map vector values))))) initial-df (rest csv-data))))) ;; 调用示例 (csv->df "data/flights.csv")
优化点说明
- 边读边处理:使用
reduce遍历每一行数据,逐步构建列向量,不需要一次性加载所有行到内存,内存占用更低,处理速度更快。 - 数值转换:新增
parse-number函数将CSV中的字符串转换为Double类型,同时处理空值和格式错误的情况,符合你需要数值向量的需求。 - 指定编码:显式指定文件编码为UTF-8,避免默认编码检测的额外开销。
- 惰性序列合理消费:
reduce会逐个消费csv/read-csv返回的惰性序列,不会产生不必要的内存堆积。
额外性能建议
- 如果文件行数极多,可以考虑使用
pmap并行处理行数据,但注意并行处理会增加内存开销,需要根据实际情况权衡。 - 若不需要保留所有列,可以在遍历前过滤掉不需要的表头,减少后续处理的数据量。
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

