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

Source Code for Module nflgame.player

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