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