Package nflgame :: Module seq
[frames] | no frames]

Source Code for Module nflgame.seq

  1  import csv 
  2  import functools 
  3  import itertools 
  4  import operator 
  5   
  6  from nflgame import OrderedDict 
  7   
  8  _BUILTIN_PREDS = { 
  9      '__lt': operator.lt, 
 10      '__le': operator.le, 
 11      '__ne': operator.ne, 
 12      '__ge': operator.ge, 
 13      '__gt': operator.gt, 
 14  } 
 15  """ 
 16  A dictionary of suffixes to predicates that can be used in Gen.filter. 
 17  The suffix corresponds to what to add to the end of a field name to invoke 
 18  the predicate it corresponds to. For example, this:: 
 19   
 20      players.filter(receiving_rec=lambda v: v > 0) 
 21   
 22  Is equivalent to:: 
 23   
 24      players.filter(receiving_rec__gt=0) 
 25   
 26  (Django users should feel right at home.) 
 27  """ 
 28   
 29   
30 -class Gen (object):
31 """ 32 Players implements a sequence type and provides a convenient API for 33 searching sets of players. 34 """ 35
36 - def __init__(self, iterable):
37 """ 38 Creates a new Players sequence from an iterable where each element 39 of the iterable is an instance of the Player class. 40 """ 41 self.__iter = iterable
42
43 - def filter(self, **kwargs):
44 """ 45 filters the sequence based on a set of criteria. Parameter 46 names should be equivalent to the properties accessible in the items 47 of the sequence. For example, where the items are instances of 48 the Stats class:: 49 50 players.filter(home=True, passing_tds=1, rushing_yds=lambda x: x>0) 51 52 Returns a sequence with only players on the home team that 53 have a single passing touchdown and more than zero rushing yards. 54 55 If a field specified does not exist for a particular item, that 56 item is excluded from the result set. 57 58 If a field is set to a value, then only items with fields that equal 59 that value are returned. 60 61 If a field is set to a function---which must be a predicate---then 62 only items with field values satisfying that function will 63 be returned. 64 65 Also, special suffixes that begin with '__' may be added to the 66 end of a field name to invoke built in predicates. 67 For example, this:: 68 69 players.filter(receiving_rec=lambda v: v > 0) 70 71 Is equivalent to:: 72 73 players.filter(receiving_rec__gt=0) 74 75 Other suffixes includes gt, le, lt, ne, ge, etc. 76 77 (Django users should feel right at home.) 78 """ 79 preds = [] 80 for k, v in kwargs.iteritems(): 81 def pred(field, value, item): 82 for suffix, p in _BUILTIN_PREDS.iteritems(): 83 if field.endswith(suffix): 84 f = field[:field.index(suffix)] 85 if f not in item.__dict__: 86 return False 87 return p(item.__dict__[f], value) 88 if field not in item.__dict__: 89 return False 90 if isinstance(value, type(lambda x: x)): 91 return value(item.__dict__[field]) 92 return item.__dict__[field] == value
93 preds.append(functools.partial(pred, k, v)) 94 95 gen = itertools.ifilter(lambda item: all([f(item) for f in preds]), 96 self) 97 return self.__class__(gen)
98
99 - def limit(self, n):
100 """ 101 Limit the sequence to N items. 102 """ 103 return self.__class__(itertools.islice(self, n))
104
105 - def sort(self, field, descending=True):
106 """ 107 sorts the sequence according to the field specified---where field is 108 a property on an item in the sequence. If descending is false, items 109 will be sorted in order from least to greatest. 110 111 Note that if field does not exist in any item being sorted, a 112 KeyError will be raised. 113 """ 114 return self.__class__(sorted(self, reverse=descending, 115 key=lambda item: item.__dict__[field]))
116
117 - def __str__(self):
118 """Returns a list of items in the sequence.""" 119 return '[%s]' % ', '.join([str(item) for item in self])
120
121 - def __iter__(self):
122 """Make this an iterable sequence.""" 123 if self.__iter is None: 124 return iter([]) 125 if isinstance(self.__iter, OrderedDict): 126 return self.__iter.itervalues() 127 return iter(self.__iter)
128
129 - def __reversed__(self):
130 """Satisfy the built in reversed.""" 131 return reversed(self.__iter)
132 133
134 -class GenDrives (Gen):
135 """ 136 GenDrives implements a sequence type and provides a convenient API 137 for searching drives. 138 """
139 - def plays(self):
140 """ 141 Returns all of the plays, in order, belonging to every drive in 142 the sequence. 143 """ 144 return GenPlays(itertools.chain(*map(lambda d: d.plays, self)))
145
146 - def players(self):
147 """ 148 Returns the combined player stats for every player that participated 149 in any of the drives in the sequence. 150 """ 151 return self.plays().players()
152
153 - def number(self, n, team=None):
154 """ 155 Gets the Nth drive where the first drive corresponds to n=1. This is 156 only useful given a complete collection of drives for an entire game. 157 158 If the team parameter is specified (i.e., team='NE'), then n will 159 be interpreted as *that* team's Nth drive. 160 """ 161 assert n > 0 162 n -= 1 163 if team is None: 164 return list(self)[n] 165 else: 166 i = 0 167 for d in self: 168 if d.team == team: 169 if i == n: 170 return d 171 i += 1 172 assert False, \ 173 'Could not find drive %d for team %s.' % (n + 1, team)
174 175
176 -class GenPlays (Gen):
177 """ 178 GenPlays implements a sequence type and provides a convenient API 179 for searching plays. 180 """
181 - def players(self):
182 """ 183 Returns the combined player stats for every play in the sequence. 184 """ 185 players = OrderedDict() 186 for play in self: 187 for player in play.players: 188 if player.playerid not in players: 189 players[player.playerid] = player 190 else: 191 players[player.playerid] += player 192 return GenPlayerStats(players)
193 194
195 -class GenPlayerStats (Gen):
196 """ 197 GenPlayerStats implements a sequence type and provides a convenient API for 198 searching sets of player statistics. 199 """
200 - def name(self, name):
201 """ 202 Returns a single player whose name equals `name`. If no such player 203 can be found, None is returned. 204 205 Note that NFL GameCenter formats their names like "T.Brady" and 206 "W.Welker". Thus, `name` should also be in this format. 207 """ 208 for p in self: 209 if p.name == name: 210 return p 211 return None
212
213 - def playerid(self, playerid):
214 """ 215 Returns a single player whose NFL GameCenter identifier equals 216 `playerid`. This probably isn't too useful, unless you're trying 217 to do ID mapping. (Players have different identifiers across NFL.com.) 218 219 If no such player with the given identifier is found, None is 220 returned. 221 """ 222 for p in self: 223 if p.playerid == playerid: 224 return p 225 return None
226
227 - def touchdowns(self):
228 """ 229 touchdowns is a convenience method for returning a Players 230 sequence of all players with at least one touchdown. 231 """ 232 def gen(): 233 for p in self: 234 for f in p.__dict__: 235 if f.endswith('tds') and p.__dict__[f] > 0: 236 yield p 237 break
238 return self.__class__(gen())
239
240 - def __filter_category(self, cat):
241 return self.__class__(itertools.ifilter(lambda p: p.has_cat(cat), 242 self))
243
244 - def passing(self):
245 """Returns players that have a "passing" statistical category.""" 246 return self.__filter_category('passing')
247
248 - def rushing(self):
249 """Returns players that have a "rushing" statistical category.""" 250 return self.__filter_category('rushing')
251
252 - def receiving(self):
253 """Returns players that have a "receiving" statistical category.""" 254 return self.__filter_category('receiving')
255
256 - def fumbles(self):
257 """Returns players that have a "fumbles" statistical category.""" 258 return self.__filter_category('fumbles')
259
260 - def kicking(self):
261 """Returns players that have a "kicking" statistical category.""" 262 return self.__filter_category('kicking')
263
264 - def punting(self):
265 """Returns players that have a "punting" statistical category.""" 266 return self.__filter_category('punting')
267
268 - def kickret(self):
269 """Returns players that have a "kickret" statistical category.""" 270 return self.__filter_category('kickret')
271
272 - def puntret(self):
273 """Returns players that have a "puntret" statistical category.""" 274 return self.__filter_category('puntret')
275
276 - def defense(self):
277 """Returns players that have a "defense" statistical category.""" 278 return self.__filter_category('defense')
279
280 - def penalty(self):
281 """Returns players that have a "penalty" statistical category.""" 282 return self.__filter_category('penalty')
283
284 - def csv(self, fileName):
285 """ 286 Given a file-name fileName, csv will write the contents of 287 the Players sequence to fileName formatted as comma-separated values. 288 The resulting file can then be opened directly with programs like 289 Excel, Google Docs, Libre Office and Open Office. 290 291 Note that since each player in a Players sequence may have differing 292 statistical categories (like a quarterback and a receiver), the 293 minimum constraining set of statisical categories is used as the 294 header row for the resulting CSV file. 295 """ 296 fields, rows = [], [] 297 players = list(self) 298 for p in players: 299 for category, stats in p.all_stats().iteritems(): 300 for stat in stats: 301 field = '%s_%s' % (category, stat) 302 if field in fields: 303 continue 304 fields.append(field) 305 for p in players: 306 d = { 307 'name': p.name, 308 'id': p.playerid, 309 'home': p.home and 'yes' or 'no', 310 } 311 for field in fields: 312 if field in p.__dict__: 313 d[field] = p.__dict__[field] 314 else: 315 d[field] = "" 316 rows.append(d) 317 318 fieldNames = ["name", "id", "home"] + fields 319 rows = [dict((f, f) for f in fieldNames)] + rows 320 csv.DictWriter(open(fileName, 'w+'), fieldNames).writerows(rows)
321
322 - def __add__(self, other):
323 """ 324 Adds two sequences of players by combining repeat players and summing 325 their statistics. 326 """ 327 players = OrderedDict() 328 for p in itertools.chain(self, other): 329 if p.playerid not in players: 330 players[p.playerid] = p 331 else: 332 players[p.playerid] += p 333 return GenPlayerStats(players)
334