【问题标题】:Using Python to create a (random) sample of n words from text files使用 Python 从文本文件中创建一个包含 n 个单词的(随机)样本
【发布时间】:2016-10-14 10:05:02
【问题描述】:

对于我的博士项目,我正在评估所有现有的荷兰语命名实体识别标记器。为了检查这些标记器的精度和召回率,我想手动注释我语料库中随机样本中的所有命名实体。该手动注释的样本将作为“黄金标准”,我将比较不同标注器的结果。

我的语料库包含 170 部荷兰小说。我正在编写一个 Python 脚本来为每部小说生成特定数量的单词的随机样本(之后我将使用它来注释)。所有小说都将存储在同一目录中。以下脚本旨在为该目录中的每本小说生成 n 行的随机样本:

import random
import os
import glob
import sys
import errno

path = '/Users/roelsmeets/Desktop/libris_corpus_clean/*.txt'
files = glob.glob(path)  

for text in files:
    try:
        with open(text, 'rt', encoding='utf-8') as f:
             # number of lines from txt file
             random_sample_input = random.sample(f.readlines(),100) 

    except IOError as exc:
    # Do not fail if a directory is found, just ignore it.
        if exc.errno != errno.EISDIR: 
            raise 


# This block of code writes the result of the previous to a new file
random_sample_output = open("randomsample", "w", encoding='utf-8') 
random_sample_input = map(lambda x: x+"\n", random_sample_input)
random_sample_output.writelines(random_sample_input)
random_sample_output.close()

这段代码有两个问题:

  1. 目前,我已将两本小说(.txt 文件)放入该目录。但是代码只为每本小说中的一本输出一个随机样本。

  2. 目前,代码从每个 .txt 文件中抽取随机数量的 LINES,但我更喜欢为每个 .txt 文件生成随机数量的 WORDS。理想情况下,我想生成一个样本,例如,170 个 .txt 文件中每个文件的前 100 个单词或最后 100 个单词。在这种情况下,样本根本就不是随机的;但到目前为止,我找不到不使用随机库来创建样本的方法。

有人可以提出如何解决这两个问题的建议吗?总的来说,我对 Python 和编程仍然很陌生(我是一名文学学者),所以我很乐意学习不同的方法。非常感谢!

【问题讨论】:

    标签: python text random nlp named-entity-recognition


    【解决方案1】:

    一些建议:

    随机选取句子,而不是单词或行。如果输入是语法句子,NE 标注器会更好地工作。所以你需要使用分句器。

    当您遍历文件时,random_sample_input 仅包含来自最后一个文件的行。您应该将写入所选内容的代码块移动到 for 循环内的文件中。然后,您可以将选定的句子写入一个文件或单独的文件。例如:

    out = open("selected-sentences.txt", "w")
    
    for text in files:
        try:
            with open(text, 'rt', encoding='utf-8') as f:
                 sentences = sentence_splitter.split(f.read())
                 for sentence in random.sample(sentences, 100):
                     print >> out, sentence
    
        except IOError as exc:
        # Do not fail if a directory is found, just ignore it.
            if exc.errno != errno.EISDIR: 
                raise 
    
    out.close()
    

    [编辑] 以下是您应该如何使用 NLTK 句子拆分器:

    import nltk.data
    sentence_splitter = nltk.data.load("tokenizers/punkt/dutch.pickle")
    text = "Dit is de eerste zin. Dit is de tweede zin."
    print sentence_splitter.tokenize(text)
    

    打印:

    ["Dit is de eerste zin.", "Dit is de tweede zin."]
    

    请注意,您需要先使用交互式控制台中的nltk.download() 下载荷兰语标记器。

    【讨论】:

    • 谢谢!您对随机句子的看法是正确的。但是,当我运行您建议的代码时。我得到 'NameError: name 'sentence_splitter' is not defined' 。有没有可以导入 sentence_splitter 的模块?
    • 也许使用 nltk 中的句子分词器?
    • 当我导入 nltk.data 并定义变量 sentence_splitter = nltk.data.load("tokenizers/punkt/dutch.pickle") ,然后运行代码时,我得到以下 AttributeError: ' PunktSentenceTokenizer' 对象没有属性 'split'。你知道这是为什么吗?
    • 应该是“tokenize”,而不是“split”。
    • 是的,我想通了。但是现在我得到一个关于'sentences = sentence_splitter.tokenize(f.readlines())'的'TypeError: expected string or bytes-like object' 为什么会这样?
    【解决方案2】:

    您只需将行拆分为单词,将它们存储在某个地方,然后在读取所有文件并存储它们的单词后,使用random.sample 选择 100。它就是我在下面的代码中所做的。但是,我不太确定它是否能够处理 170 部小说,因为它可能会导致大量内存使用。

    import random
    import os
    import glob
    import sys
    import errno
    
    path = '/Users/roelsmeets/Desktop/libris_corpus_clean/*.txt'
    files = glob.glob(path)
    words = []
    
    for text in files:
        try:
            with open(text, 'rt', encoding='utf-8') as f:
                 # number of lines from txt file
                 for line in f:
                     for word in line.split():
                         words.append(word)
    
        except IOError as exc:
        # Do not fail if a directory is found, just ignore it.
            if exc.errno != errno.EISDIR: 
                raise 
    
    random_sample_input = random.sample(words, 100)
    
    # This block of code writes the result of the previous to a new file
    random_sample_output = open("randomsample", "w", encoding='utf-8') 
    random_sample_input = map(lambda x: x+"\n", random_sample_input)
    random_sample_output.writelines(random_sample_input)
    random_sample_output.close()
    

    在上面的代码中,小说的单词越多,输出样本中表示的可能性就越大。这可能是也可能不是所需的行为。如果您希望每部小说都具有相同的思考,您可以从其中选择 100 个单词添加到 words 变量中,然后在最后从那里选择 10000 个单词。它还具有使用更少内存的副作用,因为一次只能存储一本小说。

    import random
    import os
    import glob
    import sys
    import errno
    
    path = '/Users/roelsmeets/Desktop/libris_corpus_clean/*.txt'
    files = glob.glob(path)
    words = []
    
    for text in files:
        try:
            novel = []
            with open(text, 'rt', encoding='utf-8') as f:
                 # number of lines from txt file
                 for line in f:
                     for word in line.split():
                         novel.append(word)
                 words.append(random.sample(novel, 100))
    
    
        except IOError as exc:
        # Do not fail if a directory is found, just ignore it.
            if exc.errno != errno.EISDIR: 
                raise 
    
    
    random_sample_input = random.sample(words, 100)
    
    # This block of code writes the result of the previous to a new file
    random_sample_output = open("randomsample", "w", encoding='utf-8') 
    random_sample_input = map(lambda x: x+"\n", random_sample_input)
    random_sample_output.writelines(random_sample_input)
    random_sample_output.close()
    

    第三个版本,这个版本将处理句子而不是单词,并保留标点符号。此外,每本书在保留的最后句子上具有相同的“重量”,无论其大小。请记住,句子检测是由一种非常聪明但并非万无一失的算法完成的。

    import random
    import os
    import glob
    import sys
    import errno
    import nltk.data
    
    path = '/home/clement/Documents/randomPythonScripts/data/*.txt'
    files = glob.glob(path)
    
    sentence_detector = nltk.data.load('tokenizers/punkt/dutch.pickle')
    listOfSentences = []
    
    for text in files:
        try:
            with open(text, 'rt', encoding='utf-8') as f:
                fullText = f.read()
            listOfSentences += [x.replace("\n", " ").replace("  "," ").strip() for x in random.sample(sentence_detector.tokenize(fullText), 30)]
    
        except IOError as exc:
        # Do not fail if a directory is found, just ignore it.
            if exc.errno != errno.EISDIR:
                raise
    
    random_sample_input = random.sample(listOfSentences, 15)
    print(random_sample_input)
    
    # This block of code writes the result of the previous to a new file
    random_sample_output = open("randomsample", "w", encoding='utf-8')
    random_sample_input = map(lambda x: x+"\n", random_sample_input)
    random_sample_output.writelines(random_sample_input)
    random_sample_output.close()
    

    【讨论】:

    • 谢谢!这可能会解决随机单词采样的问题。但是,vpekar 强调使用语法句子而不是行或单词会更好,所以这就是我现在正在研究的内容。
    • 关于 'sentence[0] += currentSentence ^ SyntaxError: invalid syntax 我得到一个 'Syntaxerror: invalid syntax'
    • 现在我得到两个 NameErrors:name 'novel' 和 name 'words' 没有定义。
    • 现在我得到一个 'ValueError: Sample large than population' 。即使我将其转换为 random.sample(listOfSentences, 50) 或 random.sample(listOfSentences, 10)。这很奇怪。在我的第一个代码版本中,100 行(不是句子)从来都不是这种情况。你有解释吗?
    • 我建议使用适当的分句器,因为有很多特殊情况,例如“史密斯先生”、“美国”等。
    【解决方案3】:

    这解决了这两个问题:

    import random
    import os
    import glob
    import sys
    import errno
    
    path = '/Users/roelsmeets/Desktop/libris_corpus_clean/*.txt'
    files = glob.glob(path)
    
    with open("randomsample", "w", encoding='utf-8') as random_sample_output:
        for text in files:
            try:
                with open(text, 'rt', encoding='utf-8') as f:
                    # number of lines from txt file
                    random_sample_input = random.sample(f.read().split(), 10)
    
            except IOError as exc:
                # Do not fail if a directory is found, just ignore it.
                if exc.errno != errno.EISDIR:
                raise
    
            # This block of code writes the result of the previous to a new file
            random_sample_input = map(lambda x: x + "\n", random_sample_input)
            random_sample_output.writelines(random_sample_input)
    

    【讨论】:

    • 谢谢,这很好用。有人建议我应该使用句子而不是单词,这是正确的。所以现在我正在深入研究。
    猜你喜欢
    • 2012-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-07
    相关资源
    最近更新 更多