Python - high disk usage in SumTree - python

I've encountered some weird behaviour of my python program. Basically when I tried to create adn fill a SumTree of length larger than 1000, my disk usage increases a lot to ~300MB/s then the programme died.
I'm pretty sure there's no file r/w involved in this process, and the problem is with the add function. The code is shown below.
import numpy as np
class SumTree():
trans_idx = 0
def __init__(self, capacity):
self.num_samples = 0
self.capacity = capacity
self.tree = np.zeros(2 * capacity - 1)
self.transitions = np.empty(self.capacity, dtype=object)
def add(self, p, experience):
tree_idx = self.trans_idx + self.capacity - 1
self.transitions[self.trans_idx] = experience
self.transitions.append(experience)
self.update(tree_idx, p)
self.trans_idx += 1
if self.trans_idx >= self.capacity:
self.trans_idx = 0
self.num_samples = min(self.num_samples + 1, self.capacity)
def update(self, tree_idx, p):
diff = p - self.tree[tree_idx]
self.tree[tree_idx] = p
while tree_idx != 0:
tree_idx = (tree_idx - 1) // 2
self.tree[tree_idx] += diff
def get_leaf(self, value):
parent_idx = 0
while True:
childleft_idx = 2 * parent_idx + 1
childright_idx = childleft_idx + 1
if childleft_idx >= len(self.tree):
leaf_idx = parent_idx
break
else:
if value <= self.tree[childleft_idx]:
parent_idx = childleft_idx
else:
value -= self.tree[childleft_idx]
parent_idx = childright_idx
data_idx = leaf_idx - self.capacity + 1
return leaf_idx, self.tree[leaf_idx], self.transitions[data_idx]
#property
def total_p(self):
return self.tree[0] # the root
#property
def volume(self):
return self.num_samples # number of transistions stored
Here's an example where this SumTree object will be used:
def add(self, experience)
max_p = np.max(self.tree.tree[-self.tree.capacity:])
if max_p == 0:
max_p = 1.0
exp = self.Experience(*experience)
self.tree.add(max_p, exp)
where Experience is a named tuple and self.tree is a Sumtree instance, when I removed the last line the high disk usage disappears.
Can anyone help me with this?

I finally sort this out because each experience is a tuple of namedtuple and I'm creating another namedtuple Experience from it. Fixed by changing experience to a tuple of numpy arrays.

Related

Is there a way for finding the last list(not the last value) generated from Python iteration?

A = 300
Delta = 0.01
p0 = 100
ev0 = 0
dev = 0.001
dp0 = A*p0*dev
p = []
dp = []
ev = []
class Nonlinear():
def __init__(self):
self.p, self.dp, self.ev = p0, 0, ev0-dev
def __iter__(self):
return self
def __next__(self):
self.dp, self.p, self.ev = A*(self.p+self.dp)*dev, self.p + self.dp, self.ev+dev
p.append(self.p)
dp.append(self.dp)
if self.p > 1500:
raise StopIteration()
return p
for n in Nonlinear():
print(n)
The above are all the codes I use as an replacement of Excel in iteration. The results always provide me with all the iterative lists rather than the last that I need.
I wonder:
If there is a way for finding the last list in Python 3.8.
As the calculated results will be utilized to plot a figure using matplotlib, I want to get the relationship of p and ev calculated from iteration. Also it's also very important to swift the list into numpy array as well as gain the last iterative lists of p and ev including all evalues.
I use Python 3.8. If you have any ideas about solving the issue. Please let me know.
I think this does what you want:
class Nonlinear():
def __init__(self, p0 = 100, dp0 = 0, A = 300, dev = 0.001, maxval = 1500):
self.p = p0
self.dp = dp0
self.A = A
self.dev = dev
self.maxval = maxval
def __iter__(self):
while self.p + self.dp <= self.maxval:
self.p += self.dp
self.dp = self.A * (self.p + self.dp) * self.dev
yield self.p
n = Nonlinear()
print(*n)
# 100 130.0 178.0 245.8 339.88 470.068 650.1448 899.21128 1243.694608
Or
n = [_ for _ in Nonlinear()]
# [100, 130.0, 178.0, 245.8, 339.88, 470.068, 650.1448, 899.21128, 1243.694608]
You could also write as a generator:
def nonlinear(p = 100, dp = 0, A = 300, dev = 0.001, maxval = 1500):
Adev = A * dev
while(p + dp <= maxval):
p += dp
dp = Adev * (p + dp)
yield p
As a note, be careful how you define your variables. Entering them line by line as I've done above gives different answers for p than having them listed in one line the way you have.

How to get python variable within class and method

I am currently creating my genetic algorithm and want to print the number of generations at the very end of the program when it finishes. However I am unsure how to access the counter variable that is the number of generations when it is outside of the class and method. So for example, at the end it would be like
Generation 100, average fit 18966, best fit 18947
Your best chromosone at generation 100
'\x06pzÂ\x8cYÆr¯n0q\x07l¿M8\x93Þ\x19\x87"\x01\x85\x1er\x89[F_VyER\x9b\x0bm=)\x9a\x9a¿¥\x10F\x12A\x84\x0fZ^\x14\x99\x8a4®\x9f¿*\\\xa0yi\x19E\x8aÇ+6(_<¾£cO~\x9c\x99\x932\x06\x0f\x82\x7f¤\x808xǸñA\x13\x0e<%\x06ÿ#í\x91Pô\x98 ®\r\x1b}\x89y¦\x0cqAK\tp\x95\x99ÔNj=Wn\x16\x94\x0cu!¯ñ\x13Qü[e8_ÂóU\x10\x1av_+%Q_¡ù\x87=\x08~ciÎ_Ï[\x8f#AëT\x14©qG\x89#Z«L\x9b¢\x94WL\x1dV¶R03\x84æ^ßr\x1fÃÈ\x1d\x8e Læª&®x\x94?TAÒD\x14£i\x82J\x15=w~\x03\x0c\xa0¾5\x02f5T\x91ol¢bIÞfk¬¡27W16(}6\x92\x87\n®xm0\x1a\n<8(à}ñ\x88̾\x17g\x9bj6\x8fI&\x12\x9aÂ\x9a_F\x1a\r[\x1dK\x15<.±DjcIy`98d>\x197Z\x91£%tIJ\x820\x93|\x07\x8dnÚ QÂ!Pf\x1d\nåòf\x91\x1d#S¾|\x9ff[d>O=T$ݶI\x9e»QÛÂ\x1d"¿U=û´F÷\x83C}wA\xa0É\x8aD\x93x»\x85\x7f\x14^\x0eL'
done:
100 generations
How do I exactly access the 100 from the method in the class?
import random
class GeneticAlgorithm(object):
def __init__(self, genetics):
self.genetics = genetics
pass
def run(self):
population = self.genetics.initial()
while True:
fits_pops = [(self.genetics.fitness(ch), ch) for ch in population]
if self.genetics.check_stop(fits_pops): break
population = self.next(fits_pops)
pass
return population
def next(self, fits):
parents_generator = self.genetics.parents(fits)
size = len(fits)
nexts = []
while len(nexts) < size:
parents = next(parents_generator)
cross = random.random() < self.genetics.probability_crossover()
children = self.genetics.crossover(parents) if cross else parents
for ch in children:
mutate = random.random() < self.genetics.probability_mutation()
nexts.append(self.genetics.mutation(ch) if mutate else ch)
pass
pass
return nexts[0:size]
pass
class GeneticFunctions(object):
def probability_crossover(self):
r"""returns rate of occur crossover(0.0-1.0)"""
return 1.0
def probability_mutation(self):
r"""returns rate of occur mutation(0.0-1.0)"""
return 0.0
def initial(self):
r"""returns list of initial population
"""
return []
def fitness(self, chromosome):
r"""returns domain fitness value of chromosome
"""
return len(chromosome)
def check_stop(self, fits_populations):
r"""stop run if returns True
- fits_populations: list of (fitness_value, chromosome)
"""
return False
def parents(self, fits_populations):
r"""generator of selected parents
"""
gen = iter(sorted(fits_populations))
while True:
f1, ch1 = next(gen)
f2, ch2 = next(gen)
yield (ch1, ch2)
pass
return
def crossover(self, parents):
r"""breed children
"""
return parents
def mutation(self, chromosome):
r"""mutate chromosome
"""
return chromosome
pass
if __name__ == "__main__":
"""
example: Mapped guess prepared Text
"""
class GuessText(GeneticFunctions):
def __init__(self, target_text,
limit=100, size=100,
prob_crossover=0.9, prob_mutation=0.2):
self.target = self.text2chromo(target_text)
self.counter = 0
self.limit = limit
self.size = size
self.prob_crossover = prob_crossover
self.prob_mutation = prob_mutation
pass
# GeneticFunctions interface impls
def probability_crossover(self):
return self.prob_crossover
def probability_mutation(self):
return self.prob_mutation
def initial(self):
return [self.random_chromo() for j in range(self.size)]
def fitness(self, chromo):
# larger is better, matched == 0
return -sum(abs(c - t) for c, t in zip(chromo, self.target))
def check_stop(self, fits_populations):
self.counter += 1
if self.counter % 100 == 0:
best_match = list(sorted(fits_populations))[-1][1]
fits = [f for f, ch in fits_populations]
best = -(max(fits))
ave = -(sum(fits) / len(fits))
print(
"Generation %3d, average fit %4d, best fit %4d" %
(self.counter, ave, best,
))
print("Your best chromosone at generation %3d" % self.counter)
print("%r" % self.chromo2text(best_match))
pass
return self.counter >= self.limit
def parents(self, fits_populations):
while True:
father = self.tournament(fits_populations)
mother = self.tournament(fits_populations)
yield (father, mother)
pass
pass
def crossover(self, parents):
father, mother = parents
index1 = random.randint(1, len(self.target) - 2)
index2 = random.randint(1, len(self.target) - 2)
if index1 > index2: index1, index2 = index2, index1
child1 = father[:index1] + mother[index1:index2] + father[index2:]
child2 = mother[:index1] + father[index1:index2] + mother[index2:]
return (child1, child2)
def mutation(self, chromosome):
index = random.randint(0, len(self.target) - 1)
vary = random.randint(-5, 5)
mutated = list(chromosome)
mutated[index] += vary
return mutated
# internals
def tournament(self, fits_populations):
alicef, alice = self.select_random(fits_populations)
bobf, bob = self.select_random(fits_populations)
return alice if alicef > bobf else bob
def select_random(self, fits_populations):
return fits_populations[random.randint(0, len(fits_populations)-1)]
def text2chromo(self, text):
return [ord(ch) for ch in text]
def chromo2text(self, chromo):
return "".join(chr(max(1, min(ch, 255))) for ch in chromo)
def random_chromo(self):
return [random.randint(1, 255) for i in range(len(self.target))]
pass
GeneticAlgorithm(GuessText("""The smartest and fastest Pixel yet.
Google Tensor: Our first custom-built processor.
The first processor designed by Google and made for Pixel, Tensor makes the new Pixel phones our most powerful yet.
The most advanced Pixel Camera ever.
Capture brilliant color and vivid detail with Pixels best-in-class computational photography and new pro-level lenses.""")).run()
print('done:')
print("%3d " 'generations' % counter)
pass
Define the GuessText first. Then access the counter.
gt = GuessText("""The smartest and fastest Pixel yet.
Google Tensor: Our first custom-built processor.
The first processor designed by Google and made for Pixel, Tensor makes the new Pixel phones our most powerful yet.
The most advanced Pixel Camera ever.
Capture brilliant color and vivid detail with Pixels best-in-class computational photography and new pro-level lenses.""")
GeneticAlgorithm(gt).run()
print('done:')
print("%3d " 'generations' % gt.counter)

How to call a module object? [duplicate]

This question already has answers here:
TypeError: 'module' object is not callable
(16 answers)
Closed 1 year ago.
I tried to run this Ant Colony algorithm code (ant_colony.py) in Python:
from threading import Thread
class ant_colony:
class ant(Thread):
def __init__(self, init_location, possible_locations, pheromone_map, distance_callback, alpha, beta, first_pass=False):
Thread.__init__(self)
self.init_location = init_location
self.possible_locations = possible_locations
self.route = []
self.distance_traveled = 0.0
self.location = init_location
self.pheromone_map = pheromone_map
self.distance_callback = distance_callback
self.alpha = alpha
self.beta = beta
self.first_pass = first_pass
self._update_route(init_location)
self.tour_complete = False
def run(self):
while self.possible_locations:
next = self._pick_path()
self._traverse(self.location, next)
self.tour_complete = True
def _pick_path(self):
if self.first_pass:
import random
return random.choice(self.possible_locations)
attractiveness = dict()
sum_total = 0.0
for possible_next_location in self.possible_locations:
pheromone_amount = float(self.pheromone_map[self.location][possible_next_location])
distance = float(self.distance_callback(self.location, possible_next_location))
attractiveness[possible_next_location] = pow(pheromone_amount, self.alpha)*pow(1/distance, self.beta)
sum_total += attractiveness[possible_next_location]
if sum_total == 0.0:
def next_up(x):
import math
import struct
if math.isnan(x) or (math.isinf(x) and x > 0):
return x
if x == 0.0:
x = 0.0
n = struct.unpack('<q', struct.pack('<d', x))[0]
if n >= 0:
n += 1
else:
n -= 1
return struct.unpack('<d', struct.pack('<q', n))[0]
for key in attractiveness:
attractiveness[key] = next_up(attractiveness[key])
sum_total = next_up(sum_total)
import random
toss = random.random()
cummulative = 0
for possible_next_location in attractiveness:
weight = (attractiveness[possible_next_location] / sum_total)
if toss <= weight + cummulative:
return possible_next_location
cummulative += weight
def _traverse(self, start, end):
self._update_route(end)
self._update_distance_traveled(start, end)
self.location = end
def _update_route(self, new):
self.route.append(new)
self.possible_locations.remove(new)
def _update_distance_traveled(self, start, end):
self.distance_traveled += float(self.distance_callback(start, end))
def get_route(self):
if self.tour_complete:
return self.route
return None
def get_distance_traveled(self):
if self.tour_complete:
return self.distance_traveled
return None
def __init__(self, nodes, distance_callback, start=None, ant_count=50, alpha=.5, beta=1.2, pheromone_evaporation_coefficient=.40, pheromone_constant=1000.0, iterations=80):
if type(nodes) is not dict:
raise TypeError("nodes must be dict")
if len(nodes) < 1:
raise ValueError("there must be at least one node in dict nodes")
self.id_to_key, self.nodes = self._init_nodes(nodes)
self.distance_matrix = self._init_matrix(len(nodes))
self.pheromone_map = self._init_matrix(len(nodes))
self.ant_updated_pheromone_map = self._init_matrix(len(nodes))
if not callable(distance_callback):
raise TypeError("distance_callback is not callable, should be method")
self.distance_callback = distance_callback
if start is None:
self.start = 0
else:
self.start = None
#init start to internal id of node id passed
for key, value in self.id_to_key.items():
if value == start:
self.start = key
#if we didn't find a key in the nodes passed in, then raise
if self.start is None:
raise KeyError("Key: " + str(start) + " not found in the nodes dict passed.")
if type(ant_count) is not int:
raise TypeError("ant_count must be int")
if ant_count < 1:
raise ValueError("ant_count must be >= 1")
self.ant_count = ant_count
if (type(alpha) is not int) and type(alpha) is not float:
raise TypeError("alpha must be int or float")
if alpha < 0:
raise ValueError("alpha must be >= 0")
self.alpha = float(alpha)
if (type(beta) is not int) and type(beta) is not float:
raise TypeError("beta must be int or float")
if beta < 1:
raise ValueError("beta must be >= 1")
self.beta = float(beta)
if (type(pheromone_evaporation_coefficient) is not int) and type(pheromone_evaporation_coefficient) is not float:
raise TypeError("pheromone_evaporation_coefficient must be int or float")
self.pheromone_evaporation_coefficient = float(pheromone_evaporation_coefficient)
#pheromone_constant
if (type(pheromone_constant) is not int) and type(pheromone_constant) is not float:
raise TypeError("pheromone_constant must be int or float")
self.pheromone_constant = float(pheromone_constant)
#iterations
if (type(iterations) is not int):
raise TypeError("iterations must be int")
if iterations < 0:
raise ValueError("iterations must be >= 0")
self.iterations = iterations
#other internal variable init
self.first_pass = True
self.ants = self._init_ants(self.start)
self.shortest_distance = None
self.shortest_path_seen = None
def _get_distance(self, start, end):
if not self.distance_matrix[start][end]:
distance = self.distance_callback(self.nodes[start], self.nodes[end])
if (type(distance) is not int) and (type(distance) is not float):
raise TypeError("distance_callback should return either int or float, saw: "+ str(type(distance)))
self.distance_matrix[start][end] = float(distance)
return distance
return self.distance_matrix[start][end]
def _init_nodes(self, nodes):
id_to_key = dict()
id_to_values = dict()
id = 0
for key in sorted(nodes.keys()):
id_to_key[id] = key
id_to_values[id] = nodes[key]
id += 1
return id_to_key, id_to_values
def _init_matrix(self, size, value=0.0):
ret = []
for row in range(size):
ret.append([float(value) for x in range(size)])
return ret
def _init_ants(self, start):
#allocate new ants on the first pass
if self.first_pass:
return [self.ant(start, self.nodes.keys(), self.pheromone_map, self._get_distance,
self.alpha, self.beta, first_pass=True) for x in range(self.ant_count)]
#else, just reset them to use on another pass
for ant in self.ants:
ant.__init__(start, self.nodes.keys(), self.pheromone_map, self._get_distance, self.alpha, self.beta)
def _update_pheromone_map(self):
#always a square matrix
for start in range(len(self.pheromone_map)):
for end in range(len(self.pheromone_map)):
#decay the pheromone value at this location
#tau_xy <- (1-rho)*tau_xy (ACO)
self.pheromone_map[start][end] = (1-self.pheromone_evaporation_coefficient)*self.pheromone_map[start][end]
#then add all contributions to this location for each ant that travered it
#(ACO)
#tau_xy <- tau_xy + delta tau_xy_k
# delta tau_xy_k = Q / L_k
self.pheromone_map[start][end] += self.ant_updated_pheromone_map[start][end]
def _populate_ant_updated_pheromone_map(self, ant):
route = ant.get_route()
for i in range(len(route)-1):
#find the pheromone over the route the ant traversed
current_pheromone_value = float(self.ant_updated_pheromone_map[route[i]][route[i+1]])
#update the pheromone along that section of the route
#(ACO)
# delta tau_xy_k = Q / L_k
new_pheromone_value = self.pheromone_constant/ant.get_distance_traveled()
self.ant_updated_pheromone_map[route[i]][route[i+1]] = current_pheromone_value + new_pheromone_value
self.ant_updated_pheromone_map[route[i+1]][route[i]] = current_pheromone_value + new_pheromone_value
def mainloop(self):
for _ in range(self.iterations):
#start the multi-threaded ants, calls ant.run() in a new thread
for ant in self.ants:
ant.start()
#source: http://stackoverflow.com/a/11968818/5343977
#wait until the ants are finished, before moving on to modifying shared resources
for ant in self.ants:
ant.join()
for ant in self.ants:
#update ant_updated_pheromone_map with this ant's constribution of pheromones along its route
self._populate_ant_updated_pheromone_map(ant)
#if we haven't seen any paths yet, then populate for comparisons later
if not self.shortest_distance:
self.shortest_distance = ant.get_distance_traveled()
if not self.shortest_path_seen:
self.shortest_path_seen = ant.get_route()
#if we see a shorter path, then save for return
if ant.get_distance_traveled() < self.shortest_distance:
self.shortest_distance = ant.get_distance_traveled()
self.shortest_path_seen = ant.get_route()
#decay current pheromone values and add all pheromone values we saw during traversal (from ant_updated_pheromone_map)
self._update_pheromone_map()
#flag that we finished the first pass of the ants traversal
if self.first_pass:
self.first_pass = False
#reset all ants to default for the next iteration
self._init_ants(self.start)
#reset ant_updated_pheromone_map to record pheromones for ants on next pass
self.ant_updated_pheromone_map = self._init_matrix(len(self.nodes), value=0)
#translate shortest path back into callers node id's
ret = []
for id in self.shortest_path_seen:
ret.append(self.id_to_key[id])
return ret
and my module-test file is:
import ant_colony
import math
test_nodes = {0: (0, 7), 1: (3, 9), 2: (12, 4), 3: (14, 11), 4: (8, 11) ,5: (15, 6), 6: (6, 15), 7: (15, 9), 8: (12, 10), 9: (10, 7)}
def distance(start, end):
x_distance = abs(start[0] - end[0])
y_distance = abs(start[1] - end[1])
return math.sqrt(pow(x_distance, 2) + pow(y_distance, 2))
colony = ant_colony(test_nodes, distance)
answer = colony.mainloop()
print(answer)
but when it runs, this error appears:
TypeError: 'module' object is not callable
I tried a lot of ways but they didn't work at all. I tried to test two coordinates instead of distance, I tried to test using arguments and so on, but they did not work. How can I fix it?
You can see that error says module object is not callable. Because when you wrote
import ant_colony
What you did was import that whole module when what you really wanted was just the class
so you can go ahead and do
from ant_colony import ant_colony
and you are good to go!
You are importing module but not a class. Replace this line of code:
import ant_colony
with this line of code:
from ant_colony import ant_colony
What's the difference? In my example you are importing class called ant_colony from file called ant_colony. The first thing is path to file and the second thing is the name of class, function etc.
You have to call it as ant_colony.ant_colony(...) , not ant_colony(...)
See this thread TypeError: 'module' object is not callable

Class method for merge sort recursively

I am trying to create a method for merge sort recursively. I am not so familiar with objects and i am trying to get this right. I know the algorithm works as a stand alone function, but when trying to implement as a class method, i get error.
The problem lies in this two lines of code:
left_list = left_list.merge_sort()
right_list = right_list.merge_sort()
class Lists(object):
def __init__(self):
self.capacity = 8
self.arr = [None] * capacity
self.size = 0
def merge_sort(self):
if self.size <= 1:
return self.arr
middle = self.size // 2
left_list = self.arr[:middle]
right_list = self.arr[middle:]
left_list = left_list.merge_sort()
right_list = right_list.merge_sort()
return list(self.merge(left_list, right_list))
def merge(self, left_half, right_half):
res = []
while len(left_half) != 0 and len(right_half) != 0:
if left_half[0] < right_half[0]:
res.append(left_half[0])
left_half.remove(left_half[0])
else:
res.append(right_half[0])
right_half.remove(right_half[0])
if len(left_half) == 0:
res = res + right_half
else:
res = res + left_half
return res
And of course is another function append, that appends each time at the self.arr and increases the size by 1 each time an element is appended.

How can I create a running average of the last N items in a time series?

My basic idea was to create a linked list, and as each new value comes in, add 1/N times the new value and subtract 1/N times the first value, then move the pointer to first along by one and free the memory that had been associated with first.
This won't ultimately be implemented in Python but just to get the process clear in my head, I tried to write it in Python, but my implementation is flawed. Do I need a doubly linked list for this? Is there an alternative approach (not linked-list based) that would be better?
Here's my attempt so far:
class Link:
def __init__(self,val):
self.next = None
self.value = val
class LinkedList:
def __init__(self,maxlength):
self.current_link = None
self.maxlength = maxlength
self.sum = 0.
self.average = None
self.length = 0
self._first_link = None
def add_link(self,val):
new_link = Link(val)
new_link.next = self.current_link
self.current_link = new_link
if self._first_link is None:
self._first_link = self.current_link
self.sum += val
if self.length < self.maxlength:
self.length += 1
else:
self.sum -= self._first_link.value
self._first_link = self._first_link.next # this line is flawed
self.average = self.sum/self.length
def get_first(self):
return self._first_link.value
# Main
ll = LinkedList(5)
for ii in xrange(10):
ll.add_link(ii)
print ii,ll.get_first(),ll.average
The problem is that _first_link gets set to a value that doesn’t have a next. That is, _first_link gets set to the first item that's added, but its next is None, so I don't see how to move it along by 1 as I want to. This is what makes me wonder if a doubly linked list is needed.
I'd appreciate any advice.
I think the simplest implementation is to use a circular linked list (a.k.a. a ring):
class Link(object):
def __init__(self, value=0.0):
self.next = None
self.value = value
class LinkedRing(object):
def __init__(self, length):
self.sum = 0.0
self.length = length
self.current = Link()
# Initialize all the nodes:
last = self.current
for i in xrange(length-1): # one link is already created
last.next = Link()
last = last.next
last.next = self.current # close the ring
def add_val(self, val):
self.sum -= current.value
self.sum += val
self.current.value = val
self.current = self.current.next
def average(self):
return self.sum / self.length
# Test example:
rolling_sum = LinkedRing(5)
while True:
x = float(raw_input())
rolling_sum.add_val(x)
print(">> Average: %f" % rolling_sum.average())
You can implement this using collections.deque and the numerically stable math for maintaining running averages:
import collections
class AveragingBuffer(object):
def __init__(self, maxlen):
assert( maxlen>1)
self.q=collections.deque(maxlen=maxlen)
self.xbar=0.0
def append(self, x):
if len(self.q)==self.q.maxlen:
# remove first item, update running average
d=self.q.popleft()
self.xbar=self.xbar+(self.xbar-d)/float(len(self.q))
# append new item, update running average
self.q.append(x)
self.xbar=self.xbar+(x-self.xbar)/float(len(self.q))
if __name__=="__main__":
import scipy
ab=AveragingBuffer(10)
for i in xrange(32):
ab.append(scipy.rand())
print ab.xbar, scipy.average(ab.q), len(ab.q)
Okay, I thought of a solution that works in O[1] time. I'm still curious if anyone has a linked-list-based solution, but this solution avoids the LL entirely:
class Recent:
def __init__(self,maxlength):
self.maxlength = maxlength
self.length = 0
self.values = [0 for ii in xrange(maxlength)]
self.index = 0
self.total = 0.
self.average = 0.
def add_val(self,val):
last = self.values[self.index%self.maxlength]
self.values[self.index%self.maxlength] = val
self.total += val
self.total -= last
if self.length < self.maxlength:
self.length += 1
self.average = self.total / self.length
self.index += 1
def print_vals(self):
print ""
for ii in xrange(self.length):
print ii,self.values[ii%self.maxlength]
print "average:",self.average
# Example to show it works
rr = Recent(5)
for ii in xrange(3):
rr.add_val(ii)
rr.print_vals()
for ii in xrange(13):
rr.add_val(ii)
rr.print_vals()

Categories

Resources