Как суммировать количество слов для каждого человека в диалоге? - PullRequest
2 голосов
/ 15 сентября 2011


Я начинаю изучать 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: Bla bla.

P3: Bla.

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

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

P1all = sum of words in paragraph

Есть ли способ считать "ты" или "это" и т. Д. Двумя словами?

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

Ответы [ 3 ]

5 голосов
/ 15 сентября 2011

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

Нет, вам понадобится Counter (Python2.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() - достаточно хороший токенизатор для вашегоцели.(Лингвисты не согласны с определением слова , поэтому вы всегда получите приблизительное значение.)

1 голос
/ 04 декабря 2015

Поздравляю с началом вашего приключения с 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_catalogue'. Я мог бы написать «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)

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

Итак, на данный момент я, вероятно, сделаю один или два маленьких модуля '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})}}
1 голос
/ 16 сентября 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'
...