存储一组四个(或更多)值的最佳数据结构是什么?

San*_*raj 5 python lookup logarithm data-structures

说我有以下variables和它对应的values代表一个record.

name = 'abc'
age = 23
weight = 60
height = 174
Run Code Online (Sandbox Code Playgroud)

请注意,value可能是不同的types(string,integer,float,引用到任何-其他对象,等等).

会有很多records(至少> 100,000).当所有这四个(实际上是它们)组合在一起时,每一个都record将是.换句话说,存在没有与所有4 相同.uniquevariablesvaluesrecordvalues

我试图找到一个高效的数据结构,Python这将让我(商店)获取records基于其中任何一项variableslog(n)时间复杂度.

例如:

def retrieve(name=None,age=None,weight=None,height=None) 
    if name is not None and age is None and weight is None and height is None:
        /* get all records with the given name */
    if name is None and age is not None and weight is None and height is None:
        /* get all records with the given age */
    ....
    return records
Run Code Online (Sandbox Code Playgroud)

retrieve应该调用的方式如下:

retrieve(name='abc') 
Run Code Online (Sandbox Code Playgroud)

以上应该返回 [{name:'abc', age:23, wight:50, height=175}, {name:'abc', age:28, wight:55, height=170}, etc]

retrieve(age=23) 
Run Code Online (Sandbox Code Playgroud)

以上应该返回 [{name:'abc', age:23, wight:50, height=175}, {name:'def', age:23, wight:65, height=180}, etc]

而且,我可能需要variables在将来为此记录添加一两个.例如,比如说sex = 'm'.因此,该retrieve功能必须是可扩展的.

简而言之:是否存在一个数据结构,Python其中允许storing a recordn数量columns(名称,年龄,性别,体重,身高等)并retrieving records基于任何(一个)columnin logarithmic(或理想的constant - O(1)查找时间)复杂度?

mar*_*eau 8

Python中没有一个内置的数据结构可以完成你想要的任何事情,但是它可以很容易地结合使用它来实现你的目标并且相当有效地完成目标.

例如,假设您的输入是逗号分隔值文件中的以下数据,employees.csv其中字段名称定义如第一行所示:

name,age,weight,height
Bob Barker,25,175,6ft 2in
Ted Kingston,28,163,5ft 10in
Mary Manson,27,140,5ft 6in
Sue Sommers,27,132,5ft 8in
Alice Toklas,24,124,5ft 6in
Run Code Online (Sandbox Code Playgroud)

以下是工作代码,说明如何将此数据读取并存储到记录列表中,并自动创建单独的查找表,以查找与每个记录的字段中包含的值相关联的记录.

记录是由其创建的类的实例,namedtuple因为每个__dict__类都缺少类实例通常包含的属性.使用它们可以使用点语法来访问每个字段的字段,例如record.fieldname.

查找表是defaultdict(list)实例,其平均提供类似字典的O(1)查找时间,并且还允许多个值与每个查找时间相关联.所以查找键是要搜索的字段值的值,与之关联的数据将是列表中Person存储的employees具有该值的记录的整数索引列表 - 因此它们都相对较小.

请注意,该类的代码完全是数据驱动的,因为它不包含任何硬编码的字段名称,而是在读入时从csv数据输入文件的第一行中获取.当然,当使用实例时,所有retrieve()方法调用必须提供有效的字段名称.

更新

修改为在首次读取数据文件时不为每个字段的每个唯一值创建查找表.现在,retrieve()"lazily"方法仅在需要时创建它们(并保存/缓存结果以供将来使用).也修改为在Python 2.7+中工作,包括3.x.

from collections import defaultdict, namedtuple
import csv

class DataBase(object):
    def __init__(self, csv_filename, recordname):
        # Read data from csv format file into a list of namedtuples.
        with open(csv_filename, 'r') as inputfile:
            csv_reader = csv.reader(inputfile, delimiter=',')
            self.fields = next(csv_reader)  # Read header row.
            self.Record = namedtuple(recordname, self.fields)
            self.records = [self.Record(*row) for row in csv_reader]
            self.valid_fieldnames = set(self.fields)

        # Create an empty table of lookup tables for each field name that maps
        # each unique field value to a list of record-list indices of the ones
        # that contain it.
        self.lookup_tables = {}

    def retrieve(self, **kwargs):
        """ Fetch a list of records with a field name with the value supplied
            as a keyword arg (or return None if there aren't any).
        """
        if len(kwargs) != 1: raise ValueError(
            'Exactly one fieldname keyword argument required for retrieve function '
            '(%s specified)' % ', '.join([repr(k) for k in kwargs.keys()]))
        field, value = kwargs.popitem()  # Keyword arg's name and value.
        if field not in self.valid_fieldnames:
            raise ValueError('keyword arg "%s" isn\'t a valid field name' % field)
        if field not in self.lookup_tables:  # Need to create a lookup table?
            lookup_table = self.lookup_tables[field] = defaultdict(list)
            for index, record in enumerate(self.records):
                field_value = getattr(record, field)
                lookup_table[field_value].append(index)
        # Return (possibly empty) sequence of matching records.
        return tuple(self.records[index]
                        for index in self.lookup_tables[field].get(value, []))

if __name__ == '__main__':
    empdb = DataBase('employees.csv', 'Person')

    print("retrieve(name='Ted Kingston'): {}".format(empdb.retrieve(name='Ted Kingston')))
    print("retrieve(age='27'): {}".format(empdb.retrieve(age='27')))
    print("retrieve(weight='150'): {}".format(empdb.retrieve(weight='150')))
    try:
        print("retrieve(hight='5ft 6in'):".format(empdb.retrieve(hight='5ft 6in')))
    except ValueError as e:
        print("ValueError('{}') raised as expected".format(e))
    else:
        raise type('NoExceptionError', (Exception,), {})(
            'No exception raised from "retrieve(hight=\'5ft\')" call.')
Run Code Online (Sandbox Code Playgroud)

输出:

retrieve(name='Ted Kingston'): [Person(name='Ted Kingston', age='28', weight='163', height='5ft 10in')]
retrieve(age='27'): [Person(name='Mary Manson', age='27', weight='140', height='5ft 6in'),
                     Person(name='Sue Sommers', age='27', weight='132', height='5ft 8in')]
retrieve(weight='150'): None
retrieve(hight='5ft 6in'): ValueError('keyword arg "hight" is an invalid fieldname')
                           raised as expected
Run Code Online (Sandbox Code Playgroud)