2009-09-09 4 views
4

Чтобы проиллюстрировать, что я имею в виду, вот примерсписок Преобразования объектов в список целых чисел и справочной таблицу

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 т.д.

ответ

3

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 

Edit: обратите внимание, что count().next становится count().__next__ или lambda: next(count()) в Python 3.

+0

Я просто пытался собрать это вместе, но не получил defaultdict из defaultdict ... хорошо сделано! – PaulMcG

1

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

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 
+0

Я думаю, скорость не имеет значения, но мне интересно, почему многие из другого ответы используют линейный поиск, когда они могут использовать словарь, как вы.Моя единственная жалоба заключалась в том, что при инверсии преобразования string-> index вы используете сортировку. –

2

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

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 
+0

Зачем использовать линейное время list.index? –

0

Вот мое решение, это не лучше - это просто разные :)

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 
+1

Дело в том, что 'if value not in table [key]' имеет сложность O (n), которая может быть проблемой, если в группе имеется много элементов. Я выбрал назначение индекса по словарю, потому что 'if key in dict' намного быстрее –

+0

Таким образом, вы можете использовать структуру поиска Look S. S.Lott ** - там будет **, если ключ в dict ** проверяется и нет ** index() ** потребуется. –

1

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

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'}} 
+1

Но я хочу, чтобы таблица поиска дала мне исходный элемент для заданного id –

0

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

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 
2

В ответ Отто (или кто-либо еще с string-> ид 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()) 

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