def _json_game_player_stats(game, data): """ Parses the 'home' and 'away' team stats and returns an OrderedDict mapping player id to their total game statistics as instances of nflgame.player.GamePlayerStats. """ players = OrderedDict() for team in ('home', 'away'): for category in nflgame.statmap.categories: if category not in data[team]['stats']: continue for pid, raw in iteritems(data[team]['stats'][category]): stats = {} for k, v in iteritems(raw): if k == 'name': continue stats['%s_%s' % (category, k)] = v if pid not in players: home = team == 'home' if home: team_name = game.home else: team_name = game.away players[pid] = nflgame.player.GamePlayerStats( pid, raw['name'], home, team_name) players[pid]._add_stats(stats) return players
def __sub__(self, other): assert self.playerid == other.playerid assert type(self) == type(other) new_player = GamePlayerStats(self.playerid, self.name, self.home, self.team) new_player._add_stats(self._stats) for bk, bv in iteritems(other._stats): if bk not in new_player._stats: # stat was taken away? ignore. continue new_player._stats[bk] -= bv if new_player._stats[bk] == 0: del new_player._stats[bk] else: new_player.__dict__[bk] = new_player._stats[bk] anydiffs = False for k, v in iteritems(new_player._stats): if v > 0: anydiffs = True break if not anydiffs: return None return new_player
def __init__(self, drive, playid, data): self.data = data self.drive = drive self.playid = playid self.team = data['posteam'] self.home = self.drive.home self.desc = data['desc'] self.note = data['note'] self.down = int(data['down']) self.yards_togo = int(data['ydstogo']) self.touchdown = 'touchdown' in self.desc.lower() self._stats = {} if not self.team: self.time, self.yardline = None, None else: self.time = GameClock(data['qtr'], data['time']) self.yardline = FieldPosition(self.team, data['yrdln']) # Load team statistics directly into the Play instance. # Things like third down attempts, first downs, etc. if '0' in data['players']: for info in data['players']['0']: if info['statId'] not in nflgame.statmap.idmap: continue statvals = nflgame.statmap.values(info['statId'], info['yards']) for k, v in iteritems(statvals): v = self.__dict__.get(k, 0) + v self.__dict__[k] = v self._stats[k] = v # Load the sequence of "events" in a play into a list of dictionaries. self.events = _json_play_events(data['players']) # Now load cumulative player data for this play into # a GenPlayerStats generator. We then flatten this data # and add it to the play itself so that plays can be # filter by these statistics. self.__players = _json_play_players(self, data['players']) self.players = nflgame.seq.GenPlayerStats(self.__players) for p in self.players: for k, v in iteritems(p.stats): # Sometimes we may see duplicate statistics (like tackle # assists). Let's just overwrite in this case, since this # data is from the perspective of the play. i.e., there # is one assisted tackle rather than two. self.__dict__[k] = v self._stats[k] = v
def csv(self, fileName, allfields=False): """ Given a file-name fileName, csv will write the contents of the Players sequence to fileName formatted as comma-separated values. The resulting file can then be opened directly with programs like Excel, Google Docs, Libre Office and Open Office. Note that since each player in a Players sequence may have differing statistical categories (like a quarterback and a receiver), the minimum constraining set of statisical categories is used as the header row for the resulting CSV file. This behavior can be changed by setting 'allfields' to True, which will use every available field in the header. """ import csv fields, rows = set([]), [] players = list(self) for p in players: for field, stat in iteritems(p.stats): fields.add(field) if allfields: for statId, info in iteritems(statmap.idmap): for field in info['fields']: fields.add(field) fields = sorted(list(fields)) for p in players: d = { 'name': p.name, 'id': p.playerid, 'home': p.home and 'yes' or 'no', 'team': p.team, 'pos': 'N/A', } if p.player is not None: d['pos'] = p.player.position for field in fields: if field in p.__dict__: d[field] = p.__dict__[field] else: d[field] = "" rows.append(d) fieldNames = ["name", "id", "home", "team", "pos"] + fields rows = [dict((f, f) for f in fieldNames)] + rows csv.DictWriter(open(fileName, 'w+'), fieldNames).writerows(rows)
def _json_play_players(play, data): """ Takes a single JSON play entry (data) and converts it to an OrderedDict of player statistics. play is the instance of Play that this data is part of. It is used to determine whether the player belong to the home team or not. """ players = OrderedDict() for playerid, statcats in iteritems(data): if playerid == '0': continue for info in statcats: if info['statId'] not in nflgame.statmap.idmap: continue if playerid not in players: home = play.drive.game.is_home(info['clubcode']) if home: team_name = play.drive.game.home else: team_name = play.drive.game.away stats = nflgame.player.PlayPlayerStats(playerid, info['playerName'], home, team_name) players[playerid] = stats statvals = nflgame.statmap.values(info['statId'], info['yards']) players[playerid]._add_stats(statvals) return players
def __new__(cls, eid=None, fpath=None): # If we can't get a valid JSON data, exit out and return None. try: rawData = _get_json_data(eid, fpath) except urllib.URLError: return None if rawData is None or rawData.strip() == '{}': return None game = object.__new__(cls) game.rawData = rawData try: if eid is not None: game.eid = eid game.data = json.loads(game.rawData)[game.eid] else: # For when we have rawData (fpath) and no eid. game.eid = None game.data = json.loads(game.rawData) for k, v in iteritems(game.data): if isinstance(v, dict): game.eid = k game.data = v break assert game.eid is not None except ValueError: return None return game
def formatted_stats(self): """ Returns a roughly-formatted string of all statistics for this player. """ s = [] for stat, val in iteritems(self._stats): s.append('%s: %s' % (stat, val)) return ', '.join(s)
def max_player_stats(self): """ Returns a GenPlayers sequence of player statistics that combines game statistics and play statistics by taking the max value of each corresponding statistic. This is useful when accuracy is desirable. Namely, using only play-by-play data or using only game statistics can be unreliable. That is, both are inconsistently correct. Taking the max values of each statistic reduces the chance of being wrong (particularly for stats that are in both play-by-play data and game statistics), but does not eliminate them. """ game_players = list(self.players) play_players = list(self.drives.plays().players()) max_players = OrderedDict() # So this is a little tricky. It's possible for a player to have # only statistics at the play level, and therefore not be represented # in the game level statistics. Therefore, we initialize our # max_players with play-by-play stats first. Then go back through # and combine them with available game statistics. for pplay in play_players: newp = nflgame.player.GamePlayerStats(pplay.playerid, pplay.name, pplay.home, pplay.team) maxstats = {} for stat, val in iteritems(pplay._stats): maxstats[stat] = val newp._overwrite_stats(maxstats) max_players[pplay.playerid] = newp for newp in itervalues(max_players): for pgame in game_players: if pgame.playerid != newp.playerid: continue maxstats = {} for stat, val in iteritems(pgame._stats): maxstats[stat] = max([val, newp._stats.get(stat, -MAXINT)]) newp._overwrite_stats(maxstats) break return nflgame.seq.GenPlayerStats(max_players)
def filter(self, **kwargs): """ filters the sequence based on a set of criteria. Parameter names should be equivalent to the properties accessible in the items of the sequence. For example, where the items are instances of the Stats class:: players.filter(home=True, passing_tds=1, rushing_yds=lambda x: x>0) Returns a sequence with only players on the home team that have a single passing touchdown and more than zero rushing yards. If a field specified does not exist for a particular item, that item is excluded from the result set. If a field is set to a value, then only items with fields that equal that value are returned. If a field is set to a function---which must be a predicate---then only items with field values satisfying that function will be returned. Also, special suffixes that begin with '__' may be added to the end of a field name to invoke built in predicates. For example, this:: players.filter(receiving_rec=lambda v: v > 0) Is equivalent to:: players.filter(receiving_rec__gt=0) Other suffixes includes gt, le, lt, ne, ge, etc. (Django users should feel right at home.) """ preds = [] for k, v in iteritems(kwargs): def pred(field, value, item): # TODO: Move pred function outside of loop for suffix, p in iteritems(_BUILTIN_PREDS): if field.endswith(suffix): f = field[:field.index(suffix)] if not hasattr(item, f) or getattr(item, f) is None: return False return p(getattr(item, f), value) if not hasattr(item, field) or getattr(item, field) is None: return False if isinstance(value, type(lambda x: x)): return value(getattr(item, field)) return getattr(item, field) == value preds.append(functools.partial(pred, k, v)) gen = ifilter(lambda item: all([f(item) for f in preds]), self) return self.__class__(gen)
def tds(self): """ Returns the total number of touchdowns credited to this player across all statistical categories. """ n = 0 for f, v in iteritems(self.__dict__): if f.endswith('tds'): n += v return n
def pred(field, value, item): # TODO: Move pred function outside of loop for suffix, p in iteritems(_BUILTIN_PREDS): if field.endswith(suffix): f = field[:field.index(suffix)] if not hasattr(item, f) or getattr(item, f) is None: return False return p(getattr(item, f), value) if not hasattr(item, field) or getattr(item, field) is None: return False if isinstance(value, type(lambda x: x)): return value(getattr(item, field)) return getattr(item, field) == value
def _json_play_events(data): """ Takes a single JSON play entry (data) and converts it to a list of events. """ temp = list() for playerid, statcats in iteritems(data): for info in statcats: if info['statId'] not in nflgame.statmap.idmap: continue statvals = nflgame.statmap.values(info['statId'], info['yards']) statvals['playerid'] = None if playerid == '0' else playerid statvals['playername'] = info['playerName'] or None statvals['team'] = info['clubcode'] temp.append((int(info['sequence']), statvals)) return [t[1] for t in sorted(temp, key=lambda t: t[0])]
def _overwrite_stats(self, stats): for k, v in iteritems(stats): self.__dict__[k] = v self._stats[k] = self.__dict__[k]
def _add_stats(self, stats): for k, v in iteritems(stats): self.__dict__[k] = self.__dict__.get(k, 0) + v self._stats[k] = self.__dict__[k]