2010-05-30 118 views
2

我有一个程序可以对大量实验数据进行操作。数据被存储为与以下属性的类的实例的对象的列表:Python:提高累计总和

  • time_point - 节点的集群从该样品的名称 - 样品
  • 簇的时间采取
  • 节点 - 从该取样
  • qty1的节点的名称=样品的用于第一数量
  • qty2值=样品的用于第二量

我需要从数据集中派生一些值,分为三种方式 - 一次是整个样本,一次是每个节点群,一次是每个节点。我需要推导的值取决于qty1和qty2的(时间排序的)累计和:qty1和qty2的累积和的元素方式总和的最大值,出现该最大值的时间点以及在那个时间点qty1和qty2的值。

我想出了以下解决方案:

dataset.sort(key=operator.attrgetter('time_point')) 

# For the whole set 
sys_qty1 = 0 
sys_qty2 = 0 
sys_combo = 0 
sys_max = 0 

# For the cluster grouping 
cluster_qty1 = defaultdict(int) 
cluster_qty2 = defaultdict(int) 
cluster_combo = defaultdict(int) 
cluster_max = defaultdict(int) 
cluster_peak = defaultdict(int) 

# For the node grouping 
node_qty1 = defaultdict(int) 
node_qty2 = defaultdict(int) 
node_combo = defaultdict(int) 
node_max = defaultdict(int) 
node_peak = defaultdict(int) 

for t in dataset: 
    # For the whole system ###################################################### 
    sys_qty1 += t.qty1 
    sys_qty2 += t.qty2 
    sys_combo = sys_qty1 + sys_qty2 
    if sys_combo > sys_max: 
    sys_max = sys_combo 
    # The Peak class is to record the time point and the cumulative quantities 
    system_peak = Peak(time_point=t.time_point, 
         qty1=sys_qty1, 
         qty2=sys_qty2) 
    # For the cluster grouping ################################################## 
    cluster_qty1[t.cluster] += t.qty1 
    cluster_qty2[t.cluster] += t.qty2 
    cluster_combo[t.cluster] = cluster_qty1[t.cluster] + cluster_qty2[t.cluster] 
    if cluster_combo[t.cluster] > cluster_max[t.cluster]: 
    cluster_max[t.cluster] = cluster_combo[t.cluster] 
    cluster_peak[t.cluster] = Peak(time_point=t.time_point, 
            qty1=cluster_qty1[t.cluster], 
            qty2=cluster_qty2[t.cluster]) 
    # For the node grouping ##################################################### 
    node_qty1[t.node] += t.qty1 
    node_qty2[t.node] += t.qty2 
    node_combo[t.node] = node_qty1[t.node] + node_qty2[t.node] 
    if node_combo[t.node] > node_max[t.node]: 
    node_max[t.node] = node_combo[t.node] 
    node_peak[t.node] = Peak(time_point=t.time_point, 
          qty1=node_qty1[t.node], 
          qty2=node_qty2[t.node]) 

这将产生正确的输出,但我不知道它是否可以变得更可读/ Python化,和/或更快/更可扩展性。

上面是有吸引力的,它只能通过(大)数据集循环一次,但没有吸引力,因为我基本上复制/粘贴了相同算法的三个副本。

为了避免上述的复制/粘贴问题,我想这也:

def find_peaks(level, dataset): 

    def grouping(object, attr_name): 
    if attr_name == 'system': 
     return attr_name 
    else: 
     return object.__dict__[attrname] 

    cuml_qty1 = defaultdict(int) 
    cuml_qty2 = defaultdict(int) 
    cuml_combo = defaultdict(int) 
    level_max = defaultdict(int) 
    level_peak = defaultdict(int) 

    for t in dataset: 
    cuml_qty1[grouping(t, level)] += t.qty1 
    cuml_qty2[grouping(t, level)] += t.qty2 
    cuml_combo[grouping(t, level)] = (cuml_qty1[grouping(t, level)] + 
             cuml_qty2[grouping(t, level)]) 
    if cuml_combo[grouping(t, level)] > level_max[grouping(t, level)]: 
     level_max[grouping(t, level)] = cuml_combo[grouping(t, level)] 
     level_peak[grouping(t, level)] = Peak(time_point=t.time_point, 
              qty1=node_qty1[grouping(t, level)], 
              qty2=node_qty2[grouping(t, level)]) 
    return level_peak 

system_peak = find_peaks('system', dataset) 
cluster_peak = find_peaks('cluster', dataset) 
node_peak = find_peaks('node', dataset) 

对于(不分组),系统级的计算,我也想到了这一点,这是相当:

dataset.sort(key=operator.attrgetter('time_point')) 

def cuml_sum(seq): 
    rseq = [] 
    t = 0 
    for i in seq: 
    t += i 
    rseq.append(t) 
    return rseq 

time_get = operator.attrgetter('time_point') 
q1_get = operator.attrgetter('qty1') 
q2_get = operator.attrgetter('qty2') 

timeline = [time_get(t) for t in dataset] 
cuml_qty1 = cuml_sum([q1_get(t) for t in dataset]) 
cuml_qty2 = cuml_sum([q2_get(t) for t in dataset]) 
cuml_combo = [q1 + q2 for q1, q2 in zip(cuml_qty1, cuml_qty2)] 

combo_max = max(cuml_combo) 
time_max = timeline.index(combo_max) 
q1_at_max = cuml_qty1.index(time_max) 
q2_at_max = cuml_qty2.index(time_max) 

然而,尽管这个版本的酷使用列表内涵和zip()的,它遍历数据集只三次系统级的计算,我想不出一个好办法做群集级别和节点级别的冲突,而不需要执行如下的操作:

timeline = defaultdict(int) 
cuml_qty1 = defaultdict(int) 
#...etc. 

for c in cluster_list: 
    timeline[c] = [time_get(t) for t in dataset if t.cluster == c] 
    cuml_qty1[c] = [q1_get(t) for t in dataset if t.cluster == c] 
    #...etc. 

有没有人在这里堆栈溢出有改进建议?上面的第一个片段对于我的初始数据集(大约一百万条记录)运行良好,但后面的数据集将有更多的记录和群集/节点,因此可伸缩性是一个问题。

这是我对Python的第一次非平凡使用,我想确保我正在适当地利用该语言(这取代了一组非常复杂的SQL查询,而Python版本的早期版本是基本上非常无效的直接转换)。我通常不会做很多编程,所以我可能会错过一些基本的东西。

非常感谢!

+0

您可以先执行所有节点计算,然后使用节点结果计算群集结果,然后使用群集结果计算系统范围的结果。这至少会减少你目前正在做的一些重复(相同的增加)。 – unutbu 2010-05-30 03:37:33

+0

感谢您的建议。然而,集群峰值可能与任何单个节点的峰值不同。例如,它们可能都会立即达到中等价值,从而为集群带来巨大的高峰,但对于任何单个节点而言都不是一个巨大的高峰。 – bbayles 2010-05-30 04:30:45

回答

2

这似乎是一个应用一点面向对象的经典机会。我建议将派生数据作为一个类并将累计和计算抽象为适用于该类的东西。

喜欢的东西:

class DerivedData(object): 
    def __init__(self): 
     self.qty1 = 0.0 
     self.qty2 = 0.0 
     self.combo = 0.0 
     self.max = 0.0 
     self.peak = Peak(time_point=0.0, qty1=0.0, qty2=0.0) 

    def accumulate(self, data): 
     self.qty1 += data.qty1 
     self.qty2 += data.qty2 
     self.combo = self.qty1 + self.qty2 
     if self.combo > self.max: 
      self.max = self.combo 
      self.peak = Peak(time_point=data.time_point, 
          qty1=self.qty1, 
          qty2=self.qty2) 

sys = DerivedData() 
clusters = defaultdict(DerivedData) 
nodes = defaultdict(DerivedData) 

dataset.sort(key=operator.attrgetter('time_point')) 

for t in dataset: 
    sys.accumulate(t) 
    clusters[t.cluster].accumulate(t) 
    nodes[t.node].accumulate(t) 

该解决方案从抽象的逻辑寻找峰值,但仍然只能通过数据设置一次去。

+0

彼得,非常感谢。这当然看起来好多了。我会试试看看它是如何做到的。 我应该提到,所有的时间和数据值保证是整数(事实上,每个级别的每个数量的总和等于零)。 – bbayles 2010-05-30 04:46:33