2014-02-18 3 views
2

У меня есть список объектов, каждый из которых имеет 3 атрибута, и я хочу найти, есть ли у какого-либо из моих объектов какие-либо перекрывающиеся атрибуты и вытаскивать их как наборы объектов с перекрывающимися атрибутами.Соответствие Python по повторяющимся атрибутам

Позвольте мне уточнить:

class Obj(): 
    '''My example objects! they have 3 attributes.''' 
    def __init__(a, b, c): 
     self.a = a 
     self.b = b 
     self.c = c 

>>>> obj1 = Obj(a= 1, b = 2, c = 3) 
>>>> obj2 = Obj(a= 1, b = 5, c = 6) 
>>>> obj3 = Obj(a= 10, b = 12, c = 3) 
>>>> obj4 = Obj(a= 0, b = 0, c = 0) 
>>>> obj5 = Obj(a= 100, b = 5, c = 5) 
>>>> obj6 = Obj(a = -10, b = 0, c = 56) 
>>>> obj7 = Obj(a = None, b = None, c = None) 

# obj2 matches obj1 on attribute: "a" 
# obj3 matches obj1 on attribute: "c" 
# obj5 matches obj2 on attribute: "b" 

# obj6 matches obj4 on attribute: "b" 

# obj7 matches no one 

Поэтому мой вывод должен быть:

[[obj1, obj2, obj3, obj5], [obj4, obj6], [obj7]] 

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

Редактировать: пришлось изменить несколько цифр в соответствии с моим примером. извините за опечатку!

Edit: Мои текущие попытки решения:

adict = defaultdict(list) 
for obj in list_objects: 
    adict[obj.a].append(obj) 
    adict[obj.b].append(obj) 
    adict[obj.c].append(obj) 

Тогда поиск adict.values ​​() для списков больше, чем 2. Затем объединить списки (как-то).
Я надеюсь на элегантное решение?

+0

Итак, вы хотите, чтобы «совпадения» были транзитивными, если obj2 соответствует obj1, а obj3 соответствует obj2, то obj3 соответствует obj1, даже если они не разделяют никаких атрибутов? – abarnert

+2

Похоже на [Union Find] (http://en.wikipedia.org/wiki/Union_find) –

+1

Как вы хотите справиться с тем, что 'obj4' и' obj5' соответствуют значению 'a'? –

ответ

4

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

Start with an empty set of equivalence sets 
For each value: 
    Find all the equivalence sets that have any value that matches our value 
    Remove those equivalent sets from the result set 
    Union those equivalence sets together and add our new value 
    Add that to the result set 

Это должно сделать это, не так ли?

В Python, пустое множество set(), удалить значение из набора по телефону s.remove(v), добавить значение в набор по телефону s.add(v), и объединение наборов по телефону (деструктивно) s1 |= s2, или (не разрушительно) s = set.union(s1, s2, s3, …). (Вы можете использовать это вместе с * синтаксисом: если у вас есть набор множеств, или список наборов, set.union(*s) дает объединение всех.)

Таким образом, только хитрый бит «найти все наборы эквивалентности которые имеют любой элемент, который соответствует нашему элементу ». «... имеет любое значение, которое соответствует нашему значению» - это вызов any с пониманием: any(matches(value, element) for element in equivalenceset). И «найти все наборы эквивалентности, которые ...» - это понимание: {equivalenceset for equivalenceset in equivalencesets if …}.

Очевидно, что вам также необходимо написать функцию matches, но это легко: x.a == y.a or x.b == y.b or x.c == y.c.

Этого должно быть достаточно, чтобы написать все сами.

+0

извините, что зацикливается в цикле for? список входных данных для всех объектов? (что не кажется правильным?) – user1639926

+0

@ user1639926: Внешний цикл циклически перебирает список входных данных всех объектов.Каждый из них будет помещен в набор эквивалентности (возможно, слияние нескольких наборов эквивалентности вместе, как он это делает), один за другим; как только вы их закончите, все будет сделано. Внутренние петли в понимании зацикливаются на одном наборе эквивалентности результата и на полном наборе результатов наборов эквивалентности. – abarnert

+1

@ user1639926: Если вы наклонно указываете, что это будет худший случай O (N^2) ... тогда да, это правда, но поскольку вам действительно нужно сравнивать каждый объект друг с другом, чтобы узнать если он совпадает (за исключением случая, когда вы уже выяснили, что они соответствуют транзитивному, что этот алгоритм позаботится), я не вижу никакого способа обойти это. – abarnert

Смежные вопросы