Использование ключевых аргументов в функции, чтобы сделать генерацию n-грамм необязательной

Пример того, как выглядит мой XML-файл:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="folia.xsl"?>
<FoLiA xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://ilk.uvt.nl/folia" xml:id="untitled" generator="libfolia-v0.10">
  <metadata type="native">
    <annotations>
      <token-annotation annotator="ucto" annotatortype="auto" datetime="2017-04-17T14:50:04" set="tokconfig-nl"/>
      <pos-annotation annotator="frog-mbpos-1.0" annotatortype="auto" datetime="2017-04-17T14:50:04" set="http://ilk.uvt.nl/folia/sets/frog-mbpos-cgn"/>
      <lemma-annotation annotator="frog-mblem-1.1" annotatortype="auto" datetime="2017-04-17T14:50:04" set="http://ilk.uvt.nl/folia/sets/frog-mblem-nl"/>
      <chunking-annotation annotator="frog-chunker-1.0" annotatortype="auto" datetime="2017-04-17T14:50:04" set="http://ilk.uvt.nl/folia/sets/frog-chunker-nl"/>
      <entity-annotation annotator="frog-mwu-1.0" annotatortype="auto" datetime="2017-04-17T14:50:04" set="http://ilk.uvt.nl/folia/sets/frog-mwu-nl"/>
      <entity-annotation annotator="frog-ner-1.0" annotatortype="auto" datetime="2017-04-17T14:50:04" set="http://ilk.uvt.nl/folia/sets/frog-ner-nl"/>
      <morphological-annotation annotator="frog-mbma-1.0" annotatortype="auto" datetime="2017-04-17T14:50:04" set="http://ilk.uvt.nl/folia/sets/frog-mbma-nl"/>
      <dependency-annotation annotator="frog-depparse-1.0" annotatortype="auto" set="http://ilk.uvt.nl/folia/sets/frog-depparse-nl"/>
    </annotations>
  </metadata>
  <text xml:id="untitled.text">
    <p xml:id="untitled.p.1">
      <s xml:id="untitled.p.1.s.1">
        <w xml:id="untitled.p.1.s.1.w.1" class="WORD">
          <t>De</t>
          <pos class="LID(bep,stan,rest)" confidence="0.999701" head="LID">
            <feat class="bep" subset="lwtype"/>
            <feat class="stan" subset="naamval"/>
            <feat class="rest" subset="npagr"/>
          </pos>
          <lemma class="de"/>
          <morphology>
            <morpheme>
              <t offset="0">de</t>
            </morpheme>
          </morphology>
        </w>

Я делаю функцию для генерации слов uni-, bi- и trigram из xml-файла. Я хочу сделать n-грамм необязательным, чтобы вы могли выбрать, хотите ли вы все н-граммы или, например, только униграммы. Результатом моей функции является векторизованная относительная частота слова n-грамм. Я попробовал это, используя ключевые аргументы в моих параметрах (используя True и False). Я получаю пустой словарь, поэтому я, должно быть, делаю что-то не так. Вот что у меня есть. Может кто-нибудь сказать мне, что я делаю не так?

import re
import xml.etree.ElementTree as ET

def word_ngrams(frogged_xmlfile, unigrams=True, bigrams=True, trigrams=True):
    vector = {}
    tree = ET.parse(frogged_xmlfile) #enter the xml tree
    root = tree.getroot()
    tokens = []
    words = []
    regex = re.compile(r'[^0-9] |[^(\.|\,|\?|\:|\;|\!)]')
        for node in root.iter('w'):
        for w in node.findall('t'):
            tokens.append(w.text)
    for word in tokens:
        if regex.search(word):
            words.append(word)
    if (unigrams):
        for n in [1]: #unigrams
            grams = ngrams(words, n)
            fdist = FreqDist(grams)
            total = sum(c for g,c in fdist.items())
        for gram, count in fdist.items():
            vector['w'+str(n)+'+'+' '.join(gram)] = count/total

    if (bigrams):
        for n in [2]: #bigrams
            grams = ngrams(tokens, n)
            fdist = FreqDist(grams)
            total = sum(c for g,c in fdist.items())
        for gram, count in fdist.items():
            vector['w'+str(n)+'+'+' '.join(gram)] = count/total

    if (trigrams):
        for n in [3]: #trigrams
            grams = ngrams(tokens, n)
            fdist = FreqDist(grams)
            total = sum(c for g,c in fdist.items())
        for gram, count in fdist.items():
            vector['w'+str(n)+'+'+' '.join(gram)] = count/total
    return vector
print(word_ngrams('romanfragment_frogged.xml', unigrams = True, bigrams = False, trigrams = False))

1 ответ

Решение
  1. Ваш поиск игнорирует пространство имен документа по умолчанию, поэтому он никогда не находит совпадающие теги.

  2. Ваше регулярное выражение действительно ужасно -

    "[^0-9] "                   # not-a-digit, followed by space
    "|"                         # OR
    "[^(\.|\,|\?|\:|\;|\!)]"    # bad syntax, but I think you mean not one of .,?:;!
    

    Он будет принимать любую пунктуацию, за которой следует пробел (как не цифра), или любая цифра или другой символ или пробел (как не пунктуация)! По сути, единственное, что ему не соответствует, это "строка, состоящая исключительно из знаков препинания".

    Я собираюсь догадаться, что вы действительно хотели "строку, содержащую хотя бы одну букву и не состоящую из букв", но не стесняйтесь меня поправлять.

  3. Ваш код не включает ngrams() или же FreqDist() поэтому я не могу проверить это.

  4. Отступ for gram, count ... выглядит некорректно - думаю, стоит отступить еще на один уровень.

  5. У вас много ненужного дублированного кода.

Попробуй это:

# import re
import xml.etree.ElementTree as ET

FOLIA_NAMESPACE = {
    'default': 'http://ilk.uvt.nl/folia',
    'xlink':   'http://www.w3.org/1999/xlink'
}

def is_word(s):
    return s.isalpha()
    # as a regex:
    # return re.match("[A-Za-z]+$", s) is not None

def load_words(folia_xml_file, is_word=is_word, namespace=FOLIA_NAMESPACE):
    root = ET.parse(folia_xml_file).getroot()
    tokens = root.findall(".//default:w/default:t", namespace)
    return [t.text for t in tokens if is_word(t.text)]

def make_ngram_vectors(words, n_values=[1,2,3]):
    vectors = {}
    for n in n_values:
        grams = ngrams(words, n)
        fdist = FreqDist(grams)
        total = sum(count for gram,count in fdist.items())
        for gram,count in fdist.items():
            key = "w{}+{}".format(n, " ".join(gram))
            vectors[key] = count / total
    return vectors

def main():
    words = load_words("romanfragment_frogged.xml")
    vectors = make_ngram_vectors(words, [1])
    print(vectors)

if __name__ == "__main__":
    main()

Изменить: если вы посмотрите в <FoLiA> тег в верхней части вашего XML-файла, вы увидите xmlns= (ссылка, определяющая пространство имен документа по умолчанию, т.е. какие теги доступны) и xmlns:xlink= (альтернативное пространство имен XLink, которое определяет такие теги, как xlink:href а также xlink:show - см. https://www.w3schools.com/xml/xml_xlink.asp).

ElementTree любит расширять встроенные пространства имен, чтобы ваши теги выглядели как {http://ilk.uvt.nl/folia}w, Передача dict пространства имен позволяет нам использовать более удобочитаемое форматирование, например default:w вместо.

Чтобы получить те же форматы ввода / вывода, что и в исходной функции, вы можете использовать функцию-оболочку, например:

def word_ngrams(folia_xml_file, unigrams=True, bigrams=True, trigrams=True):
    # condense parameters into n_values
    n_values = []
    if unigrams:
        n_values.append(1)
    if bigrams:
        n_values.append(2)
    if trigrams:
        n_values.append(3)
    words = load_words(folia_xml_file)
    return make_ngram_vectors(words, n_values)
Другие вопросы по тегам