Python 可以执行“选择不同的X,其中W=W,Y=Y,Z=Z和…”类型查找的数据结构
我有一套价值10万英镑的独特矢量。我需要,对于任何选择的列,提取在该列中看到的值集,在所有其他列都给定值的行中 我希望有一个解决方案,它是次线性的wrt项目计数在时间和最多线性wrt的所有项目在空间中的总大小,最好是次线性的额外空间,而不仅仅是存储项目 我能得到那个还是更好 顺便说一句:它将从python访问,并且需要易于编程或成为现有常用库的一部分 编辑:成本用于查找,不包括构建结构的时间。在进行第一次查询之前,所有将被索引的数据都是可用的 看来我在描述我要找的东西方面做得不好,所以这里有一个解决方案:Python 可以执行“选择不同的X,其中W=W,Y=Y,Z=Z和…”类型查找的数据结构,python,data-structures,Python,Data Structures,我有一套价值10万英镑的独特矢量。我需要,对于任何选择的列,提取在该列中看到的值集,在所有其他列都给定值的行中 我希望有一个解决方案,它是次线性的wrt项目计数在时间和最多线性wrt的所有项目在空间中的总大小,最好是次线性的额外空间,而不仅仅是存储项目 我能得到那个还是更好 顺便说一句:它将从python访问,并且需要易于编程或成为现有常用库的一部分 编辑:成本用于查找,不包括构建结构的时间。在进行第一次查询之前,所有将被索引的数据都是可用的 看来我在描述我要找的东西方面做得不好,所以这里有一个
class Index:
dep __init__(self, stuff): # don't care about this O() time
self.all = set(stuff)
self.index = {}
for item in stuff:
for i,v in item:
self.index.getdefault(i,set()).add(v)
def Get(self, col, have): # this O() matters
ret = []
t = array(have) # make a copy.
for i in self.index[col]:
t[col] = i
if t in self.all:
ret.append(i)
return ret
问题是,这在最坏情况下的性能非常糟糕。既然您要求的是类似SQL的查询,那么使用关系数据库如何?SQLite是标准库的一部分,可以在磁盘上使用,也可以完全在内存中使用。既然您要求的是类似SQL的查询,那么使用关系数据库如何?SQLite是标准库的一部分,可以在磁盘上使用,也可以完全在内存中使用。假设您有一个包含字段x、y和z的“tuple”类,并且有一组这样的元组保存在名为myTuples的枚举变量中。然后: A人口前:
dct = {}
for tpl in myTuples:
tmp = (tpl.y, tpl.z)
if tmp in dct:
dct[tmp].append(tpl.x)
else:
dct[tmp] = [tpl.x]
B查询:
def findAll(y,z):
tmp = (y,z)
if tmp not in dct: return ()
return [(x,y,z) for x in dct[tmp]]
我确信有一种方法可以优化代码的可读性,节省一些周期,等等。但实际上,您希望使用2元组作为键预先填充dict。如果我没有看到sub-linear的请求,那么我不会想到这一点:
A对不起,预填充是线性的。
B查询的速度应该与返回的项目数一样慢-大多数时间是次线性的,除了奇怪的边缘情况。假设您有一个包含字段x、y和z的“tuple”类,并且您有一组这样的元组保存在名为myTuples的枚举变量中。然后: A人口前:
dct = {}
for tpl in myTuples:
tmp = (tpl.y, tpl.z)
if tmp in dct:
dct[tmp].append(tpl.x)
else:
dct[tmp] = [tpl.x]
B查询:
def findAll(y,z):
tmp = (y,z)
if tmp not in dct: return ()
return [(x,y,z) for x in dct[tmp]]
我确信有一种方法可以优化代码的可读性,节省一些周期,等等。但实际上,您希望使用2元组作为键预先填充dict。如果我没有看到sub-linear的请求,那么我不会想到这一点:
A对不起,预填充是线性的。
B查询应该和返回的项目数一样慢-大多数时间是次线性的,除了奇怪的边缘情况。如果您有一个Python集“无顺序”,那么在不至少查看所有项目的情况下,无法选择所有相关的项目-因此,任何解决方案都不可能是次线性的,即根据您的需要调整项目数 如果你有一个列表,而不是一个集合,那么它可以被排序——但这不能在线性时间内实现,在一般情况下,在log N上进行排序是可以证明的,对于一般情况下的排序,你能做的最好的事情——并且构建排序的索引将是类似的——除非有一些约束允许你使用类似桶排序的方法。您可以分散在数据结构中所有插入项上保持索引所需的时间,但这不会减少构建此类索引所需的总时间,正如我所说的,只是分散它们 基于散列的未排序索引可以更快地用于您的特殊情况,您只需要按相等进行选择,不少于&c-但构建此类索引的时间在项目数量上是线性的,显然,如果不至少查看每个项目一次,就无法构建此类索引-次线性时间需要一些魔法,让您完全忽略某些项目,如果没有像分类这样的支持结构,这是不可能实现的,因为分类需要时间来实现,虽然可以提前增量实现,但这种方法不会减少所需的总时间 因此,不折不扣地说,您的需求似乎约束过度:Python、任何其他语言、任何数据库引擎等都不可能实现它们——如果按照您所说的字面意思进行解释的话。如果提前完成的增量工作不被视为违反线性和次线性要求,如果您采取预期/典型而非最坏情况的行为,并且您的项目具有友好的概率分布等,那么可能接近实现您非常苛刻的要求
例如,考虑对每个向量的D维进行维护,将一个项目在该维度中的值映射成一组这样的项目的索引;然后,通过设置交点,选择每个维度都满足D-1相等要求的项目,但第i个项目可以完成。这符合你的要求吗?正如我在上面解释的那样,并不是严格按照字面意思来理解后者,而是可能,取决于每项要求可以接受多少 一种更放松的感觉
顺便说一句,我不明白group by在这里意味着什么,因为每个组中的所有向量都是相同的,因为您说除一个维度外的所有维度都是由相等指定的,所以可能是您在SQL等价物中跳过了一个计数*,也就是说,您需要一个在第I维中有多少这样的向量具有给定值的计数。在这种情况下,可以通过上述方法实现 编辑:由于OP在评论和对其Q的编辑中有所澄清,我可以提出更详细的方法:import collections
class Searchable(object):
def __init__(self, toindex=None):
self.toindex = toindex
self.data = []
self.indices = None
def makeindices(self):
if self.indices is not None:
return
self.indices = dict((i, collections.defaultdict(set))
for i in self.toindex)
def add(self, record):
if self.toindex is None:
self.toindex = range(len(record))
self.makeindices()
where = len(self.data)
self.data.append(record)
for i in self.toindex:
self.indices[i][record[i]].add(where)
def get(self, indices_and_values, indices_to_get):
ok = set(range(len(self.data)))
for i, v in indices_and_values:
ok.intersection_update(self.indices[i][v])
result = set()
for rec in (self.data[i] for i in ok):
t = tuple(rec[i] for i in indices_to_get)
result.add(t)
return result
def main():
c = Searchable()
for r in ((1,2,3), (1,2,4), (1,5,4)):
c.add(r)
print c.get([(0,1),(1,2)], [2])
main()
这张照片
set([(3,), (4,)])
当然,可以很容易地专门化,以其他格式返回结果,接受索引到索引和/或以不同方式返回,等等。我相信它满足编辑/澄清的要求,因为对于每个索引维度/值,额外存储是一组索引,在该维度上出现所述值,搜索时间是每个索引维度的一个集合交集加上一个返回项目数的循环。如果您有一个Python集合无顺序,那么在不至少查看所有项目的情况下,无法选择所有相关项目-因此,任何解决方案都不可能根据需要对项目数进行次线性调整 如果你有一个列表,而不是一个集合,那么它可以被排序——但这不能在线性时间内实现,在一般情况下,在log N上进行排序是可以证明的,对于一般情况下的排序,你能做的最好的事情——并且构建排序的索引将是类似的——除非有一些约束允许你使用类似桶排序的方法。您可以分散在数据结构中所有插入项上保持索引所需的时间,但这不会减少构建此类索引所需的总时间,正如我所说的,只是分散它们 基于散列的未排序索引可以更快地用于您的特殊情况,您只需要按相等进行选择,不少于&c-但构建此类索引的时间在项目数量上是线性的,显然,如果不至少查看每个项目一次,就无法构建此类索引-次线性时间需要一些魔法,让您完全忽略某些项目,如果没有像分类这样的支持结构,这是不可能实现的,因为分类需要时间来实现,虽然可以提前增量实现,但这种方法不会减少所需的总时间 因此,不折不扣地说,您的需求似乎约束过度:Python、任何其他语言、任何数据库引擎等都不可能实现它们——如果按照您所说的字面意思进行解释的话。如果提前完成的增量工作不被视为违反线性和次线性要求,如果您采取预期/典型而非最坏情况的行为,并且您的项目具有友好的概率分布等,那么可能接近实现您非常苛刻的要求
例如,考虑对每个向量的D维进行维护,将一个项目在该维度中的值映射成一组这样的项目的索引;然后,通过设置交点,选择每个维度都满足D-1相等要求的项目,但第i个项目可以完成。这符合你的要求吗?正如我在上面解释的那样,并不是严格按照字面来理解后者,而是根据每项要求在多大程度上可以更轻松地理解而定
顺便说一句,我不明白group by在这里意味着什么,因为每个组中的所有向量都是相同的,因为您说除一个维度外的所有维度都是由相等指定的,所以可能是您在SQL等价物中跳过了一个计数*,也就是说,您需要一个在第I维中有多少这样的向量具有给定值的计数。在这种情况下,可以通过上述方法实现 编辑:由于OP在评论和对其Q的编辑中有所澄清,我可以提出更详细的方法:import collections
class Searchable(object):
def __init__(self, toindex=None):
self.toindex = toindex
self.data = []
self.indices = None
def makeindices(self):
if self.indices is not None:
return
self.indices = dict((i, collections.defaultdict(set))
for i in self.toindex)
def add(self, record):
if self.toindex is None:
self.toindex = range(len(record))
self.makeindices()
where = len(self.data)
self.data.append(record)
for i in self.toindex:
self.indices[i][record[i]].add(where)
def get(self, indices_and_values, indices_to_get):
ok = set(range(len(self.data)))
for i, v in indices_and_values:
ok.intersection_update(self.indices[i][v])
result = set()
for rec in (self.data[i] for i in ok):
t = tuple(rec[i] for i in indices_to_get)
result.add(t)
return result
def main():
c = Searchable()
for r in ((1,2,3), (1,2,4), (1,5,4)):
c.add(r)
print c.get([(0,1),(1,2)], [2])
main()
这张照片
set([(3,), (4,)])
当然,可以很容易地专门化,以其他格式返回结果,接受索引到索引和/或以不同方式返回,等等。我相信它满足编辑/澄清的要求,因为对于每个索引维度/值,额外存储是一组索引,在该维度上出现所述值,搜索时间是每个索引维度的一组交集加上返回项目数的循环。我假设您已经尝试过字典,您需要更灵活的方法。基本上,您需要做的是x、y和z的索引值
def build_index(vectors):
index = {x: {}, y: {}, z: {}}
for position, vector in enumerate(vectors):
if vector.x in index['x']:
index['x'][vector.x].append(position)
else:
index['x'][vector.x] = [position]
if vector.y in index['y']:
index['y'][vector.y].append(position)
else:
index['y'][vector.y] = [position]
if vector.z in index['z']:
index['z'][vector.z].append(position)
else:
index['z'][vector.z] = [position]
return index
索引查找表中的内容。例如,可以这样说,从x=42的向量中选择x、y、z:
def query_by(vectors, index, property, value):
results = []
for i in index[property][value]:
results.append(vectors[i])
vecs_x_42 = query_by(index, 'x', 42)
# now vec_x_42 is a list of all vectors where x is 42
现在要进行逻辑连接,假设从向量中选择x,y,z,其中x=42和y=3,您可以使用Python的集合来完成此操作:
def query_by(vectors, index, criteria):
sets = []
for k, v in criteria.iteritems():
if v not in index[k]:
return []
sets.append(index[k][v])
results = []
for i in set.intersection(*sets):
results.append(vectors[i])
return results
vecs_x_42_y_3 = query_by(index, {'x': 42, 'y': 3})
集合上的交集操作生成的值仅出现在两个集合中,因此您仅迭代满足这两个条件的位置
现在是问题的最后一部分,按x分组:
def group_by(vectors, property):
result = {}
for v in vectors:
value = getattr(v, property)
if value in result:
result[value].append(v)
else:
result[value] = [v]
return result
所以,让我们把这一切结合起来:
vectors = [...] # your vectors, as objects such that v.x, v.y produces the x and y values
index = build_index(vectors)
my_vectors = group_by(query_by(vectors, index, {'y':42, 'z': 3}), 'x')
# now you have in my_vectors a dictionary of vectors grouped by x value, where y=42 and z=3
更新
我更新了上面的代码并修复了一些明显的错误。它现在起作用了,它做了它声称要做的事情。在我的笔记本电脑上,一个2GHz的core 2 duo和4GB的RAM,构建索引只需要不到1秒的时间。即使数据集有100k个向量,查找也非常快。如果我有时间,我会和MySQL做一些正式的比较
你可以看到,如果你给它计时或改进它,让我知道。我想你已经试过这本字典,你需要更灵活的东西。基本上,您需要做的是x、y和z的索引值
def build_index(vectors):
index = {x: {}, y: {}, z: {}}
for position, vector in enumerate(vectors):
if vector.x in index['x']:
index['x'][vector.x].append(position)
else:
index['x'][vector.x] = [position]
if vector.y in index['y']:
index['y'][vector.y].append(position)
else:
index['y'][vector.y] = [position]
if vector.z in index['z']:
index['z'][vector.z].append(position)
else:
index['z'][vector.z] = [position]
return index
索引查找表中的内容。例如,可以这样说,从x=42的向量中选择x、y、z:
def query_by(vectors, index, property, value):
results = []
for i in index[property][value]:
results.append(vectors[i])
vecs_x_42 = query_by(index, 'x', 42)
# now vec_x_42 is a list of all vectors where x is 42
现在要进行逻辑连接,假设从向量中选择x,y,z,其中x=42和y=3,您可以使用Python的集合来完成此操作:
def query_by(vectors, index, criteria):
sets = []
for k, v in criteria.iteritems():
if v not in index[k]:
return []
sets.append(index[k][v])
results = []
for i in set.intersection(*sets):
results.append(vectors[i])
return results
vecs_x_42_y_3 = query_by(index, {'x': 42, 'y': 3})
集合上的交集操作生成的值仅出现在两个集合中,因此您仅迭代满足这两个条件的位置
现在是问题的最后一部分,按x分组:
def group_by(vectors, property):
result = {}
for v in vectors:
value = getattr(v, property)
if value in result:
result[value].append(v)
else:
result[value] = [v]
return result
所以,让我们把这一切结合起来:
vectors = [...] # your vectors, as objects such that v.x, v.y produces the x and y values
index = build_index(vectors)
my_vectors = group_by(query_by(vectors, index, {'y':42, 'z': 3}), 'x')
# now you have in my_vectors a dictionary of vectors grouped by x value, where y=42 and z=3
更新
我更新了上面的代码并修复了一些明显的错误。它现在起作用了,它做了它声称要做的事情。在我的笔记本电脑上,一个2GHz的core 2 duo和4GB的RAM,构建索引只需要不到1秒的时间。即使数据集有100k个向量,查找也非常快。如果我有时间,我会和MySQL做一些正式的比较
你可以看到,如果你计时或改进它,让我知道。那么你有3个坐标和一个向量x,y,z的开始和结束值 如何知道这七个已知值?是否有许多坐标三元组多次出现 考虑到10K的数据量很小,您必须对该函数进行非常紧密的循环,以节省查找时间
你能给出你发布的类的实际输入的例子吗?所以你有3个坐标和一个向量x,y,z的起点和终点值 如何知道这七个已知值?是否有许多坐标三元组多次出现 考虑到10K的数据量很小,您必须对该函数进行非常紧密的循环,以节省查找时间
你能给出你发布的类的真实输入的例子吗?你能给出一些示例数据和你期望的结果吗?将它们存储在一个字典中,如:{y,z:[x]}。然后将进行O1查找以查找所有X。填充将需要一段线性时间。我的答案来了。我想他是说他需要能够通过任意一组列进行查询,并在剩余的列中生成值。我的答案是否定的吗?如果值必须精确,那么您想要的很容易。如果你想搜索一系列的y和Z值,这很有趣,但是也有人问了一个不同的问题。你能给出一些示例数据和你期望的结果吗?将它们存储在字典中,如:{y,Z:[x]}。然后将进行O1查找以查找所有X。填充将需要一段线性时间。我的答案来了。我想他是说他需要能够通过任意一组列进行查询,并在剩余的列中生成值。我的答案是否定的吗?如果值必须精确,那么您想要的很容易。如果你想搜索一系列的y和Z值,这很有趣,但也有人提出了一个不同的问题。我支持亚线性怀疑论。我猜测了询问者想要什么。第三,我也忽略了约束,并给出了Python的最佳尝试。构建结构的时间不是约束。请参见编辑。分组依据应消除重复项。我的SQL有点生疏了。@BCS,就像SQL刷新器一样:选择不同的X是消除重复的最简单的方法。我支持亚线性怀疑论。我猜测了询问者想要什么。第三,我也忽略了约束,并给出了Python的最佳尝试。构建结构的时间不是约束。请参见编辑。分组依据应消除重复项。我的SQL有点生疏了。@BCS,就像SQL刷新器一样:选择不同的X是消除重复的最简单的方法。有趣的东西。。。想计时吗?哈,老实说,我从来没有用过这样的东西。我会使用RDBMS,因为问题的本质是。。。关系型。如果我有时间,我将生成一个测试数据集,并将其与MySQL或其他东西进行比较。。。想计时吗?哈,老实说,我从来没有用过这样的东西。我会使用RDBMS,因为问题的本质是。。。关系型。如果我有时间,我将生成一个测试数据集,并将其与MySQL或其他东西进行比较。这是个好主意,但考虑到我处理的是这样一个受约束的常规情况,这似乎有些过头了。此外,我还需要创建一大堆索引来快速创建一个索引。BCS:你可以用锤子做很多事情,但这并不意味着用锤子敲钉子是过分的
好主意,但考虑到我处理的是这样一个受约束的常规案件,这似乎有些过头了。此外,我还需要创建一大堆索引来快速创建一个索引。BCS:你可以用锤子做很多事情,但这并不意味着用锤子敲钉子太过分了。那会管用,但我需要8个这样的DIC,每个键有7个元组。在这一点上,我可能会预先计算所有情况下的答案,并得到O1查找。这会起作用,但我需要8个这样的DIC,每个键有7个元组。在这一点上,我可能会预先计算所有情况下的答案,并获得O1查找。我故意没有指定元组的长度,因为我需要处理多个长度,如果需要知道的话,它们明显比3长,一种情况是8,但依赖于此的解决方案将明显低于理想。我还需要能够查找任何一列,而不仅仅是一个固定列。至于实际投入;不。但是想想3到30个小整数的向量,你应该足够接近了。我故意没有指定元组的长度,因为我需要处理的长度不止一个,如果你需要知道的话,它们明显比3长,一种情况是8,但依赖于它的解将远远不理想。我还需要能够查找任何一列,而不仅仅是一个固定列。至于实际投入;不,但是想想3到30个小整数的向量,你应该足够接近了。