Python — сравнение наборов и возврат набора с наиболее подходящими элементами

#python #set

#python #набор

Вопрос:

У меня есть набор строк: {'Type A', 'Type B', 'Type C'} например, я назову его x . Набор может содержать до 10 строк.

Существует также большой список наборов, например [{'Type A', 'Type B', 'Type C'}, {'Type A', 'Type B', 'Type C'}, {'Type B', 'Type C, 'Type D'}, {'Type E', 'Type F', 'Type G'}] , и так далее.

Моя цель — вернуть все наборы в большом списке, которые содержат 60% или более тех же элементов, x что и . Итак, в этом примере он вернет первые 3 набора, но не 4-й.

Я знаю, что мог бы перебирать каждый набор, сравнивать элементы, а затем использовать количество сходств для своих дел, но это требует довольно много времени, и в моем большом списке, вероятно, будет много наборов. Есть ли лучший способ сделать это? Я думал об их использовании frozenset() и хешировании, но я не уверен, какую функцию хеширования я бы использовал и как я бы сравнивал хэши.

Любая помощь будет оценена — большое спасибо!

Комментарии:

1. Что вы подразумеваете под «сравнением элементов»? Не могли бы вы просто получить intersection из 2 наборов и проверить размер?

2. @ScottHunter о, спасибо! Я забыл о пересечениях — я думаю, это должно решить мою проблему,

3. set элементы уже хэшированы, поэтому поиск элемента — это O(1) операция. При этом ваши основные накладные расходы заключаются в переборе списка, поскольку предполагается, что в нем должно быть много наборов , и хеширование в этом не поможет.

Ответ №1:

 l = [{'Type A', 'Type B', 'Type C'}, {'Type A', 'Type B', 'Type C'}, {'Type B', 'Type C', 'Type D'}, {'Type E', 'Type F', 'Type G'}]

x = {'Type A', 'Type B', 'Type C'}

for s in l:
    print (len(x.intersection(s)))
 

Вывод:

 3
3
2
0
 

С возвращением функции и списка кортежей:

 def more_than(l,n):
    return [ (s,round(len(x.intersection(s))/len(x),2)) for s in l if len(x.intersection(s))/len(x) > n]
 
print (more_than(l,0.6))
 

Вывод:

 [({'Type B', 'Type A', 'Type C'}, 1.0), ({'Type B', 'Type A', 'Type C'}, 1.0), ({'Type B', 'Type C', 'Type D'}, 0.67)]
 

Здесь, просто для удобства, я использовал round(len(x.intersection(s))/len(x),2) which переводится round(x,y) как . round() Просто округлит ваше отношение до числа десятичных знаков, указанного с помощью y переменной.

Комментарии:

1. Обратите внимание, что это приведет только к печати наборов, но не вернет их в списке, как того требует OP. Он также не проверяет пороговое значение (60%).

2. Проверьте редактирование, первым был только принцип

3. @Synthase, не могли бы вы помочь мне выяснить, почему вы использовали 2 в этом наборе. (s, круглый(len(x.пересечение (s))/len(x),2)). Я новичок в python и учусь на других проблемах и на основе приведенных ответов.

4. @Synthase, я бы определенно поставил галочку на кнопку «Принять», но этот вопрос был поднят кем-то другим.

Ответ №2:

Как насчет этого?

 x = {'Type A', 'Type B', 'Type C'}
lst = [{'Type A', 'Type B', 'Type C'}, 
       {'Type A', 'Type B', 'Type C'}, 
       {'Type B', 'Type C', 'Type D'},
       {'Type E', 'Type F', 'Type G'}]    
[s for s in lst if len(s.intersection(x)) > len(x) * 0.6]
 

Комментарии:

1. Это будет работать быстрее, если вы присвоите len(x) * 0.6 переменной перед пониманием.