代码之家  ›  专栏  ›  技术社区  ›  Otto Allmendinger

将对象列表转换为整数列表和查阅表格

  •  4
  • Otto Allmendinger  · 技术社区  · 15 年前

    为了说明我的意思,这里有一个例子

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

    我想将这个列表和组的定义转换成整数列表和查找字典,以便组中的每个元素都得到唯一的ID。该ID应该像这样映射到查找表中的元素

    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

    7 回复  |  直到 15 年前
        1
  •  3
  •   Ants Aasma    15 年前

    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__ lambda: next(count()) 在Python 3中。

        2
  •  2
  •   Ned Batchelder    15 年前

    我的长度和复杂性大致相同:

    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
    
        3
  •  2
  •   Jonathan Graehl    15 年前

    在奥托的回答中(或其他任何人的字串->ID听写),我会替换(如果你对速度着迷的话):

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

    这更好,因为直接分配给逆数组中的每个项比排序更快。

        4
  •  1
  •   Otto Allmendinger    15 年前

    这是我自己的解决方案-我怀疑这是最好的

    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
    
        5
  •  1
  •   S.Lott    15 年前

    这有点简单,也更直接。

    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'}}
    
        6
  •  0
  •   Mikhail Churbanov    15 年前

    这是我的解决方案,不是更好-只是不同而已:)

    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
    
        7
  •  0
  •   Jochen Ritzel    15 年前

    这是我的,内部函数允许我将索引元组作为生成器编写。

    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