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 def attrget(item): 115 return item.__dict__.get(field, 0)
116 117 return self.__class__(sorted(self, reverse=descending, key=attrget)) 118
119 - def __str__(self):
120 """Returns a list of items in the sequence.""" 121 return '[%s]' % ', '.join([str(item) for item in self])
122
123 - def __iter__(self):
124 """Make this an iterable sequence.""" 125 if self.__iter is None: 126 return iter([]) 127 if isinstance(self.__iter, OrderedDict): 128 return self.__iter.itervalues() 129 return iter(self.__iter)
130
131 - def __reversed__(self):
132 """Satisfy the built in reversed.""" 133 return reversed(self.__iter)
134 135
136 -class GenDrives (Gen):
137 """ 138 GenDrives implements a sequence type and provides a convenient API 139 for searching drives. 140 """
141 - def plays(self):
142 """ 143 Returns all of the plays, in order, belonging to every drive in 144 the sequence. 145 """ 146 return GenPlays(itertools.chain(*map(lambda d: d.plays, self)))
147
148 - def players(self):
149 """ 150 Returns the combined player stats for every player that participated 151 in any of the drives in the sequence. 152 """ 153 return self.plays().players()
154
155 - def number(self, n, team=None):
156 """ 157 Gets the Nth drive where the first drive corresponds to n=1. This is 158 only useful given a complete collection of drives for an entire game. 159 160 If the team parameter is specified (i.e., team='NE'), then n will 161 be interpreted as *that* team's Nth drive. 162 """ 163 assert n > 0 164 n -= 1 165 if team is None: 166 return list(self)[n] 167 else: 168 i = 0 169 for d in self: 170 if d.team == team: 171 if i == n: 172 return d 173 i += 1 174 assert False, \ 175 'Could not find drive %d for team %s.' % (n + 1, team)
176 177
178 -class GenPlays (Gen):
179 """ 180 GenPlays implements a sequence type and provides a convenient API 181 for searching plays. 182 """
183 - def players(self):
184 """ 185 Returns the combined player stats for every play in the sequence. 186 """ 187 players = OrderedDict() 188 for play in self: 189 for player in play.players: 190 if player.playerid not in players: 191 players[player.playerid] = player 192 else: 193 players[player.playerid] += player 194 return GenPlayerStats(players)
195 196
197 -class GenPlayerStats (Gen):
198 """ 199 GenPlayerStats implements a sequence type and provides a convenient API for 200 searching sets of player statistics. 201 """
202 - def name(self, name):
203 """ 204 Returns a single player whose name equals `name`. If no such player 205 can be found, None is returned. 206 207 Note that NFL GameCenter formats their names like "T.Brady" and 208 "W.Welker". Thus, `name` should also be in this format. 209 """ 210 for p in self: 211 if p.name == name: 212 return p 213 return None
214
215 - def playerid(self, playerid):
216 """ 217 Returns a single player whose NFL GameCenter identifier equals 218 `playerid`. This probably isn't too useful, unless you're trying 219 to do ID mapping. (Players have different identifiers across NFL.com.) 220 221 If no such player with the given identifier is found, None is 222 returned. 223 """ 224 for p in self: 225 if p.playerid == playerid: 226 return p 227 return None
228
229 - def touchdowns(self):
230 """ 231 touchdowns is a convenience method for returning a Players 232 sequence of all players with at least one touchdown. 233 """ 234 def gen(): 235 for p in self: 236 for f in p.__dict__: 237 if f.endswith('tds') and p.__dict__[f] > 0: 238 yield p 239 break
240 return self.__class__(gen())
241
242 - def __filter_category(self, cat):
243 return self.__class__(itertools.ifilter(lambda p: p.has_cat(cat), 244 self))
245
246 - def passing(self):
247 """Returns players that have a "passing" statistical category.""" 248 return self.__filter_category('passing')
249
250 - def rushing(self):
251 """Returns players that have a "rushing" statistical category.""" 252 return self.__filter_category('rushing')
253
254 - def receiving(self):
255 """Returns players that have a "receiving" statistical category.""" 256 return self.__filter_category('receiving')
257
258 - def fumbles(self):
259 """Returns players that have a "fumbles" statistical category.""" 260 return self.__filter_category('fumbles')
261
262 - def kicking(self):
263 """Returns players that have a "kicking" statistical category.""" 264 return self.__filter_category('kicking')
265
266 - def punting(self):
267 """Returns players that have a "punting" statistical category.""" 268 return self.__filter_category('punting')
269
270 - def kickret(self):
271 """Returns players that have a "kickret" statistical category.""" 272 return self.__filter_category('kickret')
273
274 - def puntret(self):
275 """Returns players that have a "puntret" statistical category.""" 276 return self.__filter_category('puntret')
277
278 - def defense(self):
279 """Returns players that have a "defense" statistical category.""" 280 return self.__filter_category('defense')
281
282 - def penalty(self):
283 """Returns players that have a "penalty" statistical category.""" 284 return self.__filter_category('penalty')
285
286 - def csv(self, fileName):
287 """ 288 Given a file-name fileName, csv will write the contents of 289 the Players sequence to fileName formatted as comma-separated values. 290 The resulting file can then be opened directly with programs like 291 Excel, Google Docs, Libre Office and Open Office. 292 293 Note that since each player in a Players sequence may have differing 294 statistical categories (like a quarterback and a receiver), the 295 minimum constraining set of statisical categories is used as the 296 header row for the resulting CSV file. 297 """ 298 fields, rows = [], [] 299 players = list(self) 300 for p in players: 301 for category, stats in p.all_stats().iteritems(): 302 for stat in stats: 303 field = '%s_%s' % (category, stat) 304 if field in fields: 305 continue 306 fields.append(field) 307 for p in players: 308 d = { 309 'name': p.name, 310 'id': p.playerid, 311 'home': p.home and 'yes' or 'no', 312 } 313 for field in fields: 314 if field in p.__dict__: 315 d[field] = p.__dict__[field] 316 else: 317 d[field] = "" 318 rows.append(d) 319 320 fieldNames = ["name", "id", "home"] + fields 321 rows = [dict((f, f) for f in fieldNames)] + rows 322 csv.DictWriter(open(fileName, 'w+'), fieldNames).writerows(rows)
323
324 - def __add__(self, other):
325 """ 326 Adds two sequences of players by combining repeat players and summing 327 their statistics. 328 """ 329 players = OrderedDict() 330 for p in itertools.chain(self, other): 331 if p.playerid not in players: 332 players[p.playerid] = p 333 else: 334 players[p.playerid] += p 335 return GenPlayerStats(players)
336