Поиск слов из словаря в txt и возврат значений

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

Но я что-то напортачил. Мой процесс следующий:

  1. Словарь:

    words = {'very funny': 3,'funny': 2,'accidentally funny': 1}  
    
  2. Текстовый файл (с именем: sample.txt):

Monty Python is very funny. +3
Some standups are funny. +2
Politicians are sometimes accidentally funny. +1
Real pythons are not funny at all. +2

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

  1. Взятие текста из файла .txt:

    with open('sample.txt', 'r') as text:
        data = text.read()
    
  2. Функция:

    def counter(data): #this should find keywords
        default_value = 0 #var for stuff not included in dict
        points = 0 
        for i in data:
            points += words.get(i, default_value)  #using get to avoid valueError
        print(points)
        return points  
    
    counter(data)
    
  3. Выход:

    0  
    
    Process finished with exit code 0
    

редактировать Я знал, что кое-что забыл;]:

Проблемы

  1. Моя функция, кажется, еще ничего не считает
  2. Я хочу подсчитать эти ключевые фрагменты таким образом, чтобы одно слово «смешной» не получалось тройным, а запускало только эффект 'funny': 2. Я понятия не имею, как к этому подойти.

Это мой первый вопрос в стеке, поэтому, если я что-то напортачил, дайте мне знать.

Поскольку первый вопрос действительно хорошо объяснен: D

Netwave 13.09.2018 19:48

Каков ожидаемый результат для вашего ввода?

Austin 13.09.2018 19:50

@Austin Я хотел, чтобы очки были +3 для 1-й линии, +2 для 2-й, +1 для 3-й и снова +2 для 4-й = 8

noobcodes 13.09.2018 21:09
2
3
826
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

for I in data вот в чем проблема. поскольку данные - это str, вы в основном повторяете отдельные символы, а не полные слова.

Я предлагаю вам перевернуть вашу логику - перебрать термины и посчитать, сколько раз они появляются:

def counter(data): #this should find keywords
    points = 0 
    for word, value in words.items():
        points += value * data.count(word)
    print(points)
    return points  

Однако это означает, что некоторые термины могут оцениваться более одного раза - 'very funny'содержит'funny' тоже, поэтому предполагается, что он стоит 5 (3 от 'very funny' и 2 от 'funny')?

Ваш текст содержит 4 funny, 1 very funny и 1 accidentally funny, поэтому результат будет 4 * 2 + 3 + 1 = 12.

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

noobcodes 13.09.2018 20:55
Ответ принят как подходящий

Проблема здесь в вашей структуре данных. В общем, словари хороши, если вы хотите искать по ним. Однако это не то, что вы здесь делаете. Вместо этого я бы посоветовал составить список кортежей. Итак, ваш words будет выглядеть так:

words = [ ("very funny", 3), ("funny", 2), ("accidentally funny", 1) ]

Затем, когда вы запускаете цикл for, вы перебираете каждый символ в строке вместо каждого слова. Вместо этого вам следует перебрать значения в words и найти количество вхождений каждого слова:

import re

total = 0
for w in words:
    total += w[1] * sum(re.finditer(w[0], data))

Однако, как уже указывалось, будут найдены дубликаты. Чтобы избежать этого, вы должны заказать words в том порядке, в котором вы хотите их искать, и удалить значения, которые вы найдете в data:

words = [ ("very funny", 3), ("accidentally funny", 1), ("funny", 2) ]

total = 0
for w in words:
    total += len(list(re.finditer(w[0], data))) * w[1]
    data = data.replace(w[0], '')

Однако это не очень эффективно. Если вы хотите, чтобы это работало быстрее, я бы использовал LL парсер. По сути, вы должны разделить свои данные на пробелы и перебирать их, вытягивая следующие символы k, где k - это количество слов в самой длинной записи в words. Вы должны соединить эти слова k вместе, используя пробелы, и проверить, совпадают ли они с какой-либо из записей в words. В этом случае вы, кстати, захотите использовать словарь. Сделать это можно так:

splitData = data.split(' \r\n')
total = 0
for i in range(0, len(splitData)):

    # Longest entry in words has two words so we use i + 2
    phrase = ' '.join(splitData[i:(i + 2)])
    if (phrase in words):
        total += words[phrase]

Конечно, это решение предполагает, что все ваши записи содержат два слова, что не так. Чтобы решить эту проблему, превратите words в словарь словарей, где словарь верхнего уровня сопоставляет фразу с количеством содержащихся в ней слов:

words = {1: {"funny": 2}, 2: {"very funny": 3, "accidentally funny": 1}}
splitData = data.split(' \r\n')
total = 0
i = 0
while (i < len(splitData)):
    for l, mapping in words.items():
        phrase = ' '.join(splitData[i:(i + l)])
        if (phrase in mapping):
            total += mapping[phrase]
            i += 1
            continue
    i++

Обратите внимание, что я добавляю l к i, чтобы избежать дублирования. Опять же, вы можете использовать кортежи вместо словаря, чтобы утверждать порядок вашего поиска. Кроме того, я использую здесь цикл while вместо forloop, потому что вы не можете фактически изменить значение инварианта цикла внутри цикла в Python.

Выглядит хорошо! Я попробую это как можно скорее. Спасибо, что объяснили шаг за шагом. Это помогает МНОГО.

noobcodes 13.09.2018 21:17

@noobcodes Если решение поможет, я бы порекомендовал проголосовать за. Не только для меня, а в целом.

Woody1193 13.09.2018 22:36

Буду, как только проверю и как только получу 15 респ. Мы, новички, не можем голосовать до этого момента. ;]

noobcodes 13.09.2018 23:02

@noobcodes Я также предлагаю поставить галочку рядом с ответом, который вы хотите указать как «решение» вашей проблемы. Не обязательно должен быть моим, но это поможет другим пользователям в будущем рассмотреть ваш вопрос :)

Woody1193 13.09.2018 23:48

В части, предшествующей введению парсера LL, я получил следующее: total += len(re.finditer(w[0], data)) * w[1]TypeError: object of type 'callable_iterator' has no len()

noobcodes 14.09.2018 11:26

Мои извенения. Я обновил ответ, пожалуйста, посмотрите

Woody1193 14.09.2018 17:04

Вот что бы я сделал. Я бы разделил этот текст на список слов, а затем перебрал бы его. Я соединяю каждое слово с предыдущим. затем посмотрите слово, состоящее из двух слов, в словаре. Конечно, я не могу связать первое слово со словом перед ним. Таким образом, переменная first_iteration истинна только для первой итерации и изменяется на false в конце первой итерации на false. Надеюсь, это решит вашу проблему.

def counter(data): #this should find keywords
    default_value = 0 #var for stuff not included in dict
    points = 0 
    first_iteration = True
    wordsList = data.split()
    for i in wordList:
        if (!first_iteration):
            theWord = i + " " + prev
        points += words.get(theWord, default_value)  #using get to avoid valueError
        prev = i
        first_iteration = False
    print(points)
    return points  

counter(data)

Другие вопросы по теме