【问题标题】:Clojure read CSV and split the columns into several vectorsClojure 读取 CSV 并将列拆分为多个向量
【发布时间】:2016-02-03 09:57:30
【问题描述】:

目前我有这样的功能:

(def csv-file (.getFile  (clojure.java.io/resource "datasources.csv")))

(defn process-csv [file]
  (with-open [in-file (io/reader file)]
    (doall (csv/read-csv in-file))))

我现在需要做的是根据 csv 中的列/分组生成向量,即我的process-csv 输出如下所示:

(["atom" "neutron" "photon"] 
[10 22 3] 
[23 23 67])

我的目标是从列原子、中子和光子生成 3 个向量:

atom: [10 23]
neutron: [22 23]
photon: [3 67]

仅供参考,我在读取 csv 文件之前定义了 3 个空向量:

(def atom [])
(def neutron[])
(def photon[])

【问题讨论】:

    标签: csv vector clojure split


    【解决方案1】:

    首先你不能修改这些向量,你已经定义了。这是不可变数据结构的本质。如果您确实需要可变向量,请使用atom。

    你可以这样解决你的任务:

    user> (def items (rest '(["atom" "neutron" "photon"] 
                             [10 22 3] 
                             [23 23 67]
                             [1 2 3]
                             [5 6 7])))
    
    user> (let [[atom neutron photon] (apply map vector items)]
            {:atom atom :neutron neutron :photon photon})
    {:atom [10 23 1 5], :neutron [22 23 2 6], :photon [3 67 3 7]}
    

    这就是它的工作原理: (apply map vector items) 等于:

    (map vector [10 22 3] [23 23 67] [1 2 3] [5 6 7])

    它获取每个 coll 的第一个项目并将它们组成一个向量,然后是第二个项目,依此类推。

    此外,您可以通过完全从 csv 数据标题中获取行列名称来使其更加健壮:

    user> (def items '(["atom" "neutron" "photon"] 
                       [10 22 3] 
                       [23 23 67]
                       [1 2 3]
                       [5 6 7]))
    #'user/items
    
    user> (zipmap (map keyword (first items))
                  (apply map vector (rest items)))
    {:atom [10 23 1 5], :neutron [22 23 2 6], :photon [3 67 3 7]}
    

    【讨论】:

    • 不幸的是,这不是一个通用的解决方案。关键字在这里是硬编码的。
    • 它们不在最后一个列表中
    • 对不起,我的错
    【解决方案2】:

    我将说明您可以使用的其他一些方法,这些方法可以与 leetwinski 说明的方法结合使用。像 leetwinski 一样,我建议使用哈希映射作为最终结构,而不是包含向量的三个符号。这取决于你。

    如果你愿意,你可以使用 core.matrix 的 transpose 来做 leetwinski 对 (apply map vector ...) 所做的事情:

    (require '[clojure.core.matrix :as mx])
    (mx/transpose '(["atom" "neutron" "photon"] [10 22 3] [23 23 67]))
    

    产生:

    [["atom" 10 23] ["neutron" 22 23] ["photon" 3 67]]
    

    transpose 旨在处理任何类型的实现 core.matrix 协议的矩阵,并且正常的 Clojure 序列序列被 core.matrix 视为矩阵。

    要生成地图,这是一种方法:

    (into {} (map #(vector (keyword (first %)) (rest %))
                  (mx/transpose '(["atom" "neutron" "photon"] [10 22 3] [23 23 67]))))
    

    产生:

    {:atom (10 23), :neutron (22 23), :photon (3 67)}
    

    keyword 将字符串变成关键字。 #(vector ...) 生成一对,(into {} ...) 获取对的序列并从中生成哈希映射。

    或者,如果您想要 vars 中的向量,正如您所指定的,那么您可以使用 leetwinski 的 let 方法的变体。我建议不要 defing 符号 atom,因为这是 Clojure 中标准函数的名称。

    (let [[adam neutron proton] (mx/transpose 
                                  (rest '(["atom" "neutron" "photon"]
                                          [10 22 3]
                                          [23 23 67])))]
      (def adam adam)
      (def neutron neutron)
      (def proton proton))
    

    在let 中使用def 并不是一种很好的形式,但您可以这样做。另外,我不建议将let 定义的局部变量命名为与顶级变量相同的名称。如您所见,如果使defs 令人困惑。我在这里故意这样做只是为了展示范围规则的工作原理:在(def adam adam) 中,“adam”的第一个实例表示定义的顶级变量,而“adam”的第二个实例表示定义的本地变量通过let,包含[10 23]。结果是:

      adam ;=> [10 23]
      neutron ;=> [22 23]
      proton ;=> [3 67]
    

    (我认为可能有些微妙之处我表达不正确。如果是这样,毫无疑问会有人对此发表评论。)

    【讨论】:

      猜你喜欢
      • 2014-02-18
      • 1970-01-01
      • 1970-01-01
      • 2018-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多