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