Как суммировать количество слов для каждого человека в диалоге?


Я начинаю изучать Python и пытаюсь написать программу, которая импортирует текстовый файл, подсчитывает общее количество слов, подсчитывает количество слов в конкретном абзаце (говорит каждый участник, описывается «P1», «P2» и т. Д.), Исключить эти слова (например, «P1» и т. Д.) Из моего подсчета слов и распечатать абзацы отдельно.

Благодаря @James Hurford я получил этот код:

words = None
with open('data.txt') as f:
   words = f.read().split()
total_words = len(words)
print 'Total words:', total_words

in_para = False
para_type = None
paragraph = list()
for word in words:
  if ('P1' in word or
      'P2' in word or
      'P3' in word ):
      if in_para == False:
         in_para = True
         para_type = word
      else:
         print 'Words in paragraph', para_type, ':', len(paragraph)
         print ' '.join(paragraph)
         del paragraph[:]
         para_type = word
  else:
    paragraph.append(word)
else:
  if in_para == True:
    print 'Words in last paragraph', para_type, ':', len(paragraph)
    print ' '.join(paragraph)
  else:
    print 'No words'

Мой текстовый файл выглядит так:

P1: Бла-бла-бла.

P2: Бла-бла-бла-бла.

P1: Бла-бла.

P3: Bla.

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

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

P1all = sum of words in paragraph

Есть ли способ посчитать вас или это и т. Д. Как два слова?

Есть идеи, как это решить?


person epo3    schedule 15.09.2011    source источник


Ответы (3)


Поздравляю с началом вашего приключения с Python! Не все в этом посте может иметь смысл прямо сейчас, но добавьте его в закладки и возвращайтесь к нему, если он окажется полезным позже. В конце концов вам следует попробовать перейти от написания сценариев к разработке программного обеспечения, и вот несколько идей для вас!

С большой силой приходит большая ответственность, и как разработчику Python вам нужно быть более дисциплинированным, чем другие языки, которые не держат вас за руку и не навязывают «хороший» дизайн.

Я считаю, что лучше начать с нисходящего дизайна.

def main():
    text = get_text()
    p_text = process_text(text)
    catalogue = process_catalogue(p_text)

БУМ! Вы только что написали всю программу - теперь вам просто нужно вернуться и заполнить пробелы! Когда вы делаете это так, это кажется менее устрашающим. Лично я не считаю себя достаточно умным, чтобы решать очень большие проблемы, но я профессионал в решении небольших проблем. Так что давайте займемся одним делом за раз. Я начну с "process_text".

def process_text(text):
    b_text = bundle_dialogue_items(text)   
    f_text = filter_dialogue_items(b_text)
    c_text = clean_dialogue_items(f_text)

Я еще не совсем уверен, что эти вещи означают, но я знаю, что текстовые проблемы, как правило, следуют шаблону, называемому «карта / сокращение», что означает, что вы выполняете и оперируете чем-то, а затем вы очищаете это и объединяете, поэтому я добавил некоторые функции-заполнители. Я могу вернуться и добавить еще, если необходимо.

Теперь напишем "каталог_процесса". Я мог бы написать «process_dict», но мне это показалось неубедительным.

def process_catalogue(p_text): 
    speakers = make_catalogue(c_text)
    s_speakers = sum_words_per_paragraph_items(speakers)
    t_speakers = total_word_count(s_speakers)

Прохладный. Не так уж плохо. Вы можете подходить к этому иначе, чем я, но я подумал, что имеет смысл объединить элементы, подсчитать слова в абзаце, а затем подсчитать все слова.

Итак, на этом этапе я бы, вероятно, сделал один или два небольших модуля lib (библиотек), чтобы заполнить оставшиеся функции. Чтобы вы могли запускать это, не беспокоясь об импорте, я собираюсь поместить все это в один файл .py, но со временем вы узнаете, как их разбить, чтобы он выглядел лучше. Итак, давайте сделаем это.

# ------------------ #
# == process_text == #
# ------------------ #

def bundle_dialogue_items(lines):
    cur_speaker = None
    paragraphs = Counter()
    for line in lines:
        if re.match(p, line):
            cur_speaker, dialogue = line.split(':')
            paragraphs[cur_speaker] += 1
        else:
            dialogue = line

        res = cur_speaker, dialogue, paragraphs[cur_speaker]
        yield res


def filter_dialogue_items(lines):
    for name, dialogue, paragraph in lines:
        if dialogue:
            res = name, dialogue, paragraph
            yield res

def clean_dialogue_items(flines):
    for name, dialogue, paragraph in flines:
        s_dialogue = dialogue.strip().split()
        c_dialouge = [clean_word(w) for w in s_dialogue]
        res = name, c_dialouge, paragraph
        yield res

аааи небольшая вспомогательная функция

# ------------------- #
# == aux functions == #
# ------------------- #

to_clean = string.whitespace + string.punctuation
def clean_word(word):
    res = ''.join(c for c in word if c not in to_clean)
    return res

Так что это может быть неочевидно, но эта библиотека спроектирована как конвейер обработки данных. Существует несколько способов обработки данных, один - конвейерная обработка, а другой - пакетная обработка. Давайте посмотрим на пакетную обработку.

# ----------------------- #
# == process_catalogue == #
# ----------------------- #

speaker_stats = 'stats'
def make_catalogue(names_with_dialogue):
    speakers = {}
    for name, dialogue, paragraph in names_with_dialogue:
        speaker = speakers.setdefault(name, {})
        stats = speaker.setdefault(speaker_stats, {})
        stats.setdefault(paragraph, []).extend(dialogue)
    return speakers



word_count = 'word_count'
def sum_words_per_paragraph_items(speakers):
    for speaker in speakers:
        word_stats = speakers[speaker][speaker_stats]
        speakers[speaker][word_count] = Counter()
        for paragraph in word_stats:
            speakers[speaker][word_count][paragraph] += len(word_stats[paragraph])
    return speakers


total = 'total'
def total_word_count(speakers):
    for speaker in speakers:
        wc = speakers[speaker][word_count]
        speakers[speaker][total] = 0
        for c in wc:
            speakers[speaker][total] += wc[c]
    return speakers

Все эти вложенные словари становятся немного сложнее. В реальном производственном коде я бы заменил их некоторыми более читаемыми классами (вместе с добавлением тестов и строк документации !!), но я не хочу, чтобы это запутало больше, чем оно есть! Хорошо, для вашего удобства ниже собрано все вместе.

import pprint
import re
import string
from collections import Counter

p = re.compile(r'(\w+?):')


def get_text_line_items(text):
    for line in text.split('\n'):
        yield line


def bundle_dialogue_items(lines):
    cur_speaker = None
    paragraphs = Counter()
    for line in lines:
        if re.match(p, line):
            cur_speaker, dialogue = line.split(':')
            paragraphs[cur_speaker] += 1
        else:
            dialogue = line

        res = cur_speaker, dialogue, paragraphs[cur_speaker]
        yield res


def filter_dialogue_items(lines):
    for name, dialogue, paragraph in lines:
        if dialogue:
            res = name, dialogue, paragraph
            yield res


to_clean = string.whitespace + string.punctuation


def clean_word(word):
    res = ''.join(c for c in word if c not in to_clean)
    return res


def clean_dialogue_items(flines):
    for name, dialogue, paragraph in flines:
        s_dialogue = dialogue.strip().split()
        c_dialouge = [clean_word(w) for w in s_dialogue]
        res = name, c_dialouge, paragraph
        yield res


speaker_stats = 'stats'


def make_catalogue(names_with_dialogue):
    speakers = {}
    for name, dialogue, paragraph in names_with_dialogue:
        speaker = speakers.setdefault(name, {})
        stats = speaker.setdefault(speaker_stats, {})
        stats.setdefault(paragraph, []).extend(dialogue)
    return speakers


def clean_dict(speakers):
    for speaker in speakers:
        stats = speakers[speaker][speaker_stats]
        for paragraph in stats:
            stats[paragraph] = [''.join(c for c in word if c not in to_clean)
                                for word in stats[paragraph]]
    return speakers


word_count = 'word_count'


def sum_words_per_paragraph_items(speakers):
    for speaker in speakers:
        word_stats = speakers[speaker][speaker_stats]
        speakers[speaker][word_count] = Counter()
        for paragraph in word_stats:
            speakers[speaker][word_count][paragraph] += len(word_stats[paragraph])
    return speakers


total = 'total'


def total_word_count(speakers):
    for speaker in speakers:
        wc = speakers[speaker][word_count]
        speakers[speaker][total] = 0
        for c in wc:
            speakers[speaker][total] += wc[c]
    return speakers


def get_text():
    text = '''BOB: blah blah blah blah
blah hello goodbye etc.

JERRY:.............................................
...............

BOB:blah blah blah
blah blah blah
blah.
BOB: boopy doopy doop
P1: Bla bla bla.
P2: Bla bla bla bla.
P1: Bla bla.
P3: Bla.'''
    text = get_text_line_items(text)
    return text


def process_catalogue(c_text):
    speakers = make_catalogue(c_text)
    s_speakers = sum_words_per_paragraph_items(speakers)
    t_speakers = total_word_count(s_speakers)
    return t_speakers


def process_text(text):
    b_text = bundle_dialogue_items(text)
    f_text = filter_dialogue_items(b_text)
    c_text = clean_dialogue_items(f_text)
    return c_text


def main():

    text = get_text()
    c_text = process_text(text)
    t_speakers = process_catalogue(c_text)

    # take a look at your hard work!
    pprint.pprint(t_speakers)


if __name__ == '__main__':
    main()

Таким образом, этот сценарий почти наверняка является излишним для этого приложения, но суть в том, чтобы увидеть, как (что сомнительно) может выглядеть читаемый, поддерживаемый модульный код Python.

Почти наверняка результат выглядит примерно так:

{'BOB': {'stats': {1: ['blah',
                       'blah',
                       'blah',
                       'blah',
                       'blah',
                       'hello',
                       'goodbye',
                       'etc'],
                   2: ['blah',
                       'blah',
                       'blah',
                       'blah',
                       'blah',
                       'blah',
                       'blah'],
                   3: ['boopy', 'doopy', 'doop']},
         'total': 18,
         'word_count': Counter({1: 8, 2: 7, 3: 3})},
 'JERRY': {'stats': {1: ['', '']}, 'total': 2, 'word_count': Counter({1: 2})},
 'P1': {'stats': {1: ['Bla', 'bla', 'bla'], 2: ['Bla', 'bla']},
        'total': 5,
        'word_count': Counter({1: 3, 2: 2})},
 'P2': {'stats': {1: ['Bla', 'bla', 'bla', 'bla']},
        'total': 4,
        'word_count': Counter({1: 4})},
 'P3': {'stats': {1: ['Bla']}, 'total': 1, 'word_count': Counter({1: 1})}}
person snakes_on_a_keyboard    schedule 04.12.2015

Мне понадобится новая переменная с подсчетом слов для каждого участника, которой я смогу манипулировать позже.

Нет, вам понадобится Counter (Python 2.7+, иначе используйте defaultdict(int)) отображение людей на количество слов.

from collections import Counter
#from collections import defaultdict

words_per_person = Counter()
#words_per_person = defaultdict(int)

for ln in inputfile:
    person, text = ln.split(':', 1)
    words_per_person[person] += len(text.split())

Теперь words_per_person['P1'] содержит количество слов P1, если text.split() - достаточно хороший токенизатор для ваших целей. (Лингвисты не согласны с определением слова, поэтому вы всегда будете получать приблизительное значение.)

person Fred Foo    schedule 15.09.2011
comment
Это хороший ответ, даже не думал об использовании коллекций. :) - person James Hurford; 16.09.2011
comment
Я запустил его и получил сообщение об ошибке: строка 59, у пользователя ‹module›, text = ln.split (':', 1) ValueError: нужно более одного значения для распаковки. Что я сделал не так? Я читал темы об этой ошибке, но они не помогли: / - person epo3; 16.09.2011
comment
Это означает, что у вас есть строка, в которой нет :. - person Fred Foo; 16.09.2011

Вы можете сделать это с двумя переменными. Один для отслеживания того, что говорит человек, другой, чтобы сохранить абзацы для говорящих. Для хранения абзацев и связывания того, кому принадлежит абзац, используйте dict с человеком в качестве ключа и список абзацев, которые, по словам этого человека, связаны с этим ключом.

para_dict = dict()
para_type = None

for word in words:
    if ('P1' in word or
        'P2' in word or
        'P3' in word ):
        #extract the part we want leaving off the ':'
        para_type = word[:2]
        #create a dict with a list of lists 
        #to contain each paragraph the person uses
        if para_type not in para_dict:
            para_dict[para_type] = list()
        para_dict[para_type].append(list())
    else:
        #Append the word to the last list in the list of lists
        para_dict[para_type][-1].append(word)

Отсюда вы можете суммировать количество произнесенных слов таким образом

for person, para_list in para_dict.items():
    counts_list = list()
    for para in para_list:
        counts_list.append(len(para))
    print person, 'spoke', sum(counts_list), 'words'
person James Hurford    schedule 15.09.2011