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

Source Code for Module nflgame.player

  1  from collections import OrderedDict 
  2  import csv 
  3  import functools 
  4   
  5  categories = ("passing", "rushing", "receiving", 
  6                "fumbles", "kicking", "punting", "kickret", "puntret", 
  7                "defense") 
  8  """ 
  9  categories is a list of all individual statistical categories reported by 
 10  NFL's GameCenter. 
 11  """ 
 12   
 13  _tdfields = ('passing_tds', 'rushing_tds', 'receiving_tds', 
 14                'kickret_tds', 'puntret_tds') 
 15   
16 -class Players (object):
17 """ 18 Players implements a sequence type and provides a convenient API for 19 searching sets of players. 20 """ 21
22 - def __init__(self, iterable):
23 """ 24 Creates a new Players sequence from an iterable where each element 25 of the iterable is an instance of the Player class. 26 """ 27 self.__players = iterable 28 29 # This is pretty evil. Basically, we autogenerate a method for each 30 # stat category ("passing", "rushing", "receiving", etc...) that 31 # returns a generator which filters out any players not in that 32 # category. 33 for category in categories: 34 def gen(category): 35 def _gen(): 36 for p in self: 37 if p.__dict__[category]: 38 yield p
39 return lambda: Players(_gen())
40 self.__dict__['_%s' % category] = gen(category) 41
42 - def name(self, name):
43 """ 44 Returns a single player whose name equals `name`. If no such player 45 can be found, None is returned. 46 47 Note that NFL GameCenter formats their names like "T.Brady" and 48 "W.Welker". Thus, `name` should also be in this format. 49 """ 50 for p in self: 51 if p.name == name: 52 return p 53 return None
54
55 - def playerid(self, playerid):
56 """ 57 Returns a single player whose NFL GameCenter identifier equals 58 `playerid`. This probably isn't too useful, unless you need 59 to disambiguate between two players with the name first initial 60 and last name. 61 62 If no such player with the given identifier is found, None is 63 returned. 64 """ 65 for p in self: 66 if p.playerid == playerid: 67 return p 68 return None
69
70 - def touchdowns(self):
71 """ 72 touchdowns is a convenience method for returning a Players 73 sequence of all players with at least one touchdown. 74 75 It is essentially a filter on the following fields: passing_tds, 76 rushing_tds, receiving_tds, kickret_tds and puntret_tds. 77 """ 78 def gen(): 79 for p in self: 80 for f in _tdfields: 81 if f not in p.__dict__: 82 continue 83 if p.__dict__[f] > 0: 84 yield p
85 return Players(gen()) 86
87 - def filter(self, **kwargs):
88 """ 89 filters the Players sequence based on a set of criteria. Parameter 90 names should be equivalent to the properties accessible in instances 91 of the Player class. 92 93 For example:: 94 95 players.filter(home=True, passing_tds=1, rushing_yds=lambda x: x>0) 96 97 Returns a Players sequence with only players on the home team that 98 have a single passing touchdown and more than zero rushing yards. 99 100 If a field specified does not exist for a particular Player, that 101 Player is excluded from the result set. 102 103 If a field is set to a value, then only players with fields that equal 104 that value are returned. 105 106 If a field is set to a function---which must be a predicate---then 107 only players with field values satisfying that function will 108 be returned. 109 """ 110 preds = [] 111 for k, v in kwargs.iteritems(): 112 def pred(field, value, player): 113 if field not in player.__dict__: 114 return False 115 if isinstance(value, type(lambda x: x)): 116 return value(player.__dict__[field]) 117 return player.__dict__[field] == value
118 preds.append(functools.partial(pred, k, v)) 119 def gen(): 120 for p in self: 121 if all([f(p) for f in preds]): 122 yield p 123 return Players(gen()) 124
125 - def passing(self):
126 """Returns players that have a "passing" statistical category.""" 127 return self._passing()
128
129 - def rushing(self):
130 """Returns players that have a "rushing" statistical category.""" 131 return self._rushing()
132
133 - def receiving(self):
134 """Returns players that have a "receiving" statistical category.""" 135 return self._receiving()
136
137 - def fumbles(self):
138 """Returns players that have a "fumbles" statistical category.""" 139 return self._fumbles()
140
141 - def kicking(self):
142 """Returns players that have a "kicking" statistical category.""" 143 return self._kicking()
144
145 - def punting(self):
146 """Returns players that have a "punting" statistical category.""" 147 return self._punting()
148
149 - def kickret(self):
150 """Returns players that have a "kickret" statistical category.""" 151 return self._kickret()
152
153 - def puntret(self):
154 """Returns players that have a "puntret" statistical category.""" 155 return self._puntret()
156
157 - def defense(self):
158 """Returns players that have a "kicking" statistical category.""" 159 return self._defense()
160
161 - def limit(self, n):
162 """ 163 Limit the sequence to N players. 164 """ 165 def gen(): 166 for i, p in enumerate(self): 167 if i >= n: 168 return 169 yield p
170 return Players(gen()) 171
172 - def sort(self, field, descending=True):
173 """ 174 sorts the players according to the field specified---where field is 175 a property in a Player object. If descending is false, players will 176 be sorted in order from least to greatest. 177 178 Note that if field does not exist in any Player being sorted, a 179 KeyError will be raised. 180 """ 181 return Players(sorted(self.__players, reverse=descending, 182 key=lambda p: p.__dict__[field]))
183
184 - def csv(self, fileName):
185 """ 186 Given a file-name fileName, csv will write the contents of 187 the Players sequence to fileName formatted as comma-separated values. 188 The resulting file can then be opened directly with programs like 189 Excel, Google Docs, Libre Office and Open Office. 190 191 Note that since each player in a Players sequence may have differing 192 statistical categories (like a quarterback and a receiver), the 193 minimum constraining set of statisical categories is used as the 194 header row for the resulting CSV file. 195 """ 196 fields, rows = [], [] 197 players = list(self) 198 for p in players: 199 for category, stats in p.all_stats().iteritems(): 200 for stat in stats: 201 field = '%s_%s' % (category, stat) 202 if field in fields: 203 continue 204 fields.append(field) 205 for p in players: 206 d = { 207 'name': p.name, 208 'id': p.playerid, 209 'home': p.home and 'yes' or 'no', 210 } 211 for field in fields: 212 if field in p.__dict__: 213 d[field] = p.__dict__[field] 214 else: 215 d[field] = "" 216 rows.append(d) 217 218 fieldNames = ["name", "id", "home"] + fields 219 rows = [{f: f for f in fieldNames}] + rows 220 csv.DictWriter(open(fileName, 'w+'), fieldNames).writerows(rows)
221
222 - def __add__(self, other):
223 """ 224 Adds two Players sequences by concatenating the generators composing 225 each Players sequence. 226 """ 227 if other is None: 228 return self 229 def gen(): 230 for p in self: 231 yield p 232 for p in other: 233 yield p
234 return Players(gen()) 235
236 - def __str__(self):
237 """Returns a list of player names in the sequence.""" 238 return '[%s]' % ', '.join([str(p) for p in self])
239
240 - def __iter__(self):
241 """Make this an iterable sequence.""" 242 if self.__players is None: 243 return iter([]) 244 if isinstance(self.__players, OrderedDict): 245 return self.__players.itervalues() 246 return iter(self.__players)
247
248 - def __reversed__(self):
249 """Satisfy the built in reversed.""" 250 return reversed(self.__players)
251
252 -class Player (object):
253 """ 254 Player represents a single player and all of his statistical categories 255 for a single game. Every player has 'playerid', 'name' and 'home' fields. 256 Additionally, depending upon which statistical categories that player 257 was involved in for the game, he'll have properties such as 'passing_tds', 258 'rushing_yds', 'defense_int' and 'kicking_fgm'. 259 260 In order to know whether a paricular player belongs to a statical category, 261 you may use the filtering methods of a Players sequence or alternatively, 262 use the 'passing', 'rushing', 'kicking', etc., boolean members of this 263 class. 264 265 You may also inspect whether a player has a certain property by using 266 the special __dict__ attribute. For example:: 267 268 if 'passing_yds' in player.__dict__: 269 # Do something with player.passing_yds 270 """
271 - def __init__(self, playerid, name, home):
272 """ 273 Create a new Player instance with the player id (from NFL.com's 274 GameCenter), the player's name (e.g., "T.Brady") and whether the 275 player is playing in a home game or not. 276 """ 277 self.playerid = playerid 278 self.name = name 279 self.home = home 280 self.__stats = OrderedDict() 281 for category in categories: 282 self.__dict__[category] = False
283
284 - def tds(self):
285 """ 286 Returns the total number of touchdowns credited to this player across 287 all statistical categories. 288 """ 289 n = 0 290 for f in _tdfields: 291 if f in self.__dict__: 292 n += self.__dict__[f] 293 return n
294
295 - def all_stats(self):
296 """ 297 Returns a dict of all stats for the player. Each key is a statistical 298 category corresponding to a dict with keys corresponding to each 299 statistic and values corresponding to each statistic's value. 300 """ 301 return self.__stats
302
303 - def formatted_stats(self):
304 """ 305 Returns a roughly-formatted string of all statistics for this player. 306 """ 307 s = [] 308 for category in self.__stats.iterkeys(): 309 for stat, val in self.__stats[category].iteritems(): 310 s.append('%s_%s: %s' % (category, stat, val)) 311 return ', '.join(s)
312
313 - def _add_stats(self, category, stats):
314 assert category not in self.__stats, \ 315 'Cannot add two sets of stats from the same category ' \ 316 'to the same player "%s"' % self.name 317 self.__dict__[category] = True 318 for stat, val in stats.iteritems(): 319 if stat == "name": 320 continue 321 self.__dict__['%s_%s' % (category, stat)] = val 322 self.__stats.setdefault(category, OrderedDict())[stat] = val
323
324 - def __str__(self):
325 """ 326 Simply returns the player's name, e.g., "T.Brady". 327 """ 328 return self.name
329
330 - def __add__(self, player):
331 """ 332 Adds two players together. Only two player objects that correspond 333 to the same human (i.e., GameCenter identifier) can be added together. 334 335 If two different players are added together, an assertion will 336 be raised. 337 338 The effect of adding two player objects simply corresponds to the 339 sums of all statistical values. 340 341 Note that as soon as two players have been added, the 'home' property 342 becomes undefined. 343 """ 344 assert self.playerid == player.playerid 345 new_player = Player(self.playerid, self.name, None) 346 stats1 = self.__stats 347 stats2 = player.__stats 348 349 # Add stats from self. Piece of cake. 350 for category, stats in stats1.iteritems(): 351 new_player._add_stats(category, stats) 352 353 # Now add stats from player. A little more complicated because 354 # we need to *add* them to each value already in new_player's stats. 355 for category, stats in stats2.iteritems(): 356 if not new_player.__dict__[category]: 357 assert category not in new_player.__stats 358 new_player._add_stats(category, stats) 359 else: 360 assert category in new_player.__stats 361 for k, v in stats.iteritems(): 362 new_player.__stats[category][k] += v 363 364 return new_player
365