提高此 python 函数的性能
Improve performance of this python function
def _partition_by_context(self, labels, contexts):
# partition the labels by context
assert len(labels) == len(contexts)
by_context = collections.defaultdict(list)
for i, label in enumerate(labels):
by_context[contexts[i]].append(label)
# now remove any that don't have enough samples
keys_to_remove = []
for key, value in by_context.iteritems():
if len(value) < self._min_samples_context:
keys_to_remove.append(key)
for key in keys_to_remove:
del by_context[key]
return by_context
- labels 是一个 numpy 的浮点数数组。
- contexts 是一个 python 元组列表。每个元组的形式为
(unicode, int)
: example (u'ffcd6881167b47d492adf3f542af94c6', 2)
。上下文值经常重复。例如,上下文列表中可能有 10000 个值,但只有 100 个不同的值。
len(labels) == len(contexts)
为真,如第一行所断言
- 索引 i 处的标签与索引 i 处的上下文关联。即
labels[i]
和 contexts[i]
"go together"
此函数的要点是按上下文值对标签中的值进行分区。然后在最后,如果标签计数太低,则删除字典条目。
因此,如果所有上下文值都相同,return值将是一个包含单个条目的字典,键=上下文,值=所有标签的列表。
如果有 N 个不同的上下文值,return 值将有 N 个键(每个上下文一个),每个键的值将是与特定上下文关联的标签列表。列表中标签的顺序并不重要。
此函数使用不同的参数被调用了数百万次。我确定这是使用 gprof2dot 的瓶颈。大部分成本都在第一个 for 循环中的列表 append() 调用中。
谢谢!
尝试替换
for i, label in enumerate(labels):
by_context[contexts[i]].append(label)
与
for context, label in zip(contexts, labels):
by_context[context].append(label)
而不是使用 keys_to_remove
,尝试
n = self._min_samples_context
return {c:ls for c,ls in by_context.items() if len(ls) >= n}
看起来像这两个数组会成为一个很好的测试用例:
N = 100
labels=np.arange(N)
contexts=np.random.randint(0,len(labels)/10,len(labels))
使用这些数组,@Hugh 的改进速度提高了大约 10%。
我在其他问题上的经验表明 defaultdict
是一种收集此类值的好方法。唯一可能更快的方法是将其转换为某种 numpy
索引问题。
def _partition_by_context(self, labels, contexts):
# partition the labels by context
assert len(labels) == len(contexts)
by_context = collections.defaultdict(list)
for i, label in enumerate(labels):
by_context[contexts[i]].append(label)
# now remove any that don't have enough samples
keys_to_remove = []
for key, value in by_context.iteritems():
if len(value) < self._min_samples_context:
keys_to_remove.append(key)
for key in keys_to_remove:
del by_context[key]
return by_context
- labels 是一个 numpy 的浮点数数组。
- contexts 是一个 python 元组列表。每个元组的形式为
(unicode, int)
: example(u'ffcd6881167b47d492adf3f542af94c6', 2)
。上下文值经常重复。例如,上下文列表中可能有 10000 个值,但只有 100 个不同的值。 len(labels) == len(contexts)
为真,如第一行所断言- 索引 i 处的标签与索引 i 处的上下文关联。即
labels[i]
和contexts[i]
"go together"
此函数的要点是按上下文值对标签中的值进行分区。然后在最后,如果标签计数太低,则删除字典条目。
因此,如果所有上下文值都相同,return值将是一个包含单个条目的字典,键=上下文,值=所有标签的列表。
如果有 N 个不同的上下文值,return 值将有 N 个键(每个上下文一个),每个键的值将是与特定上下文关联的标签列表。列表中标签的顺序并不重要。
此函数使用不同的参数被调用了数百万次。我确定这是使用 gprof2dot 的瓶颈。大部分成本都在第一个 for 循环中的列表 append() 调用中。
谢谢!
尝试替换
for i, label in enumerate(labels):
by_context[contexts[i]].append(label)
与
for context, label in zip(contexts, labels):
by_context[context].append(label)
而不是使用 keys_to_remove
,尝试
n = self._min_samples_context
return {c:ls for c,ls in by_context.items() if len(ls) >= n}
看起来像这两个数组会成为一个很好的测试用例:
N = 100
labels=np.arange(N)
contexts=np.random.randint(0,len(labels)/10,len(labels))
使用这些数组,@Hugh 的改进速度提高了大约 10%。
我在其他问题上的经验表明 defaultdict
是一种收集此类值的好方法。唯一可能更快的方法是将其转换为某种 numpy
索引问题。