如何将一系列映射序列展平为一系列向量?

时间:2015-05-24 04:14:36

标签: clojure

我正在尝试在Clojure中构建一个POS标记器。我需要迭代一个文件并构建特征向量。输入是(文本pos chunk)三倍的文件,如下所示:

input from the file:  
        I PP B-NP
        am VBP B-VB
        groot NN B-NP

我编写了输入文件的函数,将每一行转换为一个映射,然后滑过可变数量的数据。

(defn lazy-file-lines
  "open a file and make it a lazy sequence."
  [filename]
  (letfn [(helper [rdr]
        (lazy-seq
         (if-let [line (.readLine rdr)]
           (cons line (helper rdr))
           (do (.close rdr) nil))))]
(helper (clojure.java.io/reader filename))))

(defn to-map
  "take a a line from a file and make it a map."
  [lines]
  (map
  #(zipmap [:text :pos :chunk] (clojure.string/split (apply str %) #" "))lines)
  )  

(defn window
  "create windows around the target word."
  [size filelines]
  (partition size 1 [] filelines))

我计划以下列方式使用上述功能:

 (take 2 (window 3(to-map(lazy-file-lines "/path/to/train.txt"))))

为序列中的前两个条目提供以下输出:

(({:chunk B-NP, :pos NN, :text Confidence} {:chunk B-PP, :pos IN, :text in} {:chunk B-NP, :pos DT, :text the}) ({:chunk B-PP, :pos IN, :text in} {:chunk B-NP, :pos DT, :text the} {:chunk I-NP, :pos NN, :text pound}))   

鉴于序列中的每个映射序列,我想为每个映射提取:pos:text并将它们放在一个向量中。像这样:

[Confidence in the NN IN DT]
[in the pound IN DT NN]

我无法概念化如何在clojure中处理这个问题。我的部分尝试解决方案如下:

(defn create-features
  "creates the features and tags from the datafile."
  [filename windowsize  & features]
 (map  #(apply select-keys % [:text :pos])
   (->>
    (lazy-file-lines filename)
    (window windowsize))))   

我认为其中一个问题是apply本身引用了一个序列,因此select-keys不在地图上运行。我不知道如何将另一个apply函数嵌入到这个中。

对此代码的任何想法都会很棒。感谢。

2 个答案:

答案 0 :(得分:1)

我不完全确定您想要的输入和输出,说实话,我不想完成您提供的所有代码来解决这个问题,因为我不认为所有代码对于这个问题都是必不可少的。其他人可能会给你一个针对你的代码量身定制的答案,但我认为真正的问题更为笼统。

我猜测你想要实现的一般想法是:

给定一系列映射序列,选择具有特定键的那些映射条目,然后返回表示映射条目的向量序列。如果这不是您想要的,我认为以下内容可能会让您了解如何继续。

此方法最有效或简洁,但它将问题分解为一系列易于理解的步骤:

(defn selkeys-or-not
  "Like select-keys, but returns nil rather than {} if no keys match."
  [keys map]
  (not-empty (select-keys map keys)))

(defn seq-seqs-maps-to-seq-vecs
  "Given a sequence of keys, and a sequence of sequences of maps,
  returns a sequence of vectors, where each vector contains key-val
  pairs from the maps for matching keys."
  [keys seq-seqs-maps]
  (let [maps (flatten seq-seqs-maps)]
    (map vec
         (apply concat
                (filter identity
                        (map (partial selkeys-or-not keys) maps))))))

第二个功能发生了什么:

首先,我们将外部序列展平,因为地图在内部序列中的事实与我们的目标无关。这为我们提供了一系列地图。

然后我们在地图序列上映射辅助函数selkeys-or-not,将我们的键传递给辅助函数。当select-keys找不到任何内容时{}会返回{},但selkeys-or-not是真实的,我们希望在这种情况下为下一步提供假值。 nil返回虚假值({})而不是nil

现在我们可以使用filter identity过滤掉apply - 过滤器返回一个包含所有值的序列,这样它的第一个参数就会返回一个真值。

此时我们有一系列地图,但我们想要一系列向量。 concat vec {}}将地图序列转换为一系列地图条目,将{{1}}映射到它们会将地图条目转换为矢量。

答案 1 :(得分:0)

(defn extract-line-seq
  [ls]
  (concat (map :text ls)
          (map :pos ls)))

(extract-line-seq '({:chunk B-NP, :pos NN, :text Confidence} {:chunk B-PP, :pos IN, :text in} {:chunk B-NP, :pos DT, :text the}))

;-> (Confidence in the NN IN DT)

You can put it into a vector if you want outside of the function. This way laziness is an option to the caller.