我用Python编写了一个程序,它花费大量时间查找对象属性和字典键值。我想知道是否有任何方法可以优化这些查找时间,可能通过C扩展来减少执行时间,或者是否需要在已编译的语言中重新实现该程序。
该程序使用图形实现一些算法。在我们的数据集上运行速度非常慢,因此我使用一个可完成的缩小数据集使用
第202行的生成器表达式为:
这个上下文函数的源代码如下。
这些查找中的每一个都应该是摊销常数时间(即 O(1)),然而,我仍然为这些查找支付了很大的代价。是否有某种方法可以加速这些查找(例如,通过将部分内容编写成 C 扩展),或者我需要将程序移动到编译语言中?
以下是提供上下文的函数的完整源代码;绝大部分执行时间都在这个函数内。
该程序使用图形实现一些算法。在我们的数据集上运行速度非常慢,因此我使用一个可完成的缩小数据集使用
cProfile
对代码进行了剖析。绝大部分时间都在一个函数中消耗,并且特别是两个语句——函数内的生成器表达式。第202行的生成器表达式为:
neighbors_in_selected_nodes = (neighbor for neighbor in
node_neighbors if neighbor in selected_nodes)
而在第204行的生成表达式是
neighbor_z_scores = (interaction_graph.node[neighbor]['weight'] for
neighbor in neighbors_in_selected_nodes)
这个上下文函数的源代码如下。
selected_nodes
是 interaction_graph
的 set
类型节点,它是一个 NetworkX Graph
实例。 node_neighbors
是来自 Graph.neighbors_iter()
的迭代器。
Graph
本身使用字典来存储节点和边。它的 Graph.node
属性是一个字典,其中每个节点都有属于其自己的字典存储节点和它们的属性(例如,'weight'
)。这些查找中的每一个都应该是摊销常数时间(即 O(1)),然而,我仍然为这些查找支付了很大的代价。是否有某种方法可以加速这些查找(例如,通过将部分内容编写成 C 扩展),或者我需要将程序移动到编译语言中?
以下是提供上下文的函数的完整源代码;绝大部分执行时间都在这个函数内。
def calculate_node_z_prime(
node,
interaction_graph,
selected_nodes
):
"""Calculates a z'-score for a given node.
The z'-score is based on the z-scores (weights) of the neighbors of
the given node, and proportional to the z-score (weight) of the
given node. Specifically, we find the maximum z-score of all
neighbors of the given node that are also members of the given set
of selected nodes, multiply this z-score by the z-score of the given
node, and return this value as the z'-score for the given node.
If the given node has no neighbors in the interaction graph, the
z'-score is defined as zero.
Returns the z'-score as zero or a positive floating point value.
:Parameters:
- `node`: the node for which to compute the z-prime score
- `interaction_graph`: graph containing the gene-gene or gene
product-gene product interactions
- `selected_nodes`: a `set` of nodes fitting some criterion of
interest (e.g., annotated with a term of interest)
"""
node_neighbors = interaction_graph.neighbors_iter(node)
neighbors_in_selected_nodes = (neighbor for neighbor in
node_neighbors if neighbor in selected_nodes)
neighbor_z_scores = (interaction_graph.node[neighbor]['weight'] for
neighbor in neighbors_in_selected_nodes)
try:
max_z_score = max(neighbor_z_scores)
# max() throws a ValueError if its argument has no elements; in this
# case, we need to set the max_z_score to zero
except ValueError, e:
# Check to make certain max() raised this error
if 'max()' in e.args[0]:
max_z_score = 0
else:
raise e
z_prime = interaction_graph.node[node]['weight'] * max_z_score
return z_prime
这是根据cProfiler排序后的前几个调用,按时间排序。
ncalls tottime percall cumtime percall filename:lineno(function)
156067701 352.313 0.000 642.072 0.000 bpln_contextual.py:204(<genexpr>)
156067701 289.759 0.000 289.759 0.000 bpln_contextual.py:202(<genexpr>)
13963893 174.047 0.000 816.119 0.000 {max}
13963885 69.804 0.000 936.754 0.000 bpln_contextual.py:171(calculate_node_z_prime)
7116883 61.982 0.000 61.982 0.000 {method 'update' of 'set' objects}
neighbors_in_selected_nodes
和neighbor_z_scores
?为什么不只用一个循环呢?这个两步骤的公式似乎没有引入任何新的东西。为什么要这样做呢?你能否更新问题,解释为什么要使用两个推导而不是一个? - S.Lottneighbors_in_selected_nodes
过滤已选择节点;这个过程链接到neighbor_z_scores
以检索权重。链接迭代器应将它们变成一个循环,而不是两个循环;该循环在max()
内进行评估。 - gotgenesif neighbor in selected_nodes:
,第二个表达式表示neighbor_z_scores.append(interaction_graph.node[neighbor]['weight'])
。通过使用生成器表达式,我避免了列表的创建和附加操作。从获取邻居到评估max(neighbor_z_scores)
,我完全处理迭代器链。 - gotgenes