Python: вернуть слова в строке, которые встречаются ровно один раз

Допустим, у меня есть функция, которая принимает некоторую строку, а затем мне нужно вернуть набор слов в этой строке, которые встречаются ровно один раз. Каков наилучший способ сделать это? Было бы полезно использовать dict? Я пробовал псевдокод, например:

counter = {}
def FindWords(string):
    for word in string.split()
        if (word is unique): counter.append(word)
return counter

Есть ли лучший способ реализовать это? Спасибо!

редактировать:

Скажем, у меня есть: «Мальчик перепрыгнул через другого мальчика». Я хочу вернуть «перепрыгнули», «через» и «другое».

Кроме того, я хотел бы вернуть это как набор, а не список.


person J. P.    schedule 03.10.2017    source источник
comment
Какой набор слов у вас есть?   -  person Ajax1234    schedule 04.10.2017
comment
Скажем, у меня есть набор слов, например: Мальчик перепрыгнул через другого мальчика. Я хочу вернуться перепрыгнули, перепрыгнули и прочее.   -  person J. P.    schedule 04.10.2017


Ответы (5)


Вы можете использовать Counter из collections и вернуть набор слов, которые встречаются только один раз.

from collections import Counter

sent = 'this is my sentence string this is also my test string'

def find_single_words(s):
    c = Counter(s.split(' '))
    return set(k for k,v in c.items() if v==1)

find_single_words(sent)
# returns:
{'also', 'sentence', 'test'}

Чтобы сделать это с помощью только базовых утилит Python, вы можете использовать словарь для подсчета вхождений, воспроизводя функциональность Counter.

sent = 'this is my sentence string this is also my test string'

def find_single_words(s):
    c = {}
    for word in s.split(' '):
        if not word in c:
             c[word] = 1
        else:
             c[word] = c[word] + 1
    return [k for k,v in c.items() if v==1]

find_single_words(sent)
# returns:
['sentence', 'also', 'test']
person James    schedule 03.10.2017
comment
Есть ли способ сделать это без экспорта внешних инструментов, таких как Counter? - person J. P.; 04.10.2017
comment
@Дж.П. collections является частью стандартной библиотеки, на самом деле это не внешний инструмент. - person James; 04.10.2017
comment
@Дж.П. я добавил дополнительную часть к моему ответу, см. выше - person James; 04.10.2017
comment
Привет спасибо! Знаете ли вы, как бы вы изменили это, если бы хотели вернуть набор вместо списка? Не могли бы вы вместо c.items() вернуть набор? - person J. P.; 04.10.2017
comment
@Дж.П. конечно, я изменил вторую часть своего ответа, чтобы вернуть набор - person James; 04.10.2017
comment
Отлично, большое спасибо! когда вы используете: return[k for k,v в c.items() if v==1], определяется ли здесь v заново как индекс c? - person J. P.; 04.10.2017
comment
@James, если вы проверите это с вводом OP (мальчик перепрыгнул через другого мальчика), ваш код вернет {'The', 'jumped', 'other', 'over', 'the'}, а это не то, что хотел OP. Слова следует перевести в нижний регистр, затем посмотреть их частотность. - person srikavineehari; 15.10.2017

Это может быть то, что вы имеете в виду.

>>> counts = {}
>>> sentence =  "The boy jumped over the other boy"
>>> for word in sentence.lower().split():
...     if word in counts:
...         counts[word]+=1
...     else:
...         counts[word]=1
...         
>>> [word for word in counts if counts[word]==1]
['other', 'jumped', 'over']
>>> set([word for word in counts if counts[word]==1])
{'other', 'jumped', 'over'}

Но использование defaultdict из коллекций, как предложил кто-то другой, приятнее.

person Bill Bell    schedule 03.10.2017
comment
Уникум не должен дарить мальчика или мальчика. Следует только давать перепрыгнул, перепрыгнул и прочее. - person J. P.; 04.10.2017
comment
Благодарю вас! Вы знаете, как вернуть это как набор, а не список? - person J. P.; 04.10.2017
comment
Добавлено, что in.set() изменяет список на набор. - person Bill Bell; 04.10.2017

Вы можете попробовать это:

s = "The boy jumped over the other boy"
s1 = {"jumped", "over", "other"}
final_counts = [s.count(i) for i in s1]

Выход:

[1, 1, 1]
person Ajax1234    schedule 03.10.2017

s='The boy jumped over the other boy'
def func(s):
    l=[]
    s=s.split(' ')  #edit for case-sensitivity here
    for i in range(len(s)):
        if s[i] not in s[i+1:] and s[i] not in s[i-1::-1]:
            l.append(s[i])
    return set(l)  #convert to set and return
print(func(s))

это должно работать очень хорошо.

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

если вы не хотите учитывать регистр, вы можете добавить s=s.lower() или s=s.upper() перед его разделением.

person TubbyStubby    schedule 03.10.2017
comment
Прохождение всего списка слов для каждого слова делает этот алгоритм O (n ^ 2), который может стать довольно медленным по мере увеличения входных данных. Использование словаря для подсчета количества вхождений будет намного лучше масштабироваться для больших входных данных. - person Bass; 04.10.2017

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

>>> sentence = "The boy jumped over the other boy"
>>> set(word for word in sentence.lower().split() if sentence.count(word) == 1)
{'other', 'over', 'jumped'}
>>> 

Изменить: это легче читать:

>>> sentence = 'The boy jumped over the other boy'
>>> words = sentence.lower().split()
>>> uniques = {word for word in words if words.count(word) == 1}
>>> uniques
{'over', 'other', 'jumped'}
>>> type(uniques)
<class 'set'>
person srikavineehari    schedule 03.10.2017