"""
ReportWithLevels
__author__ = 'Margery Harrison'
__license__ = "Public Domain"
__version__ = "1.0"
"""
import sys
class ReportWithLevels():
def __init__(self):
"""
Sets the default values for the class
"""
self.fdout=sys.stdout #default - writes report to stdout
self.number_width=10
self.level_indent=2
self.total_width=30
self.debug = True
self.min_level = 1
self.max_level = 4
#levels before which to print a newline
self.newline_before = [1]
# open the input path as file to write to
def open_outfile(self,path):
try:
self.fdout = open(path,'w')
except IOError:
msg="{0:s} Can't open and write to {1:s}".format(self.__class__.__name__,path)
sys.stderr.write(msg)
# print debug statement if debugging turned on
def debugPrint(self,message):
if self.debug:
print(message)
# Write a line out to the output file with newline at end
def writeLine(self,message):
self.fdout.write(message + '\n')
# Print message and total with indentation set by input level
def printLevel(self, level, message, total):
lev=int(level)
assert lev >= self.min_level and lev <= self.max_level,\
"input level not within current limits"
#skip a space before level 1 statements
if lev in self.newline_before:
self.writeLine('')
#s1 and s2 are number of spaces for formatting
s1 = lev * self.level_indent
s2 = self.total_width - s1
#initialize fstr to the correct number of spaces
fstr='{{0:{0:d}s}} {{1:{1:d}s}}'.format(s1,s2)
# Number format string is right justified within number_width
number_format='{{2:-{0:d}d}}'.format(self.number_width)
fstr+=number_format
self.debugPrint('level {0:d} format str= {1:s}'.format(lev,fstr))
self.writeLine(fstr.format(' ', message, int(total)))
if __name__ == '__main__':
print "Testing ReportWithLevels.printLevel()"
pl=ReportWithLevels()
pl.open_outfile("testout.txt")
pl.writeLine("This is my report")
pl.number_width=12
tot=7
for level in [1,2,3,2,2,3,4,3,1]: #range(1,4):
msg='level {0:d} msg'.format(level)
tot=tot * 12
pl.printLevel(level,msg,tot)
#pl.printLevel(8,"level 8 msg",88) #test assert error
Mostly Linux & Python syntax notes and hyperlinks.
Sunday, July 27, 2014
python: Write totals at different indentation levels
assert isinstance() not for file stream parameters?
I'm writing methods in PyCharm, and I'd like to follow its hints for the way I should be structuring my code. Here I'm passing in a parameter of type file stream, could be sys.out or a file descriptor. It prompted me to include an "assert isinstance()" for the input parameter.
"""
I was looking up what type to call it for the purposes of isinstance() and I came across http://dobesland.wordpress.com/2007/10/07/python-isinstance-considered-useful/:
def set_stream(self,fdout):"""
:param fdout: """ assert isinstance(fdout,...) I was looking up what type to call it for the purposes of isinstance() and I came across http://dobesland.wordpress.com/2007/10/07/python-isinstance-considered-useful/:
The classic example of this is python’s famous “file-like objects”, which typically implement read and/or write in the same way and are accepted by various python functions. I believe the DB API is another well-used example of this. In both cases, there is no common base-class, so it’s impossible to use isinstance() to check whether a particular object is, in fact, file-like or a database object.I guess I'll leave that assert() out. The code will break reliably enough when someone tries to write to an fdout that isn't the right sort of object.
Sunday, July 20, 2014
python: CSV analysis using Counter and DictReader and format
Here is a simple CSV file:
name,color,size,shape,number
tom,red,big,square,3
mary,blue,big,triangle,5
sally,green,small,square,2
edith,blue,small,triangle,1
wally,red,big,square,7
jon,blue,small,triangle,3
This code reads in the simple CSV and reports on it:
Here is the output:
name,color,size,shape,number
tom,red,big,square,3
mary,blue,big,triangle,5
sally,green,small,square,2
edith,blue,small,triangle,1
wally,red,big,square,7
jon,blue,small,triangle,3
import os.path import csv import collections
def printLevel(level, message, total):""" Print message and totals, with spacing determined by level Keyword arguments:
level -- integer from 1 to 4
message -- string, e.g. "Number of happy tomcats"
total -- for this version, should be an integer count
"""
lev=int(level)
if lev==1:
print("")
#fstr1='{0:5s} {1:35s} {2:-3d}'
#fstr2='{0:10s} {1:30s} {2:-3d}'
s1=5*lev #5 or 10
s2=40-s1
fstr='{{0:{0:d}s}} {{1:{1:d}s}}'.format(s1,s2)
fstr+=' {2:-3d}'
print fstr.format(' ', message, int(total))
def print_colors_shapes(c):"""Prints report on number of shapes of different colors keyword argument: c -- collection that includes values in shape_list, color_list below """
shape_list= ['square', 'triangle','circle']
color_list= ['red', 'blue', 'green','yellow']
#first print shapes
for shape in shape_list:
msg = 'Number of '+ shape + 's'
tot=c.get(shape,"0")
printLevel(1, msg,tot)
if int(tot) > 0:
for color in color_list:
msg = "Number of " + color + " " + shape + 's'
tot=c.get(color + '_' + shape,"0")
printLevel(2, msg, tot)
for color in color_list:
msg = 'Total {0} shapes'.format(color)
tot = c.get(color,'0')
printLevel(1,msg,tot)
def count_color_shape(my_reader):""" create counters for colors and shapes separate and combined keyword argument: my_reader -- of type csv.DictReader"""
c = collections.Counter()
for row in my_reader:
print row
color = row['color']
shape = row['shape']
c[color] += 1
c[shape] += 1
c[color + '_' + shape] += 1
return c
def read_dict(path):
with open(path) as csv_file:
my_reader = csv.DictReader(csv_file)
print my_reader.fieldnames
#now have the read_dict() method return the Counter datastructure
c = count_color_shape(my_reader)
print c
print_colors_shapes(c)
def test_dict():
datadir = "/Users/margery/Documents/pystuff/pyGotham/demo/data"
csv_file = 'simpleCSV.txt'
path = os.path.join(datadir, csv_file)
print path
read_dict(path)
test_dict()
Here is the output:
/usr/bin/python /Users/margery/PycharmProjects/proj3/TestDict1.py
/Users/margery/Documents/pystuff/pyGotham/demo/data/simpleCSV.txt
['name', 'color', 'size', 'shape', 'number']
{'color': 'red', 'shape': 'square', 'number': '3', 'name': 'tom', 'size': 'big'}
{'color': 'blue', 'shape': 'triangle', 'number': '5', 'name': 'mary', 'size': 'big'}
{'color': 'green', 'shape': 'square', 'number': '2', 'name': 'sally', 'size': 'small'}
{'color': 'blue', 'shape': 'triangle', 'number': '1', 'name': 'edith', 'size': 'small'}
{'color': 'red', 'shape': 'square', 'number': '7', 'name': 'wally', 'size': 'big'}
{'color': 'blue', 'shape': 'triangle', 'number': '3', 'name': 'jon', 'size': 'small'}
Counter({'blue': 3, 'square': 3, 'triangle': 3, 'blue_triangle': 3, 'red_square': 2, 'red': 2, 'green': 1, 'green_square': 1})
Number of squares 3
Number of red squares 2
Number of blue squares 0
Number of green squares 1
Number of yellow squares 0
Number of triangles 3
Number of red triangles 0
Number of blue triangles 3
Number of green triangles 0
Number of yellow triangles 0
Number of circles 0
Total red shapes 2
Total blue shapes 3
Total green shapes 1
Total yellow shapes 0
Saturday, July 19, 2014
python: csv.dictreader - That's what I should have been using
I learned late Wednesday that I'm supposed to give a talk at PyGotham about my forays into python dictionary land. So I did some research. And what I really should have been using is a csv reader flavor called dictreader. This does exist in python 2.6, so being stuck there is no excuse.
There's a good explanation at http://pymotw.com/2/csv/#using-field-names
And great examples at:
Friday, June 20, 2014
python: Links to my posts about python dictionary objects
purpose
The reason for my interest is that we generate output tab-separated files containing values that need to be tabulated into reports. I was looking for a way to generalize the code for creating these reports. In other words:Given a list of strings describing the counts saved in each column of a CSV file, what would be the best way to automate the creation of reports of counts for individual and common categories?
For example, if a list of voters had values like Party Affiliation (D/R/O/U), Gender, Zip Code, etc, I should be able to feed in these values and get a report listing totals for Female Republicans voters[R][F], Male Democrats with Zip Code 12345 voters[D][M][12345], total unaffiliated voters voters[U], etc. And then I should be able to use the same tool to automate reports for my next list of zoo animals, with columns tabulating Species, Location, Diet...
input
- list of column ID's
- dictionary mapping column ID's to corresponding strings to use in report
- collection of totals desired for the report
output
- From input 1. and the CSV file you should be able to produce the dictionary structure containing all the totals.
- From 1 and 2 you should be able to produce a function to generate strings for a report line
- From 1, 2, 3 you should be able to produce a general sort of report. (I haven't bothered with that yet.)
links
I know there is a better way to organize this blog to enable easier searching by topic but I haven't investigated how yet. Until then, collected here are the posts where I was exploring the use of multidimensional dictionaries:- Monday, November 28, 2011
python: 2-D dictionary, somewhat dynamically filled - Tuesday, March 11, 2014 6pm
python: building 3-D dictionary - Tuesday, March 11, 2014 7pm
Python: build and initialize a dictionary from a list of lists - Friday, March 28, 2014
python: 1-d dictionary for counting hierarchical attributes - Thursday, May 23, 2013
Python: given two lists of strings, create 2-d dictionary to hold int values, initialized to 0
I ended up using a 1-D dictionary to save my multidimensional values. This worked so long as I could use a single character to express all the possibilities for each dimension, with no re-use.
Friday, March 28, 2014
python: 1-d dictionary for counting heirarchical attributes
This dictionary method uses concatenated characters instead of sub-dictionaries to track the hierarchical counts. e.g Number of Dogs could be pets['D'], Number of red dogs could be pets['DR']. Number of red dogs living in Cambridge could be pets['DRC'].
What follows is the general dictionary-building method, followed by a method written to illustrate how it could be used, followed by the printout from the test method.
What follows is the general dictionary-building method, followed by a method written to illustrate how it could be used, followed by the printout from the test method.
# Given a list of lists, build a set of keys that combine fields from each listThen when I run it, it prints out:
# return a 1-d dictionary of these keys initialized to input_default
#
# @param list_of_lists = list of key-lists - Should be single unique characters.
# @param input_default [optional] Value to initialize each dictionary entry
# if input_default parameter not given, then 0 is used
def build1Dictionary(list_of_lists,input_default=0):
number_of_lists=len(list_of_lists)
if number_of_lists==0:
return dict()
keylist=list_of_lists[0]
if number_of_lists==1:
return dict.fromkeys(keylist,input_default)
for next_list in list_of_lists[1:]:
new_list=list(keylist)
for key in keylist:
for xkey in next_list:
new_list.append(key+xkey)
keylist=new_list
return dict.fromkeys(keylist,input_default)
# Here is a method to test the dictionary:
def count_pets():
#Create the dictionary
pets=['D','C','F','B']
gender=['g','b']
home=['R','S','U']
words=dict(D="Dog",C="Cat",F="Fish",B="Bird",\
g="Girl",b="Boy",R="Rural",S="Suburban",U="City")
pets_dict=build1Dictionary([pets,gender,home])
#fill the dictionary with some counts
data=["DgS","DgS","DbR","CgU","FgS","CgR","BbR"]
for pet in data:
p=pet[0:1]
g=pet[1:2]
h=pet[2:3]
pets_dict[p]+=1
pets_dict[p+g]+=1
pets_dict[p+g+h]+=1
#Print the counts indented by level of heiarchy
for p in pets:
print ("%d %s(s)" % (pets_dict[p],words[p]))
for g in gender:
if pets_dict[p+g]>0:
print (" %d owned by a %s" % \
(pets_dict[p+g],words[g]))
for h in home:
if pets_dict[p+g+h] > 0:
print(" %d in %s environment." % \
(pets_dict[p+g+h],words[h]))
if __name__ == '__main__':
count_pets()
3 Dog(s)
2 owned by a Girl
2 in Suburban environment.
1 owned by a Boy
1 in Rural environment.
2 Cat(s)
2 owned by a Girl
1 in Rural environment.
1 in City environment.
1 Fish(s)
1 owned by a Girl
1 in Suburban environment.
1 Bird(s)
1 owned by a Boy
1 in Rural environment.
Tuesday, March 11, 2014
Python: build and initialize a dictionary from a list of lists
OK, so building on the last post. Here's a general purpose function that takes a list of desired key-lists, and returns a multi-dimensional data dictionary.
##Here I test it:
## Takes dictionary and a list of desired keys
## adds the keys to the dictionary, initializing them to input parameter
#
# @param input_dictionary Name of input dictionary
# @param input_keys List of desired keys
# @param input_default [optional] Value to initialize each dictionary entry
# if input_default parameter not given, then 0 is used
#
#
def setDictionaryDefaults(input_dictionary,input_keys,input_default=0):
for key in input_keys:
input_dictionary.setdefault(key,input_default)
##
# Given a list of lists, build a multidimensional dictionary, initialized to optional input parameter
#
# @param list_of_lists = list of key-lists
# @param input_default [optional] Value to initialize each dictionary entry
# if input_default parameter not given, then 0 is used
def buildDictionary(list_of_lists,input_default=0):
d=dict()
number_of_lists=len(list_of_lists)
if number_of_lists==0:
return d
setDictionaryDefaults(d,list_of_lists[0],input_default)
if number_of_lists==1:
return d
next_d=dict()
for next_list in list_of_lists[1:]:
setDictionaryDefaults(next_d,next_list,d)
d=next_d
next_d=dict()
return d
Python 2.4.1 (#2, Jul 24 2007, 12:14:31)
[GCC 3.2.3 20030502 (Red Hat Linux 3.2.3-34)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from lists_to_dict import buildDictionary
>>> a=['a','b']
>>> c=[a]
>>> d=buildDictionary(c,1)
>>> d
{'a': 1, 'b': 1}
>>> q=['R','S','T']
>>> x=['X','XX','XXX']
>>> y=['Y','YY']
>>> c=[a,q,x,y]
>>> c
[['a', 'b'], ['R', 'S', 'T'], ['X', 'XX', 'XXX'], ['Y', 'YY']]
>>> d=buildDictionary(c,1)
>>> d
{'Y': {'X': {'S': {'a': 1, 'b': 1}, 'R': {'a': 1, 'b': 1}, 'T': {'a': 1, 'b': 1}}, 'XX': {'S': {'a': 1, 'b': 1}, 'R': {'a': 1, 'b': 1}, 'T': {'a': 1, 'b': 1}}, 'XXX': {'S': {'a': 1, 'b': 1}, 'R': {'a': 1, 'b': 1}, 'T': {'a': 1, 'b': 1}}}, 'YY': {'X': {'S': {'a': 1, 'b': 1}, 'R': {'a': 1, 'b': 1}, 'T': {'a': 1, 'b': 1}}, 'XX': {'S': {'a': 1, 'b': 1}, 'R': {'a': 1, 'b': 1}, 'T': {'a': 1, 'b': 1}}, 'XXX': {'S': {'a': 1, 'b': 1}, 'R': {'a': 1, 'b': 1}, 'T': {'a': 1, 'b': 1}}}}
>>>
>>> c=[]
>>> d=buildDictionary(c,1)
>>> d
{}
>>> a=['q','r']
>>> c=[a]
>>> buildDictionary(c)
{'q': 0, 'r': 0}
>>> d=buildDictionary(c)
>>> d
{'q': 0, 'r': 0}
>>>
python: building 3-D dictionary
We're using 2.4 so don't have defaultdict. Her's a way to create one using dict() and setdefault():
Now use it:
>>> hid=dict()
>>> hid.setdefault('H',0)
0
>>> hid.setdefault('I',0)
0
>>> bkd=dict()
>>> bkd
{}
>>> bkd.setdefault('Z',hid)
{'I': 0, 'H': 0}
>>> bkd
{'Z': {'I': 0, 'H': 0}}
>>> bkd.setdefault('CH',hid)
{'I': 0, 'H': 0}
>>> bkd
{'CH': {'I': 0, 'H': 0}, 'Z': {'I': 0, 'H': 0}}
>>> epd=dict()
>>> epd.setdefault('E',bkd)
{'CH': {'I': 0, 'H': 0}, 'Z': {'I': 0, 'H': 0}}
>>> epd.setdefault('P',bkd)
{'CH': {'I': 0, 'H': 0}, 'Z': {'I': 0, 'H': 0}}
>>> epd
{'P': {'CH': {'I': 0, 'H': 0}, 'Z': {'I': 0, 'H': 0}}, 'E': {'CH': {'I': 0, 'H': 0}, 'Z': {'I': 0, 'H': 0}}}
>>> ep=['E','P']
>>> bk=['Z','CH']
>>> hi=['H','I']
>>> i=0
>>> for e in ep:
... for b in bk:
... for h in hi:
... epd[e][b][h]=i
... i+=1
...
>>> epd
{'P': {'CH': {'I': 7, 'H': 6}, 'Z': {'I': 7, 'H': 6}}, 'E': {'CH': {'I': 7, 'H': 6}, 'Z': {'I': 7, 'H': 6}}}
>>> epd['E']['Z']['H']I want to start with lists and build dictionaries from there. Here's the start of that automation:
6
>>> def setDefaults(k,d):Now update setDefaults to take the default initializer as a parameter:
... for key in k:
... d.setdefault(key,0)
...
>>> kay=['A','B','C','D']
>>> dee=dict()
>>> setDefaults(kay,dee)
>>> dee
{'A': 0, 'C': 0, 'B': 0, 'D': 0}
>>> def setDefaults(k,d,i):OK, now here we start with three lists and build a 3-d dictionary using the setDefaults() method:
... for key in k:
... d.setdefault(key,i)
...
>>> kay
['A', 'B', 'C', 'D']
>>> d2=dict()
>>> setDefaults(kay,d2,2)
>>> d2
{'A': 2, 'C': 2, 'B': 2, 'D': 2}
>>> kay
['A', 'B', 'C', 'D']
>>> ell=['X','Y','Z']
>>> grk=['pi','ro','lamtha','tau']
>>> kay_ell=dict()
>>> kay_d=dict()
>>> setDefaults(kay,kay_d,0)
>>> kay_d
{'A': 0, 'C': 0, 'B': 0, 'D': 0}
>>> setDefaults(ell,kay_ell,kay_d)
>>> ell
['X', 'Y', 'Z']
>>> kay_ell
{'Y': {'A': 0, 'C': 0, 'B': 0, 'D': 0}, 'X': {'A': 0, 'C': 0, 'B': 0, 'D': 0}, 'Z': {'A': 0, 'C': 0, 'B': 0, 'D': 0}}
>>> kay_ell_grk=dict()
>>> setDefaults(grk,kay_ell_grk,kay_ell)
>>> kay_ell_grk
{'tau': {'Y': {'A': 0, 'C': 0, 'B': 0, 'D': 0}, 'X': {'A': 0, 'C': 0, 'B': 0, 'D': 0}, 'Z': {'A': 0, 'C': 0, 'B': 0, 'D': 0}}, 'pi': {'Y': {'A': 0, 'C': 0, 'B': 0, 'D': 0}, 'X': {'A': 0, 'C': 0, 'B': 0, 'D': 0}, 'Z': {'A': 0, 'C': 0, 'B': 0, 'D': 0}}, 'ro': {'Y': {'A': 0, 'C': 0, 'B': 0, 'D': 0}, 'X': {'A': 0, 'C': 0, 'B': 0, 'D': 0}, 'Z': {'A': 0, 'C': 0, 'B': 0, 'D': 0}}, 'lamtha': {'Y': {'A': 0, 'C': 0, 'B': 0, 'D': 0}, 'X': {'A': 0, 'C': 0, 'B': 0, 'D': 0}, 'Z': {'A': 0, 'C': 0, 'B': 0, 'D': 0}}}
>>>
Thursday, January 23, 2014
python: "splat" operator * to unpack a list to use it as method parameters
I had a long list of parameters to be re-used in different methods.
I found the answer in:
http://stackoverflow.com/questions/4979542/python-use-list-as-function-parameters
Apply the "splat" operator (*) to the parameter list, and it will work.
Here's an example:
if condition1:I wanted to define the list only once.
method1( a,b,c,d,e,f,g,h,i,j)
elif condition2:
method2( a,b,c,d,e,f,g,h,i,j)
parameter_list=a,b,c,d,e,f,g,h,i,jI didn't know how to use the values in the method call. If you use it directly, i.e.
method1(parameter_list) <- br="" nope="">->it's only one parameter, instead of the number of parameters in the list.
I found the answer in:
http://stackoverflow.com/questions/4979542/python-use-list-as-function-parameters
Apply the "splat" operator (*) to the parameter list, and it will work.
Here's an example:
>>> def sum(a,b,c):
... print a+b+c
...
>>> sum(1,2,3)
6
>>> q=1,2,3
>>> sum(q)
Traceback (most recent call last):
File "", line 1, in ?
TypeError: sum() takes exactly 3 arguments (1 given)
>>> sum(*q)
6
Tuesday, September 10, 2013
Subscribe to:
Posts (Atom)
![[ Celebrate 30 years of GNU! ]](https://static.fsf.org/nosvn/misc/GNU_30th_badge.png)