我正在尝试在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函数嵌入到这个中。
对此代码的任何想法都会很棒。感谢。
答案 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.