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.games = 1 281 self.__stats = OrderedDict() 282 for category in categories: 283 self.__dict__[category] = False
284
285 - def tds(self):
286 """ 287 Returns the total number of touchdowns credited to this player across 288 all statistical categories. 289 """ 290 n = 0 291 for f in _tdfields: 292 if f in self.__dict__: 293 n += self.__dict__[f] 294 return n
295
296 - def all_stats(self):
297 """ 298 Returns a dict of all stats for the player. Each key is a statistical 299 category corresponding to a dict with keys corresponding to each 300 statistic and values corresponding to each statistic's value. 301 """ 302 return self.__stats
303
304 - def formatted_stats(self):
305 """ 306 Returns a roughly-formatted string of all statistics for this player. 307 """ 308 s = [] 309 for category in self.__stats.iterkeys(): 310 for stat, val in self.__stats[category].iteritems(): 311 s.append('%s_%s: %s' % (category, stat, val)) 312 return ', '.join(s)
313
314 - def _add_stats(self, category, stats):
315 assert category not in self.__stats, \ 316 'Cannot add two sets of stats from the same category ' \ 317 'to the same player "%s"' % self.name 318 self.__dict__[category] = True 319 for stat, val in stats.iteritems(): 320 if stat == "name": 321 continue 322 self.__dict__['%s_%s' % (category, stat)] = val 323 self.__stats.setdefault(category, OrderedDict())[stat] = val
324
325 - def __str__(self):
326 """ 327 Simply returns the player's name, e.g., "T.Brady". 328 """ 329 return self.name
330
331 - def __add__(self, player):
332 """ 333 Adds two players together. Only two player objects that correspond 334 to the same human (i.e., GameCenter identifier) can be added together. 335 336 If two different players are added together, an assertion will 337 be raised. 338 339 The effect of adding two player objects simply corresponds to the 340 sums of all statistical values. 341 342 Note that as soon as two players have been added, the 'home' property 343 becomes undefined. 344 """ 345 assert self.playerid == player.playerid 346 new_player = Player(self.playerid, self.name, None) 347 new_player.games = self.games + player.games 348 stats1 = self.__stats 349 stats2 = player.__stats 350 351 # Add stats from self. Piece of cake. 352 for category, stats in stats1.iteritems(): 353 new_player._add_stats(category, stats) 354 355 # Now add stats from player. A little more complicated because 356 # we need to *add* them to each value already in new_player's stats. 357 for category, stats in stats2.iteritems(): 358 if not new_player.__dict__[category]: 359 assert category not in new_player.__stats 360 new_player._add_stats(category, stats) 361 else: 362 assert category in new_player.__stats 363 for k, v in stats.iteritems(): 364 if k == 'name': 365 continue 366 new_player.__dict__['%s_%s' % (category, k)] += v 367 new_player.__stats[category][k] += v 368 369 return new_player
370