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