2010-05-30 116 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