Преобразование списка объектов в список целых чисел и справочную таблицу

Чтобы проиллюстрировать, что я имею в виду, вот пример

messages = [ ('Ricky', 'Steve', 'SMS'), ('Steve', 'Karl', 'SMS'), ('Karl', 'Nora', 'Email') ] 

Я хочу преобразовать этот список и определение групп в список целых чисел и словарь поиска, чтобы каждый элемент в группе получал уникальный идентификатор. Этот идентификатор должен отображаться в элементе таблицы поиска, как это

 messages_int, lookup_table = create_lookup_list( messages, ('person', 'person', 'medium')) print messages_int [ (0, 1, 0), (1, 2, 0), (2, 3, 1) ] print lookup_table { 'person': ['Ricky', 'Steve', 'Karl', 'Nora'], 'medium': ['SMS', 'Email'] } 

Интересно, есть ли элегантное и питоническое решение этой проблемы.

Я также открыт для лучшей терминологии, чем create_lookup_list т. Д.

defaultdict сочетании с itertools.count().next метод – хороший способ присвоить идентификаторы уникальным элементам. Вот пример того, как применить это в вашем случае:

 from itertools import count from collections import defaultdict def create_lookup_list(data, domains): domain_keys = defaultdict(lambda:defaultdict(count().next)) out = [] for row in data: out.append(tuple(domain_keys[dom][val] for val, dom in zip(row, domains))) lookup_table = dict((k, sorted(d, key=d.get)) for k, d in domain_keys.items()) return out, lookup_table 

Изменить: обратите внимание, что count().next count().__next__ будет count().__next__ или lambda: next(count()) в Python 3.

Мой примерно такой же длины и сложности:

 import collections def create_lookup_list(messages, labels): # Collect all the values lookup = collections.defaultdict(set) for msg in messages: for l, v in zip(labels, msg): lookup[l].add(v) # Make the value sets lists for k, v in lookup.items(): lookup[k] = list(v) # Make the lookup_list lookup_list = [] for msg in messages: lookup_list.append([lookup[l].index(v) for l, v in zip(labels, msg)]) return lookup_list, lookup 

В ответе Отто (или кто-то еще с string-> id dicts) я бы заменил (если одержимость скоростью – это ваша вещь):

 # create the lookup table lookup_dict = {} for group in indices: lookup_dict[group] = sorted(indices[group].keys(), lambda e1, e2: indices[group][e1]-indices[group][e2]) 

от

 # k2i must map keys to consecutive ints [0,len(k2i)-1) def inverse_indices(k2i): inv=[0]*len(k2i) for k,i in k2i.iteritems(): inv[i]=k return inv lookup_table = dict((g,inverse_indices(gi)) for g,gi in indices.iteritems()) 

Это лучше, потому что прямое присваивание каждому элементу в обратном массиве происходит быстрее, чем сортировка.

Вот мое собственное решение – я сомневаюсь, что это лучший

 def create_lookup_list(input_list, groups): # use a dictionary for the indices so that the index lookup # is fast (not necessarily a requirement) indices = dict((group, {}) for group in groups) output = [] # assign indices by iterating through the list for row in input_list: newrow = [] for group, element in zip(groups, row): if element in indices[group]: index = indices[group][element] else: index = indices[group][element] = len(indices[group]) newrow.append(index) output.append(newrow) # create the lookup table lookup_dict = {} for group in indices: lookup_dict[group] = sorted(indices[group].keys(), lambda e1, e2: indices[group][e1]-indices[group][e2]) return output, lookup_dict 

Это немного проще и более прямолинейно.

 from collections import defaultdict def create_lookup_list( messages, schema ): def mapped_rows( messages ): for row in messages: newRow= [] for col, value in zip(schema,row): if value not in lookups[col]: lookups[col].append(value) code= lookups[col].index(value) newRow.append(code) yield newRow lookups = defaultdict(list) return list( mapped_rows(messages) ), dict(lookups) 

Если поисковые запросы были правильными словарями, а не списками, это могло бы быть еще более упрощено.
Сделайте свою «таблицу поиска» следующей структурой

 { 'person': {'Ricky':0, 'Steve':1, 'Karl':2, 'Nora':3}, 'medium': {'SMS':0, 'Email':1} } 

И это может быть дополнительно уменьшено по сложности.

Вы можете превратить эту рабочую копию запросов в обратную:

 >>> lookups = { 'person': {'Ricky':0, 'Steve':1, 'Karl':2, 'Nora':3}, 'medium': {'SMS':0, 'Email':1} } >>> dict( ( d, dict( (v,k) for k,v in lookups[d].items() ) ) for d in lookups ) {'person': {0: 'Ricky', 1: 'Steve', 2: 'Karl', 3: 'Nora'}, 'medium': {0: 'SMS', 1: 'Email'}} 

Вот мое решение, это не лучше – это просто разные 🙂

 def create_lookup_list(data, keys): encoded = [] table = dict([(key, []) for key in keys]) for record in data: msg_int = [] for key, value in zip(keys, record): if value not in table[key]: table[key].append(value) msg_int.append(table[key].index(value)) encoded.append(tuple(msg_int)) return encoded, table 

Вот моя, внутренняя функция позволяет мне писать индекс-кортеж как генератор.

 def create_lookup_list( data, format): table = {} indices = [] def get_index( item, form ): row = table.setdefault( form, [] ) try: return row.index( item ) except ValueError: n = len( row ) row.append( item ) return n for row in data: indices.append( tuple( get_index( item, form ) for item, form in zip( row, format ) )) return table, indices