使用 Python 在文本文件中将复数转换为单数

Converting plural to singular in a text file with Python

我有这样的 txt 文件:

word, 23
Words, 2
test, 1
tests, 4

我希望它们看起来像这样:

word, 23
word, 2
test, 1
test, 4

我希望能够在 Python 中获取一个 txt 文件并将复数单词转换为单数。这是我的代码:

import nltk

f = raw_input("Please enter a filename: ")

def openfile(f):
    with open(f,'r') as a:
       a = a.read()
       a = a.lower()
       return a

def stem(a):
    p = nltk.PorterStemmer()
    [p.stem(word) for word in a]
    return a

def returnfile(f, a):
    with open(f,'w') as d:
        d = d.write(a)
    #d.close()

print openfile(f)
print stem(openfile(f))
print returnfile(f, stem(openfile(f)))

我也试过这两个定义而不是 stem 定义:

def singular(a):
    for line in a:
        line = line[0]
        line = str(line)
        stemmer = nltk.PorterStemmer()
        line = stemmer.stem(line)
        return line

def stem(a):
    for word in a:
        for suffix in ['s']:
            if word.endswith(suffix):
                return word[:-len(suffix)]
            return word

之后我想提取重复的单词(例如 testtest)并通过将它们旁边的数字相加来合并它们。例如:

word, 25
test, 5

我不知道该怎么做。一个解决方案会很好,但不是必需的。

看来您对 Python 非常熟悉,但我仍会尝试解释一些步骤。让我们从单词去复数化的第一个问题开始。当您使用 a.read() 读入一个多行文件(在您的例子中是单词,数字 csv)时,您将把文件的整个主体读入一个大字符串。

def openfile(f):
    with open(f,'r') as a:
        a = a.read() # a will equal 'soc, 32\nsoc, 1\n...' in your example
        a = a.lower()
        return a

这很好,但是当你想将结果传递给 stem() 时,它将作为一个大字符串,而不是作为单词列表。这意味着当您使用 for word in a 遍历输入时,您将遍历输入字符串的每个单独字符并将词干分析器应用于这些单独的字符。

def stem(a):
    p = nltk.PorterStemmer()
    a = [p.stem(word) for word in a] # ['s', 'o', 'c', ',', ' ', '3', '2', '\n', ...]
    return a

这绝对不符合您的目的,我们可以做一些不同的事情。

  1. 我们可以更改它,以便我们将输入文件读取为一个行列表
  2. 我们可以使用大字符串,自己分解成一个列表。
  3. 我们可以一次逐行检查并截断行列表中的每一行。

为了方便起见,让我们从#1 开始吧。这将需要将 openfile(f) 更改为以下内容:

def openfile(f):
    with open(f,'r') as a:
        a = a.readlines() # a will equal 'soc, 32\nsoc, 1\n...' in your example
        b = [x.lower() for x in a]
        return b

这应该给我们 b 作为行列表,即 ['soc, 32'、'soc, 1'、...]。所以下一个问题变成了当我们将字符串列表传递给 stem() 时我们如何处理它。一种方法如下:

def stem(a):
    p = nltk.PorterStemmer()
    b = []
    for line in a:
        split_line = line.split(',') #break it up so we can get access to the word
        new_line = str(p.stem(split_line[0])) + ',' + split_line[1] #put it back together 
        b.append(new_line) #add it to the new list of lines
    return b

这绝对是一个非常粗略的解决方案,但应该充分遍历输入中的所有行,并将它们去复数化。这很粗糙,因为当你按比例放大时,拆分字符串并重新组装它们并不是特别快。但是,如果您对此感到满意,那么剩下的就是遍历新行列表,并将它们写入您的文件。根据我的经验,写入新文件通常更安全,但这应该可以正常工作。

def returnfile(f, a):
    with open(f,'w') as d:
        for line in a:
            d.write(line)


print openfile(f)
print stem(openfile(f))
print returnfile(f, stem(openfile(f)))

当我有以下input.txt

soc, 32
socs, 1
dogs, 8

我得到以下标准输出:

Please enter a filename: input.txt
['soc, 32\n', 'socs, 1\n', 'dogs, 8\n']
['soc, 32\n', 'soc, 1\n', 'dog, 8\n']
None

input.txt看起来像这样:

soc, 32
soc, 1
dog, 8

关于将数字与相同单词合并的第二个问题改变了我们上面的解决方案。根据评论中的建议,您应该看看使用字典来解决这个问题。与其将所有这些都作为一个大列表来做,更好的(可能更像 pythonic)方法是遍历输入的每一行,并在处理它们时阻止它们。如果您仍在努力解决这个问题,我会稍后编写相关代码。

如果你有复杂的单词要单数化,我不建议你使用词干提取,而是使用合适的 python 包 link pattern :

from pattern.text.en import singularize

plurals = ['caresses', 'flies', 'dies', 'mules', 'geese', 'mice', 'bars', 'foos',
           'families', 'dogs', 'child', 'wolves']

singles = [singularize(plural) for plural in plurals]
print(singles)

returns:

>>> ['caress', 'fly', 'dy', 'mule', 'goose', 'mouse', 'bar', 'foo', 'foo', 'family', 'family', 'dog', 'dog', 'child', 'wolf']

它并不完美,但它是我发现的最好的。 96% 基于文档:http://www.clips.ua.ac.be/pages/pattern-en#pluralization

Nodebox 英语语言学库包含用于将复数形式转换为单数形式的脚本,反之亦然。结帐教程:https://www.nodebox.net/code/index.php/Linguistics#pluralization

要将复数转换为单数,只需导入 singular 模块并使用 singular() 函数。它可以处理具有不同结尾、不规则形式等的单词的正确转换。

from en import singular
print(singular('analyses'))   
print(singular('planetoids'))
print(singular('children'))
>>> analysis
>>> planetoid
>>> child