def pad(lst,method="center"): ''' Pads a list of strings according to the method chosen to be as long as the longest element of the list ''' longest = max([len(l) for l in lst]) if method == "center": lst = [l.center(longest) for l in lst] elif method == "left": lst = [l.ljust(longest) for l in lst] elif method == "right": lst = [l.rjust(longest) for l in lst] else: raise Exception("Bad Pad Method") return lst def avgerator(dct,method="percent"): ''' Takes key:value pairs where the value is some number and returns a key:value pair where the value is scaled 0->1 ''' outdict = {} if method == "percent": #Percent (default) scales to the sum of all values total = float(sum(dct.values())) for d in dct: outdict[d] = dct[d]/total elif method == "max": #max sumes to the max value total = float(max(dct.values())) for d in dct: outdict[d] = dct[d]/total elif method == "abspercent": #Same as percent, but counts negatives as positive for the sum total = float(sum(map(abs,dct.values()))) for d in dct: outdict[d] = dct[d]/total else: raise Exception("Bad Averageator Method") return outdict def bargraph(dct,width=80,sort=True): ''' Takes a dictionary and outputs a horizontal bar graph with defined width ''' if (max(dct.values()) > 1) or (min(dct.values()) < -1): raise Exception("Dict values must be between negative one and one") if (min(dct.values()) < 0): barwidth = (width - (3 + len(dct.keys()[0]) + 6))/2 # 3 for colons, 6 for value/percent sign, other for strings negative = True else: barwidth = width - (3 + len(dct.keys()[0]) + 6) # 3 for colons, 6 for value/percent sign, other for strings negative = False bars = [] for d in dct: if dct[d] < 0: barln = int(barwidth*abs(dct[d])) spaceln = barwidth - barln bar = " " * spaceln + "#" * barln + " " * (barwidth -1) else: if negative: barln = int(barwidth*dct[d]) -1 spaceln = barwidth - barln -1 bar = " " * barwidth + "#" * barln + " " * spaceln else: barln = int(barwidth*dct[d]) spaceln = barwidth - barln bar = "#"*barln + " "*spaceln fmt = { "name":d, "value":dct[d], "bar":bar } if negative: string = "%(name)s:%(value)+.2f%%:%(bar)s:" % fmt else: string = "%(name)s:%(value).2f%%:%(bar)s:" % fmt bars.append((dct[d],string)) #output a tuple, so I can sort it if sort: bars.sort() bars,output = zip(*bars) #pull the output from the sorted list in the right order return output def doitall(l1,l2): l1 = pad(l1) d = dict(zip(l1,l2)) d2 = avgerator(d,method="abspercent") output = bargraph(d2) str = "" for o in output: str += o + "\n" return str def gridinate(listx,listy,maxx,maxy,scale=1.0): grid = [] # Remember, the x,y orientation of grid is flipped because you reference the row then the column for dy in range(maxy+1): grid.append([" " for dx in range(maxx+1)]) points = zip(listx,listy) negs = [] for x,y in points: if x < maxx and y < maxy: if y < 0: negs.append((x,y)) else: grid[y][x] = "X" grid.reverse() #make it so the bottom left is 0,0 instead of top left string = "" step = maxy for y in grid[:-1]: ''' skip the 0 point (x-axis), because it gets drawn at the time of the axis. It is the last element in the array, because it is drawn top down. ''' position = "%+ 5.1f" % (step/scale) #this is the y-coordinate printout on the left string += position + "|" #border for x in y: string += x string += "\n" step -= 1 #draw the axis: string += "%+ 5.1f|" % 0 for i in grid[-1]: if i == " ": string += "-" else: string += i string += "\n" if negs: #create the negative side of the graph. negrid = [] for dy in range(maxy+1): negrid.append([" " for dx in range(maxx+1)]) for x,y in negs: if x < maxx and abs(y) < maxy: #these y's are negative, so by making them + we draw from the top negrid[-y][x] = "X" step = -1 for y in negrid[1:]: position = "%+ 5.1f" % (step/scale) #this is the y-coordinate printout on the left string += position + "|" #border for x in y: string += x string += "\n" step -= 1 return string def coordstogrid(xlist, ylist,maxx=False,maxy=False,marks=5,scalef=1.0): if not maxx: maxx = max(xlist) if not maxy: maxy = max(ylist) string = gridinate(xlist,ylist,maxx,maxy) #Draw the x-axis scalestring = " |" #spaces just for alignment with y-axis numbering scalestring2 = " |" every = max(maxx/marks,1) for i in range(1,maxx+1): if (i%every) == 0: num = "%.f" % ((i/scalef)) lgth = len(num) if lgth > 1: test = scalestring2[(-lgth + 1):] .strip() scalestring2 = scalestring2[:(-lgth +1)] + num scalestring += "|" elif lgth == 1: scalestring2 += num scalestring += "|" else: scalestring += " " scalestring2 += " " else: scalestring += " " scalestring2 += " " string += scalestring + "\n" string += scalestring2 + "\n" return string def functiontogrid(function,maxx,scale=1,maxy=False,marks=5): maxx = (maxx+1)*scale scalef = float(scale) #making a good 'ol fashioned x,y table for graphing just like you did in pre-algebra ys = [] xs = range(maxx) for x in xs: ys.append(int(function(x/scalef)*scale)) if not maxy: maxy = min(maxx*scale,max(ys)+1) string = gridinate(xs,ys,maxx,maxy,scalef) #Draw the x-axis scalestring = " |" #spaces just for alignment with y-axis numbering scalestring2 = " |" every = max(maxx/marks,1) for i in range(1,maxx+1): if (i%every) == 0: num = "%.1f" % ((i/scalef)) lgth = len(num) if lgth > 1: test = scalestring2[(-lgth + 1):] .strip() if test == "": scalestring2 = scalestring2[:(-lgth +1)] + num scalestring += "|" else: scalestring += " " scalestring2 += " " elif lgth == 1: scalestring2 += num scalestring += "|" else: scalestring += " " scalestring2 += " " else: scalestring += " " scalestring2 += " " string += scalestring + "\n" string += scalestring2 + "\n" return string