2009-09-09 7 views
4

Pour illustrer ce que je veux dire par là, voici un exempleliste Convertir des objets à une liste d'entiers et une table de consultation

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

Je veux convertir cette liste et une définition des groupes à une liste de des entiers et un dictionnaire de recherche pour que chaque élément du groupe obtienne un identifiant unique. Cette id doit mapper l'élément dans la table de recherche comme celui-ci

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'] 
} 

Je me demande s'il y a une solution élégante et pythonique à ce problème.

Je suis également ouvert à une meilleure terminologie que create_lookup_list etc

Répondre

3

defaultdict combiné à la méthode itertools.count().next est un bon moyen d'affecter des identifiants à des éléments uniques. Voici un exemple de la façon d'appliquer dans votre cas:

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: noter que count().next devient count().__next__ ou lambda: next(count()) en Python 3.

+0

J'essayais juste de mettre cela ensemble, mais je n'ai pas eu le defaultdict d'un defaultdict ... bravo! – PaulMcG

1

Voici ma propre solution - je doute qu'il est le meilleur

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

Je suppose que la vitesse ne peut pas d'importance, mais je me demande pourquoi beaucoup de l'autre les réponses utilisent la recherche linéaire quand ils pourraient utiliser un dictionnaire comme vous le faites.Ma seule plainte serait qu'en inversant le mappage chaîne-> index, vous utilisez un tri. –

2

mine est à peu près la même longueur et de la complexité:

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

Pourquoi utiliser le time.index linéaire? –

0

Voici ma solution, ce n'est pas mieux - c'est juste différent :)

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

la chose est que 'si la valeur non dans la table [clé]' a une complexité O (n) qui peut être un problème s'il y a beaucoup d'éléments dans un groupe. J'ai choisi l'assignation d'index par dictionnaire parce que 'si la clé dans dict' est beaucoup plus rapide –

+0

Donc vous pouvez utiliser ** la structure de la table de recherche de S.Lott ** - il y aura ** si la clé dans dict ** vérifier et non ** L'appel d'index() ** sera nécessaire. –

1

Ceci est un peu plus simple et plus direct.

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) 

Si les recherches étaient des dictionnaires appropriés, pas des listes, cela pourrait être simplifié davantage.
Faites votre « table de consultation » ont la structure suivante

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

Et il peut encore être réduit en complexité.

Vous pouvez activer cette copie de travail des recherches dans c'est inverse comme suit:

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

Mais je veux que la table de recherche me donne l'élément original pour un identifiant donné –

0

est ici la mienne, la fonction intérieure me permet d'écrire le tuple index comme un générateur.

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

Dans la réponse de Otto (ou de quelqu'un d'autre avec string-> dicts id), je REPLACE (si obsédée par la vitesse est votre truc):

# 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]) 

par

# 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()) 

Cela est préférable car l'affectation directe à chaque élément dans le tableau inverse est directement plus rapide que le tri.

Questions connexes