使嵌套循环运行得更快,例如通过Python中的矢量化
我有一个2*N整数数组使嵌套循环运行得更快,例如通过Python中的矢量化,python,performance,vectorization,Python,Performance,Vectorization,我有一个2*N整数数组ids表示间隔,其中N大约是一百万。看起来像这样 0 2 1 ... 3 4 3 ... 数组中的整数可以是0,1,M-1,其中M避开for循环,仅为一个样本 将numpy导入为np 作为pd进口熊猫 df=pd.DataFrame({ “A”:np.random.randint(0,100100000), “B”:np.random.randint(0,100100000) }) df.groupby(“B”)[“A”].agg(列表) 您可以尝试下面的选项,在该选
ids
表示间隔,其中N大约是一百万。看起来像这样
0 2 1 ...
3 4 3 ...
数组中的整数可以是0,1,M-1,其中M避开for循环,仅为一个样本
将numpy导入为np
作为pd进口熊猫
df=pd.DataFrame({
“A”:np.random.randint(0,100100000),
“B”:np.random.randint(0,100100000)
})
df.groupby(“B”)[“A”].agg(列表)
您可以尝试下面的选项,在该选项中,您的外部循环隐藏在numpy的C语言实现的沿轴应用()。不确定性能优势,只有适当规模的测试才能说明(特别是在将列表转换为numpy数组时会涉及一些初始开销):
输出:
{0:array('I',[0]),1:array('I',[0,2]),2:array('I',[0,1,2]),
3:数组('I',[1])}
编辑:
我觉得在这里使用字典可能不是个好主意。我怀疑在这个特定的上下文中,字典索引实际上可能比numpy
数组索引慢。使用以下行创建并初始化inv
作为Python数组的numpy
数组。代码的其余部分可以保持原样:
inv_len = np.max(ids_arr)
inv = np.empty(shape=(inv_len,), dtype=array.array)
for i in range(inv_len):
inv[i] = array.array('I')
(注意:这假设您的应用程序没有在inv
上执行dict
-特定的操作,例如inv.items()
或inv.keys()
。但是,如果是这种情况,您可能需要额外的步骤将numpy数组转换为dict
),因为N
的顺序很大,我想出了一个看似实用的方法;如果有任何缺陷,请告诉我
对于第i个间隔[x,y]
,将其存储为[x,y,i]
。根据阵列的开始和结束时间对阵列进行排序。这应该需要O(NlogN)时间
创建频率阵列freq[2*N+1]
。对于每个间隔,使用的概念更新频率。在O(N)中生成频率
根据您的数据确定阈值。根据该值,可以将元素指定为稀疏或频繁。对于稀疏元素,不执行任何操作。仅对于频繁元素,存储它们发生的间隔
在查找过程中,如果存在频繁元素,则可以直接访问预先计算的列表。如果元素是稀疏元素,则可以在O(logN)时间内搜索间隔,因为在步骤1中对间隔进行了排序,并附加了索引
这对我来说似乎是一个实用的方法,其余的取决于你的使用。如每个查询所需的摊销时间复杂度等。如果有一个列表,其中每个间隔为(0,2*N-1),并且有N个这样的间隔,那么复杂度将为O(N^2)。如何进一步降低复杂性?这似乎是最糟糕的情况。不知道如何解决这个O(N*2)问题。考虑到N大约是一百万,O(N*2)确实是一个大问题。为什么需要这个逆映射呢?这应该决定我们是否可以在这个问题上解决一些问题case@AbhinavMathur我需要把这个逆映射作为一种查找表。计算映射处于预处理步骤中,但不应太慢。这回答了你的问题吗?@AbhinavMathur。反向查找将允许我以后的应用程序快速知道哪些间隔将包含特定点。通过“直觉”部分的例子,我需要知道哪一个区间覆盖了一个点,比如说2。这可以称为区间点问题:给定一组区间和一个点,知道哪些区间包含该点。我本可以使用范围树或分段树来实现这一点,但我希望建立一个查找表,以便以后能够非常快速地进行查找。谢谢你的提问。这是OP需要的吗?谢谢。这将如何替换我问题中的嵌套循环?首先,将原始数据转换为数据=[[0,0],[0,1],[0,2],…]之类的对列表;然后使df=pd.DataFrame(数据,列=[“A”,“B”])df.groupby(“B”)[“A”])agg(列表)谢谢。这种方法很优雅,但似乎存在内存问题。我有大约一百万个条目,每个条目都有一个范围[a,b],其中b-a大约是3000。通过你的方法,我得到了3*10^9个条目。在上面的示例中,我尝试了使用“10**9”,其中使用了100000,但由于内存不足(在16G笔记本电脑上),代码在运行几分钟后被删除。我们可以像你建议的那样使用h5py吗?谢谢!将列表转换为numpy数组的初始开销没有问题,因为它是作为numpy数组提供的。@zell-不客气。我刚刚在回答中添加了一个编辑,建议将inv
作为Python数组的numpy
数组,而不是作为Python数组的字典。谢谢!早些时候,我使用的是列表的numpy数组,但是有些人应该避免使用列表的numpy数组。看见你认为python数组的numpy数组有意义吗?尽管列表的numpy数组不符合链接要求?@zell-很高兴有了一些改进。你是坚持使用字典,还是用numpy数组替换它?还有一点需要注意:当您比较解决方案以选择更好的解决方案时,我希望您在备选解决方案中使用完全相同的数据——不仅是在id
的大小方面,而且在其内容方面)。这是因为最终构造的inv
的大小取决于ids
的内容和长度,出于同样的原因,您可能还想通过改变ids
的内容来重复测试,然后才能真正确定您的最终选择
0 -> 0, 1, 2
1 -> 2, 3
2 -> 1, 2
0 -> 0
1 -> 0, 2
2 -> 0, 1, 2
3 -> 1
for i in range(ids.shape[1]):
for j in range(ids[0][i], ids[1][i]):
inv[j].append(i)
for i in range(M): inv[i]=array.array('I')
import numpy as np
import array
ids = [[0,2,1],[3,4,3]]
ids_arr = np.array(ids) # Convert to numpy array. Expensive operation?
range_index = 0 # Initialize. To be bumped up by each invocation of my_func()
inv = {}
for i in range(np.max(ids_arr)):
inv[i] = array.array('I')
def my_func(my_slice):
global range_index
for i in range(my_slice[0], my_slice[1]):
inv[i].append(range_index)
range_index += 1
np.apply_along_axis (my_func,0,ids_arr)
print (inv)
inv_len = np.max(ids_arr)
inv = np.empty(shape=(inv_len,), dtype=array.array)
for i in range(inv_len):
inv[i] = array.array('I')